diff --git a/.editorconfig b/.editorconfig index 572b73bd2..bbe9c3107 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,10 @@ indent_style = space indent_size = 2 trim_trailing_whitespace = false +[*.rst] +indent_style = space +indent_size = 3 + [*.yml] indent_style = space indent_size = 2 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index e4c776ed9..f754d16c7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -6,15 +6,14 @@ body: 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 general question, please ask it on the [Discourse forum](https://yosyshq.discourse.group/). 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. + commercial support or work done for Yosys. - type: input id: yosys_version diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index bef410a3c..c758bf1f6 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,11 +1,8 @@ contact_links: - - name: Discussions - url: https://github.com/YosysHQ/yosys/discussions - about: "Have a question? Ask it on our discussions page!" - - name: Community Slack - url: https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA - about: "Yosys Community Slack" + - name: Discourse + url: https://yosyshq.discourse.group + about: "Have a question? Ask it on our Discourse group!" - name: IRC Channel url: https://web.libera.chat/#yosys about: "#yosys on irc.libera.chat" - + diff --git a/.github/ISSUE_TEMPLATE/docs_report.yml b/.github/ISSUE_TEMPLATE/docs_report.yml index aa65c63b9..c3ad2f7cc 100644 --- a/.github/ISSUE_TEMPLATE/docs_report.yml +++ b/.github/ISSUE_TEMPLATE/docs_report.yml @@ -6,8 +6,7 @@ body: 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 general question, please ask it on the [Discourse forum](https://yosyshq.discourse.group/). If you have found a bug in Yosys, or in building the documentation, diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index c521b5296..49d86f341 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,18 +1,17 @@ name: Feature Request -description: "Submit a feature request for Yosys" +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 general question, please ask it on the [Discourse forum](https://yosyshq.discourse.group/). + 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. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 82daf609d..d8d929f3f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,9 @@ +_If your work is part of a larger effort, please discuss your general plans on [Discourse](https://yosyshq.discourse.group/) first to align your vision with maintainers._ + _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._ +_Make sure your change comes with tests. If not possible, share how a reviewer might evaluate it._ + +_These template prompts can be deleted when you're done responding to them._ \ No newline at end of file diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml index dfdcd88c0..60fe481e7 100644 --- a/.github/actions/setup-build-env/action.yml +++ b/.github/actions/setup-build-env/action.yml @@ -1,21 +1,71 @@ name: Build environment setup description: Configure build env for Yosys builds + +inputs: + runs-on: + required: true + type: string + get-build-deps: + description: 'Install Yosys build dependencies' + default: false + required: false + type: boolean + get-docs-deps: + description: 'Install Yosys docs dependencies' + default: false + required: false + type: boolean + get-test-deps: + description: 'Install Yosys test dependencies' + default: false + required: false + type: boolean + get-iverilog: + description: 'Install iverilog' + default: false + required: false + type: boolean + runs: using: composite steps: - - name: Install Linux Dependencies + # if updating common/build/docs dependencies, make sure to update README.md + # and docs/source/getting_started/installation.rst to match. + - name: Linux common 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 + uses: awalsh128/cache-apt-pkgs-action@v1.6.0 + with: + packages: gawk git make python3 + version: ${{ inputs.runs-on }}-commonys + + - name: Linux build dependencies + if: runner.os == 'Linux' && inputs.get-build-deps == 'true' + uses: awalsh128/cache-apt-pkgs-action@v1.6.0 + with: + packages: bison clang flex libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev + version: ${{ inputs.runs-on }}-buildys + + - name: Linux docs dependencies + if: runner.os == 'Linux' && inputs.get-docs-deps == 'true' + uses: awalsh128/cache-apt-pkgs-action@v1.6.0 + with: + packages: graphviz xdot + version: ${{ inputs.runs-on }}-docsys + + # if updating test dependencies, make sure to update + # docs/source/yosys_internals/extending_yosys/test_suites.rst to match. + - name: Linux test dependencies + if: runner.os == 'Linux' && inputs.get-test-deps == 'true' + uses: awalsh128/cache-apt-pkgs-action@v1.6.0 + with: + packages: libgtest-dev + version: ${{ inputs.runs-on }}-testys - 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 + brew bundle - name: Linux runtime environment if: runner.os == 'Linux' @@ -29,7 +79,13 @@ runs: shell: bash run: | echo "${{ github.workspace }}/.local/bin" >> $GITHUB_PATH - echo "$(brew --prefix llvm)/bin" >> $GITHUB_PATH + echo "$(brew --prefix llvm@20)/bin" >> $GITHUB_PATH echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH echo "$(brew --prefix flex)/bin" >> $GITHUB_PATH echo "procs=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV + + - name: Setup iverilog + if: inputs.get-iverilog == 'true' + uses: ./.github/actions/setup-iverilog + with: + runs-on: ${{ inputs.runs-on }} diff --git a/.github/actions/setup-iverilog/action.yml b/.github/actions/setup-iverilog/action.yml new file mode 100644 index 000000000..0acb582e3 --- /dev/null +++ b/.github/actions/setup-iverilog/action.yml @@ -0,0 +1,70 @@ +name: iverilog setup +description: Cached build and install of iverilog + +inputs: + runs-on: + required: true + type: string + +runs: + using: composite + steps: + - name: iverilog Linux deps + if: steps.restore-iverilog.outputs.cache-hit != 'true' && runner.os == 'Linux' + uses: awalsh128/cache-apt-pkgs-action@v1.6.0 + with: + packages: autoconf gperf make gcc g++ bison flex libbz2-dev + version: ${{ inputs.runs-on }}-iverilog + + - name: iverilog macOS deps + if: steps.restore-iverilog.outputs.cache-hit != 'true' && runner.os == 'macOS' + shell: bash + run: | + brew install autoconf + + - name: Get iverilog + id: get-iverilog + shell: bash + run: | + git clone https://github.com/steveicarus/iverilog.git + cd iverilog + echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 + + - uses: actions/cache/restore@v4 + id: restore-iverilog + with: + path: .local/ + key: ${{ inputs.runs-on }}-${{ steps.get-iverilog.outputs.IVERILOG_GIT }} + + - name: Build iverilog + if: steps.restore-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: Check iverilog + shell: bash + run: | + iverilog -V + + - uses: actions/cache/save@v4 + id: save-iverilog + if: steps.restore-iverilog.outputs.cache-hit != 'true' + with: + path: .local/ + key: ${{ steps.restore-iverilog.outputs.cache-primary-key }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 75d799fe1..4bca5a8a5 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -10,15 +10,18 @@ jobs: 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 persist-credentials: false + - name: Setup environment + uses: ./.github/actions/setup-build-env + with: + runs-on: ubuntu-latest + get-build-deps: true + - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: diff --git a/.github/workflows/extra-builds.yml b/.github/workflows/extra-builds.yml index 0c3146e41..b22a399db 100644 --- a/.github/workflows/extra-builds.yml +++ b/.github/workflows/extra-builds.yml @@ -1,6 +1,14 @@ name: Test extra build flows -on: [push, pull_request] +on: + # always test main + push: + branches: + - main + # test PRs + pull_request: + # allow triggering tests, ignores skip check + workflow_dispatch: jobs: pre_job: @@ -11,11 +19,11 @@ jobs: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: + # don't run on documentation changes 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' + # but never cancel main + cancel_others: ${{ github.ref != 'refs/heads/main' }} vs-prep: name: Prepare Visual Studio build @@ -27,19 +35,20 @@ jobs: with: submodules: true persist-credentials: false + - run: sudo apt-get install libfl-dev - 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 + runs-on: windows-latest needs: [vs-prep, pre_job] if: needs.pre_job.outputs.should_skip != 'true' - steps: + steps: - uses: actions/download-artifact@v4 with: name: vcxsrc @@ -50,7 +59,7 @@ jobs: 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 + run: msbuild YosysVS.sln /p:PlatformToolset=v142 /p:Configuration=Release /p:WindowsTargetPlatformVersion=10.0.26100.0 wasi-build: name: WASI build @@ -64,13 +73,24 @@ jobs: persist-credentials: false - 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 + WASI_SDK=wasi-sdk-27.0-x86_64-linux + WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-x86_64-linux.tar.gz if ! [ -d ${WASI_SDK} ]; then curl -L ${WASI_SDK_URL} | tar xzf -; fi + FLEX_VER=2.6.4 + FLEX=flex-${FLEX_VER} + FLEX_URL=https://github.com/westes/flex/releases/download/v${FLEX_VER}/${FLEX}.tar.gz + if ! [ -d ${FLEX} ]; then curl -L ${FLEX_URL} | tar xzf -; fi + + mkdir -p flex-build + (cd flex-build && + ../${FLEX}/configure --prefix=$(pwd)/../flex-prefix && + make && + make install) + mkdir -p build cat > build/Makefile.conf <> Makefile.conf echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf echo "ENABLE_CCACHE := 1" >> Makefile.conf - make -j$procs ENABLE_LTO=1 + echo "ENABLE_HELP_SOURCE := 1" >> Makefile.conf + make -j$procs - name: Prepare docs shell: bash @@ -59,7 +60,6 @@ jobs: with: name: cmd-ref-${{ github.sha }} path: | - docs/source/cmd docs/source/generated docs/source/_images docs/source/code_examples diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index b88662f0f..8c1a3bbd2 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -1,6 +1,14 @@ name: Build and run tests -on: [push, pull_request] +on: + # always test main + push: + branches: + - main + # test PRs + pull_request: + # allow triggering tests, ignores skip check + workflow_dispatch: jobs: pre_job: @@ -11,11 +19,12 @@ jobs: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: + # don't run on documentation changes 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' + # but never cancel main + cancel_others: ${{ github.ref != 'refs/heads/main' }} + pre_docs_job: runs-on: ubuntu-latest outputs: @@ -24,15 +33,16 @@ jobs: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: + # don't run on readme changes 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' + # but never cancel main + cancel_others: ${{ github.ref != 'refs/heads/main' }} build-yosys: name: Reusable build runs-on: ${{ matrix.os }} + # pre_job is a subset of pre_docs_job, so we can always build for pre_docs_job needs: pre_docs_job if: needs.pre_docs_job.outputs.should_skip != 'true' env: @@ -50,6 +60,9 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-build-env + with: + runs-on: ${{ matrix.os }} + get-build-deps: true - name: Build shell: bash @@ -57,8 +70,7 @@ jobs: mkdir build cd build make -f ../Makefile config-$CC - echo 'SANITIZER = undefined' >> Makefile.conf - make -f ../Makefile -j$procs ENABLE_LTO=1 + make -f ../Makefile -j$procs - name: Log yosys-config output run: | @@ -84,7 +96,6 @@ jobs: if: needs.pre_job.outputs.should_skip != 'true' env: CC: clang - UBSAN_OPTIONS: halt_on_error=1 strategy: matrix: os: [ubuntu-latest, macos-latest] @@ -97,41 +108,10 @@ jobs: - 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 }}-${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 + runs-on: ${{ matrix.os }} + get-test-deps: true + get-iverilog: true - name: Download build artifact uses: actions/download-artifact@v4 @@ -158,6 +138,45 @@ jobs: run: | find tests/**/*.err -print -exec cat {} \; + test-cells: + name: Run test_cell + runs-on: ${{ matrix.os }} + needs: [build-yosys, pre_job] + if: needs.pre_job.outputs.should_skip != 'true' + env: + CC: clang + strategy: + matrix: + os: [ubuntu-latest] + steps: + - name: Checkout Yosys + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Setup environment + uses: ./.github/actions/setup-build-env + with: + runs-on: ${{ matrix.os }} + + - 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: test_cell + shell: bash + run: | + ./yosys -p 'test_cell -n 20 -s 1 all' + ./yosys -p 'test_cell -n 20 -s 1 -nosat -aigmap $pow $pmux' + ./yosys -p 'test_cell -n 20 -s 1 -nosat -aigmap $eqx $nex $bweqx' + ./yosys -p 'test_cell -n 20 -s 1 -aigmap $buf' + test-docs: name: Run docs tests runs-on: ${{ matrix.os }} @@ -177,6 +196,10 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-build-env + with: + runs-on: ${{ matrix.os }} + get-build-deps: true + get-docs-deps: true - name: Download build artifact uses: actions/download-artifact@v4 @@ -201,7 +224,7 @@ jobs: name: Try build docs runs-on: [self-hosted, linux, x64, fast] needs: [pre_docs_job] - if: needs.pre_docs_job.outputs.should_skip != 'true' + if: ${{ needs.pre_docs_job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }} strategy: matrix: docs-target: [html, latexpdf] @@ -221,6 +244,7 @@ jobs: run: | make config-clang echo "ENABLE_CCACHE := 1" >> Makefile.conf + echo "ENABLE_HELP_SOURCE := 1" >> Makefile.conf make -j$procs - name: Install doc prereqs diff --git a/.github/workflows/test-compile.yml b/.github/workflows/test-compile.yml index 7a706e69a..31c8bccf6 100644 --- a/.github/workflows/test-compile.yml +++ b/.github/workflows/test-compile.yml @@ -1,6 +1,14 @@ name: Compiler testing -on: [push, pull_request] +on: + # always test main + push: + branches: + - main + # test PRs + pull_request: + # allow triggering tests, ignores skip check + workflow_dispatch: jobs: pre_job: @@ -11,11 +19,11 @@ jobs: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: + # don't run on documentation changes 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' + # but never cancel main + cancel_others: ${{ github.ref != 'refs/heads/main' }} test-compile: runs-on: ${{ matrix.os }} @@ -34,11 +42,14 @@ jobs: - 'gcc-10' # newest, make sure to update maximum standard step to match - 'clang-19' - - 'gcc-13' + - 'gcc-14' include: - # macOS - - os: macos-13 - compiler: 'clang' + # macOS x86 + - os: macos-15-intel + compiler: 'clang-19' + # macOS arm + - os: macos-latest + compiler: 'clang-19' fail-fast: false steps: - name: Checkout Yosys @@ -49,6 +60,9 @@ jobs: - name: Setup environment uses: ./.github/actions/setup-build-env + with: + runs-on: ${{ matrix.os }} + get-build-deps: true - name: Setup Cpp uses: aminya/setup-cpp@v1 @@ -70,7 +84,7 @@ jobs: # maximum standard, only on newest compilers - name: Build C++20 - if: ${{ matrix.compiler == 'clang-19' || matrix.compiler == 'gcc-13' }} + if: ${{ matrix.compiler == 'clang-19' || matrix.compiler == 'gcc-14' }} shell: bash run: | make config-$CC_SHORT diff --git a/.github/workflows/test-sanitizers.yml b/.github/workflows/test-sanitizers.yml new file mode 100644 index 000000000..4c8e3ec51 --- /dev/null +++ b/.github/workflows/test-sanitizers.yml @@ -0,0 +1,78 @@ +name: Check clang sanitizers + +on: + # always test main + push: + branches: + - main + # ignore PRs due to time needed + # allow triggering tests, ignores skip check + workflow_dispatch: + +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: + # don't run on documentation changes + paths_ignore: '["**/README.md", "docs/**", "guidelines/**"]' + + run_san: + name: Build and run tests + runs-on: ${{ matrix.os }} + needs: pre_job + if: needs.pre_job.outputs.should_skip != 'true' + env: + CC: clang + ASAN_OPTIONS: halt_on_error=1 + UBSAN_OPTIONS: halt_on_error=1 + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + sanitizer: ['undefined,address'] + fail-fast: false + steps: + - name: Checkout Yosys + uses: actions/checkout@v4 + with: + submodules: true + persist-credentials: false + + - name: Setup environment + uses: ./.github/actions/setup-build-env + with: + runs-on: ${{ matrix.os }} + get-build-deps: true + get-test-deps: true + get-iverilog: true + + - name: Build + shell: bash + run: | + make config-$CC + echo 'SANITIZER = ${{ matrix.sanitizer }}' >> Makefile.conf + make -j$procs + + - name: Log yosys-config output + run: | + ./yosys-config || true + + - name: Run tests + shell: bash + run: | + make -j$procs test TARGETS= EXTRA_TARGETS= + + - name: Report errors + if: ${{ failure() }} + shell: bash + run: | + find tests/**/*.err -print -exec cat {} \; + + - name: Run unit tests + shell: bash + run: | + make -j$procs unit-test ENABLE_LIBYOSYS=1 diff --git a/.github/workflows/test-verific.yml b/.github/workflows/test-verific.yml index 013c9f8ca..6619e1124 100644 --- a/.github/workflows/test-verific.yml +++ b/.github/workflows/test-verific.yml @@ -1,6 +1,14 @@ name: Build and run tests with Verific (Linux) -on: [push, pull_request] +on: + # always test main + push: + branches: + - main + # test PRs + pull_request: + # allow triggering tests, ignores skip check + workflow_dispatch: jobs: pre-job: @@ -11,15 +19,15 @@ jobs: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: + # don't run on documentation changes 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' + # but never cancel main + cancel_others: ${{ github.ref != 'refs/heads/main' }} test-verific: needs: pre-job - if: needs.pre-job.outputs.should_skip != 'true' + if: ${{ needs.pre-job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }} runs-on: [self-hosted, linux, x64, fast] steps: - name: Checkout Yosys @@ -70,3 +78,47 @@ jobs: if: ${{ github.ref == 'refs/heads/main' }} run: | make -C sby run_ci + + - name: Run unit tests + shell: bash + run: | + make -j$procs unit-test ENABLE_LTO=1 ENABLE_LIBYOSYS=1 + + test-pyosys: + needs: pre-job + if: ${{ needs.pre-job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }} + runs-on: [self-hosted, linux, x64, fast] + steps: + - name: Checkout Yosys + uses: actions/checkout@v4 + with: + persist-credentials: false + submodules: true + - name: Install UV + run: | + curl -LsSf https://astral.sh/uv/install.sh | sh + - name: Runtime environment + run: | + echo "procs=$(nproc)" >> $GITHUB_ENV + echo "${{ github.workspace }}/.local/bin" >> $GITHUB_PATH + + - name: Build pyosys + 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_PYOSYS := 1" >> Makefile.conf + echo "PYTHON_DESTDIR := /usr/lib/python3/site-packages" >> Makefile.conf + make -j$procs + + - name: Install pyosys + run: | + make install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX= + + - name: Run pyosys tests + run: | + export PYTHONPATH=${GITHUB_WORKSPACE}/.local/usr/lib/python3/site-packages:$PYTHONPATH + python3 tests/pyosys/run_tests.py diff --git a/.github/workflows/update-flake-lock.yml b/.github/workflows/update-flake-lock.yml index de7ef04d6..b32498baf 100644 --- a/.github/workflows/update-flake-lock.yml +++ b/.github/workflows/update-flake-lock.yml @@ -6,6 +6,7 @@ on: jobs: lockfile: + if: github.repository == 'YosysHQ/Yosys' runs-on: ubuntu-latest steps: - name: Checkout repository diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 26dcba4a4..78d34db46 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -7,6 +7,7 @@ on: jobs: bump-version: + if: github.repository == 'YosysHQ/Yosys' runs-on: ubuntu-latest steps: - name: Checkout @@ -18,11 +19,8 @@ jobs: - 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')" + if: ${{ !contains(steps.log.outputs.message, 'Bump version') }} run: | make bumpversion git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" @@ -30,7 +28,7 @@ jobs: 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')" + if: ${{ !contains(steps.log.outputs.message, 'Bump version') }} uses: ad-m/github-push-action@master with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index b01ce6b3a..8e055f526 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -4,7 +4,7 @@ name: Build Wheels for PyPI on: workflow_dispatch: schedule: - - cron: '0 10 * * 0' + - cron: "0 10 * * 0" jobs: build_wheels: @@ -25,15 +25,15 @@ jobs: archs: "aarch64", }, { - name: "macOS 13", + name: "macOS 15 x64", family: "macos", - runner: "macos-13", + runner: "macos-15-intel", archs: "x86_64", }, { - name: "macOS 14", + name: "macOS 15 arm64", family: "macos", - runner: "macos-14", + runner: "macos-15", archs: "arm64", }, ## Windows is disabled because of an issue with compiling FFI as @@ -54,28 +54,25 @@ jobs: fetch-depth: 0 submodules: true persist-credentials: false - - 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 + curl -L https://github.com/libffi/libffi/releases/download/v3.4.8/libffi-3.4.8.tar.gz | tar --strip-components=1 -xzC ffi + - if: ${{ matrix.os.family == 'linux' }} + name: "[Linux] Bison 3.8.2" + shell: bash + run: | + mkdir -p bison + curl -L https://ftpmirror.gnu.org/gnu/bison/bison-3.8.2.tar.gz | tar --strip-components=1 -xzC bison ## 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 + echo "PATH=$(brew --prefix flex)/bin:$(brew --prefix bison)/bin:$PATH" >> $GITHUB_ENV - if: ${{ matrix.os.family == 'windows' }} name: "[Windows] Flex/Bison" run: | @@ -100,25 +97,30 @@ jobs: 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 + OPTFLAGS=-O3 PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig - makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a' + PATH="$PWD/bison/src:$PATH" CIBW_ENVIRONMENT_MACOS: > - CXXFLAGS=-I./boost/pfx/include - LINKFLAGS=-L./boost/pfx/lib + OPTFLAGS=-O3 PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig MACOSX_DEPLOYMENT_TARGET=11 - makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a CONFIG=clang' + makeFlags='CONFIG=clang' + PATH="$PWD/bison/src:$PATH" CIBW_BEFORE_BUILD: bash ./.github/workflows/wheels/cibw_before_build.sh - CIBW_TEST_COMMAND: python3 {project}/tests/arch/ecp5/add_sub.py + CIBW_TEST_COMMAND: python3 {project}/tests/pyosys/run_tests.py - uses: actions/upload-artifact@v4 with: name: python-wheels-${{ matrix.os.runner }} path: ./wheelhouse/*.whl upload_wheels: name: Upload Wheels + if: (github.repository == 'YosysHQ/Yosys') && (github.event_name == 'workflow_dispatch') runs-on: ubuntu-latest + # Specifying a GitHub environment is optional, but strongly encouraged + environment: pypi + permissions: + # IMPORTANT: this permission is mandatory for Trusted Publishing + id-token: write needs: build_wheels steps: - uses: actions/download-artifact@v4 @@ -132,6 +134,3 @@ jobs: 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/' }} diff --git a/.github/workflows/wheels/_run_cibw_linux.py b/.github/workflows/wheels/_run_cibw_linux.py index 894470a5a..1e8a0f497 100644 --- a/.github/workflows/wheels/_run_cibw_linux.py +++ b/.github/workflows/wheels/_run_cibw_linux.py @@ -20,11 +20,19 @@ import os import yaml import platform import subprocess +from pathlib import Path -__dir__ = os.path.dirname(os.path.abspath(__file__)) +__yosys_root__ = Path(__file__).absolute().parents[3] +for source in ["ffi", "bison"]: + if not (__yosys_root__ / source).is_dir(): + print( + "You need to download ffi and bison in a similar manner to wheels.yml first." + ) + exit(-1) -workflow = yaml.safe_load(open(os.path.join(os.path.dirname(__dir__), "wheels.yml"))) +with open(__yosys_root__ / ".github" / "workflows" / "wheels.yml") as f: + workflow = yaml.safe_load(f) env = os.environ.copy() @@ -40,5 +48,5 @@ for key, value in cibw_step["env"].items(): continue env[key] = value -env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS") or platform.machine() +env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS", platform.machine()) subprocess.check_call(["cibuildwheel"], env=env) diff --git a/.github/workflows/wheels/cibw_before_all.sh b/.github/workflows/wheels/cibw_before_all.sh index fbb8dcad8..1aef650d7 100644 --- a/.github/workflows/wheels/cibw_before_all.sh +++ b/.github/workflows/wheels/cibw_before_all.sh @@ -1,23 +1,37 @@ -set -e -set -x +#!/bin/bash +set -e -x # Build-time dependencies ## Linux Docker Images if command -v yum &> /dev/null; then - yum install -y flex bison + yum install -y flex # manylinux's bison versions are hopelessly out of date fi if command -v apk &> /dev/null; then apk add flex bison fi +if ! printf '%s\n' '%require "3.8"' '%%' 'start: ;' | bison -o /dev/null /dev/stdin ; then + ( + set -e -x + cd bison + ./configure + make clean + make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu) + ) +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 +# Runtime Dependencies +## Build Static FFI (platform-dependent but not Python version dependent) +( + set -e -x + cd ffi + ## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries + CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx + make clean + 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 +) diff --git a/.github/workflows/wheels/cibw_before_build.sh b/.github/workflows/wheels/cibw_before_build.sh index 018b0e0df..1ce96b291 100644 --- a/.github/workflows/wheels/cibw_before_build.sh +++ b/.github/workflows/wheels/cibw_before_build.sh @@ -1,8 +1,8 @@ set -e set -x -# Don't use objects from previous compiles on Windows/macOS -make clean +# Don't use Python objects from previous compiles +make clean-py # DEBUG: show python3 and python3-config outputs if [ "$(uname)" != "Linux" ]; then @@ -11,24 +11,3 @@ if [ "$(uname)" != "Linux" ]; then 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 diff --git a/.gitignore b/.gitignore index 7b4a1fb1e..a8b04ac45 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,29 @@ +## user config +/Makefile.conf + +## homebrew +/Brewfile.lock.json + +## build artifacts +/.git-abc-submodule-hash +# compiler intermediate files *.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 +*.so.dSYM/ + +## test artifacts +**/run-test.mk +*.err +*.log +*.tmp + +# compiler output files +/kernel/version_*.cc +/share /yosys /yosys.exe /yosys.js @@ -36,22 +39,50 @@ __pycache__ /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 + +# build directories /tests/unit/bintest/ /tests/unit/objtest/ /tests/ystests +/build /result /dist -/*.egg-info -/build -/venv -/boost + +# pyosys +/kernel/*.pyh +/kernel/python_wrappers.cc /ffi +/bison +/venv /*.whl +/*.egg-info + +# yosysjs dependency +/viz.js + +# other +/coverage.info +/coverage_html + + +# these really belong in global gitignore since they're not specific to this project but rather to user tool choice +# but too many people don't have a global gitignore configured: +# https://docs.github.com/en/get-started/git-basics/ignoring-files#configuring-ignored-files-for-all-repositories-on-your-computer +__pycache__ +*~ +.*.swp +/.cache +/.vscode +/.cproject +/.project +/.settings +/qtcreator.files +/qtcreator.includes +/qtcreator.config +/qtcreator.creator +/qtcreator.creator.user +/compile_commands.json diff --git a/Brewfile b/Brewfile index 3696e40b0..917f1bdb4 100644 --- a/Brewfile +++ b/Brewfile @@ -6,9 +6,9 @@ brew "git" brew "graphviz" brew "pkg-config" brew "python3" -brew "tcl-tk" +brew "uv" brew "xdot" brew "bash" -brew "boost-python3" -brew "llvm" +brew "llvm@20" brew "lld" +brew "googletest" diff --git a/CHANGELOG b/CHANGELOG index bdf30260e..6cefcc3ac 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,9 +2,101 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.53 .. Yosys 0.54-dev +Yosys 0.59 .. Yosys 0.60-dev -------------------------- +Yosys 0.58 .. Yosys 0.59 +-------------------------- + * Various + - Pyosys is rewritten using pybind11. + - alumacc: merge independent of sign. + - write_btor: Include $assert and $assume cells in -ywmap output. + - RTLIL parser rewritten for efficiency. + - Wildcards enabled for Liberty file consuming. + - timeest: Add top ports launching/sampling. + + * New commands and options + - Added "-apply_derived_type" option to "box_derive" pass. + - Added "-publish_icells" option to "chtype" pass. + - Added "-width" option to "sim" pass. + - Added "sort" pass for sorting the design objects. + - Merged "synth_ecp5" and "synth_nexus" into "synth_lattice" pass. + - Added "-strict-gw5a-dffs" and "-setundef" options to "synth_gowin" pass. + +Yosys 0.57 .. Yosys 0.58 +-------------------------- + * Various + - Run ABC passes in parallel. + - Extending support for buffer normalization. + - Overhaul of logging APIs. + - read_blif: Represent sequential elements with gate cells. + - Support multiple lib files in abc9_exe. + + * New commands and options + - Added "-wireshape" option to "show" command to allow + control the shape of wire nodes. + - Added "-relativeshare" option to "read_verilog", "synth" + and "techmap" pass for synthesis reproducibility testing. + - "write_rtlil" pass no longer sorts design, added "-sort" + option to match old behavior + - Added "-sva-continue-on-err" to "verific" pass to allow + processing designs that includes unsupported SVA. + +Yosys 0.56 .. Yosys 0.57 +-------------------------- + * New commands and options + - Added "-initstates" option to "abstract" pass. + - Added "-set-assumes" option to "equiv_induct" + and "equiv_simple" passes. + - Added "-always" option to "raise_error" pass. + - Added "-hierarchy" option to "stat" pass. + - Added "-noflatten" option to "synth_quicklogic" pass. + + * Various + - smtbmc: Support skipping steps in cover mode. + - write_btor: support $buf. + - read_verilog: support package import. + +Yosys 0.55 .. Yosys 0.56 +-------------------------- + * New commands and options + - Added "-unescape" option to "rename" pass. + - Added "-assert2cover" option to "chformal" pass. + - Added "linecoverage" pass to generate lcov report from selection. + - Added "opt_hier" pass to enable hierarchical optimization. + - Added "-hieropt" option to "synth" pass. + - Added "-expect-return", "-err-grep" and "-suffix" options + to "bugpoint" pass. + - Added "raise_error" dev pass. + + * Various + - Added groups to command reference documentation. + - Added bugpoint guide to documentation. + - verific: correctly reset Verific flags after import. + +Yosys 0.54 .. Yosys 0.55 +-------------------------- + * Various + - read_verilog: Implemented SystemVerilog unique/priority if. + - "attrmap" pass is able to alter memory attributes. + - verific: Support SVA followed-by operator in cover mode. + +Yosys 0.53 .. Yosys 0.54 +-------------------------- + * New commands and options + - Added "-genlib" option to "abc_new" and "abc9_exe" passes. + - Added "-verbose" and "-quiet" options to "libcache" pass. + - Added "-no-sort" option to "write_aiger" pass. + + * Various + - Added "muldiv_c" peepopt. + - Accept (and ignore) SystemVerilog unique/priority if. + - "read_verilog" copy inout ports in and out of functions/tasks. + - Enable single-bit vector wires in RTLIL. + + * Xilinx support + - Single-port URAM mapping to support memories 2048 x 144b + Yosys 0.52 .. Yosys 0.53 -------------------------- * New commands and options diff --git a/CODEOWNERS b/CODEOWNERS index 879bb8dee..4617c39bb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -20,6 +20,7 @@ passes/opt/opt_lut.cc @whitequark passes/techmap/abc9*.cc @eddiehung @Ravenslofty backends/aiger/xaiger.cc @eddiehung docs/ @KrystalDelusion +docs/source/using_yosys/synthesis/abc.rst @KrystalDelusion @Ravenslofty .github/workflows/*.yml @mmicko ## External Contributors @@ -29,15 +30,16 @@ docs/ @KrystalDelusion # These still override previous lines, so be careful not to # accidentally disable any of the above rules. -frontends/verilog/ @zachjs -frontends/ast/ @zachjs +frontends/verilog/ @widlarizer +frontends/ast/ @widlarizer techlibs/intel_alm/ @Ravenslofty techlibs/gowin/ @pepijndevos techlibs/gatemate/ @pu-cc # pyosys -misc/*.py @btut +pyosys/* @donn +setup.py @donn backends/firrtl @ucbjrl @azidar diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c4376cc4..403292b0b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,14 +19,14 @@ 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. +Before you submit an issue, please check out the [how-to guide for +`bugpoint`](https://yosys.readthedocs.io/en/latest/using_yosys/bugpoint.html). +This guide will take you through the process of using the [`bugpoint` +command](https://yosys.readthedocs.io/en/latest/cmd/bugpoint.html) in Yosys to +produce a [minimal, complete and verifiable +example](https://stackoverflow.com/help/minimal-reproducible-example) (MVCE). +Providing an MVCE with your bug report drastically increases the likelihood that +someone will be able to help resolve your issue. # Using pull requests @@ -60,14 +60,11 @@ 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 +If you have a question about how to use Yosys, please ask on our [Discourse forum](https://yosyshq.discourse.group/) or in our [discussions +page](https://github.com/YosysHQ/yosys/discussions). +The Discourse 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 +We have open [dev 'jour fixe' (JF) meetings](https://docs.google.com/document/d/1SapA6QAsJcsgwsdKJDgnGR2mr97pJjV4eeXg_TVJhRU/edit?usp=sharing) 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. +place to talk to us about how to implement larger PRs. diff --git a/Makefile b/Makefile index 304753d98..ccb1be1b9 100644 --- a/Makefile +++ b/Makefile @@ -23,10 +23,13 @@ ENABLE_VERIFIC_EDIF := 0 ENABLE_VERIFIC_LIBERTY := 0 ENABLE_COVER := 1 ENABLE_LIBYOSYS := 0 +ENABLE_LIBYOSYS_STATIC := 0 ENABLE_ZLIB := 1 +ENABLE_HELP_SOURCE := 0 # python wrappers ENABLE_PYOSYS := 0 +PYOSYS_USE_UV := 1 # other configuration flags ENABLE_GCOV := 0 @@ -43,7 +46,12 @@ 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) +ENABLE_THREADS := 1 +ifeq ($(ENABLE_THREADS),1) DISABLE_ABC_THREADS := 0 +else +DISABLE_ABC_THREADS := 1 +endif # clang sanitizers SANITIZER = @@ -88,15 +96,15 @@ 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 +# Unit test +UNITESTPATH := $(YOSYS_SRC)/tests/unit + +export 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 := @@ -109,18 +117,16 @@ PLUGIN_LINKFLAGS += -L"$(LIBDIR)" PLUGIN_LIBS := -lyosys_exe endif +ifeq ($(ENABLE_HELP_SOURCE),1) +CXXFLAGS += -DYOSYS_ENABLE_HELP_SOURCE +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 @@ -129,12 +135,8 @@ LINKFLAGS += -rdynamic 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 +CXXFLAGS += -I$(BREW_PREFIX)/readline/include -I$(BREW_PREFIX)/flex/include +LINKFLAGS += -L$(BREW_PREFIX)/readline/lib -L$(BREW_PREFIX)/flex/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) @@ -160,7 +162,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.53+39 +YOSYS_VER := 0.59+134 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) @@ -183,7 +185,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 53c22ab.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 03eb220.. | wc -l`/;" Makefile ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) @@ -282,12 +284,11 @@ ifeq ($(WASI_SDK),) CXX = clang++ AR = llvm-ar RANLIB = llvm-ranlib -WASIFLAGS := -target wasm32-wasi --sysroot $(WASI_SYSROOT) $(WASIFLAGS) +WASIFLAGS := -target wasm32-wasi $(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)) @@ -301,6 +302,7 @@ DISABLE_SPAWN := 1 ifeq ($(ENABLE_ABC),1) LINK_ABC := 1 +ENABLE_THREADS := 0 DISABLE_ABC_THREADS := 1 endif @@ -341,33 +343,35 @@ endif ifeq ($(ENABLE_LIBYOSYS),1) TARGETS += libyosys.so +ifeq ($(ENABLE_LIBYOSYS_STATIC),1) +TARGETS += libyosys.a endif +endif + +PY_WRAPPER_FILE = pyosys/wrappers + +# running make clean on just those and then recompiling saves a lot of +# time when running cibuildwheel +PYTHON_OBJECTS = pyosys/wrappers.o kernel/drivers.o kernel/yosys.o passes/cmds/plugin.o ifeq ($(ENABLE_PYOSYS),1) # python-config --ldflags includes -l and -L, but LINKFLAGS is only -L + +UV_ENV := +ifeq ($(PYOSYS_USE_UV),1) +UV_ENV := uv run --no-project --with 'pybind11>3,<4' --with 'cxxheaderparser' +endif + LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags)) LIBS += $(shell $(PYTHON_CONFIG) --libs) EXE_LIBS += $(filter-out $(LIBS),$(shell $(PYTHON_CONFIG_FOR_EXE) --libs)) -CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON +PYBIND11_INCLUDE ?= $(shell $(UV_ENV) $(PYTHON_EXECUTABLE) -m pybind11 --includes) +CXXFLAGS += -I$(PYBIND11_INCLUDE) -DYOSYS_ENABLE_PYTHON +CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DYOSYS_ENABLE_PYTHON -# 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) $(EXE_LIBS) $(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()") +PY_GEN_SCRIPT = $(YOSYS_SRC)/pyosys/generator.py +PY_WRAP_INCLUDES := $(shell $(UV_ENV) $(PYTHON_EXECUTABLE) $(PY_GEN_SCRIPT) --print-includes) endif # ENABLE_PYOSYS ifeq ($(ENABLE_READLINE),1) @@ -455,6 +459,12 @@ endif ifeq ($(ENABLE_DEBUG),1) CXXFLAGS := -Og -DDEBUG $(filter-out $(OPT_LEVEL),$(CXXFLAGS)) +STRIP := +endif + +ifeq ($(ENABLE_THREADS),1) +CXXFLAGS += -DYOSYS_ENABLE_THREADS +LIBS += -lpthread endif ifeq ($(ENABLE_ABC),1) @@ -468,6 +478,9 @@ else ifeq ($(ABCEXTERNAL),) TARGETS := $(PROGRAM_PREFIX)yosys-abc$(EXE) $(TARGETS) endif +ifeq ($(DISABLE_SPAWN),1) +$(error ENABLE_ABC=1 requires either LINK_ABC=1 or DISABLE_SPAWN=0) +endif endif endif @@ -519,8 +532,12 @@ ifeq ($(ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS),1) VERIFIC_COMPONENTS += extensions CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS else +# YosysHQ flavor of Verific always needs extensions linked +# if disabled it will just not be invoked but parts +# are required for it to initialize properly ifneq ($(wildcard $(VERIFIC_DIR)/extensions),) VERIFIC_COMPONENTS += extensions +OBJS += kernel/log_compat.o endif endif CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC @@ -531,7 +548,6 @@ LIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VE endif endif - ifeq ($(ENABLE_COVER),1) CXXFLAGS += -DYOSYS_ENABLE_COVER endif @@ -551,6 +567,13 @@ $(subst //,/,$(1)/$(notdir $(2))): $(2) $$(Q) cp "$(YOSYS_SRC)"/$(2) $(subst //,/,$(1)/$(notdir $(2))) endef +define add_share_file_and_rename +EXTRA_TARGETS += $(subst //,/,$(1)/$(3)) +$(subst //,/,$(1)/$(3)): $(2) + $$(P) mkdir -p $(1) + $$(Q) cp "$(YOSYS_SRC)"/$(2) $(subst //,/,$(1)/$(3)) +endef + define add_gen_share_file EXTRA_TARGETS += $(subst //,/,$(1)/$(notdir $(2))) $(subst //,/,$(1)/$(notdir $(2))): $(2) @@ -613,6 +636,7 @@ $(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/threading.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)) @@ -626,15 +650,21 @@ 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,passes/techmap/libparse.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/rtlil_bufnorm.o +OBJS += kernel/log_help.o +ifeq ($(ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS),1) +OBJS += kernel/log_compat.o +endif 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 +OBJS += kernel/drivertools.o kernel/functional.o kernel/threading.o ifeq ($(ENABLE_ZLIB),1) OBJS += kernel/fstdata.o endif @@ -744,11 +774,14 @@ $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) 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) + $(P) $(CXX) -o libyosys.so -shared -undefined dynamic_lookup -Wl,-install_name,libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) else - $(P) $(CXX) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) + $(P) $(CXX) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) endif +libyosys.a: $(filter-out kernel/driver.o,$(OBJS)) + $(P) $(AR) rcs $@ $^ + %.o: %.cc $(Q) mkdir -p $(dir $@) $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $< @@ -758,9 +791,9 @@ endif $(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) +$(PY_WRAPPER_FILE).cc: $(PY_GEN_SCRIPT) pyosys/wrappers_tpl.cc $(PY_WRAP_INCLUDES) pyosys/hashlib.h $(Q) mkdir -p $(dir $@) - $(P) $(PYTHON_EXECUTABLE) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")" + $(P) $(UV_ENV) $(PYTHON_EXECUTABLE) $(PY_GEN_SCRIPT) $(PY_WRAPPER_FILE).cc endif %.o: %.cpp @@ -828,7 +861,17 @@ check-git-abc: exit 1; \ fi -abc/abc$(EXE) abc/libabc.a: | check-git-abc +.git-abc-submodule-hash: FORCE + @new=$$(cd abc 2>/dev/null && git rev-parse HEAD 2>/dev/null || echo none); \ + old=$$(cat .git-abc-submodule-hash 2>/dev/null || echo none); \ + if [ "$$new" != "$$old" ]; then \ + echo "$$new" > .git-abc-submodule-hash; \ + fi + +abc/abc$(EXE) abc/libabc.a: .git-abc-submodule-hash | check-git-abc + @if [ "$$(cd abc 2>/dev/null && git rev-parse HEAD 2>/dev/null)" != "$$(cat ../.git-abc-submodule-hash 2>/dev/null || echo none)" ]; then \ + rm -f abc/abc$(EXE); \ + fi $(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) @@ -866,12 +909,15 @@ 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/bugpoint MK_TEST_DIRS += tests/opt MK_TEST_DIRS += tests/sat +MK_TEST_DIRS += tests/sdc MK_TEST_DIRS += tests/sim MK_TEST_DIRS += tests/svtypes MK_TEST_DIRS += tests/techmap MK_TEST_DIRS += tests/various +MK_TEST_DIRS += tests/rtlil ifeq ($(ENABLE_VERIFIC),1) ifneq ($(YOSYS_NOVERIFIC),1) MK_TEST_DIRS += tests/verific @@ -979,32 +1025,41 @@ unit-test: libyosys.so clean-unit-test: @$(MAKE) -C $(UNITESTPATH) clean +install-dev: $(PROGRAM_PREFIX)yosys-config share + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR) + $(INSTALL_SUDO) cp $(PROGRAM_PREFIX)yosys-config $(DESTDIR)$(BINDIR) + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR) + $(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/. + install: $(TARGETS) $(EXTRA_TARGETS) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR) - $(INSTALL_SUDO) cp $(filter-out libyosys.so,$(TARGETS)) $(DESTDIR)$(BINDIR) + $(INSTALL_SUDO) cp $(filter-out libyosys.so libyosys.a,$(TARGETS)) $(DESTDIR)$(BINDIR) ifneq ($(filter $(PROGRAM_PREFIX)yosys,$(TARGETS)),) - $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys + if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys; fi endif ifneq ($(filter $(PROGRAM_PREFIX)yosys-abc,$(TARGETS)),) - $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-abc + if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-abc; fi endif ifneq ($(filter $(PROGRAM_PREFIX)yosys-filterlib,$(TARGETS)),) - $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-filterlib + if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-filterlib; fi 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 + if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so; fi +ifeq ($(ENABLE_LIBYOSYS_STATIC),1) + $(INSTALL_SUDO) cp libyosys.a $(DESTDIR)$(LIBDIR)/ +endif ifeq ($(ENABLE_PYOSYS),1) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys + $(INSTALL_SUDO) cp $(YOSYS_SRC)/pyosys/__init__.py $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/__init__.py $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so $(INSTALL_SUDO) cp -r share $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys ifeq ($(ENABLE_ABC),1) $(INSTALL_SUDO) cp yosys-abc $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/yosys-abc endif - $(INSTALL_SUDO) cp misc/__init__.py $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/ endif endif ifeq ($(ENABLE_PLUGINS),1) @@ -1019,6 +1074,9 @@ uninstall: $(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR) ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so +ifeq ($(ENABLE_LIBYOSYS_STATIC),1) + $(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.a +endif ifeq ($(ENABLE_PYOSYS),1) $(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so $(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/__init__.py @@ -1026,19 +1084,8 @@ ifeq ($(ENABLE_PYOSYS),1) 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/cmds.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) + $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cmds-json $@' docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cells-json $@' @@ -1055,6 +1102,15 @@ docs/source/generated/functional/rosette.diff: backends/functional/smtlib.cc bac PHONY: docs/gen/functional_ir docs/gen/functional_ir: docs/source/generated/functional/smtlib.cc docs/source/generated/functional/rosette.diff +docs/source/generated/%.log: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) + $(Q) ./$(PROGRAM_PREFIX)yosys -qQT -h '$*' -l $@ + +docs/source/generated/chformal.cc: passes/cmds/chformal.cc docs/source/generated + $(Q) cp $< $@ + +PHONY: docs/gen/chformal +docs/gen/chformal: docs/source/generated/chformal.log docs/source/generated/chformal.cc + PHONY: docs/gen docs/usage docs/reqs docs/gen: $(TARGETS) $(Q) $(MAKE) -C docs gen @@ -1090,18 +1146,16 @@ 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 +docs/prep: docs/source/generated/cells.json docs/source/generated/cmds.json docs/gen docs/usage docs/gen/functional_ir docs/gen/chformal DOC_TARGET ?= html docs: docs/prep $(Q) $(MAKE) -C docs $(DOC_TARGET) -clean: +clean: clean-py clean-unit-test 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 $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS) 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 @@ -1113,14 +1167,20 @@ clean: 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 + -$(MAKE) -C $(YOSYS_SRC)/docs clean + rm -rf docs/util/__pycache__ rm -f libyosys.so +clean-py: + rm -f $(PY_WRAPPER_FILE).inc.cc $(PY_WRAPPER_FILE).cc + rm -f $(PYTHON_OBJECTS) + rm -f *.whl + rm -f libyosys.so libyosys.a + rm -rf kernel/*.pyh + 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 + $(MAKE) -C $(YOSYS_SRC)/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 .git-abc-submodule-hash mrproper: clean git clean -xdf @@ -1221,5 +1281,5 @@ echo-cxx: FORCE: -.PHONY: all top-all abc test install install-abc docs clean mrproper qtcreator coverage vcxsrc +.PHONY: all top-all abc test install-dev install install-abc docs clean mrproper qtcreator coverage vcxsrc .PHONY: config-clean config-clang config-gcc config-gcc-static config-gprof config-sudo diff --git a/README.md b/README.md index 71d47d76c..427d59c9e 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,9 @@ Web Site and Other Resources More information and documentation can be found on the Yosys web site: - https://yosyshq.net/yosys/ +If you have any Yosys-related questions, please post them on the Discourse group: +- https://yosyshq.discourse.group + Documentation from this repository is automatically built and available on Read the Docs: - https://yosyshq.readthedocs.io/projects/yosys @@ -34,6 +37,9 @@ verification front-end for Yosys, SBY: - https://yosyshq.readthedocs.io/projects/sby/ - https://github.com/YosysHQ/sby +The Yosys blog has news and articles from users: +- https://blog.yosyshq.com + Installation ============ @@ -74,13 +80,13 @@ 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 +For example on Ubuntu Linux 22.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 + $ sudo apt-get install gawk git make python3 lld bison clang flex \ + libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev \ + graphviz xdot + $ curl -LsSf https://astral.sh/uv/install.sh | sh The environment variable `CXX` can be used to control the C++ compiler used, or run one of the following to override it: @@ -242,7 +248,7 @@ 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: +following are used for building the website: $ sudo apt install pdf2svg faketime @@ -258,7 +264,7 @@ build process for the website. Or, run the following: Or for MacOS, using homebrew: $ brew install basictex - $ sudo tlmgr update --self + $ sudo tlmgr update --self $ sudo tlmgr install collection-latexextra latexmk tex-gyre The Python package, Sphinx, is needed along with those listed in @@ -268,5 +274,13 @@ The Python package, Sphinx, is needed along with those listed in 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 +commands. To build for pdf instead of html, call `make docs DOC_TARGET=latexpdf`. + +It is recommended to use the `ENABLE_HELP_SOURCE` make option for Yosys builds +that will be used to build the documentation. This option enables source +location tracking for passes and improves the command reference through grouping +related commands and allowing for the documentation to link to the corresponding +source files. Without this, a warning will be raised during the Sphinx build +about `Found commands assigned to group unknown` and `make docs` is configured +to fail on warnings by default. diff --git a/abc b/abc index e55d316cc..1c5ed1ce3 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit e55d316cc9a7f72a84a76eda555aa6ec083c9d0d +Subproject commit 1c5ed1ce378cc04beac30bb31abc4c37c8467042 diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc index 617d7d85f..95f4c19e2 100644 --- a/backends/aiger/aiger.cc +++ b/backends/aiger/aiger.cc @@ -132,7 +132,7 @@ struct AigerWriter return a; } - AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode, bool lmode) : module(module), zinit_mode(zinit_mode), sigmap(module) + AigerWriter(Module *module, bool no_sort, 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; @@ -152,6 +152,37 @@ struct AigerWriter if (wire->port_input) sigmap.add(wire); + // handle ports + // provided the input_bits and output_bits don't get sorted they + // will be returned in reverse order, so add them in reverse to + // match + for (auto riter = module->ports.rbegin(); riter != module->ports.rend(); ++riter) { + auto *wire = module->wire(*riter); + 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; + } + + if (wire->port_input) + input_bits.insert(bit); + + if (wire->port_output) { + if (bit != wirebit) + alias_map[wirebit] = bit; + output_bits.insert(wirebit); + } + } + } + + // handle wires for (auto wire : module->wires()) { if (wire->attributes.count(ID::init)) { @@ -167,25 +198,13 @@ struct AigerWriter 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); - } + if (bit.wire == nullptr) + continue; + if (wire->port_input || wire->port_output) 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) { @@ -200,12 +219,6 @@ struct AigerWriter } } - 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_)) @@ -343,8 +356,11 @@ struct AigerWriter } init_map.sort(); - input_bits.sort(); - output_bits.sort(); + // we are relying here on unsorted pools iterating last-in-first-out + if (!no_sort) { + input_bits.sort(); + output_bits.sort(); + } not_map.sort(); ff_map.sort(); and_map.sort(); @@ -697,7 +713,7 @@ struct AigerWriter } if (wire->port_output) { - int o = ordered_outputs.at(sig[i]); + int o = ordered_outputs.at(SigSpec(wire, i)); output_lines[o] += stringf("output %d %d %s\n", o, index, log_id(wire)); } @@ -901,6 +917,9 @@ struct AigerBackend : public Backend { log(" -symbols\n"); log(" include a symbol table in the generated AIGER file\n"); log("\n"); + log(" -no-sort\n"); + log(" don't sort input/output ports\n"); + log("\n"); log(" -map \n"); log(" write an extra file with port and latch symbols\n"); log("\n"); @@ -925,6 +944,7 @@ struct AigerBackend : public Backend { bool zinit_mode = false; bool miter_mode = false; bool symbols_mode = false; + bool no_sort = false; bool verbose_map = false; bool imode = false; bool omode = false; @@ -955,6 +975,10 @@ struct AigerBackend : public Backend { symbols_mode = true; continue; } + if (args[argidx] == "-no-sort") { + no_sort = true; + continue; + } if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) { map_filename = args[++argidx]; continue; @@ -1008,7 +1032,7 @@ struct AigerBackend : public Backend { 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); + AigerWriter writer(top_module, no_sort, zinit_mode, imode, omode, bmode, lmode); writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode); if (!map_filename.empty()) { @@ -1016,7 +1040,7 @@ struct AigerBackend : public Backend { 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)); + log_error("Can't open file `%s' for writing: %s\n", map_filename, strerror(errno)); writer.write_map(mapf, verbose_map, no_startoffset); } @@ -1027,7 +1051,7 @@ struct AigerBackend : public Backend { 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)); + log_error("Can't open file `%s' for writing: %s\n", yw_map_filename, strerror(errno)); writer.write_ywmap(json); } } diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index 97dec40e4..988bc558b 100644 --- a/backends/aiger/xaiger.cc +++ b/backends/aiger/xaiger.cc @@ -788,7 +788,7 @@ struct XAigerBackend : public Backend { 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)); + log_error("Can't open file `%s' for writing: %s\n", map_filename, strerror(errno)); writer.write_map(mapf); } } diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index c7ed3b81f..41e1b91c1 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -91,7 +91,7 @@ struct Index { 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))) + if (cell->type.in(KNOWN_OPS) || cell->type.in(ID($scopeinfo), ID($specify2), ID($specify3), ID($input_port))) continue; Module *submodule = m->design->module(cell->type); @@ -105,6 +105,13 @@ struct Index { if (allow_blackboxes) { info.found_blackboxes.insert(cell); } else { + // Even if we don't allow blackboxes these might still be + // present outside of any traversed input cones, so we + // can't bail at this point. If they are hit by a traversal + // (which can only really happen with $tribuf not + // $connect), we can still detect this as an error later. + if (cell->type == ID($connect) || (cell->type == ID($tribuf) && cell->has_attribute(ID(aiger2_zbuf)))) + continue; 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)); @@ -483,7 +490,8 @@ struct Index { { Design *design = index.design; auto &minfo = leaf_minfo(index); - log_assert(minfo.suboffsets.count(cell)); + if (!minfo.suboffsets.count(cell)) + log_error("Reached unsupport cell %s (%s in %s)\n", log_id(cell->type), log_id(cell), log_id(cell->module)); Module *def = design->module(cell->type); log_assert(def); levels.push_back(Level(index.modules.at(def), cell)); @@ -566,7 +574,7 @@ struct Index { } Lit ret; - if (!bit.wire->port_input) { + if (!bit.wire->port_input || bit.wire->port_output) { // an output of a cell Cell *driver = bit.wire->driverCell(); @@ -618,7 +626,7 @@ struct Index { if (!cursor) { log_assert(bit.wire->module == top); - log_assert(bit.wire->port_input); + log_assert(bit.wire->port_input && !bit.wire->port_output); return lits[top_minfo->windices[bit.wire] + bit.offset]; } else { log_assert(bit.wire->module == cursor->leaf_module(*this)); @@ -723,7 +731,7 @@ struct AigerWriter : Index { for (auto id : top->ports) { Wire *w = top->wire(id); log_assert(w); - if (w->port_input) + if (w->port_input && !w->port_output) for (int i = 0; i < w->width; i++) { pi_literal(SigBit(w, i)) = lit_counter; inputs.push_back(SigBit(w, i)); @@ -828,7 +836,7 @@ struct XAigerAnalysis : Index { { log_assert(cursor.is_top()); // TOOD: fix analyzer to work with hierarchy - if (bit.wire->port_input) + if (bit.wire->port_input && !bit.wire->port_output) return false; Cell *driver = bit.wire->driverCell(); @@ -838,7 +846,7 @@ struct XAigerAnalysis : Index { int max = 1; for (auto wire : mod->wires()) - if (wire->port_input) + if (wire->port_input && !wire->port_output) for (int i = 0; i < wire->width; i++) { int ilevel = visit(cursor, driver->getPort(wire->name)[i]); max = std::max(max, ilevel + 1); @@ -858,7 +866,7 @@ struct XAigerAnalysis : Index { for (auto id : top->ports) { Wire *w = top->wire(id); log_assert(w); - if (w->port_input) + if (w->port_input && !w->port_output) for (int i = 0; i < w->width; i++) pi_literal(SigBit(w, i)) = 0; } @@ -868,7 +876,7 @@ struct XAigerAnalysis : Index { 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)) + if (box->port_dir(conn.first) != RTLIL::PD_INPUT) for (auto bit : conn.second) pi_literal(bit, &cursor) = 0; } @@ -883,7 +891,7 @@ struct XAigerAnalysis : Index { 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)) + if (box->port_dir(conn.first) == RTLIL::PD_INPUT) for (auto bit : conn.second) (void) eval_po(bit); } @@ -903,6 +911,16 @@ struct XAigerWriter : AigerWriter { typedef std::pair HierBit; std::vector pos; std::vector pis; + + // * The aiger output port sequence is COs (inputs to modeled boxes), + // inputs to opaque boxes, then module outputs. COs going first is + // required by abc. + // * proper_pos_counter counts ports which follow after COs + // * The mapping file `pseudopo` and `po` statements use indexing relative + // to the first port following COs. + // * If a module output is directly driven by an opaque box, the emission + // of the po statement in the mapping file is skipped. This is done to + // aid re-integration of the mapped result. int proper_pos_counter = 0; pool driven_by_opaque_box; @@ -937,15 +955,10 @@ struct XAigerWriter : AigerWriter { lit_counter += 2; } - void append_box_ports(Cell *box, HierCursor &cursor, bool inputs) + void append_opaque_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)); + bool is_input = box->port_dir(conn.first) == RTLIL::PD_INPUT; if (is_input && inputs) { int bitp = 0; @@ -955,13 +968,14 @@ struct XAigerWriter : AigerWriter { continue; } + // Inputs to opaque boxes are proper POs as far as abc is concerned if (map_file.is_open()) { log_assert(cursor.is_top()); - map_file << "pseudopo " << proper_pos_counter++ << " " << bitp + map_file << "pseudopo " << proper_pos_counter << " " << bitp << " " << box->name.c_str() << " " << conn.first.c_str() << "\n"; } - + proper_pos_counter++; pos.push_back(std::make_pair(bit, cursor)); if (mapping_prep) @@ -969,10 +983,10 @@ struct XAigerWriter : AigerWriter { bitp++; } - } else if (is_output && !inputs) { + } else if (!is_input && !inputs) { for (auto &bit : conn.second) { - if (!bit.wire || bit.wire->port_input) - log_error("Bad connection"); + if (!bit.wire || (bit.wire->port_input && !bit.wire->port_output)) + log_error("Bad connection %s/%s ~ %s\n", log_id(box), log_id(conn.first), log_signal(conn.second)); ensure_pi(bit, cursor); @@ -1011,8 +1025,8 @@ struct XAigerWriter : AigerWriter { 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_debug(" - %s.%s (type %s): ", cursor.path(), + RTLIL::unescape_id(box->name), log_id(box->type)); Module *box_module = design->module(box->type), *box_derived; @@ -1038,7 +1052,7 @@ struct XAigerWriter : AigerWriter { }); for (auto [cursor, box, def] : opaque_boxes) - append_box_ports(box, cursor, false); + append_opaque_box_ports(box, cursor, false); holes_module = design->addModule(NEW_ID); std::vector holes_pis; @@ -1086,6 +1100,8 @@ struct XAigerWriter : AigerWriter { bit = RTLIL::Sx; } + // Nonopaque box inputs come first and are not part of + // the PO numbering used by the mapping file. pos.push_back(std::make_pair(bit, cursor)); } boxes_co_num += port->width; @@ -1106,7 +1122,7 @@ struct XAigerWriter : AigerWriter { holes_pi_idx++; } holes_wb->setPort(port_id, in_conn); - } else if (port->port_output && !port->port_input) { + } else if (port->port_output) { // primary for (int i = 0; i < port->width; i++) { SigBit bit; @@ -1138,7 +1154,7 @@ struct XAigerWriter : AigerWriter { } for (auto [cursor, box, def] : opaque_boxes) - append_box_ports(box, cursor, true); + append_opaque_box_ports(box, cursor, true); write_be32(h_buffer, 1); write_be32(h_buffer, pis.size()); @@ -1159,7 +1175,7 @@ struct XAigerWriter : AigerWriter { log_assert(port); if (port->port_input && !port->port_output) { box_co_num += port->width; - } else if (port->port_output && !port->port_input) { + } else if (port->port_output) { box_ci_num += port->width; } else { log_abort(); @@ -1182,7 +1198,7 @@ struct XAigerWriter : AigerWriter { reset_counters(); for (auto w : top->wires()) - if (w->port_input) + if (w->port_input && !w->port_output) for (int i = 0; i < w->width; i++) ensure_pi(SigBit(w, i)); @@ -1195,10 +1211,14 @@ struct XAigerWriter : AigerWriter { for (auto w : top->wires()) if (w->port_output) for (int i = 0; i < w->width; i++) { + // When a module output is directly driven by an opaque box, we + // don't emit it to the mapping file to aid re-integration, but we + // do emit a proper PO. if (map_file.is_open() && !driven_by_opaque_box.count(SigBit(w, i))) { - map_file << "po " << proper_pos_counter++ << " " << i + map_file << "po " << proper_pos_counter << " " << i << " " << w->name.c_str() << "\n"; } + proper_pos_counter++; pos.push_back(std::make_pair(SigBit(w, i), HierCursor{})); } @@ -1446,7 +1466,7 @@ struct XAiger2Backend : Backend { 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()); + log_cmd_error("Failed to open '%s' for writing\n", map_filename); } design->bufNormalize(true); diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index 5a5b9219f..ab7861802 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -157,14 +157,14 @@ struct BlifDumper f << stringf("%c", ch); f << stringf("\"\n"); } else - f << stringf("%s\n", param.second.as_string().c_str()); + f << stringf("%s\n", param.second.as_string()); } } void dump() { f << stringf("\n"); - f << stringf(".model %s\n", str(module->name).c_str()); + f << stringf(".model %s\n", str(module->name)); std::map inputs, outputs; @@ -179,7 +179,7 @@ struct BlifDumper 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(" %s", str(RTLIL::SigSpec(wire, i))); } f << stringf("\n"); @@ -187,7 +187,7 @@ struct BlifDumper 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(" %s", str(RTLIL::SigSpec(wire, i))); } f << stringf("\n"); @@ -200,7 +200,7 @@ struct BlifDumper if (!config->impltf_mode) { if (!config->false_type.empty()) { if (config->false_type == "+") - f << stringf(".names %s\n", config->false_out.c_str()); + f << stringf(".names %s\n", config->false_out); 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()); @@ -208,7 +208,7 @@ struct BlifDumper 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()); + f << stringf(".names %s\n1\n", config->true_out); 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()); @@ -216,7 +216,7 @@ struct BlifDumper 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()); + f << stringf(".names %s\n", config->undef_out); 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()); @@ -331,31 +331,31 @@ struct BlifDumper } 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(), + f << stringf(".latch %s %s%s\n", str(cell->getPort(ID::D)), str(cell->getPort(ID::Q)), 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(), + f << stringf(".latch %s %s fe %s%s\n", str(cell->getPort(ID::D)), str(cell->getPort(ID::Q)), 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(), + f << stringf(".latch %s %s re %s%s\n", str(cell->getPort(ID::D)), str(cell->getPort(ID::Q)), 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(), + f << stringf(".latch %s %s al %s%s\n", str(cell->getPort(ID::D)), str(cell->getPort(ID::Q)), 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(), + f << stringf(".latch %s %s ah %s%s\n", str(cell->getPort(ID::D)), str(cell->getPort(ID::Q)), str(cell->getPort(ID::E)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } @@ -366,10 +366,10 @@ struct BlifDumper 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()); + f << stringf(" %s", str(inputs.extract(i, 1))); auto &output = cell->getPort(ID::Y); log_assert(output.size() == 1); - f << stringf(" %s", str(output).c_str()); + f << stringf(" %s", str(output)); f << stringf("\n"); RTLIL::SigSpec mask = cell->parameters.at(ID::LUT); for (int i = 0; i < (1 << width); i++) @@ -392,10 +392,10 @@ struct BlifDumper 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()); + f << stringf(" %s", str(inputs.extract(i, 1))); auto &output = cell->getPort(ID::Y); log_assert(output.size() == 1); - f << stringf(" %s", str(output).c_str()); + f << stringf(" %s", str(output)); f << stringf("\n"); for (int i = 0; i < depth; i++) { for (int j = 0; j < width; j++) { @@ -410,11 +410,11 @@ struct BlifDumper goto internal_cell; } - f << stringf(".%s %s", subckt_or_gate(cell->type.str()), str(cell->type).c_str()); + f << stringf(".%s %s", subckt_or_gate(cell->type.str()), str(cell->type)); 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()); + f << stringf(" %s=%s", str(conn.first), str(conn.second[0])); continue; } @@ -423,11 +423,11 @@ struct BlifDumper 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()); + f << stringf(" %s[%d]=%s", str(conn.first), i, str(conn.second[i])); } 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 ? + f << stringf(" %s[%d]=%s", str(conn.first), 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()); } @@ -436,7 +436,7 @@ struct BlifDumper f << stringf("\n"); if (config->cname_mode) - f << stringf(".cname %s\n", str(cell->name).c_str()); + f << stringf(".cname %s\n", str(cell->name)); if (config->attr_mode) dump_params(".attr", cell->attributes); if (config->param_mode) @@ -445,7 +445,7 @@ struct BlifDumper if (0) { internal_cell: if (config->iname_mode) - f << stringf(".cname %s\n", str(cell->name).c_str()); + f << stringf(".cname %s\n", str(cell->name)); if (config->iattr_mode) dump_params(".attr", cell->attributes); } @@ -461,12 +461,12 @@ struct BlifDumper continue; if (config->conn_mode) - f << stringf(".conn %s %s\n", str(rhs_bit).c_str(), str(lhs_bit).c_str()); + f << stringf(".conn %s %s\n", str(rhs_bit), str(lhs_bit)); 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(), + f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type, 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(".names %s %s\n1 1\n", str(rhs_bit), str(lhs_bit)); } f << stringf(".end\n"); @@ -674,7 +674,7 @@ struct BlifBackend : public Backend { } if (!top_module_name.empty()) - log_error("Can't find top module `%s'!\n", top_module_name.c_str()); + log_error("Can't find top module `%s'!\n", top_module_name); for (auto module : mod_list) BlifDumper::dump(*f, module, design, config); diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index bfd293557..ca7cf8a7f 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -98,24 +98,22 @@ struct BtorWorker vector ywmap_states; dict ywmap_clock_bits; dict ywmap_clock_inputs; + vector ywmap_asserts; + vector ywmap_assumes; PrettyJson ywmap_json; - void btorf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3)) + template + void btorf(FmtString...> fmt, const Args &... args) { - va_list ap; - va_start(ap, fmt); - f << indent << vstringf(fmt, ap); - va_end(ap); + f << indent << fmt.format(args...); } - void infof(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3)) + template + void infof(FmtString...> fmt, const Args &... args) { - va_list ap; - va_start(ap, fmt); - info_lines.push_back(vstringf(fmt, ap)); - va_end(ap); + info_lines.push_back(fmt.format(args...)); } template @@ -129,7 +127,7 @@ struct BtorWorker 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); + string s = stringf("%s-%d", src, i); if (!srcsymbols.count(s) && !module->count_id("\\" + s)) { src = s; break; @@ -192,7 +190,7 @@ struct BtorWorker void btorf_push(const string &id) { if (verbose) { - f << indent << stringf(" ; begin %s\n", id.c_str()); + f << indent << stringf(" ; begin %s\n", id); indent += " "; } } @@ -201,7 +199,7 @@ struct BtorWorker { if (verbose) { indent = indent.substr(4); - f << indent << stringf(" ; end %s\n", id.c_str()); + f << indent << stringf(" ; end %s\n", id); } } @@ -246,7 +244,7 @@ struct BtorWorker 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()); + log_error("Found topological loop while processing cell %s. Active cells:%s\n", log_id(cell), cell_list); } cell_recursion_guard.insert(cell); @@ -322,12 +320,12 @@ struct BtorWorker 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()); + btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_b_ltz, nid_l, nid_r, getinfo(cell)); } 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()); + btorf("%d %s %d %d %d%s\n", nid, btor_op, sid, nid_a, nid_b, getinfo(cell)); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -368,7 +366,7 @@ struct BtorWorker 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()); + btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op, sid, nid_a, nid_b, getinfo(cell)); SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -394,12 +392,12 @@ struct BtorWorker 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()); + btorf("%d and %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell)); } 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()); + btorf("%d or %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell)); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -421,13 +419,13 @@ struct BtorWorker 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()); + btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell)); } 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()); + btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell)); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -452,14 +450,14 @@ struct BtorWorker 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()); + btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell)); } 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()); + btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell)); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -491,9 +489,9 @@ struct BtorWorker 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()); + btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op, sid, nid_a, nid_b, getinfo(cell)); } else { - btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); + btorf("%d %s %d %d %d%s\n", nid, btor_op, sid, nid_a, nid_b, getinfo(cell)); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -509,7 +507,7 @@ struct BtorWorker goto okay; } - if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos))) + if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos), ID($buf), ID($_BUF_))) { string btor_op; if (cell->type.in(ID($not), ID($_NOT_))) btor_op = "not"; @@ -521,14 +519,14 @@ struct BtorWorker 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 + // the $pos/$buf cells just pass through, all other cells need an actual operation applied int nid = nid_a; - if (cell->type != ID($pos)) + if (!cell->type.in(ID($pos), ID($buf), ID($_BUF_))) { 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()); + btorf("%d %s %d %d%s\n", nid, btor_op, sid, nid_a, getinfo(cell)); } if (GetSize(sig) < width) { @@ -568,9 +566,9 @@ struct BtorWorker 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()); + btorf("%d %s %d %d %d%s\n", nid, btor_op, sid, nid_a, nid_b, getinfo(cell)); else - btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); + btorf("%d %s %d %d%s\n", nid, btor_op, sid, nid_a, getinfo(cell)); SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -601,11 +599,11 @@ struct BtorWorker 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 %s %d %d%s\n", nid, btor_op, sid, nid_a, getinfo(cell)); 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()); + btorf("%d %s %d %d%s\n", nid, btor_op, sid, nid_a, getinfo(cell)); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -640,9 +638,9 @@ struct BtorWorker 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()); + btorf("%d not %d %d%s\n", nid, sid, tmp, getinfo(cell)); } else { - btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_s, nid_b, nid_a, getinfo(cell).c_str()); + btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_s, nid_b, nid_a, getinfo(cell)); } add_nid_sig(nid, sig_y); @@ -665,7 +663,7 @@ struct BtorWorker 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()); + btorf("%d ite %d %d %d %d%s\n", nid2, sid, nid_s, nid_b, nid, getinfo(cell)); else btorf("%d ite %d %d %d %d\n", nid2, sid, nid_s, nid_b, nid); nid = nid2; @@ -709,12 +707,13 @@ struct BtorWorker } } - Const initval; + Const::Builder initval_bits(GetSize(sig_q)); 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); + initval_bits.push_back(initbits.at(sig_q[i]) ? State::S1 : State::S0); else - initval.bits().push_back(State::Sx); + initval_bits.push_back(State::Sx); + Const initval = initval_bits.build(); int nid_init_val = -1; @@ -753,7 +752,7 @@ struct BtorWorker int sid = get_bv_sid(GetSize(sig_y)); int nid = next_nid++; - btorf("%d state %d%s\n", nid, sid, getinfo(cell).c_str()); + btorf("%d state %d%s\n", nid, sid, getinfo(cell)); ywmap_state(sig_y); @@ -776,7 +775,7 @@ struct BtorWorker int one_nid = get_sig_nid(State::S1); int zero_nid = get_sig_nid(State::S0); initstate_nid = next_nid++; - btorf("%d state %d%s\n", initstate_nid, sid, getinfo(cell).c_str()); + btorf("%d state %d%s\n", initstate_nid, sid, getinfo(cell)); 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); @@ -1043,15 +1042,16 @@ struct BtorWorker { 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); + Const::Builder c_bits; + c_bits.push_back(bit.data); + while (i + GetSize(c_bits) < GetSize(sig) && sig[i + GetSize(c_bits)].wire == nullptr) + c_bits.push_back(sig[i + GetSize(c_bits)].data); + Const c = c_bits.build(); 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()); + btorf("%d const %d %s\n", nid, sid, c.as_string()); consts[c] = nid; nid_width[nid] = GetSize(c); } @@ -1215,7 +1215,7 @@ struct BtorWorker int sid = get_bv_sid(GetSize(sig)); int nid = next_nid++; - btorf("%d input %d%s\n", nid, sid, getinfo(wire).c_str()); + btorf("%d input %d%s\n", nid, sid, getinfo(wire)); ywmap_input(wire); add_nid_sig(nid, sig); @@ -1260,7 +1260,7 @@ struct BtorWorker 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("%d output %d%s\n", next_nid++, nid, getinfo(wire)); btorf_pop(stringf("output %s", log_id(wire))); } @@ -1282,6 +1282,8 @@ struct BtorWorker 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); + if (ywmap_json.active()) ywmap_assumes.emplace_back(cell); + btorf_pop(log_id(cell)); } @@ -1302,10 +1304,12 @@ struct BtorWorker 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()); + infof("bad %d%s\n", nid_en_and_not_a, getinfo(cell, true)); } else { int nid = next_nid++; - btorf("%d bad %d%s\n", nid, nid_en_and_not_a, getinfo(cell, true).c_str()); + btorf("%d bad %d%s\n", nid, nid_en_and_not_a, getinfo(cell, true)); + + if (ywmap_json.active()) ywmap_asserts.emplace_back(cell); } } @@ -1327,7 +1331,7 @@ struct BtorWorker 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("%d bad %d%s\n", nid, nid_en_and_a, getinfo(cell, true)); } btorf_pop(log_id(cell)); @@ -1348,7 +1352,7 @@ struct BtorWorker continue; int this_nid = next_nid++; - btorf("%d uext %d %d %d%s\n", this_nid, sid, nid, 0, getinfo(wire).c_str()); + btorf("%d uext %d %d %d%s\n", this_nid, sid, nid, 0, getinfo(wire)); if (info_clocks.count(nid)) info_clocks[this_nid] |= info_clocks[nid]; @@ -1371,7 +1375,7 @@ struct BtorWorker 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("%d next %d %d %d%s\n", next_nid++, sid, nid, nid_q, getinfo(cell)); btorf_pop(stringf("next %s", log_id(cell))); } @@ -1430,7 +1434,7 @@ struct BtorWorker } 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("%d next %d %d %d%s\n", nid2, sid, nid, nid_head, (mem->cell ? getinfo(mem->cell) : getinfo(mem->mem))); btorf_pop(stringf("next %s", log_id(mem->memid))); } @@ -1463,6 +1467,7 @@ struct BtorWorker log_assert(cursor == 0); log_assert(GetSize(todo) == 1); btorf("%d bad %d\n", nid, todo[cursor]); + // What do we do with ywmap_asserts when using single_bad? } } @@ -1489,7 +1494,7 @@ struct BtorWorker 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)); + log_error("Can't open file `%s' for writing: %s\n", info_filename, strerror(errno)); for (auto &it : info_lines) f << it; f.close(); @@ -1528,6 +1533,18 @@ struct BtorWorker emit_ywmap_btor_sig(entry); ywmap_json.end_array(); + ywmap_json.name("asserts"); + ywmap_json.begin_array(); + for (Cell *cell : ywmap_asserts) + ywmap_json.value(witness_path(cell)); + ywmap_json.end_array(); + + ywmap_json.name("assumes"); + ywmap_json.begin_array(); + for (Cell *cell : ywmap_assumes) + ywmap_json.value(witness_path(cell)); + ywmap_json.end_array(); + ywmap_json.end_object(); } } diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 819b2c1df..d575b5879 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -637,20 +637,6 @@ std::string escape_cxx_string(const std::string &input) return output; } -std::string basename(const std::string &filepath) -{ -#ifdef _WIN32 - const std::string dir_seps = "\\/"; -#else - const std::string dir_seps = "/"; -#endif - size_t sep_pos = filepath.find_last_of(dir_seps); - if (sep_pos != std::string::npos) - return filepath.substr(sep_pos + 1); - else - return filepath; -} - template std::string get_hdl_name(T *object) { @@ -1533,7 +1519,7 @@ struct CxxrtlWorker { } // Internal cells } else if (is_internal_cell(cell->type)) { - log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str()); + log_cmd_error("Unsupported internal cell `%s'.\n", cell->type); // User cells } else if (for_debug) { // Outlines are called on demand when computing the value of a debug item. Nothing to do here. @@ -1668,26 +1654,29 @@ struct CxxrtlWorker { f << signal_temp << " == "; dump_sigspec(compare, /*is_lhs=*/false, for_debug); } else if (compare.is_fully_const()) { - RTLIL::Const compare_mask, compare_value; + RTLIL::Const::Builder compare_mask_builder(compare.size()); + RTLIL::Const::Builder compare_value_builder(compare.size()); 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); + compare_mask_builder.push_back(RTLIL::S1); + compare_value_builder.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); + compare_mask_builder.push_back(RTLIL::S0); + compare_value_builder.push_back(RTLIL::S0); break; default: log_assert(false); } } + RTLIL::Const compare_mask = compare_mask_builder.build(); + RTLIL::Const compare_value = compare_value_builder.build(); f << "and_uu<" << compare.size() << ">(" << signal_temp << ", "; dump_const(compare_mask); f << ") == "; @@ -2429,8 +2418,6 @@ struct CxxrtlWorker { 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: @@ -2438,6 +2425,9 @@ struct CxxrtlWorker { // Member wire std::vector flags; + if (!wire->name.isPublic()) + flags.push_back("GENERATED"); + if (wire->port_input && wire->port_output) flags.push_back("INOUT"); else if (wire->port_output) @@ -2854,7 +2844,7 @@ struct CxxrtlWorker { } if (split_intf) - f << "#include \"" << basename(intf_filename) << "\"\n"; + f << "#include \"" << name_from_file_path(intf_filename) << "\"\n"; else f << "#include \n"; f << "\n"; @@ -3041,7 +3031,7 @@ struct CxxrtlWorker { if (init == RTLIL::Const()) { init = RTLIL::Const(State::Sx, GetSize(bit.wire)); } - init.bits()[bit.offset] = port.init_value[i]; + init.set(bit.offset, port.init_value[i]); } } } @@ -3477,8 +3467,8 @@ struct CxxrtlWorker { }; struct CxxrtlBackend : public Backend { - static const int DEFAULT_OPT_LEVEL = 6; - static const int DEFAULT_DEBUG_LEVEL = 4; + static constexpr int DEFAULT_OPT_LEVEL = 6; + static constexpr int DEFAULT_DEBUG_LEVEL = 4; CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { } void help() override @@ -3799,7 +3789,7 @@ struct CxxrtlBackend : public Backend { 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()); + log_cmd_error("Invalid output stream \"%s\".\n", worker.print_output); worker.print_output = "std::cout"; } continue; diff --git a/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h index ae42733ad..62ca38943 100644 --- a/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h +++ b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h @@ -200,6 +200,10 @@ enum cxxrtl_flag { // node, such as inputs and dangling wires. CXXRTL_UNDRIVEN = 1 << 4, + // Generated correspond to netlist nodes that correspond to state with an internal name, that + // need to be saved, but wouldn't otherwise have a debug item generated. + CXXRTL_GENERATED = 1 << 5, + // More object flags may be added in the future, but the existing ones will never change. }; diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 9b4f5774f..fbbe2373f 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1294,6 +1294,7 @@ struct debug_item : ::cxxrtl_object { DRIVEN_SYNC = CXXRTL_DRIVEN_SYNC, DRIVEN_COMB = CXXRTL_DRIVEN_COMB, UNDRIVEN = CXXRTL_UNDRIVEN, + GENERATED = CXXRTL_GENERATED, }; debug_item(const ::cxxrtl_object &object) : cxxrtl_object(object) {} diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index 581590287..61d6ee254 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -30,9 +30,9 @@ 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() +#define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true) +#define EDIF_DEFR(_id, _ren, _bl, _br) edif_names(RTLIL::unescape_id(_id), true, _ren, _bl, _br) +#define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false) struct EdifNames { @@ -48,8 +48,8 @@ struct EdifNames 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; + return stringf("(rename %s \"%s%c%d:%d%c\")", new_id, id, delim_left, range_left, range_right, delim_right); + return new_id != id ? stringf("(rename %s \"%s\")", new_id, id) : id; } if (name_map.count(id) > 0) @@ -334,7 +334,7 @@ struct EdifBackend : public Backend { 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()); + *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string()); 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 { @@ -348,7 +348,7 @@ struct EdifBackend : public Backend { 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()); + *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val), hex_string); } }; for (auto module : sorted_modules) @@ -513,13 +513,13 @@ struct EdifBackend : public Backend { 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()); + log_warning("Exporting x-bit on %s as zero bit.\n", ref.first); 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_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first); log_abort(); } } @@ -536,7 +536,7 @@ struct EdifBackend : public Backend { } *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); for (auto &ref : it.second) - *f << stringf(" %s\n", ref.first.c_str()); + *f << stringf(" %s\n", ref.first); if (sig.wire == NULL) { if (nogndvcc) log_error("Design contains constant nodes (map with \"hilomap\" first).\n"); @@ -577,7 +577,7 @@ struct EdifBackend : public Backend { 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(" %s\n", ref.first); *f << stringf(" )"); if (attr_properties && raw_sig.wire != NULL) diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index ceb805dcb..577d95ad7 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -253,7 +253,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream const std::string extmoduleFileinfo = getFileinfo(cell); // Emit extmodule header. - f << stringf(" extmodule %s: %s\n", exported_name.c_str(), extmoduleFileinfo.c_str()); + f << stringf(" extmodule %s: %s\n", exported_name, extmoduleFileinfo); // Emit extmodule ports. for (auto wire : mod_instance->wires()) @@ -280,7 +280,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream // 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()); + f << stringf("%sdefname = %s\n", indent, blackbox_name); // Emit extmodule generic parameters. for (const auto &p : cell->parameters) @@ -301,7 +301,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream param_name.end() ); - f << stringf("%sparameter %s = %s\n", indent.c_str(), param_name.c_str(), param_value.c_str()); + f << stringf("%sparameter %s = %s\n", indent, param_name, param_value); } f << "\n"; @@ -347,7 +347,7 @@ void emit_elaborated_extmodules(RTLIL::Design *design, std::ostream &f) 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()); + log_error("Unknown cell type %s\n", cell->type); return; } @@ -417,7 +417,7 @@ struct FirrtlWorker 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); + new_expr = stringf("bits(%s, %d, %d)", wire_id, chunk.offset + chunk.width - 1, chunk.offset); } if (expr.empty()) @@ -465,7 +465,7 @@ struct FirrtlWorker // 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()); + log_warning("No instance for %s.%s\n", cell_type, cell_name); return; } @@ -477,7 +477,7 @@ struct FirrtlWorker 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())); + wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent, cell_name, cell_name_comment, instanceName, cellFileinfo)); for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { if (it->second.size() > 0) { @@ -490,7 +490,7 @@ struct FirrtlWorker 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)); + log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type, log_signal(it->second)); YS_FALLTHROUGH case FD_OUT: sourceExpr = firstName; @@ -498,27 +498,27 @@ struct FirrtlWorker 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)); + log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type, 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); + log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type, 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()); + log_error("Unknown subfield %s.%s\n", cell_type, sinkExpr); // 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%s%s <= %s %s", indent, sinkExpr, sourceExpr, cellFileinfo)); } } } @@ -535,7 +535,7 @@ struct FirrtlWorker 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()); + f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo); vector port_decls, wire_decls, mem_exprs, cell_exprs, wire_exprs; std::vector memories = Mem::get_all_memories(module); @@ -565,12 +565,12 @@ struct FirrtlWorker { if (wire->port_input && wire->port_output) log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire)); - port_decls.push_back(stringf("%s%s %s: UInt<%d> %s\n", indent.c_str(), wire->port_input ? "input" : "output", + port_decls.push_back(stringf("%s%s %s: UInt<%d> %s\n", indent, 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())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, wireName, wire->width, wireFileinfo)); } } @@ -602,7 +602,7 @@ struct FirrtlWorker if (cell->type.in(ID($not), ID($logic_not), ID($_NOT_), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor))) { string a_expr = make_expr(cell->getPort(ID::A)); - wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, y_width, cellFileinfo)); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; @@ -610,7 +610,7 @@ struct FirrtlWorker // 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); + a_expr = stringf("pad(%s, %d)", a_expr, y_width); } // Assume the FIRRTL width is a single bit. @@ -622,27 +622,27 @@ struct FirrtlWorker firrtl_width = a_width; } else if (cell->type == ID($logic_not)) { primop = "eq"; - a_expr = stringf("%s, UInt(0)", a_expr.c_str()); + a_expr = stringf("%s, UInt(0)", a_expr); } 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()); + a_expr = stringf("xorr(%s)", a_expr); } 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); + a_expr = stringf("%s, %cInt<%d>(0)", a_expr, a_signed ? 'S' : 'U', a_width); } - string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str()); + string expr = stringf("%s(%s)", primop, a_expr); if ((firrtl_is_signed && !always_uint)) - expr = stringf("asUInt(%s)", expr.c_str()); + expr = stringf("asUInt(%s)", expr); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -654,13 +654,13 @@ struct FirrtlWorker string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); std::string cellFileinfo = getFileinfo(cell); - wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, y_width, cellFileinfo)); 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_expr = stringf("pad(%s, %d)", a_expr, y_width); a_width = y_width; } } @@ -670,7 +670,7 @@ struct FirrtlWorker 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_expr = stringf("pad(%s, %d)", b_expr, y_width); b_width = y_width; } } @@ -680,11 +680,11 @@ struct FirrtlWorker 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_expr = stringf("pad(%s, %d)", a_expr, y_width); a_width = y_width; } if (b_width < y_width) { - b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); + b_expr = stringf("pad(%s, %d)", b_expr, y_width); b_width = y_width; } } @@ -856,23 +856,23 @@ struct FirrtlWorker string expr; // Deal with $xnor == ~^ (not xor) if (primop == "xnor") { - expr = stringf("not(xor(%s, %s))", a_expr.c_str(), b_expr.c_str()); + expr = stringf("not(xor(%s, %s))", a_expr, b_expr); } else { - expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str()); + expr = stringf("%s(%s, %s)", primop, a_expr, b_expr); } // 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); + expr = stringf("bits(%s, %d, 0)", expr, y_width - 1); } else if (firrtl_is_signed && (firrtl_width + 1) < y_width) { - expr = stringf("pad(%s, %d)", expr.c_str(), y_width); + expr = stringf("pad(%s, %d)", expr, y_width); } if ((firrtl_is_signed && !always_uint)) - expr = stringf("asUInt(%s)", expr.c_str()); + expr = stringf("asUInt(%s)", expr); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -885,11 +885,11 @@ struct FirrtlWorker string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); string s_expr = make_expr(cell->getPort(ID::S)); - wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent, y_id, width, cellFileinfo)); - string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str()); + string expr = stringf("mux(%s, %s, %s)", s_expr, b_expr, a_expr); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -911,9 +911,9 @@ struct FirrtlWorker string expr = make_expr(cell->getPort(ID::D)); string clk_expr = "asClock(" + make_expr(cell->getPort(ID::CLK)) + ")"; - wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent.c_str(), y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str())); + wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent, y_id, width, clk_expr, cellFileinfo)); - cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent, y_id, expr, cellFileinfo)); register_reverse_wire_map(y_id, cell->getPort(ID::Q)); continue; @@ -926,7 +926,7 @@ struct FirrtlWorker string a_expr = make_expr(cell->getPort(ID::A)); // Get the initial bit selector string b_expr = make_expr(cell->getPort(ID::B)); - wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent, y_id, y_width)); if (cell->getParam(ID::B_SIGNED).as_bool()) { // Use validif to constrain the selection (test the sign bit) @@ -934,9 +934,9 @@ struct FirrtlWorker 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()); + string expr = stringf("dshr(%s, %s)", a_expr, b_expr); - cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s\n", indent, y_id, expr)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } @@ -948,21 +948,21 @@ struct FirrtlWorker string b_expr = make_expr(cell->getPort(ID::B)); auto b_string = b_expr.c_str(); string expr; - wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent, y_id, 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); + std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr, gen_dshl(b_expr, b_width), y_width); + std::string dshr = stringf("dshr(%s, %s)", a_expr, 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); + expr = stringf("dshr(%s, %s)", a_expr, b_string); } - cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s\n", indent, y_id, expr)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } @@ -973,10 +973,10 @@ struct FirrtlWorker // 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); + a_expr = stringf("pad(%s, %d)", a_expr, 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())); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent, y_id, y_width)); + cell_exprs.push_back(stringf("%s%s <= %s\n", indent, y_id, a_expr)); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } @@ -999,7 +999,7 @@ struct FirrtlWorker 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)); + string port_name(stringf("%s.r%d", mem_id, i)); if (port.clk_enable) log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid)); @@ -1010,17 +1010,17 @@ struct FirrtlWorker 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()); + rpe << stringf("%s%s.addr <= %s\n", indent, port_name, addr_expr); + rpe << stringf("%s%s.en <= %s\n", indent, port_name, ena_expr); + rpe << stringf("%s%s.clk <= asClock(%s)\n", indent, port_name, clk_expr); cell_exprs.push_back(rpe.str()); - register_reverse_wire_map(stringf("%s.data", port_name.c_str()), port.data); + register_reverse_wire_map(stringf("%s.data", port_name), 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)); + string port_name(stringf("%s.w%d", mem_id, i)); if (!port.clk_enable) log_error("Unclocked write port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid)); @@ -1037,18 +1037,18 @@ struct FirrtlWorker 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()); + wpe << stringf("%s%s.data <= %s\n", indent, port_name, data_expr); + wpe << stringf("%s%s.addr <= %s\n", indent, port_name, addr_expr); + wpe << stringf("%s%s.en <= %s\n", indent, port_name, ena_expr); + wpe << stringf("%s%s.clk <= asClock(%s)\n", indent, port_name, clk_expr); + wpe << stringf("%s%s.mask <= %s\n", indent, port_name, mask_expr); cell_exprs.push_back(wpe.str()); } std::ostringstream me; - me << stringf(" mem %s:\n", mem_id.c_str()); + me << stringf(" mem %s:\n", mem_id); 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++) @@ -1068,8 +1068,8 @@ struct FirrtlWorker 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())); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent, y_id, y_width)); + cell_exprs.push_back(stringf("%s%s <= %s\n", indent, y_id, expr)); register_reverse_wire_map(y_id, conn.first); } @@ -1112,7 +1112,7 @@ struct FirrtlWorker chunk_width++; } - new_expr = stringf("bits(%s, %d, %d)", start_map.first.c_str(), + new_expr = stringf("bits(%s, %d, %d)", start_map.first, start_map.second + chunk_width - 1, start_map.second); is_valid = true; } @@ -1135,13 +1135,13 @@ struct FirrtlWorker 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())); + wire_decls.push_back(stringf("%swire %s: UInt<1> %s\n", indent, unconn_id, wireFileinfo)); // `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_decls.push_back(stringf("%s%s is invalid\n", indent, unconn_id)); } - wire_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), make_id(wire->name), expr.c_str(), wireFileinfo.c_str())); + wire_exprs.push_back(stringf("%s%s <= %s %s\n", indent, make_id(wire->name), expr, wireFileinfo)); } else { if (make_unconn_id) { unconn_id.clear(); @@ -1149,7 +1149,7 @@ struct FirrtlWorker // `invalid` is a firrtl construction for simulation so we will not // tag it with a @[fileinfo] tag as it doesn't directly correspond to // a specific line of verilog code. - wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), make_id(wire->name))); + wire_decls.push_back(stringf("%s%s is invalid\n", indent, make_id(wire->name))); } } @@ -1223,6 +1223,7 @@ struct FirrtlBackend : public Backend { Pass::call(design, "demuxmap"); Pass::call(design, "bwmuxmap"); + used_names.clear(); namecache.clear(); autoid_counter = 0; @@ -1248,7 +1249,7 @@ struct FirrtlBackend : public Backend { 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()); + *f << stringf("circuit %s: %s\n", make_id(top->name), circuitFileinfo); emit_elaborated_extmodules(design, *f); @@ -1262,6 +1263,7 @@ struct FirrtlBackend : public Backend { } } + used_names.clear(); namecache.clear(); autoid_counter = 0; } diff --git a/backends/functional/cxx.cc b/backends/functional/cxx.cc index 1f677120a..7f4ad1ea7 100644 --- a/backends/functional/cxx.cc +++ b/backends/functional/cxx.cc @@ -268,7 +268,7 @@ struct FunctionalCxxBackend : public Backend extra_args(f, filename, args, argidx, design); for (auto module : design->selected_modules()) { - log("Dumping module `%s'.\n", module->name.c_str()); + log("Dumping module `%s'.\n", module->name); printCxx(*f, filename, module); } } diff --git a/backends/functional/smtlib.cc b/backends/functional/smtlib.cc index 3eacf407c..1504c8fba 100644 --- a/backends/functional/smtlib.cc +++ b/backends/functional/smtlib.cc @@ -285,7 +285,7 @@ struct FunctionalSmtBackend : public Backend { extra_args(f, filename, args, argidx, design); for (auto module : design->selected_modules()) { - log("Processing module `%s`.\n", module->name.c_str()); + log("Processing module `%s`.\n", module->name); SmtModule smt(module); smt.write(*f); } diff --git a/backends/functional/smtlib_rosette.cc b/backends/functional/smtlib_rosette.cc index 639efb46e..d46104d9c 100644 --- a/backends/functional/smtlib_rosette.cc +++ b/backends/functional/smtlib_rosette.cc @@ -19,8 +19,8 @@ */ #include "kernel/functional.h" -#include "kernel/yosys.h" #include "kernel/sexpr.h" +#include "kernel/yosys.h" #include USING_YOSYS_NAMESPACE @@ -29,26 +29,24 @@ 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 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 -}; + // reserved for our own purposes + "inputs", "state", "name", nullptr}; struct SmtrScope : public Functional::Scope { - SmtrScope() { - for(const char **p = reserved_keywords; *p != nullptr; p++) + SmtrScope() + { + for (const char **p = reserved_keywords; *p != nullptr; p++) reserve(*p); } - bool is_character_legal(char c, int index) override { + bool is_character_legal(char c, int index) override + { return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("@$%^&_+=.", c)); } }; @@ -56,10 +54,11 @@ struct SmtrScope : public Functional::Scope { struct SmtrSort { Functional::Sort sort; SmtrSort(Functional::Sort sort) : sort(sort) {} - SExpr to_sexpr() const { - if(sort.is_memory()) { + 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()) { + } else if (sort.is_signal()) { return list("bitvector", sort.width()); } else { log_error("unknown sort"); @@ -67,7 +66,8 @@ struct SmtrSort { } }; -class SmtrStruct { +class SmtrStruct +{ struct Field { SmtrSort sort; std::string accessor; @@ -77,19 +77,22 @@ class SmtrStruct { vector fields; SmtrScope &global_scope; SmtrScope local_scope; -public: + + public: std::string name; SmtrStruct(std::string name, SmtrScope &scope) : global_scope(scope), local_scope(), name(name) {} - void insert(IdString field_name, SmtrSort sort) { + 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) { + void write_definition(SExprWriter &w) + { vector field_list; - for(const auto &field : fields) { + for (const auto &field : fields) { field_list.emplace_back(field.name); } w.push(); @@ -102,23 +105,26 @@ public: } w.pop(); } - template void write_value(SExprWriter &w, Fn fn) { + template void write_value(SExprWriter &w, Fn fn) + { w.open(list(name)); - for(auto field_name : field_names) { + 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) { + 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 smt_const(RTLIL::Const const &c) +{ std::string s = "#b"; - for(int i = c.size(); i-- > 0; ) + for (int i = c.size(); i-- > 0;) s += c[i] == State::S1 ? '1' : '0'; return s; } @@ -131,15 +137,9 @@ struct SmtrPrintVisitor : public Functional::AbstractVisitor { 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 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)); } @@ -166,8 +166,9 @@ struct SmtrPrintVisitor : public Functional::AbstractVisitor { 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) + 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); @@ -176,12 +177,20 @@ struct SmtrPrintVisitor : public Functional::AbstractVisitor { 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 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); } + 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 { @@ -196,12 +205,10 @@ struct SmtrModule { SmtrStruct state_struct; SmtrModule(Module *module, bool assoc_list_helpers) - : 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) + : 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"); if (assoc_list_helpers) { @@ -222,19 +229,17 @@ struct SmtrModule { { w.push(); w.open(list("define", list(name, "inputs", "state"))); - auto inlined = [&](Functional::Node n) { - return n.fn() == Functional::Fn::constant; - }; + 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)) + 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)) { + 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); } @@ -256,7 +261,7 @@ struct SmtrModule { 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++) { + for (int i = 0; i < 1 << state->sort.addr_width(); i++) { w << list("bv", smt_const(contents[i]), state->sort.data_width()); } w.close(); @@ -303,7 +308,7 @@ struct SmtrModule { } void write(std::ostream &out) - { + { SExprWriter w(out); input_struct.write_definition(w); @@ -324,8 +329,9 @@ struct SmtrModule { 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---| + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_functional_rosette [options] [filename]\n"); log("\n"); @@ -347,8 +353,7 @@ struct FunctionalSmtrBackend : public Backend { log_header(design, "Executing Functional Rosette Backend.\n"); size_t argidx; - for (argidx = 1; argidx < args.size(); argidx++) - { + for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-provides") provides = true; else if (args[argidx] == "-assoc-list-helpers") diff --git a/backends/functional/test_generic.cc b/backends/functional/test_generic.cc index a9dfd0c70..c01649a0f 100644 --- a/backends/functional/test_generic.cc +++ b/backends/functional/test_generic.cc @@ -105,7 +105,7 @@ struct MemContentsTest { 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()); + values.append(word); } insert_concatenated(low, values); } @@ -116,7 +116,9 @@ struct MemContentsTest { struct FunctionalTestGeneric : public Pass { - FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {} + FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") { + internal(); + } void help() override { @@ -141,7 +143,7 @@ struct FunctionalTestGeneric : public Pass */ for (auto module : design->selected_modules()) { - log("Dumping module `%s'.\n", module->name.c_str()); + log("Dumping module `%s'.\n", module->name); 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"; diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc index dcf107de3..78eab17da 100644 --- a/backends/intersynth/intersynth.cc +++ b/backends/intersynth/intersynth.cc @@ -100,13 +100,13 @@ struct IntersynthBackend : public Backend { } extra_args(f, filename, args, argidx); - log("Output filename: %s\n", filename.c_str()); + log("Output filename: %s\n", filename); 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()); + log_error("Can't open lib file `%s'.\n", filename); 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); @@ -172,7 +172,7 @@ struct IntersynthBackend : public Backend { 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()); + node_code += stringf(" %s %s", log_id(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig)); } } for (auto ¶m : cell->parameters) { @@ -199,13 +199,13 @@ struct IntersynthBackend : public Backend { if (!flag_notypes) { *f << stringf("### Connection Types\n"); for (auto code : conntypes_code) - *f << stringf("%s", code.c_str()); + *f << stringf("%s", code); *f << stringf("\n### Cell Types\n"); for (auto code : celltypes_code) - *f << stringf("%s", code.c_str()); + *f << stringf("%s", code); } *f << stringf("\n### Netlists\n"); - *f << stringf("%s", netlists_code.c_str()); + *f << stringf("%s", netlists_code); for (auto lib : libs) delete lib; diff --git a/backends/jny/jny.cc b/backends/jny/jny.cc index 4aacb4e20..ee0c0d14c 100644 --- a/backends/jny/jny.cc +++ b/backends/jny/jny.cc @@ -125,7 +125,7 @@ struct JnyWriter f << "{\n"; f << " \"$schema\": \"https://raw.githubusercontent.com/YosysHQ/yosys/main/misc/jny.schema.json\",\n"; - f << stringf(" \"generator\": \"%s\",\n", escape_string(yosys_maybe_version()).c_str()); + f << stringf(" \"generator\": \"%s\",\n", escape_string(yosys_maybe_version())); f << " \"version\": \"0.0.1\",\n"; f << " \"invocation\": \"" << escape_string(invk) << "\",\n"; f << " \"features\": ["; @@ -232,7 +232,7 @@ struct JnyWriter 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 << stringf(" %s\"name\": \"%s\",\n", _indent, escape_string(RTLIL::unescape_id(mod->name))); f << _indent << " \"cell_sorts\": [\n"; bool first_sort{true}; @@ -280,7 +280,7 @@ struct JnyWriter f << ",\n"; f << _indent << " {\n"; - f << stringf(" %s\"name\": \"%s\",\n", _indent.c_str(), escape_string(RTLIL::unescape_id(con.first)).c_str()); + f << stringf(" %s\"name\": \"%s\",\n", _indent, escape_string(RTLIL::unescape_id(con.first))); f << _indent << " \"direction\": \""; if (port_cell->input(con.first)) f << "i"; @@ -290,7 +290,7 @@ struct JnyWriter 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 << stringf(" %s\"range\": [%d, %d]\n", _indent, con.second.size(), 0); f << _indent << " }"; first_port = false; @@ -304,7 +304,7 @@ struct JnyWriter const auto _indent = gen_indent(indent_level); f << _indent << "{\n"; - f << stringf(" %s\"type\": \"%s\",\n", _indent.c_str(), sort.first.c_str()); + f << stringf(" %s\"type\": \"%s\",\n", _indent, sort.first); f << _indent << " \"ports\": [\n"; write_cell_ports(port_cell, indent_level + 2); @@ -351,10 +351,10 @@ struct JnyWriter 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()); + f << stringf(" %s\"%s\": ", _indent, escape_string(RTLIL::unescape_id(param.first))); write_param_val(param_val); } else { - f << stringf(" %s\"%s\": true", _indent.c_str(), escape_string(RTLIL::unescape_id(param.first)).c_str()); + f << stringf(" %s\"%s\": true", _indent, escape_string(RTLIL::unescape_id(param.first))); } first_param = false; @@ -366,7 +366,7 @@ struct JnyWriter log_assert(cell != nullptr); f << _indent << " {\n"; - f << stringf(" %s\"name\": \"%s\"", _indent.c_str(), escape_string(RTLIL::unescape_id(cell->name)).c_str()); + f << stringf(" %s\"name\": \"%s\"", _indent, escape_string(RTLIL::unescape_id(cell->name))); if (_include_connections) { f << ",\n" << _indent << " \"connections\": [\n"; @@ -553,7 +553,7 @@ struct JnyPass : public Pass { 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)); + log_error("Can't open file `%s' for writing: %s\n", filename, strerror(errno)); } f = ff; invk << filename; @@ -568,7 +568,7 @@ struct JnyPass : public Pass { if (!empty) { delete f; } else { - log("%s", buf.str().c_str()); + log("%s", buf.str()); } } diff --git a/backends/json/json.cc b/backends/json/json.cc index 98e929dfa..b04083622 100644 --- a/backends/json/json.cc +++ b/backends/json/json.cc @@ -135,7 +135,7 @@ struct JsonWriter bool first = true; for (auto ¶m : parameters) { f << stringf("%s\n", first ? "" : ","); - f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first).c_str()); + f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first)); write_parameter_value(param.second); first = false; } @@ -155,7 +155,7 @@ struct JsonWriter 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(" %s: {\n", get_name(module->name)); f << stringf(" \"attributes\": {"); write_parameters(module->attributes, /*for_module=*/true); @@ -174,7 +174,7 @@ struct JsonWriter if (use_selection && !module->selected(w)) continue; f << stringf("%s\n", first ? "" : ","); - f << stringf(" %s: {\n", get_name(n).c_str()); + f << stringf(" %s: {\n", get_name(n)); 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); @@ -182,7 +182,7 @@ struct JsonWriter 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(" \"bits\": %s\n", get_bits(w)); f << stringf(" }"); first = false; } @@ -196,13 +196,13 @@ struct JsonWriter 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(" %s: {\n", get_name(c->name)); f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0"); - f << stringf(" \"type\": %s,\n", get_name(c->type).c_str()); + f << stringf(" \"type\": %s,\n", get_name(c->type)); if (aig_mode) { Aig aig(c); if (!aig.name.empty()) { - f << stringf(" \"model\": \"%s\",\n", aig.name.c_str()); + f << stringf(" \"model\": \"%s\",\n", aig.name); aig_models.insert(aig); } } @@ -220,7 +220,7 @@ struct JsonWriter 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()); + f << stringf(" %s: \"%s\"", get_name(conn.first), direction); first2 = false; } f << stringf("\n },\n"); @@ -229,7 +229,7 @@ struct JsonWriter 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()); + f << stringf(" %s: %s", get_name(conn.first), get_bits(conn.second)); first2 = false; } f << stringf("\n }\n"); @@ -245,7 +245,7 @@ struct JsonWriter 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(" %s: {\n", get_name(it.second->name)); f << stringf(" \"hide_name\": %s,\n", it.second->name[0] == '$' ? "1" : "0"); f << stringf(" \"attributes\": {"); write_parameters(it.second->attributes); @@ -265,9 +265,9 @@ struct JsonWriter if (use_selection && !module->selected(w)) continue; f << stringf("%s\n", first ? "" : ","); - f << stringf(" %s: {\n", get_name(w->name).c_str()); + f << stringf(" %s: {\n", get_name(w->name)); f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0"); - f << stringf(" \"bits\": %s,\n", get_bits(w).c_str()); + f << stringf(" \"bits\": %s,\n", get_bits(w)); if (w->start_offset) f << stringf(" \"offset\": %d,\n", w->start_offset); if (w->upto) @@ -291,7 +291,7 @@ struct JsonWriter design->sort(); f << stringf("{\n"); - f << stringf(" \"creator\": %s,\n", get_string(yosys_maybe_version()).c_str()); + f << stringf(" \"creator\": %s,\n", get_string(yosys_maybe_version())); f << stringf(" \"modules\": {\n"); vector modules = use_selection ? design->selected_modules() : design->modules(); bool first_module = true; @@ -308,7 +308,7 @@ struct JsonWriter for (auto &aig : aig_models) { if (!first_model) f << stringf(",\n"); - f << stringf(" \"%s\": [\n", aig.name.c_str()); + f << stringf(" \"%s\": [\n", aig.name); int node_idx = 0; for (auto &node : aig.nodes) { if (node_idx != 0) @@ -701,7 +701,7 @@ struct JsonPass : public Pass { 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)); + log_error("Can't open file `%s' for writing: %s\n", filename, strerror(errno)); } f = ff; } else { @@ -714,7 +714,7 @@ struct JsonPass : public Pass { if (!empty) { delete f; } else { - log("%s", buf.str().c_str()); + log("%s", buf.str()); } } } JsonPass; diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc index adde37356..d47bebf82 100644 --- a/backends/rtlil/rtlil_backend.cc +++ b/backends/rtlil/rtlil_backend.cc @@ -24,12 +24,23 @@ #include "rtlil_backend.h" #include "kernel/yosys.h" +#include "kernel/utils.h" #include +#include USING_YOSYS_NAMESPACE using namespace RTLIL_BACKEND; YOSYS_NAMESPACE_BEGIN +void RTLIL_BACKEND::dump_attributes(std::ostream &f, std::string indent, const RTLIL::AttrObject *obj) +{ + for (const auto& [name, value] : reversed(obj->attributes)) { + f << stringf("%s" "attribute %s ", indent, name); + dump_const(f, value); + f << stringf("\n"); + } +} + void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int width, int offset, bool autoint) { if (width < 0) @@ -50,7 +61,9 @@ void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi return; } } - f << stringf("%d'", width); + if ((data.flags & RTLIL::CONST_FLAG_UNSIZED) == 0) { + f << stringf("%d'", width); + } if (data.flags & RTLIL::CONST_FLAG_SIGNED) { f << stringf("s"); } @@ -96,11 +109,11 @@ void RTLIL_BACKEND::dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, 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()); + f << stringf("%s", chunk.wire->name); else if (chunk.width == 1) - f << stringf("%s [%d]", chunk.wire->name.c_str(), chunk.offset); + f << stringf("%s [%d]", chunk.wire->name, chunk.offset); else - f << stringf("%s [%d:%d]", chunk.wire->name.c_str(), chunk.offset+chunk.width-1, chunk.offset); + f << stringf("%s [%d:%d]", chunk.wire->name, chunk.offset+chunk.width-1, chunk.offset); } } @@ -110,8 +123,9 @@ void RTLIL_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, boo 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); + auto chunks = sig.chunks(); + for (const auto& chunk : reversed(chunks)) { + dump_sigchunk(f, chunk, false); f << stringf(" "); } f << stringf("}"); @@ -120,16 +134,12 @@ void RTLIL_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, boo 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"); - } + dump_attributes(f, indent, wire); 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" "# driver %s %s\n", indent, + wire->driverCell()->name, wire->driverPort()); } - f << stringf("%s" "wire ", indent.c_str()); + f << stringf("%s" "wire ", indent); if (wire->width != 1) f << stringf("width %d ", wire->width); if (wire->upto) @@ -144,101 +154,85 @@ void RTLIL_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL:: f << stringf("inout %d ", wire->port_id); if (wire->is_signed) f << stringf("signed "); - f << stringf("%s\n", wire->name.c_str()); + f << stringf("%s\n", wire->name); } 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()); + dump_attributes(f, indent, memory); + f << stringf("%s" "memory ", indent); 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()); + f << stringf("%s\n", memory->name); } 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); + dump_attributes(f, indent, cell); + f << stringf("%s" "cell %s %s\n", indent, cell->type, cell->name); + for (const auto& [name, param] : reversed(cell->parameters)) { + f << stringf("%s parameter%s%s%s %s ", indent, + (param.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", + (param.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "", + (param.flags & RTLIL::CONST_FLAG_UNSIZED) != 0 ? " unsized" : "", + name); + dump_const(f, param); 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); + for (const auto& [port, sig] : reversed(cell->connections_)) { + f << stringf("%s connect %s ", indent, port); + dump_sigspec(f, sig); 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()); + f << stringf("%s" "end\n", indent); } 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); + for (const auto& [lhs, rhs] : cs->actions) { + f << stringf("%s" "assign ", indent); + dump_sigspec(f, lhs); f << stringf(" "); - dump_sigspec(f, it->second); + dump_sigspec(f, rhs); f << stringf("\n"); } - for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) - dump_proc_switch(f, indent, *it); + for (const auto& sw : cs->switches) + dump_proc_switch(f, indent, sw); } 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"); - } + dump_attributes(f, indent, sw); - f << stringf("%s" "switch ", indent.c_str()); + f << stringf("%s" "switch ", indent); dump_sigspec(f, sw->signal); f << stringf("\n"); - for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) + for (const auto case_ : sw->cases) { - 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++) { + dump_attributes(f, indent, case_); + f << stringf("%s case ", indent); + for (size_t i = 0; i < case_->compare.size(); i++) { if (i > 0) f << stringf(" , "); - dump_sigspec(f, (*it)->compare[i]); + dump_sigspec(f, case_->compare[i]); } f << stringf("\n"); - dump_proc_case_body(f, indent + " ", *it); + dump_proc_case_body(f, indent + " ", case_); } - f << stringf("%s" "end\n", indent.c_str()); + f << stringf("%s" "end\n", indent); } void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy) { - f << stringf("%s" "sync ", indent.c_str()); + f << stringf("%s" "sync ", indent); switch (sy->type) { case RTLIL::ST0: f << stringf("low "); if (0) case RTLIL::ST1: f << stringf("high "); @@ -253,21 +247,17 @@ void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT case RTLIL::STi: f << stringf("init\n"); break; } - for (auto &it: sy->actions) { - f << stringf("%s update ", indent.c_str()); - dump_sigspec(f, it.first); + for (const auto& [lhs, rhs] : sy->actions) { + f << stringf("%s update ", indent); + dump_sigspec(f, lhs); f << stringf(" "); - dump_sigspec(f, it.second); + dump_sigspec(f, rhs); 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_attributes(f, indent, &it); + f << stringf("%s memwr %s ", indent, it.memid); dump_sigspec(f, it.address); f << stringf(" "); dump_sigspec(f, it.data); @@ -281,21 +271,17 @@ void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT 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_attributes(f, indent, proc); + f << stringf("%s" "process %s\n", indent, proc->name); 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()); + for (auto* sync : proc->syncs) + dump_proc_sync(f, indent + " ", sync); + f << stringf("%s" "end\n", indent); } 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()); + f << stringf("%s" "connect ", indent); dump_sigspec(f, left); f << stringf(" "); dump_sigspec(f, right); @@ -309,13 +295,9 @@ void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu 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"); - } + dump_attributes(f, indent, module); - f << stringf("%s" "module %s\n", indent.c_str(), module->name.c_str()); + f << stringf("%s" "module %s\n", indent, module->name); if (!module->avail_parameters.empty()) { if (only_selected) @@ -323,9 +305,9 @@ void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu 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()); + f << stringf("%s" " parameter %s\n", indent, p); } else { - f << stringf("%s" " parameter %s ", indent.c_str(), p.c_str()); + f << stringf("%s" " parameter %s ", indent, p); dump_const(f, it->second); f << stringf("\n"); } @@ -335,40 +317,40 @@ void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu if (print_body) { - for (auto it : module->wires()) - if (!only_selected || design->selected(module, it)) { + for (const auto& [_, wire] : reversed(module->wires_)) + if (!only_selected || design->selected(module, wire)) { if (only_selected) f << stringf("\n"); - dump_wire(f, indent + " ", it); + dump_wire(f, indent + " ", wire); } - for (auto it : module->memories) - if (!only_selected || design->selected(module, it.second)) { + for (const auto& [_, mem] : reversed(module->memories)) + if (!only_selected || design->selected(module, mem)) { if (only_selected) f << stringf("\n"); - dump_memory(f, indent + " ", it.second); + dump_memory(f, indent + " ", mem); } - for (auto it : module->cells()) - if (!only_selected || design->selected(module, it)) { + for (const auto& [_, cell] : reversed(module->cells_)) + if (!only_selected || design->selected(module, cell)) { if (only_selected) f << stringf("\n"); - dump_cell(f, indent + " ", it); + dump_cell(f, indent + " ", cell); } - for (auto it : module->processes) - if (!only_selected || design->selected(module, it.second)) { + for (const auto& [_, process] : reversed(module->processes)) + if (!only_selected || design->selected(module, process)) { if (only_selected) f << stringf("\n"); - dump_proc(f, indent + " ", it.second); + dump_proc(f, indent + " ", process); } bool first_conn_line = true; - for (auto it = module->connections().begin(); it != module->connections().end(); ++it) { + for (const auto& [lhs, rhs] : module->connections()) { bool show_conn = !only_selected || design->selected_whole_module(module->name); if (!show_conn) { - RTLIL::SigSpec sigs = it->first; - sigs.append(it->second); + RTLIL::SigSpec sigs = lhs; + sigs.append(rhs); for (auto &c : sigs.chunks()) { if (c.wire == NULL || !design->selected(module, c.wire)) continue; @@ -378,14 +360,14 @@ void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu if (show_conn) { if (only_selected && first_conn_line) f << stringf("\n"); - dump_conn(f, indent + " ", it->first, it->second); + dump_conn(f, indent + " ", lhs, rhs); first_conn_line = false; } } } if (print_header) - f << stringf("%s" "end\n", indent.c_str()); + f << stringf("%s" "end\n", indent); } void RTLIL_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n) @@ -394,7 +376,7 @@ void RTLIL_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool onl if (!flag_m) { int count_selected_mods = 0; - for (auto module : design->modules()) { + for (auto* module : design->modules()) { if (design->selected_whole_module(module->name)) flag_m = true; if (design->selected(module)) @@ -410,7 +392,7 @@ void RTLIL_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool onl f << stringf("autoidx %d\n", autoidx); } - for (auto module : design->modules()) { + for (const auto& [_, module] : reversed(design->modules_)) { if (!only_selected || design->selected(module)) { if (only_selected) f << stringf("\n"); @@ -438,10 +420,14 @@ struct RTLILBackend : public Backend { log(" -selected\n"); log(" only write selected parts of the design.\n"); log("\n"); + log(" -sort\n"); + log(" sort design in-place (used to be default).\n"); + log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool selected = false; + bool do_sort = false; log_header(design, "Executing RTLIL backend.\n"); @@ -452,13 +438,18 @@ struct RTLILBackend : public Backend { selected = true; continue; } + if (arg == "-sort") { + do_sort = true; + continue; + } break; } extra_args(f, filename, args, argidx); - design->sort(); + log("Output filename: %s\n", filename); - log("Output filename: %s\n", filename.c_str()); + if (do_sort) + design->sort(); *f << stringf("# Generated by %s\n", yosys_maybe_version()); RTLIL_BACKEND::dump_design(*f, design, selected, true, false); @@ -528,10 +519,10 @@ struct DumpPass : public Pass { if (!empty) { rewrite_filename(filename); std::ofstream *ff = new std::ofstream; - ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc); + ff->open(filename, 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)); + log_error("Can't open file `%s' for writing: %s\n", filename, strerror(errno)); } f = ff; } else { @@ -543,7 +534,7 @@ struct DumpPass : public Pass { if (!empty) { delete f; } else { - log("%s", buf.str().c_str()); + log("%s", buf.str()); } } } DumpPass; diff --git a/backends/rtlil/rtlil_backend.h b/backends/rtlil/rtlil_backend.h index 35829729c..dd7347def 100644 --- a/backends/rtlil/rtlil_backend.h +++ b/backends/rtlil/rtlil_backend.h @@ -31,6 +31,7 @@ YOSYS_NAMESPACE_BEGIN namespace RTLIL_BACKEND { + void dump_attributes(std::ostream &f, std::string indent, const RTLIL::AttrObject *obj); 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); diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc index e70c62a71..8ebc685f0 100644 --- a/backends/simplec/simplec.cc +++ b/backends/simplec/simplec.cc @@ -218,8 +218,8 @@ struct SimplecWorker 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())); + util_declarations.push_back(stringf("#ifndef %s", s)); + util_declarations.push_back(stringf("#define %s", s)); } string util_get_bit(const string &signame, int n, int idx) @@ -232,33 +232,33 @@ struct SimplecWorker 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("static inline bool %s(const %s *sig)", util_name, sigtype(n))); 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(" return (sig->%s >> %d) & 1;", value_name, 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()); + return stringf("%s(&%s)", util_name, signame); } 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()); + return stringf(" %s.value_0_0 = %s;", signame, expr); 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("static inline void %s(%s *sig, bool value)", util_name, sigtype(n))); util_declarations.push_back(stringf("{")); int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize; @@ -266,9 +266,9 @@ struct SimplecWorker #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(" sig->%s |= 1UL << %d;", value_name, word_offset)); util_declarations.push_back(stringf(" else")); - util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name.c_str(), word_offset)); + util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name, 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)); @@ -279,7 +279,7 @@ struct SimplecWorker generated_utils.insert(util_name); } - return stringf(" %s(&%s, %s);", util_name.c_str(), signame.c_str(), expr.c_str()); + return stringf(" %s(&%s, %s);", util_name, signame, expr); } void create_module_struct(Module *mod) @@ -339,38 +339,38 @@ struct SimplecWorker 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()); + string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name)); 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(stringf("#ifndef %s", ifdef_name)); + struct_declarations.push_back(stringf("#define %s", ifdef_name)); + struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name))); 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(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), 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(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), 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))); + struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width), cid(w->name), 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 %s_state_t %s; // %s", cid(c->type), cid(c->name), log_id(c))); struct_declarations.push_back(stringf("};")); struct_declarations.push_back("#endif"); @@ -407,14 +407,14 @@ struct SimplecWorker 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()); + if (cell->type == ID($_AND_)) expr = stringf("%s & %s", a_expr, b_expr); + if (cell->type == ID($_NAND_)) expr = stringf("!(%s & %s)", a_expr, b_expr); + if (cell->type == ID($_OR_)) expr = stringf("%s | %s", a_expr, b_expr); + if (cell->type == ID($_NOR_)) expr = stringf("!(%s | %s)", a_expr, b_expr); + if (cell->type == ID($_XOR_)) expr = stringf("%s ^ %s", a_expr, b_expr); + if (cell->type == ID($_XNOR_)) expr = stringf("!(%s ^ %s)", a_expr, b_expr); + if (cell->type == ID($_ANDNOT_)) expr = stringf("%s & (!%s)", a_expr, b_expr); + if (cell->type == ID($_ORNOT_)) expr = stringf("%s | (!%s)", a_expr, b_expr); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -436,8 +436,8 @@ struct SimplecWorker 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()); + if (cell->type == ID($_AOI3_)) expr = stringf("!((%s & %s) | %s)", a_expr, b_expr, c_expr); + if (cell->type == ID($_OAI3_)) expr = stringf("!((%s | %s) & %s)", a_expr, b_expr, c_expr); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -461,8 +461,8 @@ struct SimplecWorker 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()); + if (cell->type == ID($_AOI4_)) expr = stringf("!((%s & %s) | (%s & %s))", a_expr, b_expr, c_expr, d_expr); + if (cell->type == ID($_OAI4_)) expr = stringf("!((%s | %s) & (%s | %s))", a_expr, b_expr, c_expr, d_expr); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -484,9 +484,9 @@ struct SimplecWorker 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()); + string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr, + cell->type == ID($_NMUX_) ? "!" : "", b_expr, + cell->type == ID($_NMUX_) ? "!" : "", 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) + @@ -504,7 +504,7 @@ struct SimplecWorker while (work->dirty) { if (verbose && (!work->dirty_bits.empty() || !work->dirty_cells.empty())) - log(" In %s:\n", work->log_prefix.c_str()); + log(" In %s:\n", work->log_prefix); while (!work->dirty_bits.empty() || !work->dirty_cells.empty()) { @@ -517,8 +517,8 @@ struct SimplecWorker 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))); + log(" Propagating %s.%s[%d:%d].\n", work->log_prefix, log_id(chunk.wire), chunk.offset+chunk.width-1, chunk.offset); + funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix, log_signal(chunk))); } for (SigBit bit : dirtysig) @@ -539,7 +539,7 @@ struct SimplecWorker 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, + log(" Propagating %s.%s[%d] -> %s.%s[%d].\n", work->log_prefix, log_id(bit.wire), bit.offset, work->parent->log_prefix.c_str(), log_id(parent_bit.wire), parent_bit.offset); } @@ -556,11 +556,11 @@ struct SimplecWorker 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, + log(" Propagating %s.%s[%d] -> %s.%s.%s[%d].\n", work->log_prefix, 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)), + log(" Marking cell %s.%s (via %s.%s[%d]).\n", work->log_prefix, log_id(std::get<0>(port)), work->log_prefix.c_str(), log_id(bit.wire), bit.offset); work->set_dirty(std::get<0>(port)); } @@ -579,7 +579,7 @@ struct SimplecWorker 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)); + log(" Evaluating %s (%s, best of %d).\n", hiername, log_id(cell->type), GetSize(work->dirty_cells)); if (activated_cells.count(hiername)) reactivated_cells.insert(hiername); @@ -630,13 +630,13 @@ struct SimplecWorker void make_func(HierDirtyFlags *work, const string &func_name, const vector &preamble) { - log("Generating function %s():\n", func_name.c_str()); + log("Generating function %s():\n", func_name); 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(stringf("static void %s(struct %s_state_t *state)", func_name, cid(work->module->name))); funct_declarations.push_back("{"); for (auto &line : preamble) funct_declarations.push_back(line); @@ -657,7 +657,7 @@ struct SimplecWorker { SigSpec sig = sigmaps.at(module)(w); Const val = w->attributes.at(ID::init); - val.bits().resize(GetSize(sig), State::Sx); + val.resize(GetSize(sig), State::Sx); for (int i = 0; i < GetSize(sig); i++) if (val[i] == State::S0 || val[i] == State::S1) { diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index 87f5a08c8..d80622029 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -82,27 +82,27 @@ struct Smt2Worker 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); + decl_str = stringf("(define-fun |%s| ((state |%s_s|)) Bool (= ((_ extract %d %d) state) #b1))", name, 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); + decl_str = stringf("(define-fun |%s| ((state |%s_s|)) (_ BitVec %d) ((_ extract %d %d) state))", name, 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()); + decl_str = stringf(" (|%s| Bool)", name); } else { - decl_str = stringf(" (|%s| (_ BitVec %d))", name.c_str(), width); + decl_str = stringf(" (|%s| (_ BitVec %d))", name, width); } } else { if (width == 0) { - decl_str = stringf("(declare-fun |%s| (|%s_s|) Bool)", name.c_str(), get_id(module)); + decl_str = stringf("(declare-fun |%s| (|%s_s|) Bool)", name, get_id(module)); } else { - decl_str = stringf("(declare-fun |%s| (|%s_s|) (_ BitVec %d))", name.c_str(), get_id(module), width); + decl_str = stringf("(declare-fun |%s| (|%s_s|) (_ BitVec %d))", name, get_id(module), width); } } @@ -130,7 +130,7 @@ struct Smt2Worker 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()); + log_error("Memory %s.%s not allowed in module with smtlib2_module attribute", get_id(module), mem.memid); mem.narrow(); mem_dict[mem.memid] = &mem; @@ -383,7 +383,7 @@ struct Smt2Worker } 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())); + subexpr.push_back(stringf("(ite %s #b1 #b0)", get_bool(sig[i], state_name))); continue; } @@ -495,7 +495,7 @@ struct Smt2Worker } if (width != GetSize(sig_y) && type != 'b') - processed_expr = stringf("((_ extract %d 0) %s)", GetSize(sig_y)-1, processed_expr.c_str()); + processed_expr = stringf("((_ extract %d 0) %s)", GetSize(sig_y)-1, processed_expr); if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell)); @@ -617,14 +617,14 @@ struct Smt2Worker 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())); + 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)); 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()); + log("Wire %s is maximized\n", cell->getPort(QY).as_wire()->name.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()); + log("Wire %s is minimized\n", cell->getPort(QY).as_wire()->name.str()); } bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst)); @@ -722,7 +722,7 @@ struct Smt2Worker 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'); + return export_bvop(cell, stringf("(%s A #b%s)", is_and ? "=" : "distinct", bits), 'b'); } if (cell->type == ID($reduce_and)) return export_reduce(cell, "(and A)", true); @@ -746,7 +746,7 @@ struct Smt2Worker 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(), + processed_expr = stringf("(ite %s %s %s)", get_bool(sig_s[i]), get_bv(sig_b.extract(i*width, width)).c_str(), processed_expr.c_str()); if (verbose) @@ -1079,24 +1079,24 @@ struct Smt2Worker RTLIL::SigSpec sig = sigmap(wire); Const val = wire->attributes.at(ID::init); - val.bits().resize(GetSize(sig), State::Sx); + val.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; + val.set(i, State::S0); + mask.set(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))); + init_list.push_back(stringf("(= (bvand %s #b%s) #b%s) ; %s", get_bv(sig), mask.as_string(), val.as_string(), 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))); + init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig), val.as_string(), 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))); + init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]), val[i] == State::S1 ? "true" : "false", get_id(wire))); } } @@ -1131,7 +1131,7 @@ struct Smt2Worker } 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())); + 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())); else decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, get_id(cell))); @@ -1180,11 +1180,11 @@ struct Smt2Worker 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(), + hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)), 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(), + hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]), get_id(cell->type), get_id(w), i, cell_state.c_str(), get_id(cell->type), get_id(w), i)); } } @@ -1204,25 +1204,25 @@ struct Smt2Worker { 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())); + trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d, expr_q, get_id(cell), log_signal(cell->getPort(ID::Q)))); + ex_state_eq.push_back(stringf("(= %s %s)", get_bool(cell->getPort(ID::Q)), get_bool(cell->getPort(ID::Q), "other_state"))); } 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())); + trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d, expr_q, get_id(cell), log_signal(cell->getPort(ID::Q)))); + ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort(ID::Q)), get_bv(cell->getPort(ID::Q), "other_state"))); } 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)))); + trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d, expr_q, 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())); + ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort(ID::Y)), get_bv(cell->getPort(ID::Y), "other_state"))); } } @@ -1341,11 +1341,11 @@ struct Smt2Worker 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))); + trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d, expr_q, 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))); + hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d, final_memstate, get_id(mem->memid))); Const init_data = mem->get_init_data(); @@ -1361,10 +1361,10 @@ struct Smt2Worker 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; + initmask.set(k, State::S1); } else { - initmask.bits()[k] = State::S0; - initword.bits()[k] = State::S0; + initmask.set(k, State::S0); + initword.set(k, State::S0); } } @@ -1402,7 +1402,7 @@ struct Smt2Worker expr = "\n " + ex_state_eq.front() + "\n"; } else { for (auto &str : ex_state_eq) - expr += stringf("\n %s", str.c_str()); + expr += stringf("\n %s", str); expr += "\n)"; } } @@ -1415,7 +1415,7 @@ struct Smt2Worker expr = "\n " + ex_input_eq.front() + "\n"; } else { for (auto &str : ex_input_eq) - expr += stringf("\n %s", str.c_str()); + expr += stringf("\n %s", str); expr += "\n)"; } } @@ -1429,7 +1429,7 @@ struct Smt2Worker assert_expr = "\n " + assert_list.front() + "\n"; } else { for (auto &str : assert_list) - assert_expr += stringf("\n %s", str.c_str()); + assert_expr += stringf("\n %s", str); assert_expr += "\n)"; } } @@ -1442,7 +1442,7 @@ struct Smt2Worker assume_expr = "\n " + assume_list.front() + "\n"; } else { for (auto &str : assume_list) - assume_expr += stringf("\n %s", str.c_str()); + assume_expr += stringf("\n %s", str); assume_expr += "\n)"; } } @@ -1455,7 +1455,7 @@ struct Smt2Worker init_expr = "\n " + init_list.front() + "\n"; } else { for (auto &str : init_list) - init_expr += stringf("\n %s", str.c_str()); + init_expr += stringf("\n %s", str); init_expr += "\n)"; } } @@ -1776,7 +1776,7 @@ struct Smt2Backend : public Backend { if (args[argidx] == "-tpl" && argidx+1 < args.size()) { template_f.open(args[++argidx]); if (template_f.fail()) - log_error("Can't open template file `%s'.\n", args[argidx].c_str()); + log_error("Can't open template file `%s'.\n", args[argidx]); continue; } if (args[argidx] == "-bv" || args[argidx] == "-mem") { @@ -1846,7 +1846,7 @@ struct Smt2Backend : public Backend { *f << stringf("; yosys-smt2-stdt\n"); for (auto &it : solver_options) - *f << stringf("; yosys-smt2-solver-option %s %s\n", it.first.c_str(), it.second.c_str()); + *f << stringf("; yosys-smt2-solver-option %s %s\n", it.first, it.second); std::vector sorted_modules; @@ -1913,7 +1913,7 @@ struct Smt2Backend : public Backend { } if (topmod) - *f << stringf("; yosys-smt2-topmod %s\n", topmod_id.c_str()); + *f << stringf("; yosys-smt2-topmod %s\n", topmod_id); *f << stringf("; end of yosys output\n"); diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py index d17bad3fb..4e47117b3 100644 --- a/backends/smt2/smtbmc.py +++ b/backends/smt2/smtbmc.py @@ -1875,6 +1875,11 @@ elif covermode: smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step-1, step)) smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) + if step < skip_steps: + print_msg("Skipping step %d.." % (step)) + step += 1 + continue + while "1" in cover_mask: print_msg("Checking cover reachability in step %d.." % (step)) smt_push() diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc index 1c2b2a224..a6ccbf27f 100644 --- a/backends/smv/smv.cc +++ b/backends/smv/smv.cc @@ -59,7 +59,7 @@ struct SmvWorker { if (!idcache.count(id)) { - string name = stringf("_%s", id.c_str()); + string name = stringf("_%s", id); if (name.compare(0, 2, "_\\") == 0) name = "_" + name.substr(2); @@ -163,15 +163,15 @@ struct SmvWorker if (width >= 0) { if (is_signed) { if (GetSize(sig) > width) - s = stringf("signed(resize(%s, %d))", s.c_str(), width); + s = stringf("signed(resize(%s, %d))", s, width); else - s = stringf("resize(signed(%s), %d)", s.c_str(), width); + s = stringf("resize(signed(%s), %d)", s, width); } else - s = stringf("resize(%s, %d)", s.c_str(), width); + s = stringf("resize(%s, %d)", s, width); } else if (is_signed) - s = stringf("signed(%s)", s.c_str()); + s = stringf("signed(%s)", s); else if (count_chunks > 1) - s = stringf("(%s)", s.c_str()); + s = stringf("(%s)", s); strbuf.push_back(s); return strbuf.back().c_str(); @@ -262,7 +262,7 @@ struct SmvWorker if (cell->type == ID($sshr) && signed_a) { expr_a = rvalue_s(sig_a, width); - expr = stringf("resize(unsigned(%s %s %s), %d)", expr_a.c_str(), op.c_str(), rvalue(sig_b.extract(0, shift_b_width)), width_y); + expr = stringf("resize(unsigned(%s %s %s), %d)", expr_a, op, rvalue(sig_b.extract(0, shift_b_width)), width_y); if (shift_b_width < GetSize(sig_b)) expr = stringf("%s != 0ud%d_0 ? (bool(%s) ? !0ud%d_0 : 0ud%d_0) : %s", rvalue(sig_b.extract(shift_b_width, GetSize(sig_b) - shift_b_width)), GetSize(sig_b) - shift_b_width, @@ -278,8 +278,8 @@ struct SmvWorker // f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b)); definitions.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b))); - string expr_shl = stringf("resize(%s << %s[%d:0], %d)", expr_a.c_str(), b_shl, shift_b_width-1, width_y); - string expr_shr = stringf("resize(%s >> %s[%d:0], %d)", expr_a.c_str(), b_shr, shift_b_width-1, width_y); + string expr_shl = stringf("resize(%s << %s[%d:0], %d)", expr_a, b_shl, shift_b_width-1, width_y); + string expr_shr = stringf("resize(%s >> %s[%d:0], %d)", expr_a, b_shr, shift_b_width-1, width_y); if (shift_b_width < GetSize(sig_b)) { expr_shl = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", b_shl, GetSize(sig_b)-1, shift_b_width, @@ -288,7 +288,7 @@ struct SmvWorker GetSize(sig_b)-shift_b_width, width_y, expr_shr.c_str()); } - expr = stringf("bool(%s) ? %s : %s", rvalue(sig_b[GetSize(sig_b)-1]), expr_shl.c_str(), expr_shr.c_str()); + expr = stringf("bool(%s) ? %s : %s", rvalue(sig_b[GetSize(sig_b)-1]), expr_shl, expr_shr); } else { @@ -297,13 +297,13 @@ struct SmvWorker else expr_a = stringf("resize(unsigned(%s), %d)", rvalue_s(sig_a, width_ay), width); - expr = stringf("resize(%s %s %s[%d:0], %d)", expr_a.c_str(), op.c_str(), rvalue_u(sig_b), shift_b_width-1, width_y); + expr = stringf("resize(%s %s %s[%d:0], %d)", expr_a, op, rvalue_u(sig_b), shift_b_width-1, width_y); if (shift_b_width < GetSize(sig_b)) expr = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", rvalue_u(sig_b), GetSize(sig_b)-1, shift_b_width, GetSize(sig_b)-shift_b_width, width_y, expr.c_str()); } - definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort(ID::Y)), expr.c_str())); + definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort(ID::Y)), expr)); continue; } @@ -426,7 +426,7 @@ struct SmvWorker if (cell->type == ID($reduce_or)) expr = stringf("%s != 0ub%d_0", expr_a, width_a); if (cell->type == ID($reduce_bool)) expr = stringf("%s != 0ub%d_0", expr_a, width_a); - definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr, width_y)); continue; } @@ -445,7 +445,7 @@ struct SmvWorker if (cell->type == ID($reduce_xnor)) expr = "!(" + expr + ")"; - definitions.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(%s, %d);", expr_y, expr, width_y)); continue; } @@ -463,7 +463,7 @@ struct SmvWorker if (cell->type == ID($logic_and)) expr = expr_a + " & " + expr_b; if (cell->type == ID($logic_or)) expr = expr_a + " | " + expr_b; - definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr, width_y)); continue; } @@ -475,7 +475,7 @@ struct SmvWorker string expr_a = stringf("(%s = 0ub%d_0)", rvalue(cell->getPort(ID::A)), width_a); const char *expr_y = lvalue(cell->getPort(ID::Y)); - definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a, width_y)); continue; } @@ -491,7 +491,7 @@ struct SmvWorker expr += stringf("bool(%s) ? %s : ", rvalue(sig_s[i]), rvalue(sig_b.extract(i*width, width))); expr += rvalue(sig_a); - definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort(ID::Y)), expr.c_str())); + definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort(ID::Y)), expr)); continue; } @@ -505,7 +505,7 @@ struct SmvWorker if (cell->type.in(ID($_BUF_), ID($_NOT_))) { string op = cell->type == ID($_NOT_) ? "!" : ""; - definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort(ID::Y)), op.c_str(), rvalue(cell->getPort(ID::A)))); + definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort(ID::Y)), op, rvalue(cell->getPort(ID::A)))); continue; } @@ -650,7 +650,7 @@ struct SmvWorker for (int k = GetSize(sig)-1; k >= 0; k--) bits += sig[k] == State::S1 ? '1' : '0'; - expr = stringf("0ub%d_%s", GetSize(bits), bits.c_str()) + expr; + expr = stringf("0ub%d_%s", GetSize(bits), bits) + expr; } else if (sigmap(SigBit(wire, i)) == SigBit(wire, i)) { @@ -683,36 +683,36 @@ struct SmvWorker } } - definitions.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str())); + definitions.push_back(stringf("%s := %s;", cid(wire->name), expr)); } if (!inputvars.empty()) { f << stringf(" IVAR\n"); for (const string &line : inputvars) - f << stringf(" %s\n", line.c_str()); + f << stringf(" %s\n", line); } if (!vars.empty()) { f << stringf(" VAR\n"); for (const string &line : vars) - f << stringf(" %s\n", line.c_str()); + f << stringf(" %s\n", line); } if (!definitions.empty()) { f << stringf(" DEFINE\n"); for (const string &line : definitions) - f << stringf(" %s\n", line.c_str()); + f << stringf(" %s\n", line); } if (!assignments.empty()) { f << stringf(" ASSIGN\n"); for (const string &line : assignments) - f << stringf(" %s\n", line.c_str()); + f << stringf(" %s\n", line); } if (!invarspecs.empty()) { for (const string &line : invarspecs) - f << stringf(" INVARSPEC %s\n", line.c_str()); + f << stringf(" INVARSPEC %s\n", line); } } }; @@ -756,7 +756,7 @@ struct SmvBackend : public Backend { if (args[argidx] == "-tpl" && argidx+1 < args.size()) { template_f.open(args[++argidx]); if (template_f.fail()) - log_error("Can't open template file `%s'.\n", args[argidx].c_str()); + log_error("Can't open template file `%s'.\n", args[argidx]); continue; } if (args[argidx] == "-verbose") { @@ -795,7 +795,7 @@ struct SmvBackend : public Backend { modules.erase(module); if (module == nullptr) - log_error("Module '%s' not found.\n", stmt[1].c_str()); + log_error("Module '%s' not found.\n", stmt[1]); *f << stringf("-- SMV description generated by %s\n", yosys_maybe_version()); diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc index e55db95e1..16458d647 100644 --- a/backends/spice/spice.cc +++ b/backends/spice/spice.cc @@ -51,16 +51,16 @@ static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg, if (s.wire->port_id) use_inames = true; if (s.wire->width > 1) - f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums).c_str(), s.offset); + f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums), s.offset); else - f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums).c_str()); + f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums)); } else { if (s == RTLIL::State::S0) - f << stringf(" %s", neg.c_str()); + f << stringf(" %s", neg); else if (s == RTLIL::State::S1) - f << stringf(" %s", pos.c_str()); + f << stringf(" %s", pos); else - f << stringf(" %s%d", ncpf.c_str(), nc_counter++); + f << stringf(" %s%d", ncpf, nc_counter++); } } @@ -119,7 +119,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De } } - f << stringf(" %s\n", spice_id2str(cell->type).c_str()); + f << stringf(" %s\n", spice_id2str(cell->type)); } for (auto &conn : module->connections()) @@ -127,7 +127,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De f << (buf == "DC" ? stringf("V%d", conn_counter++) : stringf("X%d", cell_counter++)); print_spice_net(f, conn.second.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); - f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf.c_str())); + f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf)); } } @@ -242,23 +242,23 @@ struct SpiceBackend : public Backend { ports.at(wire->port_id-1) = wire; } - *f << stringf(".SUBCKT %s", spice_id2str(module->name).c_str()); + *f << stringf(".SUBCKT %s", spice_id2str(module->name)); for (RTLIL::Wire *wire : ports) { log_assert(wire != NULL); if (wire->width > 1) { for (int i = 0; i < wire->width; i++) - *f << stringf(" %s.%d", spice_id2str(wire->name).c_str(), big_endian ? wire->width - 1 - i : i); + *f << stringf(" %s.%d", spice_id2str(wire->name), big_endian ? wire->width - 1 - i : i); } else - *f << stringf(" %s", spice_id2str(wire->name).c_str()); + *f << stringf(" %s", spice_id2str(wire->name)); } *f << stringf("\n"); print_spice_module(*f, module, design, neg, pos, buf, ncpf, big_endian, use_inames); - *f << stringf(".ENDS %s\n\n", spice_id2str(module->name).c_str()); + *f << stringf(".ENDS %s\n\n", spice_id2str(module->name)); } if (!top_module_name.empty()) { if (top_module == NULL) - log_error("Can't find top module `%s'!\n", top_module_name.c_str()); + log_error("Can't find top module `%s'!\n", top_module_name); print_spice_module(*f, top_module, design, neg, pos, buf, ncpf, big_endian, use_inames); *f << stringf("\n"); } diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 19be9914e..8d77160fd 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -28,12 +28,71 @@ #include "kernel/ff.h" #include "kernel/mem.h" #include "kernel/fmt.h" +#include "backends/verilog/verilog_backend.h" #include #include #include #include USING_YOSYS_NAMESPACE + +using namespace VERILOG_BACKEND; + +const pool VERILOG_BACKEND::verilog_keywords() { + static const pool res = { + // IEEE 1800-2017 Annex B + "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before", + "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle", + "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint", + "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker", + "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage", + "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually", + "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function", + "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies", + "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface", + "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint", + "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor", + "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive", + "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent", + "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat", + "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until", + "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify", + "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on", + "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1", + "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with", + "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while", + "wildcard", "wire", "with", "within", "wor", "xnor", "xor", + }; + return res; +} + +bool VERILOG_BACKEND::char_is_verilog_escaped(char c) { + if ('0' <= c && c <= '9') + return false; + if ('a' <= c && c <= 'z') + return false; + if ('A' <= c && c <= 'Z') + return false; + if (c == '_') + return false; + + return true; +} + +bool VERILOG_BACKEND::id_is_verilog_escaped(const std::string &str) { + if ('0' <= str[0] && str[0] <= '9') + return true; + + for (int i = 0; str[i]; i++) + if (char_is_verilog_escaped(str[i])) + return true; + + if (verilog_keywords().count(str)) + return true; + + return false; +} + PRIVATE_NAMESPACE_BEGIN bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs, noparallelcase; @@ -49,22 +108,30 @@ IdString initial_id; void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) { - const char *str = id.c_str(); - - if (*str == '$' && may_rename && !norename) - auto_name_map[id] = auto_name_counter++; - - if (str[0] != '\\' || str[1] != '_' || str[2] == 0) + auto it = id.begin(); + auto it_end = id.end(); + if (it == it_end) return; - for (int i = 2; str[i] != 0; i++) { - if (str[i] == '_' && str[i+1] == 0) - continue; - if (str[i] < '0' || str[i] > '9') + if (*it == '$' && may_rename && !norename) + auto_name_map[id] = auto_name_counter++; + + if (*it != '\\' || (it + 1) == it_end || *(it + 1) != '_' || (it + 2) == it_end) + return; + + std::string s; + it += 2; + while (it != it_end) { + char ch = *it; + if (ch == '_' && (it + 1) == it_end) + break; + if (ch < '0' || ch > '9') return; + s.push_back(ch); + ++it; } - int num = atoi(str+2); + int num = atoi(s.c_str()); if (num >= auto_name_offset) auto_name_offset = num + 1; } @@ -94,70 +161,25 @@ void reset_auto_counter(RTLIL::Module *module) if (verbose) for (auto it = auto_name_map.begin(); it != auto_name_map.end(); ++it) - log(" renaming `%s' to `%s_%0*d_'.\n", it->first.c_str(), auto_prefix.c_str(), auto_name_digits, auto_name_offset + it->second); + log(" renaming `%s' to `%s_%0*d_'.\n", it->first, auto_prefix, auto_name_digits, auto_name_offset + it->second); } std::string next_auto_id() { - return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_counter++); + return stringf("%s_%0*d_", auto_prefix, auto_name_digits, auto_name_offset + auto_name_counter++); } std::string id(RTLIL::IdString internal_id, bool may_rename = true) { const char *str = internal_id.c_str(); - bool do_escape = false; if (may_rename && auto_name_map.count(internal_id) != 0) - return stringf("%s_%0*d_", auto_prefix.c_str(), auto_name_digits, auto_name_offset + auto_name_map[internal_id]); + return stringf("%s_%0*d_", auto_prefix, auto_name_digits, auto_name_offset + auto_name_map[internal_id]); if (*str == '\\') str++; - if ('0' <= *str && *str <= '9') - do_escape = true; - - for (int i = 0; str[i]; i++) - { - if ('0' <= str[i] && str[i] <= '9') - continue; - if ('a' <= str[i] && str[i] <= 'z') - continue; - if ('A' <= str[i] && str[i] <= 'Z') - continue; - if (str[i] == '_') - continue; - do_escape = true; - break; - } - - static const pool keywords = { - // IEEE 1800-2017 Annex B - "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before", - "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle", - "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint", - "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker", - "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage", - "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually", - "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function", - "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies", - "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface", - "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint", - "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor", - "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive", - "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent", - "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat", - "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until", - "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify", - "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on", - "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1", - "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with", - "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while", - "wildcard", "wire", "with", "within", "wor", "xnor", "xor", - }; - if (keywords.count(str)) - do_escape = true; - - if (do_escape) + if (id_is_verilog_escaped(str)) return "\\" + std::string(str) + " "; return std::string(str); } @@ -313,19 +335,20 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o void dump_reg_init(std::ostream &f, SigSpec sig) { - Const initval; bool gotinit = false; + Const::Builder initval_bits(sig.size()); for (auto bit : active_sigmap(sig)) { if (active_initdata.count(bit)) { - initval.bits().push_back(active_initdata.at(bit)); + initval_bits.push_back(active_initdata.at(bit)); gotinit = true; } else { - initval.bits().push_back(State::Sx); + initval_bits.push_back(State::Sx); } } if (gotinit) { + Const initval = initval_bits.build(); f << " = "; dump_const(f, initval); } @@ -337,19 +360,19 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal); } else { if (chunk.width == chunk.wire->width && chunk.offset == 0) { - f << stringf("%s", id(chunk.wire->name).c_str()); + f << stringf("%s", id(chunk.wire->name)); } else if (chunk.width == 1) { if (chunk.wire->upto) - f << stringf("%s[%d]", id(chunk.wire->name).c_str(), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); + f << stringf("%s[%d]", id(chunk.wire->name), (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); else - f << stringf("%s[%d]", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset); + f << stringf("%s[%d]", id(chunk.wire->name), chunk.offset + chunk.wire->start_offset); } else { if (chunk.wire->upto) - f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(), + f << stringf("%s[%d:%d]", id(chunk.wire->name), (chunk.wire->width - (chunk.offset + chunk.width - 1) - 1) + chunk.wire->start_offset, (chunk.wire->width - chunk.offset - 1) + chunk.wire->start_offset); else - f << stringf("%s[%d:%d]", id(chunk.wire->name).c_str(), + f << stringf("%s[%d:%d]", id(chunk.wire->name), (chunk.offset + chunk.width - 1) + chunk.wire->start_offset, chunk.offset + chunk.wire->start_offset); } @@ -367,8 +390,9 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) dump_sigchunk(f, sig.as_chunk()); } else { f << stringf("{ "); - for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) { - if (it != sig.chunks().rbegin()) + auto chunks = sig.chunks(); + for (auto it = chunks.rbegin(); it != chunks.rend(); ++it) { + if (it != chunks.rbegin()) f << stringf(", "); dump_sigchunk(f, *it, true); } @@ -383,8 +407,9 @@ void dump_attributes(std::ostream &f, std::string indent, dictfirst == ID::single_bit_vector) continue; if (it->first == ID::init && regattr) continue; - f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str()); + f << stringf("%s" "%s %s", indent, as_comment ? "/*" : "(*", id(it->first)); f << stringf(" = "); if (modattr && (it->second == State::S0 || it->second == Const(0))) f << stringf(" 0 "); @@ -392,7 +417,7 @@ void dump_attributes(std::ostream &f, std::string indent, dictsecond, -1, 0, false, as_comment); - f << stringf(" %s%s", as_comment ? "*/" : "*)", term.c_str()); + f << stringf(" %s%s", as_comment ? "*/" : "*)", term); } } @@ -401,16 +426,16 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) dump_attributes(f, indent, wire->attributes, "\n", /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name)); #if 0 if (wire->port_input && !wire->port_output) - f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); + f << stringf("%s" "input %s", indent, reg_wires.count(wire->name) ? "reg " : ""); else if (!wire->port_input && wire->port_output) - f << stringf("%s" "output %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); + f << stringf("%s" "output %s", indent, reg_wires.count(wire->name) ? "reg " : ""); else if (wire->port_input && wire->port_output) - f << stringf("%s" "inout %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); + f << stringf("%s" "inout %s", indent, reg_wires.count(wire->name) ? "reg " : ""); else - f << stringf("%s" "%s ", indent.c_str(), reg_wires.count(wire->name) ? "reg" : "wire"); + f << stringf("%s" "%s ", indent, reg_wires.count(wire->name) ? "reg" : "wire"); if (wire->width != 1) f << stringf("[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset); - f << stringf("%s;\n", id(wire->name).c_str()); + f << stringf("%s;\n", id(wire->name)); #else // do not use Verilog-2k "output reg" syntax in Verilog export std::string range = ""; @@ -419,22 +444,25 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) range = stringf(" [%d:%d]", wire->start_offset, wire->width - 1 + wire->start_offset); else range = stringf(" [%d:%d]", wire->width - 1 + wire->start_offset, wire->start_offset); + } else { + if (wire->attributes.count(ID::single_bit_vector)) + range = stringf(" [%d:%d]", wire->start_offset, wire->start_offset); } if (wire->port_input && !wire->port_output) - f << stringf("%s" "input%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); + f << stringf("%s" "input%s %s;\n", indent, range, id(wire->name)); if (!wire->port_input && wire->port_output) - f << stringf("%s" "output%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); + f << stringf("%s" "output%s %s;\n", indent, range, id(wire->name)); if (wire->port_input && wire->port_output) - f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); + f << stringf("%s" "inout%s %s;\n", indent, range, id(wire->name)); if (reg_wires.count(wire->name)) { - f << stringf("%s" "reg%s %s", indent.c_str(), range.c_str(), id(wire->name).c_str()); + f << stringf("%s" "reg%s %s", indent, range, id(wire->name)); if (wire->attributes.count(ID::init)) { f << stringf(" = "); dump_const(f, wire->attributes.at(ID::init)); } f << stringf(";\n"); } else - f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); + f << stringf("%s" "wire%s %s;\n", indent, range, id(wire->name)); #endif } @@ -443,7 +471,7 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) std::string mem_id = id(mem.memid); dump_attributes(f, indent, mem.attributes); - f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), mem.width-1, mem_id.c_str(), mem.size+mem.start_offset-1, mem.start_offset); + f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent, mem.width-1, mem_id, mem.size+mem.start_offset-1, mem.start_offset); // for memory block make something like: // reg [7:0] memid [3:0]; @@ -454,7 +482,7 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) { if (extmem) { - std::string extmem_filename = stringf("%s-%d.mem", extmem_prefix.c_str(), extmem_counter++); + std::string extmem_filename = stringf("%s-%d.mem", extmem_prefix, extmem_counter++); std::string extmem_filename_esc; for (auto c : extmem_filename) @@ -472,11 +500,11 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) else extmem_filename_esc += c; } - f << stringf("%s" "initial $readmemb(\"%s\", %s);\n", indent.c_str(), extmem_filename_esc.c_str(), mem_id.c_str()); + f << stringf("%s" "initial $readmemb(\"%s\", %s);\n", indent, extmem_filename_esc, mem_id); std::ofstream extmem_f(extmem_filename, std::ofstream::trunc); if (extmem_f.fail()) - log_error("Can't open file `%s' for writing: %s\n", extmem_filename.c_str(), strerror(errno)); + log_error("Can't open file `%s' for writing: %s\n", extmem_filename, strerror(errno)); else { Const data = mem.get_init_data(); @@ -501,7 +529,7 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) } else { - f << stringf("%s" "initial begin\n", indent.c_str()); + f << stringf("%s" "initial begin\n", indent); for (auto &init : mem.inits) { int words = GetSize(init.data) / mem.width; int start = init.addr.as_int(); @@ -518,16 +546,16 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) j++, width++; if (width == mem.width) { - f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i + start); + f << stringf("%s" " %s[%d] = ", indent, mem_id, i + start); } else { - f << stringf("%s" " %s[%d][%d:%d] = ", indent.c_str(), mem_id.c_str(), i + start, j, start_j); + f << stringf("%s" " %s[%d][%d:%d] = ", indent, mem_id, i + start, j, start_j); } dump_const(f, init.data.extract(i*mem.width+start_j, width)); f << stringf(";\n"); } } } - f << stringf("%s" "end\n", indent.c_str()); + f << stringf("%s" "end\n", indent); } } @@ -548,11 +576,11 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) { std::ostringstream os; dump_sigspec(os, port.clk); - clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "pos" : "neg", os.str().c_str()); + clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "pos" : "neg", os.str()); if (port.arst != State::S0) { std::ostringstream os2; dump_sigspec(os2, port.arst); - clk_domain_str += stringf(", posedge %s", os2.str().c_str()); + clk_domain_str += stringf(", posedge %s", os2.str()); clk_to_arst_cond[clk_domain_str] = os2.str(); } } @@ -577,13 +605,13 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) // if (rd_en) temp_id <= array_reg[r_addr]; // assign r_data = temp_id; std::string temp_id = next_auto_id(); - lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.data.size() - 1, temp_id.c_str()) ); + lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.data.size() - 1, temp_id) ); bool has_indent = false; if (port.arst != State::S0) { std::ostringstream os; - os << stringf("%s <= ", temp_id.c_str()); + os << stringf("%s <= ", temp_id); dump_sigspec(os, port.arst_value); os << ";\n"; clk_to_arst_body[clk_domain_str].push_back(os.str()); @@ -596,7 +624,7 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) os << stringf(")\n"); clk_to_lof_body[clk_domain_str].push_back(os.str()); std::ostringstream os2; - os2 << stringf("%s" "%s <= ", indent.c_str(), temp_id.c_str()); + os2 << stringf("%s" "%s <= ", indent, temp_id); dump_sigspec(os2, port.srst_value); os2 << ";\n"; clk_to_lof_body[clk_domain_str].push_back(os2.str()); @@ -628,7 +656,7 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) os << temp_id; if (port.wide_log2) os << stringf("[%d:%d]", (sub + 1) * mem.width - 1, sub * mem.width); - os << stringf(" <= %s[", mem_id.c_str()); + os << stringf(" <= %s[", mem_id); dump_sigspec(os, addr); os << stringf("];\n"); clk_to_lof_body[clk_domain_str].push_back(os.str()); @@ -703,7 +731,7 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) std::ostringstream os2; if (has_indent) os2 << indent; - os2 << stringf("%s" "%s <= ", indent.c_str(), temp_id.c_str()); + os2 << stringf("%s" "%s <= ", indent, temp_id); dump_sigspec(os2, port.srst_value); os2 << ";\n"; clk_to_lof_body[clk_domain_str].push_back(os2.str()); @@ -716,14 +744,14 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) { std::ostringstream os; dump_sigspec(os, port.init_value); - std::string line = stringf("initial %s = %s;\n", temp_id.c_str(), os.str().c_str()); + std::string line = stringf("initial %s = %s;\n", temp_id, os.str()); clk_to_lof_body[""].push_back(line); } { std::ostringstream os; dump_sigspec(os, port.data); - std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str()); + std::string line = stringf("assign %s = %s;\n", os.str(), temp_id); clk_to_lof_body[""].push_back(line); } } @@ -735,11 +763,11 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) // temp_id <= r_addr; // assign r_data = array_reg[temp_id]; std::string temp_id = next_auto_id(); - lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.addr.size() - 1 - port.wide_log2, temp_id.c_str()) ); + lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.addr.size() - 1 - port.wide_log2, temp_id) ); { std::ostringstream os; dump_sigspec(os, port.addr.extract_end(port.wide_log2)); - std::string line = stringf("%s <= %s;\n", temp_id.c_str(), os.str().c_str()); + std::string line = stringf("%s <= %s;\n", temp_id, os.str()); clk_to_lof_body[clk_domain_str].push_back(line); } for (int sub = 0; sub < (1 << port.wide_log2); sub++) @@ -747,11 +775,12 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) std::ostringstream os; os << "assign "; dump_sigspec(os, port.data.extract(sub * mem.width, mem.width)); - os << stringf(" = %s[", mem_id.c_str());; + os << stringf(" = %s[", mem_id);; if (port.wide_log2) { - Const addr_lo; + Const::Builder addr_lo_builder(port.wide_log2); for (int i = 0; i < port.wide_log2; i++) - addr_lo.bits().push_back(State(sub >> i & 1)); + addr_lo_builder.push_back(State(sub >> i & 1)); + Const addr_lo = addr_lo_builder.build(); os << "{"; os << temp_id; os << ", "; @@ -774,7 +803,7 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) std::ostringstream os, os2; dump_sigspec(os, port.data.extract(sub * mem.width, mem.width)); dump_sigspec(os2, addr); - std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str()); + std::string line = stringf("assign %s = %s[%s];\n", os.str(), mem_id, os2.str()); clk_to_lof_body[""].push_back(line); } } @@ -823,11 +852,11 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) } if (root.clk_enable) { - f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", root.clk_polarity ? "pos" : "neg"); + f << stringf("%s" "always%s @(%sedge ", indent, systemverilog ? "_ff" : "", root.clk_polarity ? "pos" : "neg"); dump_sigspec(f, root.clk); f << ") begin\n"; } else { - f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_latch" : " @*"); + f << stringf("%s" "always%s begin\n", indent, systemverilog ? "_latch" : " @*"); } for (int pidx = 0; pidx < GetSize(mem.wr_ports); pidx++) @@ -861,15 +890,15 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) if (wen_bit == State::S0) continue; - f << stringf("%s%s", indent.c_str(), indent.c_str()); + f << stringf("%s%s", indent, indent); if (wen_bit != State::S1) { f << stringf("if ("); dump_sigspec(f, wen_bit); f << stringf(")\n"); - f << stringf("%s%s%s", indent.c_str(), indent.c_str(), indent.c_str()); + f << stringf("%s%s%s", indent, indent, indent); } - f << stringf("%s[", mem_id.c_str()); + f << stringf("%s[", mem_id); dump_sigspec(f, addr); if (width == GetSize(port.en)) f << stringf("] <= "); @@ -881,7 +910,7 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) } } - f << stringf("%s" "end\n", indent.c_str()); + f << stringf("%s" "end\n", indent); } // Output Verilog that looks something like this: // reg [..] _3_; @@ -904,7 +933,7 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) // the reg ... definitions for(auto ® : lof_reg_declarations) { - f << stringf("%s" "%s", indent.c_str(), reg.c_str()); + f << stringf("%s" "%s", indent, reg); } // the block of expressions by clock domain for(auto &pair : clk_to_lof_body) @@ -913,27 +942,27 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) std::vector lof_lines = pair.second; if( clk_domain != "") { - f << stringf("%s" "always%s @(%s) begin\n", indent.c_str(), systemverilog ? "_ff" : "", clk_domain.c_str()); + f << stringf("%s" "always%s @(%s) begin\n", indent, systemverilog ? "_ff" : "", clk_domain); bool has_arst = clk_to_arst_cond.count(clk_domain) != 0; if (has_arst) { - f << stringf("%s%s" "if (%s) begin\n", indent.c_str(), indent.c_str(), clk_to_arst_cond[clk_domain].c_str()); + f << stringf("%s%s" "if (%s) begin\n", indent, indent, clk_to_arst_cond[clk_domain]); for(auto &line : clk_to_arst_body[clk_domain]) - f << stringf("%s%s%s" "%s", indent.c_str(), indent.c_str(), indent.c_str(), line.c_str()); - f << stringf("%s%s" "end else begin\n", indent.c_str(), indent.c_str()); + f << stringf("%s%s%s" "%s", indent, indent, indent, line); + f << stringf("%s%s" "end else begin\n", indent, indent); for(auto &line : lof_lines) - f << stringf("%s%s%s" "%s", indent.c_str(), indent.c_str(), indent.c_str(), line.c_str()); - f << stringf("%s%s" "end\n", indent.c_str(), indent.c_str()); + f << stringf("%s%s%s" "%s", indent, indent, indent, line); + f << stringf("%s%s" "end\n", indent, indent); } else { for(auto &line : lof_lines) - f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str()); + f << stringf("%s%s" "%s", indent, indent, line); } - f << stringf("%s" "end\n", indent.c_str()); + f << stringf("%s" "end\n", indent); } else { // the non-clocked assignments for(auto &line : lof_lines) - f << stringf("%s" "%s", indent.c_str(), line.c_str()); + f << stringf("%s" "%s", indent, line); } } } @@ -950,7 +979,7 @@ void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, b std::string cellname(RTLIL::Cell *cell) { - if (!norename && cell->name[0] == '$' && RTLIL::builtin_ff_cell_types().count(cell->type) && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_))) + if (!norename && cell->name[0] == '$' && cell->is_builtin_ff() && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_))) { RTLIL::SigSpec sig = cell->getPort(ID::Q); if (GetSize(sig) != 1 || sig.is_fully_const()) @@ -986,9 +1015,9 @@ no_special_reg_name: void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = %s ", op.c_str()); + f << stringf(" = %s ", op); dump_attributes(f, "", cell->attributes, " "); dump_cell_expr_port(f, cell, "A", true); f << stringf(";\n"); @@ -996,11 +1025,11 @@ void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell, std::string op) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); dump_cell_expr_port(f, cell, "A", true); - f << stringf(" %s ", op.c_str()); + f << stringf(" %s ", op); dump_attributes(f, "", cell->attributes, " "); dump_cell_expr_port(f, cell, "B", true); f << stringf(";\n"); @@ -1012,7 +1041,7 @@ void dump_cell_expr_print(std::ostream &f, std::string indent, const RTLIL::Cell fmt.parse_rtlil(cell); std::vector args = fmt.emit_verilog(); - f << stringf("%s" "$write(", indent.c_str()); + f << stringf("%s" "$write(", indent); bool first = true; for (auto &arg : args) { if (first) { @@ -1046,19 +1075,19 @@ void dump_cell_expr_check(std::ostream &f, std::string indent, const RTLIL::Cell std::string flavor = cell->getParam(ID(FLAVOR)).decode_string(); std::string label = ""; if (cell->name.isPublic()) { - label = stringf("%s: ", id(cell->name).c_str()); + label = stringf("%s: ", id(cell->name)); } if (flavor == "assert") - f << stringf("%s" "%s" "assert (", indent.c_str(), label.c_str()); + f << stringf("%s" "%s" "assert (", indent, label); else if (flavor == "assume") - f << stringf("%s" "%s" "assume (", indent.c_str(), label.c_str()); + f << stringf("%s" "%s" "assume (", indent, label); else if (flavor == "live") - f << stringf("%s" "%s" "assert (eventually ", indent.c_str(), label.c_str()); + f << stringf("%s" "%s" "assert (eventually ", indent, label); else if (flavor == "fair") - f << stringf("%s" "%s" "assume (eventually ", indent.c_str(), label.c_str()); + f << stringf("%s" "%s" "assume (eventually ", indent, label); else if (flavor == "cover") - f << stringf("%s" "%s" "cover (", indent.c_str(), label.c_str()); + f << stringf("%s" "%s" "cover (", indent, label); else log_abort(); dump_sigspec(f, cell->getPort(ID::A)); @@ -1068,7 +1097,7 @@ void dump_cell_expr_check(std::ostream &f, std::string indent, const RTLIL::Cell bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) { if (cell->type == ID($_NOT_)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); f << stringf("~"); @@ -1079,7 +1108,34 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type.in(ID($_BUF_), ID($buf))) { - f << stringf("%s" "assign ", indent.c_str()); + if (cell->type == ID($buf) && cell->getPort(ID::A).has_const(State::Sz)) { + RTLIL::SigSpec a = cell->getPort(ID::A); + RTLIL::SigSpec y = cell->getPort(ID::Y); + a.extend_u0(GetSize(y)); + + if (a.has_const(State::Sz)) { + SigSpec new_a; + SigSpec new_y; + for (int i = 0; i < GetSize(a); ++i) { + SigBit b = a[i]; + if (b == State::Sz) + continue; + new_a.append(b); + new_y.append(y[i]); + } + a = std::move(new_a); + y = std::move(new_y); + } + if (!y.empty()) { + f << stringf("%s" "assign ", indent); + dump_sigspec(f, y); + f << stringf(" = "); + dump_sigspec(f, a); + f << stringf(";\n"); + } + return true; + } + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); dump_cell_expr_port(f, cell, "A", false); @@ -1088,7 +1144,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); if (cell->type.in(ID($_NAND_), ID($_NOR_), ID($_XNOR_))) @@ -1113,7 +1169,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type == ID($_MUX_)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); dump_cell_expr_port(f, cell, "S", false); @@ -1127,7 +1183,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type == ID($_NMUX_)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = !("); dump_cell_expr_port(f, cell, "S", false); @@ -1141,13 +1197,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type.in(ID($_AOI3_), ID($_OAI3_))) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = ~(("); dump_cell_expr_port(f, cell, "A", false); - f << stringf(cell->type == ID($_AOI3_) ? " & " : " | "); + f << (cell->type == ID($_AOI3_) ? " & " : " | "); dump_cell_expr_port(f, cell, "B", false); - f << stringf(cell->type == ID($_AOI3_) ? ") |" : ") &"); + f << (cell->type == ID($_AOI3_) ? ") |" : ") &"); dump_attributes(f, "", cell->attributes, " "); f << stringf(" "); dump_cell_expr_port(f, cell, "C", false); @@ -1156,17 +1212,17 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (cell->type.in(ID($_AOI4_), ID($_OAI4_))) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = ~(("); dump_cell_expr_port(f, cell, "A", false); - f << stringf(cell->type == ID($_AOI4_) ? " & " : " | "); + f << (cell->type == ID($_AOI4_) ? " & " : " | "); dump_cell_expr_port(f, cell, "B", false); - f << stringf(cell->type == ID($_AOI4_) ? ") |" : ") &"); + f << (cell->type == ID($_AOI4_) ? ") |" : ") &"); dump_attributes(f, "", cell->attributes, " "); f << stringf(" ("); dump_cell_expr_port(f, cell, "C", false); - f << stringf(cell->type == ID($_AOI4_) ? " & " : " | "); + f << (cell->type == ID($_AOI4_) ? " & " : " | "); dump_cell_expr_port(f, cell, "D", false); f << stringf("));\n"); return true; @@ -1241,32 +1297,32 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) int size_max = std::max(size_a, std::max(size_b, size_y)); // intentionally one wider than maximum width - f << stringf("%s" "wire [%d:0] %s, %s, %s;\n", indent.c_str(), size_max, buf_a.c_str(), buf_b.c_str(), buf_num.c_str()); - f << stringf("%s" "assign %s = ", indent.c_str(), buf_a.c_str()); + f << stringf("%s" "wire [%d:0] %s, %s, %s;\n", indent, size_max, buf_a, buf_b, buf_num); + f << stringf("%s" "assign %s = ", indent, buf_a); dump_cell_expr_port(f, cell, "A", true); f << stringf(";\n"); - f << stringf("%s" "assign %s = ", indent.c_str(), buf_b.c_str()); + f << stringf("%s" "assign %s = ", indent, buf_b); dump_cell_expr_port(f, cell, "B", true); f << stringf(";\n"); - f << stringf("%s" "assign %s = ", indent.c_str(), buf_num.c_str()); + f << stringf("%s" "assign %s = ", indent, buf_num); f << stringf("("); dump_sigspec(f, sig_a.extract(sig_a.size()-1)); f << stringf(" == "); dump_sigspec(f, sig_b.extract(sig_b.size()-1)); f << stringf(") || "); dump_sigspec(f, sig_a); - f << stringf(" == 0 ? %s : ", buf_a.c_str()); - f << stringf("$signed(%s - (", buf_a.c_str()); + f << stringf(" == 0 ? %s : ", buf_a); + f << stringf("$signed(%s - (", buf_a); dump_sigspec(f, sig_b.extract(sig_b.size()-1)); - f << stringf(" ? %s + 1 : %s - 1));\n", buf_b.c_str(), buf_b.c_str()); + f << stringf(" ? %s + 1 : %s - 1));\n", buf_b, buf_b); - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = $signed(%s) / ", buf_num.c_str()); + f << stringf(" = $signed(%s) / ", buf_num); dump_attributes(f, "", cell->attributes, " "); - f << stringf("$signed(%s);\n", buf_b.c_str()); + f << stringf("$signed(%s);\n", buf_b); return true; } else { // same as truncating division @@ -1285,22 +1341,22 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) SigSpec sig_b = cell->getPort(ID::B); std::string temp_id = next_auto_id(); - f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str()); + f << stringf("%s" "wire [%d:0] %s = ", indent, GetSize(cell->getPort(ID::A))-1, temp_id); dump_cell_expr_port(f, cell, "A", true); f << stringf(" %% "); dump_attributes(f, "", cell->attributes, " "); dump_cell_expr_port(f, cell, "B", true); f << stringf(";\n"); - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = ("); dump_sigspec(f, sig_a.extract(sig_a.size()-1)); f << stringf(" == "); dump_sigspec(f, sig_b.extract(sig_b.size()-1)); - f << stringf(") || %s == 0 ? $signed(%s) : ", temp_id.c_str(), temp_id.c_str()); + f << stringf(") || %s == 0 ? $signed(%s) : ", temp_id, temp_id); dump_cell_expr_port(f, cell, "B", true); - f << stringf(" + $signed(%s);\n", temp_id.c_str()); + f << stringf(" + $signed(%s);\n", temp_id); return true; } else { // same as truncating modulo @@ -1311,7 +1367,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($shift)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); if (cell->getParam(ID::B_SIGNED).as_bool()) @@ -1339,13 +1395,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($shiftx)) { std::string temp_id = next_auto_id(); - f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str()); + f << stringf("%s" "wire [%d:0] %s = ", indent, GetSize(cell->getPort(ID::A))-1, temp_id); dump_sigspec(f, cell->getPort(ID::A)); f << stringf(";\n"); - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = %s[", temp_id.c_str()); + f << stringf(" = %s[", temp_id); if (cell->getParam(ID::B_SIGNED).as_bool()) f << stringf("$signed("); dump_sigspec(f, cell->getPort(ID::B)); @@ -1358,7 +1414,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($mux)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); dump_sigspec(f, cell->getPort(ID::S)); @@ -1377,51 +1433,51 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) int s_width = cell->getPort(ID::S).size(); std::string func_name = cellname(cell); - f << stringf("%s" "function [%d:0] %s;\n", indent.c_str(), width-1, func_name.c_str()); - f << stringf("%s" " input [%d:0] a;\n", indent.c_str(), width-1); - f << stringf("%s" " input [%d:0] b;\n", indent.c_str(), s_width*width-1); - f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1); + f << stringf("%s" "function [%d:0] %s;\n", indent, width-1, func_name); + f << stringf("%s" " input [%d:0] a;\n", indent, width-1); + f << stringf("%s" " input [%d:0] b;\n", indent, s_width*width-1); + f << stringf("%s" " input [%d:0] s;\n", indent, s_width-1); dump_attributes(f, indent + " ", cell->attributes); if (noparallelcase) - f << stringf("%s" " case (s)\n", indent.c_str()); + f << stringf("%s" " case (s)\n", indent); else { if (!noattr) - f << stringf("%s" " (* parallel_case *)\n", indent.c_str()); - f << stringf("%s" " casez (s)", indent.c_str()); - f << stringf(noattr ? " // synopsys parallel_case\n" : "\n"); + f << stringf("%s" " (* parallel_case *)\n", indent); + f << stringf("%s" " casez (s)", indent); + f << (noattr ? " // synopsys parallel_case\n" : "\n"); } for (int i = 0; i < s_width; i++) { - f << stringf("%s" " %d'b", indent.c_str(), s_width); + f << stringf("%s" " %d'b", indent, s_width); for (int j = s_width-1; j >= 0; j--) f << stringf("%c", j == i ? '1' : noparallelcase ? '0' : '?'); f << stringf(":\n"); - f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width); + f << stringf("%s" " %s = b[%d:%d];\n", indent, func_name, (i+1)*width-1, i*width); } if (noparallelcase) { - f << stringf("%s" " %d'b", indent.c_str(), s_width); + f << stringf("%s" " %d'b", indent, s_width); for (int j = s_width-1; j >= 0; j--) f << '0'; f << stringf(":\n"); } else - f << stringf("%s" " default:\n", indent.c_str()); - f << stringf("%s" " %s = a;\n", indent.c_str(), func_name.c_str()); + f << stringf("%s" " default:\n", indent); + f << stringf("%s" " %s = a;\n", indent, func_name); if (noparallelcase) { - f << stringf("%s" " default:\n", indent.c_str()); - f << stringf("%s" " %s = %d'bx;\n", indent.c_str(), func_name.c_str(), width); + f << stringf("%s" " default:\n", indent); + f << stringf("%s" " %s = %d'bx;\n", indent, func_name, width); } - f << stringf("%s" " endcase\n", indent.c_str()); - f << stringf("%s" "endfunction\n", indent.c_str()); + f << stringf("%s" " endcase\n", indent); + f << stringf("%s" "endfunction\n", indent); - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); - f << stringf(" = %s(", func_name.c_str()); + f << stringf(" = %s(", func_name); dump_sigspec(f, cell->getPort(ID::A)); f << stringf(", "); dump_sigspec(f, cell->getPort(ID::B)); @@ -1433,7 +1489,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($tribuf)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); dump_sigspec(f, cell->getPort(ID::EN)); @@ -1445,7 +1501,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($slice)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); dump_sigspec(f, cell->getPort(ID::A)); @@ -1455,7 +1511,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($concat)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = { "); dump_sigspec(f, cell->getPort(ID::B)); @@ -1467,7 +1523,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($lut)) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); dump_const(f, cell->parameters.at(ID::LUT)); @@ -1478,7 +1534,30 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - if (RTLIL::builtin_ff_cell_types().count(cell->type)) + if (cell->type == ID($input_port)) + return true; + + if (cell->type == ID($connect)) + { + int width = cell->getParam(ID::WIDTH).as_int() ; + if (width == 1) { + f << stringf("%s" "tran(", indent); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(", "); + dump_sigspec(f, cell->getPort(ID::B)); + f << stringf(");\n"); + } else { + auto tran_id = next_auto_id(); + f << stringf("%s" "tran %s[%d:0](", indent, tran_id, width - 1); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(", "); + dump_sigspec(f, cell->getPort(ID::B)); + f << stringf(");\n"); + } + return true; + } + + if (cell->is_builtin_ff()) { FfData ff(nullptr, cell); @@ -1491,9 +1570,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (!out_is_reg_wire) { if (ff.width == 1) - f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str()); + f << stringf("%s" "reg %s", indent, reg_name); else - f << stringf("%s" "reg [%d:0] %s", indent.c_str(), ff.width-1, reg_name.c_str()); + f << stringf("%s" "reg [%d:0] %s", indent, ff.width-1, reg_name); dump_reg_init(f, ff.sig_q); f << ";\n"; } @@ -1508,7 +1587,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) Const val_arst, val_srst; std::string reg_bit_name, sig_set_name, sig_clr_name, sig_arst_name, sig_aload_name; if (chunky) { - reg_bit_name = stringf("%s[%d]", reg_name.c_str(), i); + reg_bit_name = stringf("%s[%d]", reg_name, i); if (ff.has_gclk || ff.has_clk) sig_d = ff.sig_d[i]; if (ff.has_aload) @@ -1529,14 +1608,14 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (ff.sig_set[i].wire == NULL) { sig_set_name = next_auto_id(); - f << stringf("%s" "wire %s = ", indent.c_str(), sig_set_name.c_str()); + f << stringf("%s" "wire %s = ", indent, sig_set_name); dump_const(f, ff.sig_set[i].data); f << stringf(";\n"); } if (ff.sig_clr[i].wire == NULL) { sig_clr_name = next_auto_id(); - f << stringf("%s" "wire %s = ", indent.c_str(), sig_clr_name.c_str()); + f << stringf("%s" "wire %s = ", indent, sig_clr_name); dump_const(f, ff.sig_clr[i].data); f << stringf(";\n"); } @@ -1544,7 +1623,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (ff.sig_arst[0].wire == NULL) { sig_arst_name = next_auto_id(); - f << stringf("%s" "wire %s = ", indent.c_str(), sig_arst_name.c_str()); + f << stringf("%s" "wire %s = ", indent, sig_arst_name); dump_const(f, ff.sig_arst[0].data); f << stringf(";\n"); } @@ -1552,7 +1631,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (ff.sig_aload[0].wire == NULL) { sig_aload_name = next_auto_id(); - f << stringf("%s" "wire %s = ", indent.c_str(), sig_aload_name.c_str()); + f << stringf("%s" "wire %s = ", indent, sig_aload_name); dump_const(f, ff.sig_aload[0].data); f << stringf(";\n"); } @@ -1563,90 +1642,90 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (ff.has_clk) { // FFs. - f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", ff.pol_clk ? "pos" : "neg"); + f << stringf("%s" "always%s @(%sedge ", indent, systemverilog ? "_ff" : "", ff.pol_clk ? "pos" : "neg"); dump_sigspec(f, ff.sig_clk); if (ff.has_sr) { f << stringf(", %sedge ", ff.pol_set ? "pos" : "neg"); if (ff.sig_set[i].wire == NULL) - f << stringf("%s", sig_set_name.c_str()); + f << stringf("%s", sig_set_name); else dump_sigspec(f, ff.sig_set[i]); f << stringf(", %sedge ", ff.pol_clr ? "pos" : "neg"); if (ff.sig_clr[i].wire == NULL) - f << stringf("%s", sig_clr_name.c_str()); + f << stringf("%s", sig_clr_name); else dump_sigspec(f, ff.sig_clr[i]); } else if (ff.has_arst) { f << stringf(", %sedge ", ff.pol_arst ? "pos" : "neg"); if (ff.sig_arst[0].wire == NULL) - f << stringf("%s", sig_arst_name.c_str()); + f << stringf("%s", sig_arst_name); else dump_sigspec(f, ff.sig_arst); } else if (ff.has_aload) { f << stringf(", %sedge ", ff.pol_aload ? "pos" : "neg"); if (ff.sig_aload[0].wire == NULL) - f << stringf("%s", sig_aload_name.c_str()); + f << stringf("%s", sig_aload_name); else dump_sigspec(f, ff.sig_aload); } f << stringf(")\n"); - f << stringf("%s" " ", indent.c_str()); + f << stringf("%s" " ", indent); if (ff.has_sr) { f << stringf("if (%s", ff.pol_clr ? "" : "!"); if (ff.sig_clr[i].wire == NULL) - f << stringf("%s", sig_clr_name.c_str()); + f << stringf("%s", sig_clr_name); else dump_sigspec(f, ff.sig_clr[i]); - f << stringf(") %s <= 1'b0;\n", reg_bit_name.c_str()); - f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!"); + f << stringf(") %s <= 1'b0;\n", reg_bit_name); + f << stringf("%s" " else if (%s", indent, ff.pol_set ? "" : "!"); if (ff.sig_set[i].wire == NULL) - f << stringf("%s", sig_set_name.c_str()); + f << stringf("%s", sig_set_name); else dump_sigspec(f, ff.sig_set[i]); - f << stringf(") %s <= 1'b1;\n", reg_bit_name.c_str()); - f << stringf("%s" " else ", indent.c_str()); + f << stringf(") %s <= 1'b1;\n", reg_bit_name); + f << stringf("%s" " else ", indent); } else if (ff.has_arst) { f << stringf("if (%s", ff.pol_arst ? "" : "!"); if (ff.sig_arst[0].wire == NULL) - f << stringf("%s", sig_arst_name.c_str()); + f << stringf("%s", sig_arst_name); else dump_sigspec(f, ff.sig_arst); - f << stringf(") %s <= ", reg_bit_name.c_str()); + f << stringf(") %s <= ", reg_bit_name); dump_sigspec(f, val_arst); f << stringf(";\n"); - f << stringf("%s" " else ", indent.c_str()); + f << stringf("%s" " else ", indent); } else if (ff.has_aload) { f << stringf("if (%s", ff.pol_aload ? "" : "!"); if (ff.sig_aload[0].wire == NULL) - f << stringf("%s", sig_aload_name.c_str()); + f << stringf("%s", sig_aload_name); else dump_sigspec(f, ff.sig_aload); - f << stringf(") %s <= ", reg_bit_name.c_str()); + f << stringf(") %s <= ", reg_bit_name); dump_sigspec(f, sig_ad); f << stringf(";\n"); - f << stringf("%s" " else ", indent.c_str()); + f << stringf("%s" " else ", indent); } if (ff.has_srst && ff.has_ce && ff.ce_over_srst) { f << stringf("if (%s", ff.pol_ce ? "" : "!"); dump_sigspec(f, ff.sig_ce); f << stringf(")\n"); - f << stringf("%s" " if (%s", indent.c_str(), ff.pol_srst ? "" : "!"); + f << stringf("%s" " if (%s", indent, ff.pol_srst ? "" : "!"); dump_sigspec(f, ff.sig_srst); - f << stringf(") %s <= ", reg_bit_name.c_str()); + f << stringf(") %s <= ", reg_bit_name); dump_sigspec(f, val_srst); f << stringf(";\n"); - f << stringf("%s" " else ", indent.c_str()); + f << stringf("%s" " else ", indent); } else { if (ff.has_srst) { f << stringf("if (%s", ff.pol_srst ? "" : "!"); dump_sigspec(f, ff.sig_srst); - f << stringf(") %s <= ", reg_bit_name.c_str()); + f << stringf(") %s <= ", reg_bit_name); dump_sigspec(f, val_srst); f << stringf(";\n"); - f << stringf("%s" " else ", indent.c_str()); + f << stringf("%s" " else ", indent); } if (ff.has_ce) { f << stringf("if (%s", ff.pol_ce ? "" : "!"); @@ -1655,38 +1734,38 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } } - f << stringf("%s <= ", reg_bit_name.c_str()); + f << stringf("%s <= ", reg_bit_name); dump_sigspec(f, sig_d); f << stringf(";\n"); } else { // Latches. - f << stringf("%s" "always%s\n", indent.c_str(), systemverilog ? "_latch" : " @*"); + f << stringf("%s" "always%s\n", indent, systemverilog ? "_latch" : " @*"); - f << stringf("%s" " ", indent.c_str()); + f << stringf("%s" " ", indent); if (ff.has_sr) { f << stringf("if (%s", ff.pol_clr ? "" : "!"); dump_sigspec(f, ff.sig_clr[i]); - f << stringf(") %s = 1'b0;\n", reg_bit_name.c_str()); - f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!"); + f << stringf(") %s = 1'b0;\n", reg_bit_name); + f << stringf("%s" " else if (%s", indent, ff.pol_set ? "" : "!"); dump_sigspec(f, ff.sig_set[i]); - f << stringf(") %s = 1'b1;\n", reg_bit_name.c_str()); + f << stringf(") %s = 1'b1;\n", reg_bit_name); if (ff.has_aload) - f << stringf("%s" " else ", indent.c_str()); + f << stringf("%s" " else ", indent); } else if (ff.has_arst) { f << stringf("if (%s", ff.pol_arst ? "" : "!"); dump_sigspec(f, ff.sig_arst); - f << stringf(") %s = ", reg_bit_name.c_str()); + f << stringf(") %s = ", reg_bit_name); dump_sigspec(f, val_arst); f << stringf(";\n"); if (ff.has_aload) - f << stringf("%s" " else ", indent.c_str()); + f << stringf("%s" " else ", indent); } if (ff.has_aload) { f << stringf("if (%s", ff.pol_aload ? "" : "!"); dump_sigspec(f, ff.sig_aload); - f << stringf(") %s = ", reg_bit_name.c_str()); + f << stringf(") %s = ", reg_bit_name); dump_sigspec(f, sig_ad); f << stringf(";\n"); } @@ -1694,9 +1773,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } if (!out_is_reg_wire) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, ff.sig_q); - f << stringf(" = %s;\n", reg_name.c_str()); + f << stringf(" = %s;\n", reg_name); } return true; @@ -1704,7 +1783,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type.in(ID($assert), ID($assume), ID($cover))) { - f << stringf("%s" "always%s if (", indent.c_str(), systemverilog ? "_comb" : " @*"); + f << stringf("%s" "always%s if (", indent, systemverilog ? "_comb" : " @*"); dump_sigspec(f, cell->getPort(ID::EN)); f << stringf(") %s(", cell->type.c_str()+1); dump_sigspec(f, cell->getPort(ID::A)); @@ -1714,7 +1793,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type.in(ID($specify2), ID($specify3))) { - f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str()); + f << stringf("%s" "specify\n%s ", indent, indent); SigSpec en = cell->getPort(ID::EN); if (en != State::S1) { @@ -1766,16 +1845,16 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) decimal = bak_decimal; - f << stringf("%s" "endspecify\n", indent.c_str()); + f << stringf("%s" "endspecify\n", indent); return true; } if (cell->type == ID($specrule)) { - f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str()); + f << stringf("%s" "specify\n%s ", indent, indent); IdString spec_type = cell->getParam(ID::TYPE).decode_string(); - f << stringf("%s(", spec_type.c_str()); + f << stringf("%s(", spec_type); if (cell->getParam(ID::SRC_PEN).as_bool()) f << (cell->getParam(ID::SRC_POL).as_bool() ? "posedge ": "negedge "); @@ -1818,7 +1897,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << ");\n"; decimal = bak_decimal; - f << stringf("%s" "endspecify\n", indent.c_str()); + f << stringf("%s" "endspecify\n", indent); return true; } @@ -1828,9 +1907,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->getParam(ID::TRG_ENABLE).as_bool()) return true; - f << stringf("%s" "always @*\n", indent.c_str()); + f << stringf("%s" "always @*\n", indent); - f << stringf("%s" " if (", indent.c_str()); + f << stringf("%s" " if (", indent); dump_sigspec(f, cell->getPort(ID::EN)); f << stringf(")\n"); @@ -1844,9 +1923,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->getParam(ID::TRG_ENABLE).as_bool()) return true; - f << stringf("%s" "always @*\n", indent.c_str()); + f << stringf("%s" "always @*\n", indent); - f << stringf("%s" " if (", indent.c_str()); + f << stringf("%s" " if (", indent); dump_sigspec(f, cell->getPort(ID::EN)); f << stringf(") begin\n"); @@ -1855,18 +1934,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) Fmt fmt; fmt.parse_rtlil(cell); if (!fmt.parts.empty()) { - f << stringf("%s" " if (!", indent.c_str()); + f << stringf("%s" " if (!", indent); dump_sigspec(f, cell->getPort(ID::A)); f << stringf(")\n"); dump_cell_expr_print(f, indent + " ", cell); } } else { - f << stringf("%s" " /* message omitted */\n", indent.c_str()); + f << stringf("%s" " /* message omitted */\n", indent); } dump_cell_expr_check(f, indent + " ", cell); - f << stringf("%s" " end\n", indent.c_str()); + f << stringf("%s" " end\n", indent); return true; } @@ -1895,26 +1974,26 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) } dump_attributes(f, indent, cell->attributes); - f << stringf("%s" "%s", indent.c_str(), id(cell->type, false).c_str()); + f << stringf("%s" "%s", indent, id(cell->type, false)); if (!defparam && cell->parameters.size() > 0) { f << stringf(" #("); for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) { if (it != cell->parameters.begin()) f << stringf(","); - f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str()); + f << stringf("\n%s .%s(", indent, id(it->first)); if (it->second.size() > 0) dump_const(f, it->second); f << stringf(")"); } - f << stringf("\n%s" ")", indent.c_str()); + f << stringf("\n%s" ")", indent); } std::string cell_name = cellname(cell); if (cell_name != id(cell->name)) - f << stringf(" %s /* %s */ (", cell_name.c_str(), id(cell->name).c_str()); + f << stringf(" %s /* %s */ (", cell_name, id(cell->name)); else - f << stringf(" %s (", cell_name.c_str()); + f << stringf(" %s (", cell_name); bool first_arg = true; std::set numbered_ports; @@ -1927,7 +2006,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (!first_arg) f << stringf(","); first_arg = false; - f << stringf("\n%s ", indent.c_str()); + f << stringf("\n%s ", indent); dump_sigspec(f, it->second); numbered_ports.insert(it->first); goto found_numbered_port; @@ -1941,26 +2020,26 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (!first_arg) f << stringf(","); first_arg = false; - f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str()); + f << stringf("\n%s .%s(", indent, id(it->first)); if (it->second.size() > 0) dump_sigspec(f, it->second); f << stringf(")"); } - f << stringf("\n%s" ");\n", indent.c_str()); + f << stringf("\n%s" ");\n", indent); if (defparam && cell->parameters.size() > 0) { for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) { - f << stringf("%sdefparam %s.%s = ", indent.c_str(), cell_name.c_str(), id(it->first).c_str()); + f << stringf("%sdefparam %s.%s = ", indent, cell_name, id(it->first)); dump_const(f, it->second); f << stringf(";\n"); } } - if (siminit && RTLIL::builtin_ff_cell_types().count(cell->type) && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_))) { + if (siminit && cell->is_builtin_ff() && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_))) { std::stringstream ss; dump_reg_init(ss, cell->getPort(ID::Q)); if (!ss.str().empty()) { - f << stringf("%sinitial %s.Q", indent.c_str(), cell_name.c_str()); + f << stringf("%sinitial %s.Q", indent, cell_name); f << ss.str(); f << ";\n"; } @@ -1970,9 +2049,9 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector &cells) { if (trg.size() == 0) { - f << stringf("%s" "initial begin\n", indent.c_str()); + f << stringf("%s" "initial begin\n", indent); } else { - f << stringf("%s" "always @(", indent.c_str()); + f << stringf("%s" "always @(", indent); for (int i = 0; i < trg.size(); i++) { if (i != 0) f << " or "; @@ -1989,7 +2068,7 @@ void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int(); }); for (auto cell : cells) { - f << stringf("%s" " if (", indent.c_str()); + f << stringf("%s" " if (", indent); dump_sigspec(f, cell->getPort(ID::EN)); f << stringf(") begin\n"); @@ -2001,22 +2080,22 @@ void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec Fmt fmt; fmt.parse_rtlil(cell); if (!fmt.parts.empty()) { - f << stringf("%s" " if (!", indent.c_str()); + f << stringf("%s" " if (!", indent); dump_sigspec(f, cell->getPort(ID::A)); f << stringf(")\n"); dump_cell_expr_print(f, indent + " ", cell); } } else { - f << stringf("%s" " /* message omitted */\n", indent.c_str()); + f << stringf("%s" " /* message omitted */\n", indent); } dump_cell_expr_check(f, indent + " ", cell); } - f << stringf("%s" " end\n", indent.c_str()); + f << stringf("%s" " end\n", indent); } - f << stringf("%s" "end\n", indent.c_str()); + f << stringf("%s" "end\n", indent); } void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) @@ -2026,7 +2105,7 @@ void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, if (chunk.is_wire() && reg_wires.count(chunk.wire->name)) all_chunks_wires = false; if (!simple_lhs && all_chunks_wires) { - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, left); f << stringf(" = "); dump_sigspec(f, right); @@ -2035,9 +2114,9 @@ void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, int offset = 0; for (auto &chunk : left.chunks()) { if (chunk.is_wire() && reg_wires.count(chunk.wire->name)) - f << stringf("%s" "always%s\n%s ", indent.c_str(), systemverilog ? "_comb" : " @*", indent.c_str()); + f << stringf("%s" "always%s\n%s ", indent, systemverilog ? "_comb" : " @*", indent); else - f << stringf("%s" "assign ", indent.c_str()); + f << stringf("%s" "assign ", indent); dump_sigspec(f, chunk); f << stringf(" = "); dump_sigspec(f, right.extract(offset, GetSize(chunk))); @@ -2054,7 +2133,7 @@ void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs) for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { if (it->first.size() == 0) continue; - f << stringf("%s ", indent.c_str()); + f << stringf("%s ", indent); dump_sigspec(f, it->first); f << stringf(" = "); dump_sigspec(f, it->second); @@ -2115,28 +2194,28 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo int number_of_stmts = cs->switches.size() + cs->actions.size(); if (!omit_trailing_begin && number_of_stmts >= 2) - f << stringf("%s" "begin\n", indent.c_str()); + f << stringf("%s" "begin\n", indent); dump_case_actions(f, indent, cs); for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) dump_proc_switch(f, indent + " ", *it); if (!omit_trailing_begin && number_of_stmts == 0) - f << stringf("%s /* empty */;\n", indent.c_str()); + f << stringf("%s /* empty */;\n", indent); if (omit_trailing_begin || number_of_stmts >= 2) - f << stringf("%s" "end\n", indent.c_str()); + f << stringf("%s" "end\n", indent); } void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw) { if (sw->signal.size() == 0) { - f << stringf("%s" "begin\n", indent.c_str()); + f << stringf("%s" "begin\n", indent); for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { if ((*it)->compare.size() == 0) dump_case_body(f, indent + " ", *it); } - f << stringf("%s" "end\n", indent.c_str()); + f << stringf("%s" "end\n", indent); return; } @@ -2144,7 +2223,7 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw return; dump_attributes(f, indent, sw->attributes); - f << stringf("%s" "casez (", indent.c_str()); + f << stringf("%s" "casez (", indent); dump_sigspec(f, sw->signal); f << stringf(")\n"); @@ -2152,10 +2231,10 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw bool got_default = false; dump_attributes(f, indent + " ", (*it)->attributes, "\n", /*modattr=*/false, /*regattr=*/false, /*as_comment=*/true); if ((*it)->compare.size() == 0) { - f << stringf("%s default", indent.c_str()); + f << stringf("%s default", indent); got_default = true; } else { - f << stringf("%s ", indent.c_str()); + f << stringf("%s ", indent); for (size_t i = 0; i < (*it)->compare.size(); i++) { if (i > 0) f << stringf(", "); @@ -2176,10 +2255,10 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw if (sw->cases.empty()) { // Verilog does not allow empty cases. - f << stringf("%s default: ;\n", indent.c_str()); + f << stringf("%s default: ;\n", indent); } - f << stringf("%s" "endcase\n", indent.c_str()); + f << stringf("%s" "endcase\n", indent); } void case_body_find_regs(RTLIL::CaseRule *cs) @@ -2208,7 +2287,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo return; } - f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_comb" : " @*"); + f << stringf("%s" "always%s begin\n", indent, systemverilog ? "_comb" : " @*"); if (!systemverilog) f << indent + " " << "if (" << id(initial_id) << ") begin end\n"; dump_case_body(f, indent, &proc->root_case, true); @@ -2221,11 +2300,11 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo indent = backup_indent; if (sync->type == RTLIL::STa) { - f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_comb" : " @*"); + f << stringf("%s" "always%s begin\n", indent, systemverilog ? "_comb" : " @*"); } else if (sync->type == RTLIL::STi) { - f << stringf("%s" "initial begin\n", indent.c_str()); + f << stringf("%s" "initial begin\n", indent); } else { - f << stringf("%s" "always%s @(", indent.c_str(), systemverilog ? "_ff" : ""); + f << stringf("%s" "always%s @(", indent, systemverilog ? "_ff" : ""); if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1) f << stringf("posedge "); if (sync->type == RTLIL::STn || sync->type == RTLIL::ST0) @@ -2237,7 +2316,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo indent += " "; if (sync->type == RTLIL::ST0 || sync->type == RTLIL::ST1) { - f << stringf("%s" "if (%s", indent.c_str(), sync->type == RTLIL::ST0 ? "!" : ""); + f << stringf("%s" "if (%s", indent, sync->type == RTLIL::ST0 ? "!" : ""); dump_sigspec(f, sync->signal); f << stringf(") begin\n"); ends = indent + "end\n" + ends; @@ -2248,7 +2327,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo for (size_t j = 0; j < proc->syncs.size(); j++) { RTLIL::SyncRule *sync2 = proc->syncs[j]; if (sync2->type == RTLIL::ST0 || sync2->type == RTLIL::ST1) { - f << stringf("%s" "if (%s", indent.c_str(), sync2->type == RTLIL::ST1 ? "!" : ""); + f << stringf("%s" "if (%s", indent, sync2->type == RTLIL::ST1 ? "!" : ""); dump_sigspec(f, sync2->signal); f << stringf(") begin\n"); ends = indent + "end\n" + ends; @@ -2260,14 +2339,14 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo for (auto it = sync->actions.begin(); it != sync->actions.end(); ++it) { if (it->first.size() == 0) continue; - f << stringf("%s ", indent.c_str()); + f << stringf("%s ", indent); dump_sigspec(f, it->first); f << stringf(" <= "); dump_sigspec(f, it->second); f << stringf(";\n"); } - f << stringf("%s", ends.c_str()); + f << stringf("%s", ends); } } @@ -2314,7 +2393,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) continue; } - if (!RTLIL::builtin_ff_cell_types().count(cell->type) || !cell->hasPort(ID::Q) || cell->type.in(ID($ff), ID($_FF_))) + if (!cell->is_builtin_ff() || !cell->hasPort(ID::Q) || cell->type.in(ID($ff), ID($_FF_))) continue; RTLIL::SigSpec sig = cell->getPort(ID::Q); @@ -2338,14 +2417,14 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } dump_attributes(f, indent, module->attributes, "\n", /*modattr=*/true); - f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str()); + f << stringf("%s" "module %s(", indent, id(module->name, false)); int cnt = 0; for (auto port : module->ports) { Wire *wire = module->wire(port); if (wire) { if (port != module->ports[0]) f << stringf(", "); - f << stringf("%s", id(wire->name).c_str()); + f << stringf("%s", id(wire->name)); if (cnt==20) { f << stringf("\n"); cnt = 0; } else cnt++; continue; } @@ -2356,8 +2435,16 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) f << indent + " " << "reg " << id(initial_id) << " = 0;\n"; } - for (auto w : module->wires()) + // first dump input / output according to their order in module->ports + for (auto port : module->ports) + dump_wire(f, indent + " ", module->wire(port)); + + for (auto w : module->wires()) { + // avoid duplication + if (w->port_id) + continue; dump_wire(f, indent + " ", w); + } for (auto &mem : Mem::get_all_memories(module)) dump_memory(f, indent + " ", mem); @@ -2374,7 +2461,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) for (auto it = module->connections().begin(); it != module->connections().end(); ++it) dump_conn(f, indent + " ", it->first, it->second); - f << stringf("%s" "endmodule\n", indent.c_str()); + f << stringf("%s" "endmodule\n", indent); active_module = NULL; active_sigmap.clear(); active_initdata.clear(); @@ -2594,7 +2681,7 @@ struct VerilogBackend : public Backend { Pass::call(design, "clean_zerowidth"); log_pop(); - design->sort(); + design->sort_modules(); *f << stringf("/* Generated by %s */\n", yosys_maybe_version()); @@ -2606,7 +2693,8 @@ struct VerilogBackend : public Backend { log_cmd_error("Can't handle partially selected module %s!\n", log_id(module->name)); continue; } - log("Dumping module `%s'.\n", module->name.c_str()); + log("Dumping module `%s'.\n", module->name); + module->sort(); dump_module(*f, "", module); } diff --git a/frontends/rtlil/rtlil_frontend.h b/backends/verilog/verilog_backend.h similarity index 58% rename from frontends/rtlil/rtlil_frontend.h rename to backends/verilog/verilog_backend.h index 31cfb80b4..7e550a37c 100644 --- a/frontends/rtlil/rtlil_frontend.h +++ b/backends/verilog/verilog_backend.h @@ -17,36 +17,23 @@ * * --- * - * A very simple and straightforward frontend for the RTLIL text - * representation. + * A simple and straightforward Verilog backend. * */ -#ifndef RTLIL_FRONTEND_H -#define RTLIL_FRONTEND_H +#ifndef VERILOG_BACKEND_H +#define VERILOG_BACKEND_H -#include "kernel/yosys.h" +#include YOSYS_NAMESPACE_BEGIN +namespace VERILOG_BACKEND { -namespace RTLIL_FRONTEND { - extern std::istream *lexin; - extern RTLIL::Design *current_design; - extern bool flag_nooverwrite; - extern bool flag_overwrite; - extern bool flag_lib; -} + const pool verilog_keywords(); + bool char_is_verilog_escaped(char c); + bool id_is_verilog_escaped(const std::string &str); +}; /* namespace VERILOG_BACKEND */ 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 - +#endif /* VERILOG_BACKEND_H */ diff --git a/docs/.gitignore b/docs/.gitignore index 65bbcdeae..30a903f9a 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,5 +1,4 @@ /build/ -/source/cmd /source/generated /source/_images/**/*.log /source/_images/**/*.aux @@ -7,3 +6,4 @@ /source/_images/**/*.svg /source/_images/**/*.dot /source/_images/code_examples +/venv diff --git a/docs/Makefile b/docs/Makefile index a8874bb83..fb3e03b79 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -47,7 +47,7 @@ help: .PHONY: clean clean: clean-examples rm -rf $(BUILDDIR)/* - rm -rf source/cmd util/__pycache__ + rm -rf util/__pycache__ rm -rf source/generated $(MAKE) -C source/_images clean diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css index b08194c05..60faf6812 100644 --- a/docs/source/_static/custom.css +++ b/docs/source/_static/custom.css @@ -18,3 +18,8 @@ .literal-block-wrapper .code-block-caption .caption-number { padding-right: 0.5em } + +/* Don't double shrink text in a literal in an optionlist */ +kbd .option>.literal { + font-size: revert; +} diff --git a/docs/source/appendix/auxlibs.rst b/docs/source/appendix/auxlibs.rst index 8c78ed6b3..192ac0944 100644 --- a/docs/source/appendix/auxlibs.rst +++ b/docs/source/appendix/auxlibs.rst @@ -29,8 +29,7 @@ ezSAT The files in ``libs/ezsat`` provide a library for simplifying generating CNF formulas for SAT solvers. It also contains bindings of MiniSAT. The ezSAT -library is written by C. Wolf. It is used by the `sat` pass (see -:doc:`/cmd/sat`). +library is written by C. Wolf. It is used by the `sat` pass. fst --- @@ -78,4 +77,4 @@ SubCircuit The files in ``libs/subcircuit`` provide a library for solving the subcircuit isomorphism problem. It is written by C. Wolf and based on the Ullmann Subgraph Isomorphism Algorithm :cite:p:`UllmannSubgraphIsomorphism`. It is used by the -extract pass (see :doc:`../cmd/extract`). +`extract` pass. diff --git a/docs/source/appendix/primer.rst b/docs/source/appendix/primer.rst index 50656af78..f54f99e52 100644 --- a/docs/source/appendix/primer.rst +++ b/docs/source/appendix/primer.rst @@ -72,7 +72,7 @@ circuits. Tools exist to synthesize high level code (usually in the form of C/C++/SystemC code with additional metadata) to behavioural HDL code (usually in the form of Verilog or VHDL code). Aside from the many commercial tools for high level -synthesis there are also a number of FOSS tools for high level synthesis . +synthesis there are also a number of FOSS tools for high level synthesis. Behavioural level ~~~~~~~~~~~~~~~~~ @@ -185,7 +185,7 @@ advantage that it has a unique normalized form. The latter has much better worst case performance and is therefore better suited for the synthesis of large logic functions. -Good FOSS tools exists for multi-level logic synthesis . +Good FOSS tools exists for multi-level logic synthesis. Yosys contains basic logic synthesis functionality but can also use ABC for the logic synthesis step. Using ABC is recommended. @@ -221,7 +221,7 @@ 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 +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. @@ -572,7 +572,7 @@ of lexical tokens given in :numref:`Tab. %s `. TOK_SEMICOLON \- ============== =============== -The lexer is usually generated by a lexer generator (e.g. flex ) from a +The lexer is usually generated by a lexer generator (e.g. flex) from a description file that is using regular expressions to specify the text pattern that should match the individual tokens. diff --git a/docs/source/appendix/rtlil_text.rst b/docs/source/appendix/rtlil_text.rst index b1bc9c582..352b1af2e 100644 --- a/docs/source/appendix/rtlil_text.rst +++ b/docs/source/appendix/rtlil_text.rst @@ -63,6 +63,10 @@ significant bit first. Bits may be any of: - ``m``: A marked bit (internal use only) - ``-``: A don't care value +When the bit representation has fewer bits than the width, it is padded to the width with +the most significant explicit bit, or ``0`` if the most significant explicit bit is ``1``, +or ``x`` if there are no explicit bits. + An *integer* is simply a signed integer value in decimal format. **Warning:** Integer constants are limited to 32 bits. That is, they may only be in the range :math:`[-2147483648, 2147483648)`. Integers outside this range will result in an @@ -133,6 +137,7 @@ wires, memories, cells, processes, and connections. ::= * ::= module ::= ( + | | | | @@ -170,6 +175,11 @@ See :ref:`sec:rtlil_sigspec` for an overview of signal specifications. | [ (:)? ] | { * } +When a ```` is specified, the wire must have been previously declared. + +When a signal slice is specified, the left-hand integer must be greather than or +equal to the right-hand integer. + Connections ^^^^^^^^^^^ @@ -268,7 +278,7 @@ may have zero or more attributes. .. code:: BNF ::= * - := * switch + ::= * switch ::= * ::= case ? ::= (, )* @@ -295,3 +305,4 @@ be: | sync always ::= low | high | posedge | negedge | edge ::= update + | * memwr diff --git a/docs/source/cell/word_mux.rst b/docs/source/cell/word_mux.rst index 3eca310f3..234d1016b 100644 --- a/docs/source/cell/word_mux.rst +++ b/docs/source/cell/word_mux.rst @@ -24,8 +24,8 @@ are zero, the value from ``A`` input is sent to the output. If the :math:`n`\ 'th bit from ``S`` is set, the value :math:`n`\ 'th ``WIDTH`` bits wide slice of the ``B`` input is sent to the output. When more than one bit from ``S`` is set the output is undefined. Cells of this type are used to model "parallel cases" -(defined by using the ``parallel_case`` attribute or detected by an -optimization). +(defined by using the ``parallel_case`` attribute, the ``unique`` or ``unique0`` +SystemVerilog keywords, or detected by an optimization). The `$tribuf` cell is used to implement tristate logic. Cells of this type have a ``WIDTH`` parameter and inputs ``A`` and ``EN`` and an output ``Y``. The ``A`` diff --git a/docs/source/cmd/index_backends.rst b/docs/source/cmd/index_backends.rst new file mode 100644 index 000000000..373c26def --- /dev/null +++ b/docs/source/cmd/index_backends.rst @@ -0,0 +1,5 @@ +Writing output files +-------------------- + +.. autocmdgroup:: backends + :members: diff --git a/docs/source/cmd/index_formal.rst b/docs/source/cmd/index_formal.rst new file mode 100644 index 000000000..b8b134c17 --- /dev/null +++ b/docs/source/cmd/index_formal.rst @@ -0,0 +1,5 @@ +Formal verification +------------------- + +.. autocmdgroup:: formal + :members: diff --git a/docs/source/cmd/index_frontends.rst b/docs/source/cmd/index_frontends.rst new file mode 100644 index 000000000..b64fdc9b9 --- /dev/null +++ b/docs/source/cmd/index_frontends.rst @@ -0,0 +1,5 @@ +Reading input files +------------------- + +.. autocmdgroup:: frontends + :members: diff --git a/docs/source/cmd/index_internal.rst b/docs/source/cmd/index_internal.rst new file mode 100644 index 000000000..ab9c13aba --- /dev/null +++ b/docs/source/cmd/index_internal.rst @@ -0,0 +1,152 @@ +Internal commands for developers +-------------------------------- + +.. autocmdgroup:: internal + :members: + +Writing command help +-------------------- + +- use `chformal` as an example +- generated help content below + +.. _chformal autocmd: + +.. autocmd:: chformal + :noindex: + +The ``formatted_help()`` method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``PrettyHelp::get_current()`` +- ``PrettyHelp::set_group()`` + + + used with ``.. autocmdgroup:: `` + + can assign group and return false + + if no group is set, will try to use ``source_location`` and assign group + from path to source file + +- return value + + + true means help content added to current ``PrettyHelp`` + + false to use ``Pass::help()`` + +- adding content + + + help content is a list of ``ContentListing`` nodes, each one having a type, + body, and its own list of children ``ContentListing``\ s + + ``PrettyHelp::get_root()`` returns the root ``ContentListing`` (``type="root"``) + + ``ContentListing::{usage, option, codeblock, paragraph}`` each add a + ``ContentListing`` to the current node, with type the same as the method + + * the first argument is the body of the new node + * ``usage`` shows how to call the command (i.e. its "signature") + * ``paragraph`` content is formatted as a paragraph of text with line breaks + added automatically + * ``codeblock`` content is displayed verbatim, use line breaks as desired; + takes an optional ``language`` argument for assigning the language in RST + output for code syntax highlighting (use ``yoscrypt`` for yosys script + syntax highlighting) + * ``option`` lists a single option for the command, usually starting with a + dash (``-``); takes an optional second argument which adds a paragraph + node as a means of description + + + ``ContentListing::open_usage`` creates and returns a new usage node, can be + used to e.g. add text/options specific to a given usage of the command + + ``ContentListing::open_option`` creates and returns a new option node, can + be used to e.g. add multiple paragraphs to an option's description + + paragraphs are treated as raw RST, allowing for inline formatting and + references as if it were written in the RST file itself + +.. literalinclude:: /generated/chformal.cc + :language: c++ + :start-at: bool formatted_help() + :end-before: void execute + :caption: ``ChformalPass::formatted_help()`` from :file:`passes/cmds/chformal.cc` + +Dumping command help to json +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `help -dump-cmds-json cmds.json` + + + generates a ``ContentListing`` for each command registered in Yosys + + tries to parse unformatted ``Pass::help()`` output if + ``Pass::formatted_help()`` is unimplemented or returns false + + * if a line starts with four spaces followed by the name of the command then + a space, it is parsed as a signature (usage node) + * if a line is indented and starts with a dash (``-``), it is parsed as an + option + * anything else is parsed as a codeblock and added to either the root node + or the current option depending on the indentation + + + dictionary of command name to ``ContentListing`` + + * uses ``ContentListing::to_json()`` recursively for each node in root + * root node used for source location of class definition + * includes flags set during pass constructor (e.g. ``experimental_flag`` set + by ``Pass::experimental()``) + * also title (``short_help`` argument in ``Pass::Pass``), group, and class + name + + + dictionary of group name to list of commands in that group + +- used by sphinx autodoc to generate help content + +.. literalinclude:: /generated/cmds.json + :language: json + :start-at: "chformal": { + :end-before: "chparam": { + :caption: `chformal` in generated :file:`cmds.json` + +.. note:: Synthesis command scripts are special cased + + If the final block of help output starts with the string `"The following + commands are executed by this synthesis command:\n"`, then the rest of the + code block is formatted as ``yoscrypt`` (e.g. `synth_ice40`). The caveat + here is that if the ``script()`` calls ``run()`` on any commands *prior* to + the first ``check_label`` then the auto detection will break and revert to + unformatted code (e.g. `synth_fabulous`). + +Command line rendering +~~~~~~~~~~~~~~~~~~~~~~ + +- if ``Pass::formatted_help()`` returns true, will call + ``PrettyHelp::log_help()`` + + + traverse over the children of the root node and render as plain text + + effectively the reverse of converting unformatted ``Pass::help()`` text + + lines are broken at 80 characters while maintaining indentation (controlled + by ``MAX_LINE_LEN`` in :file:`kernel/log_help.cc`) + + each line is broken into words separated by spaces, if a given word starts + and ends with backticks they will be stripped + +- if it returns false it will call ``Pass::help()`` which should call ``log()`` + directly to print and format help text + + + if ``Pass::help()`` is not overridden then a default message about missing + help will be displayed + +.. literalinclude:: /generated/chformal.log + :lines: 2- + +RST generated from autocmd +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- below is the raw RST output from ``autocmd`` (``YosysCmdDocumenter`` class in + :file:`docs/util/cmd_documenter.py`) for `chformal` command +- heading will be rendered as a subheading of the most recent heading (see + `chformal autocmd`_ above rendered under `Writing command help`_) +- ``.. cmd:def:: `` line is indexed for cross references with ``:cmd:ref:`` + directive (`chformal autocmd`_ above uses ``:noindex:`` option so that + `chformal` still links to the correct location) + + + ``:title:`` option controls text that appears when hovering over the + `chformal` link + +- commands with warning flags (experimental or internal) add a ``.. warning`` + block before any of the help content +- if a command has no ``source_location`` the ``.. note`` at the bottom will + instead link to :doc:`/cmd/index_other` + +.. autocmd_rst:: chformal diff --git a/docs/source/cmd/index_kernel.rst b/docs/source/cmd/index_kernel.rst new file mode 100644 index 000000000..c6891b5e5 --- /dev/null +++ b/docs/source/cmd/index_kernel.rst @@ -0,0 +1,5 @@ +Yosys kernel commands +--------------------- + +.. autocmdgroup:: kernel + :members: diff --git a/docs/source/cmd/index_other.rst b/docs/source/cmd/index_other.rst new file mode 100644 index 000000000..540cf9e49 --- /dev/null +++ b/docs/source/cmd/index_other.rst @@ -0,0 +1,9 @@ +:orphan: + +Other commands +============== + +Unknown source location + +.. autocmdgroup:: unknown + :members: diff --git a/docs/source/cmd/index_passes.rst b/docs/source/cmd/index_passes.rst new file mode 100644 index 000000000..b652be004 --- /dev/null +++ b/docs/source/cmd/index_passes.rst @@ -0,0 +1,14 @@ +Passes +------ + +.. toctree:: + :maxdepth: 2 + :glob: + + /cmd/index_passes_hierarchy + /cmd/index_passes_proc + /cmd/index_passes_fsm + /cmd/index_passes_memory + /cmd/index_passes_opt + /cmd/index_passes_techmap + /cmd/index_passes_* diff --git a/docs/source/cmd/index_passes_cmds.rst b/docs/source/cmd/index_passes_cmds.rst new file mode 100644 index 000000000..d7b448f07 --- /dev/null +++ b/docs/source/cmd/index_passes_cmds.rst @@ -0,0 +1,5 @@ +Design modification +------------------- + +.. autocmdgroup:: passes/cmds + :members: diff --git a/docs/source/cmd/index_passes_equiv.rst b/docs/source/cmd/index_passes_equiv.rst new file mode 100644 index 000000000..6ed2c3c18 --- /dev/null +++ b/docs/source/cmd/index_passes_equiv.rst @@ -0,0 +1,5 @@ +Equivalence checking +-------------------- + +.. autocmdgroup:: passes/equiv + :members: diff --git a/docs/source/cmd/index_passes_fsm.rst b/docs/source/cmd/index_passes_fsm.rst new file mode 100644 index 000000000..43af5dce6 --- /dev/null +++ b/docs/source/cmd/index_passes_fsm.rst @@ -0,0 +1,5 @@ +FSM handling +------------ + +.. autocmdgroup:: passes/fsm + :members: diff --git a/docs/source/cmd/index_passes_hierarchy.rst b/docs/source/cmd/index_passes_hierarchy.rst new file mode 100644 index 000000000..27a0faeb7 --- /dev/null +++ b/docs/source/cmd/index_passes_hierarchy.rst @@ -0,0 +1,5 @@ +Working with hierarchy +---------------------- + +.. autocmdgroup:: passes/hierarchy + :members: diff --git a/docs/source/cmd/index_passes_memory.rst b/docs/source/cmd/index_passes_memory.rst new file mode 100644 index 000000000..b4edb88e7 --- /dev/null +++ b/docs/source/cmd/index_passes_memory.rst @@ -0,0 +1,5 @@ +Memory handling +--------------- + +.. autocmdgroup:: passes/memory + :members: diff --git a/docs/source/cmd/index_passes_opt.rst b/docs/source/cmd/index_passes_opt.rst new file mode 100644 index 000000000..ddeb5ce10 --- /dev/null +++ b/docs/source/cmd/index_passes_opt.rst @@ -0,0 +1,5 @@ +Optimization passes +------------------- + +.. autocmdgroup:: passes/opt + :members: diff --git a/docs/source/cmd/index_passes_proc.rst b/docs/source/cmd/index_passes_proc.rst new file mode 100644 index 000000000..1ad8d85b4 --- /dev/null +++ b/docs/source/cmd/index_passes_proc.rst @@ -0,0 +1,5 @@ +Converting process blocks +------------------------- + +.. autocmdgroup:: passes/proc + :members: diff --git a/docs/source/cmd/index_passes_sat.rst b/docs/source/cmd/index_passes_sat.rst new file mode 100644 index 000000000..a2571fedb --- /dev/null +++ b/docs/source/cmd/index_passes_sat.rst @@ -0,0 +1,5 @@ +Simulating circuits +------------------- + +.. autocmdgroup:: passes/sat + :members: diff --git a/docs/source/cmd/index_passes_status.rst b/docs/source/cmd/index_passes_status.rst new file mode 100644 index 000000000..a157ed840 --- /dev/null +++ b/docs/source/cmd/index_passes_status.rst @@ -0,0 +1,5 @@ +Design status +------------- + +.. autocmdgroup:: passes/status + :members: diff --git a/docs/source/cmd/index_passes_techmap.rst b/docs/source/cmd/index_passes_techmap.rst new file mode 100644 index 000000000..1682cd181 --- /dev/null +++ b/docs/source/cmd/index_passes_techmap.rst @@ -0,0 +1,7 @@ +Technology mapping +------------------ + +.. seealso:: :doc:`/cmd/index_techlibs` + +.. autocmdgroup:: passes/techmap + :members: diff --git a/docs/source/cmd/index_techlibs.rst b/docs/source/cmd/index_techlibs.rst new file mode 100644 index 000000000..043620a3b --- /dev/null +++ b/docs/source/cmd/index_techlibs.rst @@ -0,0 +1,11 @@ +Technology libraries +==================== + +Listed in alphabetical order. + +.. toctree:: + :maxdepth: 2 + :glob: + + /cmd/index_techlibs_common + /cmd/index_techlibs_* diff --git a/docs/source/cmd/index_techlibs_achronix.rst b/docs/source/cmd/index_techlibs_achronix.rst new file mode 100644 index 000000000..d4d96d24a --- /dev/null +++ b/docs/source/cmd/index_techlibs_achronix.rst @@ -0,0 +1,5 @@ +Achronix +------------------ + +.. autocmdgroup:: techlibs/achronix + :members: diff --git a/docs/source/cmd/index_techlibs_anlogic.rst b/docs/source/cmd/index_techlibs_anlogic.rst new file mode 100644 index 000000000..8a2e6b577 --- /dev/null +++ b/docs/source/cmd/index_techlibs_anlogic.rst @@ -0,0 +1,5 @@ +Anlogic +------------------ + +.. autocmdgroup:: techlibs/anlogic + :members: diff --git a/docs/source/cmd/index_techlibs_common.rst b/docs/source/cmd/index_techlibs_common.rst new file mode 100644 index 000000000..532f4291e --- /dev/null +++ b/docs/source/cmd/index_techlibs_common.rst @@ -0,0 +1,5 @@ +Generic +------------------ + +.. autocmdgroup:: techlibs/common + :members: diff --git a/docs/source/cmd/index_techlibs_coolrunner2.rst b/docs/source/cmd/index_techlibs_coolrunner2.rst new file mode 100644 index 000000000..23d91a500 --- /dev/null +++ b/docs/source/cmd/index_techlibs_coolrunner2.rst @@ -0,0 +1,5 @@ +CoolRunner-II +------------------ + +.. autocmdgroup:: techlibs/coolrunner2 + :members: diff --git a/docs/source/cmd/index_techlibs_easic.rst b/docs/source/cmd/index_techlibs_easic.rst new file mode 100644 index 000000000..c6398ddf3 --- /dev/null +++ b/docs/source/cmd/index_techlibs_easic.rst @@ -0,0 +1,5 @@ +eASIC +------------------ + +.. autocmdgroup:: techlibs/easic + :members: diff --git a/docs/source/cmd/index_techlibs_fabulous.rst b/docs/source/cmd/index_techlibs_fabulous.rst new file mode 100644 index 000000000..96f04f40a --- /dev/null +++ b/docs/source/cmd/index_techlibs_fabulous.rst @@ -0,0 +1,5 @@ +FABulous +------------------ + +.. autocmdgroup:: techlibs/fabulous + :members: diff --git a/docs/source/cmd/index_techlibs_gatemate.rst b/docs/source/cmd/index_techlibs_gatemate.rst new file mode 100644 index 000000000..951d0000b --- /dev/null +++ b/docs/source/cmd/index_techlibs_gatemate.rst @@ -0,0 +1,5 @@ +Gatemate +------------------ + +.. autocmdgroup:: techlibs/gatemate + :members: diff --git a/docs/source/cmd/index_techlibs_gowin.rst b/docs/source/cmd/index_techlibs_gowin.rst new file mode 100644 index 000000000..cdcb1c2ee --- /dev/null +++ b/docs/source/cmd/index_techlibs_gowin.rst @@ -0,0 +1,5 @@ +Gowin +------------------ + +.. autocmdgroup:: techlibs/gowin + :members: diff --git a/docs/source/cmd/index_techlibs_greenpak4.rst b/docs/source/cmd/index_techlibs_greenpak4.rst new file mode 100644 index 000000000..add1ab102 --- /dev/null +++ b/docs/source/cmd/index_techlibs_greenpak4.rst @@ -0,0 +1,5 @@ +GreenPAK4 +------------------ + +.. autocmdgroup:: techlibs/greenpak4 + :members: diff --git a/docs/source/cmd/index_techlibs_ice40.rst b/docs/source/cmd/index_techlibs_ice40.rst new file mode 100644 index 000000000..1c4b2d07a --- /dev/null +++ b/docs/source/cmd/index_techlibs_ice40.rst @@ -0,0 +1,5 @@ +iCE40 +------------------ + +.. autocmdgroup:: techlibs/ice40 + :members: diff --git a/docs/source/cmd/index_techlibs_intel.rst b/docs/source/cmd/index_techlibs_intel.rst new file mode 100644 index 000000000..6b3a26222 --- /dev/null +++ b/docs/source/cmd/index_techlibs_intel.rst @@ -0,0 +1,5 @@ +Intel (MAX10, Cyclone IV) +------------------------- + +.. autocmdgroup:: techlibs/intel + :members: diff --git a/docs/source/cmd/index_techlibs_intel_alm.rst b/docs/source/cmd/index_techlibs_intel_alm.rst new file mode 100644 index 000000000..afadfc13e --- /dev/null +++ b/docs/source/cmd/index_techlibs_intel_alm.rst @@ -0,0 +1,5 @@ +Intel ALM (Cyclone V, Arria V, Cyclone 10 GX) +--------------------------------------------- + +.. autocmdgroup:: techlibs/intel_alm + :members: diff --git a/docs/source/cmd/index_techlibs_lattice.rst b/docs/source/cmd/index_techlibs_lattice.rst new file mode 100644 index 000000000..985bf0bbd --- /dev/null +++ b/docs/source/cmd/index_techlibs_lattice.rst @@ -0,0 +1,5 @@ +Lattice +------------------ + +.. autocmdgroup:: techlibs/lattice + :members: diff --git a/docs/source/cmd/index_techlibs_microchip.rst b/docs/source/cmd/index_techlibs_microchip.rst new file mode 100644 index 000000000..06613a59c --- /dev/null +++ b/docs/source/cmd/index_techlibs_microchip.rst @@ -0,0 +1,5 @@ +Microchip +------------------ + +.. autocmdgroup:: techlibs/microchip + :members: diff --git a/docs/source/cmd/index_techlibs_microchip_sf2.rst b/docs/source/cmd/index_techlibs_microchip_sf2.rst new file mode 100644 index 000000000..4ebe47f33 --- /dev/null +++ b/docs/source/cmd/index_techlibs_microchip_sf2.rst @@ -0,0 +1,5 @@ +Microchip - SmartFusion2/IGLOO2 +----------------------------------- + +.. autocmdgroup:: techlibs/sf2 + :members: diff --git a/docs/source/cmd/index_techlibs_nanoxplore.rst b/docs/source/cmd/index_techlibs_nanoxplore.rst new file mode 100644 index 000000000..9eff4681c --- /dev/null +++ b/docs/source/cmd/index_techlibs_nanoxplore.rst @@ -0,0 +1,5 @@ +NanoXplore +------------------ + +.. autocmdgroup:: techlibs/nanoxplore + :members: diff --git a/docs/source/cmd/index_techlibs_quicklogic.rst b/docs/source/cmd/index_techlibs_quicklogic.rst new file mode 100644 index 000000000..54d199eb0 --- /dev/null +++ b/docs/source/cmd/index_techlibs_quicklogic.rst @@ -0,0 +1,5 @@ +QuickLogic +------------------ + +.. autocmdgroup:: techlibs/quicklogic + :members: diff --git a/docs/source/cmd/index_techlibs_xilinx.rst b/docs/source/cmd/index_techlibs_xilinx.rst new file mode 100644 index 000000000..df5112b7e --- /dev/null +++ b/docs/source/cmd/index_techlibs_xilinx.rst @@ -0,0 +1,5 @@ +Xilinx +------------------ + +.. autocmdgroup:: techlibs/xilinx + :members: diff --git a/docs/source/cmd_ref.rst b/docs/source/cmd_ref.rst index acf2d1d41..668516a0b 100644 --- a/docs/source/cmd_ref.rst +++ b/docs/source/cmd_ref.rst @@ -1,5 +1,3 @@ -.. _cmd_ref: - ================================================================================ Command line reference ================================================================================ @@ -7,10 +5,31 @@ Command line reference .. literalinclude:: /generated/yosys :start-at: Usage -.. toctree:: - :caption: Command reference - :maxdepth: 1 - :glob: +.. _cmd_ref: - /appendix/env_vars - /cmd/* +Command reference +----------------- + +.. todo:: Can we warn on command groups that aren't included anywhere? + +:ref:`List of all commands` + +.. toctree:: + :maxdepth: 2 + + /appendix/env_vars + /cmd/index_frontends + /cmd/index_backends + /cmd/index_kernel + /cmd/index_formal + +.. toctree:: + :maxdepth: 3 + + /cmd/index_passes + /cmd/index_techlibs + +.. toctree:: + :maxdepth: 2 + + /cmd/index_internal diff --git a/docs/source/code_examples/extensions/my_cmd.cc b/docs/source/code_examples/extensions/my_cmd.cc index e6660469c..d52268b4a 100644 --- a/docs/source/code_examples/extensions/my_cmd.cc +++ b/docs/source/code_examples/extensions/my_cmd.cc @@ -10,7 +10,7 @@ struct MyPass : public Pass { { log("Arguments to my_cmd:\n"); for (auto &arg : args) - log(" %s\n", arg.c_str()); + log(" %s\n", arg); log("Modules in current design:\n"); for (auto mod : design->modules()) diff --git a/docs/source/code_examples/functional/dummy.cc b/docs/source/code_examples/functional/dummy.cc index 3d84b84ba..42b05b339 100644 --- a/docs/source/code_examples/functional/dummy.cc +++ b/docs/source/code_examples/functional/dummy.cc @@ -16,7 +16,7 @@ struct FunctionalDummyBackend : public Backend { for (auto module : design->selected_modules()) { - log("Processing module `%s`.\n", module->name.c_str()); + log("Processing module `%s`.\n", module->name); // convert module to FunctionalIR auto ir = Functional::IR::from_module(module); diff --git a/docs/source/code_examples/macro_commands/opt.ys b/docs/source/code_examples/macro_commands/opt.ys index cb883bc58..ebd938836 100644 --- a/docs/source/code_examples/macro_commands/opt.ys +++ b/docs/source/code_examples/macro_commands/opt.ys @@ -9,6 +9,7 @@ do opt_merge opt_share (-full only) opt_dff (except when called with -noff) + opt_hier (-hier only) opt_clean opt_expr while diff --git a/docs/source/code_examples/macro_commands/prep.ys b/docs/source/code_examples/macro_commands/prep.ys new file mode 100644 index 000000000..1bec907f6 --- /dev/null +++ b/docs/source/code_examples/macro_commands/prep.ys @@ -0,0 +1,23 @@ +#start:The following commands are executed by this synthesis command: +#end:$ +begin: + hierarchy -check [-top | -auto-top] + +coarse: + proc [-ifx] + flatten (if -flatten) + future + opt_expr -keepdc + opt_clean + check + opt -noff -keepdc + wreduce -keepdc [-memx] + memory_dff (if -rdff) + memory_memx (if -memx) + opt_clean + memory_collect + opt -noff -keepdc -fast + +check: + stat + check diff --git a/docs/source/code_examples/pyosys/pass.py b/docs/source/code_examples/pyosys/pass.py new file mode 100644 index 000000000..2108b48ab --- /dev/null +++ b/docs/source/code_examples/pyosys/pass.py @@ -0,0 +1,37 @@ +from pyosys import libyosys as ys + +class AllEnablePass(ys.Pass): + def __init__(self): + super().__init__( + "all_enable", + "makes all _DFF_P_ registers require an enable signal" + ) + + def execute(self, args, design): + ys.log_header(design, "Adding enable signals\n") + ys.log_push() + top_module = design.top_module() + + if "\\enable" not in top_module.wires_: + enable_line = top_module.addWire("\\enable") + enable_line.port_input = True + top_module.fixup_ports() + + for cell in top_module.cells_.values(): + if cell.type != "$_DFF_P_": + continue + cell.type = "$_DFFE_PP_" + cell.setPort("\\E", ys.SigSpec(enable_line)) + ys.log_pop() + +p = AllEnablePass() # register the pass + +# using the pass + +design = ys.Design() +ys.run_pass("read_verilog tests/simple/fiedler-cooley.v", design) +ys.run_pass("hierarchy -check -auto-top", design) +ys.run_pass("synth", design) +ys.run_pass("all_enable", design) +ys.run_pass("write_verilog out.v", design) +ys.run_pass("synth_ice40 -json out.json", design) diff --git a/docs/source/code_examples/pyosys/simple_database.py b/docs/source/code_examples/pyosys/simple_database.py new file mode 100644 index 000000000..4cfe6b586 --- /dev/null +++ b/docs/source/code_examples/pyosys/simple_database.py @@ -0,0 +1,51 @@ +from pyosys import libyosys as ys + +# loading design +design = ys.Design() + +ys.run_pass("read_verilog tests/simple/fiedler-cooley.v", design) +ys.run_pass("hierarchy -check -auto-top", design) + +# top module inspection +top_module = design.top_module() + +for id, wire in top_module.wires_.items(): + if not wire.port_input and not wire.port_output: + continue + description = "input" if wire.port_input else "output" + description += " " + wire.name.str() + if wire.width != 1: + frm = wire.start_offset + to = wire.start_offset + wire.width + if wire.upto: + to, frm = frm, to + description += f" [{to}:{frm}]" + print(description) + +# synth + +ys.run_pass("synth", design) + +# adding the enable line + +enable_line = top_module.addWire("\\enable") +enable_line.port_input = True +top_module.fixup_ports() + +# hooking the enable line to the internal dff cells + +for cell in top_module.cells_.values(): + if cell.type != "$_DFF_P_": + continue + cell.type = "$_DFFE_PP_" + cell.setPort("\\E", ys.SigSpec(enable_line)) + +# run check + +top_module.check() +ys.run_pass("stat", design) + +# write outputs + +ys.run_pass("write_verilog out.v", design) +ys.run_pass("synth_ice40 -json out.json", design) diff --git a/docs/source/conf.py b/docs/source/conf.py index bfcb28730..fb106bddb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,7 +6,7 @@ import os project = 'YosysHQ Yosys' author = 'YosysHQ GmbH' copyright ='2025 YosysHQ GmbH' -yosys_ver = "0.53" +yosys_ver = "0.59" # select HTML theme html_theme = 'furo-ys' @@ -43,10 +43,14 @@ html_static_path = ['_static', "_images"] # default to no highlight highlight_language = 'none' -# default single quotes to attempt auto reference, or fallback to code +# default single quotes to attempt auto reference, or fallback to yoscrypt default_role = 'autoref' +rst_prolog = """ +.. role:: yoscrypt(code) + :language: yoscrypt +""" -extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex'] +extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex', 'sphinx_inline_tabs'] if os.getenv("READTHEDOCS"): # Use rtds_action if we are building on read the docs and have a github token env var @@ -64,7 +68,6 @@ if os.getenv("READTHEDOCS"): # Ensure that autosectionlabel will produce unique names autosectionlabel_prefix_document = True -autosectionlabel_maxdepth = 1 # include todos for previews extensions.append('sphinx.ext.todo') @@ -106,12 +109,14 @@ latex_elements = { # custom cmd-ref parsing/linking sys.path += [os.path.dirname(__file__) + "/../"] -extensions.append('util.cmdref') +extensions.append('util.custom_directives') # use autodocs extensions.append('sphinx.ext.autodoc') -extensions.append('util.cellref') +extensions.append('util.cell_documenter') cells_json = Path(__file__).parent / 'generated' / 'cells.json' +extensions.append('util.cmd_documenter') +cmds_json = Path(__file__).parent / 'generated' / 'cmds.json' from sphinx.application import Sphinx def setup(app: Sphinx) -> None: diff --git a/docs/source/getting_started/example_synth.rst b/docs/source/getting_started/example_synth.rst index e215586cc..ccf0d252b 100644 --- a/docs/source/getting_started/example_synth.rst +++ b/docs/source/getting_started/example_synth.rst @@ -70,7 +70,7 @@ 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 +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: begin: :end-before: flatten: @@ -143,8 +143,8 @@ 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 +To handle these, let us now introduce the next command: :cmd:title:`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 @@ -188,7 +188,7 @@ opt_expr `. .. note:: - :doc:`/cmd/clean` can also be called with two semicolons after any command, + :cmd:title:`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 @@ -215,8 +215,8 @@ 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`. +- :cmd:title:`design`, and +- :cmd:title:`read_verilog`. .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -251,7 +251,7 @@ our design won't run into this issue, we can skip the ``-defer``. 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 +you can do so with :cmd:title:`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`. @@ -283,7 +283,7 @@ 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 +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: flatten: :end-before: coarse: @@ -355,7 +355,7 @@ Part 1 In the iCE40 flow, we start with the following commands: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: coarse: :end-before: wreduce @@ -371,7 +371,7 @@ 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 +:cmd:title:`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. @@ -403,7 +403,7 @@ Part 2 The next group of commands performs a series of optimizations: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-at: wreduce :end-before: t:$mul @@ -411,7 +411,7 @@ The next group of commands performs a series of optimizations: :caption: ``coarse`` section (part 2) :name: synth_coarse2 -First up is :doc:`/cmd/wreduce`. If we run this we get the following: +First up is :cmd:title:`wreduce`. If we run this we get the following: .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -432,7 +432,7 @@ the schematic and see the output of that cell has now changed. ``rdata`` output after `wreduce` -The next two (new) commands are :doc:`/cmd/peepopt` and :doc:`/cmd/share`. +The next two (new) commands are :cmd:title:`peepopt` and :cmd:title:`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 @@ -440,7 +440,7 @@ 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`. +:cmd:title:`memory_dff`. .. literalinclude:: /code_examples/fifo/fifo.out :language: doscon @@ -475,7 +475,7 @@ 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 +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-at: t:$mul :end-before: alumacc @@ -514,7 +514,7 @@ Part 4 That brings us to the fourth and final part for the iCE40 synthesis flow: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-at: alumacc :end-before: map_ram: @@ -543,7 +543,7 @@ 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 +The other new command in this part is :cmd:title:`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, @@ -594,7 +594,7 @@ Memory blocks Mapping to hard memory blocks uses a combination of `memory_libmap` and `techmap`. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_ram: :end-before: map_ffram: @@ -636,7 +636,7 @@ into flip flops (the ``logic fallback``) with `memory_map`. .. |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 +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_ffram: :end-before: map_gates: @@ -671,7 +671,7 @@ 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 +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_gates: :end-before: map_ffs: @@ -700,7 +700,7 @@ 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 +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_ffs: :end-before: map_luts: @@ -725,7 +725,7 @@ LUTs `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 +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_luts: :end-before: map_cells: @@ -742,7 +742,7 @@ commands are, refer to :doc:`/using_yosys/synthesis/abc`. Finally we use `techmap` to map the generic `$lut` cells to iCE40 ``SB_LUT4`` cells. -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :language: yoscrypt :start-after: map_cells: :end-before: check: @@ -784,19 +784,18 @@ Final steps The next section of the iCE40 synth flow performs some sanity checking and final tidy up: -.. literalinclude:: /cmd/synth_ice40.rst +.. literalinclude:: /code_examples/macro_commands/synth_ice40.ys :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`. +- :cmd:title:`autoname`, +- :cmd:title:`stat`, and +- :cmd:title:`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 @@ -835,9 +834,9 @@ 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`. +- `write_blif`, +- `write_edif`, and +- `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 @@ -848,4 +847,4 @@ is beyond the scope of this documentation. .. _nextpnr: https://github.com/YosysHQ/nextpnr -.. seealso:: :doc:`/cmd/synth_ice40` +.. seealso:: :cmd:title:`synth_ice40` diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst index fe96b2314..43b996353 100644 --- a/docs/source/getting_started/installation.rst +++ b/docs/source/getting_started/installation.rst @@ -88,64 +88,71 @@ 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 +such as GNU Flex, GNU Bison (>=3.8), Make, and Python (>=3.11). 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: +Installing all prerequisites: -.. 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): +.. tab:: Ubuntu 22.04 .. 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 + sudo apt-get install gawk git make python3 lld bison clang flex \ + libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev \ + graphviz xdot + curl -LsSf https://astral.sh/uv/install.sh | sh + +.. tab:: macOS 13 (with Homebrew) + + .. code:: console + + brew tap Homebrew/bundle && brew bundle + +.. tab:: MacPorts + + .. code:: console + + sudo port install bison flex readline gawk libffi graphviz \ + pkgconfig python311 zlib tcl + +.. tab:: FreeBSD + + .. code:: console + + pkg install bison flex readline gawk libffi graphviz \ + pkgconf python311 tcl-wrapper + + .. note:: On FreeBSD system use gmake instead of make. To run tests use: + ``MAKE=gmake CXX=cxx CC=cc gmake test`` + +.. tab:: 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,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. You may also need to specify ``CXXSTD=gnu++17`` to + resolve missing ``strdup`` function when using gcc. It is instead + recommended to use Windows Subsystem for Linux (WSL) and follow the + instructions for Ubuntu. + +.. + tab:: MSYS2 (MINGW64) + + .. code:: console + + pacman -S bison flex mingw-w64-x86_64-gcc git libffi-devel libreadline-devel make pkg-config python3 tcl-devel 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 @@ -214,7 +221,7 @@ Running the build system From the root ``yosys`` directory, call the following commands: .. code:: console - + make sudo make install @@ -227,7 +234,7 @@ To use a separate (out-of-tree) build directory, provide a path to the Makefile. Out-of-tree builds require a clean source tree. -.. seealso:: +.. seealso:: Refer to :doc:`/yosys_internals/extending_yosys/test_suites` for details on testing Yosys once compiled. diff --git a/docs/source/getting_started/scripting_intro.rst b/docs/source/getting_started/scripting_intro.rst index 01954c661..c44ce82a8 100644 --- a/docs/source/getting_started/scripting_intro.rst +++ b/docs/source/getting_started/scripting_intro.rst @@ -26,7 +26,7 @@ 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" + $ yosys -p 'read_verilog fifo.v; :this is a comment; prep' .. warning:: @@ -42,6 +42,13 @@ 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. +.. warning:: + + Take care when using the ``yosys -p`` option. Some shells such as bash will + perform substitution options inside of a double quoted string, such as ``!`` + for history substitution and ``$`` for variable substitution; single quotes + should be used instead to pass the string to Yosys without substitution. + The synthesis starter script ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -122,7 +129,7 @@ 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`. +:cmd:title:`select`. .. _show_intro: @@ -219,7 +226,7 @@ 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`. +:cmd:title:`show`. .. seealso:: :ref:`interactive_show` on the :doc:`/using_yosys/more_scripting/interactive_investigation` page. diff --git a/docs/source/index.rst b/docs/source/index.rst index 61dc114ef..403100093 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -5,7 +5,7 @@ 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`. +available, go to :ref:`cmd_ref`. .. todo:: look into command ref improvements diff --git a/docs/source/requirements.txt b/docs/source/requirements.txt index 203205169..403bfb1c6 100644 --- a/docs/source/requirements.txt +++ b/docs/source/requirements.txt @@ -1,3 +1,4 @@ furo-ys @ git+https://github.com/YosysHQ/furo-ys sphinxcontrib-bibtex rtds-action +sphinx-inline-tabs diff --git a/docs/source/using_yosys/bugpoint.rst b/docs/source/using_yosys/bugpoint.rst new file mode 100644 index 000000000..c524470af --- /dev/null +++ b/docs/source/using_yosys/bugpoint.rst @@ -0,0 +1,446 @@ +Minimizing failing (or bugged) designs +====================================== + +.. TODO:: pending merge of https://github.com/YosysHQ/yosys/pull/5068 + +This document is a how-to guide for reducing problematic designs to the bare +minimum needed for reproducing the issue. This is a Yosys specific alternative +to the Stack Overflow article: `How to create a Minimal, Reproducible Example`_, +and is intended to help when there's something wrong with your design, or with +Yosys itself. + +.. _How to create a Minimal, Reproducible Example: https://stackoverflow.com/help/minimal-reproducible-example + +.. note:: + + This guide assumes a moderate degree of familiarity with Yosys and requires + some amount of problem solving ability. + + +Before you start +---------------- + +The first (and often overlooked) step, is to check for and *read* any error +messages or warnings. Passing the ``-q`` flag when running Yosys will make it +so that only warnings and error messages are written to the console. Don't just +read the last message either, there may be warnings that indicate a problem +before it happens. While some things may only be regarded as warnings, such as +multiple drivers for the same signal or logic loops, these can cause problems in +some synthesis flows but not others. + +A Yosys error (one that starts with ``ERROR:``) may give you a line number from +your design, or the name of the object causing issues. If so, you may already +have enough information to resolve the problem, or at least understand why it's +happening. + +.. note:: + + If you're not already, try using the latest version from the `Yosys GitHub`_. + You may find that your issue has already been fixed! And even if it isn't, + testing with two different versions is a good way to ensure reproducibility. + +.. _Yosys GitHub: https://github.com/YosysHQ/yosys + +Another thing to be aware of is that Yosys generally doesn't perform rigorous +checking of input designs to ensure they are valid. This is especially true for +the `read_verilog` frontend. It is instead recommended that you try load it +with `iverilog`_ or `verilator`_ first, as an invalid design can often lead to +unexpected issues. + +.. _iverilog: https://steveicarus.github.io/iverilog/ +.. _verilator: https://www.veripool.org/verilator/ + +If you're using a custom synthesis script, try take a bit of time to figure out +which command is failing. Calling ``echo on`` at the start of your script will +`echo` each command executed; the last echo before the error should then be +where the error has come from. Check the help message for the failing command; +does it indicate limited support, or mention some other command that needs to be +run first? You can also try to call `check` and/or ``hierarchy -check`` before +the failure to see if they report and errors or warnings. + + +Minimizing RTLIL designs with bugpoint +-------------------------------------- + +Yosys provides the `bugpoint` command for reducing a failing design to the +smallest portion of that design which still results in failure. While initially +developed for Yosys crashes, `bugpoint` can also be used for designs that lead +to non-fatal errors, or even failures in other tools that use the output of a +Yosys script. + +.. note:: + + Make sure to back up your code (design source and yosys script(s)) before + making any modifications. Even if the code itself isn't important, this can + help avoid "losing" the error while trying to debug it. + +Can I use bugpoint? +~~~~~~~~~~~~~~~~~~~ + +The first thing to be aware of is that `bugpoint` is not available in every +build of Yosys. Because the command works by invoking external processes, it +requires that Yosys can spawn executables. Notably this means `bugpoint` is not +able to be used in WebAssembly builds such as that available via YoWASP. The +easiest way to check your build of Yosys is by running ``yosys -h bugpoint``. If +Yosys displays the help text for `bugpoint` then it is available for use. + +.. code-block:: console + :caption: `bugpoint` is unavailable + + $ yosys -h bugpoint + + -- Running command `help bugpoint' -- + No such command or cell type: bugpoint + +Next you need to separate loading the design from the failure point; you should +be aiming to reproduce the failure by running ``yosys -s -s +``. If the failure occurs while loading the design, such as during +`read_verilog` you will instead have to minimize the input design yourself. +Check out the instructions for :ref:`using_yosys/bugpoint:minimizing verilog +designs` below. + +.. note:: + + You should also be able to run the two scripts separately, calling first + ``yosys -s -p 'write_rtlil design.il'`` and then ``yosys -s + design.il``. If this doesn't work then it may mean that the + failure isn't reproducible from RTLIL and `bugpoint` won't work either. + +When we talk about failure points here, it doesn't just mean crashes or errors +in Yosys. The ```` script can also be a user-defined failure such +as the `select` command with one of the ``-assert-*`` options; an example where +this might be useful is when a pass is supposed to remove a certain kind of +cell, but there is some edge case where the cell is not removed. Another +use-case would be minimizing a design which fails with the `equiv_opt` command, +suggesting that the optimization in question alters the circuit in some way. + +It is even possible to use `bugpoint` with failures *external* to Yosys, by +making use of the `exec` command in ````. This is especially useful +when Yosys is outputting an invalid design, or when some other tool is +incompatible with the design. Be sure to use the ``exec -expect-*`` options so +that the pass/fail can be detected correctly. Multiple calls to `exec` can be +made, or even entire shell scripts: + +.. code-block:: yoscrypt + + exec -expect-return 1 --bash + +Our final failure we can use with `bugpoint` is one returned by a wrapper +process, such as ``valgrind`` or ``timeout``. In this case you will be calling +something like `` yosys -s design.il``. Here, Yosys is +run under a wrapper process which checks for some failure state, like a memory +leak or excessive runtime. + + +How do I use bugpoint? +~~~~~~~~~~~~~~~~~~~~~~ + +At this point you should have: + +1. either an RTLIL file containing the design to minimize (referred to here as + ``design.il``), or a Yosys script, ````, which loads it; and +2. a Yosys script, ````, which produces the failure and returns a + non-zero return status. + +Now call ``yosys -qq -s design.il`` and take note of the error(s) +that get printed. A template script, ````, is provided here which +you can use. Make sure to configure it with the correct filenames and use only +one of the methods to load the design. Fill in the ``-grep`` option with the +error message printed just before. If you are using a wrapper process for your +failure state, add the ``-runner ""`` option to the `bugpoint` call. + +.. code-block:: yoscrypt + :caption: ```` template script + + # Load design + read_rtlil design.il + ## OR + script + + # Call bugpoint with failure + bugpoint -script -grep "" + + # Save minimized design + write_rtlil min.il + +The ``-grep`` option is used to search the log file generated by the Yosys under +test. If the error message is generated by something else, such as a wrapper +process or compiler sanitizer, then you should instead use ``-err_grep``. For +an OS error, like a SEGFAULT, you can also use ``-expect-return`` to check the +error code returned. + +.. note:: + + Checking the error message or return status is optional, but highly + recommended. `bugpoint` can quite easily introduce bugs by creating + malformed designs that commands were not intended to handle. By having some + way to check the error, `bugpoint` can ensure that it is the *right* error + being reproduced. This is even more important when ```` contains + more than one command. + +By default, `bugpoint` is able to remove any part of the design. In order to +keep certain parts, for instance because you already know they are related to +the failure, you can use the ``bugpoint_keep`` attribute. This can be done with +``(* bugpoint_keep *)`` in Verilog, ``attribute \bugpoint_keep 1`` in RTLIL, or +``setattr -set bugpoint_keep 1 [selection]`` from a Yosys script. It is also +possible to limit `bugpoint` to only removing certain *kinds* of objects, such +as only removing entire modules or cells (instances of modules). For more about +the options available, check ``help bugpoint`` or :cmd:title:`bugpoint`. + +In some situations, it may also be helpful to use `setenv` before `bugpoint` to +set environment variables for the spawned processes. An example of this is +``setenv UBSAN_OPTIONS halt_on_error=1`` for where you are trying to raise an +error on undefined behaviour but only want the child process to halt on error. + +.. note:: + + Using `setenv` in this way may or may not affect the current process. For + instance the ``UBSAN_OPTIONS halt_on_error`` here only affects child + processes, as does the :doc:`Yosys environment variable` + ``ABC`` because they are only read on start-up. While others, such as + ``YOSYS_NOVERIFIC`` and ``HOME``, are evaluated each time they are used. + +Once you have finished configuration, you can now run ``yosys ``. +The first thing `bugpoint` will do is test the input design fails. If it +doesn't, make sure you are using the right ``yosys`` executable; unless the +``-yosys`` option is provided, it will use whatever the shell defaults to, *not* +the current ``yosys``. If you are using the ``-runner`` option, try replacing +the `bugpoint` command with ``write_rtlil test.il`` and then on a new line, +``! yosys -s test.il`` to check it works as expected and +returns a non-zero status. + +.. seealso:: + + For more on script parsing and the use of ``!``, check out + :ref:`getting_started/scripting_intro:script parsing`. + +Depending on the size of your design, and the length of your ````, +`bugpoint` may take some time; remember, it will run ``yosys -s `` +on each iteration of the design. The bigger the design, the more iterations. +The longer the ````, the longer each iteration will take. As the +design shrinks and `bugpoint` converges, each iteration should take less and +less time. Once all simplifications are exhausted and there are no more objects +that can be removed, the script will continue and the minimized design can be +saved. + + +What do I do with the minimized design? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First off, check the minimized design still fails. This is especially important +if you're not using `write_rtlil` to output the minimized design. For example, +if you ran :ref:`bugpoint_script` below, then calling ``yosys -s +min.v`` should still fail in the same way. + +.. code-block:: yoscrypt + :caption: example `bugpoint` minimizer + :name: bugpoint_script + + read_verilog design.v + bugpoint -script + write_verilog min.v + +The `write_rtlil` command is generally more reliable, since `bugpoint` will have +run that exact code through the failing script. Other ``write_*`` commands +convert from the RTLIL and then back again during the ``read_*`` which can +result in differences which mean the design no longer fails. + +.. note:: + + Simply calling Yosys with the output of ``write_*``, as in ``yosys -s + min.v``, does not guarantee that the corresponding ``read_*`` + will be used. For more about this, refer to + :doc:`/using_yosys/more_scripting/load_design`, or load the design explicitly + with ``yosys -p 'read_verilog min.v' -s ``. + +Once you've verified the failure still happens, check out +:ref:`using_yosys/bugpoint:identifying issues` for more on what to do next. + + +Minimizing Verilog designs +-------------------------- + +.. seealso:: + + This section is not specific to Yosys, so feel free to use another guide such + as Stack Overflow's `How to create a Minimal, Reproducible Example`_. + +Be sure to check any errors or warnings for messages that might identify source +lines or object names that might be causing the failure, and back up your source +code before modifying it. If you have multiple source files, you should start +by reducing them down to a single file. If a specific file is failing to read, +try removing everything else and just focus on that one. If your source uses +the ``include`` directive, replace it with the contents of the file referenced. + +Unlike RTLIL designs where we can use `bugpoint`, Yosys does not provide any +tools for minimizing Verilog designs. Instead, you should use an external tool +like `C-Reduce`_ (with the ``--not-c`` flag) or `sv-bugpoint`_. + +.. _C-Reduce: https://github.com/csmith-project/creduce +.. _sv-bugpoint: https://github.com/antmicro/sv-bugpoint + +C-Reduce +~~~~~~~~ + +As a very brief overview for using C-Reduce, you want your failing source design +(``test.v``), and some shell script which checks for the error being +investigated (``test.sh``). Below is an :ref:`egtest` which uses `logger` and +the ``-expect error "" 1`` option to perform a similar role to +``bugpoint -grep``, along with ``verilator`` to lint the code and make sure it +is still valid. + +.. code-block:: bash + :caption: Example test.sh for C-Reduce + :name: egtest + + #!/bin/bash + verilator --lint-only test.v &&/ + yosys -p 'logger -expect error "unsupported" 1; read_verilog test.v' + +.. code-block:: verilog + :caption: input test.v + + module top(input clk, a, b, c, output x, y, z); + always @(posedge clk) begin + if (a == 1'b1) + $stop; + end + assign x = a; + assign y = a ^ b; + assign z = c; + endmodule + +In this example ``read_verilog test.v`` is giving an error message that contains +the string "unsupported" because the ``$stop`` system task is only supported in +``initial`` blocks. By calling ``creduce ./test.sh test.v --not-c`` we can +minimize the design to just the failing code, while still being valid Verilog. + +.. code-block:: verilog + :caption: output test.v + + module a; + always begin $stop; + end endmodule + + +sv-bugpoint +~~~~~~~~~~~ + +sv-bugpoint works quite similarly to C-Reduce, except it requires an output +directory to be provided and the check script needs to accept the target file as +an input argument: ``sv-bugpoint outDir/ test.sh test.v`` + +.. code-block:: bash + :caption: Example test.sh for sv-bugpoint + + #!/bin/bash + verilator --lint-only $1 &&/ + yosys -p "logger -expect error \"unsupported\" 1; read_verilog $1" + +Notice that the commands for ``yosys -p`` are now in double quotes (``"``), and +the quotes around the error string are escaped (``\"``). This is necessary for +the ``$1`` argument subsitution to work correctly. + + +Doing it manually +~~~~~~~~~~~~~~~~~ + +If for some reason you are unable to use a tool to minimize your code, you can +still do it manually. But it can be a time consuming process and requires a lot +of iteration. At any point in the process, you can check for anything that is +unused or totally disconnected (ports, wires, etc) and remove them. If a +specific module is causing the problem, try to set that as the top module +instead. Any parameters should have their default values changed to match the +failing usage. + +As a rule of thumb, try to split things roughly in half at each step; similar to +a "binary search". If you have 10 cells (instances of modules) in your top +module, and have no idea what is causing the issue, split them into two groups +of 5 cells. For each group of cells, try remove them and see if the failure +still happens. If the error still occurs with the first group removed, but +disappears when the second group is removed, then the first group can be safely +removed. If a module has no more instances, remove it entirely. Repeat this +for each remaining group of cells until each group only has 1 cell in it and no +more cells can be removed without making the error disappear. You can also +repeat this for each module still in your design. + +After minimizing the number of cells, do the same for the process blocks in your +top module. And again for any generate blocks and combinational blocks. +Remember to check for any ports or signals which are no longer used and remove +those too. Any signals which are written but never read can also be removed. + +.. note:: + + Depending on where the design is failing, there are some commands which may + help in identifying unused objects in the design. `hierarchy` will identify + which modules are used and which are not, but check for ``$paramod`` modules + before removing unused ones. ``debug clean`` will list all unused wires in + each module, as well as unused cells which were automatically generated + (giving the line number of the source that generated them). Adding the + ``-purge`` flag will also include named wires that would normally be ignored + by `clean`. Though when there are large numbers of unused wires it is often + easier to just delete sections of the code and see what happens. + +Next, try to remove or reduce assignments (``a = b``) and operations (``a + +b``). A good place to start is by checking for any wires/registers which are +read but never written. Try removing the signal declaration and replacing +references to it with ``'0`` or ``'x``. Do this with any constants too. Try to +replace strings with numeric values, and wide signals with smaller ones, then +see if the error persists. + +Check if there are any operations that you can simplify, like replacing ``a & +'0`` with ``'0``. If you have enable or reset logic, try removing it and see if +the error still occurs. Try reducing ``if .. else`` and ``case`` blocks to a +single case. Even if that doesn't work, you may still be able to remove some +paths; start with cases that appear to be unreachable and go from there. + +.. note:: + + When sharing code on the `Yosys GitHub`_, please try to keep things in + English. Declarations and strings should stick to the letters a-z and + numbers 0-9, unless the error is arising because of the names/characters + used. + + +Identifying issues +------------------ + +When identifying issues, it is quite useful to understand the conditions under +which the issue is occurring. While there are occasionally bugs that affect a +significant number of designs, Yosys changes are tested on a variety of designs +and operating systems which typically catch any such issues before they make it +into the main branch. So what is is it about your situation that makes it +unusual? + +.. note:: + + If you have access to a different platform you could also check if your issue + is reproducible there. Some issues may be specific to the platform or build + of Yosys. + +Try to match the minimized design back to its original context. Could you +achieve the same thing a different way, and if so, does this other method have +the same issue? Try to change the design in small ways and see what happens; +while `bugpoint` can reduce and simplify a design, it doesn't *change* much. +What happens if you change operators, for example a left shift (or `$shl`) to a +right shift (or `$shr`)? Try to see if the issue is tied to specific +parameters, widths, or values. + +Search `the existing issues`_ and see if someone has already made a bug report. +This is where changing the design and finding the limits of what causes the +failure really comes in handy. If you're more familiar with how the problem can +arise, you may be able to find a related issue more easily. If an issue already +exists for one case of the problem but you've found other cases, you can comment +on the issue and help get it solved. If there are no existing or related issues +already, then check out the steps for +:ref:`yosys_internals/extending_yosys/contributing:reporting bugs`. + +.. _the existing issues: https://github.com/YosysHQ/yosys/issues + +.. warning:: + + If you are using a fuzzer to find bugs, follow the instructions for + :doc:`/yosys_internals/extending_yosys/advanced_bugpoint`. **Do not** open + more than one fuzzer generated issue at a time if you can not identify the + root cause. If you are found to be doing this, your issues may be closed + without further investigation. diff --git a/docs/source/using_yosys/index.rst b/docs/source/using_yosys/index.rst index 55bd5c291..93dd3629b 100644 --- a/docs/source/using_yosys/index.rst +++ b/docs/source/using_yosys/index.rst @@ -15,3 +15,6 @@ ways Yosys can interact with designs for a deeper investigation. synthesis/index more_scripting/index + bugpoint + verilog + pyosys diff --git a/docs/source/using_yosys/more_scripting/interactive_investigation.rst b/docs/source/using_yosys/more_scripting/interactive_investigation.rst index c6180306d..0d1a17503 100644 --- a/docs/source/using_yosys/more_scripting/interactive_investigation.rst +++ b/docs/source/using_yosys/more_scripting/interactive_investigation.rst @@ -73,7 +73,7 @@ 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. +load. Signals that are multiple bits wide are shown as thick arrows. Finally *processes* are shown in boxes with round corners. Processes are Yosys' internal representation of the decision-trees and synchronization events @@ -311,8 +311,8 @@ 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 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Design Investigation +~~~~~~~~~~~~~~~~~~~~ Yosys can also be used to investigate designs (or netlists created from other tools). @@ -323,10 +323,10 @@ tools). 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. +- :cmd:title:`show`. +- :cmd:title:`dump`. +- :cmd:title:`add` and :cmd:title:`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|_. @@ -358,7 +358,7 @@ reorganizing a module in Yosys and checking the resulting circuit. .. figure:: /_images/code_examples/scrambler/scrambler_p02.* :class: width-helper invert-helper -Analyzing the resulting circuit with :doc:`/cmd/eval`: +Analyzing the resulting circuit with :cmd:title:`eval`: .. todo:: replace inline code diff --git a/docs/source/using_yosys/more_scripting/load_design.rst b/docs/source/using_yosys/more_scripting/load_design.rst index bbc55a36b..9aa028418 100644 --- a/docs/source/using_yosys/more_scripting/load_design.rst +++ b/docs/source/using_yosys/more_scripting/load_design.rst @@ -1,11 +1,107 @@ Loading a design -~~~~~~~~~~~~~~~~ +---------------- -keyword: Frontends +.. _input files: -- :doc:`/cmd/read_verilog` +Input files on the command line +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. todo:: include ``read_verilog < ``read -vlog2k`` + + ``.sv`` -> ``read -sv`` + + ``.vhd`` and ``.vhdl`` -> ``read -vhdl`` + + ``.blif`` and ``.eblif`` -> `read_blif` + + ``.json`` -> `read_json` + + ``.il`` -> `read_rtlil` (direct textual representation of Yosys internal + state) + +- command line also supports + + + ``.ys`` -> `script` + + ``.tcl`` -> `tcl` + + ``-`` -> reads stdin and treats it as a script + +The `read` command +~~~~~~~~~~~~~~~~~~ + +- standard method of loading designs +- also for defining macros and include directories +- uses `verific` command if available + + + ``-verific`` and ``-noverific`` options to enforce with/without Verific + + check ``help read`` for more about the options available and the filetypes + supported + + elaborate designs with ``verific -import [options] `` (or use + `hierarchy`) + +- fallback to `read_verilog` with ``-defer`` option + + + does not compile design until `hierarchy` command as discussed in + :doc:`/getting_started/example_synth` + + more similar to `verific` behaviour + +- ``read -define`` et al mapped to `verific` or `verilog_defines` +- similarly, ``read -incdir`` et al mapped to `verific` or `verilog_defaults` + +.. note:: + + The Verific frontend for Yosys, which provides the :cmd:ref:`verific` + command, requires Yosys to be built with Verific. For full functionality, + custom modifications to the Verific source code from YosysHQ are required, + but limited useability can be achieved with some stock Verific builds. Check + :doc:`/yosys_internals/extending_yosys/build_verific` for more. + +.. _Frontend: + +Yosys frontends +~~~~~~~~~~~~~~~ + +- :doc:`/cmd/index_frontends` +- typically start with ``read_`` +- built-in support for heredocs + + + in-line code with ``<`_ in + Python. + - ``std::set`` and hashlib ``pool`` support the same methods as ``set``\s in + Python. While ``set`` is ordered, ``pool`` is not and modifications may + cause a complete reordering of the set. + - ``dict`` supports the same methods as ``dict``\s in Python, albeit it is + unordered, and modifications may cause a complete reordering of the + dictionary. + - ``idict`` uses a custom set of methods because it doesn't map very cleanly + to an existing Python data structure. See ``pyosys/hashlib.h`` for more + info. + + For most operations, the Python equivalents are also supported as arguments + where they will automatically be cast to the right type, so you do not have + to manually instantiate the right underlying C++ object(s) yourself. + +Modifying the Database +---------------------- + +.. warning:: + + Any modifications to the database may invalidate previous references held + by Python, just as if you were writing C++. Pyosys does not currently attempt + to keep deleted objects alive if a reference is held by Python. + +You are not restricted to inspecting the database either: you have the ability +to modify it, and introduce new elements and/or changes to your design. + +As a demonstrative example, let's assume we want to add an enable line to all +flip-flops in our fiedler-cooley design. + +First of all, we will run :yoscrypt:`synth` to convert all of the logic to +Yosys's internal cell structure (see :ref:`sec:celllib_gates`): + +.. literalinclude:: /code_examples/pyosys/simple_database.py + :start-after: # synth + :end-before: adding the enable line + :language: python + +Next, we need to add the new port. The method for this is ``Module::addWire``\. + +.. tip:: + + IdString is Yosys's internal representation of strings used as identifiers + within Verilog designs. They are efficient as only integers are stored and + passed around, but they can be translated to and from normal strings at will. + + Pyosys will automatically cast Python strings to IdStrings for you, but the + rules around IdStrings apply, namely that *broadly*: + + - Identifiers for internal cells must start with ``$``\. + - All other identifiers must start with ``\``\. + +.. literalinclude:: /code_examples/pyosys/simple_database.py + :start-after: adding the enable line + :end-before: hooking the enable line + :language: python + +Notice how we modified the wire then called a method to make Yosys re-process +the ports. + +Next, we can iterate over all constituent cells, and if they are of the type +``$_DFF_P_``, we do two things: + +1. Change their type to ``$_DFFE_PP_`` to enable hooking up an enable signal. +2. Hooking up the enable signal. + +.. literalinclude:: /code_examples/pyosys/simple_database.py + :start-after: hooking the enable line + :end-before: run check + :language: python + +To verify that you did everything correctly, it is prudent to call ``.check()`` +on the module you're manipulating as follows after you're done with a set of +changes: + +.. literalinclude:: /code_examples/pyosys/simple_database.py + :start-after: run check + :end-before: write output + :language: python + +And then finally, write your outputs. Here, I choose an intermediate Verilog +file and :yoscrypt:`synth_ice40` to map it to the iCE40 architecture. + +.. literalinclude:: /code_examples/pyosys/simple_database.py + :start-after: write output + :language: python + +And voilà, you will note that in the intermediate output, all ``always @`` +statements should have an ``if (enable)``\. + +Encapsulating as Passes +----------------------- + +Just like when writing C++, you can encapsulate routines in terms of "passes", +which adds your Pass to a global registry of commands accessible using +``run_pass``\. + +.. literalinclude:: /code_examples/pyosys/pass.py + :language: python + +In general, abstract classes and virtual methods are not really supported by +Pyosys due to their complexity, but there are two exceptions which are: + +- ``Pass`` in ``kernel/register.h`` +- ``Monitor`` in ``kernel/rtlil.h`` diff --git a/docs/source/using_yosys/synthesis/abc.rst b/docs/source/using_yosys/synthesis/abc.rst index 91de775e6..ba12cabc1 100644 --- a/docs/source/using_yosys/synthesis/abc.rst +++ b/docs/source/using_yosys/synthesis/abc.rst @@ -176,5 +176,6 @@ 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. +reduce design area by mapping other logic to slower cells with greater logic +density. diff --git a/docs/source/using_yosys/synthesis/fsm.rst b/docs/source/using_yosys/synthesis/fsm.rst index 6fad81d54..07a3cc9cc 100644 --- a/docs/source/using_yosys/synthesis/fsm.rst +++ b/docs/source/using_yosys/synthesis/fsm.rst @@ -10,7 +10,7 @@ other commands: :start-after: #end: :caption: Passes called by `fsm` -See also :doc:`/cmd/fsm`. +See also :doc:`/cmd/index_passes_fsm`. The algorithms used for FSM detection and extraction are influenced by a more general reported technique :cite:p:`fsmextract`. diff --git a/docs/source/using_yosys/synthesis/memory.rst b/docs/source/using_yosys/synthesis/memory.rst index a8f2280f7..9b81fb6dc 100644 --- a/docs/source/using_yosys/synthesis/memory.rst +++ b/docs/source/using_yosys/synthesis/memory.rst @@ -26,7 +26,7 @@ Some quick notes: decoder logic and registers. For more information about `memory`, such as disabling certain sub commands, see -:doc:`/cmd/memory`. +:doc:`/cmd/index_passes_memory`. Example ------- diff --git a/docs/source/using_yosys/synthesis/opt.rst b/docs/source/using_yosys/synthesis/opt.rst index 743b24997..56330bc37 100644 --- a/docs/source/using_yosys/synthesis/opt.rst +++ b/docs/source/using_yosys/synthesis/opt.rst @@ -11,8 +11,8 @@ 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: +this pass after each major step in the synthesis script. This macro command +calls the following ``opt_*`` commands: .. literalinclude:: /code_examples/macro_commands/opt.ys :language: yoscrypt @@ -192,6 +192,13 @@ control inputs. Called with ``-nodffe`` and ``-nosdff``, this pass is used to prepare a design for :doc:`/using_yosys/synthesis/fsm`. +Hierarchical optimization - `opt_hier` pass +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This pass considers the design hierarchy and propagates unused signals, constant +signals, and tied-together signals across module boundaries to facilitate +optimization by other passes. + Removing unused cells and wires - `opt_clean` pass ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -226,7 +233,5 @@ Other optimizations .. todo:: more on the other optimizations -- :doc:`/cmd/wreduce` -- :doc:`/cmd/peepopt` -- :doc:`/cmd/share` +- Check :doc:`/cmd/index_passes_opt` for more. - `abc` and `abc9`, see also: :doc:`abc`. diff --git a/docs/source/using_yosys/synthesis/proc.rst b/docs/source/using_yosys/synthesis/proc.rst index b7cd348d7..c4ab0f0ea 100644 --- a/docs/source/using_yosys/synthesis/proc.rst +++ b/docs/source/using_yosys/synthesis/proc.rst @@ -17,7 +17,7 @@ commands in a sensible order: After all the ``proc_*`` commands, `opt_expr` is called. This can be disabled by calling :yoscrypt:`proc -noopt`. For more information about `proc`, such as -disabling certain sub commands, see :doc:`/cmd/proc`. +disabling certain sub commands, see :doc:`/cmd/index_passes_proc`. Many commands can not operate on modules with "processess" in them. Usually a call to `proc` is the first command in the actual synthesis procedure after diff --git a/docs/source/using_yosys/synthesis/synth.rst b/docs/source/using_yosys/synthesis/synth.rst index e3d82931b..db81d0790 100644 --- a/docs/source/using_yosys/synthesis/synth.rst +++ b/docs/source/using_yosys/synthesis/synth.rst @@ -6,44 +6,23 @@ Synth commands Packaged ``synth_*`` commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following is a list of all synth commands included in Yosys for different -platforms. Each command runs a script of sub commands specific to the platform -being targeted. Note that not all of these scripts are actively maintained and -may not be up-to-date. - -- :doc:`/cmd/synth_achronix` -- :doc:`/cmd/synth_anlogic` -- :doc:`/cmd/synth_coolrunner2` -- :doc:`/cmd/synth_easic` -- :doc:`/cmd/synth_ecp5` -- :doc:`/cmd/synth_efinix` -- :doc:`/cmd/synth_fabulous` -- :doc:`/cmd/synth_gatemate` -- :doc:`/cmd/synth_gowin` -- :doc:`/cmd/synth_greenpak4` -- :doc:`/cmd/synth_ice40` -- :doc:`/cmd/synth_intel` (MAX10, Cyclone IV) -- :doc:`/cmd/synth_intel_alm` (Cyclone V, Arria V, Cyclone 10 GX) -- :doc:`/cmd/synth_lattice` -- :doc:`/cmd/synth_nexus` -- :doc:`/cmd/synth_quicklogic` -- :doc:`/cmd/synth_sf2` -- :doc:`/cmd/synth_xilinx` +A list of all synth commands included in Yosys for different platforms can be +found under :doc:`/cmd/index_techlibs`. Each command runs a script of sub +commands specific to the platform being targeted. Note that not all of these +scripts are actively maintained and may not be up-to-date. General synthesis ~~~~~~~~~~~~~~~~~ In addition to the above hardware-specific synth commands, there is also -:doc:`/cmd/prep`. This command is limited to coarse-grain synthesis, without +:cmd:title:`prep`. This command is limited to coarse-grain synthesis, without getting into any architecture-specific mappings or optimizations. Among other things, this is useful for design verification. The following commands are executed by the `prep` command: -.. literalinclude:: /cmd/prep.rst +.. literalinclude:: /code_examples/macro_commands/prep.ys :start-at: begin: - :end-before: .. only:: latex - :dedent: :doc:`/getting_started/example_synth` covers most of these commands and what they do. diff --git a/docs/source/using_yosys/synthesis/techmap_synth.rst b/docs/source/using_yosys/synthesis/techmap_synth.rst index 9e9d42df5..54a715342 100644 --- a/docs/source/using_yosys/synthesis/techmap_synth.rst +++ b/docs/source/using_yosys/synthesis/techmap_synth.rst @@ -33,9 +33,9 @@ reader may find this map file as :file:`techlibs/common/techmap.v` in the Yosys source tree. Additional features have been added to techmap to allow for conditional mapping -of cells (see :doc:`/cmd/techmap`). This can for example be useful if the target -architecture supports hardware multipliers for certain bit-widths but not for -others. +of cells (see :doc:`/cmd/index_passes_techmap`). This can for example be useful +if the target architecture supports hardware multipliers for certain bit-widths +but not for others. A usual synthesis flow would first use the techmap pass to directly map some RTL cells to coarse-grain cells provided by the target architecture (if any) and diff --git a/docs/source/yosys_internals/verilog.rst b/docs/source/using_yosys/verilog.rst similarity index 93% rename from docs/source/yosys_internals/verilog.rst rename to docs/source/using_yosys/verilog.rst index a1941963d..a557360b7 100644 --- a/docs/source/yosys_internals/verilog.rst +++ b/docs/source/using_yosys/verilog.rst @@ -1,8 +1,6 @@ Notes 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 --------------------------------- @@ -11,7 +9,7 @@ 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 + IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002 - The ``tri``, ``triand`` and ``trior`` net types @@ -96,7 +94,7 @@ Verilog Attributes and non-standard features - 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 ``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. @@ -358,20 +356,37 @@ from SystemVerilog: files being read into the same design afterwards. - typedefs are supported (including inside packages) - - type casts are currently not supported + + - type casts are currently not supported - enums are supported (including inside packages) - - but are currently not strongly typed + + - 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 + + - 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. + - array assignment of unpacked arrays is currently not supported + - array literals are currently not supported + +- SystemVerilog interfaces (SVIs), including modports for specifying whether + ports are inputs or outputs, are partially supported. + + - interfaces must be provided as *named* arguments, not positional arguments. + i.e. ``foo bar(.intf(intf0), .x(x));`` is supported but ``foo bar(intf0, + x);`` is not. - Assignments within expressions are supported. + +- The ``unique``, ``unique0``, and ``priority`` SystemVerilog keywords are + supported on ``if`` and ``case`` conditionals. (The Verilog frontend + will process conditionals using these keywords by annotating their + representation with the appropriate ``full_case`` and/or ``parallel_case`` + attributes, which are described above.) + +- SystemVerilog string literals are supported (triple-quoted strings and + escape sequences such as line continuations and hex escapes). diff --git a/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst b/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst new file mode 100644 index 000000000..22e4b1b7a --- /dev/null +++ b/docs/source/yosys_internals/extending_yosys/advanced_bugpoint.rst @@ -0,0 +1,271 @@ +Identifying the root cause of bugs +================================== + +This document references Yosys internals and is intended for people interested +in solving or investigating Yosys bugs. This also applies if you are using a +fuzzing tool; fuzzers have a tendency to find many variations of the same bug, +so identifying the root cause is important for avoiding issue spam. + +If you're familiar with C/C++, you might try to have a look at the source code +of the command that's failing. Even if you can't fix the problem yourself, it +can be very helpful for anyone else investigating if you're able to identify +where the issue is arising. + + +Finding the failing command +--------------------------- + +Using the ``-L`` flag can help here, allowing you to specify a file to log to, +such as ``yosys -L out.log -s script.ys``. Most commands will print a header +message when they begin; something like ``2.48. Executing HIERARCHY pass +(managing design hierarchy).`` The last header message will usually be the +failing command. There are some commands which don't print a header message, so +you may want to add ``echo on`` to the start of your script. The `echo` command +echoes each command executed, along with any arguments given to it. For the +`hierarchy` example above this might be ``yosys> hierarchy -check``. + +.. note:: + + It may also be helpful to use the `log` command to add messages which you can + then search for either in the terminal or the logfile. This can be quite + useful if your script contains script-passes, like the + :doc:`/using_yosys/synthesis/synth`, which call many sub-commands and you're + not sure exactly which script-pass is calling the failing command. + + +Minimizing scripts +------------------ + +.. warning:: + + This section is intended as **advanced usage**, and generally not necessary + for normal bug reports. + +If you're using a command line prompt, such as ``yosys -p 'synth_xilinx' -o +design.json design.v``, consider converting it to a script. It's generally much +easier to iterate over changes to a script in a file rather than one on the +command line, as well as being better for sharing with others. + +.. code-block:: yoscrypt + :caption: example script, ``script.ys``, for prompt ``yosys -p 'synth_xilinx' -o design.json design.v`` + + read_verilog design.v + synth_xilinx + write_json design.json + +Next up you want to remove everything *after* the error occurs. If your final +command calls sub-commands, replace it with its contents first. In the case of +the :doc:`/using_yosys/synthesis/synth`, as well as certain other script-passes, +you can use the ``-run`` option to simplify this. For example we can replace +``synth -top -lut`` with the :ref:`replace_synth`. The options ``-top + -lut`` can be provided to each `synth` step, or to just the step(s) where +it is relevant, as done here. + +.. code-block:: yoscrypt + :caption: example replacement script for `synth` command + :name: replace_synth + + synth -top -run :coarse + synth -lut -run coarse:fine + synth -lut -run fine:check + synth -run check: + +Say we ran :ref:`replace_synth` and were able to remove the ``synth -run +check:`` and still got our error, then we check the log and we see the last +thing before the error was ``7.2. Executing MEMORY_MAP pass (converting memories +to logic and flip-flops)``. By checking the output of ``yosys -h synth`` (or the +`synth` help page) we can see that the `memory_map` pass is called in the +``fine`` step. We can then update our script to the following: + +.. code-block:: yoscrypt + :caption: example replacement script for `synth` when `memory_map` is failing + + synth -top -run :fine + opt -fast -full + memory_map + +By giving `synth` the option ``-run :fine``, we are telling it to run from the +beginning of the script until the ``fine`` step, where we then give it the exact +commands to run. There are some cases where the commands given in the help +output are not an exact match for what is being run, but are instead a +simplification. If you find that replacing the script-pass with its contents +causes the error to disappear, or change, try calling the script-pass with +``echo on`` to see exactly what commands are being called and what options are +used. + +.. warning:: + + Before continuing further, *back up your code*. The following steps can + remove context and lead to over-minimizing scripts, hiding underlying issues. + Check out :ref:`yosys_internals/extending_yosys/advanced_bugpoint:Why + context matters` to learn more. + +When a problem is occurring many steps into a script, minimizing the design at +the start of the script isn't always enough to identify the cause of the issue. +Each extra step of the script can lead to larger sections of the input design +being needed for the specific problem to be preserved until it causes a crash. +So to find the smallest possible reproducer it can sometimes be helpful to +remove commands prior to the failure point. + +The simplest way to do this is by writing out the design, resetting the current +state, and reading back the design: + +.. code-block:: yoscrypt + + write_rtlil ; design -reset; read_rtlil ; + +In most cases, this can be inserted immediately before the failing command while +still producing the error, allowing you to :ref:`minimize your +RTLIL` with the +```` output. For our previous example with `memory_map`, if +:ref:`map_reset` still gives the same error, then we should now be able to call +``yosys design.il -p 'memory_map'`` to reproduce it. + +.. code-block:: yoscrypt + :caption: resetting the design immediately before failure + :name: map_reset + + synth -top -run :fine + opt -fast -full + write_rtlil design.il; design -reset; read_rtlil design.il; + memory_map + +If that doesn't give the error (or doesn't give the same error), then you should +try to move the write/reset/read earlier in the script until it does. If you +have no idea where exactly you should put the reset, the best way is to use a +"binary search" type approach, reducing the possible options by half after each +attempt. + +.. note:: + + By default, `write_rtlil` doesn't include platform specific IP blocks and + other primitive cell models which are typically loaded with a ``read_verilog + -lib`` command at the start of the synthesis script. You may have to + duplicate these commands *after* the call to ``design -reset``. It is also + possible to write out *everything* with ``select =*; write_rtlil -selected + ``. + +As an example, your script has 16 commands in it before failing on the 17th. If +resetting immediately before the 17th doesn't reproduce the error, try between +the 8th and 9th (8 is half of the total 16). If that produces the error then +you can remove everything before the `read_rtlil` and try reset again in the +middle of what's left, making sure to use a different name for the output file +so that you don't overwrite what you've already got. If the error isn't +produced then you need to go earlier still, so in this case you would do between +the 4th and 5th (4 is half of the previous 8). Repeat this until you can't +reduce the remaining commands any further. + +A more conservative, but more involved, method is to remove or comment out +commands prior to the failing command. Each command, or group of commands, can +be disabled one at a time while checking if the error still occurs, eventually +giving the smallest subset of commands needed to take the original input through +to the error. The difficulty with this method is that depending on your design, +some commands may be necessary even if they aren't needed to reproduce the +error. For example, if your design includes ``process`` blocks, many commands +will fail unless you run the `proc` command. While this approach can do a +better job of maintaining context, it is often easier to *recover* the context +after the design has been minimized for producing the error. For more on +recovering context, checkout +:ref:`yosys_internals/extending_yosys/advanced_bugpoint:Why context matters`. + + +Why context matters +------------------- + +Sometimes when a command is raising an error, you're seeing a symptom rather +than the underlying issue. It's possible that an earlier command may be putting +the design in an invalid state, which isn't picked up until the error is raised. +This is particularly true for the pre-packaged +:doc:`/using_yosys/synthesis/synth`, which rely on a combination of generic and +architecture specific passes. As new features are added to Yosys and more +designs are supported, the types of cells output by a pass can grow and change; +and sometimes this leads to a mismatch in what a pass is intended to handle. + +If you minimized your script, and removed commands prior to the failure to get a +smaller reproducer, try to work backwards and find which commands may have +contributed to the design failing. From the minimized design you should have +some understanding of the cell or cells which are producing the error; but where +did those cells come from? The name and/or type of the cell can often point you +in the right direction: + +.. code-block:: + + # internal cell types start with a $ + # lowercase for word-level, uppercase for bit-level + $and + $_AND_ + + # cell types with $__ are typically intermediate cells used in techmapping + $__MUL16X16 + + # cell types without a $ are either user-defined or architecture specific + my_module + SB_MAC16 + + # object names might give you the name of the pass that created them + $procdff$1204 + $memory\rom$rdmux[0][0][0]$a$1550 + + # or even the line number in the Yosys source + $auto$muxcover.cc:557:implement_best_cover$2152 + $auto$alumacc.cc:495:replace_alu$1209 + +Try running the unminimized script and search the log for the names of the +objects in your minimized design. In the case of cells you can also search for +the type of the cell. Remember that calling `stat` will list all the types of +cells currently used in the design, and ``select -list =*`` will list the names +of of all the current objects. You can add these commands to your script, or +use an interactive terminal to run each command individually. Adding them to +the script can be more repeatable, but if it takes a long time to run to the +point you're interested in then an interactive shell session can give you more +flexibility once you reach that point. You can also add a call to the `shell` +command at any point in a script to start an interactive session at a given +point; allowing you to script any preparation steps, then come back once it's +done. + +The ``--dump-design`` option +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Yosys provides the ``--dump-design`` option (or ``-P`` for short) for dumping +the design at specific steps of the script based on the log header. If the last +step before an error is ``7.2. Executing MEMORY_MAP pass (converting memories to +logic and flip-flops)``, then calling Yosys with ``--dump-design 7.2:bad.il`` +will save the design *before* this command runs, in the file ``bad.il``. + +It is also possible to use this option multiple times, e.g. ``-P2:hierarchy.il +-P7 -P7.2:bad.il``, to get multiple dumps in the same run. This can make it +easier to follow the design through each step to find where certain cells or +connections are coming from. ``--dump-design ALL`` is also allowed, writing out +the design at each log header. + +A worked example +~~~~~~~~~~~~~~~~ + +Say you did all the minimization and found that an error in `synth_xilinx` +occurs when a call to ``techmap -map +/xilinx/cells_map.v`` with +``MIN_MUX_INPUTS`` defined parses a `$_MUX16_` with all inputs set to ``1'x``. +You could fix the bug in ``+/xilinx/cells_map.v``, but that might only solve +this one case while leaving other problems that haven't been found yet. So you +step through the original script, calling `stat` after each step to find when +the `$_MUX16_` is added. + +You find that the `$_MUX16_` is introduced by a call to `muxcover`, but all the +inputs are defined, so calling `techmap` now works as expected. From running +`bugpoint` with the failing techmap you know that the cell with index ``2297`` +will fail, so you call ``select top/*$2297`` to limit to just that cell. This +can then be saved with ``design -save pre_bug`` or ``write_rtlil -selected +pre_bug.il``, so that you don't have to re-run all the earlier steps to get back +here. + +Next you step through the remaining commands and call `dump` after each to find +when the inputs are disconnected. You find that ``opt -full`` has optimized +away portions of the circuit, leading to `opt_expr` setting the undriven mux +inputs to ``x``, but failing to remove the now unnecessary `$_MUX16_`. Now +you've identified a problem in `opt_expr` that affects all of the wide muxes, +and could happen in any synthesis flow, not just `synth_xilinx`. + +.. seealso:: + + This example is taken from `YosysHQ/yosys#4590 + `_ and can be reproduced with a + version of Yosys between 0.45 and 0.51. diff --git a/docs/source/yosys_internals/extending_yosys/contributing.rst b/docs/source/yosys_internals/extending_yosys/contributing.rst index 69258aa5f..70170fc48 100644 --- a/docs/source/yosys_internals/extending_yosys/contributing.rst +++ b/docs/source/yosys_internals/extending_yosys/contributing.rst @@ -7,7 +7,7 @@ Contributing to Yosys |CONTRIBUTING|_ file. .. |CONTRIBUTING| replace:: :file:`CONTRIBUTING.md` -.. _CONTRIBUTING: https://github.com/YosysHQ/yosys/CONTRIBUTING.md +.. _CONTRIBUTING: https://github.com/YosysHQ/yosys/blob/main/CONTRIBUTING.md Coding Style ------------ @@ -42,3 +42,137 @@ for implicit type casts, always use ``GetSize(foobar)`` instead of ``foobar.size()``. (``GetSize()`` is defined in :file:`kernel/yosys.h`) Use range-based for loops whenever applicable. + + +Reporting bugs +-------------- + +- use the `bug report template`_ + +.. _bug report template: https://github.com/YosysHQ/yosys/issues/new?template=bug_report.yml + +- short title briefly describing the issue, e.g. + + techmap of wide mux with undefined inputs raises error during synth_xilinx + + + tells us what's happening ("raises error") + + gives the command affected (`techmap`) + + an overview of the input design ("wide mux with undefined inputs") + + and some context where it was found ("during `synth_xilinx`") + + +Reproduction Steps +~~~~~~~~~~~~~~~~~~ + +- ideally a code-block (starting and ending with triple backquotes) containing + the minimized design (Verilog or RTLIL), followed by a code-block containing + the minimized yosys script OR a command line call to yosys with + code-formatting (starting and ending with single backquotes) + +.. code-block:: markdown + + min.v + ```verilog + // minimized Verilog design + ``` + + min.ys + ``` + read_verilog min.v + # minimum sequence of commands to reproduce error + ``` + + OR + + `yosys -p ': minimum sequence of commands;' min.v` + +- alternatively can provide a single code-block which includes the minimized + design as a "here document" followed by the sequence of commands which + reproduce the error + + + see :doc:`/using_yosys/more_scripting/load_design` for more on heredocs. + +.. code-block:: markdown + + ``` + read_rtlil </path/to/file#L139-L147`` + + clicking on "Preview" should reveal a code block containing the lines of + source specified, with a link to the source file at the given commit + + +Additional details +~~~~~~~~~~~~~~~~~~ + +- once you have created the issue, any additional details can be added as a + comment on that issue +- could include any additional context as to what you were doing when you first + encountered the bug +- was this issue discovered through the use of a fuzzer +- if you've minimized the script, consider including the `bugpoint` script you + used, or the original script, e.g. + +.. code-block:: markdown + + Minimized with + ``` + read_verilog design.v + # original sequence of commands prior to error + bugpoint -script -grep "" + write_rtlil min.il + ``` + + OR + + Minimized from + `yosys -p ': original sequence of commands to produce error;' design.v` + +- if you're able to, it may also help to share the original un-minimized design + + + if the design is too big for a comment, consider turning it into a `Gist`_ + +.. _Gist: https://gist.github.com/ diff --git a/docs/source/yosys_internals/extending_yosys/extensions.rst b/docs/source/yosys_internals/extending_yosys/extensions.rst index d30dd2bae..74a7d72d6 100644 --- a/docs/source/yosys_internals/extending_yosys/extensions.rst +++ b/docs/source/yosys_internals/extending_yosys/extensions.rst @@ -9,7 +9,7 @@ Writing extensions .. todo:: update to use :file:`/code_examples/extensions/test*.log` This chapter contains some bits and pieces of information about programming -yosys extensions. Don't be afraid to ask questions on the YosysHQ Slack. +yosys extensions. Don't be afraid to ask questions on the YosysHQ Discourse. .. todo:: mention coding guide diff --git a/docs/source/yosys_internals/extending_yosys/index.rst b/docs/source/yosys_internals/extending_yosys/index.rst index 4ee21517b..72843ecd6 100644 --- a/docs/source/yosys_internals/extending_yosys/index.rst +++ b/docs/source/yosys_internals/extending_yosys/index.rst @@ -11,6 +11,7 @@ of interest for developers looking to customise Yosys builds. extensions build_verific functional_ir + advanced_bugpoint contributing test_suites diff --git a/docs/source/yosys_internals/extending_yosys/test_suites.rst b/docs/source/yosys_internals/extending_yosys/test_suites.rst index 6627fdbdd..eeee863aa 100644 --- a/docs/source/yosys_internals/extending_yosys/test_suites.rst +++ b/docs/source/yosys_internals/extending_yosys/test_suites.rst @@ -1,7 +1,72 @@ Testing Yosys ============= -.. TODO:: more about the included test suite and how to add tests +.. todo:: adding tests (makefile-tests vs seed-tests) + +Running the included test suite +------------------------------- + +The Yosys source comes with a test suite to avoid regressions and keep +everything working as expected. Tests can be run by calling ``make test`` from +the root Yosys directory. + +Functional tests +~~~~~~~~~~~~~~~~ + +Testing functional backends (see +:doc:`/yosys_internals/extending_yosys/functional_ir`) has a few requirements in +addition to those listed in :ref:`getting_started/installation:Build +prerequisites`: + +.. tab:: Ubuntu + + .. code:: console + + sudo apt-get install racket + raco pkg install rosette + pip install pytest-xdist pytest-xdist-gnumake + +.. tab:: macOS + + .. code:: console + + brew install racket + raco pkg install rosette + pip install pytest-xdist pytest-xdist-gnumake + +If you don't have one of the :ref:`getting_started/installation:CAD suite(s)` +installed, you should also install Z3 `following their +instructions `_. + +Then, set the :makevar:`ENABLE_FUNCTIONAL_TESTS` make variable when calling +``make test`` and the functional tests will be run as well. + +Unit tests +~~~~~~~~~~ + +Running the unit tests requires the following additional packages: + +.. tab:: Ubuntu + + .. code:: console + + sudo apt-get install libgtest-dev + +.. tab:: macOS + + No additional requirements. + +Unit tests can be run with ``make unit-test``. + +Docs tests +~~~~~~~~~~ + +There are some additional tests for checking examples included in the +documentation, which can be run by calling ``make test`` from the +:file:`yosys/docs` sub-directory (or ``make -C docs test`` from the root). This +also includes checking some macro commands to ensure that descriptions of them +are kept up to date, and is mostly intended for CI. + Automatic testing ----------------- diff --git a/docs/source/yosys_internals/flow/verilog_frontend.rst b/docs/source/yosys_internals/flow/verilog_frontend.rst index e53466687..8381641b3 100644 --- a/docs/source/yosys_internals/flow/verilog_frontend.rst +++ b/docs/source/yosys_internals/flow/verilog_frontend.rst @@ -47,9 +47,9 @@ 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. +The lexer keeps track of the current location in the Verilog source code with +a ``VerilogLexer::out_loc`` and uses it to construct parser-defined +symbol objects. Finally the lexer identifies and handles special comments such as "``// synopsys translate_off``" and "``// synopsys full_case``". (It is recommended to use @@ -178,21 +178,22 @@ properties: - | 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. + code location by the ``AST::AstNode`` constructor. The ``location`` type + is a manual reimplementation of the bison-provided location type. This + type is defined at ``frontends/verilog/verilog_location.h``. -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 +The ``AST::AstNode`` constructor can be called with up to 4 child nodes. This simplifies the creation of AST nodes for simple expressions a bit. For example the bison code for parsing multiplications: .. code:: none - :number-lines: + :number-lines: - basic_expr '*' attr basic_expr { - $$ = new AstNode(AST_MUL, $1, $4); - append_attr($$, $3); - } | + basic_expr TOK_ASTER attr basic_expr { + $$ = std::make_unique(AST_MUL, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), $3); + } | The generated AST data structure is then passed directly to the AST frontend that performs the actual conversion to RTLIL. @@ -204,7 +205,7 @@ tree respectively. Transforming AST to RTLIL ------------------------- -The AST Frontend converts a set of modules in AST representation to modules in +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. @@ -626,7 +627,7 @@ pass and the passes it launches: | This pass replaces the ``RTLIL::SyncRule``\ s to d-type flip-flops (with asynchronous resets if necessary). -- | `proc_dff` +- | `proc_memwr` | This pass replaces the ``RTLIL::MemWriteAction``\ s with `$memwr` cells. - | `proc_clean` diff --git a/docs/source/yosys_internals/formats/rtlil_rep.rst b/docs/source/yosys_internals/formats/rtlil_rep.rst index b0cbfe3a5..dbd69c7e4 100644 --- a/docs/source/yosys_internals/formats/rtlil_rep.rst +++ b/docs/source/yosys_internals/formats/rtlil_rep.rst @@ -194,17 +194,18 @@ RTLIL::SigSpec A "signal" is everything that can be applied to a cell port. I.e. -- | Any constant value of arbitrary bit-width +- | A bit from a wire (``RTLIL::SigBit``) + | 1em For example: ``mywire[24]`` + +- | A range of bits from a wire (wire variant of ``RTLIL::SigChunk``) + | 1em For example: ``mywire, mywire[15:8]`` + +- | Any constant value of arbitrary bit-width (``std::vector>`` variant of ``RTLIL::SigChunk``) | 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. +The ``RTLIL::SigSpec`` data type is used to represent signals. +It contains a single ``RTLIL::SigChunk`` or a vector of ``RTLIL::SigBit``. +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. diff --git a/docs/source/yosys_internals/hashing.rst b/docs/source/yosys_internals/hashing.rst index c6e22c0cf..1255f6f7c 100644 --- a/docs/source/yosys_internals/hashing.rst +++ b/docs/source/yosys_internals/hashing.rst @@ -36,7 +36,7 @@ The main characteristics are: 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: +creates a bijective map from ``K`` to incrementing integers. For example: :: @@ -45,9 +45,9 @@ creates a bijective map from ``K`` to the integers. For example: 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 + log("%s\n", si[42])); // will print hello + log("%s\n", si[43])); // will print world + log("%s\n", si[44])); // will throw exception It is not possible to remove elements from an idict. @@ -138,7 +138,7 @@ Previously, the interface to implement hashing on custom types was just 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`. +based on the existance and value of ``YS_HASHING_VERSION``. .. code-block:: cpp :caption: Example hash compatibility wrapper diff --git a/docs/source/yosys_internals/index.rst b/docs/source/yosys_internals/index.rst index 3dd4224fa..483cc2bf8 100644 --- a/docs/source/yosys_internals/index.rst +++ b/docs/source/yosys_internals/index.rst @@ -38,5 +38,4 @@ as reference to implement a similar system in any language. formats/index extending_yosys/index techmap - verilog hashing diff --git a/docs/util/cellref.py b/docs/util/cell_documenter.py similarity index 100% rename from docs/util/cellref.py rename to docs/util/cell_documenter.py diff --git a/docs/util/cmd_documenter.py b/docs/util/cmd_documenter.py new file mode 100644 index 000000000..9347d8ffd --- /dev/null +++ b/docs/util/cmd_documenter.py @@ -0,0 +1,443 @@ +#!/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__) + +# cmd signature +cmd_ext_sig_re = re.compile( + r'''^ ([\w/]+::)? # optional group + ([\w$._]+?) # module name + (?:\.([\w_]+))? # optional: thing name + (::[\w_]+)? # attribute + \s* $ # and nothing more + ''', re.VERBOSE) + +class YosysCmdContentListing: + type: str + body: str + source_file: str + source_line: int + options: dict[str, str] + content: list[YosysCmdContentListing] + + def __init__( + self, + type: str = "", + body: str = "", + source_file: str = "unknown", + source_line: int = 0, + options: dict[str, str] = {}, + content: list[dict[str]] = [], + ): + self.type = type + self.body = body + self.source_file = source_file + self.source_line = source_line + self.options = options + self.content = [YosysCmdContentListing(**c) for c in content] + +class YosysCmd: + name: str + title: str + content: list[YosysCmdContentListing] + group: str + source_file: str + source_line: int + source_func: str + experimental_flag: bool + internal_flag: bool + + def __init__( + self, + name:str = "", title:str = "", + content: list[dict[str]] = [], + group: str = 'unknown', + source_file: str = "", + source_line: int = 0, + source_func: str = "", + experimental_flag: bool = False, + internal_flag: bool = False, + ) -> None: + self.name = name + self.title = title + self.content = [YosysCmdContentListing(**c) for c in content] + self.group = group + self.source_file = source_file + self.source_line = source_line + self.source_func = source_func + self.experimental_flag = experimental_flag + self.internal_flag = internal_flag + +class YosysCmdGroupDocumenter(Documenter): + objtype = 'cmdgroup' + priority = 10 + object: tuple[str, list[str]] + lib_key = 'groups' + + option_spec = Documenter.option_spec.copy() + option_spec.update({ + 'caption': autodoc.annotation_option, + 'members': autodoc.members_option, + 'source': autodoc.bool_option, + 'linenos': autodoc.bool_option, + }) + + __cmd_lib: dict[str, list[str] | dict[str]] | None = None + @property + def cmd_lib(self) -> dict[str, list[str] | dict[str]]: + if not self.__cmd_lib: + self.__cmd_lib = {} + cmds_obj: dict[str, dict[str, dict[str]]] + try: + with open(self.config.cmds_json, "r") as f: + cmds_obj = json.loads(f.read()) + except FileNotFoundError: + logger.warning( + f"unable to find cmd lib at {self.config.cmds_json}", + type = 'cmdref', + subtype = 'cmd_lib' + ) + cmds_obj = {} + for (name, obj) in cmds_obj.get(self.lib_key, {}).items(): + self.__cmd_lib[name] = obj + return self.__cmd_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 cmd + try: + self.object = (self.modname, self.cmd_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: + pass + + def add_content(self, more_content: Any | None) -> None: + pass + + 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='cmdref') + + 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 cmd lib to import from + logger.warning( + f"don't know which cmd lib to import for autodocumenting {self.name}", + type = 'cmdref' + ) + return + + sourcename = self.get_sourcename() + + imported_object = self.import_object(); + if self.lib_key == 'groups' and self.name == 'unknown': + if imported_object: + logger.warning(f"Found commands assigned to group {self.name}: {[x[0] for x in self.object]}", type='cmdref') + else: + return + elif not imported_object: + log_msg = f"unable to load {self.name} with {type(self)}" + if self.lib_key == 'groups': + logger.info(log_msg, type = 'cmdref') + self.add_line(f'.. warning:: No commands found for group {self.name!r}', sourcename) + self.add_line('', sourcename) + self.add_line(' Documentation may have been built without ``source_location`` support.', sourcename) + self.add_line(' Try check :doc:`/cmd/index_other`.', sourcename) + else: + logger.warning(log_msg, type = 'cmdref') + return + + # check __module__ of object (for members not given explicitly) + # if check_module: + # if not self.check_module(): + # return + + 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='cmdref') + 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 YosysCmdDocumenter(YosysCmdGroupDocumenter): + objtype = 'cmd' + priority = 15 + object: YosysCmd + lib_key = 'cmds' + + @classmethod + def can_document_member( + cls, + member: Any, + membername: str, + isattr: bool, + parent: Any + ) -> bool: + if membername.startswith('$'): + return False + return isinstance(parent, YosysCmdGroupDocumenter) + + def parse_name(self) -> bool: + try: + matched = cmd_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='cmdref') + 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 = YosysCmd(self.modname, **self.object[1]) + return True + return False + + def get_sourcename(self) -> str: + try: + return self.object.source_file + except AttributeError: + return super().get_sourcename() + + def format_name(self) -> str: + return self.object.name + + def format_signature(self, **kwargs: Any) -> str: + return self.fullname + self.attribute + + def add_directive_header(self, sig: str) -> None: + domain = getattr(self, 'domain', self.objtype) + directive = getattr(self, 'directivetype', 'def') + source_name = self.object.source_file + source_line = self.object.source_line + + title = f'{self.object.name} - {self.object.title}' + self.add_line(title, source_name, source_line) + self.add_line('#' * len(title), source_name, source_line) + + # cmd definition + self.add_line(f'.. {domain}:{directive}:: {sig}', source_name, source_line) + if self.object.title: + self.add_line(f' :title: {self.object.title}', source_name, source_line) + + if self.options.noindex: + self.add_line(' :noindex:', source_name) + + def add_content(self, more_content: Any | None) -> None: + # set sourcename and add content from attribute documentation + domain = getattr(self, 'domain', self.objtype) + source_name = self.object.source_file + source_line = self.object.source_line + + if self.object.experimental_flag: + self.add_line(f'.. warning:: This command is experimental', source_name, source_line) + self.add_line('\n', source_name) + + if self.object.internal_flag: + self.add_line(f'.. warning:: This command is intended for internal developer use only', source_name, source_line) + self.add_line('\n', source_name) + + def render(content_list: YosysCmdContentListing, indent: int=0): + content_source = content_list.source_file or source_name + indent_str = ' '*indent + if content_list.type == 'usage': + if content_list.body: + self.add_line(f'{indent_str}.. {domain}:{content_list.type}:: {self.name}::{content_list.body}', content_source) + else: + self.add_line(f'{indent_str}.. {domain}:{content_list.type}:: {self.name}::', content_source) + self.add_line(f'{indent_str} :noindex:', source_name) + self.add_line('', source_name) + elif content_list.type == 'option': + self.add_line(f'{indent_str}:{content_list.type} {content_list.body}:', content_source) + elif content_list.type == 'text': + self.add_line(f'{indent_str}{content_list.body}', content_source) + self.add_line('', source_name) + elif content_list.type == 'code': + language_str = content_list.options.get('language', '') + self.add_line(f'{indent_str}.. code-block:: {language_str}', source_name) + self.add_line('', source_name) + for body_line in content_list.body.splitlines(): + self.add_line(f'{indent_str} {body_line}', content_source) + self.add_line('', source_name) + else: + logger.warning(f"unknown content type '{content_list.type}'") + for content in content_list.content: + render(content, indent+1) + + for content in self.object.content: + render(content) + + if self.get_sourcename() != 'unknown': + self.add_line('\n', source_name) + self.add_line(f'.. note:: Help text automatically generated from :file:`{source_name}:{source_line}`', source_name) + + # 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, [] + +class YosysCmdRstDocumenter(YosysCmdDocumenter): + objtype = 'cmd_rst' + priority = 0 + + @classmethod + def can_document_member(cls, *args) -> bool: + return False + + def add_directive_header(self, sig): + source_name = self.object.source_file + cmd = self.object.name + self.add_line(f'.. code-block:: rst', source_name) + self.add_line(f' :caption: Generated rst for ``.. autocmd:: {cmd}``', source_name) + + def add_content(self, more_content): + source_name = self.object.source_file + cmd = self.object.name + self.domain = 'cmd' + super().add_directive_header(cmd) + self.add_line('', source_name) + self.indent += self.content_indent + super().add_content(more_content) + +def setup(app: Sphinx) -> dict[str, Any]: + app.add_config_value('cmds_json', False, 'html', [Path, PosixPath, WindowsPath]) + app.setup_extension('sphinx.ext.autodoc') + app.add_autodocumenter(YosysCmdGroupDocumenter) + app.add_autodocumenter(YosysCmdDocumenter) + app.add_autodocumenter(YosysCmdRstDocumenter) + return { + 'version': '2', + 'parallel_read_safe': True, + } diff --git a/docs/util/cmdref.py b/docs/util/custom_directives.py similarity index 81% rename from docs/util/cmdref.py rename to docs/util/custom_directives.py index a31b08e0d..b90584aa7 100644 --- a/docs/util/cmdref.py +++ b/docs/util/custom_directives.py @@ -4,20 +4,21 @@ from __future__ import annotations import re from typing import cast +import warnings from docutils import nodes -from docutils.nodes import Node, Element, system_message +from docutils.nodes import Node, Element, Text 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.roles import XRefRole, SphinxRole 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.util.docfields import Field, GroupedField from sphinx import addnodes class TocNode(ObjectDescription): @@ -31,7 +32,7 @@ class TocNode(ObjectDescription): signode['ids'].append(idx) def _object_hierarchy_parts(self, sig_node: addnodes.desc_signature) -> tuple[str, ...]: - if 'fullname' not in sig_node: + if 'tocname' not in sig_node: return () modname = sig_node.get('module') @@ -57,16 +58,56 @@ class TocNode(ObjectDescription): return '.'.join(parents + [name]) return '' -class CommandNode(TocNode): +class NodeWithOptions(TocNode): + """A custom node with options.""" + + doc_field_types = [ + GroupedField('opts', label='Options', names=('option', 'options', 'opt', 'opts')), + ] + + def transform_content(self, contentnode: addnodes.desc_content) -> None: + """hack `:option -thing: desc` into a proper option list with yoscrypt highlighting""" + newchildren = [] + for node in contentnode: + newnode = node + if isinstance(node, nodes.field_list): + newnode = nodes.option_list() + for field in node: + is_option = False + option_list_item = nodes.option_list_item() + for child in field: + if isinstance(child, nodes.field_name): + option_group = nodes.option_group() + option_list_item += option_group + option = nodes.option() + option_group += option + name, text = child.rawsource.split(' ', 1) + is_option = name == 'option' + literal = nodes.literal(text=text) + literal['classes'] += ['code', 'highlight', 'yoscrypt'] + literal['language'] = 'yoscrypt' + option += literal + if not is_option: warnings.warn(f'unexpected option \'{name}\' in {field.source}') + elif isinstance(child, nodes.field_body): + description = nodes.description() + description += child.children + option_list_item += description + if is_option: + newnode += option_list_item + newchildren.append(newnode) + contentnode.children = newchildren + +class CommandNode(NodeWithOptions): """A custom node that describes a command.""" name = 'cmd' required_arguments = 1 - option_spec = { + option_spec = NodeWithOptions.option_spec.copy() + option_spec.update({ 'title': directives.unchanged, 'tags': directives.unchanged - } + }) def handle_signature(self, sig, signode: addnodes.desc_signature): signode['fullname'] = sig @@ -93,6 +134,46 @@ class CommandNode(TocNode): idx, 0)) +class CommandUsageNode(NodeWithOptions): + """A custom node that describes command usages""" + + name = 'cmdusage' + + option_spec = NodeWithOptions.option_spec + option_spec.update({ + 'usage': directives.unchanged, + }) + + def handle_signature(self, sig: str, signode: addnodes.desc_signature): + parts = sig.split('::') + if len(parts) > 2: parts.pop(0) + use = parts[-1] + signode['fullname'] = '::'.join(parts) + usage = self.options.get('usage', use) + if usage: + signode['tocname'] = usage + signode += addnodes.desc_name(text=usage) + 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 PropNode(TocNode): name = 'prop' fieldname = 'props' @@ -393,7 +474,7 @@ class TagIndex(Index): lis.append(( dispname, 0, docname, anchor, - docname, '', typ + '', '', '' )) ret = [(k, v) for k, v in sorted(content.items())] @@ -432,18 +513,19 @@ class CommandIndex(Index): Qualifier and description are not rendered e.g. in LaTeX output. """ - content = {} + content: dict[str, list[tuple]] = {} 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: + title = self.domain.data['obj2title'].get(name) lis = content.setdefault(self.shortname, []) lis.append(( dispname, 0, docname, anchor, - '', '', typ + '', '', title )) ret = [(k, v) for k, v in sorted(content.items())] @@ -507,16 +589,27 @@ class PropIndex(TagIndex): return (ret, True) +class TitleRefRole(XRefRole): + """XRefRole used which has the cmd title as the displayed text.""" + pass + +class OptionRole(SphinxRole): + def run(self) -> tuple[list[Node], list]: + return self.inliner.interpreted(self.rawtext, self.text, 'yoscrypt', self.lineno) + class CommandDomain(Domain): name = 'cmd' label = 'Yosys commands' roles = { - 'ref': XRefRole() + 'ref': XRefRole(), + 'title': TitleRefRole(), + 'option': OptionRole(), } directives = { 'def': CommandNode, + 'usage': CommandUsageNode, } indices = { @@ -542,7 +635,7 @@ class CommandDomain(Domain): 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] @@ -552,9 +645,17 @@ class CommandDomain(Domain): 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) + + if typ == 'title': + # caller wants the title in the content of the node + cmd = contnode.astext() + contnode = Text(f'{cmd} - {title}') + return make_refnode(builder, fromdocname, todocname, + targ, contnode) + else: + # cmd title as hover text + return make_refnode(builder, fromdocname, todocname, + targ, contnode, title) else: print(f"Missing ref for {target} in {fromdocname} ") return None @@ -592,10 +693,18 @@ class CellDomain(CommandDomain): 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}>' + words = text.split(' ') + if len(words) == 2 and words[0] == "help": + IsLinkable = True + thing = words[1] + else: + IsLinkable = len(words) == 1 and words[0][0] != '-' + thing = words[0] + if IsLinkable: + role = 'cell:ref' if thing[0] == '$' else 'cmd:ref' + text = f'{text} <{thing}>' + else: + role = 'yoscrypt' return inliner.interpreted(rawtext, text, role, lineno) def setup(app: Sphinx): @@ -622,4 +731,7 @@ def setup(app: Sphinx): app.add_role('autoref', autoref) - return {'version': '0.2'} + return { + 'version': '0.3', + 'parallel_read_safe': False, + } diff --git a/examples/cxx-api/scopeinfo_example.cc b/examples/cxx-api/scopeinfo_example.cc index 0882ba804..fd5d2a781 100644 --- a/examples/cxx-api/scopeinfo_example.cc +++ b/examples/cxx-api/scopeinfo_example.cc @@ -77,9 +77,9 @@ struct ScopeinfoExamplePass : public Pass { continue; } - log("%s %s\n", wire_scope.first.path_str().c_str(), log_id(wire_scope.second)); + log("%s %s\n", wire_scope.first.path_str(), log_id(wire_scope.second)); for (auto src : index.sources(wire)) - log(" - %s\n", src.c_str()); + log(" - %s\n", src); } } } diff --git a/examples/python-api/pass.py b/examples/python-api/pass.py index dbef0a13f..d5e5868b0 100755 --- a/examples/python-api/pass.py +++ b/examples/python-api/pass.py @@ -1,22 +1,25 @@ #!/usr/bin/python3 -import libyosys as ys +from pyosys import libyosys as ys + +from pathlib import Path import matplotlib.pyplot as plt -import numpy as np + +__file_dir__ = Path(__file__).absolute().parent class CellStatsPass(ys.Pass): def __init__(self): super().__init__("cell_stats", "Shows cell stats as plot") - def py_help(self): + def help(self): ys.log("This pass uses the matplotlib library to display cell stats\n") - def py_execute(self, args, design): + def execute(self, args, design): ys.log_header(design, "Plotting cell stats\n") cell_stats = {} - for module in design.selected_whole_modules_warn(): + for module in design.all_selected_whole_modules(): for cell in module.selected_cells(): if cell.type.str() in cell_stats: cell_stats[cell.type.str()] += 1 @@ -26,7 +29,14 @@ class CellStatsPass(ys.Pass): plt.xticks(range(len(cell_stats)), list(cell_stats.keys())) plt.show() - def py_clear_flags(self): + def clear_flags(self): ys.log("Clear Flags - CellStatsPass\n") -p = CellStatsPass() +p = CellStatsPass() # register + +if __name__ == "__main__": + design = ys.Design() + ys.run_pass(f"read_verilog {__file_dir__.parents[1] / 'tests' / 'simple' / 'fiedler-cooley.v'}", design) + ys.run_pass("prep", design) + ys.run_pass("opt -full", design) + ys.run_pass("cell_stats", design) diff --git a/flake.nix b/flake.nix index 19ba59f17..1993b803b 100644 --- a/flake.nix +++ b/flake.nix @@ -41,7 +41,7 @@ packages.default = yosys; defaultPackage = yosys; devShell = pkgs.mkShell { - buildInputs = with pkgs; [ clang llvmPackages.bintools gcc bison flex libffi tcl readline python3 zlib git gtest abc-verifier verilog boost python3Packages.boost ]; + buildInputs = with pkgs; [ clang llvmPackages.bintools gcc bison flex libffi tcl readline python3 zlib git gtest abc-verifier verilog ]; }; } ); diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 37ace27fd..4df37c0cd 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -284,7 +284,7 @@ end_of_header: 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::IdString escaped_s = stringf("\\%s", s); RTLIL::Wire* wire; if (c == 'i') wire = inputs[l1]; else if (c == 'l') wire = latches[l1]; @@ -448,7 +448,7 @@ void AigerReader::parse_xaiger() bool success = ce.eval(o); log_assert(success); log_assert(o.wire == nullptr); - lut_mask.bits()[gray] = o.data; + lut_mask.set(gray, o.data); } RTLIL::Cell *output_cell = module->cell(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID)); log_assert(output_cell); @@ -476,7 +476,7 @@ void AigerReader::parse_xaiger() else if (c == 'n') { parse_xaiger_literal(f); f >> s; - log_debug("n: '%s'\n", s.c_str()); + log_debug("n: '%s'\n", s); } else if (c == 'h') { f.ignore(sizeof(uint32_t)); @@ -830,7 +830,7 @@ void AigerReader::post_process() log_debug(" -> %s\n", log_id(escaped_s)); } else { - RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); + RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index); existing = module->wire(indexed_name); if (!existing) module->rename(wire, indexed_name); @@ -877,7 +877,7 @@ void AigerReader::post_process() log_debug(" -> %s\n", log_id(escaped_s)); } else { - RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); + RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s, index); existing = module->wire(indexed_name); if (!existing) module->rename(wire, indexed_name); @@ -909,7 +909,7 @@ void AigerReader::post_process() module->rename(cell, escaped_s); } else - log_error("Symbol type '%s' not recognised.\n", type.c_str()); + log_error("Symbol type '%s' not recognised.\n", type); } } @@ -922,7 +922,7 @@ void AigerReader::post_process() RTLIL::Wire *wire = module->wire(name); if (wire) - module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0))); + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name, 0))); // Do not make ports with a mix of input/output into // wide ports @@ -942,7 +942,7 @@ void AigerReader::post_process() wire->port_output = port_output; for (int i = min; i <= max; i++) { - RTLIL::IdString other_name = stringf("%s[%d]", name.c_str(), i); + RTLIL::IdString other_name = stringf("%s[%d]", name, i); RTLIL::Wire *other_wire = module->wire(other_name); if (other_wire) { other_wire->port_input = false; @@ -971,9 +971,9 @@ void AigerReader::post_process() 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())); + module->rename(cell, stringf("$lut%s", y_port.wire->name)); else - module->rename(cell, stringf("$lut%s[%d]", y_port.wire->name.c_str(), y_port.offset)); + module->rename(cell, stringf("$lut%s[%d]", y_port.wire->name, y_port.offset)); } } diff --git a/frontends/aiger2/xaiger.cc b/frontends/aiger2/xaiger.cc index 616bec9e7..510da0be8 100644 --- a/frontends/aiger2/xaiger.cc +++ b/frontends/aiger2/xaiger.cc @@ -91,7 +91,7 @@ struct Xaiger2Frontend : public Frontend { 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()); + log_error("Failed to open map file '%s'\n", map_filename); unsigned int M, I, L, O, A; std::string header; @@ -110,7 +110,8 @@ struct Xaiger2Frontend : public Frontend { for (int i = 0; i < (int) O; i++) { int po; *f >> po; - log_assert(f->get() == '\n'); + int c = f->get(); + log_assert(c == '\n'); outputs.push_back(po); } @@ -388,7 +389,7 @@ struct Xaiger2Frontend : public Frontend { if (f->eof()) break; log_assert(!f->fail()); - log("input file: %s\n", scratch.c_str()); + log("input file: %s\n", scratch); } log_debug("co_counter=%d\n", co_counter); diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 431f7b4f8..984c4294c 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -38,9 +38,7 @@ 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; + bool sv_mode_but_global_and_used_for_literally_one_condition; unsigned long long astnodes = 0; unsigned long long astnode_count() { return astnodes; } } @@ -174,6 +172,7 @@ std::string AST::type2str(AstNodeType type) X(AST_MODPORT) X(AST_MODPORTMEMBER) X(AST_PACKAGE) + X(AST_IMPORT) X(AST_WIRETYPE) X(AST_TYPEDEF) X(AST_STRUCT) @@ -192,16 +191,16 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id) if (attributes.count(id) == 0) return false; - AstNode *attr = attributes.at(id); + auto& attr = attributes.at(id); if (attr->type != AST_CONSTANT) - attr->input_error("Attribute `%s' with non-constant value!\n", id.c_str()); + attr->input_error("Attribute `%s' with non-constant value!\n", id); 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) +AstNode::AstNode(AstSrcLocType loc, AstNodeType type, std::unique_ptr child1, std::unique_ptr child2, std::unique_ptr child3, std::unique_ptr child4) { static unsigned int hashidx_count = 123456789; hashidx_count = mkhash_xorshift(hashidx_count); @@ -209,7 +208,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch astnodes++; this->type = type; - filename = current_filename; + location = loc; is_input = false; is_output = false; is_reg = false; @@ -239,56 +238,73 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch in_param = false; if (child1) - children.push_back(child1); + children.push_back(std::move(child1)); if (child2) - children.push_back(child2); + children.push_back(std::move(child2)); if (child3) - children.push_back(child3); + children.push_back(std::move(child3)); if (child4) - children.push_back(child4); + children.push_back(std::move(child4)); fixup_hierarchy_flags(); } // create a (deep recursive) copy of a node -AstNode *AstNode::clone() const +std::unique_ptr 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 + auto that = std::make_unique(this->location, this->type); + cloneInto(*that.get()); return that; } // create a (deep recursive) copy of a node use 'other' as target root node -void AstNode::cloneInto(AstNode *other) const +void AstNode::cloneInto(AstNode &other) const { - AstNode *tmp = clone(); - tmp->in_lvalue_from_above = other->in_lvalue_from_above; - tmp->in_param_from_above = other->in_param_from_above; - other->delete_children(); - *other = *tmp; - tmp->children.clear(); - tmp->attributes.clear(); - other->fixup_hierarchy_flags(); - delete tmp; + other.type = type; + other.str = str; + other.bits = bits; + other.is_input = is_input; + other.is_output = is_output; + other.is_reg = is_reg; + other.is_logic = is_logic; + other.is_signed = is_signed; + other.is_string = is_string; + other.is_wand = is_wand; + other.is_wor = is_wor; + other.range_valid = range_valid; + other.range_swapped = range_swapped; + other.was_checked = was_checked; + other.is_unsized = is_unsized; + other.is_custom_type = is_custom_type; + other.port_id = port_id, + other.range_left = range_left, + other.range_right = range_right; + other.integer = integer; + other.realvalue = realvalue; + other.is_enum = is_enum; + other.dimensions = dimensions; + other.unpacked_dimensions = unpacked_dimensions; + other.id2ast = id2ast; + other.basic_prep = basic_prep; + other.lookahead = lookahead; + other.location = location; + other.in_lvalue = in_lvalue; + other.in_param = in_param; + // Keep in_lvalue_from_above and in_param_from_above untouched + + other.delete_children(); + for (auto& child : this->children) + other.children.push_back(child->clone()); + for (auto& [key, val] : this->attributes) + other.attributes[key] = (val->clone()); + // fixup to set flags on cloned children + other.fixup_hierarchy_flags(); } // 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(); } @@ -423,18 +439,18 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const { case AST_MODULE: fprintf(f, "%s" "module %s(", indent.c_str(), id2vl(str).c_str()); - for (auto child : children) + for (const 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) + for (const 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); + rem_children1.push_back(child.get()); for (auto child : rem_children1) if (child->type == AST_WIRE || child->type == AST_AUTOWIRE || child->type == AST_MEMORY) @@ -470,7 +486,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const fprintf(f, "%s" "reg", (is_input || is_output) ? " " : indent.c_str()); if (is_signed) fprintf(f, " signed"); - for (auto child : children) { + for (const auto& child : children) { fprintf(f, " "); child->dumpVlog(f, ""); } @@ -486,7 +502,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const fprintf(f, "%s" "memory", indent.c_str()); if (is_signed) fprintf(f, " signed"); - for (auto child : children) { + for (const auto& child : children) { fprintf(f, " "); child->dumpVlog(f, ""); if (first) @@ -500,7 +516,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const 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) { + for (const auto& child : children) { fprintf(f, first ? "(" : ", "); child->dumpVlog(f, ""); first = false; @@ -517,7 +533,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const else fprintf(f, "[%d:%d]", range_left, range_right); } else { - for (auto child : children) { + for (const auto& child : children) { fprintf(f, "%c", first ? '[' : ':'); child->dumpVlog(f, ""); first = false; @@ -527,13 +543,13 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const break; case AST_MULTIRANGE: - for (auto child : children) + for (const auto& child : children) child->dumpVlog(f, ""); break; case AST_ALWAYS: fprintf(f, "%s" "always @", indent.c_str()); - for (auto child : children) { + for (const auto& child : children) { if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) continue; fprintf(f, first ? "(" : ", "); @@ -541,7 +557,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const first = false; } fprintf(f, first ? "*\n" : ")\n"); - for (auto child : children) { + for (const auto& child : children) { if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) child->dumpVlog(f, indent + " "); } @@ -549,7 +565,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const case AST_INITIAL: fprintf(f, "%s" "initial\n", indent.c_str()); - for (auto child : children) { + for (const auto& child : children) { if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) child->dumpVlog(f, indent + " "); } @@ -562,7 +578,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const fprintf(f, "posedge "); if (type == AST_NEGEDGE) fprintf(f, "negedge "); - for (auto child : children) + for (const auto& child : children) child->dumpVlog(f, ""); break; @@ -574,7 +590,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const else fprintf(f, "%s", id2vl(str).c_str()); } - for (auto child : children) + for (const auto& child : children) child->dumpVlog(f, ""); break; @@ -602,7 +618,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const children[0]->dumpVlog(f, indent); } else { fprintf(f, "%s" "begin\n", indent.c_str()); - for (auto child : children) + for (const auto& child : children) child->dumpVlog(f, indent + " "); fprintf(f, "%s" "end\n", indent.c_str()); } @@ -618,7 +634,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const children[0]->dumpVlog(f, ""); fprintf(f, ")\n"); for (size_t i = 1; i < children.size(); i++) { - AstNode *child = children[i]; + const auto& child = children[i]; child->dumpVlog(f, indent + " "); } fprintf(f, "%s" "endcase\n", indent.c_str()); @@ -627,7 +643,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const case AST_COND: case AST_CONDX: case AST_CONDZ: - for (auto child : children) { + for (const auto& child : children) { if (child->type == AST_BLOCK) { fprintf(f, ":\n"); child->dumpVlog(f, indent + " "); @@ -663,7 +679,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const case AST_CONCAT: fprintf(f, "{"); for (int i = GetSize(children)-1; i >= 0; i--) { - auto child = children[i]; + const auto& child = children[i]; if (!first) fprintf(f, ", "); child->dumpVlog(f, ""); @@ -818,16 +834,16 @@ bool AstNode::contains(const AstNode *other) const { if (this == other) return true; - for (auto child : children) + for (const 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) +std::unique_ptr AstNode::mkconst_int(AstSrcLocType loc, uint32_t v, bool is_signed, int width) { - AstNode *node = new AstNode(AST_CONSTANT); + auto node = std::make_unique(loc, AST_CONSTANT); node->integer = v; node->is_signed = is_signed; for (int i = 0; i < width; i++) { @@ -841,9 +857,9 @@ AstNode *AstNode::mkconst_int(uint32_t v, bool is_signed, int width) } // 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) +std::unique_ptr AstNode::mkconst_bits(AstSrcLocType loc, const std::vector &v, bool is_signed, bool is_unsized) { - AstNode *node = new AstNode(AST_CONSTANT); + auto node = std::make_unique(loc, AST_CONSTANT); node->is_signed = is_signed; node->bits = v; for (size_t i = 0; i < 32; i++) { @@ -859,15 +875,15 @@ AstNode *AstNode::mkconst_bits(const std::vector &v, bool is_signe return node; } -AstNode *AstNode::mkconst_bits(const std::vector &v, bool is_signed) +std::unique_ptr AstNode::mkconst_bits(AstSrcLocType loc, const std::vector &v, bool is_signed) { - return mkconst_bits(v, is_signed, false); + return mkconst_bits(loc, 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) +std::unique_ptr AstNode::mkconst_str(AstSrcLocType loc, const std::vector &v) { - AstNode *node = mkconst_str(RTLIL::Const(v).decode_string()); + auto node = mkconst_str(loc, RTLIL::Const(v).decode_string()); while (GetSize(node->bits) < GetSize(v)) node->bits.push_back(RTLIL::State::S0); log_assert(node->bits == v); @@ -875,14 +891,14 @@ AstNode *AstNode::mkconst_str(const std::vector &v) } // create an AST node for a constant (using a string as value) -AstNode *AstNode::mkconst_str(const std::string &str) +std::unique_ptr AstNode::mkconst_str(AstSrcLocType loc, const std::string &str) { - AstNode *node; + std::unique_ptr 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); + node = AstNode::mkconst_int(loc, 0, false, 8); } else { std::vector data; data.reserve(str.size() * 8); @@ -893,7 +909,7 @@ AstNode *AstNode::mkconst_str(const std::string &str) ch = ch >> 1; } } - node = AstNode::mkconst_bits(data, false); + node = AstNode::mkconst_bits(loc, data, false); } node->is_string = true; @@ -902,18 +918,19 @@ AstNode *AstNode::mkconst_str(const std::string &str) } // 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) +std::unique_ptr AstNode::mktemp_logic(AstSrcLocType loc, 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++); + auto wire_owned = std::make_unique(loc, AST_WIRE, std::make_unique(loc, AST_RANGE, mkconst_int(loc, range_left, true), mkconst_int(loc, range_right, true))); + auto* wire = wire_owned.get(); + wire->str = stringf("%s%s:%d$%d", name, RTLIL::encode_filename(*location.begin.filename), location.begin.line, autoidx++); if (nosync) - wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(loc, 1, false)); wire->is_signed = is_signed; wire->is_logic = true; - mod->children.push_back(wire); + mod->children.push_back(std::move(wire_owned)); while (wire->simplify(true, 1, -1, false)) { } - AstNode *ident = new AstNode(AST_IDENTIFIER); + auto ident = std::make_unique(loc, AST_IDENTIFIER); ident->str = wire->str; ident->id2ast = wire; @@ -967,16 +984,17 @@ RTLIL::Const AstNode::asParaConst() const { if (type == AST_REALVALUE) { - AstNode *strnode = AstNode::mkconst_str(stringf("%f", realvalue)); + auto strnode = AstNode::mkconst_str(location, 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; + if (is_unsized) + val.flags |= RTLIL::CONST_FLAG_UNSIZED; return val; } @@ -1060,8 +1078,10 @@ RTLIL::Const AstNode::realAsConst(int width) bool is_negative = v < 0; if (is_negative) v *= -1; + RTLIL::Const::Builder b(width); for (int i = 0; i < width; i++, v /= 2) - result.bits().push_back((fmod(floor(v), 2) != 0) ? RTLIL::State::S1 : RTLIL::State::S0); + b.push_back((fmod(floor(v), 2) != 0) ? RTLIL::State::S1 : RTLIL::State::S0); + result = b.build(); if (is_negative) result = const_neg(result, result, false, false, result.size()); } @@ -1070,7 +1090,7 @@ RTLIL::Const AstNode::realAsConst(int width) 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); + return stringf("%s:%d.%d-%d.%d", location.begin.filename->c_str(), location.begin.line, location.begin.column, location.end.line, location.end.column); } void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast) @@ -1078,7 +1098,7 @@ 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) { +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); @@ -1086,29 +1106,29 @@ static bool param_has_no_default(const AstNode *param) { (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) +static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool defer, std::unique_ptr 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()); + log("Storing AST representation for module `%s'.\n", ast->str); else if (!quiet) { - log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str()); + log("Generating RTLIL representation for module `%s'.\n", ast->str); } AstModule *module = new AstModule; current_module = module; - module->ast = NULL; + module->ast = nullptr; 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; + std::unique_ptr ast_before_simplify; if (original_ast != NULL) - ast_before_simplify = original_ast; + ast_before_simplify = std::move(original_ast); else ast_before_simplify = ast->clone(); @@ -1125,15 +1145,15 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d 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()); + for (auto& node : ast->children) + if (node->type == AST_PARAMETER && param_has_no_default(node.get())) + node->input_error("Parameter `%s' has no default value and has not been overridden!\n", node->str); bool blackbox_module = flag_lib; if (!blackbox_module && !flag_noblackbox) { blackbox_module = true; - for (auto child : ast->children) { + for (const 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) @@ -1163,36 +1183,33 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d ast->dumpVlog(NULL, " "); log("--- END OF AST DUMP ---\n"); } - + for (auto &attr: ast->attributes) + log_assert((bool)attr.second.get()); if (flag_nowb && ast->attributes.count(ID::whitebox)) { - delete ast->attributes.at(ID::whitebox); ast->attributes.erase(ID::whitebox); } - + for (auto &attr: ast->attributes) + log_assert((bool)attr.second.get()); 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 (flag_lib && !flag_nowb) { + ast->attributes[ID::whitebox] = std::move( + ast->attributes[ID::lib_whitebox] + ); } + ast->attributes.erase(ID::lib_whitebox); } + for (auto &attr: ast->attributes) + log_assert((bool)attr.second.get()); if (!blackbox_module && ast->attributes.count(ID::blackbox)) { - AstNode *n = ast->attributes.at(ID::blackbox); + auto& 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); + auto& 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(); @@ -1200,62 +1217,59 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d if (ast->attributes.count(ID::noblackbox)) { if (blackbox_module) { - AstNode *n = ast->attributes.at(ID::noblackbox); + auto& 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); } - + for (auto &attr: ast->attributes) + log_assert((bool)attr.second.get()); 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) { + 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); + new_children.push_back(std::move(child)); } else if (child->type == AST_PARAMETER) { - new_children.push_back(child); + new_children.push_back(std::move(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; + new_children.push_back(std::move(child)); } } ast->children.swap(new_children); if (ast->attributes.count(ID::blackbox) == 0) { - ast->set_attribute(ID::blackbox, AstNode::mkconst_int(1, false)); + ast->set_attribute(ID::blackbox, AstNode::mkconst_int(ast->location, 1, false)); } } ignoreThisSignalsInInitial = RTLIL::SigSpec(); for (auto &attr : ast->attributes) { + log_assert((bool)attr.second.get()); if (attr.second->type != AST_CONSTANT) - ast->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); + ast->input_error("Attribute `%s' with non-constant value!\n", attr.first); module->attributes[attr.first] = attr.second->asAttrConst(); } for (size_t i = 0; i < ast->children.size(); i++) { - AstNode *node = ast->children[i]; + const auto& 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]; + const auto& node = ast->children[i]; if (node->type != AST_WIRE && node->type != AST_MEMORY && node->type != AST_INITIAL) node->genRTLIL(); } @@ -1263,7 +1277,7 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d ignoreThisSignalsInInitial.sort_and_unify(); for (size_t i = 0; i < ast->children.size(); i++) { - AstNode *node = ast->children[i]; + const auto& node = ast->children[i]; if (node->type == AST_INITIAL) node->genRTLIL(); } @@ -1277,14 +1291,14 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d continue; module->attributes[attr.first] = attr.second->asAttrConst(); } - for (const AstNode *node : ast->children) + for (const auto& 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->ast = std::move(ast_before_simplify); module->nolatches = flag_nolatches; module->nomeminit = flag_nomeminit; module->nomem2reg = flag_nomem2reg; @@ -1311,8 +1325,8 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d RTLIL::Module * AST_INTERNAL::process_and_replace_module(RTLIL::Design *design, RTLIL::Module *old_module, - AstNode *new_ast, - AstNode *original_ast) + AST::AstNode *new_ast, + std::unique_ptr 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. @@ -1335,7 +1349,7 @@ AST_INTERNAL::process_and_replace_module(RTLIL::Design *design, } // 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); + RTLIL::Module* new_module = process_module(design, new_ast, false, std::move(original_ast)); if (is_top) new_module->set_bool_attribute(ID::top); @@ -1347,17 +1361,17 @@ AST_INTERNAL::process_and_replace_module(RTLIL::Design *design, static void rename_in_package_stmts(AstNode *pkg) { std::unordered_set idents; - for (AstNode *item : pkg->children) + for (auto& item : pkg->children) idents.insert(item->str); - std::function rename = - [&rename, &idents, pkg](AstNode *node) { - for (AstNode *child : node->children) { + std::function&)> rename = + [&rename, &idents, pkg](std::unique_ptr& node) { + for (auto& child : node->children) { if (idents.count(child->str)) child->str = pkg->str + "::" + child->str.substr(1); rename(child); } }; - for (AstNode *item : pkg->children) + for (auto& item : pkg->children) if (item->type == AST_FUNCTION || item->type == AST_TASK) rename(item); } @@ -1390,18 +1404,18 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump ast->fixup_hierarchy_flags(true); log_assert(current_ast->type == AST_DESIGN); - for (AstNode *child : current_ast->children) + for (const auto& child : current_ast->children) { if (child->type == AST_MODULE || child->type == AST_INTERFACE) { - for (auto n : design->verilog_globals) + 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& 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()); + auto cloned_node = o->clone(); + // log("cloned node %s\n", type2str(cloned_node->type)); if (cloned_node->type == AST_ENUM) { for (auto &e : cloned_node->children) { log_assert(e->type == AST_ENUM_ITEM); @@ -1410,7 +1424,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump } else { cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1); } - child->children.push_back(cloned_node); + child->children.push_back(std::move(cloned_node)); } } @@ -1419,10 +1433,10 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump bool defer_local = defer; if (!defer_local) - for (const AstNode *node : child->children) - if (node->type == AST_PARAMETER && param_has_no_default(node)) + for (const auto& node : child->children) + if (node->type == AST_PARAMETER && param_has_no_default(node.get())) { - log("Deferring `%s' because it contains parameter(s) without defaults.\n", child->str.c_str()); + log("Deferring `%s' because it contains parameter(s) without defaults.\n", child->str); defer_local = true; break; } @@ -1434,7 +1448,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump 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()); + log_file_error(*child->location.begin.filename, child->location.begin.line, "Re-definition of module `%s'!\n", child->str); } else if (nooverwrite) { log("Ignoring re-definition of module `%s' at %s.\n", child->str.c_str(), child->loc_string().c_str()); @@ -1447,13 +1461,13 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump } } - process_module(design, child, defer_local); + process_module(design, child.get(), 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); + rename_in_package_stmts(child.get()); design->verilog_packages.push_back(child->clone()); current_scope.clear(); } @@ -1470,16 +1484,9 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump 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 @@ -1504,7 +1511,7 @@ std::pair AST::split_modport_from_type(std::string name interface_modport = seglist[1]; } else { // Erroneous port type - log_error("More than two '.' in signal port type (%s)\n", name_type.c_str()); + log_error("More than two '.' in signal port type (%s)\n", name_type); } } return std::pair(interface_type, interface_modport); @@ -1516,7 +1523,7 @@ 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 ch.get(); return NULL; } @@ -1524,7 +1531,8 @@ AstNode * AST::find_modport(AstNode *intf, std::string name) 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))); + auto loc = module_ast->location; + auto wire = std::make_unique(loc, AST_WIRE, std::make_unique(loc, AST_RANGE, AstNode::mkconst_int(loc, w->width -1, true), AstNode::mkconst_int(loc, 0, true))); std::string origname = log_id(w->name); std::string newname = intfname + "." + origname; wire->str = newname; @@ -1543,16 +1551,13 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule } } if (found_in_modport) { - module_ast->children.push_back(wire); - } - else { // If not found in modport, do not create port - delete wire; + module_ast->children.push_back(std::move(wire)); } } else { // If no modport, set inout wire->is_input = true; wire->is_output = true; - module_ast->children.push_back(wire); + module_ast->children.push_back(std::move(wire)); } } } @@ -1570,7 +1575,7 @@ bool AstModule::reprocess_if_necessary(RTLIL::Design *design) 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); + process_and_replace_module(design, this, ast.get(), NULL); return true; } } @@ -1583,32 +1588,33 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dictclone(); + auto new_ast = ast->clone(); + auto loc = ast->location; 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))); + auto wire = std::make_unique(loc, AST_WIRE, std::make_unique(loc, AST_RANGE, AstNode::mkconst_int(loc, w->width -1, true), AstNode::mkconst_int(loc, 0, true))); std::string newname = log_id(w->name); newname = intfname + "." + newname; wire->str = newname; - new_ast->children.push_back(wire); + new_ast->children.push_back(std::move(wire)); } } - AstNode *ast_before_replacing_interface_ports = new_ast->clone(); + auto 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]; + const auto& 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]; + const auto& 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; @@ -1616,11 +1622,11 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dictmodule(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); + auto celltype_for_intf = std::make_unique(loc, AST_CELLTYPE); celltype_for_intf->str = interface_type; - AstNode *cell_for_intf = new AstNode(AST_CELL, celltype_for_intf); + auto cell_for_intf = std::make_unique(loc, AST_CELL, std::move(celltype_for_intf)); cell_for_intf->str = name_port + "_inst_from_top_dummy"; - new_ast->children.push_back(cell_for_intf); + new_ast->children.push_back(std::move(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 @@ -1628,9 +1634,9 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dictmodules_ 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 + AstNode *modport = find_modport(ast_module_of_interface->ast.get(), 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); + explode_interface_port(new_ast.get(), intfmodule, name_port, modport); } break; } @@ -1642,9 +1648,7 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dictset_bool_attribute(ID::interfaces_replaced_in_module); @@ -1654,7 +1658,7 @@ void AstModule::expand_interfaces(RTLIL::Design *design, const dict ¶meters, const dict &interfaces, const dict &modports, bool /*mayfail*/) { - AstNode *new_ast = NULL; + std::unique_ptr new_ast = NULL; std::string modname = derive_common(design, parameters, &new_ast); // Since interfaces themselves may be instantiated with different parameters, @@ -1690,14 +1694,14 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict 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; + AstNode *ast_node_of_interface = ast_module_of_interface->ast.get(); 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); + explode_interface_port(new_ast.get(), intfmodule, intfname, modport); } - process_module(design, new_ast, false); + process_module(design, new_ast.get(), false); design->module(modname)->check(); RTLIL::Module* mod = design->module(modname); @@ -1720,7 +1724,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dictset_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()); + log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname); } } @@ -1731,10 +1735,9 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict 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); + process_module(design, new_ast.get(), false, NULL, quiet); design->module(modname)->check(); } else if (!quiet) { - log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); + log("Found cached RTLIL representation for module `%s'.\n", modname); } - delete new_ast; return modname; } @@ -1766,7 +1768,10 @@ static std::string serialize_param_value(const RTLIL::Const &val) { res.push_back('s'); if (val.flags & RTLIL::ConstFlags::CONST_FLAG_REAL) res.push_back('r'); - res += stringf("%d", GetSize(val)); + if (val.flags & RTLIL::ConstFlags::CONST_FLAG_UNSIZED) + res.push_back('u'); + else + res += stringf("%d", GetSize(val)); res.push_back('\''); res.append(val.as_string("?")); return res; @@ -1775,7 +1780,7 @@ static std::string serialize_param_value(const RTLIL::Const &val) { 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()); + para_info += stringf("%s=%s", elem.first, serialize_param_value(elem.second)); if (para_info.size() > 60) return "$paramod$" + sha1(para_info) + stripped_name; @@ -1784,7 +1789,7 @@ std::string AST::derived_module_name(std::string stripped_name, const std::vecto } // 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 AstModule::derive_common(RTLIL::Design *design, const dict ¶meters, std::unique_ptr* new_ast_out, bool quiet) { std::string stripped_name = name.str(); (*new_ast_out) = nullptr; @@ -1794,21 +1799,21 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict> named_parameters; - for (const auto child : ast->children) { + 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)); + log("Parameter %s = %s\n", child->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)); + log("Parameter %d (%s) = %s\n", para_counter, child->str, log_signal(it->second)); named_parameters.emplace_back(child->str, it->second); continue; } @@ -1822,45 +1827,45 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict rewritten; rewritten.reserve(GetSize(parameters)); - AstNode *new_ast = ast->clone(); + auto new_ast = ast->clone(); + auto loc = ast->location; if (!new_ast->attributes.count(ID::hdlname)) - new_ast->set_attribute(ID::hdlname, AstNode::mkconst_str(stripped_name.substr(1))); + new_ast->set_attribute(ID::hdlname, AstNode::mkconst_str(loc, stripped_name.substr(1))); para_counter = 0; - for (auto child : new_ast->children) { + 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)); + log("Parameter %s = %s\n", child->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)); + log("Parameter %d (%s) = %s\n", para_counter, child->str, log_signal(it->second)); goto rewrite_parameter; } continue; rewrite_parameter: - if (param_has_no_default(child)) + if (param_has_no_default(child.get())) 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] = std::make_unique(loc, 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()); + child->children[0] = AstNode::mkconst_str(loc, it->second.decode_string()); else - child->children[0] = AstNode::mkconst_bits(it->second.to_bits(), (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0); + child->children[0] = AstNode::mkconst_bits(loc, it->second.to_bits(), (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0, (it->second.flags & RTLIL::CONST_FLAG_UNSIZED) != 0); rewritten.insert(it->first); } @@ -1868,17 +1873,17 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict(loc, AST_DEFPARAM, std::make_unique(loc, 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())); + defparam->children.push_back(AstNode::mkconst_str(loc, 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); + defparam->children.push_back(AstNode::mkconst_bits(loc, param.second.to_bits(), (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0)); + new_ast->children.push_back(std::move(defparam)); } new_ast->fixup_hierarchy_flags(true); - (*new_ast_out) = new_ast; + new_ast_out->reset(new_ast.release()); return modname; } @@ -1924,11 +1929,9 @@ void AstModule::loadconfig() const flag_autowire = autowire; } -void AstNode::input_error(const char *format, ...) const +void AstNode::formatted_input_error(std::string str) const { - va_list ap; - va_start(ap, format); - logv_file_error(filename, location.first_line, format, ap); + log_formatted_file_error(*location.begin.filename, location.begin.line, std::move(str)); } YOSYS_NAMESPACE_END diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 2c2d408ce..fd8ecddd7 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -28,6 +28,7 @@ #include "kernel/rtlil.h" #include "kernel/fmt.h" +#include "frontends/verilog/verilog_location.h" #include #include @@ -153,6 +154,7 @@ namespace AST AST_MODPORT, AST_MODPORTMEMBER, AST_PACKAGE, + AST_IMPORT, AST_WIRETYPE, AST_TYPEDEF, @@ -162,12 +164,7 @@ namespace AST 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) {} - }; + using AstSrcLocType = Location; // convert an node type to a string (e.g. for debug output) std::string type2str(AstNodeType type); @@ -183,10 +180,10 @@ namespace AST AstNodeType type; // the list of child nodes for this node - std::vector children; + std::vector> children; // the list of attributes assigned to this node - std::map attributes; + std::map> attributes; bool get_bool_attribute(RTLIL::IdString id); // node content - most of it is unused in most node types @@ -212,7 +209,7 @@ namespace AST int unpacked_dimensions; // this is set by simplify and used during RTLIL generation - AstNode *id2ast; + AstNode* id2ast; // this is used by simplify to detect if basic analysis has been performed already on the node bool basic_prep; @@ -223,7 +220,6 @@ namespace AST // 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? @@ -234,9 +230,9 @@ namespace AST 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; + AstNode(AstSrcLocType loc, AstNodeType type = AST_NONE, std::unique_ptr child1 = nullptr, std::unique_ptr child2 = nullptr, std::unique_ptr child3 = nullptr, std::unique_ptr child4 = nullptr); + std::unique_ptr clone() const; + void cloneInto(AstNode &other) const; void delete_children(); ~AstNode(); @@ -265,14 +261,14 @@ namespace AST // 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); + std::unique_ptr 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_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 mem2reg_remove(pool &mem2reg_set); void meminfo(int &mem_width, int &mem_size, int &addr_bits); bool detect_latch(const std::string &var); const RTLIL::Module* lookup_cell_module(); @@ -288,7 +284,7 @@ namespace AST }; 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); + std::unique_ptr eval_const_function(AstNode *fcall, bool must_succeed); bool is_simple_const_expr(); // helper for parsing format strings @@ -305,29 +301,30 @@ namespace AST 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); + void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = nullptr); + void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = nullptr); // 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); + RTLIL::SigSpec genWidthRTLIL(int width, bool sgn, const dict *new_subst_ptr = nullptr); // compare AST nodes bool operator==(const AstNode &other) const; bool operator!=(const AstNode &other) const; bool contains(const AstNode *other) const; + AstNode operator=(AstNode) = delete; // 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); + static std::unique_ptr mkconst_int(AstSrcLocType loc, uint32_t v, bool is_signed, int width = 32); + static std::unique_ptr mkconst_bits(AstSrcLocType loc, const std::vector &v, bool is_signed, bool is_unsized); + static std::unique_ptr mkconst_bits(AstSrcLocType loc, const std::vector &v, bool is_signed); + static std::unique_ptr mkconst_str(AstSrcLocType loc, const std::vector &v); + static std::unique_ptr mkconst_str(AstSrcLocType loc, 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); + std::unique_ptr mktemp_logic(AstSrcLocType loc, 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); @@ -356,12 +353,12 @@ namespace AST // 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(); + std::unique_ptr clone_at_zero(); - void set_attribute(RTLIL::IdString key, AstNode *node) + void set_attribute(RTLIL::IdString key, std::unique_ptr node) { - attributes[key] = node; node->set_in_param_flag(true); + attributes[key] = std::move(node); } // helper to set in_lvalue/in_param flags from the hierarchy context (the actual flag @@ -377,11 +374,16 @@ namespace AST void fixup_hierarchy_flags(bool force_descend = false); // helpers for indexing - AstNode *make_index_range(AstNode *node, bool unpacked_range = false); + std::unique_ptr 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)); + [[noreturn]] void formatted_input_error(std::string str) const; + template + [[noreturn]] void input_error(FmtString...> fmt, const Args &... args) const + { + formatted_input_error(fmt.format(args...)); + } }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code @@ -391,12 +393,11 @@ namespace AST // 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; + std::unique_ptr 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); + std::string derive_common(RTLIL::Design *design, const dict ¶meters, std::unique_ptr* 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; @@ -406,9 +407,9 @@ namespace AST // 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)(); + // extern std::string current_filename; + // also set by the language frontend to control some AST processing + extern bool sv_mode_but_global_and_used_for_literally_one_condition; // for stats unsigned long long astnode_count(); @@ -418,7 +419,7 @@ namespace AST 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); + std::unique_ptr dpi_call(AstSrcLocType loc, 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); @@ -464,7 +465,7 @@ namespace AST_INTERNAL process_and_replace_module(RTLIL::Design *design, RTLIL::Module *old_module, AST::AstNode *new_ast, - AST::AstNode *original_ast = nullptr); + std::unique_ptr original_ast = nullptr); } YOSYS_NAMESPACE_END diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index d6fcc26bd..4c2a7fac9 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -40,7 +40,7 @@ static ffi_fptr resolve_fn (std::string symbol_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()); + log_error("unable to resolve '%s': can't find plugin `%s'\n", symbol_name, plugin_name); void *symbol = dlsym(loaded_plugins.at(plugin_name), real_symbol_name.c_str()); @@ -61,12 +61,12 @@ static ffi_fptr resolve_fn (std::string symbol_name) if (symbol != nullptr) return (ffi_fptr) symbol; - log_error("unable to resolve '%s'.\n", symbol_name.c_str()); + log_error("unable to resolve '%s'.\n", symbol_name); } -AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector &argtypes, const std::vector &args) +std::unique_ptr AST::dpi_call(AstSrcLocType loc, const std::string &rtype, const std::string &fname, const std::vector &argtypes, const std::vector> &args) { - AST::AstNode *newNode = nullptr; + std::unique_ptr 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); @@ -74,32 +74,32 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, ffi_cif cif; int status; - log("Calling DPI function `%s' and returning `%s':\n", fname.c_str(), rtype.c_str()); + log("Calling DPI function `%s' and returning `%s':\n", fname, rtype); 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)); + log(" arg %d (%s): %f\n", i, argtypes[i], 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)); + log(" arg %d (%s): %f\n", i, argtypes[i], 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)); + log(" arg %d (%s): %lld\n", i, argtypes[i], (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)); + log(" arg %d (%s): %llx\n", i, argtypes[i], (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); + log_error("invalid argtype '%s' for argument %d.\n", argtypes[i], i); } } @@ -116,7 +116,7 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, types[args.size()] = &ffi_type_pointer; values[args.size()] = &value_store[args.size()].ptr; } else { - log_error("invalid rtype '%s'.\n", rtype.c_str()); + log_error("invalid rtype '%s'.\n", rtype); } if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types.data())) != FFI_OK) @@ -125,11 +125,11 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values.data()); if (rtype == "real") { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(loc, 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 = std::make_unique(loc, AST_REALVALUE); newNode->realvalue = value_store[args.size()].f32; log(" return realvalue: %g\n", newNode->asReal(true)); } else if (rtype == "chandle") { @@ -137,10 +137,10 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, 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); + newNode = AstNode::mkconst_bits(loc, bits, false); log(" return chandle: %llx\n", (unsigned long long)newNode->asInt(false)); } else { - newNode = AstNode::mkconst_int(value_store[args.size()].i32, false); + newNode = AstNode::mkconst_int(loc, value_store[args.size()].i32, false); log(" return integer: %lld\n", (long long)newNode->asInt(true)); } @@ -153,9 +153,9 @@ YOSYS_NAMESPACE_END YOSYS_NAMESPACE_BEGIN -AST::AstNode *AST::dpi_call(const std::string&, const std::string &fname, const std::vector&, const std::vector&) +std::unique_ptr AST::dpi_call(AstSrcLocType, 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()); + log_error("Can't call DPI function `%s': this version of yosys is built without plugin support\n", fname); } YOSYS_NAMESPACE_END diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index d3982b92b..c26750c98 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -45,7 +45,7 @@ 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++); + IdString name = stringf("%s$%s:%d$%d", type, RTLIL::encode_filename(*that->location.begin.filename), that->location.begin.line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); set_src_attr(cell, that); @@ -56,7 +56,7 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, IdString type, int result_width 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()); + that->input_error("Attribute `%s' with non-constant value!\n", attr.first); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -77,7 +77,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s return; } - IdString name = stringf("$extend$%s:%d$%d", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); + IdString name = stringf("$extend$%s:%d$%d", RTLIL::encode_filename(*that->location.begin.filename), that->location.begin.line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, ID($pos)); set_src_attr(cell, that); @@ -85,10 +85,10 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s set_src_attr(wire, that); wire->is_signed = that->is_signed; - if (that != NULL) + if (that != nullptr) 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()); + that->input_error("Attribute `%s' with non-constant value!\n", attr.first); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -104,7 +104,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s // helper function for creating RTLIL code for binary operations static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { - IdString name = stringf("%s$%s:%d$%d", type.c_str(), RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); + IdString name = stringf("%s$%s:%d$%d", type, RTLIL::encode_filename(*that->location.begin.filename), that->location.begin.line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); set_src_attr(cell, that); @@ -114,7 +114,7 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width 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()); + that->input_error("Attribute `%s' with non-constant value!\n", attr.first); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -138,7 +138,7 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const log_assert(cond.size() == 1); std::stringstream sstr; - sstr << "$ternary$" << RTLIL::encode_filename(that->filename) << ":" << that->location.first_line << "$" << (autoidx++); + sstr << "$ternary$" << RTLIL::encode_filename(*that->location.begin.filename) << ":" << that->location.begin.line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($mux)); set_src_attr(cell, that); @@ -149,7 +149,7 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const 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()); + that->input_error("Attribute `%s' with non-constant value!\n", attr.first); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -195,22 +195,22 @@ struct AST_INTERNAL::LookaheadRewriter 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) + auto wire = std::make_unique(node->location, 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->str = stringf("$lookahead%s$%d", node->str, autoidx++); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(node->location, 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); + lookaheadids[node->str] = make_pair(node->id2ast, wire.get()); wire->genRTLIL(); + current_ast_mod->children.push_back(std::move(wire)); } } - for (auto child : node->children) - collect_lookaheadids(child); + for (auto& child : node->children) + collect_lookaheadids(child.get()); } bool has_lookaheadids(AstNode *node) @@ -218,8 +218,8 @@ struct AST_INTERNAL::LookaheadRewriter if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) != 0) return true; - for (auto child : node->children) - if (has_lookaheadids(child)) + for (auto& child : node->children) + if (has_lookaheadids(child.get())) return true; return false; @@ -230,8 +230,8 @@ struct AST_INTERNAL::LookaheadRewriter if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) == 0) return true; - for (auto child : node->children) - if (has_nonlookaheadids(child)) + for (auto& child : node->children) + if (has_nonlookaheadids(child.get())) return true; return false; @@ -241,16 +241,16 @@ struct AST_INTERNAL::LookaheadRewriter { if (node->type == AST_ASSIGN_LE) { - if (has_lookaheadids(node->children[0])) + if (has_lookaheadids(node->children[0].get())) { - if (has_nonlookaheadids(node->children[0])) + if (has_nonlookaheadids(node->children[0].get())) log_error("incompatible mix of lookahead and non-lookahead IDs in LHS expression.\n"); - rewrite_lookaheadids(node->children[0], true); + rewrite_lookaheadids(node->children[0].get(), true); node->type = AST_ASSIGN_EQ; } - rewrite_lookaheadids(node->children[1], lhs); + rewrite_lookaheadids(node->children[1].get(), lhs); return; } @@ -261,21 +261,22 @@ struct AST_INTERNAL::LookaheadRewriter lhs = false; } - for (auto child : node->children) - rewrite_lookaheadids(child, lhs); + for (auto& child : node->children) + rewrite_lookaheadids(child.get(), lhs); } LookaheadRewriter(AstNode *top) { - // top->dumpAst(NULL, "REWRITE-BEFORE> "); - // top->dumpVlog(NULL, "REWRITE-BEFORE> "); + // top->dumpAst(nullptr, "REWRITE-BEFORE> "); + // top->dumpVlog(nullptr, "REWRITE-BEFORE> "); AstNode *block = nullptr; + auto loc = top->location; - for (auto c : top->children) + for (auto& c : top->children) if (c->type == AST_BLOCK) { log_assert(block == nullptr); - block = c; + block = c.get(); } log_assert(block != nullptr); @@ -284,25 +285,25 @@ struct AST_INTERNAL::LookaheadRewriter for (auto it : lookaheadids) { - AstNode *ref_orig = new AstNode(AST_IDENTIFIER); + auto ref_orig = std::make_unique(loc, 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); + auto ref_temp = std::make_unique(loc, 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); + auto init_assign = std::make_unique(loc, AST_ASSIGN_EQ, ref_temp->clone(), ref_orig->clone()); + auto final_assign = std::make_unique(loc, AST_ASSIGN_LE, std::move(ref_orig), std::move(ref_temp)); - block->children.insert(block->children.begin(), init_assign); - block->children.push_back(final_assign); + block->children.insert(block->children.begin(), std::move(init_assign)); + block->children.push_back(std::move(final_assign)); } - // top->dumpAst(NULL, "REWRITE-AFTER> "); - // top->dumpVlog(NULL, "REWRITE-AFTER> "); + // top->dumpAst(nullptr, "REWRITE-AFTER> "); + // top->dumpVlog(nullptr, "REWRITE-AFTER> "); } }; @@ -310,7 +311,7 @@ struct AST_INTERNAL::LookaheadRewriter struct AST_INTERNAL::ProcessGenerator { // input and output structures - AstNode *always; + std::unique_ptr always; RTLIL::SigSpec initSyncSignals; RTLIL::Process *proc; RTLIL::SigSpec outputSignals; @@ -341,30 +342,30 @@ struct AST_INTERNAL::ProcessGenerator // 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) + ProcessGenerator(std::unique_ptr a, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(std::move(a)), initSyncSignals(initSyncSignalsArg), last_effect_priority(0) { // rewrite lookahead references - LookaheadRewriter la_rewriter(always); + LookaheadRewriter la_rewriter(always.get()); // 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); + proc = current_module->addProcess(stringf("$proc$%s:%d$%d", RTLIL::encode_filename(*always->location.begin.filename), always->location.begin.line, autoidx++)); + set_src_attr(proc, always.get()); 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()); + always->input_error("Attribute `%s' with non-constant value!\n", attr.first); 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); + collect_lvalues(subst_lvalue_from, always.get(), 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) + 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)) { @@ -381,14 +382,14 @@ struct AST_INTERNAL::ProcessGenerator 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("Note: Assuming pure combinatorial block at %s in\n", always->loc_string()); 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) + 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)) @@ -420,9 +421,9 @@ struct AST_INTERNAL::ProcessGenerator } // process the AST - for (auto child : always->children) + for (auto& child : always->children) if (child->type == AST_BLOCK) - processAst(child); + processAst(child.get()); for (auto sync: proc->syncs) processMemWrites(sync); @@ -472,7 +473,7 @@ struct AST_INTERNAL::ProcessGenerator for (int i = 0; i < GetSize(chunks); i++) { RTLIL::SigChunk &chunk = chunks[i]; - if (chunk.wire == NULL) + if (chunk.wire == nullptr) continue; std::string wire_name; @@ -484,7 +485,7 @@ struct AST_INTERNAL::ProcessGenerator } while (current_module->wires_.count(wire_name) > 0); RTLIL::Wire *wire = current_module->addWire(wire_name, chunk.width); - set_src_attr(wire, always); + set_src_attr(wire, always.get()); chunk.wire = wire; chunk.offset = 0; @@ -499,10 +500,10 @@ struct AST_INTERNAL::ProcessGenerator switch (ast->type) { case AST_CASE: - for (auto child : ast->children) + 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); + collect_lvalues(reg, child.get(), type_eq, type_le, false); } break; @@ -511,19 +512,19 @@ struct AST_INTERNAL::ProcessGenerator case AST_CONDZ: case AST_ALWAYS: case AST_INITIAL: - for (auto child : ast->children) + for (auto& child : ast->children) if (child->type == AST_BLOCK) - collect_lvalues(reg, child, type_eq, type_le, false); + collect_lvalues(reg, child.get(), type_eq, type_le, false); break; case AST_BLOCK: - for (auto child : ast->children) { + 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); + collect_lvalues(reg, child.get(), type_eq, type_le, false); } break; @@ -583,8 +584,8 @@ struct AST_INTERNAL::ProcessGenerator switch (ast->type) { case AST_BLOCK: - for (auto child : ast->children) - processAst(child); + for (auto& child : ast->children) + processAst(child.get()); break; case AST_ASSIGN_EQ: @@ -629,7 +630,7 @@ struct AST_INTERNAL::ProcessGenerator 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()); + ast->input_error("Attribute `%s' with non-constant value!\n", attr.first); sw->attributes[attr.first] = attr.second->asAttrConst(); } @@ -641,9 +642,9 @@ struct AST_INTERNAL::ProcessGenerator 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) + RTLIL::CaseRule *default_case = nullptr; + RTLIL::CaseRule *last_generated_case = nullptr; + for (auto& child : ast->children) { if (child == ast->children[0]) continue; @@ -657,14 +658,14 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::CaseRule *backup_case = current_case; current_case = new RTLIL::CaseRule; - set_src_attr(current_case, child); + set_src_attr(current_case, child.get()); last_generated_case = current_case; addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); - for (auto node : child->children) { + for (auto& node : child->children) { if (node->type == AST_DEFAULT) default_case = current_case; else if (node->type == AST_BLOCK) - processAst(node); + processAst(node.get()); else current_case->compare.push_back(node->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap())); } @@ -678,7 +679,7 @@ struct AST_INTERNAL::ProcessGenerator subst_rvalue_map.restore(); } - if (last_generated_case != NULL && ast->get_bool_attribute(ID::full_case) && default_case == NULL) { + if (last_generated_case != nullptr && ast->get_bool_attribute(ID::full_case) && default_case == nullptr) { #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 @@ -690,7 +691,7 @@ struct AST_INTERNAL::ProcessGenerator sw->cases.push_back(default_case); #endif } else { - if (default_case == NULL) { + if (default_case == nullptr) { default_case = new RTLIL::CaseRule; addChunkActions(default_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); } @@ -723,7 +724,7 @@ struct AST_INTERNAL::ProcessGenerator 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++); + sstr << ast->str << "$" << ast->location.begin.filename << ":" << ast->location.begin.line << "$" << (autoidx++); Wire *en = current_module->addWire(sstr.str() + "_EN", 1); set_src_attr(en, ast); @@ -731,16 +732,17 @@ struct AST_INTERNAL::ProcessGenerator current_case->actions.push_back(SigSig(en, true)); RTLIL::SigSpec triggers; - RTLIL::Const polarity; + RTLIL::Const::Builder polarity_builder; for (auto sync : proc->syncs) { if (sync->type == RTLIL::STp) { triggers.append(sync->signal); - polarity.bits().push_back(RTLIL::S1); + polarity_builder.push_back(RTLIL::S1); } else if (sync->type == RTLIL::STn) { triggers.append(sync->signal); - polarity.bits().push_back(RTLIL::S0); + polarity_builder.push_back(RTLIL::S0); } } + RTLIL::Const polarity = polarity_builder.build(); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print)); set_src_attr(cell, ast); @@ -760,14 +762,14 @@ struct AST_INTERNAL::ProcessGenerator default_base = 16; std::vector args; - for (auto node : ast->children) { + 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; + arg.filename = *node->location.begin.filename; + arg.first_line = node->location.begin.line; if (node->type == AST_CONSTANT && node->is_string) { arg.type = VerilogFmtArg::STRING; arg.str = node->bitsAsConst().decode_string(); @@ -793,7 +795,7 @@ struct AST_INTERNAL::ProcessGenerator 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()); + log_file_error(*ast->location.begin.filename, ast->location.begin.line, "Found unsupported invocation of system task `%s'!\n", ast->str); } break; @@ -813,7 +815,7 @@ struct AST_INTERNAL::ProcessGenerator 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++); + cellname = stringf("$%s$%s:%d$%d", flavor, RTLIL::encode_filename(*ast->location.begin.filename), ast->location.begin.line, autoidx++); else cellname = ast->str; check_unique_id(current_module, cellname, ast, "procedural assertion"); @@ -828,22 +830,23 @@ struct AST_INTERNAL::ProcessGenerator current_case->actions.push_back(SigSig(en, true)); RTLIL::SigSpec triggers; - RTLIL::Const polarity; + RTLIL::Const::Builder polarity_builder; for (auto sync : proc->syncs) { if (sync->type == RTLIL::STp) { triggers.append(sync->signal); - polarity.bits().push_back(RTLIL::S1); + polarity_builder.push_back(RTLIL::S1); } else if (sync->type == RTLIL::STn) { triggers.append(sync->signal); - polarity.bits().push_back(RTLIL::S0); + polarity_builder.push_back(RTLIL::S0); } } + RTLIL::Const polarity = polarity_builder.build(); 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()); + log_file_error(*ast->location.begin.filename, ast->location.begin.line, "Attribute `%s' with non-constant value!\n", attr.first); cell->attributes[attr.first] = attr.second->asAttrConst(); } cell->setParam(ID::FLAVOR, flavor); @@ -866,8 +869,8 @@ struct AST_INTERNAL::ProcessGenerator break; default: - // ast->dumpAst(NULL, "ast> "); - // current_ast_mod->dumpAst(NULL, "mod> "); + // ast->dumpAst(nullptr, "ast> "); + // current_ast_mod->dumpAst(nullptr, "mod> "); log_abort(); } } @@ -876,14 +879,14 @@ struct AST_INTERNAL::ProcessGenerator { // Maps per-memid AST_MEMWR IDs to indices in the mem_write_actions array. dict, int> port_map; - for (auto child : always->children) + 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); + set_src_attr(&action, child.get()); 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()); @@ -892,7 +895,7 @@ struct AST_INTERNAL::ProcessGenerator 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]; + priority_mask.set(new_bit, orig_priority_mask[i]); } action.priority_mask = priority_mask; sync->mem_write_actions.push_back(action); @@ -971,11 +974,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun bool sub_sign_hint = true; int sub_width_hint = -1; int this_width = 0; - AstNode *range = NULL; - AstNode *id_ast = NULL; + AstNode *range = nullptr; + AstNode *id_ast = nullptr; bool local_found_real = false; - if (found_real == NULL) + if (found_real == nullptr) found_real = &local_found_real; switch (type) @@ -1006,7 +1009,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun } } if (!id_ast) - input_error("Failed to resolve identifier %s for width detection!\n", str.c_str()); + input_error("Failed to resolve identifier %s for width detection!\n", 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; @@ -1016,53 +1019,50 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun 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()); + input_error("Failed to detect width for parameter %s!\n", str); } if (children.size() != 0) - range = children[0]; + range = children[0].get(); } 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> "); + // current_ast_mod->dumpAst(nullptr, "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()); + // id_ast->dumpAst(nullptr, "decl> "); + // dumpAst(nullptr, "ref> "); + input_error("Failed to detect width of signal access `%s'!\n", str); } } else { this_width = id_ast->range_left - id_ast->range_right + 1; if (children.size() != 0) - range = children[0]; + range = children[0].get(); } } 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()); + input_error("Failed to detect width of memory access `%s'!\n", str); this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1; if (children.size() > 1) - range = children[1]; + range = children[1].get(); } 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); + auto 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()); + input_error("Failed to detect width for identifier %s!\n", 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(); + auto left_at_zero_ast = children[0]->children[0]->clone_at_zero(); + auto 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()); + input_error("Unsupported expression on dynamic range select on signal `%s'!\n", 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; @@ -1106,7 +1106,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun break; case AST_CONCAT: - for (auto child : children) { + for (auto& child : children) { sub_width_hint = 0; sub_sign_hint = true; child->detectSignWidthWorker(sub_width_hint, sub_sign_hint); @@ -1135,7 +1135,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_BIT_OR: case AST_BIT_XOR: case AST_BIT_XNOR: - for (auto child : children) + for (auto& child : children) child->detectSignWidthWorker(width_hint, sign_hint, found_real); break; @@ -1175,7 +1175,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_MUL: case AST_DIV: case AST_MOD: - for (auto child : children) + for (auto& child : children) child->detectSignWidthWorker(width_hint, sign_hint, found_real); break; @@ -1195,7 +1195,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun 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()); + input_error("Failed to detect width of memory access `%s'!\n", str); this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1; width_hint = max(width_hint, this_width); break; @@ -1216,12 +1216,13 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun width_hint = max(width_hint, sub_width_hint); sign_hint &= sub_sign_hint; }; - visit_case_expr(children[0]); + visit_case_expr(children[0].get()); for (size_t i = 1; i < children.size(); i++) { - AstNode *child = children[i]; - for (AstNode *v : child->children) + AstNode *child = children[i].get(); + for (auto& v : child->children) { if (v->type != AST_DEFAULT && v->type != AST_BLOCK) - visit_case_expr(v); + visit_case_expr(v.get()); + } } break; } @@ -1240,7 +1241,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun 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()); + RTLIL::unescape_id(str)); width_hint = max(width_hint, int(children[0]->asInt(true))); } break; @@ -1267,11 +1268,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun // 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()); + input_error("Function call to %s resolved to something that isn't a function!\n", RTLIL::unescape_id(str)); const AstNode *wire = nullptr; - for (const AstNode *child : func->children) + for (const auto& child : func->children) if (child->str == func->str) { - wire = child; + wire = child.get(); break; } log_assert(wire && wire->type == AST_WIRE); @@ -1280,20 +1281,18 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (!wire->children.empty()) { log_assert(wire->children.size() == 1); - const AstNode *range = wire->children.at(0); + const AstNode *range = wire->children.at(0).get(); log_assert(range->type == AST_RANGE && range->children.size() == 2); - AstNode *left = range->children.at(0)->clone(); - AstNode *right = range->children.at(1)->clone(); + auto left = range->children.at(0)->clone(); + auto 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()); + RTLIL::unescape_id(str)); result_width = abs(int(left->asInt(true) - right->asInt(true))); - delete left; - delete right; } width_hint = max(width_hint, result_width); break; @@ -1305,7 +1304,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun 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()); + input_error("Don't know how to detect sign and width for %s node!\n", type2str(type)); + } if (*found_real) @@ -1342,8 +1342,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // be instantiated for this type of AST node. IdString type_name; - current_filename = filename; - switch (type) { // simply ignore this nodes. @@ -1361,6 +1359,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_GENIF: case AST_GENCASE: case AST_PACKAGE: + case AST_IMPORT: case AST_ENUM: case AST_MODPORT: case AST_MODPORTMEMBER: @@ -1409,7 +1408,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (flag_pwires) { if (GetSize(children) < 1 || children[0]->type != AST_CONSTANT) - input_error("Parameter `%s' with non-constant value!\n", str.c_str()); + input_error("Parameter `%s' with non-constant value!\n", str); RTLIL::Const val = children[0]->bitsAsConst(); RTLIL::IdString id = str; @@ -1423,7 +1422,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); + input_error("Attribute `%s' with non-constant value!\n", attr.first); wire->attributes[attr.first] = attr.second->asAttrConst(); } } @@ -1432,10 +1431,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // 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()); + input_error("Signal `%s' with non-constant width!\n", 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); + input_error("Signal `%s' with invalid width range %d!\n", str, range_left - range_right + 1); RTLIL::IdString id = str; check_unique_id(current_module, id, this, "signal"); @@ -1446,11 +1445,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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()); + input_error("Attribute `%s' with non-constant value!\n", attr.first); wire->attributes[attr.first] = attr.second->asAttrConst(); } @@ -1466,7 +1466,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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()); + input_error("Memory `%s' with non-constant width or size!\n", str); RTLIL::Memory *memory = new RTLIL::Memory; set_src_attr(memory, this); @@ -1484,7 +1484,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); + input_error("Attribute `%s' with non-constant value!\n", attr.first); memory->attributes[attr.first] = attr.second->asAttrConst(); } } @@ -1507,7 +1507,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } 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)); + log_file_warning(*location.begin.filename, location.begin.line, "converting real value %e to binary %s.\n", realvalue, log_signal(sig)); return sig; } @@ -1516,11 +1516,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // shifter cell is created and the output signal of this cell is returned case AST_IDENTIFIER: { - RTLIL::Wire *wire = NULL; + RTLIL::Wire *wire = nullptr; RTLIL::SigChunk chunk; bool is_interface = false; - AST::AstNode *member_node = NULL; + AST::AstNode *member_node = nullptr; int add_undef_bits_msb = 0; int add_undef_bits_lsb = 0; @@ -1539,13 +1539,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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()); + log_file_warning(*location.begin.filename, location.begin.line, "Identifier `%s' is implicitly declared.\n", str); else - input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); + input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str); } 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()); + input_error("Parameter %s does not evaluate to constant value!\n", str); chunk = RTLIL::Const(id2ast->children[0]->bits); goto use_const_chunk; } @@ -1560,17 +1560,17 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) is_interface = true; } else { - input_error("Identifier `%s' doesn't map to any signal!\n", str.c_str()); + input_error("Identifier `%s' doesn't map to any signal!\n", str); } if (id2ast->type == AST_MEMORY) - input_error("Identifier `%s' does map to an unexpanded memory!\n", str.c_str()); + input_error("Identifier `%s' does map to an unexpanded memory!\n", 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()); + IdString dummy_wire_name = stringf("$dummywireforinterface%s", str); RTLIL::Wire *dummy_wire = current_module->wire(dummy_wire_name); if (!dummy_wire) { dummy_wire = current_module->addWire(dummy_wire_name); @@ -1607,14 +1607,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } 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(); + auto left_at_zero_ast = children[0]->children[0]->clone_at_zero(); + auto 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()); + input_error("Unsupported expression on dynamic range select on signal `%s'!\n", 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 ? + auto fake_ast = std::make_unique(children[0]->location, 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) @@ -1635,10 +1635,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } 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; + RTLIL::SigSpec sig = binop2rtlil(fake_ast.get(), ID($shiftx), width, fake_ast->children[0]->genRTLIL(), shift_val); return sig; } else { chunk.width = children[0]->range_left - children[0]->range_right + 1; @@ -1647,10 +1644,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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", + log_file_warning(*location.begin.filename, location.begin.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", + log_file_warning(*location.begin.filename, location.begin.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 { @@ -1664,10 +1661,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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", + log_file_warning(*location.begin.filename, location.begin.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", + log_file_warning(*location.begin.filename, location.begin.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); } } @@ -1941,7 +1938,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_MEMRD: { std::stringstream sstr; - sstr << "$memrd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$memrd$" << str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($memrd)); set_src_attr(cell, this); @@ -1979,7 +1976,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_MEMINIT: { std::stringstream sstr; - sstr << "$meminit$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$meminit$" << str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++); SigSpec en_sig = children[2]->genRTLIL(); @@ -2024,7 +2021,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) IdString cellname; if (str.empty()) - cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); + cellname = stringf("$%s$%s:%d$%d", flavor, RTLIL::encode_filename(*location.begin.filename), location.begin.line, autoidx++); else cellname = str; check_unique_id(current_module, cellname, this, "procedural assertion"); @@ -2037,7 +2034,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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()); + input_error("Attribute `%s' with non-constant value!\n", attr.first); cell->attributes[attr.first] = attr.second->asAttrConst(); } cell->setParam(ID(FLAVOR), flavor); @@ -2067,7 +2064,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) new_left.append(left[i]); new_right.append(right[i]); } - log_file_warning(filename, location.first_line, "Ignoring assignment to constant bits:\n" + log_file_warning(*location.begin.filename, location.begin.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)); @@ -2091,7 +2088,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) cell->set_bool_attribute(ID::module_not_derived); for (auto it = children.begin(); it != children.end(); it++) { - AstNode *child = *it; + auto* child = it->get(); if (child->type == AST_CELLTYPE) { cell->type = child->str; if (flag_icells && cell->type.begins_with("\\$")) @@ -2100,9 +2097,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } if (child->type == AST_PARASET) { IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str; - const AstNode *value = child->children[0]; + const auto* value = child->children[0].get(); if (value->type == AST_REALVALUE) - log_file_warning(filename, location.first_line, "Replacing floating point parameter %s.%s = %f with string.\n", + log_file_warning(*location.begin.filename, location.begin.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", @@ -2113,7 +2110,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (child->type == AST_ARGUMENT) { RTLIL::SigSpec sig; if (child->children.size() > 0) { - AstNode *arg = child->children[0]; + auto* arg = child->children[0].get(); int local_width_hint = -1; bool local_sign_hint = false; // don't inadvertently attempt to detect the width of interfaces @@ -2153,7 +2150,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - input_error("Attribute `%s' with non-constant value.\n", attr.first.c_str()); + input_error("Attribute `%s' with non-constant value.\n", attr.first); cell->attributes[attr.first] = attr.second->asAttrConst(); } if (cell->type == ID($specify2)) { @@ -2185,33 +2182,30 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // use ProcessGenerator for always blocks case AST_ALWAYS: { - AstNode *always = this->clone(); - ProcessGenerator generator(always); + ProcessGenerator generator(this->clone()); ignoreThisSignalsInInitial.append(generator.outputSignals); - delete always; } break; case AST_INITIAL: { - AstNode *always = this->clone(); - ProcessGenerator generator(always, ignoreThisSignalsInInitial); - delete always; + auto always = this->clone(); + ProcessGenerator generator(this->clone(), ignoreThisSignalsInInitial); } 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()); + log_file_info(*location.begin.filename, location.begin.line, "%s.\n", children[0]->str); else - log_file_info(filename, location.first_line, "\n"); + log_file_info(*location.begin.filename, location.begin.line, "\n"); } else if (str == "$warning") { if (sz > 0) - log_file_warning(filename, location.first_line, "%s.\n", children[0]->str.c_str()); + log_file_warning(*location.begin.filename, location.begin.line, "%s.\n", children[0]->str); else - log_file_warning(filename, location.first_line, "\n"); + log_file_warning(*location.begin.filename, location.begin.line, "\n"); } else if (str == "$error") { if (sz > 0) - input_error("%s.\n", children[0]->str.c_str()); + input_error("%s.\n", children[0]->str); else input_error("\n"); } else if (str == "$fatal") { @@ -2220,11 +2214,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // 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()); + input_error("FATAL: %s.\n", children[0]->str); else input_error("FATAL.\n"); } else { - input_error("Unknown elaboration system task '%s'.\n", str.c_str()); + input_error("Unknown elaboration system task '%s'.\n", str); } } break; @@ -2243,17 +2237,17 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_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)); + RTLIL::unescape_id(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()); + RTLIL::unescape_id(str)); width = children[0]->asInt(true); } if (width <= 0) - input_error("Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str()); + input_error("Failed to detect width of %s!\n", RTLIL::unescape_id(str)); Cell *cell = current_module->addCell(myid, str.substr(1)); set_src_attr(cell, this); @@ -2280,7 +2274,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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()); + input_error("Don't know how to generate RTLIL code for %s node!\n", type2str(type)); } return RTLIL::SigSpec(); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 2fa33d508..83174e963 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -30,30 +30,20 @@ #include "libs/sha1/sha1.h" #include "frontends/verilog/verilog_frontend.h" #include "ast.h" +#include "kernel/io.h" #include #include #include #include -// For std::gcd in C++17 -// #include +#include +#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) { @@ -87,7 +77,7 @@ void AstNode::fixup_hierarchy_flags(bool force_descend) case AST_PARASET: case AST_PREFIX: in_param = true; - for (auto child : children) + for (auto& child : children) child->set_in_param_flag(true, force_descend); break; @@ -95,7 +85,7 @@ void AstNode::fixup_hierarchy_flags(bool force_descend) case AST_WIRE: case AST_GENIF: case AST_GENCASE: - for (auto child : children) + 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); @@ -103,19 +93,21 @@ void AstNode::fixup_hierarchy_flags(bool force_descend) case AST_GENFOR: case AST_FOR: - for (auto child : children) + for (auto& child : children) { + log_assert((bool)child); 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) + for (auto& child : children) child->set_in_param_flag(in_param, force_descend); } - for (auto attr : attributes) + for (auto& attr : attributes) attr.second->set_in_param_flag(true, force_descend); in_lvalue = in_lvalue_from_above; @@ -131,14 +123,14 @@ void AstNode::fixup_hierarchy_flags(bool force_descend) break; default: - for (auto child : children) + for (auto& child : children) child->set_in_lvalue_flag(in_lvalue, force_descend); } if (force_descend) { - for (auto child : children) + for (auto& child : children) child->fixup_hierarchy_flags(true); - for (auto attr : attributes) + for (auto& attr : attributes) attr.second->fixup_hierarchy_flags(true); } } @@ -148,12 +140,12 @@ void AstNode::fixup_hierarchy_flags(bool force_descend) 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]; + AstNode *node_arg = children[index].get(); while (node_arg->simplify(true, stage, -1, false)) { } VerilogFmtArg arg = {}; - arg.filename = filename; - arg.first_line = location.first_line; + arg.filename = *location.begin.filename; + arg.first_line = location.begin.line; if (node_arg->type == AST_CONSTANT && node_arg->is_string) { arg.type = VerilogFmtArg::STRING; arg.str = node_arg->bitsAsConst().decode_string(); @@ -170,10 +162,10 @@ Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_ 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); + log_file_info(*location.begin.filename, location.begin.line, "Skipping system task `%s' with non-constant argument at position %zu.\n", 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); + log_file_error(*location.begin.filename, location.begin.line, "Failed to evaluate system task `%s' with non-constant argument at position %zu.\n", str, index + 1); } args.push_back(arg); } @@ -189,17 +181,17 @@ void AstNode::annotateTypedEnums(AstNode *template_node) 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("enum_type=%s (count=%lu)\n", enum_type, current_scope.count(enum_type)); // log("current scope:\n"); // for (auto &it : current_scope) - // log(" %s\n", it.first.c_str()); + // log(" %s\n", it.first); 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]; + AstNode *enum_item0 = enum_node->children[0].get(); log_assert(enum_item0->type == AST_ENUM_ITEM); int width; if (!enum_item0->range_valid) @@ -210,7 +202,7 @@ void AstNode::annotateTypedEnums(AstNode *template_node) 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){ + for (auto& enum_item : enum_node->children){ log_assert(enum_item->type == AST_ENUM_ITEM); //get is_signed bool is_signed; @@ -237,20 +229,20 @@ void AstNode::annotateTypedEnums(AstNode *template_node) RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed); enum_item_str.append(val.as_string()); //set attribute for available val to enum item name mappings - set_attribute(enum_item_str.c_str(), mkconst_str(enum_item->str)); + set_attribute(enum_item_str.c_str(), mkconst_str(location, enum_item->str)); } } } -static AstNode *make_range(int left, int right, bool is_signed = false) +static std::unique_ptr make_range(AstSrcLocType loc, 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); + auto range = std::make_unique(loc, 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->children.push_back(AstNode::mkconst_int(loc, left, true)); + range->children.push_back(AstNode::mkconst_int(loc, right, true)); range->is_signed = is_signed; return range; } @@ -259,7 +251,7 @@ 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()); + node->input_error("Non-constant range in declaration of %s\n", node->str); } // note: range swapping has already been checked for return rnode->range_left - rnode->range_right + 1; @@ -274,7 +266,7 @@ static int add_dimension(AstNode *node, AstNode *rnode) [[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()); + node->input_error("Unpacked array in packed struct/union member %s\n", node->str); } static int size_packed_struct(AstNode *snode, int base_offset) @@ -287,7 +279,7 @@ static int size_packed_struct(AstNode *snode, int base_offset) int packed_width = -1; // examine members from last to first for (auto it = snode->children.rbegin(); it != snode->children.rend(); ++it) { - auto node = *it; + auto node = it->get(); int width; if (node->type == AST_STRUCT || node->type == AST_UNION) { // embedded struct or union @@ -297,14 +289,14 @@ static int size_packed_struct(AstNode *snode, int base_offset) 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]); + width = range_width(node, node->children[0].get()); 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]; + auto rnode = node->children[1].get(); 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 }); @@ -312,7 +304,7 @@ static int size_packed_struct(AstNode *snode, int base_offset) } else { width *= add_dimension(node, rnode); } - add_dimension(node, node->children[0]); + add_dimension(node, node->children[0].get()); } else { // The Yosys extension for unpacked arrays in packed structs / unions @@ -321,11 +313,9 @@ static int size_packed_struct(AstNode *snode, int base_offset) } } else { // Vector - add_dimension(node, node->children[0]); + add_dimension(node, node->children[0].get()); } // 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) { @@ -336,12 +326,10 @@ static int size_packed_struct(AstNode *snode, int base_offset) struct_array_packing_error(node); } width = 1; - for (auto rnode : node->children[0]->children) { - width *= add_dimension(node, rnode); + for (auto& rnode : node->children[0]->children) { + width *= add_dimension(node, rnode.get()); } // range nodes are now redundant - for (AstNode *child : node->children) - delete child; node->children.clear(); } else if (node->range_left < 0) { @@ -371,7 +359,7 @@ static int size_packed_struct(AstNode *snode, int base_offset) } 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); + node->input_error("member %s of a packed union has %d bits, expecting %d\n", node->str, width, packed_width); } } else { @@ -389,85 +377,88 @@ static int size_packed_struct(AstNode *snode, int base_offset) return width; } -static AstNode *node_int(int ival) +static std::unique_ptr node_int(AstSrcLocType loc, int ival) { - return AstNode::mkconst_int(ival, true); + return AstNode::mkconst_int(loc, ival, true); } -static AstNode *multiply_by_const(AstNode *expr_node, int stride) +static std::unique_ptr multiply_by_const(std::unique_ptr expr_node, int stride) { - return new AstNode(AST_MUL, expr_node, node_int(stride)); + auto loc = expr_node->location; + return std::make_unique(loc, AST_MUL, std::move(expr_node), node_int(loc, stride)); } -static AstNode *normalize_index(AstNode *expr, AstNode *decl_node, int dimension) +static std::unique_ptr normalize_index(AstNode *expr, AstNode *decl_node, int dimension) { - expr = expr->clone(); + auto new_expr = expr->clone(); + auto loc = expr->location; int offset = decl_node->dimensions[dimension].range_right; if (offset) { - expr = new AstNode(AST_SUB, expr, node_int(offset)); + new_expr = std::make_unique(loc, AST_SUB, std::move(new_expr), node_int(loc, 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); + new_expr = std::make_unique(loc, AST_SUB, node_int(loc, left), std::move(new_expr)); } - return expr; + return new_expr; } -static AstNode *index_offset(AstNode *offset, AstNode *rnode, AstNode *decl_node, int dimension, int &stride) +static std::unique_ptr index_offset(std::unique_ptr 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; + auto right = normalize_index(rnode->children.back().get(), decl_node, dimension); + auto add_offset = stride > 1 ? multiply_by_const(std::move(right), stride) : std::move(right); + return offset ? std::make_unique(rnode->location, AST_ADD, std::move(offset), std::move(add_offset)) : std::move(add_offset); } -static AstNode *index_msb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *decl_node, int dimension, int stride) +static std::unique_ptr index_msb_offset(std::unique_ptr lsb_offset, AstNode *rnode, AstNode *decl_node, int dimension, int stride) { log_assert(rnode->children.size() <= 2); + auto loc = rnode->location; // Offset to add to LSB - AstNode *add_offset; + std::unique_ptr add_offset; if (rnode->children.size() == 1) { // Index, e.g. s.a[i] - add_offset = node_int(stride - 1); + add_offset = node_int(rnode->location, 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); + auto left = normalize_index(rnode->children[0].get(), decl_node, dimension); + auto right = normalize_index(rnode->children[1].get(), decl_node, dimension); + add_offset = std::make_unique(loc, AST_SUB, std::move(left), std::move(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)); + auto slice_width = std::make_unique(loc, AST_ADD, std::move(add_offset), node_int(loc, 1)); + add_offset = std::make_unique(loc, AST_SUB, multiply_by_const(std::move(slice_width), stride), node_int(loc, 1)); } } - return new AstNode(AST_ADD, lsb_offset, add_offset); + return std::make_unique(loc, AST_ADD, std::move(lsb_offset), std::move(add_offset)); } -AstNode *AstNode::make_index_range(AstNode *decl_node, bool unpacked_range) +std::unique_ptr 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); + return make_range(decl_node->location, decl_node->range_left - decl_node->range_right, 0); } log_assert(children.size() == 1); // Range operations - AstNode *rnode = children[0]; - AstNode *offset = NULL; + AstNode *rnode = children[0].get(); + std::unique_ptr offset = nullptr; int dim = unpacked_range ? 0 : decl_node->unpacked_dimensions; int max_dim = unpacked_range ? decl_node->unpacked_dimensions : GetSize(decl_node->dimensions); @@ -478,31 +469,31 @@ AstNode *AstNode::make_index_range(AstNode *decl_node, bool unpacked_range) // Calculate LSB offset for the final index / slice if (rnode->type == AST_RANGE) { - offset = index_offset(offset, rnode, decl_node, dim, stride); + offset = index_offset(std::move(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); + rnode = mrnode->children[dim].get(); + offset = index_offset(std::move(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()); + input_error("Unsupported range operation for %s\n", str); } - AstNode *index_range = new AstNode(AST_RANGE); + std::unique_ptr index_range = std::make_unique(rnode->location, 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); + std::unique_ptrmsb_offset = index_msb_offset(offset->clone(), rnode, decl_node, dim, stride); + index_range->children.push_back(std::move(msb_offset)); } - index_range->children.push_back(offset); + index_range->children.push_back(std::move(offset)); return index_range; } @@ -510,8 +501,8 @@ AstNode *AstNode::make_index_range(AstNode *decl_node, bool unpacked_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)) + if (attributes.count(ID::wiretype) && (member_node = attributes.at(ID::wiretype).get()) && + (member_node->type == AST_STRUCT_ITEM || member_node->type == AST_STRUCT || member_node->type == AST_UNION)) { return member_node; } @@ -523,20 +514,21 @@ 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) { + for (auto &node : snode->children) { auto member_name = name + "." + node->str; - current_scope[member_name] = node; + current_scope[member_name] = node.get(); if (node->type != AST_STRUCT_ITEM) { // embedded struct or union - add_members_to_scope(node, name + "." + node->str); + add_members_to_scope(node.get(), name + "." + node->str); } } } -static AstNode *make_packed_struct(AstNode *template_node, std::string &name, decltype(AstNode::attributes) &attributes) +std::unique_ptr 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)); + auto loc = template_node->location; + auto wnode = std::make_unique(loc, AST_WIRE, make_range(loc, template_node->range_left, 0)); wnode->str = name; wnode->is_logic = true; wnode->range_valid = true; @@ -547,24 +539,25 @@ static AstNode *make_packed_struct(AstNode *template_node, std::string &name, de // 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; + current_scope[name] = wnode.get(); // 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) +static void prepend_ranges(std::unique_ptr &range, AstNode *range_add) { // Convert range to multirange. + auto loc = range->location; if (range->type == AST_RANGE) - range = new AstNode(AST_MULTIRANGE, range); + range = std::make_unique(loc, AST_MULTIRANGE, std::move(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) + for (auto& child : range_add->children) range->children.insert(range->children.begin() + i++, child->clone()); } } @@ -575,16 +568,16 @@ 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]; + const AstNode* lhs = node->children[0].get(); if (lhs->type == AST_IDENTIFIER && lhs->str == var->str) return false; } - for (const AstNode* child : node->children) { + for (auto& child : node->children) { // if this child shadows the given variable - if (child != var && child->str == var->str && child->type == AST_WIRE) + if (child.get() != 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)) + if (!node_contains_assignment_to(child.get(), var)) return false; } return true; @@ -621,13 +614,13 @@ const RTLIL::Module* AstNode::lookup_cell_module() auto reprocess_after = [this] (const std::string &modname) { if (!attributes.count(ID::reprocess_after)) - set_attribute(ID::reprocess_after, AstNode::mkconst_str(modname)); + set_attribute(ID::reprocess_after, AstNode::mkconst_str(location, modname)); }; const AstNode *celltype = nullptr; - for (const AstNode *child : children) + for (auto& child : children) if (child->type == AST_CELLTYPE) { - celltype = child; + celltype = child.get(); break; } log_assert(celltype != nullptr); @@ -644,7 +637,7 @@ const RTLIL::Module* AstNode::lookup_cell_module() // build a mapping from true param name to param value size_t para_counter = 0; dict cell_params_map; - for (AstNode *child : children) { + for (auto& child : children) { if (child->type != AST_PARASET) continue; @@ -652,7 +645,7 @@ const RTLIL::Module* AstNode::lookup_cell_module() 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]; + const AstNode *value = child->children[0].get(); if (value->type != AST_REALVALUE && value->type != AST_CONSTANT) return nullptr; // let genrtlil handle this error cell_params_map[paraname] = value->asParaConst(); @@ -684,29 +677,29 @@ 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)) + for (auto& child : node->children) + if (contains_unbased_unsized(child.get())) 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) +void add_wire_for_ref(Location loc, 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); + auto left = AstNode::mkconst_int(loc, ref->width - 1 + ref->start_offset, true); + auto right = AstNode::mkconst_int(loc, ref->start_offset, true); if (ref->upto) std::swap(left, right); - AstNode *range = new AstNode(AST_RANGE, left, right); + auto range = std::make_unique(loc, AST_RANGE, std::move(left), std::move(right)); - AstNode *wire = new AstNode(AST_WIRE, range); + auto wire = std::make_unique(loc, AST_WIRE, std::move(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; + current_scope[str] = wire.get(); + current_ast_mod->children.push_back(std::move(wire)); } enum class IdentUsage { @@ -733,10 +726,10 @@ static IdentUsage always_asgn_before_use(const AstNode *node, const std::string bool all_defined = true; bool any_used = false; bool has_default = false; - for (const AstNode *child : node->children) { + for (auto& 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); + IdentUsage nested = always_asgn_before_use(child.get(), target); if (nested != IdentUsage::Assigned && child->type == AST_COND) all_defined = false; if (nested == IdentUsage::SyncRequired) @@ -753,20 +746,20 @@ static IdentUsage always_asgn_before_use(const AstNode *node, const std::string // 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); + auto& 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); + for (auto& child : node->children) { + IdentUsage nested = always_asgn_before_use(child.get(), target); if (nested != IdentUsage::NotReferenced) return nested; } return IdentUsage::NotReferenced; } -AstNode *AstNode::clone_at_zero() +std::unique_ptr AstNode::clone_at_zero() { int width_hint; bool sign_hint; @@ -789,14 +782,13 @@ AstNode *AstNode::clone_at_zero() YS_FALLTHROUGH case AST_MEMRD: detectSignWidth(width_hint, sign_hint); - return mkconst_int(0, sign_hint, width_hint); + return mkconst_int(location, 0, sign_hint, width_hint); default: break; } - AstNode *that = new AstNode; - *that = *this; + auto that = clone(); for (auto &it : that->children) it = it->clone_at_zero(); for (auto &it : that->attributes) @@ -818,8 +810,8 @@ static bool try_determine_range_width(AstNode *range, int &result_width) return true; } - AstNode *left_at_zero_ast = range->children[0]->clone_at_zero(); - AstNode *right_at_zero_ast = range->children[1]->clone_at_zero(); + auto left_at_zero_ast = range->children[0]->clone_at_zero(); + auto 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)) {} @@ -831,8 +823,6 @@ static bool try_determine_range_width(AstNode *range, int &result_width) 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; } @@ -844,7 +834,7 @@ 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->set_attribute(auto_nosync_prefix + wire->str, AstNode::mkconst_int(block->location, 1, false)); } // block names can be prefixed with an explicit scope during elaboration @@ -885,21 +875,66 @@ static void check_auto_nosync(AstNode *node) // 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)); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(wire->location, 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); + for (auto& child : node->children) + check_auto_nosync(child.get()); } +class PackageImporter { + std::set import_items; + bool is_wildcard; + const AstNode* node; +public: + PackageImporter(const AstNode* n, const AstNode* child) : node(n) { + is_wildcard = child->children.empty(); + // For specific imports, collect the list of items to import + if (!is_wildcard) { + for (auto& item : child->children) { + import_items.insert(item->str); + } + } + } + + void import(std::map& scope, AstNode* to_import) const { + // Check if this is a specific import and if this item should be imported + if (!is_wildcard && import_items.count(to_import->str) == 0) + return; + + if (to_import->type == AST_PARAMETER || to_import->type == AST_LOCALPARAM || + to_import->type == AST_TYPEDEF || to_import->type == AST_FUNCTION || + to_import->type == AST_TASK || to_import->type == AST_ENUM) { + // For wildcard imports, check if item already exists (from specific import) + if (is_wildcard && scope.count(to_import->str) > 0) + return; + scope[to_import->str] = to_import; + } + if (to_import->type == AST_ENUM) { + for (auto& enode : to_import->children) { + log_assert(enode->type==AST_ENUM_ITEM); + // Check if this enum item should be imported + if (!is_wildcard && import_items.count(enode->str) == 0) + continue; + // For wildcard imports, check if item already exists (from specific import) + if (is_wildcard && scope.count(enode->str) > 0) + continue; + if (scope.count(enode->str) == 0) + scope[enode->str] = enode.get(); + else + node->input_error("enum item %s already exists in current scope\n", enode->str); + } + } + } +}; + // 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(). @@ -918,15 +953,15 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin static bool unevaluated_tern_branch = false; - AstNode *newNode = NULL; + std::unique_ptr newNode = nullptr; 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("AST simplify[%d] depth %d at %s:%d on %s %p:\n", stage, recursion_counter, location.begin.filename, location.begin.line, type2str(type), 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, "> "); + // dumpAst(nullptr, "> "); #endif if (stage == 0) @@ -975,22 +1010,22 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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)); + // log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->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()); + std::string message = stringf("Replacing memory %s with list of registers.", mem->str); bool first_element = true; for (auto &place : mem2reg_places[it.first]) { - message += stringf("%s%s", first_element ? " See " : ", ", place.c_str()); + message += stringf("%s%s", first_element ? " See " : ", ", place); first_element = false; } - log_warning("%s\n", message.c_str()); + log_warning("%s\n", message); } silent_activate: - // log("Note: Replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags)); + // log("Note: Replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str, long(memflags)); mem2reg_set.insert(mem); } @@ -1005,30 +1040,27 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (node->children[0]->range_swapped) std::swap(data_range_left, data_range_right); + auto loc = node->location; 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); + auto reg = std::make_unique(loc, AST_WIRE, std::make_unique(loc, AST_RANGE, + mkconst_int(loc, data_range_left, true), mkconst_int(loc, data_range_right, true))); + reg->str = stringf("%s[%d]", node->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.begin.filename = node->location.begin.filename; reg->location = node->location; - children.push_back(reg); while (reg->simplify(true, 1, -1, false)) { } + children.push_back(std::move(reg)); } } - AstNode *async_block = NULL; - while (mem2reg_as_needed_pass2(mem2reg_set, this, NULL, async_block)) { } + AstNode* async_block = nullptr; + while (mem2reg_as_needed_pass2(mem2reg_set, this, nullptr, async_block)) { } - vector delnodes; - mem2reg_remove(mem2reg_set, delnodes); - - for (auto node : delnodes) - delete node; + mem2reg_remove(mem2reg_set); } while (simplify(const_fold, 2, width_hint, sign_hint)) { } @@ -1036,8 +1068,6 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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) { @@ -1049,22 +1079,22 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // 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()); + log_file_warning(*location.begin.filename, location.begin.line, "Ignoring call to system %s %s.\n", type == AST_FCALL ? "function" : "task", 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")) + (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()); + log_file_warning(*location.begin.filename, location.begin.line, "System task `%s' outside initial or always block is unsupported.\n", str); delete_children(); str = std::string(); } else { // simplify the expressions and convert them to a special cell later in genrtlil - for (auto node : children) + for (auto& node : children) while (node->simplify(true, stage, -1, false)) {} if (current_always->type == AST_INITIAL && !flag_nodisplay && stage == 2) { @@ -1080,7 +1110,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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()); + log("%s", fmt.render()); } return false; @@ -1103,17 +1133,66 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin int counter = 0; label_genblks(existing, counter); std::map this_wire_scope; + + // Process package imports after clearing the scope but before processing module declarations for (size_t i = 0; i < children.size(); i++) { - AstNode *node = children[i]; + AstNode *child = children[i].get(); + if (child->type == AST_IMPORT) { + // Find the package in the design + AstNode *package_node = nullptr; + + // First look in current_ast->children (for packages in same file) + if (current_ast != nullptr) { + for (auto &design_child : current_ast->children) { + if (design_child->type == AST_PACKAGE) { + if (design_child->str == child->str) { + package_node = design_child.get(); + break; + } + } + } + } + + // If not found, look in design->verilog_packages (for packages from other files) + if (!package_node && simplify_design_context != nullptr) { + for (auto &design_package : simplify_design_context->verilog_packages) { + // Handle both with and without leading backslash + std::string package_name = design_package->str; + if (package_name[0] == '\\') { + package_name = package_name.substr(1); + } + if (package_name == child->str || design_package->str == child->str) { + package_node = design_package.get(); + break; + } + } + } + + if (package_node) { + PackageImporter importer(this, child); + // Import names from the package into current scope + for (auto& pkg_child : package_node->children) { + importer.import(current_scope, pkg_child.get()); + } + // Remove the import node since it's been processed + children.erase(children.begin() + i); + i--; // Adjust index since we removed an element + } else { + // If we can't find the package, just remove the import node to avoid errors later + log_warning("Package `%s' not found for import, removing import statement\n", child->str); + children.erase(children.begin() + i); + i--; // Adjust index since we removed an element + } + } + } + for (size_t i = 0; i < children.size(); i++) { + AstNode* node = children[i].get(); 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)); - } + for (auto& c : node->children[0]->children) { + if (!c->is_simple_const_expr()) + set_attribute(ID::dynports, AstNode::mkconst_int(c->location, 1, true)); } } if (this_wire_scope.count(node->str) > 0) { @@ -1123,16 +1202,15 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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]; + AstNode* r = node->children[0].get(); 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]; + auto &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; @@ -1159,17 +1237,14 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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()); + input_error("Incompatible re-declaration of wire %s.\n", node->str); continue; } this_wire_scope[node->str] = node; @@ -1183,22 +1258,22 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } if (node->type == AST_ENUM) { current_scope[node->str] = node; - for (auto enode : node->children) { + for (auto& enode : node->children) { log_assert(enode->type==AST_ENUM_ITEM); if (current_scope.count(enode->str) == 0) - current_scope[enode->str] = enode; + current_scope[enode->str] = enode.get(); else - input_error("enum item %s already exists\n", enode->str.c_str()); + input_error("enum item %s already exists\n", enode->str); } } } for (size_t i = 0; i < children.size(); i++) { - AstNode *node = children[i]; + auto& 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){ + for (auto& enode : node->children){ log_assert(enode->type==AST_ENUM_ITEM); while (node->simplify(true, 1, -1, false)) did_something = true; @@ -1206,29 +1281,29 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } } - for (AstNode *child : children) + for (auto& child : children) if (child->type == AST_ALWAYS && child->attributes.count(ID::always_comb)) - check_auto_nosync(child); + check_auto_nosync(child.get()); } // 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]; + auto& 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; + current_scope[node->str] = node.get(); } if (node->type == AST_ENUM) { - current_scope[node->str] = node; - for (auto enode : node->children) { + current_scope[node->str] = node.get(); + for (auto& enode : node->children) { log_assert(enode->type==AST_ENUM_ITEM); if (current_scope.count(enode->str) == 0) - current_scope[enode->str] = enode; + current_scope[enode->str] = enode.get(); else - input_error("enum item %s already exists in package\n", enode->str.c_str()); + input_error("enum item %s already exists in package\n", enode->str); } } } @@ -1250,7 +1325,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin current_always_clocked = false; if (type == AST_ALWAYS) - for (auto child : children) { + 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 && @@ -1262,7 +1337,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (type == AST_CELL) { bool lookup_suggested = false; - for (AstNode *child : children) { + for (auto& child : children) { // simplify any parameters to constants if (child->type == AST_PARASET) while (child->simplify(true, 1, -1, false)) { } @@ -1272,7 +1347,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (child->type == AST_ARGUMENT) { if (child->children.size() != 1) continue; - const AstNode *value = child->children[0]; + const auto& value = child->children[0]; if (value->type == AST_IDENTIFIER) { const AstNode *elem = value->id2ast; if (elem == nullptr) { @@ -1289,7 +1364,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // to be indirected to produce an unsigned connection lookup_suggested = true; } - else if (contains_unbased_unsized(value)) + else if (contains_unbased_unsized(value.get())) // unbased unsized literals extend to width of the context lookup_suggested = true; } @@ -1300,7 +1375,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin module = lookup_cell_module(); if (module) { size_t port_counter = 0; - for (AstNode *child : children) { + for (auto& child : children) { if (child->type != AST_ARGUMENT) continue; @@ -1324,39 +1399,44 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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; + { + auto arg_check = child->children[0].get(); - // only add indirection for standard inputs or outputs - if (ref->port_input == ref->port_output) - continue; + // plain identifiers never need indirection; this also prevents + // adding infinite levels of indirection + if (arg_check->type == AST_IDENTIFIER && arg_check->children.empty()) + continue; + // only add indirection for standard inputs or outputs + if (ref->port_input == ref->port_output) + continue; + } + + auto arg = std::move(child->children[0]); did_something = true; // create the indirection wire std::stringstream sstr; - sstr << "$indirect$" << ref->name.c_str() << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$indirect$" << ref->name.c_str() << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++); std::string tmp_str = sstr.str(); - add_wire_for_ref(ref, tmp_str); + add_wire_for_ref(location, ref, tmp_str); - AstNode *asgn = new AstNode(AST_ASSIGN); - current_ast_mod->children.push_back(asgn); + auto asgn_owned = std::make_unique(child->location, AST_ASSIGN); + auto* asgn = asgn_owned.get(); + current_ast_mod->children.push_back(std::move(asgn_owned)); - AstNode *ident = new AstNode(AST_IDENTIFIER); + auto ident = std::make_unique(child->location, 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); + asgn->children.push_back(std::move(ident)); + asgn->children.push_back(std::move(arg)); } else { log_assert(!ref->port_input && ref->port_output); - asgn->children.push_back(arg); - asgn->children.push_back(ident); + asgn->children.push_back(std::move(arg)); + asgn->children.push_back(std::move(ident)); } asgn->fixup_hierarchy_flags(); } @@ -1393,7 +1473,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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()); + log_warning("wire '%s' is assigned in a block at %s.\n", children[0]->str, loc_string()); if (type == AST_ASSIGN && children[0]->id2ast->is_reg) { bool is_rand_reg = false; if (children[1]->type == AST_FCALL) { @@ -1407,7 +1487,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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()); + log_warning("reg '%s' is assigned in a continuous assignment at %s.\n", children[0]->str, loc_string()); } children[0]->was_checked = true; } @@ -1416,7 +1496,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin case AST_STRUCT: case AST_UNION: if (!basic_prep) { - for (auto *node : children) { + for (auto& node : children) { // resolve any ranges while (!node->basic_prep && node->simplify(true, stage, -1, false)) { did_something = true; @@ -1430,9 +1510,10 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // 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); + current_ast_mod->children.push_back(std::move(wnode)); } basic_prep = true; + is_custom_type = false; } break; @@ -1453,9 +1534,9 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin break; case AST_ENUM: - //log("\nENUM %s: %d child %d\n", str.c_str(), basic_prep, children[0]->basic_prep); + //log("\nENUM %s: %d child %d\n", str, basic_prep, children[0]->basic_prep); if (!basic_prep) { - for (auto item_node : children) { + for (auto& item_node : children) { while (!item_node->basic_prep && item_node->simplify(false, stage, -1, false)) did_something = true; } @@ -1472,8 +1553,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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); + size_packed_struct(attributes[ID::wiretype].get(), 0); + add_members_to_scope(attributes[ID::wiretype].get(), str); } } while (!children[0]->basic_prep && children[0]->simplify(false, stage, -1, false) == true) @@ -1501,27 +1582,26 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin break; case AST_CAST_SIZE: { - int width = 1; - AstNode *node; - AstNode *child = children[0]; - - if (child->type == AST_WIRE) { + if (children[0]->type == AST_WIRE) { + int width = 1; + std::unique_ptr node; + auto* child = children[0].get(); 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); + node = mkconst_int(child->location, 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()); + input_error("Unknown identifier `%s' used as type name\n", type_name); 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()); + input_error("`%s' does not name a type\n", type_name); log_assert(resolved_type_node->children.size() == 1); - AstNode *template_node = resolved_type_node->children[0]; + auto* template_node = resolved_type_node->children[0].get(); // Ensure typedef itself is fully simplified while (template_node->simplify(const_fold, stage, width_hint, sign_hint)) {}; @@ -1530,9 +1610,9 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin { case AST_WIRE: { if (template_node->children.size() > 0 && template_node->children[0]->type == AST_RANGE) - width = range_width(this, template_node->children[0]); + width = range_width(this, template_node->children[0].get()); child->delete_children(); - node = mkconst_int(width, true); + node = mkconst_int(child->location, width, true); break; } @@ -1540,18 +1620,17 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin case AST_UNION: { child->delete_children(); width = size_packed_struct(template_node, 0); - node = mkconst_int(width, false); + node = mkconst_int(child->location, width, false); break; } default: - log_error("Don't know how to translate static cast of type %s\n", type2str(template_node->type).c_str()); + log_error("Don't know how to translate static cast of type %s\n", type2str(template_node->type)); } } - delete child; children.erase(children.begin()); - children.insert(children.begin(), node); + children.insert(children.begin(), std::move(node)); } detect_width_simple = true; @@ -1608,7 +1687,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin case AST_GT: width_hint = -1; sign_hint = true; - for (auto child : children) { + for (auto& child : children) { while (!child->basic_prep && child->simplify(false, stage, -1, false) == true) did_something = true; child->detectSignWidthWorker(width_hint, sign_hint); @@ -1646,7 +1725,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (type == AST_REPLICATE) while (children[0]->simplify(true, stage, -1, false) == true) did_something = true; - for (auto child : children) + for (auto& child : children) while (!child->basic_prep && child->simplify(false, stage, -1, false) == true) did_something = true; detectSignWidth(width_hint, sign_hint); @@ -1663,11 +1742,11 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin bool backup_unevaluated_tern_branch = unevaluated_tern_branch; AstNode *chosen = get_tern_choice().first; - unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[2]; + unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[2].get(); 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]; + unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[1].get(); while (!children[2]->basic_prep && children[2]->simplify(false, stage, -1, false)) did_something = true; @@ -1704,12 +1783,12 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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]); + std::vector> new_children; + new_children.push_back(std::move(children[0])); for (int i = 1; i < GetSize(children); i++) { - AstNode *child = children[i]; + auto& child = children[i]; log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); - for (auto v : child->children) { + for (auto& v : child->children) { if (v->type == AST_DEFAULT) goto keep_const_cond; if (v->type == AST_BLOCK) @@ -1720,8 +1799,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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]; + // This is the only reachable case. Skip to the end + i = GetSize(children); goto keep_const_cond; } continue; @@ -1730,9 +1809,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } if (0) keep_const_cond: - new_children.push_back(child); - else - delete child; + new_children.push_back(std::move(child)); } new_children.swap(children); } @@ -1758,7 +1835,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin break; if (type == AST_GENBLOCK) break; - if (type == AST_CELLARRAY && children[i]->type == AST_CELL) + if (type == AST_CELLARRAY && (children[i]->type == AST_CELL || children[i]->type == AST_PRIMITIVE)) continue; if (type == AST_BLOCK && !str.empty()) break; @@ -1768,7 +1845,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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]; + unevaluated_tern_branch = chosen && chosen != children[i].get(); } while (did_something_here && i < children.size()) { bool const_fold_here = const_fold; @@ -1780,10 +1857,10 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin const_fold_here = true; if (type == AST_BLOCK) { current_block = this; - current_block_child = children[i]; + current_block_child = children[i].get(); } if ((type == AST_ALWAYS || type == AST_INITIAL) && children[i]->type == AST_BLOCK) - current_top_block = children[i]; + current_top_block = children[i].get(); if (i == 0 && child_0_is_self_determined) width_hint_here = -1, sign_hint_here = false; if (i == 1 && child_1_is_self_determined) @@ -1797,7 +1874,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin did_something = true; } if (stage == 2 && children[i]->type == AST_INITIAL && current_ast_mod != this) { - current_ast_mod->children.push_back(children[i]); + current_ast_mod->children.push_back(std::move(children[i])); children.erase(children.begin() + (i--)); did_something = true; } @@ -1837,14 +1914,12 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin current_always_clocked = backup_current_always_clocked; for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) { - if (it->second == NULL) + if (it->second == nullptr) 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(); @@ -1869,26 +1944,26 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } if (pos == std::string::npos) - input_error("Can't find object for defparam `%s`!\n", RTLIL::unescape_id(paramname).c_str()); + input_error("Can't find object for defparam `%s`!\n", RTLIL::unescape_id(paramname)); 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()); + RTLIL::unescape_id(modname), RTLIL::unescape_id(paramname)); - AstNode *paraset = new AstNode(AST_PARASET, children[1]->clone(), GetSize(children) > 2 ? children[2]->clone() : NULL); + auto paraset = std::make_unique(location, AST_PARASET, children[1]->clone(), GetSize(children) > 2 ? children[2]->clone() : nullptr); paraset->str = paramname; AstNode *cell = current_scope.at(modname); - cell->children.insert(cell->children.begin() + 1, paraset); + cell->children.insert(cell->children.begin() + 1, std::move(paraset)); delete_children(); } // resolve typedefs if (type == AST_TYPEDEF) { log_assert(children.size() == 1); - auto type_node = children[0]; + 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; @@ -1903,13 +1978,13 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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()); + input_error("Unknown identifier `%s' used as type name\n", type_name); } 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()); + input_error("`%s' does not name a type\n", type_name); log_assert(resolved_type_node->children.size() == 1); - AstNode *template_node = resolved_type_node->children[0]; + auto& 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 @@ -1918,10 +1993,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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); - if (newNode->attributes.count(ID::wiretype)) - delete newNode->attributes[ID::wiretype]; - newNode->set_attribute(ID::wiretype, mkconst_str(resolved_type_node->str)); + newNode = make_packed_struct(template_node.get(), str, attributes); + newNode->set_attribute(ID::wiretype, mkconst_str(newNode->location, resolved_type_node->str)); // add original input/output attribute to resolved wire newNode->is_input = this->is_input; newNode->is_output = this->is_output; @@ -1932,9 +2005,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // Prepare replacement node. newNode = template_node->clone(); newNode->str = str; - if (newNode->attributes.count(ID::wiretype)) - delete newNode->attributes[ID::wiretype]; - newNode->set_attribute(ID::wiretype, mkconst_str(resolved_type_node->str)); + newNode->set_attribute(ID::wiretype, mkconst_str(newNode->location, resolved_type_node->str)); newNode->is_input = is_input; newNode->is_output = is_output; newNode->is_wand = is_wand; @@ -1943,30 +2014,30 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin newNode->set_attribute(pair.first, pair.second->clone()); // if an enum then add attributes to support simulator tracing - newNode->annotateTypedEnums(template_node); + newNode->annotateTypedEnums(template_node.get()); 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()); + input_error("Cannot extend unpacked type `%s' with packed dimensions\n", type_name); // Add packed dimensions. if (add_packed_dimensions) { - AstNode *packed = children[1]; + auto& packed = children[1]; if (newNode->children.empty()) newNode->children.insert(newNode->children.begin(), packed->clone()); else - prepend_ranges(newNode->children[0], packed); + prepend_ranges(newNode->children[0], packed.get()); } // Add unpacked dimensions. if (type == AST_MEMORY) { - AstNode *unpacked = children.back(); + auto& unpacked = children.back(); if (GetSize(newNode->children) < 2) newNode->children.push_back(unpacked->clone()); else - prepend_ranges(newNode->children[1], unpacked); + prepend_ranges(newNode->children[1], unpacked.get()); newNode->type = type; } @@ -1987,14 +2058,14 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // 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]; + auto expr = std::move(children.front()); children.erase(children.begin()); while (is_custom_type && simplify(const_fold, stage, width_hint, sign_hint)) {}; type = param_type; - children.insert(children.begin(), expr); + children.insert(children.begin(), std::move(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()); + input_error("unpacked array type `%s' cannot be used for a parameter\n", children[1]->str); fixup_hierarchy_flags(); did_something = true; } @@ -2004,7 +2075,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // resolve constant prefixes if (type == AST_PREFIX) { if (children[0]->type != AST_CONSTANT) { - // dumpAst(NULL, "> "); + // dumpAst(nullptr, "> "); input_error("Index in generate block prefix syntax is not constant!\n"); } if (children[1]->type == AST_PREFIX) @@ -2014,7 +2085,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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); + newNode->str = stringf("%s[%d].%s", str, children[0]->integer, second_part); goto apply_newNode; } @@ -2025,7 +2096,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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); + newNode = mkconst_bits(location, new_value.to_bits(), children[1]->is_signed); goto apply_newNode; } @@ -2069,13 +2140,13 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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]; + auto* val = attributes[ID::force_upto].get(); 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]; + auto* val = attributes[ID::force_downto].get(); if (val->type != AST_CONSTANT) input_error("Attribute `force_downto' with non-constant value!\n"); force_downto = val->asAttrConst().as_bool(); @@ -2086,6 +2157,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin std::swap(range_left, range_right); range_swapped = force_upto; } + if (range_left == range_right && !attributes.count(ID::single_bit_vector)) + set_attribute(ID::single_bit_vector, mkconst_int(location, 1, false)); } } else { if (!range_valid) @@ -2094,6 +2167,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin range_swapped = false; range_left = 0; range_right = 0; + attributes.erase(ID::single_bit_vector); } } @@ -2104,19 +2178,19 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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); + for (auto& range : children[i]->children) { + width *= add_dimension(this, range.get()); 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)); + auto loc = children[i]->location; + children[i] = std::make_unique(loc, AST_RANGE, mkconst_int(loc, left, true), mkconst_int(loc, right, true)); fixup_hierarchy_flags(); did_something = true; } else if (children[i]->type == AST_RANGE) { - add_dimension(this, children[i]); + add_dimension(this, children[i].get()); if (i) unpacked_dimensions++; } } @@ -2128,15 +2202,15 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // 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)) + 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; + std::unique_ptr unpacked_range = nullptr; + std::unique_ptr packed_range = nullptr; if (id2ast->unpacked_dimensions) { if (id2ast->unpacked_dimensions > 1) { @@ -2144,8 +2218,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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; + auto& range = children[0]->type == AST_MULTIRANGE ? children[0]->children[0] : children[0]; + unpacked_range = std::move(range); range = nullptr; } } @@ -2156,21 +2230,19 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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; + auto& range = children[0]->type == AST_MULTIRANGE ? children[0]->children[dims_sel - 1] : children[0]; + packed_range = std::move(range); range = nullptr; } } - for (auto &it : children) - delete it; children.clear(); if (unpacked_range) - children.push_back(unpacked_range); + children.push_back(std::move(unpacked_range)); if (packed_range) - children.push_back(packed_range); + children.push_back(std::move(packed_range)); fixup_hierarchy_flags(); basic_prep = true; @@ -2185,20 +2257,21 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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", + log_file_warning(*location.begin.filename, location.begin.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); + children[0] = mkconst_bits(location, 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; + RTLIL::Const val; + if (children[0]->is_unsized) { + val = children[0]->bitsAsUnsizedConst(width); + } else { + val = children[0]->bitsAsConst(width); + } + children[0] = mkconst_bits(location, val.to_bits(), is_signed); fixup_hierarchy_flags(); } children[0]->is_signed = is_signed; @@ -2210,8 +2283,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } 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] = std::make_unique(location, AST_REALVALUE); children[0]->realvalue = as_realvalue; fixup_hierarchy_flags(); did_something = true; @@ -2240,7 +2312,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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 = std::make_unique(location, AST_IDENTIFIER, std::move(range)); newNode->str = sname; // save type and original number of dimensions for $size() etc. newNode->set_attribute(ID::wiretype, item_node->clone()); @@ -2252,7 +2324,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } newNode->basic_prep = true; if (item_node->is_signed) - newNode = new AstNode(AST_TO_SIGNED, newNode); + newNode = std::make_unique(location, AST_TO_SIGNED, std::move(newNode)); goto apply_newNode; } } @@ -2263,8 +2335,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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()); + for (auto& node : current_scope_ast->children) { + //log("looking at mod scope child %s\n", type2str(node->type)); switch (node->type) { case AST_PARAMETER: case AST_LOCALPARAM: @@ -2275,19 +2347,19 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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()); + //log("found child %s, %s\n", type2str(node->type), node->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; + //log("add %s, type %s to scope\n", str, type2str(node->type)); + current_scope[node->str] = node.get(); } break; case AST_ENUM: - current_scope[node->str] = node; - for (auto enum_node : node->children) { + current_scope[node->str] = node.get(); + 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; + //log("\nadding enum item %s to scope\n", str); + current_scope[str] = enum_node.get(); } } break; @@ -2298,15 +2370,15 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } 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()); + input_error("Identifier `%s' is implicitly declared outside of a module.\n", str); } else if (flag_autowire || str == "\\$global_clock") { - AstNode *auto_wire = new AstNode(AST_AUTOWIRE); + auto auto_wire = std::make_unique(location, AST_AUTOWIRE); auto_wire->str = str; - current_ast_mod->children.push_back(auto_wire); - current_scope[str] = auto_wire; + current_scope[str] = auto_wire.get(); + current_ast_mod->children.push_back(std::move(auto_wire)); did_something = true; } else { - input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); + input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str); } } if (id2ast != current_scope[str]) { @@ -2318,7 +2390,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // 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) + if (id2ast == nullptr || 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; @@ -2331,41 +2403,41 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin std::swap(data_range_left, data_range_right); std::stringstream sstr; - sstr << "$mem2bits$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2bits$" << str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.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))); + auto wire_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, data_range_left, true), mkconst_int(location, data_range_right, true))); + auto* wire = wire_owned.get(); + current_ast_mod->children.push_back(std::move(wire_owned)); wire->str = wire_id; if (current_block) - wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); - current_ast_mod->children.push_back(wire); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire->simplify(true, 1, -1, false)) { } - AstNode *data = clone(); - delete data->children[1]; + auto data = clone(); data->children.pop_back(); - AstNode *assign = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), data); + auto assign = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), std::move(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) + while (assign_idx < current_block->children.size() && current_block->children[assign_idx].get() != current_block_child) assign_idx++; log_assert(assign_idx < current_block->children.size()); - current_block->children.insert(current_block->children.begin()+assign_idx, assign); + current_block->children.insert(current_block->children.begin()+assign_idx, std::move(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); + auto proc = std::make_unique(location, AST_ALWAYS, std::make_unique(location, AST_BLOCK)); + proc->children[0]->children.push_back(std::move(assign)); + current_ast_mod->children.push_back(std::move(proc)); } - newNode = new AstNode(AST_IDENTIFIER, children[1]->clone()); + newNode = std::make_unique(location, AST_IDENTIFIER, children[1]->clone()); newNode->str = wire_id; newNode->integer = integer; // save original number of dimensions for $size() etc. newNode->id2ast = wire; @@ -2377,8 +2449,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (type == AST_REPEAT) { - AstNode *count = children[0]; - AstNode *body = children[1]; + auto count = std::move(children[0]); + auto body = std::move(children[1]); // eval count expression while (count->simplify(true, stage, 32, true)) { } @@ -2392,22 +2464,20 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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]; + auto& init_ast = children[0]; + auto& while_ast = children[1]; + auto& next_ast = children[2]; + auto* body_ast = children[3].get(); 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); + body_ast = body_ast->children.at(0).get(); const char* loop_type_str = "procedural"; const char* var_type_str = "register"; @@ -2423,16 +2493,16 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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) + if (init_ast->children[0]->id2ast == nullptr || 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) + if (next_ast->children[0]->id2ast == nullptr || 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(); + auto varbuf = init_ast->children[1]->clone(); { int expr_width_hint = -1; bool expr_sign_hint = true; @@ -2457,23 +2527,23 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } } - varbuf = new AstNode(AST_LOCALPARAM, varbuf); + varbuf = std::make_unique(location, AST_LOCALPARAM, std::move(varbuf)); varbuf->str = init_ast->children[0]->str; AstNode *backup_scope_varbuf = current_scope[varbuf->str]; - current_scope[varbuf->str] = varbuf; + current_scope[varbuf->str] = varbuf.get(); 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->children[current_block_idx].get() != current_block_child) current_block_idx++; } while (1) { // eval 2nd expression - AstNode *buf = while_ast->clone(); + auto buf = while_ast->clone(); { int expr_width_hint = -1; bool expr_sign_hint = true; @@ -2485,10 +2555,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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; @@ -2501,27 +2569,26 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin std::string prefix = sstr.str(); // create a scoped localparam for the current value of the loop variable - AstNode *local_index = varbuf->clone(); + auto 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); + current_scope[local_index->str] = local_index.get(); + current_ast_mod->children.push_back(std::move(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]); + current_ast_mod->children.push_back(std::move(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]); + current_block->children.insert(current_block->children.begin() + current_block_idx++, std::move(buf->children[i])); } buf->children.clear(); - delete buf; // eval 3rd expression buf = next_ast->children[1]->clone(); @@ -2534,21 +2601,18 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } 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()); + input_error("Right hand side of 3rd expression of %s for-loop is not constant (%s)!\n", loop_type_str, type2str(buf->type)); - delete varbuf->children[0]; - varbuf->children[0] = buf; + varbuf->children[0] = std::move(buf); } if (type == AST_FOR) { - AstNode *buf = next_ast->clone(); - delete buf->children[1]; + auto buf = next_ast->clone(); buf->children[1] = varbuf->children[0]->clone(); - current_block->children.insert(current_block->children.begin() + current_block_idx++, buf); + current_block->children.insert(current_block->children.begin() + current_block_idx++, std::move(buf)); } current_scope[varbuf->str] = backup_scope_varbuf; - delete varbuf; delete_children(); did_something = true; } @@ -2559,7 +2623,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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); + log_assert(!sv_mode_but_global_and_used_for_literally_one_condition); children[i]->input_error("Local declaration in unnamed block is only supported in SystemVerilog mode!\n"); } } @@ -2574,19 +2638,19 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin && 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) + for (auto& child : children) if (child->type == AST_WIRE && !child->attributes.count(ID::nosync)) - mark_auto_nosync(this, child); + mark_auto_nosync(this, child.get()); - std::vector new_children; + 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]; + current_scope[children[i]->str] = children[i].get(); + current_ast_mod->children.push_back(std::move(children[i])); } else - new_children.push_back(children[i]); + new_children.push_back(std::move(children[i])); children.swap(new_children); did_something = true; @@ -2602,7 +2666,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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]); + current_ast_mod->children.push_back(std::move(children[i])); } children.clear(); @@ -2612,7 +2676,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // simplify generate-if blocks if (type == AST_GENIF && children.size() != 0) { - AstNode *buf = children[0]->clone(); + auto buf = children[0]->clone(); while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) @@ -2620,17 +2684,15 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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; + buf = children.size() > 2 ? children[2]->clone() : nullptr; } if (buf) { if (buf->type != AST_GENBLOCK) - buf = new AstNode(AST_GENBLOCK, buf); + buf = std::make_unique(location, AST_GENBLOCK, std::move(buf)); if (!buf->str.empty()) { buf->expand_genblock(buf->str + "."); @@ -2638,11 +2700,10 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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]); + current_ast_mod->children.push_back(std::move(buf->children[i])); } buf->children.clear(); - delete buf; } delete_children(); @@ -2652,7 +2713,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // simplify generate-case blocks if (type == AST_GENCASE && children.size() != 0) { - AstNode *buf = children[0]->clone(); + auto buf = children[0]->clone(); while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) @@ -2662,24 +2723,23 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin bool ref_signed = buf->is_signed; RTLIL::Const ref_value = buf->bitsAsConst(); - delete buf; - AstNode *selected_case = NULL; + AstNode *selected_case = nullptr; 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); + AstNode *this_genblock = nullptr; + for (auto& child : children.at(i)->children) { + log_assert(this_genblock == nullptr); if (child->type == AST_GENBLOCK) - this_genblock = child; + this_genblock = child.get(); } - for (auto child : children.at(i)->children) + for (auto& child : children.at(i)->children) { if (child->type == AST_DEFAULT) { - if (selected_case == NULL) + if (selected_case == nullptr) selected_case = this_genblock; continue; } @@ -2696,7 +2756,6 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } 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; @@ -2706,7 +2765,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin } } - if (selected_case != NULL) + if (selected_case != nullptr) { log_assert(selected_case->type == AST_GENBLOCK); buf = selected_case->clone(); @@ -2717,11 +2776,10 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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]); + current_ast_mod->children.push_back(std::move(buf->children[i])); } buf->children.clear(); - delete buf; } delete_children(); @@ -2734,20 +2792,27 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (!children.at(0)->range_valid) input_error("Non-constant array range on cell array.\n"); - newNode = new AstNode(AST_GENBLOCK); + newNode = std::make_unique(location, 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; + if (this->children.at(1)->type == AST_PRIMITIVE) { + // Move the range to the AST_PRIMITIVE node and replace this with the AST_PRIMITIVE node handled below + newNode = std::move(this->children.at(1)); + newNode->range_left = this->children.at(0)->range_left; + newNode->range_right = this->children.at(0)->range_right; + newNode->range_valid = true; + goto apply_newNode; + } + 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); + auto new_cell_owned = children.at(1)->clone(); + auto* new_cell = new_cell_owned.get(); + newNode->children.push_back(std::move(new_cell_owned)); 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()); - } + + 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); } goto apply_newNode; @@ -2757,58 +2822,61 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin if (type == AST_PRIMITIVE) { if (children.size() < 2) - input_error("Insufficient number of arguments for primitive `%s'!\n", str.c_str()); + input_error("Insufficient number of arguments for primitive `%s'!\n", str); - std::vector children_list; - for (auto child : children) { + 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]); + children_list.push_back(std::move(child->children[0])); child->children.clear(); - delete child; } children.clear(); + // TODO handle bit-widths of primitives and support cell arrays for more primitives + + if (range_valid && str != "tran") + input_error("Cell arrays of primitives are currently not supported.\n"); + 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()); + input_error("Invalid number of arguments for primitive `%s'!\n", str); std::vector z_const(1, RTLIL::State::Sz); - AstNode *mux_input = children_list.at(1); + auto& mux_input = children_list.at(1); if (str == "notif0" || str == "notif1") { - mux_input = new AstNode(AST_BIT_NOT, mux_input); + mux_input = std::make_unique(location, AST_BIT_NOT, std::move(mux_input)); } - AstNode *node = new AstNode(AST_TERNARY, children_list.at(2)); + auto node = std::make_unique(location, AST_TERNARY, std::move(children_list.at(2))); if (str == "bufif0") { - node->children.push_back(AstNode::mkconst_bits(z_const, false)); - node->children.push_back(mux_input); + node->children.push_back(AstNode::mkconst_bits(location, z_const, false)); + node->children.push_back(std::move(mux_input)); } else { - node->children.push_back(mux_input); - node->children.push_back(AstNode::mkconst_bits(z_const, false)); + node->children.push_back(std::move(mux_input)); + node->children.push_back(AstNode::mkconst_bits(location, z_const, false)); } str.clear(); type = AST_ASSIGN; - children.push_back(children_list.at(0)); + children.push_back(std::move(children_list.at(0))); children.back()->was_checked = true; - children.push_back(node); + children.push_back(std::move(node)); fixup_hierarchy_flags(); did_something = true; } - else if (str == "buf" || str == "not") + else if (str == "buf" || str == "not" || str == "tran") { - AstNode *input = children_list.back(); + auto& input = children_list.back(); if (str == "not") - input = new AstNode(AST_BIT_NOT, input); + input = std::make_unique(location, AST_BIT_NOT, std::move(input)); - newNode = new AstNode(AST_GENBLOCK); + newNode = std::make_unique(location, 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.push_back(std::make_unique(location, AST_ASSIGN, std::move(*it), input->clone())); newNode->children.back()->was_checked = true; } - delete input; did_something = true; } @@ -2831,20 +2899,20 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin op_type = AST_BIT_XOR, invert_results = true; log_assert(op_type != AST_NONE); - AstNode *node = children_list[1]; + auto& 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 = std::make_unique(location, op_type, std::move(node), std::move(children_list[i])); node->location = location; } if (invert_results) - node = new AstNode(AST_BIT_NOT, node); + node = std::make_unique(location, AST_BIT_NOT, std::move(node)); str.clear(); type = AST_ASSIGN; - children.push_back(children_list[0]); + children.push_back(std::move(children_list[0])); children.back()->was_checked = true; - children.push_back(node); + children.push_back(std::move(node)); fixup_hierarchy_flags(); did_something = true; } @@ -2859,7 +2927,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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) + if (children[0]->id2ast == nullptr || children[0]->id2ast->type != AST_WIRE) goto skip_dynamic_range_lvalue_expansion; if (!children[0]->id2ast->range_valid) goto skip_dynamic_range_lvalue_expansion; @@ -2871,11 +2939,11 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin int wire_offset = children[0]->id2ast->range_right; int result_width = 1; - AstNode *shift_expr = NULL; - AstNode *range = children[0]->children[0]; + std::unique_ptr shift_expr = nullptr; + auto& 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 (!try_determine_range_width(range.get(), result_width)) + input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str); if (range->children.size() >= 2) shift_expr = range->children[1]->clone(); @@ -2918,14 +2986,14 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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; + shift_expr->children[0]->children[0].get() : + shift_expr.get(); // 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)) + (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; @@ -2957,9 +3025,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // // 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); + bitno_div = std::gcd((long long)stride, shift_mod); } } } @@ -2973,11 +3039,14 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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); + auto rvalue = mktemp_logic(location, "$bitselwrite$rvalue$", current_ast_mod, true, rvalue_width - 1, 0, rvalue_sign); + auto* rvalue_leaky = rvalue.get(); + log("make 1\n"); + auto case_node_owned = std::make_unique(location, AST_CASE, std::move(shift_expr)); + auto* case_node = case_node_owned.get(); + newNode = std::make_unique(location, AST_BLOCK, + std::make_unique(location, AST_ASSIGN_EQ, std::move(rvalue), children[1]->clone()), + std::move(case_node_owned)); did_something = true; for (int i = 1 - result_width; i < wire_width; i++) { @@ -2990,26 +3059,26 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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(); + auto cond = std::make_unique(location, AST_COND, mkconst_int(location, start_bit, case_sign_hint, max_width)); + auto 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); + lvalue->children.push_back(std::make_unique(location, AST_RANGE, + mkconst_int(location, end_bit, true), mkconst_int(location, start_bit, true))); + cond->children.push_back(std::make_unique(location, AST_BLOCK, std::make_unique(location, std::move(type), std::move(lvalue), rvalue_leaky->clone()))); + case_node->children.push_back(std::move(cond)); } } else { // mask and shift operations // dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb) - AstNode *lvalue = children[0]->clone(); + auto lvalue = children[0]->clone(); lvalue->delete_children(); if (member_node) lvalue->set_attribute(ID::wiretype, member_node->clone()); - AstNode *old_data = lvalue->clone(); + auto old_data = lvalue->clone(); if (type == AST_ASSIGN_LE) old_data->lookahead = true; @@ -3018,51 +3087,52 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin shift_expr->detectSignWidth(shift_width_hint, shift_sign_hint); // All operations are carried out in a new block. - newNode = new AstNode(AST_BLOCK); + newNode = std::make_unique(location, 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)); - + auto pos = mktemp_logic(location, "$bitselwrite$pos$", current_ast_mod, true, shift_width_hint - 1, 0, shift_sign_hint); // Calculate lsb from position. - AstNode *shift_val = pos->clone(); + auto shift_val = pos->clone(); + + newNode->children.push_back(std::make_unique(location, AST_ASSIGN_EQ, std::move(pos), std::move(shift_expr))); // 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); + shift_val = std::make_unique(location, AST_CAST_SIZE, mkconst_int(location, shift_width_hint + 1, true), std::move(shift_val)); if (!shift_sign_hint) - shift_val = new AstNode(AST_TO_SIGNED, shift_val); + shift_val = std::make_unique(location, AST_TO_SIGNED, std::move(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)); + shift_val = std::make_unique(location, AST_SUB, std::move(shift_val), mkconst_int(location, 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); + shift_val = std::make_unique(location, AST_SUB, mkconst_int(location, wire_width - result_width, true), std::move(shift_val)); // AST_SHIFT uses negative amounts for shifting left - shift_val = new AstNode(AST_NEG, shift_val); + shift_val = std::make_unique(location, AST_NEG, std::move(shift_val)); + auto also_shift_val = shift_val->clone(); // dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb) did_something = true; - AstNode *bitmask = mkconst_bits(std::vector(result_width, State::S1), false); + auto bitmask = mkconst_bits(location, 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)))); + std::make_unique(location, std::move(type), + std::move(lvalue), + std::make_unique(location, AST_BIT_OR, + std::make_unique(location, AST_BIT_AND, + std::move(old_data), + std::make_unique(location, AST_BIT_NOT, + std::make_unique(location, AST_SHIFT, + std::move(bitmask), + std::move(shift_val)))), + std::make_unique(location, AST_SHIFT, + std::make_unique(location, AST_TO_UNSIGNED, + std::make_unique(location, AST_CAST_SIZE, + mkconst_int(location, result_width, true), + children[1]->clone())), + std::move(also_shift_val))))); newNode->fixup_hierarchy_flags(true); } @@ -3072,11 +3142,11 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin 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 && + if (stage > 1 && type == AST_IDENTIFIER && id2ast != nullptr && 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 = std::make_unique(location, AST_MEMRD, children[0]->children[0]->clone()); newNode->str = str; newNode->id2ast = id2ast; goto apply_newNode; @@ -3087,39 +3157,41 @@ skip_dynamic_range_lvalue_expansion:; { bool found_nontrivial_member = false; - for (auto child : children[0]->children) { - if (child->type == AST_IDENTIFIER && child->id2ast != NULL && child->id2ast->type == AST_MEMORY) + for (auto& child : children[0]->children) { + if (child->type == AST_IDENTIFIER && child->id2ast != nullptr && child->id2ast->type == AST_MEMORY) found_nontrivial_member = true; } if (found_nontrivial_member) { - newNode = new AstNode(AST_BLOCK); + newNode = std::make_unique(location, 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); + auto wire_tmp_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, width_hint-1, true), mkconst_int(location, 0, true))); + auto wire_tmp = wire_tmp_owned.get(); + wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", RTLIL::encode_filename(*location.begin.filename), location.begin.line, autoidx++); current_scope[wire_tmp->str] = wire_tmp; - wire_tmp->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); + current_ast_mod->children.push_back(std::move(wire_tmp_owned)); + wire_tmp->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire_tmp->simplify(true, 1, -1, false)) { } wire_tmp->is_logic = true; - AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER); + auto wire_tmp_id_owned = std::make_unique(location, AST_IDENTIFIER); + auto* wire_tmp_id = wire_tmp_id_owned.get(); wire_tmp_id->str = wire_tmp->str; - newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, wire_tmp_id, children[1]->clone())); + newNode->children.push_back(std::make_unique(location, AST_ASSIGN_EQ, std::move(wire_tmp_id_owned), children[1]->clone())); newNode->children.back()->was_checked = true; int cursor = 0; - for (auto child : children[0]->children) + 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)); + auto rhs = wire_tmp_id->clone(); + rhs->children.push_back(std::make_unique(location, AST_RANGE, AstNode::mkconst_int(location, cursor+child_width_hint-1, true), AstNode::mkconst_int(location, cursor, true))); + newNode->children.push_back(std::make_unique(location, type, child->clone(), std::move(rhs))); cursor += child_width_hint; } @@ -3138,15 +3210,15 @@ skip_dynamic_range_lvalue_expansion:; 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++); + sstr << "$memwr$" << children[0]->str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.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); + newNode = std::make_unique(location, AST_BLOCK); + auto defNode = std::make_unique(location, AST_BLOCK); int data_range_left = children[0]->id2ast->children[0]->range_left; int data_range_right = children[0]->id2ast->children[0]->range_right; @@ -3165,74 +3237,75 @@ skip_dynamic_range_lvalue_expansion:; for (int i = 0; i < mem_width; i++) set_bits_en.push_back(RTLIL::State::S1); - AstNode *node_addr = nullptr; + std::unique_ptr 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))); + auto wire_addr_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, addr_bits-1, true), mkconst_int(location, 0, true))); + auto* wire_addr = wire_addr_owned.get(); wire_addr->str = id_addr; wire_addr->was_checked = true; - current_ast_mod->children.push_back(wire_addr); + current_ast_mod->children.push_back(std::move(wire_addr_owned)); 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)); + auto assign_addr = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), mkconst_bits(location, x_bits_addr, false)); assign_addr->children[0]->str = id_addr; assign_addr->children[0]->was_checked = true; - defNode->children.push_back(assign_addr); + defNode->children.push_back(std::move(assign_addr)); - assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); + assign_addr = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, 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); + newNode->children.push_back(std::move(assign_addr)); - node_addr = new AstNode(AST_IDENTIFIER); + node_addr = std::make_unique(location, AST_IDENTIFIER); node_addr->str = id_addr; } - AstNode *node_data = nullptr; + std::unique_ptr 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))); + auto wire_data_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, mem_width-1, true), mkconst_int(location, 0, true))); + auto* wire_data = wire_data_owned.get(); 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; + current_ast_mod->children.push_back(std::move(wire_data_owned)); 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)); + auto assign_data = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), mkconst_bits(location, x_bits_data, false)); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; - defNode->children.push_back(assign_data); + defNode->children.push_back(std::move(assign_data)); - node_data = new AstNode(AST_IDENTIFIER); + node_data = std::make_unique(location, 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))); + auto wire_en_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, mem_width-1, true), mkconst_int(location, 0, true))); + auto* wire_en = wire_en_owned.get(); 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; + current_ast_mod->children.push_back(std::move(wire_en_owned)); 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)); + auto assign_en_first = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), mkconst_int(location, 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); + defNode->children.push_back(std::move(assign_en_first)); - AstNode *node_en = new AstNode(AST_IDENTIFIER); + auto node_en = std::make_unique(location, 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; + current_top_block->children.insert(current_top_block->children.begin(), std::move(defNode)); - AstNode *assign_data = nullptr; - AstNode *assign_en = nullptr; + std::unique_ptr assign_data = nullptr; + std::unique_ptr assign_en = nullptr; if (children[0]->children.size() == 2) { if (children[0]->children[1]->range_valid) @@ -3243,25 +3316,25 @@ skip_dynamic_range_lvalue_expansion:; 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 = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), + std::make_unique(location, AST_CONCAT, mkconst_bits(location, 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 = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), mkconst_bits(location, 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; + auto& the_range = children[0]->children[1]; + std::unique_ptr 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 (!try_determine_range_width(the_range.get(), width)) + input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str); if (the_range->children.size() >= 2) offset_ast = the_range->children[1]->clone(); @@ -3269,65 +3342,63 @@ skip_dynamic_range_lvalue_expansion:; 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)); + offset_ast = std::make_unique(location, AST_SUB, std::move(offset_ast), mkconst_int(location, 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 = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), + std::make_unique(location, 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 = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), + std::make_unique(location, AST_SHIFT_LEFT, mkconst_bits(location, 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 = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, 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 = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), mkconst_bits(location, 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); + newNode->children.push_back(std::move(assign_data)); if (assign_en) - newNode->children.push_back(assign_en); + newNode->children.push_back(std::move(assign_en)); - AstNode *wrnode; + std::unique_ptr wrnode; if (current_always->type == AST_INITIAL) - wrnode = new AstNode(AST_MEMINIT, node_addr, node_data, node_en, mkconst_int(1, false)); + wrnode = std::make_unique(location, AST_MEMINIT, std::move(node_addr), std::move(node_data), std::move(node_en), mkconst_int(location, 1, false)); else - wrnode = new AstNode(AST_MEMWR, node_addr, node_data, node_en); + wrnode = std::make_unique(location, AST_MEMWR, std::move(node_addr), std::move(node_data), std::move(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)); + wrnode->children.push_back(mkconst_int(location, 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)); + wrnode->children.push_back(mkconst_bits(location, priority_mask, false)); current_memwr_visible[wrnode->str].insert(portid); - current_always->children.push_back(wrnode); + current_always->children.push_back(std::move(wrnode)); } else { - current_ast_mod->children.push_back(wrnode); + current_ast_mod->children.push_back(std::move(wrnode)); } if (newNode->children.empty()) { - delete newNode; - newNode = new AstNode(); + newNode = std::make_unique(location); } goto apply_newNode; } @@ -3341,21 +3412,23 @@ skip_dynamic_range_lvalue_expansion:; { int myidx = autoidx++; - AstNode *wire = new AstNode(AST_WIRE); + auto wire_owned = std::make_unique(location, AST_WIRE); + auto* wire = wire_owned.get(); + current_ast_mod->children.push_back(std::move(wire_owned)); 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))); + auto cell_owned = std::make_unique(location, AST_CELL, std::make_unique(location, AST_CELLTYPE), std::make_unique(location, AST_ARGUMENT, std::make_unique(location, AST_IDENTIFIER))); + auto* cell = cell_owned.get(); 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); + current_ast_mod->children.push_back(std::move(cell_owned)); while (cell->simplify(true, 1, -1, false)) { } - newNode = new AstNode(AST_IDENTIFIER); + newNode = std::make_unique(location, AST_IDENTIFIER); newNode->str = wire->str; newNode->id2ast = wire; goto apply_newNode; @@ -3370,28 +3443,27 @@ skip_dynamic_range_lvalue_expansion:; 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())); + RTLIL::unescape_id(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()); + RTLIL::unescape_id(str)); if (GetSize(children) == 2) { - AstNode *buf = children[1]->clone(); + auto 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()); + input_error("Failed to evaluate system function `%s' with non-constant value.\n", str); num_steps = buf->asInt(true); - delete buf; } AstNode *block = nullptr; - for (auto child : current_always->children) + for (auto& child : current_always->children) if (child->type == AST_BLOCK) - block = child; + block = child.get(); log_assert(block != nullptr); @@ -3401,41 +3473,42 @@ skip_dynamic_range_lvalue_expansion:; } int myidx = autoidx++; - AstNode *outreg = nullptr; + 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))); + auto reg_owned = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, + mkconst_int(location, width_hint-1, true), mkconst_int(location, 0, true))); + auto* reg = reg_owned.get(); + current_ast_mod->children.push_back(std::move(reg_owned)); - reg->str = stringf("$past$%s:%d$%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, myidx, i); + reg->str = stringf("$past$%s:%d$%d$%d", RTLIL::encode_filename(*location.begin.filename), location.begin.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); + auto regid = std::make_unique(location, AST_IDENTIFIER); regid->str = reg->str; regid->id2ast = reg; regid->was_checked = true; - AstNode *rhs = nullptr; + std::unique_ptr rhs = nullptr; if (outreg == nullptr) { rhs = children.at(0)->clone(); } else { - rhs = new AstNode(AST_IDENTIFIER); + rhs = std::make_unique(location, AST_IDENTIFIER); rhs->str = outreg->str; rhs->id2ast = outreg; } - block->children.push_back(new AstNode(AST_ASSIGN_LE, regid, rhs)); + block->children.push_back(std::make_unique(location, AST_ASSIGN_LE, std::move(regid), std::move(rhs))); outreg = reg; } - newNode = new AstNode(AST_IDENTIFIER); + newNode = std::make_unique(location, AST_IDENTIFIER); newNode->str = outreg->str; newNode->id2ast = outreg; goto apply_newNode; @@ -3445,31 +3518,31 @@ skip_dynamic_range_lvalue_expansion:; { if (GetSize(children) != 1) input_error("System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); + RTLIL::unescape_id(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()); + RTLIL::unescape_id(str)); - AstNode *present = children.at(0)->clone(); - AstNode *past = clone(); + auto present = children.at(0)->clone(); + auto past = clone(); past->str = "\\$past"; if (str == "\\$stable") - newNode = new AstNode(AST_EQ, past, present); + newNode = std::make_unique(location, AST_EQ, std::move(past), std::move(present)); else if (str == "\\$changed") - newNode = new AstNode(AST_NE, past, present); + newNode = std::make_unique(location, AST_NE, std::move(past), std::move(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))); + newNode = std::make_unique(location, AST_LOGIC_AND, + std::make_unique(location, AST_LOGIC_NOT, std::make_unique(location, AST_BIT_AND, std::move(past), mkconst_int(location, 1, false))), + std::make_unique(location, AST_BIT_AND, std::move(present), mkconst_int(location, 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)))); + newNode = std::make_unique(location, AST_LOGIC_AND, + std::make_unique(location, AST_BIT_AND, std::move(past), mkconst_int(location, 1, false)), + std::make_unique(location, AST_LOGIC_NOT, std::make_unique(location, AST_BIT_AND, std::move(present), mkconst_int(location, 1, false)))); else log_abort(); @@ -3487,61 +3560,59 @@ skip_dynamic_range_lvalue_expansion:; { if (children.size() != 1) input_error("System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(str).c_str(), int(children.size())); + RTLIL::unescape_id(str), int(children.size())); - AstNode *buf = children[0]->clone(); + auto 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()); + input_error("Failed to evaluate system function `%s' with non-constant value.\n", 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); + newNode = mkconst_int(location, 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") + 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())); + RTLIL::unescape_id(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())); + RTLIL::unescape_id(str), int(children.size())); if (children.size() == 2) { - AstNode *buf = children[1]->clone(); + auto 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(); + auto 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; + AstNode *id_ast = nullptr; buf->detectSignWidth(width_hint, sign_hint); if (buf->type == AST_IDENTIFIER) { id_ast = buf->id2ast; - if (id_ast == NULL && current_scope.count(buf->str)) + if (id_ast == nullptr && 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()); + input_error("Failed to resolve identifier %s for width detection!\n", buf->str); if (id_ast->type == AST_WIRE || id_ast->type == AST_MEMORY) { // Check for item in packed struct / union @@ -3556,7 +3627,7 @@ skip_dynamic_range_lvalue_expansion:; // 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); + input_error("Dimension %d out of range in `%s', as it only has %d dimensions!\n", dim, id_ast->str, dims); expr_dimensions = dims - dim + 1; expr_unpacked_dimensions = std::max(id_ast->unpacked_dimensions - dim + 1, 0); @@ -3577,7 +3648,6 @@ skip_dynamic_range_lvalue_expansion:; left = high = width - 1; expr_dimensions = 1; } - delete buf; if (str == "\\$dimensions") result = expr_dimensions; else if (str == "\\$unpacked_dimensions") @@ -3597,7 +3667,7 @@ skip_dynamic_range_lvalue_expansion:; else { // str == "\\$bits" result = width * mem_depth; } - newNode = mkconst_int(result, true); + newNode = mkconst_int(location, result, true); goto apply_newNode; } @@ -3613,18 +3683,18 @@ skip_dynamic_range_lvalue_expansion:; 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())); + RTLIL::unescape_id(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())); + RTLIL::unescape_id(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()); + RTLIL::unescape_id(str)); int child_width_hint = width_hint; bool child_sign_hint = sign_hint; children[0]->detectSignWidth(child_width_hint, child_sign_hint); @@ -3635,7 +3705,7 @@ skip_dynamic_range_lvalue_expansion:; 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()); + RTLIL::unescape_id(str)); int child_width_hint = width_hint; bool child_sign_hint = sign_hint; children[1]->detectSignWidth(child_width_hint, child_sign_hint); @@ -3643,10 +3713,10 @@ skip_dynamic_range_lvalue_expansion:; } if (str == "\\$rtoi") { - newNode = AstNode::mkconst_int(x, true); + newNode = AstNode::mkconst_int(location, x, true); } else { - newNode = new AstNode(AST_REALVALUE); - if (str == "\\$ln") newNode->realvalue = ::log(x); + newNode = std::make_unique(location, 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); @@ -3675,64 +3745,62 @@ skip_dynamic_range_lvalue_expansion:; if (str == "\\$sformatf") { Fmt fmt = processFormat(stage, /*sformat_like=*/true); - newNode = AstNode::mkconst_str(fmt.render()); + newNode = AstNode::mkconst_str(location, 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())); + RTLIL::unescape_id(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]; + auto& 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()); + input_error("Failed to evaluate system function `%s' with non-constant control bit argument.\n", str); if (node->bits.size() != 1) - input_error("Failed to evaluate system function `%s' with control bit width != 1.\n", str.c_str()); + input_error("Failed to evaluate system function `%s' with control bit width != 1.\n", 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); + auto& exp = children[0]; + exp->detectSignWidth(exp_width, exp_sign, nullptr); - newNode = mkconst_int(0, false); + newNode = mkconst_int(location, 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)); + // ^^ ^^ + auto lsh_node = std::make_unique(location, AST_SHIFT_LEFT, exp->clone(), mkconst_int(location, i, false)); + auto rsh_node = std::make_unique(location, AST_SHIFT_RIGHT, std::move(lsh_node), mkconst_int(location, exp_width - 1, false)); - AstNode *or_node = nullptr; + std::unique_ptr 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)); + // ^^^ + auto eq_node = std::make_unique(location, AST_EQX, rsh_node->clone(), mkconst_bits(location, {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); + or_node = std::make_unique(location, AST_LOGIC_OR, std::move(or_node), std::move(eq_node)); else - or_node = eq_node; + or_node = std::move(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); + newNode = std::make_unique(location, AST_ADD, std::move(newNode), std::move(or_node)); } goto apply_newNode; @@ -3741,24 +3809,24 @@ skip_dynamic_range_lvalue_expansion:; 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())); + RTLIL::unescape_id(str), int(children.size())); - AstNode *countbits = clone(); + auto countbits = clone(); countbits->str = "\\$countbits"; if (str == "\\$countones") { - countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); - newNode = countbits; + countbits->children.push_back(mkconst_bits(location, {RTLIL::State::S1}, false)); + newNode = std::move(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)); + countbits->children.push_back(mkconst_bits(location, {RTLIL::Sx}, false)); + countbits->children.push_back(mkconst_bits(location, {RTLIL::Sz}, false)); + newNode = std::make_unique(location, AST_GT, std::move(countbits), mkconst_int(location, 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)); + countbits->children.push_back(mkconst_bits(location, {RTLIL::State::S1}, false)); + newNode = std::make_unique(location, AST_EQ, std::move(countbits), mkconst_int(location, 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)); + countbits->children.push_back(mkconst_bits(location, {RTLIL::State::S1}, false)); + newNode = std::make_unique(location, AST_LE, std::move(countbits), mkconst_int(location, 1, false)); } else { log_abort(); } @@ -3772,7 +3840,7 @@ skip_dynamic_range_lvalue_expansion:; std::string rtype, fname; std::vector argtypes; - std::vector args; + std::vector> args; rtype = RTLIL::unescape_id(dpi_decl->children.at(0)->str); fname = RTLIL::unescape_id(dpi_decl->children.at(1)->str); @@ -3790,10 +3858,7 @@ skip_dynamic_range_lvalue_expansion:; 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; + newNode = dpi_call(dpi_decl->location, rtype, fname, argtypes, args); goto apply_newNode; } @@ -3801,7 +3866,7 @@ skip_dynamic_range_lvalue_expansion:; 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()); + input_error("Can't resolve function name `%s'.\n", str); } if (type == AST_TCALL) @@ -3809,42 +3874,42 @@ skip_dynamic_range_lvalue_expansion:; 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' outside initial block is unsupported.\n", str); - input_error("System task `%s' executed.\n", str.c_str()); + input_error("System task `%s' executed.\n", 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())); + RTLIL::unescape_id(str), int(children.size())); - AstNode *node_filename = children[0]->clone(); + auto 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()); + input_error("Failed to evaluate system function `%s' with non-constant 1st argument.\n", str); - AstNode *node_memory = children[1]->clone(); + auto 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()); + input_error("Failed to evaluate system function `%s' with non-memory 2nd argument.\n", str); int start_addr = -1, finish_addr = -1; if (GetSize(children) > 2) { - AstNode *node_addr = children[2]->clone(); + auto 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()); + input_error("Failed to evaluate system function `%s' with non-constant 3rd argument.\n", str); start_addr = int(node_addr->asInt(false)); } if (GetSize(children) > 3) { - AstNode *node_addr = children[3]->clone(); + auto 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()); + input_error("Failed to evaluate system function `%s' with non-constant 4th argument.\n", str); finish_addr = int(node_addr->asInt(false)); } @@ -3852,40 +3917,39 @@ skip_dynamic_range_lvalue_expansion:; if (current_always->type == AST_INITIAL) { pool queue; log_assert(current_always->children[0]->type == AST_BLOCK); - queue.insert(current_always->children[0]); + queue.insert(current_always->children[0].get()); while (!unconditional_init && !queue.empty()) { pool next_queue; - for (auto n : queue) - for (auto c : n->children) { - if (c == this) + for (auto& n : queue) + for (auto& c : n->children) { + if (c.get() == this) unconditional_init = true; - next_queue.insert(c); + next_queue.insert(c.get()); } 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()); + input_error("Can't resolve task name `%s'.\n", str); } std::stringstream sstr; - sstr << str << "$func$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++) << '.'; + sstr << str << "$func$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.line << "$" << (autoidx++) << '.'; std::string prefix = sstr.str(); - AstNode *decl = current_scope[str]; + auto* decl = current_scope[str]; if (unevaluated_tern_branch && decl->is_recursive_function()) goto replace_fcall_later; - decl = decl->clone(); + auto decl_clone = decl->clone(); + decl = decl_clone.get(); // sketchy? decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion decl->expand_genblock(prefix); @@ -3893,20 +3957,19 @@ skip_dynamic_range_lvalue_expansion:; { bool require_const_eval = decl->has_const_only_constructs(); bool all_args_const = true; - for (auto child : children) { + 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(); + auto func_workspace = decl->clone(); func_workspace->set_in_param_flag(true); func_workspace->str = prefix_id(prefix, "$result"); + // func_workspace->dumpAst(stdout, "func_workspace "); newNode = func_workspace->eval_const_function(this, in_param || require_const_eval); - delete func_workspace; if (newNode) { - delete decl; goto apply_newNode; } } @@ -3914,41 +3977,42 @@ skip_dynamic_range_lvalue_expansion:; 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()); + input_error("Function %s can only be called with constant arguments.\n", str); } size_t arg_count = 0; dict wire_cache; - vector new_stmts; - vector output_assignments; + vector> new_stmts; + vector> output_assignments; - if (current_block == NULL) + if (current_block == nullptr) { log_assert(type == AST_FCALL); - AstNode *wire = NULL; + std::unique_ptr wire = nullptr; std::string res_name = prefix_id(prefix, "$result"); - for (auto child : decl->children) + for (auto& child : decl->children) if (child->type == AST_WIRE && child->str == res_name) wire = child->clone(); - log_assert(wire != NULL); + log_assert(wire != nullptr); 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)) { } + auto* wire_leaky = wire.get(); + current_scope[wire->str] = wire_leaky; + current_ast_mod->children.push_back(std::move(wire)); + while (wire_leaky->simplify(true, 1, -1, false)) { } - AstNode *lvalue = new AstNode(AST_IDENTIFIER); - lvalue->str = wire->str; + auto lvalue = std::make_unique(location, AST_IDENTIFIER); + lvalue->str = wire_leaky->str; - AstNode *always = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK, - new AstNode(AST_ASSIGN_EQ, lvalue, clone()))); + auto always = std::make_unique(location, AST_ALWAYS, std::make_unique(location, AST_BLOCK, + std::make_unique(location, AST_ASSIGN_EQ, std::move(lvalue), clone()))); always->children[0]->children[0]->was_checked = true; - current_ast_mod->children.push_back(always); + current_ast_mod->children.push_back(std::move(always)); goto replace_fcall_with_id; } @@ -3965,57 +4029,57 @@ skip_dynamic_range_lvalue_expansion:; } else celltype = RTLIL::escape_id(celltype); - AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE)); + auto cell = std::make_unique(location, AST_CELL, std::make_unique(location, AST_CELLTYPE)); cell->str = prefix.substr(0, GetSize(prefix)-1); cell->children[0]->str = celltype; - for (auto attr : decl->attributes) + 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()); + auto cell_arg = std::make_unique(location, AST_PARASET, attr.second->clone()); cell_arg->str = RTLIL::escape_id(attr.first.substr(strlen("\\via_celltype_defparam_"))); - cell->children.push_back(cell_arg); + cell->children.push_back(std::move(cell_arg)); } - for (auto child : decl->children) + 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(); + auto wire = child->clone(); wire->port_id = 0; wire->is_input = false; wire->is_output = false; - current_ast_mod->children.push_back(wire); + current_ast_mod->children.push_back(std::move(wire)); while (wire->simplify(true, 1, -1, false)) { } - AstNode *wire_id = new AstNode(AST_IDENTIFIER); + auto wire_id = std::make_unique(location, 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()); + auto arg = children[arg_count++]->clone(); + auto assign = child->is_input ? + std::make_unique(location, AST_ASSIGN_EQ, wire_id->clone(), std::move(arg)) : + std::make_unique(location, AST_ASSIGN_EQ, std::move(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) + if (it->get() != current_block_child) continue; - current_block->children.insert(it, assign); + current_block->children.insert(it, std::move(assign)); break; } } - AstNode *cell_arg = new AstNode(AST_ARGUMENT, wire_id); + auto cell_arg = std::make_unique(location, AST_ARGUMENT, std::move(wire_id)); cell_arg->str = child->str == str ? outport : child->str; - cell->children.push_back(cell_arg); + cell->children.push_back(std::move(cell_arg)); } - current_ast_mod->children.push_back(cell); + current_ast_mod->children.push_back(std::move(cell)); goto replace_fcall_with_id; } - for (auto child : decl->children) + 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; @@ -4025,7 +4089,7 @@ skip_dynamic_range_lvalue_expansion:; wire = wire_cache.at(child->str); bool contains_value = wire->type == AST_LOCALPARAM; if (wire->children.size() == contains_value) { - for (auto c : child->children) + for (auto& c : child->children) wire->children.push_back(c->clone()); } else if (!child->children.empty()) { while (child->simplify(true, stage, -1, false)) { } @@ -4035,44 +4099,42 @@ skip_dynamic_range_lvalue_expansion:; goto tcall_incompatible_wires; } else { tcall_incompatible_wires: - input_error("Incompatible re-declaration of wire %s.\n", child->str.c_str()); + input_error("Incompatible re-declaration of wire %s.\n", child->str); } } } else { - wire = child->clone(); + current_ast_mod->children.push_back(child->clone()); + wire = current_ast_mod->children.back().get(); 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)); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); if (child->type == AST_ENUM_ITEM) - wire->set_attribute(ID::enum_base_type, child->attributes[ID::enum_base_type]); + wire->set_attribute(ID::enum_base_type, std::move(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(); + auto 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)) { + if (child->is_input && child->type == AST_WIRE && arg->type == AST_CONSTANT && node_contains_assignment_to(decl, child.get())) { 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) + for (auto& 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) { @@ -4080,48 +4142,53 @@ skip_dynamic_range_lvalue_expansion:; break; } if (!uses_explicit_size) { - AstNode* range = new AstNode(); + auto range = std::make_unique(location); 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)); + range->children.push_back(mkconst_int(location, 0, true)); + range->children.push_back(mkconst_int(location, 0, true)); + wire->children.push_back(std::move(range)); } } wire->fixup_hierarchy_flags(); // updates the sizing while (wire->simplify(true, 1, -1, false)) { } - delete arg; continue; } - AstNode *wire_id = new AstNode(AST_IDENTIFIER); + + auto wire_id = std::make_unique(location, 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); + if (child->is_input) { + auto assign = std::make_unique(location, AST_ASSIGN_EQ, wire_id->clone(), arg->clone()); + assign->children[0]->was_checked = true; + new_stmts.push_back(std::move(assign)); + } + + if (child->is_output) { + auto assign = std::make_unique(location, AST_ASSIGN_EQ, arg->clone(), wire_id->clone()); + assign->children[0]->was_checked = true; + output_assignments.push_back(std::move(assign)); + } } } - for (auto child : decl->children) + 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()); + new_stmts.reserve(new_stmts.size() + output_assignments.size()); + std::move(output_assignments.begin(), output_assignments.end(), std::back_inserter(new_stmts)); 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()); + if (it->get() == current_block_child) { + current_block->children.insert(it, + std::make_move_iterator(new_stmts.begin()), + std::make_move_iterator(new_stmts.end())); break; } } replace_fcall_with_id: - delete decl; if (type == AST_FCALL) { delete_children(); type = AST_IDENTIFIER; @@ -4168,7 +4235,7 @@ replace_fcall_later:; else data.push_back(RTLIL::State::Sx); } - newNode = mkconst_bits(data, false); + newNode = mkconst_bits(location, data, false); } else if (children.size() == 0) newNode = current_scope[str]->children[0]->clone(); @@ -4180,14 +4247,14 @@ replace_fcall_later:; 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); + newNode = mkconst_bits(location, 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); + newNode = mkconst_bits(location, y.to_bits(), type == AST_TO_SIGNED); } break; if (0) { case AST_BIT_AND: const_func = RTLIL::const_and; } @@ -4197,7 +4264,7 @@ replace_fcall_later:; 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); + newNode = mkconst_bits(location, y.to_bits(), sign_hint); } break; if (0) { case AST_REDUCE_AND: const_func = RTLIL::const_reduce_and; } @@ -4207,16 +4274,16 @@ replace_fcall_later:; 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); + newNode = mkconst_bits(location, 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); + newNode = mkconst_bits(location, y.to_bits(), false); } else if (children[0]->isConst()) { - newNode = mkconst_int(children[0]->asReal(sign_hint) == 0, false, 1); + newNode = mkconst_int(location, children[0]->asReal(sign_hint) == 0, false, 1); } break; if (0) { case AST_LOGIC_AND: const_func = RTLIL::const_logic_and; } @@ -4224,27 +4291,27 @@ replace_fcall_later:; 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); + newNode = mkconst_bits(location, 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); + newNode = mkconst_int(location, (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); + newNode = mkconst_int(location, (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 (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); + newNode = mkconst_bits(location, y.to_bits(), sign_hint); } else if (type == AST_POW && children[0]->isConst() && children[1]->isConst()) { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(location, AST_REALVALUE); newNode->realvalue = pow(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint)); } break; @@ -4261,19 +4328,19 @@ replace_fcall_later:; 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); + newNode = mkconst_bits(location, 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; + case AST_LT: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) < children[1]->asReal(cmp_signed), false, 1); break; + case AST_LE: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) <= children[1]->asReal(cmp_signed), false, 1); break; + case AST_EQ: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; + case AST_NE: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; + case AST_EQX: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; + case AST_NEX: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; + case AST_GE: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) >= children[1]->asReal(cmp_signed), false, 1); break; + case AST_GT: newNode = mkconst_int(location, children[0]->asReal(cmp_signed) > children[1]->asReal(cmp_signed), false, 1); break; default: log_abort(); } } @@ -4286,10 +4353,10 @@ replace_fcall_later:; 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); + newNode = mkconst_bits(location, y.to_bits(), sign_hint); } else if (children[0]->isConst() && children[1]->isConst()) { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(location, 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; @@ -4305,10 +4372,10 @@ replace_fcall_later:; 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); + newNode = mkconst_bits(location, y.to_bits(), sign_hint); } else if (children[0]->isConst()) { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(location, AST_REALVALUE); if (type == AST_NEG) newNode->realvalue = -children[0]->asReal(sign_hint); else @@ -4322,21 +4389,21 @@ replace_fcall_later:; AstNode *choice = pair.first; AstNode *not_choice = pair.second; - if (choice != NULL) { + if (choice != nullptr) { 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); + newNode = std::make_unique(location, 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()); + newNode = mkconst_str(location, y.to_bits()); else - newNode = mkconst_bits(y.to_bits(), sign_hint); + newNode = mkconst_bits(location, y.to_bits(), sign_hint); } } else if (choice->isConst()) { @@ -4348,10 +4415,10 @@ replace_fcall_later:; 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); + a.set(i, RTLIL::State::Sx); + newNode = mkconst_bits(location, a.to_bits(), sign_hint); } else if (children[1]->isConst() && children[2]->isConst()) { - newNode = new AstNode(AST_REALVALUE); + newNode = std::make_unique(location, AST_REALVALUE); if (children[1]->asReal(sign_hint) == children[2]->asReal(sign_hint)) newNode->realvalue = children[1]->asReal(sign_hint); else @@ -4370,7 +4437,7 @@ replace_fcall_later:; val = children[1]->bitsAsUnsizedConst(width); else val = children[1]->bitsAsConst(width); - newNode = mkconst_bits(val.to_bits(), children[1]->is_signed); + newNode = mkconst_bits(location, val.to_bits(), children[1]->is_signed); } break; case AST_CONCAT: @@ -4382,14 +4449,14 @@ replace_fcall_later:; 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); + newNode = string_op ? mkconst_str(location, tmp_bits) : mkconst_bits(location, 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); + newNode = children.at(1)->is_string ? mkconst_str(location, tmp_bits) : mkconst_bits(location, tmp_bits, false); break; default: not_const: @@ -4403,12 +4470,11 @@ apply_newNode: // fprintf(stderr, "----\n"); // dumpAst(stderr, "- "); // newNode->dumpAst(stderr, "+ "); - log_assert(newNode != NULL); - newNode->filename = filename; + log_assert(newNode != nullptr); + newNode->location.begin.filename = location.begin.filename; newNode->location = location; - newNode->cloneInto(this); + newNode->cloneInto(*this); fixup_hierarchy_flags(); - delete newNode; did_something = true; } @@ -4421,21 +4487,21 @@ apply_newNode: void AstNode::replace_result_wire_name_in_function(const std::string &from, const std::string &to) { - for (AstNode *child : children) + for (auto& 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) +std::unique_ptr 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); + auto block = std::make_unique(location, AST_BLOCK); - AstNode *meminit = nullptr; + AstNode* meminit = nullptr; int next_meminit_cursor=0; vector meminit_bits; vector en_bits; @@ -4447,19 +4513,14 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m 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); + std::string path = parent_from_file_path(*location.begin.filename); 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()); + input_error("Can not open file `%s` for %s.\n", mem_filename, 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; @@ -4505,43 +4566,50 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m char *endptr; cursor = strtol(nptr, &endptr, 16); if (!*nptr || *endptr) - input_error("Can not parse address `%s` for %s.\n", nptr, str.c_str()); + input_error("Can not parse address `%s` for %s.\n", nptr, str); continue; } - AstNode *value = VERILOG_FRONTEND::const2ast(stringf("%d'%c", mem_width, is_readmemh ? 'h' : 'b') + token); + VERILOG_FRONTEND::ConstParser p{memory->location}; + auto value = p.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->children[1] = AstNode::mkconst_bits(location, meminit_bits, false); + meminit->children[3] = AstNode::mkconst_int(location, meminit_size, false); } - meminit = new AstNode(AST_MEMINIT); - meminit->children.push_back(AstNode::mkconst_int(cursor, false)); + auto meminit_owned = std::make_unique(location, AST_MEMINIT); + meminit = meminit_owned.get(); + meminit->children.push_back(AstNode::mkconst_int(location, cursor, false)); meminit->children.push_back(nullptr); - meminit->children.push_back(AstNode::mkconst_bits(en_bits, false)); + meminit->children.push_back(AstNode::mkconst_bits(location, 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); + current_ast_mod->children.push_back(std::move(meminit_owned)); 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.push_back( + std::make_unique(location, + AST_ASSIGN_EQ, std::make_unique(location, + AST_IDENTIFIER, std::make_unique(location, + AST_RANGE, AstNode::mkconst_int(location, + cursor, false))), + std::move(value))); block->children.back()->children[0]->str = memory->str; block->children.back()->children[0]->id2ast = memory; block->children.back()->children[0]->was_checked = true; @@ -4557,8 +4625,8 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m } if (meminit != nullptr) { - meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); - meminit->children[3] = AstNode::mkconst_int(meminit_size, false); + meminit->children[1] = AstNode::mkconst_bits(location, meminit_bits, false); + meminit->children[3] = AstNode::mkconst_int(location, meminit_size, false); } return block; @@ -4618,7 +4686,7 @@ void AstNode::expand_genblock(const std::string &prefix) }; for (size_t i = 0; i < children.size(); i++) { - AstNode *child = children[i]; + auto* child = children[i].get(); switch (child->type) { case AST_WIRE: @@ -4644,9 +4712,9 @@ void AstNode::expand_genblock(const std::string &prefix) case AST_ENUM: current_scope[child->str] = child; - for (auto enode : child->children){ + for (auto& enode : child->children){ log_assert(enode->type == AST_ENUM_ITEM); - prefix_node(enode); + prefix_node(enode.get()); } break; @@ -4656,7 +4724,7 @@ void AstNode::expand_genblock(const std::string &prefix) } for (size_t i = 0; i < children.size(); i++) { - AstNode *child = children[i]; + auto& 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 @@ -4688,7 +4756,7 @@ void AstNode::label_genblks(std::set& existing, int &counter) // seeing a proper generate control flow construct increments the // counter once ++counter; - for (AstNode *child : children) + for (auto& child : children) child->label_genblks(existing, counter); break; @@ -4705,7 +4773,7 @@ void AstNode::label_genblks(std::set& existing, int &counter) // within a genblk, the counter starts fresh std::set existing_local = existing; int counter_local = 0; - for (AstNode *child : children) + for (auto& child : children) child->label_genblks(existing_local, counter_local); break; } @@ -4714,7 +4782,7 @@ void AstNode::label_genblks(std::set& existing, int &counter) // track names which could conflict with implicit genblk names if (str.rfind("\\genblk", 0) == 0) existing.insert(str); - for (AstNode *child : children) + for (auto& child : children) child->label_genblks(existing, counter); break; } @@ -4725,12 +4793,12 @@ static void mark_memories_assign_lhs_complex(dict> & dict &mem2reg_candidates, AstNode *that) { for (auto &child : that->children) - mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child); + mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child.get()); 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_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*that->location.begin.filename), that->location.begin.line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS; } } @@ -4749,7 +4817,7 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg { // 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); + mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, lhs_child.get()); if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY) { @@ -4758,14 +4826,14 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg // activate mem2reg if this is assigned in an async proc if (flags & AstNode::MEM2REG_FL_ASYNC) { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ASYNC)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*location.begin.filename), location.begin.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)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*location.begin.filename), location.begin.line)); proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1; } @@ -4782,11 +4850,11 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg // remember where this is if (flags & MEM2REG_FL_INIT) { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*location.begin.filename), location.begin.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_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*location.begin.filename), location.begin.line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ELSE; } } @@ -4803,7 +4871,7 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg // 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_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(*location.begin.filename), location.begin.line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_EQ2; } } @@ -4815,11 +4883,11 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg if ((type == AST_MODULE || type == AST_INTERFACE) && get_bool_attribute(ID::mem2reg)) children_flags |= AstNode::MEM2REG_FL_ALL; - dict *proc_flags_p = NULL; + dict *proc_flags_p = nullptr; if (type == AST_ALWAYS) { int count_edge_events = 0; - for (auto child : children) + for (auto& child : children) if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) count_edge_events++; if (count_edge_events != 1) @@ -4835,12 +4903,12 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg flags |= children_flags; log_assert((flags & ~0x000000ff) == 0); - for (auto child : children) + 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) { + 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 @@ -4876,7 +4944,7 @@ bool AstNode::mem2reg_check(pool &mem2reg_set) return true; } -void AstNode::mem2reg_remove(pool &mem2reg_set, vector &delnodes) +void AstNode::mem2reg_remove(pool &mem2reg_set) { log_assert(mem2reg_set.count(this) == 0); @@ -4884,17 +4952,16 @@ void AstNode::mem2reg_remove(pool &mem2reg_set, vector &deln id2ast = nullptr; for (size_t i = 0; i < children.size(); i++) { - if (mem2reg_set.count(children[i]) > 0) { - delnodes.push_back(children[i]); + if (mem2reg_set.count(children[i].get()) > 0) { children.erase(children.begin() + (i--)); } else { - children[i]->mem2reg_remove(mem2reg_set, delnodes); + children[i]->mem2reg_remove(mem2reg_set); } } } // actually replace memories with registers -bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block) +bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *block, AstNode* async_block) { bool did_something = false; @@ -4921,9 +4988,12 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, if (length != 0) { - AstNode *block = new AstNode(AST_INITIAL, new AstNode(AST_BLOCK)); - mod->children.push_back(block); - block = block->children[0]; + auto block_owned = std::make_unique(location, + AST_INITIAL, std::make_unique(location, + AST_BLOCK)); + auto block = block_owned.get(); + mod->children.push_back(std::move(block_owned)); + block = block->children[0].get(); int wordsz = GetSize(data) / length; @@ -4937,11 +5007,13 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, while (epos < wordsz && en[epos] == State::S1) epos++; int clen = epos - pos; - AstNode *range = new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false)); + auto range = std::make_unique(location, + AST_RANGE, AstNode::mkconst_int(location, + cursor+i, false)); if (pos != 0 || epos != wordsz) { int left; int right; - AstNode *mrange = id2ast->children[0]; + auto& mrange = id2ast->children[0]; if (mrange->range_left < mrange->range_right) { right = mrange->range_right - pos; left = mrange->range_right - epos + 1; @@ -4949,42 +5021,48 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, 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))); + range = std::make_unique(location, + AST_MULTIRANGE, std::move(range), std::make_unique(location, + AST_RANGE, + AstNode::mkconst_int(location, left, true), + AstNode::mkconst_int(location, right, true))); } - AstNode *target = new AstNode(AST_IDENTIFIER, range); + auto target = std::make_unique(location, AST_IDENTIFIER, std::move(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))); + block->children.push_back(std::make_unique(location, + AST_ASSIGN_EQ, + std::move(target), + mkconst_bits(location, + data.extract(i*wordsz + pos, clen).to_bits(), + false))); pos = epos; } } } } - AstNode *newNode = new AstNode(AST_NONE); - newNode->cloneInto(this); - delete newNode; - + auto newNode = std::make_unique(location, AST_NONE); + newNode->cloneInto(*this); did_something = true; } - if (type == AST_ASSIGN && block == NULL && children[0]->mem2reg_check(mem2reg_set)) + if (type == AST_ASSIGN && block == nullptr && 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); + if (async_block == nullptr) { + auto async_block_owned = std::make_unique(location, AST_ALWAYS, std::make_unique(location, AST_BLOCK)); + async_block = async_block_owned.get(); + mod->children.push_back(std::move(async_block_owned)); } - AstNode *newNode = clone(); + auto 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; + async_block->children[0]->children.push_back(std::move(newNode)); + newNode = std::make_unique(location, AST_NONE); + newNode->cloneInto(*this); did_something = true; } @@ -4992,64 +5070,64 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, children[0]->children[0]->children[0]->type != AST_CONSTANT) { std::stringstream sstr; - sstr << "$mem2reg_wr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2reg_wr$" << children[0]->str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.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))); + auto wire_addr = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, addr_bits-1, true), mkconst_int(location, 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); + wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire_addr->simplify(true, 1, -1, false)) { } + mod->children.push_back(std::move(wire_addr)); - AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + auto wire_data = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, mem_width-1, true), mkconst_int(location, 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); + wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire_data->simplify(true, 1, -1, false)) { } + mod->children.push_back(std::move(wire_data)); - log_assert(block != NULL); + log_assert(block != nullptr); size_t assign_idx = 0; - while (assign_idx < block->children.size() && block->children[assign_idx] != this) + while (assign_idx < block->children.size() && block->children[assign_idx].get() != 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()); + auto assign_addr = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, 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); + block->children.insert(block->children.begin()+assign_idx+1, std::move(assign_addr)); - AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); + auto case_node = std::make_unique(location, AST_CASE, std::make_unique(location, 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)); + auto cond_node = std::make_unique(location, AST_COND, AstNode::mkconst_int(location, i, false, addr_bits), std::make_unique(location, AST_BLOCK)); + auto assign_reg = std::make_unique(location, type, std::make_unique(location, AST_IDENTIFIER), std::make_unique(location, 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[0]->str = stringf("%s[%d]", children[0]->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); + cond_node->children[1]->children.push_back(std::move(assign_reg)); + case_node->children.push_back(std::move(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); + block->children.insert(block->children.begin()+assign_idx+2, std::move(case_node)); children[0]->delete_children(); children[0]->range_valid = false; - children[0]->id2ast = NULL; + children[0]->id2ast = nullptr; children[0]->str = id_data; type = AST_ASSIGN_EQ; children[0]->was_checked = true; @@ -5060,7 +5138,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, if (mem2reg_check(mem2reg_set)) { - AstNode *bit_part_sel = NULL; + std::unique_ptr bit_part_sel = nullptr; if (children.size() == 2) bit_part_sel = children[1]->clone(); @@ -5074,10 +5152,10 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, (right <= id && id <= left); if (valid_const_access) { - str = stringf("%s[%d]", str.c_str(), id); + str = stringf("%s[%d]", str, id); delete_children(); range_valid = false; - id2ast = NULL; + id2ast = nullptr; } else { @@ -5090,7 +5168,6 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, else width = bit_part_sel->children[0]->integer - bit_part_sel->children[1]->integer; - delete bit_part_sel; bit_part_sel = nullptr; } else @@ -5105,69 +5182,68 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, 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; + auto constant = AstNode::mkconst_bits(location, x_bits, false); + constant->cloneInto(*this); } } else { std::stringstream sstr; - sstr << "$mem2reg_rd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2reg_rd$" << str << "$" << RTLIL::encode_filename(*location.begin.filename) << ":" << location.begin.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))); + auto wire_addr = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, addr_bits-1, true), mkconst_int(location, 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); + wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire_addr->simplify(true, 1, -1, false)) { } + mod->children.push_back(std::move(wire_addr)); - AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + auto wire_data = std::make_unique(location, AST_WIRE, std::make_unique(location, AST_RANGE, mkconst_int(location, mem_width-1, true), mkconst_int(location, 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); + wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(location, 1, false)); while (wire_data->simplify(true, 1, -1, false)) { } + mod->children.push_back(std::move(wire_data)); - AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone()); + auto assign_addr = std::make_unique(location, block ? AST_ASSIGN_EQ : AST_ASSIGN, std::make_unique(location, 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)); + auto case_node = std::make_unique(location, AST_CASE, std::make_unique(location, 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)); + auto cond_node = std::make_unique(location, AST_COND, AstNode::mkconst_int(location, i, false, addr_bits), std::make_unique(location, AST_BLOCK)); + auto assign_reg = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), std::make_unique(location, 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); + assign_reg->children[1]->str = stringf("%s[%d]", str, i); + cond_node->children[1]->children.push_back(std::move(assign_reg)); + case_node->children.push_back(std::move(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)); + auto cond_node = std::make_unique(location, AST_COND, std::make_unique(location, AST_DEFAULT), std::make_unique(location, AST_BLOCK)); + auto assign_reg = std::make_unique(location, AST_ASSIGN_EQ, std::make_unique(location, AST_IDENTIFIER), AstNode::mkconst_bits(location, 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); + cond_node->children[1]->children.push_back(std::move(assign_reg)); + case_node->children.push_back(std::move(cond_node)); // fixup on the full hierarchy below case_node case_node->fixup_hierarchy_flags(true); @@ -5178,34 +5254,37 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, 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); + block->children.insert(block->children.begin()+assign_idx, std::move(case_node)); + block->children.insert(block->children.begin()+assign_idx, std::move(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); + auto proc = std::make_unique(location, AST_ALWAYS, std::make_unique(location, AST_BLOCK, std::move(case_node))); + mod->children.push_back(std::move(proc)); + mod->children.push_back(std::move(assign_addr)); mod->fixup_hierarchy_flags(); } delete_children(); range_valid = false; - id2ast = NULL; + id2ast = nullptr; str = id_data; } if (bit_part_sel) { - children.push_back(bit_part_sel); + children.push_back(std::move(bit_part_sel)); fixup_hierarchy_flags(); } did_something = true; } - log_assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0); + log_assert(id2ast == nullptr || mem2reg_set.count(id2ast) == 0); + + std::vector children_list; + for (auto& child : children) + children_list.push_back(child.get()); - 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; @@ -5290,7 +5369,7 @@ bool AstNode::has_const_only_constructs() { if (type == AST_WHILE || type == AST_REPEAT) return true; - for (auto child : children) + for (auto& child : children) if (child->has_const_only_constructs()) return true; return false; @@ -5300,7 +5379,7 @@ bool AstNode::is_simple_const_expr() { if (type == AST_IDENTIFIER) return false; - for (auto child : children) + for (auto& child : children) if (!child->is_simple_const_expr()) return false; return true; @@ -5333,11 +5412,13 @@ bool AstNode::replace_variables(std::map &varia 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; + const RTLIL::Const &val = variables.at(str).val; + std::vector new_bits; + new_bits.reserve(width); + for (int i = 0; i < width; i++) + new_bits.push_back(val[offset+i]); + auto newNode = mkconst_bits(location, new_bits, variables.at(str).is_signed); + newNode->cloneInto(*this); return true; } @@ -5348,32 +5429,31 @@ bool AstNode::replace_variables(std::map &varia } // attempt to statically evaluate a functions with all-const arguments -AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) +std::unique_ptr 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; + auto block = std::make_unique(location, AST_BLOCK); + std::unique_ptr result = nullptr; size_t argidx = 0; - for (auto child : children) + for (auto& child : children) { block->children.push_back(child->clone()); } block->set_in_param_flag(true); + std::vector> temporary_nodes; while (!block->children.empty()) { - AstNode *stmt = block->children.front(); + auto& 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> "); + log("%20s %40s\n", it.first, log_signal(it.second.val)); + stmt->dumpAst(nullptr, "stmt> "); #endif - if (stmt->type == AST_WIRE) { while (stmt->simplify(true, 1, -1, false)) { } @@ -5388,7 +5468,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) // 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()); + input_error("Incompatible re-declaration of constant function wire %s.\n", stmt->str); } variable.val = RTLIL::Const(RTLIL::State::Sx, width); variable.offset = stmt->range_swapped ? stmt->range_left : stmt->range_right; @@ -5398,7 +5478,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) 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++); + variable.arg = fcall->children.at(argidx++).get(); } // load the constant arg's value into this variable if (variable.arg) { @@ -5409,10 +5489,10 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) variable.val = variable.arg->realAsConst(width); } } - current_scope[stmt->str] = stmt; + current_scope[stmt->str] = stmt.get(); + temporary_nodes.push_back(std::move(stmt)); block->children.erase(block->children.begin()); - to_delete.push_back(stmt); continue; } @@ -5422,10 +5502,10 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) { while (stmt->simplify(true, 1, -1, false)) { } - current_scope[stmt->str] = stmt; + current_scope[stmt->str] = stmt.get(); + temporary_nodes.push_back(std::move(stmt)); block->children.erase(block->children.begin()); - to_delete.push_back(stmt); continue; } @@ -5466,11 +5546,11 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) 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); + AstNode *range = stmt->children.at(0)->children.at(0).get(); 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()); + range->input_error("Non-constant range\n%s: ... called from here.\n", fcall->loc_string()); } int offset = min(range->range_left, range->range_right); int width = std::abs(range->range_left - range->range_right) + 1; @@ -5480,28 +5560,32 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) int index = i + offset - v.offset; if (v.range_swapped) index = -index; - v.val.bits().at(index) = r.at(i); + v.val.set(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->type = AST_WHILE; + log_assert(stmt->children.size() > 2); + auto yoink0 = std::move(stmt->children.at(0)); + log_assert(stmt->children.size() > 2); + auto yoink2 = std::move(stmt->children.at(2)); + stmt->children.at(3)->children.push_back(std::move(yoink2)); stmt->children.erase(stmt->children.begin() + 2); stmt->children.erase(stmt->children.begin()); - stmt->type = AST_WHILE; + block->children.insert(block->children.begin(), std::move(yoink0)); + log_assert(stmt->children.size() == 2); continue; } if (stmt->type == AST_WHILE) { - AstNode *cond = stmt->children.at(0)->clone(); + auto cond = stmt->children.at(0)->clone(); if (!cond->replace_variables(variables, fcall, must_succeed)) goto finished; cond->set_in_param_flag(true); @@ -5517,17 +5601,14 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) 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(); + auto num = stmt->children.at(0)->clone(); if (!num->replace_variables(variables, fcall, must_succeed)) goto finished; num->set_in_param_flag(true); @@ -5540,41 +5621,41 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) fcall->loc_string().c_str()); } + temporary_nodes.push_back(std::move(stmt)); 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()); + block->children.insert(block->children.begin(), temporary_nodes.back()->children.at(1)->clone()); - delete stmt; - delete num; continue; } if (stmt->type == AST_CASE) { - AstNode *expr = stmt->children.at(0)->clone(); + auto 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; + AstNode *sel_case = nullptr; + std::unique_ptr sel_case_copy = nullptr; 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(); + sel_case = stmt->children.at(i)->children.back().get(); 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(); + auto 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 = std::make_unique(location, AST_EQ, expr->clone(), std::move(cond)); cond->set_in_param_flag(true); while (cond->simplify(true, 1, -1, false)) { } @@ -5586,20 +5667,19 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) } found_match = cond->asBool(); - delete cond; } if (found_match) { - sel_case = stmt->children.at(i)->children.back(); + sel_case = stmt->children.at(i)->children.back().get(); break; } } + if (sel_case) + sel_case_copy = sel_case->clone(); block->children.erase(block->children.begin()); - if (sel_case) - block->children.insert(block->children.begin(), sel_case->clone()); - delete stmt; - delete expr; + if (sel_case_copy) + block->children.insert(block->children.begin(), std::move(sel_case_copy)); continue; } @@ -5607,15 +5687,19 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) { if (!stmt->str.empty()) stmt->expand_genblock(stmt->str + "."); - + auto* stmt_leaky = stmt.get(); + temporary_nodes.push_back(std::move(stmt)); block->children.erase(block->children.begin()); - block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end()); - stmt->children.clear(); + block->children.reserve(block->children.size() + stmt_leaky->children.size()); + block->children.insert(block->children.begin(), + std::make_move_iterator(stmt_leaky->children.begin()), + std::make_move_iterator(stmt_leaky->children.end())); + stmt_leaky->children.clear(); block->fixup_hierarchy_flags(); - delete stmt; continue; } + // log("C\n"); if (!must_succeed) goto finished; stmt->input_error("Unsupported language construct in constant function\n%s: ... called from here.\n", @@ -5623,17 +5707,10 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) log_abort(); } - result = AstNode::mkconst_bits(variables.at(str).val.to_bits(), variables.at(str).is_signed); + result = AstNode::mkconst_bits(location, 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; } @@ -5644,15 +5721,14 @@ void AstNode::allocateDefaultEnumValues() if (children.front()->attributes.count(ID::enum_base_type)) return; // already elaborated int last_enum_int = -1; - for (auto node : children) { + for (auto& node : children) { log_assert(node->type==AST_ENUM_ITEM); - node->set_attribute(ID::enum_base_type, mkconst_str(str)); + node->set_attribute(ID::enum_base_type, mkconst_str(node->location, 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); + node->children[i] = AstNode::mkconst_int(node->location, ++last_enum_int, true); break; case AST_CONSTANT: // explicit constant (or folded expression) @@ -5680,8 +5756,8 @@ bool AstNode::is_recursive_function() const if (it != current_scope.end() && visit(it->second)) return true; } - for (const AstNode *child : node->children) { - if (visit(child)) + for (auto& child : node->children) { + if (visit(child.get())) return true; } return false; @@ -5711,9 +5787,9 @@ std::pair AstNode::get_tern_choice() AstNode *choice = nullptr, *not_choice = nullptr; if (found_sure_true) - choice = children[1], not_choice = children[2]; + choice = children[1].get(), not_choice = children[2].get(); else if (!found_maybe_true) - choice = children[2], not_choice = children[1]; + choice = children[2].get(), not_choice = children[1].get(); return {choice, not_choice}; } diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc index f6b894563..bff347ea2 100644 --- a/frontends/blif/blifparse.cc +++ b/frontends/blif/blifparse.cc @@ -149,7 +149,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool if (buffer[0] == '.') { if (lutptr) { - for (auto &bit : lutptr->bits()) + for (auto bit : *lutptr) if (bit == RTLIL::State::Sx) bit = lut_default_state; lutptr = NULL; @@ -245,7 +245,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool if (undef_wire != nullptr) module->rename(undef_wire, stringf("$undef$%d", ++blif_maxnum)); - autoidx = std::max(autoidx, blif_maxnum+1); + autoidx.ensure_at_least(blif_maxnum+1); blif_maxnum = 0; } @@ -321,9 +321,10 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool const_v = Const(str); } else { int n = strlen(v); - const_v.bits().resize(n); + Const::Builder const_v_builder(n); for (int i = 0; i < n; i++) - const_v.bits()[i] = v[n-i-1] != '0' ? State::S1 : State::S0; + const_v_builder.push_back(v[n-i-1] != '0' ? State::S1 : State::S0); + const_v = const_v_builder.build(); } if (!strcmp(cmd, ".attr")) { if (obj_attributes == nullptr) { @@ -362,17 +363,17 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool goto no_latch_clock; if (!strcmp(edge, "re")) - cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q)); + cell = module->addDffGate(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); + cell = module->addDffGate(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)); + cell = module->addDlatchGate(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); + cell = module->addDlatchGate(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)); + cell = module->addFfGate(NEW_ID, blif_wire(d), blif_wire(q)); } else { cell = module->addCell(NEW_ID, dff_name); cell->setPort(ID::D, blif_wire(d)); @@ -563,21 +564,23 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool log_assert(sopcell->parameters[ID::WIDTH].as_int() == input_len); sopcell->parameters[ID::DEPTH] = sopcell->parameters[ID::DEPTH].as_int() + 1; + Const::Builder table_bits_builder(input_len * 2); 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); + table_bits_builder.push_back(State::S1); + table_bits_builder.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); + table_bits_builder.push_back(State::S0); + table_bits_builder.push_back(State::S1); break; default: - sopcell->parameters[ID::TABLE].bits().push_back(State::S0); - sopcell->parameters[ID::TABLE].bits().push_back(State::S0); + table_bits_builder.push_back(State::S0); + table_bits_builder.push_back(State::S0); break; } + sopcell->parameters[ID::TABLE].append(table_bits_builder.build()); if (sopmode == -1) { sopmode = (*output == '1'); @@ -605,7 +608,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool goto try_next_value; } } - lutptr->bits().at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1; + lutptr->set(i, !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1); try_next_value:; } @@ -618,7 +621,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool 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()); + log_error("Syntax error in line %d: %s\n", line_count, err_reason); } struct BlifFrontend : public Frontend { diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc index 1aab81015..743ac5d9e 100644 --- a/frontends/json/jsonparse.cc +++ b/frontends/json/jsonparse.cc @@ -258,7 +258,7 @@ Const json_parse_attr_param_value(JsonNode *node) } } else if (node->type == 'N') { - value = Const(node->data_number, 32); + value = Const(node->data_number); if (node->data_number < 0) value.flags |= RTLIL::CONST_FLAG_SIGNED; } else @@ -289,7 +289,7 @@ void json_parse_attr_param(dict &results, JsonNode *node) void json_import(Design *design, string &modname, JsonNode *node) { - log("Importing module %s from JSON tree.\n", modname.c_str()); + log("Importing module %s from JSON tree.\n", modname); Module *module = new RTLIL::Module; module->name = RTLIL::escape_id(modname.c_str()); @@ -367,7 +367,7 @@ void json_import(Design *design, string &modname, JsonNode *node) 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()); + log_error("JSON port node '%s' has invalid '%s' direction attribute.\n", log_id(port_name), port_direction_node->data_string); port_wire->port_id = port_id; diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index 3228f02fb..80553347c 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -47,7 +47,7 @@ static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *& 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()); + log_error("Can't resolve wire name %s.\n", RTLIL::unescape_id(id)); expr += id_len; return module->wires_.at(id); @@ -537,7 +537,7 @@ struct LibertyFrontend : public Frontend { 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()); + log_header(design, "Executing Liberty frontend: %s\n", filename); LibertyParser parser(*f, filename); int cell_count = 0; @@ -550,7 +550,7 @@ struct LibertyFrontend : public Frontend { if (cell->id != "cell" || cell->args.size() != 1) continue; - // log("Processing cell type %s.\n", RTLIL::unescape_id(cell_name).c_str()); + // log("Processing cell type %s.\n", RTLIL::unescape_id(cell_name)); std::map> type_map = global_type_map; parse_type_map(type_map, cell); @@ -582,9 +582,9 @@ struct LibertyFrontend : public Frontend { { 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)); + log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), 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()); + log("Ignoring cell %s with missing or invalid direction for pin %s.\n", log_id(module->name), node->args.at(0)); delete module; goto skip_cell; } @@ -596,7 +596,7 @@ struct LibertyFrontend : public Frontend { 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()); + log("Ignoring cell %s with a bus interface %s.\n", log_id(module->name), node->args.at(0)); delete module; goto skip_cell; } @@ -613,7 +613,7 @@ struct LibertyFrontend : public Frontend { } 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)); + log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0), log_id(module->name)); simple_comb_cell = false; diff --git a/frontends/rpc/rpc_frontend.cc b/frontends/rpc/rpc_frontend.cc index ec3952661..c21867b30 100644 --- a/frontends/rpc/rpc_frontend.cc +++ b/frontends/rpc/rpc_frontend.cc @@ -83,17 +83,17 @@ struct RpcServer { std::string request; json_request.dump(request); request += '\n'; - log_debug("RPC frontend request: %s", request.c_str()); + log_debug("RPC frontend request: %s", request); write(request); std::string response = read(); - log_debug("RPC frontend response: %s", response.c_str()); + log_debug("RPC frontend response: %s", response); 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()); + log_cmd_error("parsing JSON failed: %s\n", error); if (json_response["error"].is_string()) - log_cmd_error("RPC frontend returned an error: %s\n", json_response["error"].string_value().c_str()); + log_cmd_error("RPC frontend returned an error: %s\n", json_response["error"].string_value()); return json_response; } @@ -111,7 +111,7 @@ struct RpcServer { } } else is_valid = false; if (!is_valid) - log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str()); + log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump()); return modules; } @@ -149,7 +149,7 @@ struct RpcServer { 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()); + log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump()); return std::make_pair(frontend, source); } }; @@ -163,12 +163,12 @@ struct RpcModule : RTLIL::Module { 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()); + log_header(design, "Executing RPC frontend `%s' for module `%s'.\n", server->name, stripped_name); 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))); + log("Parameter %s = %s\n", param.first, log_signal(RTLIL::SigSpec(param.second))); + parameter_info += stringf("%s=%s", param.first, log_signal(RTLIL::SigSpec(param.second))); } std::string derived_name; @@ -180,7 +180,7 @@ struct RpcModule : RTLIL::Module { 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()); + log("Found cached RTLIL representation for module `%s'.\n", derived_name); } else { std::string command, input; std::tie(command, input) = server->derive_module(stripped_name.substr(1), parameters); @@ -202,7 +202,7 @@ struct RpcModule : RTLIL::Module { } } if (!found_derived_top) - log_cmd_error("RPC frontend did not return requested module `%s`!\n", stripped_name.c_str()); + log_cmd_error("RPC frontend did not return requested module `%s`!\n", stripped_name); for (auto module : derived_design->modules()) for (auto cell : module->cells()) @@ -256,7 +256,7 @@ struct HandleRpcServer : RpcServer { 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()); + log_cmd_error("WriteFile failed: %s\n", get_last_error_str()); offset += data_written; } while(offset < (ssize_t)data.length()); } @@ -268,7 +268,7 @@ struct HandleRpcServer : RpcServer { 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()); + log_cmd_error("ReadFile failed: %s\n", get_last_error_str()); offset += data_read; data.resize(offset); size_t term_pos = data.find('\n', offset); @@ -437,7 +437,7 @@ struct RpcFrontend : public Pass { 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()); + log_error("SearchPathW failed: %s\n", get_last_error_str()); goto cleanup_exec; } command_path_w.resize(command_path_len_w - 1); @@ -448,19 +448,19 @@ struct RpcFrontend : public Pass { 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()); + log_error("CreatePipe failed: %s\n", get_last_error_str()); goto cleanup_exec; } if (!SetHandleInformation(send_w, HANDLE_FLAG_INHERIT, 0)) { - log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str()); + log_error("SetHandleInformation failed: %s\n", get_last_error_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()); + log_error("CreatePipe failed: %s\n", get_last_error_str()); goto cleanup_exec; } if (!SetHandleInformation(recv_r, HANDLE_FLAG_INHERIT, 0)) { - log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str()); + log_error("SetHandleInformation failed: %s\n", get_last_error_str()); goto cleanup_exec; } @@ -470,7 +470,7 @@ struct RpcFrontend : public Pass { 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()); + log_error("CreateProcessW failed: %s\n", get_last_error_str()); goto cleanup_exec; } CloseHandle(proc_info.hProcess); @@ -550,7 +550,7 @@ cleanup_exec: 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()); + log_error("CreateFileW failed: %s\n", get_last_error_str()); goto cleanup_path; } @@ -586,7 +586,7 @@ cleanup_path: 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()); + log("Linking module `%s'.\n", module_name); RpcModule *module = new RpcModule; module->name = "$abstract\\" + module_name; module->server = server; diff --git a/frontends/rtlil/Makefile.inc b/frontends/rtlil/Makefile.inc index d0c0cfcf8..4649507f7 100644 --- a/frontends/rtlil/Makefile.inc +++ b/frontends/rtlil/Makefile.inc @@ -1,19 +1 @@ - -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 - diff --git a/frontends/rtlil/rtlil_frontend.cc b/frontends/rtlil/rtlil_frontend.cc index 2c1910d13..271962725 100644 --- a/frontends/rtlil/rtlil_frontend.cc +++ b/frontends/rtlil/rtlil_frontend.cc @@ -17,27 +17,771 @@ * * --- * - * A very simple and straightforward frontend for the RTLIL text - * representation. + * A handwritten recursive-descent parser 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); -} +#include "kernel/utils.h" +#include +#include +#include YOSYS_NAMESPACE_BEGIN +struct RTLILFrontendWorker { + // Forbid constants of more than 1 Gb. + // This will help us not explode on malicious RTLIL. + static constexpr int MAX_CONST_WIDTH = 1024 * 1024 * 1024; + + std::istream *f = nullptr; + RTLIL::Design *design; + bool flag_nooverwrite = false; + bool flag_overwrite = false; + bool flag_lib = false; + + int line_num; + std::string line_buf; + // Substring of line_buf. Always newline-terminated, thus never empty. + std::string_view line; + + RTLIL::Module *current_module; + dict attrbuf; + std::vector*> switch_stack; + std::vector case_stack; + + template + [[noreturn]] + void error(FmtString...> fmt, const Args &... args) + { + log_error("Parser error in line %d: %s\n", line_num, fmt.format(args...)); + } + + template + void warning(FmtString...> fmt, const Args &... args) + { + log_warning("In line %d: %s\n", line_num, fmt.format(args...)); + } + + // May return an empty line if the stream is not good(). + void advance_to_next_nonempty_line() + { + if (!f->good()) { + line = "\n"; + return; + } + while (true) { + std::getline(*f, line_buf); + line_num++; + if (line_buf.empty() || line_buf[line_buf.size() - 1] != '\n') + line_buf += '\n'; + line = line_buf; + consume_whitespace_and_comments(); + if (line[0] != '\n' || !f->good()) + break; + } + } + + void consume_whitespace_and_comments() + { + while (true) { + switch (line[0]) { + case ' ': + case '\t': + line = line.substr(1); + break; + case '#': + line = "\n"; + return; + default: + return; + } + } + } + + bool try_parse_keyword(std::string_view keyword) + { + int keyword_size = keyword.size(); + if (keyword != line.substr(0, keyword_size)) + return false; + // This index is safe because `line` is always newline-terminated + // and `keyword` never contains a newline. + char ch = line[keyword_size]; + if (ch >= 'a' && ch <= 'z') + return false; + line = line.substr(keyword_size); + consume_whitespace_and_comments(); + return true; + } + + std::string error_token() + { + std::string result; + for (char ch : line) { + if (ch == '\n' || ch == ' ' || ch == '\t') + break; + result += ch; + } + return result; + } + + void expect_keyword(std::string_view keyword) + { + if (!try_parse_keyword(keyword)) + error("Expected token `%s', got `%s'.", keyword, error_token()); + } + + bool try_parse_char(char ch) + { + if (line[0] != ch) + return false; + line = line.substr(1); + consume_whitespace_and_comments(); + return true; + } + + void expect_char(char ch) + { + if (!try_parse_char(ch)) + error("Expected `%c', got `%s'.", ch, error_token()); + } + + bool try_parse_eol() + { + if (line[0] != '\n') + return false; + advance_to_next_nonempty_line(); + return true; + } + + void expect_eol() + { + if (!try_parse_eol()) + error("Expected EOL, got `%s'.", error_token()); + } + + std::optional try_parse_id() + { + char ch = line[0]; + if (ch != '\\' && ch != '$') + return std::nullopt; + int idx = 1; + while (true) { + ch = line[idx]; + if (ch <= ' ' && (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) + break; + ++idx; + } + IdString result(line.substr(0, idx)); + line = line.substr(idx); + consume_whitespace_and_comments(); + return result; + } + + RTLIL::IdString parse_id() + { + std::optional id = try_parse_id(); + if (!id.has_value()) + error("Expected ID, got `%s'.", error_token()); + return std::move(*id); + } + + long long parse_integer() + { + long long result = parse_integer_alone(); + consume_whitespace_and_comments(); + return result; + } + + long long parse_integer_alone() + { + int idx = 0; + if (line[idx] == '-') + ++idx; + while (true) { + char ch = line[idx]; + if (ch < '0' || ch > '9') + break; + ++idx; + } + long long result; + if (std::from_chars(line.data(), line.data() + idx, result, 10).ec != std::errc{}) + error("Invalid integer `%s'.", error_token()); + line = line.substr(idx); + return result; + } + + std::string parse_string() + { + if (line[0] != '\"') + error("Expected string, got `%s'.", error_token()); + std::string str; + int idx = 1; + while (true) { + int start_idx = idx; + char ch; + while (true) { + ch = line[idx]; + if (ch == '"' || ch == '\n' || ch == '\\' || ch == 0) + break; + ++idx; + } + str.append(line.data() + start_idx, line.data() + idx); + ++idx; + if (ch == '"') + break; + if (ch == 0) + error("Null byte in string literal: `%s'.", line); + if (ch == '\n') + error("Unterminated string literal: `%s'.", line); + ch = line[idx++]; + if (ch == 'n') { + ch = '\n'; + } else if (ch == 't') { + ch = '\t'; + } else if (ch >= '0' && ch <= '7') { + int v = ch - '0'; + char next_ch = line[idx + 1]; + if (next_ch >= '0' && next_ch <= '7') { + ++idx; + v = v*8 + (next_ch - '0'); + next_ch = line[idx + 1]; + if (next_ch >= '0' && next_ch <= '7') { + ++idx; + v = v*8 + (next_ch - '0'); + } + } + ch = v; + } + str += ch; + } + line = line.substr(idx); + consume_whitespace_and_comments(); + return str; + } + + RTLIL::Const parse_const() + { + if (line[0] == '"') + return RTLIL::Const(parse_string()); + + bool negative_value = line[0] == '-'; + long long width = parse_integer_alone(); + // Can't test value<0 here because we need to stop parsing after '-0' + if (negative_value || line[0] != '\'') { + if (width < INT_MIN || width > INT_MAX) + error("Integer %lld out of range before `%s'.", width, error_token()); + consume_whitespace_and_comments(); + return RTLIL::Const(width); + } + + int idx = 1; + bool is_signed = line[1] == 's'; + if (is_signed) + ++idx; + + std::vector bits; + if (width > MAX_CONST_WIDTH) + error("Constant width %lld out of range before `%s`.", width, error_token()); + bits.reserve(width); + while (true) { + RTLIL::State bit; + switch (line[idx]) { + 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 'm': bit = RTLIL::Sm; break; + case '-': bit = RTLIL::Sa; break; + default: goto done; + } + bits.push_back(bit); + ++idx; + } + done: + std::reverse(bits.begin(), bits.end()); + + if (GetSize(bits) > width) + bits.resize(width); + else if (GetSize(bits) < width) { + RTLIL::State extbit = RTLIL::Sx; + if (!bits.empty()) { + extbit = bits.back(); + if (extbit == RTLIL::S1) + extbit = RTLIL::S0; + } + bits.resize(width, extbit); + } + + RTLIL::Const val(std::move(bits)); + if (is_signed) + val.flags |= RTLIL::CONST_FLAG_SIGNED; + line = line.substr(idx); + consume_whitespace_and_comments(); + return val; + } + + RTLIL::SigSpec parse_sigspec() + { + RTLIL::SigSpec sig; + + if (try_parse_char('{')) { + std::vector parts; + while (!try_parse_char('}')) + parts.push_back(parse_sigspec()); + for (auto it = parts.rbegin(); it != parts.rend(); ++it) + sig.append(std::move(*it)); + } else { + // We could add a special path for parsing IdStrings that must already exist, + // as here. + // We don't need to addref/release in this case. + std::optional id = try_parse_id(); + if (id.has_value()) { + RTLIL::Wire *wire = current_module->wire(*id); + if (wire == nullptr) + error("Wire `%s' not found.", *id); + sig = RTLIL::SigSpec(wire); + } else { + sig = RTLIL::SigSpec(parse_const()); + } + } + + while (try_parse_char('[')) { + int left = parse_integer(); + if (left >= sig.size() || left < 0) + error("bit index %d out of range", left); + if (try_parse_char(':')) { + int right = parse_integer(); + if (right < 0) + error("bit index %d out of range", right); + if (left < right) + error("invalid slice [%d:%d]", left, right); + sig = sig.extract(right, left-right+1); + } else { + sig = sig.extract(left); + } + expect_char(']'); + } + + return sig; + } + + void parse_module() + { + RTLIL::IdString module_name = parse_id(); + expect_eol(); + + bool delete_current_module = false; + if (design->has(module_name)) { + RTLIL::Module *existing_mod = design->module(module_name); + if (!flag_overwrite && (flag_lib || (attrbuf.count(ID::blackbox) && attrbuf.at(ID::blackbox).as_bool()))) { + log("Ignoring blackbox re-definition of module %s.\n", module_name); + delete_current_module = true; + } else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) { + error("RTLIL error: redefinition of module %s.", module_name); + } else if (flag_nooverwrite) { + log("Ignoring re-definition of module %s.\n", module_name); + delete_current_module = true; + } else { + log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", module_name); + design->remove(existing_mod); + } + } + + current_module = new RTLIL::Module; + current_module->name = std::move(module_name); + current_module->attributes = std::move(attrbuf); + if (!delete_current_module) + design->add(current_module); + + while (true) + { + if (try_parse_keyword("attribute")) { + parse_attribute(); + continue; + } + if (try_parse_keyword("parameter")) { + parse_parameter(); + continue; + } + if (try_parse_keyword("connect")) { + parse_connect(); + continue; + } + if (try_parse_keyword("wire")) { + parse_wire(); + continue; + } + if (try_parse_keyword("cell")) { + parse_cell(); + continue; + } + if (try_parse_keyword("memory")) { + parse_memory(); + continue; + } + if (try_parse_keyword("process")) { + parse_process(); + continue; + } + if (try_parse_keyword("end")) { + expect_eol(); + break; + } + error("Unexpected token in module body: %s", error_token()); + } + + if (attrbuf.size() != 0) + error("dangling attribute"); + current_module->fixup_ports(); + if (delete_current_module) + delete current_module; + else if (flag_lib) + current_module->makeblackbox(); + current_module = nullptr; + } + + void parse_attribute() + { + RTLIL::IdString id = parse_id(); + RTLIL::Const c = parse_const(); + attrbuf.insert({std::move(id), std::move(c)}); + expect_eol(); + } + + void parse_parameter() + { + RTLIL::IdString id = parse_id(); + current_module->avail_parameters(id); + if (try_parse_eol()) + return; + RTLIL::Const c = parse_const(); + current_module->parameter_default_values.insert({std::move(id), std::move(c)}); + expect_eol(); + } + + void parse_wire() + { + RTLIL::Wire *wire; + int width = 1; + int start_offset = 0; + int port_id = 0; + bool port_input = false; + bool port_output = false; + bool upto = false; + bool is_signed = false; + + while (true) + { + std::optional id = try_parse_id(); + if (id.has_value()) { + if (current_module->wire(*id) != nullptr) + error("RTLIL error: redefinition of wire %s.", *id); + wire = current_module->addWire(std::move(*id)); + break; + } + if (try_parse_keyword("width")) + width = parse_integer(); + else if (try_parse_keyword("upto")) + upto = true; + else if (try_parse_keyword("signed")) + is_signed = true; + else if (try_parse_keyword("offset")) + start_offset = parse_integer(); + else if (try_parse_keyword("input")) { + port_id = parse_integer(); + port_input = true; + } else if (try_parse_keyword("output")) { + port_id = parse_integer(); + port_output = true; + } else if (try_parse_keyword("inout")) { + port_id = parse_integer(); + port_input = true; + port_output = true; + } else if (try_parse_eol()) + error("Missing wire ID"); + else + error("Unexpected wire option: %s", error_token()); + } + + wire->attributes = std::move(attrbuf); + wire->width = width; + wire->upto = upto; + wire->start_offset = start_offset; + wire->is_signed = is_signed; + wire->port_id = port_id; + wire->port_input = port_input; + wire->port_output = port_output; + expect_eol(); + } + + void parse_memory() + { + RTLIL::Memory *memory = new RTLIL::Memory; + memory->attributes = std::move(attrbuf); + + int width = 1; + int start_offset = 0; + int size = 0; + while (true) + { + std::optional id = try_parse_id(); + if (id.has_value()) { + if (current_module->memories.count(*id) != 0) + error("RTLIL error: redefinition of memory %s.", *id); + memory->name = std::move(*id); + break; + } + if (try_parse_keyword("width")) + width = parse_integer(); + else if (try_parse_keyword("size")) + size = parse_integer(); + else if (try_parse_keyword("offset")) + start_offset = parse_integer(); + else if (try_parse_eol()) + error("Missing memory ID"); + else + error("Unexpected memory option: %s", error_token()); + } + memory->width = width; + memory->start_offset = start_offset; + memory->size = size; + current_module->memories.insert({memory->name, memory}); + expect_eol(); + } + + void parse_cell() + { + RTLIL::IdString cell_type = parse_id(); + RTLIL::IdString cell_name = parse_id(); + expect_eol(); + + if (current_module->cell(cell_name) != nullptr) + error("RTLIL error: redefinition of cell %s.", cell_name); + RTLIL::Cell *cell = current_module->addCell(cell_name, cell_type); + cell->attributes = std::move(attrbuf); + + while (true) + { + if (try_parse_keyword("parameter")) { + bool is_signed = false; + bool is_real = false; + bool is_unsized = false; + if (try_parse_keyword("signed")) { + is_signed = true; + } else if (try_parse_keyword("real")) { + is_real = true; + } else if (try_parse_keyword("unsized")) { + is_unsized = true; + } + RTLIL::IdString param_name = parse_id(); + RTLIL::Const val = parse_const(); + if (is_signed) + val.flags |= RTLIL::CONST_FLAG_SIGNED; + if (is_real) + val.flags |= RTLIL::CONST_FLAG_REAL; + if (is_unsized) + val.flags |= RTLIL::CONST_FLAG_UNSIZED; + cell->parameters.insert({std::move(param_name), std::move(val)}); + expect_eol(); + } else if (try_parse_keyword("connect")) { + RTLIL::IdString port_name = parse_id(); + if (cell->hasPort(port_name)) + error("RTLIL error: redefinition of cell port %s.", port_name); + cell->setPort(std::move(port_name), parse_sigspec()); + expect_eol(); + } else if (try_parse_keyword("end")) { + expect_eol(); + break; + } else { + error("Unexpected token in cell body: %s", error_token()); + } + } + } + + void parse_connect() + { + if (attrbuf.size() != 0) + error("dangling attribute"); + RTLIL::SigSpec s1 = parse_sigspec(); + RTLIL::SigSpec s2 = parse_sigspec(); + current_module->connect(std::move(s1), std::move(s2)); + expect_eol(); + } + + void parse_case_body(RTLIL::CaseRule *current_case) + { + while (true) + { + if (try_parse_keyword("attribute")) + parse_attribute(); + else if (try_parse_keyword("switch")) + parse_switch(); + else if (try_parse_keyword("assign")) { + if (attrbuf.size() != 0) + error("dangling attribute"); + // See https://github.com/YosysHQ/yosys/pull/4765 for discussion on this + // warning + if (!switch_stack.back()->empty()) + warning("case rule assign statements after switch statements may cause unexpected behaviour. " + "The assign statement is reordered to come before all switch statements."); + RTLIL::SigSpec s1 = parse_sigspec(); + RTLIL::SigSpec s2 = parse_sigspec(); + current_case->actions.push_back(RTLIL::SigSig(std::move(s1), std::move(s2))); + expect_eol(); + } else + return; + } + } + + void parse_switch() + { + RTLIL::SwitchRule *rule = new RTLIL::SwitchRule; + rule->signal = parse_sigspec(); + rule->attributes = std::move(attrbuf); + switch_stack.back()->push_back(rule); + expect_eol(); + + while (true) { + if (try_parse_keyword("attribute")) { + parse_attribute(); + continue; + } + + if (try_parse_keyword("end")) { + expect_eol(); + break; + } + + expect_keyword("case"); + RTLIL::CaseRule *case_rule = new RTLIL::CaseRule; + case_rule->attributes = std::move(attrbuf); + rule->cases.push_back(case_rule); + switch_stack.push_back(&case_rule->switches); + case_stack.push_back(case_rule); + + if (!try_parse_eol()) { + while (true) { + case_rule->compare.push_back(parse_sigspec()); + if (try_parse_eol()) + break; + expect_char(','); + } + } + + parse_case_body(case_rule); + + switch_stack.pop_back(); + case_stack.pop_back(); + } + } + + void parse_process() + { + RTLIL::IdString proc_name = parse_id(); + expect_eol(); + + if (current_module->processes.count(proc_name) != 0) + error("RTLIL error: redefinition of process %s.", proc_name); + RTLIL::Process *proc = current_module->addProcess(std::move(proc_name)); + proc->attributes = std::move(attrbuf); + + switch_stack.clear(); + switch_stack.push_back(&proc->root_case.switches); + case_stack.clear(); + case_stack.push_back(&proc->root_case); + + parse_case_body(&proc->root_case); + + while (try_parse_keyword("sync")) + { + RTLIL::SyncRule *rule = new RTLIL::SyncRule; + + if (try_parse_keyword("low")) rule->type = RTLIL::ST0; + else if (try_parse_keyword("high")) rule->type = RTLIL::ST1; + else if (try_parse_keyword("posedge")) rule->type = RTLIL::STp; + else if (try_parse_keyword("negedge")) rule->type = RTLIL::STn; + else if (try_parse_keyword("edge")) rule->type = RTLIL::STe; + else if (try_parse_keyword("always")) rule->type = RTLIL::STa; + else if (try_parse_keyword("global")) rule->type = RTLIL::STg; + else if (try_parse_keyword("init")) rule->type = RTLIL::STi; + else error("Unexpected sync type: %s", error_token()); + + if (rule->type != RTLIL::STa && rule->type != RTLIL::STg && rule->type != RTLIL::STi) + rule->signal = parse_sigspec(); + proc->syncs.push_back(rule); + expect_eol(); + + bool attributes_in_update_list = false; + while (true) + { + if (try_parse_keyword("update")) { + RTLIL::SigSpec s1 = parse_sigspec(); + RTLIL::SigSpec s2 = parse_sigspec(); + rule->actions.push_back(RTLIL::SigSig(std::move(s1), std::move(s2))); + expect_eol(); + continue; + } + + if (try_parse_keyword("attribute")) { + attributes_in_update_list = true; + parse_attribute(); + continue; + } + + if (!try_parse_keyword("memwr")) + break; + + RTLIL::MemWriteAction act; + act.attributes = std::move(attrbuf); + act.memid = parse_id(); + act.address = parse_sigspec(); + act.data = parse_sigspec(); + act.enable = parse_sigspec(); + act.priority_mask = parse_const(); + rule->mem_write_actions.push_back(std::move(act)); + expect_eol(); + } + // The old parser allowed dangling attributes before a "sync" to carry through + // the "sync", so we will too, for now. + if (attributes_in_update_list && attrbuf.size() > 0) + error("dangling attribute"); + } + + expect_keyword("end"); + expect_eol(); + } + + RTLILFrontendWorker(RTLIL::Design *design) : design(design) {} + + void parse(std::istream *f) + { + this->f = f; + line_num = 0; + advance_to_next_nonempty_line(); + while (f->good()) + { + if (try_parse_keyword("attribute")) { + parse_attribute(); + continue; + } + if (try_parse_keyword("module")) { + parse_module(); + continue; + } + if (try_parse_keyword("autoidx")) { + autoidx = std::max(autoidx, parse_integer()); + expect_eol(); + continue; + } + error("Unexpected token: %s", error_token()); + } + if (attrbuf.size() != 0) + error("dangling attribute"); + } +}; + struct RTLILFrontend : public Frontend { RTLILFrontend() : Frontend("rtlil", "read modules from RTLIL file") { } void help() override @@ -63,9 +807,7 @@ struct RTLILFrontend : public Frontend { } 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; + RTLILFrontendWorker worker(design); log_header(design, "Executing RTLIL frontend.\n"); @@ -73,33 +815,27 @@ struct RTLILFrontend : public Frontend { 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; + worker.flag_nooverwrite = true; + worker.flag_overwrite = false; continue; } if (arg == "-overwrite") { - RTLIL_FRONTEND::flag_nooverwrite = false; - RTLIL_FRONTEND::flag_overwrite = true; + worker.flag_nooverwrite = false; + worker.flag_overwrite = true; continue; } if (arg == "-lib") { - RTLIL_FRONTEND::flag_lib = true; + worker.flag_lib = true; continue; } break; } extra_args(f, filename, args, argidx); - log("Input filename: %s\n", filename.c_str()); + log("Input filename: %s\n", filename); - RTLIL_FRONTEND::lexin = f; - RTLIL_FRONTEND::current_design = design; - rtlil_frontend_yydebug = false; - rtlil_frontend_yyrestart(NULL); - rtlil_frontend_yyparse(); - rtlil_frontend_yylex_destroy(); + worker.parse(f); } } RTLILFrontend; YOSYS_NAMESPACE_END - diff --git a/frontends/rtlil/rtlil_lexer.l b/frontends/rtlil/rtlil_lexer.l deleted file mode 100644 index c374dd395..000000000 --- a/frontends/rtlil/rtlil_lexer.l +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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; -} diff --git a/frontends/rtlil/rtlil_parser.y b/frontends/rtlil/rtlil_parser.y deleted file mode 100644 index fc7615364..000000000 --- a/frontends/rtlil/rtlil_parser.y +++ /dev/null @@ -1,524 +0,0 @@ -/* - * 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; - }; diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 95bede420..5790e92f0 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -123,13 +123,13 @@ void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefil 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); + msg_type == VERIFIC_PROGRAM_ERROR ? "PROGRAM_ERROR" : "UNKNOWN", message_id ? 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()); + string full_message = stringf("%s%s\n", message_prefix, message); #ifdef VERIFIC_LINEFILE_INCLUDES_COLUMNS log_verific_callback(int(msg_type), message_id, LineFile::GetFileName(linefile), linefile ? linefile->GetLeftLine() : 0, linefile ? linefile->GetLeftCol() : 0, @@ -141,9 +141,9 @@ void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefil #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()); + log_warning_noprefix("%s%s\n", message_prefix, message); else - log("%s%s\n", message_prefix.c_str(), message.c_str()); + log("%s%s\n", message_prefix, message); } if (verific_error_msg.empty() && (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_PROGRAM_ERROR)) verific_error_msg = message; @@ -166,6 +166,25 @@ string get_full_netlist_name(Netlist *nl) return nl->CellBaseName(); } +std::string format_src_location(DesignObj *obj) +{ + if (obj == nullptr || !obj->Linefile()) + return std::string(); +#ifdef VERIFIC_LINEFILE_INCLUDES_COLUMNS + return stringf("%s:%d.%d-%d.%d", LineFile::GetFileName(obj->Linefile()), obj->Linefile()->GetLeftLine(), obj->Linefile()->GetLeftCol(), obj->Linefile()->GetRightLine(), obj->Linefile()->GetRightCol()); +#else + return stringf("%s:%d", LineFile::GetFileName(obj->Linefile()), LineFile::GetLineNo(obj->Linefile())); +#endif +} + +std::string announce_src_location(DesignObj *obj) +{ + std::string loc = format_src_location(obj); + if (loc.empty()) + return std::string(); + return loc + ": "; +} + #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT class YosysStreamCallBackHandler : public VerificStreamCallBackHandler { @@ -200,8 +219,8 @@ YosysStreamCallBackHandler verific_read_cb; // ================================================================== -VerificImporter::VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_names, bool mode_verific, bool mode_autocover, bool mode_fullinit) : - mode_gates(mode_gates), mode_keep(mode_keep), mode_nosva(mode_nosva), +VerificImporter::VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_sva_continue, 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_sva_continue(mode_sva_continue), mode_names(mode_names), mode_verific(mode_verific), mode_autocover(mode_autocover), mode_fullinit(mode_fullinit) { @@ -232,7 +251,7 @@ 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("$%s:%d", RTLIL::encode_filename(Verific::LineFile::GetFileName(obj->Linefile())), Verific::LineFile::GetLineNo(obj->Linefile())); s += stringf("$%d", autoidx++); return s; } @@ -250,7 +269,7 @@ 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()); + log_error("Error parsing VHDL %s.\n", typ); } static const RTLIL::Const extract_vhdl_bit_vector(std::string &val, std::string &typ) @@ -261,19 +280,19 @@ static const RTLIL::Const extract_vhdl_bit_vector(std::string &val, std::string c.flags |= RTLIL::CONST_FLAG_SIGNED; return c; } - log_error("Error parsing VHDL %s.\n", typ.c_str()); + log_error("Error parsing VHDL %s.\n", typ); } static const RTLIL::Const extract_vhdl_integer(std::string &val) { char *end; - return RTLIL::Const((int)std::strtol(val.c_str(), &end, 10), 32); + return RTLIL::Const((int)std::strtol(val.c_str(), &end, 10)); } 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); + return RTLIL::Const((int)val[1]); log_error("Error parsing VHDL character.\n"); } @@ -311,7 +330,7 @@ static const RTLIL::Const extract_vhdl_const(const char *value, bool output_sig } 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); + c = RTLIL::Const((int)decimal); } else if (val == "false") { c = RTLIL::Const::from_string("0"); } else if (val == "true") { @@ -344,7 +363,7 @@ static const RTLIL::Const extract_verilog_const(const char *value, bool allow_s } 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); + c = RTLIL::Const((int)decimal); } else if (allow_string) { c = RTLIL::Const(val); } else { @@ -416,13 +435,8 @@ void VerificImporter::import_attributes(dict &att 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 + attributes[ID::src] = format_src_location(obj); FOREACH_ATTRIBUTE(obj, mi, attr) { if (attr->Key()[0] == ' ' || attr->Value() == nullptr) @@ -472,7 +486,7 @@ void VerificImporter::import_attributes(dict &att 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)); + attributes.emplace(stringf("\\enum_value_%s", value.as_string()), RTLIL::escape_id(k)); } #ifdef VERIFIC_VHDL_SUPPORT else if (nl->IsFromVhdl()) { @@ -508,7 +522,7 @@ void VerificImporter::import_attributes(dict &att } } if (p == nullptr) - log_error("Expected TypeRange value '%s' to be of form \"\" or .\n", v); + log_error("%sExpected TypeRange value '%s' to be of form \"\" or .\n", announce_src_location(obj), v); } #endif } @@ -988,7 +1002,7 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr 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()); + log_error("%sCan't import Verific OPER_SHIFT_RIGHT instance %s: carry_in is neither 0 nor msb of left input\n", announce_src_location(inst), inst->Name()); import_attributes(cell->attributes, inst); return true; } @@ -1039,7 +1053,7 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr 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()); + log_error("%sCan't import Verific OPER_LESSTHAN instance %s: carry_in is neither 0 nor 1\n", announce_src_location(inst), inst->Name()); import_attributes(cell->attributes, inst); return true; } @@ -1469,7 +1483,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma if (design->has(module_name)) { if (!nl->IsOperator() && !is_blackbox(nl)) - log_cmd_error("Re-definition of module `%s'.\n", netlist_name.c_str()); + log_cmd_error("Re-definition of module `%s'.\n", netlist_name); return; } @@ -1557,9 +1571,12 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex()); wire->upto = portbus->IsUp(); import_attributes(wire->attributes, portbus, nl, portbus->Size()); + if (portbus->Size() == 1) + wire->set_bool_attribute(ID::single_bit_vector); SetIter si ; Port *port ; FOREACH_PORT_OF_PORTBUS(portbus, si, port) { + wire->port_id = nl->IndexOf(port) + 1; import_attributes(wire->attributes, port->GetNet(), nl, portbus->Size()); break; } @@ -1623,7 +1640,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma 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", + log_error("%sVerific RamNet %s is connected to unsupported instance type %s (%s).\n", announce_src_location(pr->GetInst()), net->Name(), pr->GetInst()->View()->Owner()->Name(), pr->GetInst()->Name()); } @@ -1647,7 +1664,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma if (*ascii_initdata == 0) break; if (*ascii_initdata == '0' || *ascii_initdata == '1') { - initval.bits()[bit_idx] = (*ascii_initdata == '0') ? State::S0 : State::S1; + initval.set(bit_idx, (*ascii_initdata == '0') ? State::S0 : State::S1); initval_valid = true; } ascii_initdata++; @@ -1755,6 +1772,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma break; } import_attributes(wire->attributes, netbus, nl, netbus->Size()); + if (netbus->Size() == 1) + wire->set_bool_attribute(ID::single_bit_vector); RTLIL::Const initval = Const(State::Sx, GetSize(wire)); bool initval_valid = false; @@ -1769,9 +1788,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma if (init_nets.count(net)) { if (init_nets.at(net) == '0') - initval.bits().at(bitidx) = State::S0; + initval.set(bitidx, State::S0); if (init_nets.at(net) == '1') - initval.bits().at(bitidx) = State::S1; + initval.set(bitidx, State::S1); initval_valid = true; init_nets.erase(net); } @@ -1844,13 +1863,13 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma 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 (GetSize(initval) < GetSize(bit.wire)) + initval.resize(GetSize(bit.wire), State::Sx); if (it.second == '0') - initval.bits().at(bit.offset) = State::S0; + initval.set(bit.offset, State::S0); if (it.second == '1') - initval.bits().at(bit.offset) = State::S1; + initval.set(bit.offset, State::S1); bit.wire->attributes[ID::init] = initval; } @@ -1911,7 +1930,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma { 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()); + log_error("%sMemory net '%s' missing, possibly no driver, use verific -flatten.\n", announce_src_location(inst), inst->GetInput()->Name()); int numchunks = int(inst->OutputSize()) / memory->width; int chunksbits = ceil_log2(numchunks); @@ -1922,7 +1941,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma 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)); + RTLIL::IdString(stringf("%s_%d", inst_name, i)), ID($memrd)); cell->parameters[ID::MEMID] = memory->name.str(); cell->parameters[ID::CLK_ENABLE] = false; cell->parameters[ID::CLK_POLARITY] = true; @@ -1942,7 +1961,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma { 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()); + log_error("%sMemory net '%s' missing, possibly no driver, use verific -flatten.\n", announce_src_location(inst), inst->GetInput()->Name()); int numchunks = int(inst->Input2Size()) / memory->width; int chunksbits = ceil_log2(numchunks); @@ -1952,7 +1971,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma 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)); + RTLIL::IdString(stringf("%s_%d", inst_name, i)), ID($memwr)); cell->parameters[ID::MEMID] = memory->name.str(); cell->parameters[ID::CLK_ENABLE] = false; cell->parameters[ID::CLK_POLARITY] = true; @@ -1977,7 +1996,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma 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()); + log_warning("%sUnsupported Verific operator: %s (fallback to gate level implementation provided by verific)\n", announce_src_location(inst), inst->View()->Owner()->Name()); } else { if (import_netlist_instance_gates(inst, inst_name)) continue; @@ -2037,7 +2056,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma } Const qx_init = Const(State::S1, width); - qx_init.bits().resize(2 * width, State::S0); + qx_init.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); @@ -2163,10 +2182,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma if (inst->IsPrimitive()) { if (!mode_keep) - log_error("Unsupported Verific primitive %s of type %s\n", inst->Name(), inst->View()->Owner()->Name()); + log_error("%sUnsupported Verific primitive %s of type %s\n", announce_src_location(inst), 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()); + log_warning("%sUnsupported Verific primitive %s of type %s\n", announce_src_location(inst), inst->Name(), inst->View()->Owner()->Name()); } import_verific_cells: @@ -2302,7 +2321,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma continue; if (non_ff_bits.count(SigBit(wire, i))) - initval.bits()[i] = State::Sx; + initval.set(i, State::Sx); } if (wire->port_input) { @@ -2312,6 +2331,12 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma wire->attributes.erase(ID::init); } } + + if (num_sva_continue) { + log_warning("Encountered %d items containing unsupported SVA!\n", num_sva_continue); + log_warning("Unsupported SVA imported as 'x and marked using the `unsupported_sva' attribute due to -sva-continue-on-err.\n"); + } + num_sva_continue = 0; } // ================================================================== @@ -2489,7 +2514,7 @@ Cell *VerificClocking::addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const 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]; + initval.set(offset+i, val[c.offset+i]); } offset += GetSize(c); } @@ -2560,7 +2585,7 @@ Cell *VerificClocking::addAldff(IdString name, RTLIL::SigSpec sig_aload, RTLIL:: 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]; + initval.set(offset+i, val[c.offset+i]); } offset += GetSize(c); } @@ -2651,7 +2676,7 @@ struct VerificExtNets 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()); + log_error("No common ancestor found between %s and %s.\n", get_full_netlist_name(A), get_full_netlist_name(B)); } void run(Netlist *nl) @@ -2675,17 +2700,17 @@ struct VerificExtNets 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()); + log("Fixing external net reference on port %s.%s.%s:\n", get_full_netlist_name(nl), 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()); + log(" external net owner: %s\n", get_full_netlist_name(ext_nl)); 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()); + log(" common ancestor: %s\n", get_full_netlist_name(ca_nl)); Net *ca_net = route_up(net, !port->IsOutput(), ca_nl); Net *new_net = ca_net; @@ -2974,6 +2999,9 @@ std::set import_tops(const char* work, std::mapChangePortBusStructures(1 /* hierarchical */); @@ -3043,7 +3072,7 @@ std::string verific_import(Design *design, const std::mapsecond; if (nl_done.count(it->first) == 0) { - VerificImporter importer(false, false, false, false, false, false, false); + VerificImporter importer(false, 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())); } @@ -3052,7 +3081,7 @@ std::string verific_import(Design *design, const std::map\n"); log(" Maximum number of ctrl bits for SVA checker FSMs (default=16).\n"); log("\n"); @@ -3341,7 +3375,7 @@ struct VerificPass : public Pass { char block[4096]; while (1) { if (fgets(block, 4096, Frontend::current_script_file == nullptr? stdin : Frontend::current_script_file) == nullptr) - log_error("Unexpected end of file in here document '%s'!\n", filename.c_str()); + log_error("Unexpected end of file in here document '%s'!\n", filename); buffer += block; if (buffer.size() > 0 && (buffer[buffer.size() - 1] == '\n' || buffer[buffer.size() - 1] == '\r')) break; @@ -3428,7 +3462,6 @@ struct VerificPass : public Pass { 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" @@ -3461,11 +3494,13 @@ struct VerificPass : public Pass { RuntimeFlags::SetVar("veri_extract_dualport_rams", 0); RuntimeFlags::SetVar("veri_extract_multiport_rams", 1); RuntimeFlags::SetVar("veri_allow_any_ram_in_loop", 1); + RuntimeFlags::SetVar("veri_replace_const_exprs", 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_replace_const_exprs", 1); RuntimeFlags::SetVar("vhdl_support_variable_slice", 1); RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0); @@ -3485,6 +3520,14 @@ struct VerificPass : public Pass { // WARNING: instantiating unknown module 'XYZ' (VERI-1063) Message::SetMessageType("VERI-1063", VERIFIC_ERROR); + // Downgrade warnings about things that are normal + // VERIFIC-WARNING [VERI-1209] foo.sv:98: expression size 7 truncated to fit in target size 6 + Message::SetMessageType("VERI-1209", VERIFIC_INFO); + // VERIFIC-WARNING [VERI-1142] foo.sv:55: system task 'display' is ignored for synthesis + Message::SetMessageType("VERI-1142", VERIFIC_INFO); + // VERIFIC-WARNING [VERI-2418] foo.svh:503: parameter 'all_cfgs_gp' declared inside package 'bp_common_pkg' shall be treated as localparam + Message::SetMessageType("VERI-2418", VERIFIC_INFO); + // https://github.com/YosysHQ/yosys/issues/1055 RuntimeFlags::SetVar("veri_elaborate_top_level_modules_having_interface_ports", 1) ; #endif @@ -3543,6 +3586,9 @@ struct VerificPass : public Pass { } 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::SetMessageType("VERI-1209", new_type); + Message::SetMessageType("VERI-1142", new_type); + Message::SetMessageType("VERI-2418", new_type); Message::SetAllMessageType(VERIFIC_INFO, new_type); } else if (Strings::compare(args[argidx].c_str(), "comments")) { Message::SetAllMessageType(VERIFIC_COMMENT, new_type); @@ -3804,7 +3850,7 @@ struct VerificPass : public Pass { 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()); + log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", filename); set_units_to_blackbox(map, work, flag_lib); } verific_import_pending = true; @@ -3829,7 +3875,7 @@ struct VerificPass : public Pass { 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()); + log_cmd_error("Reading `%s' in VHDL_93 mode failed.\n", filename); set_units_to_blackbox(map, work, flag_lib); } verific_import_pending = true; @@ -3854,7 +3900,7 @@ struct VerificPass : public Pass { 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()); + log_cmd_error("Reading `%s' in VHDL_2K mode failed.\n", filename); set_units_to_blackbox(map, work, flag_lib); } verific_import_pending = true; @@ -3879,7 +3925,7 @@ struct VerificPass : public Pass { 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()); + log_cmd_error("Reading `%s' in VHDL_2019 mode failed.\n", filename); set_units_to_blackbox(map, work, flag_lib); } verific_import_pending = true; @@ -3904,7 +3950,7 @@ struct VerificPass : public Pass { 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()); + log_cmd_error("Reading `%s' in VHDL_2008 mode failed.\n", filename); set_units_to_blackbox(map, work, flag_lib); } verific_import_pending = true; @@ -3918,7 +3964,7 @@ struct VerificPass : public Pass { 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()); + log_cmd_error("Reading `%s' in EDIF mode failed.\n", filename); } goto check_error; } @@ -3941,7 +3987,7 @@ struct VerificPass : public Pass { 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()); + log_cmd_error("Reading `%s' in LIBERTY mode failed.\n", filename); SynlibLibrary *lib = synlib_file::GetLastLibraryAnalyzed(); if (lib && flag_lib) { MapIter mi ; @@ -4013,7 +4059,8 @@ struct VerificPass : public Pass { { 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_nosva = false, mode_sva_continue = false; + bool 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; @@ -4051,6 +4098,10 @@ struct VerificPass : public Pass { mode_nosva = true; continue; } + if (args[argidx] == "-sva-continue-on-err") { + mode_sva_continue = true; + continue; + } if (args[argidx] == "-L" && argidx+1 < GetSize(args)) { verific_sva_fsm_limit = atoi(args[++argidx].c_str()); continue; @@ -4078,7 +4129,7 @@ struct VerificPass : public Pass { 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()); + log_warning_noprefix("-chparam %s already specified: overwriting.\n", key); continue; } if (args[argidx] == "-V") { @@ -4107,6 +4158,12 @@ struct VerificPass : public Pass { if (argidx > GetSize(args) && args[argidx].compare(0, 1, "-") == 0) cmd_error(args, argidx, "unknown option"); + if ((unsigned long)verific_sva_fsm_limit >= sizeof(1ull)*8) + log_cmd_error("-L %d: limit too large; maximum allowed value is %zu.\n", verific_sva_fsm_limit, sizeof(1ull)*8-1); + + if (already_imported) + log_warning("Note that all Verific flags were reset to defaults after last -import.\n"); + std::set top_mod_names; if (mode_all) @@ -4175,7 +4232,7 @@ struct VerificPass : public Pass { auto it = nl_todo.begin(); Netlist *nl = it->second; if (nl_done.count(it->first) == 0) { - VerificImporter importer(mode_gates, mode_keep, mode_nosva, + VerificImporter importer(mode_gates, mode_keep, mode_nosva, mode_sva_continue, 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())); @@ -4184,6 +4241,7 @@ struct VerificPass : public Pass { } verific_cleanup(); + already_imported = true; goto check_error; } @@ -4205,7 +4263,7 @@ struct VerificPass : public Pass { } lines.sort(); for (auto &line : lines) - log("verific -cfg %s\n", line.c_str()); + log("verific -cfg %s\n", line); goto check_error; } @@ -4264,7 +4322,7 @@ struct VerificPass : public Pass { } if (!verific_error_msg.empty()) - log_error("%s\n", verific_error_msg.c_str()); + log_error("%s\n", verific_error_msg); } #else /* YOSYS_ENABLE_VERIFIC */ diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h index 4e9c7a305..f33a380f7 100644 --- a/frontends/verific/verific.h +++ b/frontends/verific/verific.h @@ -73,10 +73,12 @@ struct VerificImporter std::map sva_posedge_map; pool any_all_nets; - bool mode_gates, mode_keep, mode_nosva, mode_names, mode_verific; + bool mode_gates, mode_keep, mode_nosva, mode_sva_continue, 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); + int num_sva_continue = 0; + + VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_sva_continue, bool mode_names, bool mode_verific, bool mode_autocover, bool mode_fullinit); RTLIL::SigBit net_map_at(Verific::Net *net); diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index 860d3c166..dffe6e8e0 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -124,6 +124,7 @@ struct SvaFsm { Module *module; VerificClocking clocking; + std::function parser_error; SigBit trigger_sig = State::S1, disable_sig; SigBit throughout_sig = State::S1; @@ -148,6 +149,7 @@ struct SvaFsm module = clking.module; clocking = clking; trigger_sig = trig; + parser_error = [](std::string msg){ log_error("%s", msg); }; startNode = createNode(); acceptNode = createNode(); @@ -475,18 +477,18 @@ struct SvaFsm 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); + parser_error(stringf("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++) + for (unsigned long long i = 0; i < (1ull << 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]); + for (int j = 0; j < GetSize(dnode.ctrl); j++) + if (ctrl_val[j] == State::S1) + ctrl_bits.insert(dnode.ctrl[j]); vector new_state; bool accept = false, cond = false; @@ -575,7 +577,7 @@ struct SvaFsm if (delta_pos >= 0 && i_within_j && j_within_i) { did_something = true; - values[i].bits()[delta_pos] = State::Sa; + values[i].set(delta_pos, State::Sa); values[j] = values.back(); values.pop_back(); goto next_pair; @@ -1023,20 +1025,20 @@ struct VerificSvaImporter [[noreturn]] void parser_error(std::string errmsg) { - if (!importer->mode_keep) - log_error("%s", errmsg.c_str()); - log_warning("%s", errmsg.c_str()); + if (!importer->mode_keep && !importer->mode_sva_continue) + log_error("%s", errmsg); + log_warning("%s", errmsg); 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))); + parser_error(stringf("%s at %s:%d.\n", errmsg, 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()); + parser_error(stringf("%s at %s (%s)", errmsg, inst->View()->Owner()->Name(), inst->Name()), inst->Linefile()); } [[noreturn]] void parser_error(Instance *inst) @@ -1260,6 +1262,7 @@ struct VerificSvaImporter if (inst->Type() == PRIM_SVA_FIRST_MATCH) { SvaFsm match_fsm(clocking); + match_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); }; match_fsm.createLink(parse_sequence(match_fsm, match_fsm.createStartNode(), inst->GetInput()), match_fsm.acceptNode); int node = fsm.createNode(); @@ -1426,12 +1429,15 @@ struct VerificSvaImporter if (inst->Type() == PRIM_SVA_SEQ_AND || inst->Type() == PRIM_SVA_AND) { SvaFsm fsm1(clocking); + fsm1.parser_error = [&](std::string msg) { this->parser_error(msg); }; fsm1.createLink(parse_sequence(fsm1, fsm1.createStartNode(), inst->GetInput1()), fsm1.acceptNode); SvaFsm fsm2(clocking); + fsm2.parser_error = [&](std::string msg) { this->parser_error(msg); }; fsm2.createLink(parse_sequence(fsm2, fsm2.createStartNode(), inst->GetInput2()), fsm2.acceptNode); SvaFsm combined_fsm(clocking); + combined_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); }; fsm1.getDFsm(combined_fsm, combined_fsm.createStartNode(), -1, combined_fsm.acceptNode); fsm2.getDFsm(combined_fsm, combined_fsm.createStartNode(), -1, combined_fsm.acceptNode); @@ -1456,6 +1462,7 @@ struct VerificSvaImporter if (inst->Type() == PRIM_SVA_INTERSECT || inst->Type() == PRIM_SVA_WITHIN) { SvaFsm intersect_fsm(clocking); + intersect_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); }; if (inst->Type() == PRIM_SVA_INTERSECT) { @@ -1562,6 +1569,7 @@ struct VerificSvaImporter int node; SvaFsm antecedent_fsm(clocking, trig); + antecedent_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); }; node = parse_sequence(antecedent_fsm, antecedent_fsm.createStartNode(), antecedent_net); if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) { int next_node = antecedent_fsm.createNode(); @@ -1613,15 +1621,19 @@ struct VerificSvaImporter } else if (inst->Type() == PRIM_SVA_OVERLAPPED_IMPLICATION || - inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) + inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION || + (mode_cover && ( + inst->Type() == PRIM_SVA_OVERLAPPED_FOLLOWED_BY || + inst->Type() == PRIM_SVA_NON_OVERLAPPED_FOLLOWED_BY))) { Net *antecedent_net = inst->GetInput1(); Net *consequent_net = inst->GetInput2(); int node; SvaFsm antecedent_fsm(clocking, trig); + antecedent_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); }; node = parse_sequence(antecedent_fsm, antecedent_fsm.createStartNode(), antecedent_net); - if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) { + if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION || inst->Type() == PRIM_SVA_NON_OVERLAPPED_FOLLOWED_BY) { int next_node = antecedent_fsm.createNode(); antecedent_fsm.createEdge(node, next_node); node = next_node; @@ -1674,6 +1686,7 @@ struct VerificSvaImporter } SvaFsm consequent_fsm(clocking, antecedent_match); + consequent_fsm.parser_error = [&](std::string msg) { this->parser_error(msg); }; node = parse_sequence(consequent_fsm, consequent_fsm.createStartNode(), consequent_net); consequent_fsm.createLink(node, consequent_fsm.acceptNode); @@ -1693,6 +1706,7 @@ struct VerificSvaImporter } SvaFsm fsm(clocking, trig); + fsm.parser_error = [&](std::string msg) { this->parser_error(msg); }; int node = parse_sequence(fsm, fsm.createStartNode(), net); fsm.createLink(node, fsm.acceptNode); @@ -1707,30 +1721,34 @@ struct VerificSvaImporter void import() { - try - { - module = importer->module; - netlist = root->Owner(); + 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())); + int initial_cell_count = GetSize(module->cells_); + int initial_wire_count = GetSize(module->wires_); + int initial_connection_count = GetSize(module->connections_); - bool is_user_declared = root->IsUserDeclared(); + 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())); - // 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; - } + 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); + RTLIL::IdString root_name = module->uniquify(importer->mode_names || is_user_declared ? RTLIL::escape_id(root->Name()) : NEW_ID); + try + { // parse SVA sequence into trigger signal clocking = VerificClocking(importer, root->GetInput(), true); @@ -1833,6 +1851,36 @@ struct VerificSvaImporter } catch (ParserErrorException) { + if (importer->mode_sva_continue) { + + std::vector remove_cells; + pool remove_wires; + + for (int i = 0, end = GetSize(module->cells_) - initial_cell_count; i != end; ++i) + remove_cells.push_back(module->cells_.element(i)->second); + + for (int i = 0, end = GetSize(module->wires_) - initial_wire_count; i != end; ++i) + remove_wires.emplace(module->wires_.element(i)->second); + + for (auto cell : remove_cells) + module->remove(cell); + module->remove(remove_wires); + + module->connections_.resize(initial_connection_count); + + RTLIL::Cell *c = nullptr; + + if (mode_assert) c = module->addAssert(root_name, State::Sx, State::Sx); + if (mode_assume) c = module->addAssume(root_name, State::Sx, State::Sx); + if (mode_cover) c = module->addCover(root_name, State::Sx, State::Sx); + + if (c) { + importer->import_attributes(c->attributes, root); + c->set_bool_attribute(ID(unsupported_sva)); + } + + importer->num_sva_continue++; + } } } }; diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc index 2c923f0b7..3f937f3c2 100644 --- a/frontends/verilog/Makefile.inc +++ b/frontends/verilog/Makefile.inc @@ -10,6 +10,10 @@ frontends/verilog/verilog_parser.tab.cc: frontends/verilog/verilog_parser.y frontends/verilog/verilog_parser.tab.hh: frontends/verilog/verilog_parser.tab.cc +frontends/verilog/verilog_frontend.o: frontends/verilog/verilog_parser.tab.hh +frontends/verilog/preproc.o: frontends/verilog/verilog_parser.tab.hh + +frontends/verilog/verilog_lexer.h: frontends/verilog/verilog_parser.tab.hh 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 $< @@ -20,5 +24,6 @@ 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/verilog_error.o OBJS += frontends/verilog/const2ast.o diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index a4dfbc7ec..573af336b 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -42,14 +42,23 @@ YOSYS_NAMESPACE_BEGIN using namespace AST; +using namespace VERILOG_FRONTEND; + +void ConstParser::log_maybe_loc_error(std::string msg) { + log_file_error(*loc.begin.filename, loc.begin.line, "%s", msg); +} + +void ConstParser::log_maybe_loc_warn(std::string msg) { + log_file_warning(*loc.begin.filename, loc.begin.line, "%s", msg); +} // divide an arbitrary length decimal number by two and return the rest -static int my_decimal_div_by_two(std::vector &digits) +int ConstParser::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"); + log_maybe_loc_error("Invalid use of [a-fxz?] in decimal constant.\n"); digits[i] += carry * 10; carry = digits[i] % 2; digits[i] /= 2; @@ -60,7 +69,7 @@ static int my_decimal_div_by_two(std::vector &digits) } // find the number of significant bits in a binary number (not including the sign bit) -static int my_ilog2(int x) +int ConstParser::my_ilog2(int x) { int ret = 0; while (x != 0 && x != -1) { @@ -71,7 +80,7 @@ static int my_ilog2(int x) } // 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) +void ConstParser::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; @@ -102,8 +111,8 @@ static void my_strtobin(std::vector &data, const char *str, int le 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); + log_maybe_loc_error(stringf("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) @@ -126,7 +135,7 @@ static void my_strtobin(std::vector &data, const char *str, int le } 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); + log_maybe_loc_error(stringf("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) @@ -140,21 +149,19 @@ static void my_strtobin(std::vector &data, const char *str, int le } 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"); + log_maybe_loc_error("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()); + log_maybe_loc_warn(stringf("Literal has a width of %d bit, but value requires %d bit.\n", + len_in_bits, len)); } - // convert the Verilog code for a constant to an AST node -AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn_z) +std::unique_ptr ConstParser::const2ast(std::string code, char case_type, bool warn_z) { if (warn_z) { - AstNode *ret = const2ast(code, case_type); + auto 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()); + log_maybe_loc_warn("Yosys has only limited support for tri-state logic at the moment.\n"); return ret; } @@ -172,7 +179,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn ch = ch >> 1; } } - AstNode *ast = AstNode::mkconst_bits(data, false); + auto ast = AstNode::mkconst_bits(loc, data, false); ast->str = code; return ast; } @@ -191,7 +198,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn 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); + return AstNode::mkconst_bits(loc, data, true); } // unsized constant @@ -239,10 +246,11 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn if (is_signed && data.back() == State::S1) data.push_back(State::S0); } - return AstNode::mkconst_bits(data, is_signed, is_unsized); + return AstNode::mkconst_bits(loc, data, is_signed, is_unsized); } return NULL; } + YOSYS_NAMESPACE_END diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index e33b0a2c3..7675bab62 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -34,6 +34,7 @@ #include "preproc.h" #include "verilog_frontend.h" +#include "frontends/verilog/verilog_parser.tab.hh" #include "kernel/log.h" #include #include @@ -241,7 +242,7 @@ struct 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()); + log_error("Duplicate macro arguments with name `%s'.\n", name); } name_to_pos[name] = args.size(); @@ -264,7 +265,7 @@ struct arg_map_t // (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 stringf("macro_%s_arg%d", macro_name, pos); } // Return definitions for the macro arguments (so that substituting in the macro body and @@ -740,7 +741,7 @@ read_define(const std::string &filename, 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()); + log_file_error(filename, 0, "Invalid name for macro definition: >>%s<<.\n", name); } } @@ -749,7 +750,9 @@ 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) + const std::list &include_dirs, + ParseState &parse_state, + ParseMode &parse_mode) { define_map_t defines; defines.merge(pre_defines); @@ -786,14 +789,14 @@ frontend_verilog_preproc(std::istream &f, else if (ifdef_pass_level > 0) ifdef_pass_level--; else - log_error("Found %s outside of macro conditional branch!\n", tok.c_str()); + log_error("Found %s outside of macro conditional branch!\n", tok); 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()); + log_error("Found %s outside of macro conditional branch!\n", tok); ifdef_pass_level--; ifdef_fail_level = 1; ifdef_already_satisfied = true; @@ -810,7 +813,7 @@ frontend_verilog_preproc(std::istream &f, 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()); + log_error("Found %s outside of macro conditional branch!\n", tok); ifdef_pass_level--; ifdef_fail_level = 1; ifdef_already_satisfied = true; @@ -892,11 +895,7 @@ frontend_verilog_preproc(std::istream &f, // 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 + fixed_fn = parent_from_file_path(filename) + fn; ff.open(fixed_fn); } if (ff.fail() && fn.size() > 0 && fn_relative) { @@ -961,11 +960,11 @@ frontend_verilog_preproc(std::istream &f, } if (tok == "`resetall") { - default_nettype_wire = true; + parse_state.default_nettype_wire = true; continue; } - if (tok == "`undefineall" && sv_mode) { + if (tok == "`undefineall" && parse_mode.sv) { defines.clear(); global_defines_cache.clear(); continue; diff --git a/frontends/verilog/preproc.h b/frontends/verilog/preproc.h index 330855a92..8333f7661 100644 --- a/frontends/verilog/preproc.h +++ b/frontends/verilog/preproc.h @@ -35,6 +35,11 @@ YOSYS_NAMESPACE_BEGIN struct define_body_t; struct arg_map_t; +namespace VERILOG_FRONTEND { + struct ParseState; + struct ParseMode; +}; + struct define_map_t { define_map_t(); @@ -71,7 +76,9 @@ 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); + const std::list &include_dirs, + VERILOG_FRONTEND::ParseState &parse_state, + VERILOG_FRONTEND::ParseMode &parse_mode); YOSYS_NAMESPACE_END diff --git a/frontends/verilog/verilog_error.cc b/frontends/verilog/verilog_error.cc new file mode 100644 index 000000000..b968b3e21 --- /dev/null +++ b/frontends/verilog/verilog_error.cc @@ -0,0 +1,45 @@ +/* + * 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_common.h" +#include "frontends/verilog/verilog_error.h" +#include "frontends/verilog/verilog_location.h" + +USING_YOSYS_NAMESPACE + +/** + * Legacy behavior is to only track lines. Now we have columns too, but we don't + * report them in errors. + * TODO: report columns, too + */ + +[[noreturn]] +void VERILOG_FRONTEND::formatted_err_at_loc(Location loc, std::string str) +{ + YOSYS_NAMESPACE_PREFIX log_file_error(loc.begin.filename ? *(loc.begin.filename) : "UNKNOWN", loc.begin.line, + "%s\n", std::move(str)); +} + +void VERILOG_FRONTEND::formatted_warn_at_loc(Location loc, std::string str) +{ + YOSYS_NAMESPACE_PREFIX log_file_warning(loc.begin.filename ? *(loc.begin.filename) : "UNKNOWN", loc.begin.line, + "%s\n", std::move(str)); +} diff --git a/frontends/verilog/verilog_error.h b/frontends/verilog/verilog_error.h new file mode 100644 index 000000000..ede489b26 --- /dev/null +++ b/frontends/verilog/verilog_error.h @@ -0,0 +1,31 @@ +#ifndef VERILOG_ERROR_H +#define VERILOG_ERROR_H + +#include "kernel/yosys_common.h" +#include "frontends/ast/ast.h" +#include "frontends/verilog/verilog_location.h" + +YOSYS_NAMESPACE_BEGIN + +namespace VERILOG_FRONTEND +{ + [[noreturn]] + void formatted_err_at_loc(Location loc, std::string str); + template + [[noreturn]] + void err_at_loc(Location loc, FmtString...> fmt, const Args &... args) + { + formatted_err_at_loc(std::move(loc), fmt.format(args...)); + } + + void formatted_warn_at_loc(Location loc, std::string str); + template + void warn_at_loc(Location loc, FmtString...> fmt, const Args &... args) + { + formatted_warn_at_loc(std::move(loc), fmt.format(args...)); + } +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 14a0f16c0..6c9e67dc5 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -31,10 +31,14 @@ #endif #include "verilog_frontend.h" +#include "verilog_lexer.h" +#include "verilog_error.h" +#include "verilog_location.h" #include "preproc.h" #include "kernel/yosys.h" #include "libs/sha1/sha1.h" #include +#include YOSYS_NAMESPACE_BEGIN using namespace VERILOG_FRONTEND; @@ -46,13 +50,13 @@ 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); + if (node->type == AST::AST_DPI_FUNCTION) + err_at_loc(node->location, "Found DPI function %s.\n", node->str); + for (auto& child : node->children) + error_on_dpi_function(child.get()); } -static void add_package_types(dict &user_types, std::vector &package_list) +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. @@ -61,7 +65,7 @@ static void add_package_types(dict &user_types, std 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; + user_types[s] = node.get(); } } } @@ -225,6 +229,10 @@ struct VerilogFrontend : public Frontend { log(" add 'dir' to the directories which are used when searching include\n"); log(" files\n"); log("\n"); + log(" -relativeshare\n"); + log(" use paths relative to share directory for source locations\n"); + log(" where possible (experimental).\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"); @@ -250,6 +258,8 @@ struct VerilogFrontend : public Frontend { bool flag_dump_vlog1 = false; bool flag_dump_vlog2 = false; bool flag_dump_rtlil = false; + bool flag_debug_lexer = false; + bool flag_debug_parser = false; bool flag_nolatches = false; bool flag_nomeminit = false; bool flag_nomem2reg = false; @@ -266,22 +276,25 @@ struct VerilogFrontend : public Frontend { bool flag_noblackbox = false; bool flag_nowb = false; bool flag_nosynthesis = false; + bool flag_yydebug = false; + bool flag_relative_share = 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; + ParseMode parse_mode = {}; + ParseState parse_state = {}; + parse_mode.sv = false; + parse_mode.formal = false; + parse_mode.noassert = false; + parse_mode.noassume = false; + parse_mode.norestrict = false; + parse_mode.assume_asserts = false; + parse_mode.assert_assumes = false; + parse_mode.lib = false; + parse_mode.specify = false; + parse_state.default_nettype_wire = true; args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end()); @@ -289,11 +302,11 @@ struct VerilogFrontend : public Frontend { for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-sv") { - sv_mode = true; + parse_mode.sv = true; continue; } if (arg == "-formal") { - formal_mode = true; + parse_mode.formal = true; continue; } if (arg == "-nosynthesis") { @@ -301,23 +314,23 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-noassert") { - noassert_mode = true; + parse_mode.noassert = true; continue; } if (arg == "-noassume") { - noassume_mode = true; + parse_mode.noassume = true; continue; } if (arg == "-norestrict") { - norestrict_mode = true; + parse_mode.norestrict = true; continue; } if (arg == "-assume-asserts") { - assume_asserts_mode = true; + parse_mode.assume_asserts = true; continue; } if (arg == "-assert-assumes") { - assert_assumes_mode = true; + parse_mode.assert_assumes = true; continue; } if (arg == "-nodisplay") { @@ -329,7 +342,8 @@ struct VerilogFrontend : public Frontend { flag_dump_ast2 = true; flag_dump_vlog1 = true; flag_dump_vlog2 = true; - frontend_verilog_yydebug = true; + flag_debug_lexer = true; + flag_debug_parser = true; continue; } if (arg == "-dump_ast1") { @@ -357,7 +371,9 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-yydebug") { - frontend_verilog_yydebug = true; + flag_yydebug = true; + flag_debug_lexer = true; + flag_debug_parser = true; continue; } if (arg == "-nolatches") { @@ -393,7 +409,7 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-lib") { - lib_mode = true; + parse_mode.lib = true; defines_map.add("BLACKBOX", ""); continue; } @@ -402,7 +418,7 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-specify") { - specify_mode = true; + parse_mode.specify = true; continue; } if (arg == "-noopt") { @@ -432,13 +448,18 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-noautowire") { - default_nettype_wire = false; + parse_state.default_nettype_wire = false; continue; } if (arg == "-setattr" && argidx+1 < args.size()) { attributes.push_back(RTLIL::escape_id(args[++argidx])); continue; } + if (arg == "-relativeshare") { + flag_relative_share = true; + log_experimental("read_verilog -relativeshare"); + continue; + } if (arg == "-D" && argidx+1 < args.size()) { std::string name = args[++argidx], value; size_t equal = name.find('='); @@ -469,76 +490,89 @@ struct VerilogFrontend : public Frontend { break; } - if (formal_mode || !flag_nosynthesis) - defines_map.add(formal_mode ? "FORMAL" : "SYNTHESIS", "1"); + if (parse_mode.formal || !flag_nosynthesis) + defines_map.add(parse_mode.formal ? "FORMAL" : "SYNTHESIS", "1"); extra_args(f, filename, args, argidx); - log_header(design, "Executing Verilog-2005 frontend: %s\n", filename.c_str()); + log_header(design, "Executing Verilog-2005 frontend: %s\n", filename); log("Parsing %s%s input from `%s' to AST representation.\n", - formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str()); + parse_mode.formal ? "formal " : "", parse_mode.sv ? "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; + log("verilog frontend filename %s\n", filename.c_str()); + if (flag_relative_share) { + auto share_path = proc_share_dirname(); + if (filename.substr(0, share_path.length()) == share_path) + filename = std::string("+/") + filename.substr(share_path.length()); + log("new filename %s\n", filename.c_str()); + } + AST::sv_mode_but_global_and_used_for_literally_one_condition = parse_mode.sv; std::string code_after_preproc; + parse_state.lexin = f; if (!flag_nopp) { - code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, *design->verilog_defines, include_dirs); + code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, *design->verilog_defines, include_dirs, parse_state, parse_mode); 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); + log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc); + parse_state.lexin = new std::istringstream(code_after_preproc); } + auto filename_shared = std::make_shared(filename); + auto top_loc = Location(); + top_loc.begin.filename = filename_shared; + parse_state.current_ast = new AST::AstNode(top_loc, AST::AST_DESIGN); + VerilogLexer lexer(&parse_state, &parse_mode, filename_shared); + frontend_verilog_yy::parser parser(&lexer, &parse_state, &parse_mode); + lexer.set_debug(flag_debug_lexer); + parser.set_debug_level(flag_debug_parser ? 1 : 0); + // make package typedefs available to parser - add_package_types(pkg_user_types, design->verilog_packages); + add_package_types(parse_state.pkg_user_types, design->verilog_packages); UserTypeMap global_types_map; - for (auto def : design->verilog_globals) { + for (auto& def : design->verilog_globals) { if (def->type == AST::AST_TYPEDEF) { - global_types_map[def->str] = def; + global_types_map[def->str] = def.get(); } } - log_assert(user_type_stack.empty()); + log_assert(parse_state.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)); + parse_state.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()); + parse_state.user_type_stack.push_back(UserTypeMap()); - frontend_verilog_yyset_lineno(1); - frontend_verilog_yyrestart(NULL); - frontend_verilog_yyparse(); - frontend_verilog_yylex_destroy(); + if (flag_yydebug) { + lexer.set_debug(true); + parser.set_debug_level(1); + } + parser.parse(); + // frontend_verilog_yyset_lineno(1); - for (auto &child : current_ast->children) { + for (auto &child : parse_state.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); + child->attributes[attr] = AST::AstNode::mkconst_int(top_loc, 1, false); } if (flag_nodpi) - error_on_dpi_function(current_ast); + error_on_dpi_function(parse_state.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); + AST::process(design, parse_state.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, parse_mode.lib, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, parse_state.default_nettype_wire); if (!flag_nopp) - delete lexin; + delete parse_state.lexin; // only the previous and new global type maps remain - log_assert(user_type_stack.size() == 2); - user_type_stack.clear(); + log_assert(parse_state.user_type_stack.size() == 2); + parse_state.user_type_stack.clear(); - delete current_ast; - current_ast = NULL; + delete parse_state.current_ast; + parse_state.current_ast = NULL; log("Successfully finished Verilog frontend.\n"); } @@ -753,25 +787,10 @@ struct VerilogFileList : public Pass { break; } - extra_args(args, argidx, design); + extra_args(args, argidx, design, false); } } 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); -} diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index 8454e7999..83c0e37a1 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -31,70 +31,33 @@ #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; + /* Ephemeral context class */ + struct ConstParser { + AST::AstSrcLocType loc; + private: + void log_maybe_loc_error(std::string msg); + void log_maybe_loc_warn(std::string msg); + // divide an arbitrary length decimal number by two and return the rest + int my_decimal_div_by_two(std::vector &digits); + // find the number of significant bits in a binary number (not including the sign bit) + int my_ilog2(int x); + // parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?') + void my_strtobin(std::vector &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized); + public: + // convert the Verilog code for a constant to an AST node + std::unique_ptr const2ast(std::string code, char case_type = 0, bool warn_z = false); - // 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 diff --git a/frontends/verilog/verilog_lexer.h b/frontends/verilog/verilog_lexer.h new file mode 100644 index 000000000..363c17b76 --- /dev/null +++ b/frontends/verilog/verilog_lexer.h @@ -0,0 +1,48 @@ +#ifndef VERILOG_LEXER_H +#define VERILOG_LEXER_H + +#include "kernel/yosys.h" +#include "frontends/ast/ast.h" +#include "frontends/verilog/verilog_parser.tab.hh" +#include +#include + +#if ! defined(yyFlexLexerOnce) +#define yyFlexLexer frontend_verilog_yyFlexLexer +#include +#endif + +YOSYS_NAMESPACE_BEGIN + +namespace VERILOG_FRONTEND { + using parser = frontend_verilog_yy::parser; + class VerilogLexer : public frontend_verilog_yyFlexLexer { + ParseState* extra; + ParseMode* mode; + parser::location_type out_loc; + public: + VerilogLexer(ParseState* e, ParseMode* m, std::shared_ptr filename) : frontend_verilog_yyFlexLexer(e->lexin), extra(e), mode(m) { + out_loc.begin.filename = filename; + } + ~VerilogLexer() override {} + // autogenerated body due to YY_DECL + parser::symbol_type nextToken(); + // get rid of override virtual function warning + using FlexLexer::yylex; + parser::symbol_type terminate() { + return parser::make_FRONTEND_VERILOG_YYEOF(out_loc); + } + private: + std::shared_ptr current_filename; + std::vector> fn_stack; + std::vector ln_stack; + int LexerInput(char* buf, int max_size) override { + return readsome(*extra->lexin, buf, max_size); + } + }; + +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 8a3734302..62a7f7bbb 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -32,6 +32,13 @@ * */ +%option c++ +%option yyclass="VerilogLexer" +%option noyywrap +%option nounput +%option yylineno +%option prefix="frontend_verilog_yy" + %{ #ifdef __clang__ @@ -41,88 +48,234 @@ #pragma clang diagnostic ignored "-Wmisleading-indentation" #endif -#include "kernel/log.h" -#include "frontends/verilog/verilog_frontend.h" +#include "frontends/verilog/verilog_lexer.h" #include "frontends/ast/ast.h" -#include "verilog_parser.tab.hh" +#include "frontends/verilog/verilog_location.h" +#include "kernel/log.h" +#include +#include USING_YOSYS_NAMESPACE using namespace AST; using namespace VERILOG_FRONTEND; - -#define YYSTYPE FRONTEND_VERILOG_YYSTYPE -#define YYLTYPE FRONTEND_VERILOG_YYLTYPE +using parser = frontend_verilog_yy::parser; YOSYS_NAMESPACE_BEGIN -namespace VERILOG_FRONTEND { - std::vector fn_stack; - std::vector ln_stack; - YYLTYPE real_location; - YYLTYPE old_location; -} +#undef YY_DECL +#define YY_DECL parser::symbol_type VerilogLexer::nextToken() + +#undef yyterminate +#define yyterminate() return terminate() + YOSYS_NAMESPACE_END #define SV_KEYWORD(_tok) \ - if (sv_mode) return _tok; \ + if (mode->sv) 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; + "recognized unless read_verilog is called with -sv!\n", YYText(), \ + current_filename->c_str(), yylineno); \ + string_t val = std::make_unique(std::string("\\") + YYText()); \ + return parser::make_TOK_ID(std::move(val), out_loc); #define NON_KEYWORD() \ - yylval->string = new std::string(std::string("\\") + yytext); \ - return TOK_ID; + string_t val = std::make_unique(std::string("\\") + YYText()); \ + return parser::make_TOK_ID(std::move(val), out_loc); -#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; + out_loc.step(); \ + for(int i = 0; YYText()[i] != '\0'; ++i){ \ + if(YYText()[i] == '\n') { \ + out_loc.lines(); \ + } \ + else { \ + out_loc.columns(); \ + } \ + } \ + out_loc.begin.filename = current_filename; \ + out_loc.end.filename = current_filename; #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) +static bool isUserType(ParseState* extra, 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) { + for (auto it = extra->user_type_stack.rbegin(); it != extra->user_type_stack.rend(); ++it) { if (it->count(s) > 0) { - return true; + return true; } } return false; } +parser::symbol_type char_tok(char c, parser::location_type loc) { + switch (c) { + case '!': return parser::make_TOK_EXCL(loc); + case '#': return parser::make_TOK_HASH(loc); + case '%': return parser::make_TOK_PERC(loc); + case '&': return parser::make_TOK_AMP(loc); + case '(': return parser::make_TOK_LPAREN(loc); + case ')': return parser::make_TOK_RPAREN(loc); + case '*': return parser::make_TOK_ASTER(loc); + case '+': return parser::make_TOK_PLUS(loc); + case ',': return parser::make_TOK_COMMA(loc); + case '-': return parser::make_TOK_MINUS(loc); + case '.': return parser::make_TOK_DOT(loc); + case '/': return parser::make_TOK_SLASH(loc); + case ':': return parser::make_TOK_COL(loc); + case ';': return parser::make_TOK_SEMICOL(loc); + case '<': return parser::make_TOK_LT(loc); + case '=': return parser::make_TOK_EQ(loc); + case '>': return parser::make_TOK_GT(loc); + case '?': return parser::make_TOK_QUE(loc); + case '@': return parser::make_TOK_AT(loc); + case '[': return parser::make_TOK_LBRA(loc); + case ']': return parser::make_TOK_RBRA(loc); + case '^': return parser::make_TOK_CARET(loc); + case '_': return parser::make_TOK_UNDER(loc); + case '{': return parser::make_TOK_LCURL(loc); + case '|': return parser::make_TOK_PIPE(loc); + case '}': return parser::make_TOK_RCURL(loc); + case '~': return parser::make_TOK_TILDE(loc); + case 'n': return parser::make_TOK_n(loc); + case 'p': return parser::make_TOK_p(loc); + case 'x': return parser::make_TOK_x(loc); + case 'z': return parser::make_TOK_z(loc); + case 0: return parser::make_FRONTEND_VERILOG_YYEOF(loc); + default: + return parser::make_ch_t(c, loc); + } +} +static bool is_hex_dig(char c, int *val, parser::location_type loc) +{ + if ('0' <= c && c <= '9') { + *val = c - '0'; + return true; + } else if ('a' <= c && c <= 'f') { + *val = c - 'a' + 0xA; + return true; + } else if ('A' <= c && c <= 'F') { + *val = c - 'A' + 0xA; + return true; + } else if (c == 'x' || c == 'X' || c == 'z' || c == 'Z' || c == '?') { + log_file_warning(*loc.begin.filename, loc.begin.line, "'%c' not a valid digit in hex escape sequence.\n", c); + *val = 0; // not semantically valid in hex escape... + return true; // ...but still processed as part of hex token + } + + return false; +} + +static bool is_oct_dig(char c, int *val, parser::location_type loc) +{ + if ('0' <= c && c <= '7') { + *val = c - '0'; + return true; + } else if (c == 'x' || c == 'X' || c == 'z' || c == 'Z' || c == '?') { + log_file_warning(*loc.begin.filename, loc.begin.line, "'%c' not a valid digit in octal escape sequence.\n", c); + *val = 0; // not semantically valid in octal escape... + return true; // ...but still processed as part of octal token + } + + return false; +} + +static parser::symbol_type process_str(char *str, int len, bool triple, parser::location_type loc) +{ + char *in, *out; // Overwrite input buffer: flex manual states "Actions + // are free to modify 'yytext' except for lengthening it". + + for (in = str, out = str; in < str + len; in++) + switch (*in) { + case '\n': + case '\r': + if (in + 1 < str + len && (in[1] ^ *in) == ('\n' ^ '\r')) + in++; + if (!triple) + log_file_warning(*loc.begin.filename, loc.begin.line, "Multi-line string literals should be triple-quoted or escaped.\n"); + *out++ = '\n'; + break; + case '\\': + in++; + log_assert(in < str + len); + switch (*in) { + case 'a': + *out++ = '\a'; + break; + case 'f': + *out++ = '\f'; + break; + case 'n': + *out++ = '\n'; + break; + case 'r': /* not part of IEEE-1800 2023, but seems + like a good idea to support it anyway */ + *out++ = '\r'; + break; + case 't': + *out++ = '\t'; + break; + case 'v': + *out++ = '\v'; + break; + case 'x': + int val; + if (in + 1 < str + len && is_hex_dig(in[1], &val, loc)) { + *out = val; + in++; + if (in + 1 < str + len && is_hex_dig(in[1], &val, loc)) { + *out = *out * 0x10 + val; + in++; + } + out++; + } else + log_file_warning(*loc.begin.filename, loc.begin.line, "ignoring invalid hex escape.\n"); + break; + case '\\': + *out++ = '\\'; + break; + case '"': + *out++ = '"'; + break; + case '\n': + case '\r': + if (in + 1 < str + len && (in[1] ^ *in) == ('\n' ^ '\r')) + in++; + break; + default: + if ('0' <= *in && *in <= '7') { + int val; + + *out = *in - '0'; + if (in + 1 < str + len && is_oct_dig(in[1], &val, loc)) { + *out = *out * 010 + val; + in++; + if (in + 1 < str + len && is_oct_dig(in[1], &val, loc)) { + if (*out >= 040) + log_file_warning(*loc.begin.filename, loc.begin.line, "octal escape exceeds \\377\n"); + *out = *out * 010 + val; + in++; + } + } + out++; + } else + *out++ = *in; + } + break; + default: + *out++ = *in; + } + + return parser::make_TOK_STRING(std::make_unique(str, out - str), loc); +} + %} -%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 @@ -134,47 +287,46 @@ 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; + ln_stack.push_back(yylineno); + std::string filename = YYText()+11; + if (!filename.empty() && filename.front() == '"') + filename = filename.substr(1); + if (!filename.empty() && filename.back() == '"') + filename = filename.substr(0, filename.size()-1); + current_filename = std::make_shared(filename); + yylineno = (0); + out_loc.begin.line = out_loc.end.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(); + yylineno = (ln_stack.back()); + out_loc.begin.line = out_loc.end.line = ln_stack.back(); ln_stack.pop_back(); } "`line"[ \t]+[^ \t\r\n]+[ \t]+\"[^ \r\n]+\"[^\r\n]*\n { - char *p = yytext + 5; + const 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); + yylineno = (atoi(p)); + out_loc.begin.line = out_loc.end.line = atoi(p); while (*p && *p != ' ' && *p != '\t') p++; while (*p == ' ' || *p == '\t') p++; - char *q = *p ? p + 1 : p; + const char *q = *p ? p + 1 : p; while (*q && *q != '"') q++; - current_filename = std::string(p).substr(1, q-p-1); + current_filename = std::make_shared(std::string(p).substr(1, q-p-1)); } "`file_notfound "[^\n]* { - log_error("Can't open include file `%s'!\n", yytext + 15); + log_error("Can't open include file `%s'!\n", YYText() + 15); } "`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */ @@ -183,261 +335,224 @@ TIME_SCALE_SUFFIX [munpf]?s "`endcelldefine"[^\n]* /* ignore `endcelldefine */ "`default_nettype"[ \t]+[^ \t\r\n/]+ { - char *p = yytext; + const 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; + extra->default_nettype_wire = false; else if (!strcmp(p, "wire")) - VERILOG_FRONTEND::default_nettype_wire = true; + extra->default_nettype_wire = true; else - frontend_verilog_yyerror("Unsupported default nettype: %s", p); + err_at_loc(out_loc, "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); + err_at_loc(out_loc, "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; } +"module" { return parser::make_TOK_MODULE(out_loc); } +"endmodule" { return parser::make_TOK_ENDMODULE(out_loc); } +"function" { return parser::make_TOK_FUNCTION(out_loc); } +"endfunction" { return parser::make_TOK_ENDFUNCTION(out_loc); } +"task" { return parser::make_TOK_TASK(out_loc); } +"endtask" { return parser::make_TOK_ENDTASK(out_loc); } +"specify" { return mode->specify ? parser::make_TOK_SPECIFY(out_loc) : parser::make_TOK_IGNORED_SPECIFY(out_loc); } +"endspecify" { return parser::make_TOK_ENDSPECIFY(out_loc); } +"specparam" { return parser::make_TOK_SPECPARAM(out_loc); } +"package" { SV_KEYWORD(parser::make_TOK_PACKAGE(out_loc)); } +"endpackage" { SV_KEYWORD(parser::make_TOK_ENDPACKAGE(out_loc)); } +"import" { SV_KEYWORD(parser::make_TOK_IMPORT(out_loc)); } +"interface" { SV_KEYWORD(parser::make_TOK_INTERFACE(out_loc)); } +"endinterface" { SV_KEYWORD(parser::make_TOK_ENDINTERFACE(out_loc)); } +"modport" { SV_KEYWORD(parser::make_TOK_MODPORT(out_loc)); } +"parameter" { return parser::make_TOK_PARAMETER(out_loc); } +"localparam" { return parser::make_TOK_LOCALPARAM(out_loc); } +"defparam" { return parser::make_TOK_DEFPARAM(out_loc); } +"assign" { return parser::make_TOK_ASSIGN(out_loc); } +"always" { return parser::make_TOK_ALWAYS(out_loc); } +"initial" { return parser::make_TOK_INITIAL(out_loc); } +"begin" { return parser::make_TOK_BEGIN(out_loc); } +"end" { return parser::make_TOK_END(out_loc); } +"if" { return parser::make_TOK_IF(out_loc); } +"ifnone" { return parser::make_TOK_IFNONE(out_loc); } +"else" { return parser::make_TOK_ELSE(out_loc); } +"for" { return parser::make_TOK_FOR(out_loc); } +"posedge" { return parser::make_TOK_POSEDGE(out_loc); } +"negedge" { return parser::make_TOK_NEGEDGE(out_loc); } +"or" { return parser::make_TOK_OR(out_loc); } +"case" { return parser::make_TOK_CASE(out_loc); } +"casex" { return parser::make_TOK_CASEX(out_loc); } +"casez" { return parser::make_TOK_CASEZ(out_loc); } +"endcase" { return parser::make_TOK_ENDCASE(out_loc); } +"default" { return parser::make_TOK_DEFAULT(out_loc); } +"generate" { return parser::make_TOK_GENERATE(out_loc); } +"endgenerate" { return parser::make_TOK_ENDGENERATE(out_loc); } +"while" { return parser::make_TOK_WHILE(out_loc); } +"repeat" { return parser::make_TOK_REPEAT(out_loc); } +"automatic" { return parser::make_TOK_AUTOMATIC(out_loc); } -"unique" { SV_KEYWORD(TOK_UNIQUE); } -"unique0" { SV_KEYWORD(TOK_UNIQUE0); } -"priority" { SV_KEYWORD(TOK_PRIORITY); } +"unique" { SV_KEYWORD(parser::make_TOK_UNIQUE(out_loc)); } +"unique0" { SV_KEYWORD(parser::make_TOK_UNIQUE0(out_loc)); } +"priority" { SV_KEYWORD(parser::make_TOK_PRIORITY(out_loc)); } -"always_comb" { SV_KEYWORD(TOK_ALWAYS_COMB); } -"always_ff" { SV_KEYWORD(TOK_ALWAYS_FF); } -"always_latch" { SV_KEYWORD(TOK_ALWAYS_LATCH); } +"always_comb" { SV_KEYWORD(parser::make_TOK_ALWAYS_COMB(out_loc)); } +"always_ff" { SV_KEYWORD(parser::make_TOK_ALWAYS_FF(out_loc)); } +"always_latch" { SV_KEYWORD(parser::make_TOK_ALWAYS_LATCH(out_loc)); } /* 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; + if (!strcmp(YYText(), "default")) + return parser::make_TOK_DEFAULT(out_loc); + string_t val = std::make_unique(std::string("\\") + YYText()); + return parser::make_TOK_SVA_LABEL(std::move(val), out_loc); } -"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); } +"assert" { if (mode->formal) return parser::make_TOK_ASSERT(out_loc); SV_KEYWORD(parser::make_TOK_ASSERT(out_loc)); } +"assume" { if (mode->formal) return parser::make_TOK_ASSUME(out_loc); SV_KEYWORD(parser::make_TOK_ASSUME(out_loc)); } +"cover" { if (mode->formal) return parser::make_TOK_COVER(out_loc); SV_KEYWORD(parser::make_TOK_COVER(out_loc)); } +"restrict" { if (mode->formal) return parser::make_TOK_RESTRICT(out_loc); SV_KEYWORD(parser::make_TOK_RESTRICT(out_loc)); } +"property" { if (mode->formal) return parser::make_TOK_PROPERTY(out_loc); SV_KEYWORD(parser::make_TOK_PROPERTY(out_loc)); } +"rand" { if (mode->formal) return parser::make_TOK_RAND(out_loc); SV_KEYWORD(parser::make_TOK_RAND(out_loc)); } +"const" { if (mode->formal) return parser::make_TOK_CONST(out_loc); SV_KEYWORD(parser::make_TOK_CONST(out_loc)); } +"checker" { if (mode->formal) return parser::make_TOK_CHECKER(out_loc); SV_KEYWORD(parser::make_TOK_CHECKER(out_loc)); } +"endchecker" { if (mode->formal) return parser::make_TOK_ENDCHECKER(out_loc); SV_KEYWORD(parser::make_TOK_ENDCHECKER(out_loc)); } +"bind" { if (mode->formal) return parser::make_TOK_BIND(out_loc); SV_KEYWORD(parser::make_TOK_BIND(out_loc)); } +"final" { SV_KEYWORD(parser::make_TOK_FINAL(out_loc)); } +"logic" { SV_KEYWORD(parser::make_TOK_LOGIC(out_loc)); } +"var" { SV_KEYWORD(parser::make_TOK_VAR(out_loc)); } +"bit" { SV_KEYWORD(parser::make_TOK_LOGIC(out_loc)); } +"int" { SV_KEYWORD(parser::make_TOK_INT(out_loc)); } +"byte" { SV_KEYWORD(parser::make_TOK_BYTE(out_loc)); } +"shortint" { SV_KEYWORD(parser::make_TOK_SHORTINT(out_loc)); } +"longint" { SV_KEYWORD(parser::make_TOK_LONGINT(out_loc)); } +"void" { SV_KEYWORD(parser::make_TOK_VOID(out_loc)); } -"eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } -"s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } +"eventually" { if (mode->formal) return parser::make_TOK_EVENTUALLY(out_loc); SV_KEYWORD(parser::make_TOK_EVENTUALLY(out_loc)); } +"s_eventually" { if (mode->formal) return parser::make_TOK_EVENTUALLY(out_loc); SV_KEYWORD(parser::make_TOK_EVENTUALLY(out_loc)); } -"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; } +"input" { return parser::make_TOK_INPUT(out_loc); } +"output" { return parser::make_TOK_OUTPUT(out_loc); } +"inout" { return parser::make_TOK_INOUT(out_loc); } +"wire" { return parser::make_TOK_WIRE(out_loc); } +"tri" { return parser::make_TOK_WIRE(out_loc); } +"wor" { return parser::make_TOK_WOR(out_loc); } +"trior" { return parser::make_TOK_WOR(out_loc); } +"wand" { return parser::make_TOK_WAND(out_loc); } +"triand" { return parser::make_TOK_WAND(out_loc); } +"reg" { return parser::make_TOK_REG(out_loc); } +"integer" { return parser::make_TOK_INTEGER(out_loc); } +"signed" { return parser::make_TOK_SIGNED(out_loc); } +"unsigned" { SV_KEYWORD(parser::make_TOK_UNSIGNED(out_loc)); } +"genvar" { return parser::make_TOK_GENVAR(out_loc); } +"real" { return parser::make_TOK_REAL(out_loc); } -"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); } +"enum" { SV_KEYWORD(parser::make_TOK_ENUM(out_loc)); } +"typedef" { SV_KEYWORD(parser::make_TOK_TYPEDEF(out_loc)); } +"struct" { SV_KEYWORD(parser::make_TOK_STRUCT(out_loc)); } +"union" { SV_KEYWORD(parser::make_TOK_UNION(out_loc)); } +"packed" { SV_KEYWORD(parser::make_TOK_PACKED(out_loc)); } {UNSIGNED_NUMBER} { - yylval->string = new std::string(yytext); - return TOK_CONSTVAL; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_CONSTVAL(std::move(val), out_loc); } \'[01zxZX] { - yylval->string = new std::string(yytext); - return TOK_UNBASED_UNSIZED_CONSTVAL; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_UNBASED_UNSIZED_CONSTVAL(std::move(val), out_loc); } \'[sS]?[bodhBODH] { BEGIN(BASED_CONST); - yylval->string = new std::string(yytext); - return TOK_BASE; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_BASE(std::move(val), out_loc); } [0-9a-fA-FzxZX?][0-9a-fA-FzxZX?_]* { BEGIN(0); - yylval->string = new std::string(yytext); - return TOK_BASED_CONSTVAL; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_BASED_CONSTVAL(std::move(val), out_loc); } {FIXED_POINT_NUMBER_DEC} { - yylval->string = new std::string(yytext); - return TOK_REALVAL; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_REALVAL(std::move(val), out_loc); } {FIXED_POINT_NUMBER_NO_DEC} { - yylval->string = new std::string(yytext); - return TOK_REALVAL; + string_t val = std::make_unique(YYText()); + return parser::make_TOK_REALVAL(std::move(val), out_loc); } -\" { 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; } +\"([^\\"]|\\.|\\\n)*\" { return process_str(yytext + 1, yyleng - 2, false, out_loc); } -and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 { - yylval->string = new std::string(yytext); - return TOK_PRIMITIVE; +\"{3}(\"{0,2}([^\\"]|\\.|\\\n))*\"{3} { return process_str(yytext + 3, yyleng - 6, true, out_loc); } + +and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1|tran { + auto val = std::make_unique(YYText()); + return parser::make_TOK_PRIMITIVE(std::move(val), out_loc); } -supply0 { return TOK_SUPPLY0; } -supply1 { return TOK_SUPPLY1; } +supply0 { return parser::make_TOK_SUPPLY0(out_loc); } +supply1 { return parser::make_TOK_SUPPLY1(out_loc); } "$"(display[bho]?|write[bho]?|strobe|monitor|time|realtime|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { - yylval->string = new std::string(yytext); - return TOK_ID; + auto val = std::make_unique(YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } "$"(setup|hold|setuphold|removal|recovery|recrem|skew|timeskew|fullskew|nochange) { - if (!specify_mode) REJECT; - yylval->string = new std::string(yytext); - return TOK_ID; + if (!mode->specify) REJECT; + auto val = std::make_unique(YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } "$"(info|warning|error|fatal) { - yylval->string = new std::string(yytext); - return TOK_MSG_TASKS; + auto val = std::make_unique(YYText()); + return parser::make_TOK_MSG_TASKS(std::move(val), out_loc); } -"$signed" { return TOK_TO_SIGNED; } -"$unsigned" { return TOK_TO_UNSIGNED; } +"$signed" { return parser::make_TOK_TO_SIGNED(out_loc); } +"$unsigned" { return parser::make_TOK_TO_UNSIGNED(out_loc); } [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) { + auto s = std::string("\\") + YYText(); + if (extra->pkg_user_types.count(s) > 0) { // package qualified typedefed name - yylval->string = new std::string(s); - return TOK_PKG_USER_TYPE; + auto val = std::make_unique(s); + return parser::make_TOK_PKG_USER_TYPE(std::move(val), out_loc); } else { // backup before :: just return first part - size_t len = strchr(yytext, ':') - yytext; + size_t len = strchr(YYText(), ':') - YYText(); yyless(len); - yylval->string = new std::string(std::string("\\") + yytext); - return TOK_ID; + auto val = std::make_unique(std::string("\\") + YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } } [a-zA-Z_$][a-zA-Z0-9_$]* { - auto s = std::string("\\") + yytext; - if (isUserType(s)) { + auto s = std::string("\\") + YYText(); + if (isUserType(extra, s)) { // previously typedefed name - yylval->string = new std::string(s); - return TOK_USER_TYPE; + auto val = std::make_unique(s); + return parser::make_TOK_USER_TYPE(std::move(val), out_loc); } else { - yylval->string = new std::string(std::string("\\") + yytext); - return TOK_ID; + auto val = std::make_unique(std::string("\\") + YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } } [a-zA-Z_$][a-zA-Z0-9_$\.]* { - yylval->string = new std::string(std::string("\\") + yytext); - return TOK_ID; + auto val = std::make_unique(std::string("\\") + YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } "/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" { @@ -473,7 +588,7 @@ supply1 { return TOK_SUPPLY1; } ); printed_warning = true; } - return TOK_SYNOPSYS_FULL_CASE; + return parser::make_TOK_SYNOPSYS_FULL_CASE(out_loc); } parallel_case { static bool printed_warning = false; @@ -487,119 +602,115 @@ supply1 { return TOK_SUPPLY1; } ); printed_warning = true; } - return TOK_SYNOPSYS_PARALLEL_CASE; + return parser::make_TOK_SYNOPSYS_PARALLEL_CASE(out_loc); } . /* 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; + return parser::make_TOK_DPI_FUNCTION(out_loc); } [a-zA-Z_$][a-zA-Z0-9_$]* { - yylval->string = new std::string(std::string("\\") + yytext); - return TOK_ID; + auto val = std::make_unique(std::string("\\") + YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } [ \t\r\n] /* ignore whitespaces */ ";" { BEGIN(0); - return *yytext; + return char_tok(*YYText(), out_loc); } . { - return *yytext; + return char_tok(*YYText(), out_loc); } "\\"[^ \t\r\n]+ { - yylval->string = new std::string(yytext); - return TOK_ID; + auto val = std::make_unique(YYText()); + return parser::make_TOK_ID(std::move(val), out_loc); } -"(*" { return ATTR_BEGIN; } -"*)" { return ATTR_END; } +"(*" { return parser::make_ATTR_BEGIN(out_loc); } +"*)" { return parser::make_ATTR_END(out_loc); } -"{*" { return DEFATTR_BEGIN; } -"*}" { return DEFATTR_END; } +"{*" { return parser::make_DEFATTR_BEGIN(out_loc); } +"*}" { return parser::make_DEFATTR_END(out_loc); } -"**" { return OP_POW; } -"||" { return OP_LOR; } -"&&" { return OP_LAND; } -"==" { return OP_EQ; } -"!=" { return OP_NE; } -"<=" { return OP_LE; } -">=" { return OP_GE; } +"**" { return parser::make_OP_POW(out_loc); } +"||" { return parser::make_OP_LOR(out_loc); } +"&&" { return parser::make_OP_LAND(out_loc); } +"==" { return parser::make_OP_EQ(out_loc); } +"!=" { return parser::make_OP_NE(out_loc); } +"<=" { return parser::make_OP_LE(out_loc); } +">=" { return parser::make_OP_GE(out_loc); } -"===" { return OP_EQX; } -"!==" { return OP_NEX; } +"===" { return parser::make_OP_EQX(out_loc); } +"!==" { return parser::make_OP_NEX(out_loc); } -"~&" { return OP_NAND; } -"~|" { return OP_NOR; } -"~^" { return OP_XNOR; } -"^~" { return OP_XNOR; } +"~&" { return parser::make_OP_NAND(out_loc); } +"~|" { return parser::make_OP_NOR(out_loc); } +"~^" { return parser::make_OP_XNOR(out_loc); } +"^~" { return parser::make_OP_XNOR(out_loc); } -"<<" { return OP_SHL; } -">>" { return OP_SHR; } -"<<<" { return OP_SSHL; } -">>>" { return OP_SSHR; } +"<<" { return parser::make_OP_SHL(out_loc); } +">>" { return parser::make_OP_SHR(out_loc); } +"<<<" { return parser::make_OP_SSHL(out_loc); } +">>>" { return parser::make_OP_SSHR(out_loc); } -"'" { return OP_CAST; } +"'" { return parser::make_OP_CAST(out_loc); } -"::" { return TOK_PACKAGESEP; } -"++" { return TOK_INCREMENT; } -"--" { return TOK_DECREMENT; } +"::" { return parser::make_TOK_PACKAGESEP(out_loc); } +"++" { return parser::make_TOK_INCREMENT(out_loc); } +"--" { return parser::make_TOK_DECREMENT(out_loc); } -"+:" { return TOK_POS_INDEXED; } -"-:" { return TOK_NEG_INDEXED; } +"+:" { return parser::make_TOK_POS_INDEXED(out_loc); } +"-:" { return parser::make_TOK_NEG_INDEXED(out_loc); } -".*" { return TOK_WILDCARD_CONNECT; } +".*" { return parser::make_TOK_WILDCARD_CONNECT(out_loc); } -"|=" { 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); } +"|=" { SV_KEYWORD(parser::make_TOK_BIT_OR_ASSIGN(out_loc)); } +"&=" { SV_KEYWORD(parser::make_TOK_BIT_AND_ASSIGN(out_loc)); } +"+=" { SV_KEYWORD(parser::make_TOK_ADD_ASSIGN(out_loc)); } +"-=" { SV_KEYWORD(parser::make_TOK_SUB_ASSIGN(out_loc)); } +"^=" { SV_KEYWORD(parser::make_TOK_BIT_XOR_ASSIGN(out_loc)); } +"/=" { SV_KEYWORD(parser::make_TOK_DIV_ASSIGN(out_loc)); } +"%=" { SV_KEYWORD(parser::make_TOK_MOD_ASSIGN(out_loc)); } +"*=" { SV_KEYWORD(parser::make_TOK_MUL_ASSIGN(out_loc)); } +"<<=" { SV_KEYWORD(parser::make_TOK_SHL_ASSIGN(out_loc)); } +">>=" { SV_KEYWORD(parser::make_TOK_SHR_ASSIGN(out_loc)); } +"<<<=" { SV_KEYWORD(parser::make_TOK_SSHL_ASSIGN(out_loc)); } +">>>=" { SV_KEYWORD(parser::make_TOK_SSHR_ASSIGN(out_loc)); } [-+]?[=*]> { - if (!specify_mode) REJECT; - yylval->string = new std::string(yytext); - return TOK_SPECIFY_OPER; + if (!mode->specify) REJECT; + auto val = std::make_unique(YYText()); + return parser::make_TOK_SPECIFY_OPER(std::move(val), out_loc); } "&&&" { - if (!specify_mode) return TOK_IGNORED_SPECIFY_AND; - return TOK_SPECIFY_AND; + if (!mode->specify) return parser::make_TOK_IGNORED_SPECIFY_AND(out_loc); + return parser::make_TOK_SPECIFY_AND(out_loc); } -{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; } +{UNSIGNED_NUMBER}{TIME_SCALE_SUFFIX} { return parser::make_TOK_TIME_SCALE(out_loc); } +{FIXED_POINT_NUMBER_DEC}{TIME_SCALE_SUFFIX} { return parser::make_TOK_TIME_SCALE(out_loc); } +{FIXED_POINT_NUMBER_NO_DEC}{TIME_SCALE_SUFFIX} { return parser::make_TOK_TIME_SCALE(out_loc); } "/*" { 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; } +. { return char_tok(*YYText(), out_loc); } +<*>. { BEGIN(0); return char_tok(*YYText(), out_loc); } %% -// this is a hack to avoid the 'yyinput defined but not used' error msgs -void *frontend_verilog_avoid_input_warnings() { - return (void*)&yyinput; -} - diff --git a/frontends/verilog/verilog_location.h b/frontends/verilog/verilog_location.h new file mode 100644 index 000000000..8ffe0552d --- /dev/null +++ b/frontends/verilog/verilog_location.h @@ -0,0 +1,97 @@ +#ifndef VERILOG_LOCATION_H +#define VERILOG_LOCATION_H + +#include +#include +#include +#include +#include + +/** + * Provide frontend-wide location tracking like what bison generates + * but using shared_ptr for filename + */ + +struct Position { + std::shared_ptr filename; + int line; + int column; + + Position(std::shared_ptr filename, int line = 1, int column = 1) + : filename(filename), line(line), column(column) {} + Position() = default; + Position(const Position& other) = default; + Position& operator=(const Position& other) = default; + + void advance() { ++column; } + void columns(int count = 1) { + column += count; + } + + void lines(int count = 1) { + line += count; + column = 1; + } + std::string to_string() const { + std::ostringstream oss; + if (filename && !filename->empty()) { + oss << *filename << ":"; + } + oss << line << ":" << column; + return oss.str(); + } +}; + +struct Location { + Position begin; + Position end; + + Location() = default; + Location(const Position& b, const Position& e) + : begin(b), end(e) {} + Location(const Location& other) = default; + Location& operator=(const Location& other) = default; + + void step() { begin = end; } + + void columns(int count = 1) { + end.columns(count); + } + + void lines(int count = 1) { + end.lines(count); + } + std::string to_string() const { + std::ostringstream oss; + bool same_file = (!begin.filename && !end.filename) || + (begin.filename && end.filename && + *begin.filename == *end.filename); + + if (same_file) { + if (begin.filename && !begin.filename->empty()) + oss << *begin.filename << ":"; + + if (begin.line == end.line) { + if (begin.column == end.column) { + oss << begin.line << ":" << begin.column; + } else { + oss << begin.line << ":" << begin.column + << "-" << end.column; + } + } else { + oss << begin.line << ":" << begin.column + << "-" << end.line << ":" << end.column; + } + } else { + oss << begin.to_string() << "-" << end.to_string(); + } + + return oss.str(); + } +}; + +static inline std::ostream& operator<<(std::ostream& os, const Location& loc) { + return os << loc.to_string(); +} + +#endif diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 9d0956c8e..684727d5b 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -33,366 +33,460 @@ * */ -%require "3.0" +%require "3.6" +%language "c++" +%define api.value.type variant +%define api.prefix {frontend_verilog_yy} +%define api.token.constructor +%define api.location.type {Location} -%{ -#include -#include -#include -#include "frontends/verilog/verilog_frontend.h" -#include "frontends/verilog/verilog_parser.tab.hh" -#include "kernel/log.h" +%param { YOSYS_NAMESPACE_PREFIX VERILOG_FRONTEND::VerilogLexer* lexer } +%parse-param { YOSYS_NAMESPACE_PREFIX VERILOG_FRONTEND::ParseState* extra } +%parse-param { YOSYS_NAMESPACE_PREFIX VERILOG_FRONTEND::ParseMode* mode } -#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; +%code requires { + #include "kernel/yosys_common.h" + #include "frontends/verilog/verilog_error.h" + #include "frontends/verilog/verilog_location.h" + YOSYS_NAMESPACE_BEGIN + namespace VERILOG_FRONTEND { + struct ParseState; + struct ParseMode; + class VerilogLexer; + }; + YOSYS_NAMESPACE_END } -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) +%code provides { + USING_YOSYS_NAMESPACE; + using namespace AST; + using namespace VERILOG_FRONTEND; + using parser = frontend_verilog_yy::parser; + YOSYS_NAMESPACE_BEGIN + namespace VERILOG_FRONTEND { + typedef std::map UserTypeMap; + struct ParseState { + int port_counter; + dict port_stubs; + std::unique_ptr>> attr_list; + dict> default_attr_list; + std::stack>>> attr_list_stack; + std::unique_ptr>> albuf; + std::vector user_type_stack; + dict pkg_user_types; + std::vector ast_stack; + std::unique_ptr astbuf1, astbuf2, astbuf3; + AstNode* cell_hack; + AstNode* member_hack; + 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 current_wire_rand, current_wire_const; + bool current_modport_input, current_modport_output; + bool default_nettype_wire = true; + std::istream* lexin; -#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) + AstNode* saveChild(std::unique_ptr child); + AstNode* pushChild(std::unique_ptr child); + void addWiretypeNode(std::string *name, AstNode* node); + void addTypedefNode(std::string *name, std::unique_ptr node); + void enterTypeScope(); + void exitTypeScope(); + bool isInLocalScope(const std::string *name); + void rewriteGenForDeclInit(AstNode *loop); + void ensureAsgnExprAllowed(const parser::location_type loc, bool sv_mode); + const AstNode *addIncOrDecStmt(std::unique_ptr>> stmt_attr, + std::unique_ptr lhs, + std::unique_ptr>> op_attr, AST::AstNodeType op, + parser::location_type loc); + std::unique_ptr addIncOrDecExpr(std::unique_ptr lhs, + std::unique_ptr>> attr, + AST::AstNodeType op, parser::location_type loc, bool undo, bool sv_mode); + // add a binary operator assignment statement, e.g., a += b + std::unique_ptr addAsgnBinopStmt(std::unique_ptr>> attr, + std::unique_ptr eq_lhs, AST::AstNodeType op, std::unique_ptr rhs); + }; + struct ParseMode { + bool noassert = false; + bool noassume = false; + bool norestrict = false; + bool sv = false; + bool formal = false; + bool lib = false; + bool specify = false; + bool assume_asserts = false; + bool assert_assumes = false; + }; + }; + YOSYS_NAMESPACE_END +} -int frontend_verilog_yylex(YYSTYPE *yylval_param, YYLTYPE *yyloc_param); +%code { + #include + #include + #include + #include + #include "kernel/log.h" + #include "frontends/verilog/verilog_lexer.h" -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; + USING_YOSYS_NAMESPACE + using namespace AST; + using namespace VERILOG_FRONTEND; + + // Silly little C adapter between C++ bison and C++ flex + auto frontend_verilog_yylex(YOSYS_NAMESPACE_PREFIX VERILOG_FRONTEND::VerilogLexer* lexer) { + return lexer->nextToken(); } - 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(); - } -} + #define SET_AST_NODE_LOC(WHICH, BEGIN, END) (WHICH)->location = location_range(BEGIN, END) -static void free_attr(dict *al) -{ - for (auto &it : *al) - delete it.second; - delete al; -} + #define SET_RULE_LOC(LHS, BEGIN, END) \ + do { (LHS).begin = BEGIN.begin; \ + (LHS).end = (END).end; } while(0) -struct specify_target { - char polarity_op; - AstNode *dst, *dat; -}; + YOSYS_NAMESPACE_BEGIN + namespace VERILOG_FRONTEND { -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."); + static Location location_range(Location begin, Location end) { + return Location(begin.begin, end.end); } - 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; + static void append_attr(AstNode *ast, std::unique_ptr>> al) + { + for (auto &it : *al) { + ast->attributes[it.first] = std::move(it.second); } } - if (!valid) - frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form [:]"); - } - return range_node; -} + static void append_attr_clone(AstNode *ast, std::unique_ptr>> &al) + { + for (auto &it : *al) { + ast->attributes[it.first] = it.second->clone(); + } + } -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 std::unique_ptr makeRange(parser::location_type loc, int msb = 31, int lsb = 0, bool isSigned = true) + { + auto range = std::make_unique(loc, AST_RANGE); + range->children.push_back(AstNode::mkconst_int(loc, msb, true)); + range->children.push_back(AstNode::mkconst_int(loc, lsb, true)); + range->is_signed = isSigned; + return range; + } -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 addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = true) + { + auto range = makeRange(parent->location, msb, lsb, isSigned); + parent->children.push_back(std::move(range)); + } -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); -} + static std::unique_ptr checkRange(AstNode *type_node, std::unique_ptr range_node) + { + if (type_node->range_left >= 0 && type_node->range_right >= 0) { + // type already restricts the range + if (range_node) { + err_at_loc(type_node->location, "integer/genvar types cannot have packed dimensions."); + } + else { + range_node = makeRange(type_node->location, type_node->range_left, type_node->range_right, false); + } + } -// 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); + 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) + err_at_loc(type_node->location, "wire/reg/logic packed dimension must be of the form [:]"); + } - // 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); + return range_node; + } - // 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()); + 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(std::make_unique(rangeNode->location, AST_SUB, std::move(rangeNode->children[0]), AstNode::mkconst_int(rangeNode->location, 1, true))); + rangeNode->children[0] = AstNode::mkconst_int(rangeNode->location, 0, false); + } + } - // 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); + static void rewriteAsMemoryNode(AstNode *node, std::unique_ptr rangeNode) + { + node->type = AST_MEMORY; + if (rangeNode->type == AST_MULTIRANGE) { + for (auto& child : rangeNode->children) + rewriteRange(child.get()); + } else + rewriteRange(rangeNode.get()); + node->children.push_back(std::move(rangeNode)); + } - // 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); + static void checkLabelsMatch(const frontend_verilog_yy::parser::location_type& loc, const char *element, const std::string* before, const std::string *after) + { + if (!before && after) + err_at_loc(loc, "%s missing where end label (%s) was given.", + element, after->c_str() + 1); + if (before && after && *before != *after) + err_at_loc(loc, "%s (%s) and end label (%s) don't match.", + element, before->c_str() + 1, after->c_str() + 1); + } - body->children.insert(body->children.begin(), indirect); + AstNode* ParseState::saveChild(std::unique_ptr child) { + auto* child_leaky = child.get(); + ast_stack.back()->children.push_back(std::move(child)); + return child_leaky; + } + AstNode* ParseState::pushChild(std::unique_ptr child) { + auto* child_leaky = saveChild(std::move(child)); + ast_stack.push_back(child_leaky); + return child_leaky; + } - // 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); + void ParseState::addWiretypeNode(std::string *name, AstNode* node) + { + log_assert(node); + node->is_custom_type = true; + node->children.push_back(std::make_unique(node->location, AST_WIRETYPE)); + node->children.back()->str = *name; + } + + void ParseState::addTypedefNode(std::string *name, std::unique_ptr node) + { + log_assert((bool)node); + AstNode* tnode = saveChild(std::make_unique(node->location, AST_TYPEDEF, std::move(node))); + log_assert((bool)name); + 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; + } + } + + void ParseState::enterTypeScope() + { + user_type_stack.push_back(UserTypeMap()); + } + + void ParseState::exitTypeScope() + { + user_type_stack.pop_back(); + } + + bool ParseState::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); + } + + // 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. + void ParseState::rewriteGenForDeclInit(AstNode *loop) + { + // check if this generate for loop contains an inline declaration + log_assert(loop->type == AST_GENFOR); + auto& 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].get(); + AstNode *cond = loop->children[2].get(); + AstNode *incr = loop->children[3].get(); + AstNode *body = loop->children[4].get(); + 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); + + // rename and move the genvar declaration to the containing description + decl->str = new_str; + log_assert(current_ast_mod != nullptr); + current_ast_mod->children.push_back(std::move(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 + auto indirect = std::make_unique(loop->location, AST_LOCALPARAM); + indirect->str = old_str; + auto ident = std::make_unique(loop->location, AST_IDENTIFIER); + ident->str = new_str; + indirect->children.push_back(std::move(ident)); + + body->children.insert(body->children.begin(), std::move(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 (auto& child : node->children) + substitute(child.get()); + }; + substitute(init); + substitute(cond); + substitute(incr); + loop->children.erase(loop->children.begin()); + } + + void ParseState::ensureAsgnExprAllowed(const parser::location_type loc, bool sv_mode) + { + if (!sv_mode) + err_at_loc(loc, "Assignments within expressions are only supported in SystemVerilog mode."); + if (ast_stack.back()->type != AST_BLOCK) + err_at_loc(loc, "Assignments within expressions are only permitted within procedures."); + } + + // add a pre/post-increment/decrement statement + const AstNode *ParseState::addIncOrDecStmt(std::unique_ptr>> stmt_attr, + std::unique_ptr lhs, + std::unique_ptr>> op_attr, AST::AstNodeType op, + Location loc) + { + auto one = AstNode::mkconst_int(loc, 1, true); + auto rhs = std::make_unique(loc, op, lhs->clone(), std::move(one)); + if (op_attr) + append_attr(rhs.get(), std::move(op_attr)); + auto stmt_owned = std::make_unique(loc, AST_ASSIGN_EQ, std::move(lhs), std::move(rhs)); + auto* stmt = stmt_owned.get(); + ast_stack.back()->children.push_back(std::move(stmt_owned)); + if (stmt_attr) + append_attr(stmt, std::move(stmt_attr)); + return stmt; + } + + // create a pre/post-increment/decrement expression, and add the corresponding statement + std::unique_ptr ParseState::addIncOrDecExpr(std::unique_ptr lhs, + std::unique_ptr>> attr, + AST::AstNodeType op, Location loc, bool undo, bool sv_mode) + { + ensureAsgnExprAllowed(loc, sv_mode); + const AstNode *stmt = addIncOrDecStmt(nullptr, std::move(lhs), std::move(attr), op, loc); + log_assert(stmt->type == AST_ASSIGN_EQ); + auto expr = stmt->children[0]->clone(); + if (undo) { + auto one = AstNode::mkconst_int(loc, 1, false, 1); + auto minus_one = std::make_unique(loc, AST_NEG, std::move(one)); + expr = std::make_unique(loc, op, std::move(expr), std::move(minus_one)); + } + return expr; + } + + // add a binary operator assignment statement, e.g., a += b + std::unique_ptr ParseState::addAsgnBinopStmt(std::unique_ptr>> attr, + std::unique_ptr eq_lhs, AST::AstNodeType op, std::unique_ptr rhs) + { + Location loc = location_range(eq_lhs->location, rhs->location); + if (op == AST_SHIFT_LEFT || op == AST_SHIFT_RIGHT || + op == AST_SHIFT_SLEFT || op == AST_SHIFT_SRIGHT) { + rhs = std::make_unique(rhs->location, AST_TO_UNSIGNED, std::move(rhs)); + } + auto binop_lhs = eq_lhs->clone(); + auto eq_rhs_owned = std::make_unique(loc, op, std::move(binop_lhs), std::move(rhs)); + auto ret_lhs = eq_lhs->clone(); + auto stmt_owned = std::make_unique(loc, AST_ASSIGN_EQ, std::move(eq_lhs), std::move(eq_rhs_owned)); + auto* stmt = stmt_owned.get(); + ast_stack.back()->children.push_back(std::move(stmt_owned)); + if (attr) + append_attr(stmt, std::move(attr)); + return ret_lhs; + } }; - substitute(init); - substitute(cond); - substitute(incr); -} + YOSYS_NAMESPACE_END -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); + void frontend_verilog_yy::parser::error(const frontend_verilog_yy::parser::location_type& loc, const std::string& msg) + { + err_at_loc(loc, "%s", msg); } - 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" + #include + #include + #include + #include "frontends/verilog/verilog_frontend.h" + + struct specify_target { + char polarity_op; + std::unique_ptr dst, dat; + specify_target& operator=(specify_target&& other) noexcept { + if (this != &other) { + dst = std::move(other.dst); + dat = std::move(other.dat); + polarity_op = other.polarity_op; + } + return *this; + } + }; + + struct specify_triple { + std::unique_ptr t_min, t_avg, t_max; + specify_triple& operator=(specify_triple&& other) noexcept { + if (this != &other) { + t_min = std::move(other.t_min); + t_avg = std::move(other.t_avg); + t_max = std::move(other.t_max); + } + return *this; + } + }; + + struct specify_rise_fall { + specify_triple rise; + specify_triple fall; + }; + + using string_t = std::unique_ptr; + using ast_t = std::unique_ptr; + using al_t = std::unique_ptr>>; + using specify_target_ptr_t = std::unique_ptr; + using specify_triple_ptr_t = std::unique_ptr; + using specify_rise_fall_ptr_t = std::unique_ptr; + using boolean_t = bool; + using ch_t = char; + using integer_t = int; + using ast_node_type_t = YOSYS_NAMESPACE_PREFIX AST::AstNodeType; } -%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 string_t "string" +%token ast_t +%token al_t +%token specify_target_ptr_t "specify target" +%token specify_triple_ptr_t "specify triple" +%token specify_rise_fall_ptr_t "specify rise and fall" +%token boolean_t "boolean" +%token ch_t "invalid token" +%token integer_t "integer" +%token ast_node_type_t -%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_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 @@ -401,7 +495,7 @@ static const AstNode *addAsgnBinopStmt(dict *attr, AstNode * %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_BEGIN TOK_END TOK_IF TOK_ELSE TOK_IFNONE 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 @@ -417,42 +511,75 @@ static const AstNode *addAsgnBinopStmt(dict *attr, AstNode * %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 +%token TOK_IMPORT -%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 +%token TOK_EXCL "'!'" +%token TOK_HASH "'#'" +%token TOK_PERC "'%'" +%token TOK_AMP "'&'" +%token TOK_LPAREN "'('" +%token TOK_RPAREN "')'" +%token TOK_ASTER "'*'" +%token TOK_PLUS "'+'" +%token TOK_COMMA "','" +%token TOK_MINUS "'-'" +%token TOK_DOT "'.'" +%token TOK_SLASH "'/'" +%token TOK_COL "':'" +%token TOK_SEMICOL "';'" +%token TOK_LT "'<'" +%token TOK_EQ "'='" +%token TOK_GT "'>'" +%token TOK_QUE "'?'" +%token TOK_AT "'@'" +%token TOK_LBRA "'['" +%token TOK_RBRA "']'" +%token TOK_CARET "'^'" +%token TOK_UNDER "'_'" +%token TOK_LCURL "'{'" +%token TOK_PIPE "'|'" +%token TOK_RCURL "'}'" +%token TOK_TILDE "'~'" +%token TOK_n "'n'" +%token TOK_p "'p'" +%token TOK_x "'x'" +%token TOK_z "'z'" -%type specify_target -%type specify_triple specify_opt_triple -%type specify_rise_fall -%type specify_if specify_condition -%type specify_edge +%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 if_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 TOK_PIPE OP_NOR +%left TOK_CARET OP_XNOR +%left TOK_AMP OP_NAND %left OP_EQ OP_NE OP_EQX OP_NEX -%left '<' OP_LE OP_GE '>' +%left TOK_LT OP_LE OP_GE TOK_GT %left OP_SHL OP_SHR OP_SSHL OP_SSHR -%left '+' '-' -%left '*' '/' '%' +%left TOK_PLUS TOK_MINUS +%left TOK_ASTER TOK_SLASH TOK_PERC %left OP_POW %precedence OP_CAST %precedence UNARY_OPS -%define parse.error verbose +%define parse.error detailed %define parse.lac full %precedence FAKE_THEN @@ -464,15 +591,13 @@ static const AstNode *addAsgnBinopStmt(dict *attr, AstNode * %% input: { - (void)frontend_verilog_yynerrs; - ast_stack.clear(); - ast_stack.push_back(current_ast); + (void)yynerrs_; + extra->ast_stack.clear(); + extra->ast_stack.push_back(extra->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(); + extra->ast_stack.pop_back(); + log_assert(GetSize(extra->ast_stack) == 0); + extra->default_attr_list.clear(); }; design: @@ -483,24 +608,25 @@ design: localparam_decl design | typedef_decl design | package design | + import_stmt 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(); + if (extra->attr_list) + extra->attr_list_stack.push(std::move(extra->attr_list)); + extra->attr_list = std::make_unique>>(); + for (auto &it : extra->default_attr_list) + (*extra->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(); + $$ = std::move(extra->attr_list); + if (!extra->attr_list_stack.empty()) { + extra->attr_list = std::move(extra->attr_list_stack.top()); + extra->attr_list_stack.pop(); } else - attr_list = nullptr; + extra->attr_list = nullptr; }; attr_opt: @@ -511,20 +637,18 @@ attr_opt: 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(); + if (extra->attr_list != nullptr) + extra->attr_list_stack.push(std::move(extra->attr_list)); + extra->attr_list = std::make_unique>>(); + extra->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(); + extra->attr_list->swap(extra->default_attr_list); + extra->attr_list.reset(); + if (!extra->attr_list_stack.empty()) { + extra->attr_list = std::move(extra->attr_list_stack.top()); + extra->attr_list_stack.pop(); } else - attr_list = nullptr; + extra->attr_list = nullptr; } DEFATTR_END; opt_attr_list: @@ -532,195 +656,170 @@ opt_attr_list: attr_list: attr_assign | - attr_list ',' attr_assign; + attr_list TOK_COMMA 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; + (*extra->attr_list)[*$1] = AstNode::mkconst_int(@1, 1, false); } | - hierarchical_id '=' expr { - if (attr_list->count(*$1) != 0) - delete (*attr_list)[*$1]; - (*attr_list)[*$1] = $3; - delete $1; + hierarchical_id TOK_EQ expr { + (*extra->attr_list)[*$1] = std::move($3); }; hierarchical_id: TOK_ID { - $$ = $1; + $$ = std::move($1); } | hierarchical_id TOK_PACKAGESEP TOK_ID { if ($3->compare(0, 1, "\\") == 0) *$1 += "::" + $3->substr(1); else *$1 += "::" + *$3; - delete $3; - $$ = $1; + $$ = std::move($1); } | - hierarchical_id '.' TOK_ID { + hierarchical_id TOK_DOT TOK_ID { if ($3->compare(0, 1, "\\") == 0) *$1 += "." + $3->substr(1); else *$1 += "." + *$3; - delete $3; - $$ = $1; + $$ = std::move($1); }; hierarchical_type_id: - TOK_USER_TYPE - | TOK_PKG_USER_TYPE // package qualified type name - | '(' TOK_USER_TYPE ')' { $$ = $2; } // non-standard grammar + TOK_USER_TYPE {$$ = std::move($1); } + | TOK_PKG_USER_TYPE {$$ = std::move($1); } // package qualified type name + | TOK_LPAREN TOK_USER_TYPE TOK_RPAREN { $$ = std::move($2); } // non-standard grammar ; module: attr TOK_MODULE { - enterTypeScope(); + extra->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; + extra->do_not_require_port_stubs = false; + AstNode* mod = extra->pushChild(std::make_unique(@$, AST_MODULE)); + extra->current_ast_mod = mod; + extra->port_stubs.clear(); + extra->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(); + append_attr(mod, std::move($1)); + } module_para_opt module_args_opt TOK_SEMICOL module_body TOK_ENDMODULE opt_label { + if (extra->port_stubs.size() != 0) + err_at_loc(@7, "Missing details for module port `%s'.", + extra->port_stubs.begin()->first.c_str()); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @$); + extra->ast_stack.pop_back(); + log_assert(extra->ast_stack.size() == 1); + checkLabelsMatch(@11, "Module name", $4.get(), $11.get()); + extra->current_ast_mod = nullptr; + extra->exitTypeScope(); }; module_para_opt: - '#' '(' { astbuf1 = nullptr; } module_para_list { if (astbuf1) delete astbuf1; } ')' | %empty; + TOK_HASH TOK_LPAREN module_para_list TOK_RPAREN | %empty; module_para_list: - single_module_para | module_para_list ',' single_module_para; + single_module_para | module_para_list TOK_COMMA 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); + extra->astbuf1 = std::make_unique(@$, AST_PARAMETER); + extra->astbuf1->children.push_back(AstNode::mkconst_int(@2, 0, true)); + append_attr(extra->astbuf1.get(), std::move($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); + extra->astbuf1 = std::make_unique(@$, AST_LOCALPARAM); + extra->astbuf1->children.push_back(AstNode::mkconst_int(@2, 0, true)); + append_attr(extra->astbuf1.get(), std::move($1)); } param_type single_param_decl | single_param_decl; module_args_opt: - '(' ')' | %empty | '(' module_args optional_comma ')'; + TOK_LPAREN TOK_RPAREN | %empty | TOK_LPAREN module_args optional_comma TOK_RPAREN; module_args: - module_arg | module_args ',' module_arg; + module_arg | module_args TOK_COMMA module_arg; optional_comma: - ',' | %empty; + TOK_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; + TOK_EQ expr { + if (extra->ast_stack.back()->children.size() > 0 && extra->ast_stack.back()->children.back()->type == AST_WIRE) { + if (extra->ast_stack.back()->children.back()->is_input) { + auto& n = extra->ast_stack.back()->children.back(); + n->attributes[ID::defaultvalue] = std::move($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)))); + auto wire = std::make_unique(@$, AST_IDENTIFIER); + wire->str = extra->ast_stack.back()->children.back()->str; + if (extra->ast_stack.back()->children.back()->is_reg || extra->ast_stack.back()->children.back()->is_logic) + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_INITIAL, std::make_unique(@$, AST_BLOCK, std::make_unique(@$, AST_ASSIGN_LE, std::move(wire), std::move($2))))); else - ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2)); + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_ASSIGN, std::move(wire), std::move($2))); } } else - frontend_verilog_yyerror("SystemVerilog interface in module port list cannot have a default value."); + err_at_loc(@2, "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(); + if (extra->ast_stack.back()->children.size() > 0 && extra->ast_stack.back()->children.back()->type == AST_WIRE) { + auto node = extra->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); + node->port_id = ++extra->port_counter; + SET_AST_NODE_LOC(node.get(), @1, @1); + extra->ast_stack.back()->children.push_back(std::move(node)); } else { - if (port_stubs.count(*$1) != 0) - frontend_verilog_yyerror("Duplicate module port `%s'.", $1->c_str()); - port_stubs[*$1] = ++port_counter; + if (extra->port_stubs.count(*$1) != 0) + err_at_loc(@1, "Duplicate module port `%s'.", *$1); + extra->port_stubs[*$1] = ++extra->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; + extra->astbuf1 = std::make_unique(@$, AST_INTERFACEPORT); + extra->astbuf1->children.push_back(std::make_unique(@$, AST_INTERFACEPORTTYPE)); + extra->astbuf1->children[0]->str = *$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. + if (!mode->sv) + err_at_loc(@3, "Interface found in port list (%s). This is not supported unless read_verilog is called with -sv!", *$3); + extra->astbuf2 = extra->astbuf1->clone(); // really only needed if multiple instances of same type. + extra->astbuf2->str = *$3; + extra->astbuf2->port_id = ++extra->port_counter; + extra->ast_stack.back()->children.push_back(std::move(extra->astbuf2)); } module_arg_opt_assignment | attr wire_type range_or_multirange TOK_ID { - AstNode *node = $2; + auto node = std::move($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); + SET_AST_NODE_LOC(node.get(), @4, @4); + node->port_id = ++extra->port_counter; + auto range = checkRange(node.get(), std::move($3)); + if (range != nullptr) + node->children.push_back(std::move(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; + err_at_loc(@4, "Module port `%s' is neither input nor output.", *$4); + if (node->is_reg && node->is_input && !node->is_output && !mode->sv) + err_at_loc(@4, "Input port `%s' is declared as register.", *$4); + append_attr(node.get(), std::move($1)); + extra->ast_stack.back()->children.push_back(std::move(node)); } module_arg_opt_assignment | - '.' '.' '.' { - do_not_require_port_stubs = true; + TOK_DOT TOK_DOT TOK_DOT { + extra->do_not_require_port_stubs = true; }; package: attr TOK_PACKAGE { - enterTypeScope(); + extra->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; + AstNode* mod = extra->pushChild(std::make_unique(@$, AST_PACKAGE)); + extra->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(); + append_attr(mod, std::move($1)); + } TOK_SEMICOL package_body TOK_ENDPACKAGE opt_label { + extra->ast_stack.pop_back(); + checkLabelsMatch(@9, "Package name", $4.get(), $9.get()); + extra->current_ast_mod = nullptr; + extra->exitTypeScope(); }; package_body: @@ -729,27 +828,52 @@ package_body: package_body_stmt: typedef_decl | localparam_decl | param_decl | task_func_decl; +import_stmt: + TOK_IMPORT TOK_ID TOK_PACKAGESEP TOK_ASTER TOK_SEMICOL { + // Create an import node to track wildcard package imports + auto import_node = std::make_unique(@$, AST_IMPORT); + import_node->str = *$2; + extra->ast_stack.back()->children.push_back(std::move(import_node)); + } | + TOK_IMPORT TOK_ID TOK_PACKAGESEP { + // Start a specific import: create and push the AST_IMPORT node + AstNode* import_node = extra->pushChild(std::make_unique(@$, AST_IMPORT)); + import_node->str = *$2; + } import_item_list TOK_SEMICOL { + // Done collecting specific items, pop the AST_IMPORT node + extra->ast_stack.pop_back(); + }; + +import_item_list: + import_item | + import_item_list TOK_COMMA import_item ; + +import_item: + TOK_ID { + // Append this specific import name under the current AST_IMPORT + auto item_node = std::make_unique(@$, AST_NONE); + item_node->str = *$1; + extra->ast_stack.back()->children.push_back(std::move(item_node)); + }; + interface: TOK_INTERFACE { - enterTypeScope(); + extra->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; + extra->do_not_require_port_stubs = false; + AstNode* intf = extra->pushChild(std::make_unique(@$, AST_INTERFACE)); + extra->current_ast_mod = intf; + extra->port_stubs.clear(); + extra->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(); + } module_para_opt module_args_opt TOK_SEMICOL interface_body TOK_ENDINTERFACE { + if (extra->port_stubs.size() != 0) + err_at_loc(@6, "Missing details for module port `%s'.", + extra->port_stubs.begin()->first.c_str()); + extra->ast_stack.pop_back(); + log_assert(extra->ast_stack.size() == 1); + extra->current_ast_mod = nullptr; + extra->exitTypeScope(); }; interface_body: @@ -761,27 +885,24 @@ interface_body_stmt: bind_directive: TOK_BIND { - AstNode *bnode = new AstNode(AST_BIND); - ast_stack.back()->children.push_back(bnode); - ast_stack.push_back(bnode); + (void)extra->pushChild(std::make_unique(@$, AST_BIND)); } bind_target { // bind_target should have added at least one child - log_assert(ast_stack.back()->children.size() >= 1); + log_assert(extra->ast_stack.back()->children.size() >= 1); } TOK_ID { - // The single_cell parser in cell_list_no_array uses astbuf1 as + // The single_cell parser in cell_list_no_array uses extra->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; + extra->astbuf1 = std::make_unique(@$, AST_CELL); + extra->astbuf1->children.push_back(std::make_unique(@$, AST_CELLTYPE)); + extra->astbuf1->children[0]->str = *$5; } - cell_parameter_list_opt cell_list_no_array ';' { + cell_parameter_list_opt cell_list_no_array TOK_SEMICOL { // cell_list should have added at least one more child - log_assert(ast_stack.back()->children.size() >= 2); - delete astbuf1; - ast_stack.pop_back(); + log_assert(extra->ast_stack.back()->children.size() >= 2); + (void)extra->astbuf1.reset(); + extra->ast_stack.pop_back(); }; // bind_target matches the target of the bind (everything before @@ -790,7 +911,7 @@ bind_directive: // 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 '.' +// by considering foo as a degenerate hierarchical identifier with no TOK_DOT // characters, followed by no bit select (which matches bind_target_instance in // the spec). // @@ -802,82 +923,81 @@ bind_target: // An optional list of target instances for a bind statement, introduced by a // colon. opt_bind_target_instance_list: - ':' bind_target_instance_list | + TOK_COL bind_target_instance_list | %empty; bind_target_instance_list: bind_target_instance | - bind_target_instance_list ',' bind_target_instance; + bind_target_instance_list TOK_COMMA bind_target_instance; -// A single target instance for a bind statement. The top of ast_stack will be +// A single target instance for a bind statement. The top of extra->ast_stack will be // the bind node where we should add it. bind_target_instance: hierarchical_id { - auto *node = new AstNode(AST_IDENTIFIER); + auto node = std::make_unique(@$, AST_IDENTIFIER); node->str = *$1; - delete $1; - ast_stack.back()->children.push_back(node); + extra->ast_stack.back()->children.push_back(std::move(node)); }; mintypmax_expr: - expr { delete $1; } | - expr ':' expr ':' expr { delete $1; delete $3; delete $5; }; + expr { } | + expr TOK_COL expr TOK_COL expr { }; non_opt_delay: - '#' TOK_ID { delete $2; } | - '#' TOK_CONSTVAL { delete $2; } | - '#' TOK_REALVAL { delete $2; } | + TOK_HASH TOK_ID { } | + TOK_HASH TOK_CONSTVAL { } | + TOK_HASH TOK_REALVAL { } | // 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 ')'; + TOK_HASH TOK_TIME_SCALE | + TOK_HASH TOK_LPAREN TOK_TIME_SCALE TOK_RPAREN | + TOK_HASH TOK_LPAREN mintypmax_expr TOK_RPAREN | + TOK_HASH TOK_LPAREN mintypmax_expr TOK_COMMA mintypmax_expr TOK_RPAREN | + TOK_HASH TOK_LPAREN mintypmax_expr TOK_COMMA mintypmax_expr TOK_COMMA mintypmax_expr TOK_RPAREN; delay: non_opt_delay | %empty; io_wire_type: - { astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } + { extra->astbuf3 = std::make_unique(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; } wire_type_token_io wire_type_const_rand opt_wire_type_token wire_type_signedness - { $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; + { $$ = std::move(extra->astbuf3); SET_RULE_LOC(@$, @2, @$); }; non_io_wire_type: - { astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } + { extra->astbuf3 = std::make_unique(@$, AST_WIRE); extra->current_wire_rand = false; extra->current_wire_const = false; } wire_type_const_rand wire_type_token wire_type_signedness - { $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; + { $$ = std::move(extra->astbuf3); SET_RULE_LOC(@$, @2, @$); }; wire_type: - io_wire_type | - non_io_wire_type; + io_wire_type { $$ = std::move($1); } | + non_io_wire_type { $$ = std::move($1); }; wire_type_token_io: TOK_INPUT { - astbuf3->is_input = true; + extra->astbuf3->is_input = true; } | TOK_OUTPUT { - astbuf3->is_output = true; + extra->astbuf3->is_output = true; } | TOK_INOUT { - astbuf3->is_input = true; - astbuf3->is_output = true; + extra->astbuf3->is_input = true; + extra->astbuf3->is_output = true; }; wire_type_signedness: - TOK_SIGNED { astbuf3->is_signed = true; } | - TOK_UNSIGNED { astbuf3->is_signed = false; } | + TOK_SIGNED { extra->astbuf3->is_signed = true; } | + TOK_UNSIGNED { extra->astbuf3->is_signed = false; } | %empty; wire_type_const_rand: TOK_RAND TOK_CONST { - current_wire_rand = true; - current_wire_const = true; + extra->current_wire_rand = true; + extra->current_wire_const = true; } | TOK_CONST { - current_wire_const = true; + extra->current_wire_const = true; } | TOK_RAND { - current_wire_rand = true; + extra->current_wire_rand = true; } | %empty; @@ -892,35 +1012,35 @@ wire_type_token: } | // regs TOK_REG { - astbuf3->is_reg = true; + extra->astbuf3->is_reg = true; } | TOK_VAR TOK_REG { - astbuf3->is_reg = true; + extra->astbuf3->is_reg = true; } | // logics TOK_VAR { - astbuf3->is_logic = true; + extra->astbuf3->is_logic = true; } | TOK_VAR logic_type { - astbuf3->is_logic = true; + extra->astbuf3->is_logic = true; } | logic_type { - astbuf3->is_logic = true; + extra->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; + extra->astbuf3->type = AST_GENVAR; + extra->astbuf3->is_reg = true; + extra->astbuf3->is_signed = true; + extra->astbuf3->range_left = 31; + extra->astbuf3->range_right = 0; }; net_type: TOK_WOR { - astbuf3->is_wor = true; + extra->astbuf3->is_wor = true; } | TOK_WAND { - astbuf3->is_wand = true; + extra->astbuf3->is_wand = true; } | TOK_WIRE; @@ -928,12 +1048,12 @@ logic_type: TOK_LOGIC { } | integer_atom_type { - astbuf3->range_left = $1 - 1; - astbuf3->range_right = 0; - astbuf3->is_signed = true; + extra->astbuf3->range_left = $1 - 1; + extra->astbuf3->range_right = 0; + extra->astbuf3->is_signed = true; } | hierarchical_type_id { - addWiretypeNode($1, astbuf3); + extra->addWiretypeNode($1.get(), extra->astbuf3.get()); }; integer_atom_type: @@ -944,59 +1064,59 @@ integer_atom_type: TOK_BYTE { $$ = 8; } ; integer_vector_type: - TOK_LOGIC { $$ = TOK_LOGIC; } | - TOK_REG { $$ = TOK_REG; } ; + TOK_LOGIC { $$ = token::TOK_LOGIC; } | + TOK_REG { $$ = token::TOK_REG; } ; non_opt_range: - '[' expr ':' expr ']' { - $$ = new AstNode(AST_RANGE); - $$->children.push_back($2); - $$->children.push_back($4); + TOK_LBRA expr TOK_COL expr TOK_RBRA { + $$ = std::make_unique(@$, AST_RANGE); + $$->children.push_back(std::move($2)); + $$->children.push_back(std::move($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))); + TOK_LBRA expr TOK_POS_INDEXED expr TOK_RBRA { + $$ = std::make_unique(@$, AST_RANGE); + auto expr = std::make_unique(@$, AST_SELFSZ, std::move($2)); + $$->children.push_back(std::make_unique(@$, AST_SUB, std::make_unique(@$, AST_ADD, expr->clone(), std::move($4)), AstNode::mkconst_int(@$, 1, true))); + $$->children.push_back(std::make_unique(@$, AST_ADD, std::move(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)); + TOK_LBRA expr TOK_NEG_INDEXED expr TOK_RBRA { + $$ = std::make_unique(@$, AST_RANGE); + auto expr = std::make_unique(@$, AST_SELFSZ, std::move($2)); + $$->children.push_back(std::make_unique(@$, AST_ADD, expr->clone(), AstNode::mkconst_int(@$, 0, true))); + $$->children.push_back(std::make_unique(@$, AST_SUB, std::make_unique(@$, AST_ADD, std::move(expr), AstNode::mkconst_int(@$, 1, true)), std::move($4))); } | - '[' expr ']' { - $$ = new AstNode(AST_RANGE); - $$->children.push_back($2); + TOK_LBRA expr TOK_RBRA { + $$ = std::make_unique(@$, AST_RANGE); + $$->children.push_back(std::move($2)); }; non_opt_multirange: non_opt_range non_opt_range { - $$ = new AstNode(AST_MULTIRANGE, $1, $2); + $$ = std::make_unique(@$, AST_MULTIRANGE, std::move($1), std::move($2)); } | non_opt_multirange non_opt_range { - $$ = $1; - $$->children.push_back($2); + $$ = std::move($1); + $$->children.push_back(std::move($2)); }; range: non_opt_range { - $$ = $1; + $$ = std::move($1); } | %empty { - $$ = NULL; + $$ = nullptr; }; range_or_multirange: - range { $$ = $1; } | - non_opt_multirange { $$ = $1; }; + range { $$ = std::move($1); } | + non_opt_multirange { $$ = std::move($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 ';' | + module_body TOK_SEMICOL | %empty; module_body_stmt: @@ -1005,61 +1125,43 @@ module_body_stmt: 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); + TOK_CHECKER TOK_ID TOK_SEMICOL { + AstNode* node = extra->pushChild(std::make_unique(@$, 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(); + extra->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; + extra->current_function_or_task = extra->saveChild(std::make_unique(@$, AST_DPI_FUNCTION, AstNode::mkconst_str(@3, *$3), AstNode::mkconst_str(@4, *$4))); + extra->current_function_or_task->str = *$4; + append_attr(extra->current_function_or_task, std::move($1)); + } opt_dpi_function_args TOK_SEMICOL { + extra->current_function_or_task = nullptr; } | - 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_EQ TOK_ID TOK_ID { + extra->current_function_or_task = extra->saveChild(std::make_unique(@$, AST_DPI_FUNCTION, AstNode::mkconst_str(@5, *$5), AstNode::mkconst_str(@3, *$3))); + extra->current_function_or_task->str = *$6; + append_attr(extra->current_function_or_task, std::move($1)); + } opt_dpi_function_args TOK_SEMICOL { + extra->current_function_or_task = nullptr; } | - 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_DPI_FUNCTION TOK_ID TOK_COL TOK_ID TOK_EQ TOK_ID TOK_ID { + extra->current_function_or_task = extra->saveChild(std::make_unique(@$, AST_DPI_FUNCTION, AstNode::mkconst_str(@7, *$7), AstNode::mkconst_str(location_range(@3, @5), *$3 + ":" + RTLIL::unescape_id(*$5)))); + extra->current_function_or_task->str = *$8; + append_attr(extra->current_function_or_task, std::move($1)); + } opt_dpi_function_args TOK_SEMICOL { + extra->current_function_or_task = nullptr; } | 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(); + extra->current_function_or_task = extra->pushChild(std::make_unique(@$, AST_TASK)); + extra->current_function_or_task->str = *$4; + append_attr(extra->current_function_or_task, std::move($1)); + extra->current_function_or_task_port_id = 1; + } task_func_args_opt TOK_SEMICOL task_func_body TOK_ENDTASK { + extra->current_function_or_task = nullptr; + extra->ast_stack.pop_back(); } | attr TOK_FUNCTION opt_automatic TOK_VOID TOK_ID { // The difference between void functions and tasks is that @@ -1067,56 +1169,49 @@ task_func_decl: // 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(); + extra->current_function_or_task = extra->pushChild(std::make_unique(@$, AST_TASK)); + extra->current_function_or_task->str = *$5; + append_attr(extra->current_function_or_task, std::move($1)); + extra->current_function_or_task_port_id = 1; + } task_func_args_opt TOK_SEMICOL task_func_body TOK_ENDFUNCTION { + extra->current_function_or_task = nullptr; + extra->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); + extra->current_function_or_task = extra->pushChild(std::make_unique(@$, AST_FUNCTION)); + extra->current_function_or_task->str = *$5; + append_attr(extra->current_function_or_task, std::move($1)); + auto outreg = std::make_unique(@$, AST_WIRE); outreg->str = *$5; outreg->is_signed = false; outreg->is_reg = true; - if ($4 != NULL) { - outreg->children.push_back($4); + if ($4 != nullptr) { outreg->is_signed = $4->is_signed; $4->is_signed = false; outreg->is_custom_type = $4->type == AST_WIRETYPE; + outreg->children.push_back(std::move($4)); } - 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(); + extra->current_function_or_task->children.push_back(std::move(outreg)); + extra->current_function_or_task_port_id = 1; + } task_func_args_opt TOK_SEMICOL task_func_body TOK_ENDFUNCTION { + extra->current_function_or_task = nullptr; + extra->ast_stack.pop_back(); }; func_return_type: hierarchical_type_id { - $$ = new AstNode(AST_WIRETYPE); + $$ = std::make_unique(@$, AST_WIRETYPE); $$->str = *$1; - delete $1; } | opt_type_vec opt_signedness_default_unsigned { - $$ = makeRange(0, 0, $2); + $$ = makeRange(@$, 0, 0, $2); } | opt_type_vec opt_signedness_default_unsigned non_opt_range { - $$ = $3; + $$ = std::move($3); $$->is_signed = $2; } | integer_atom_type opt_signedness_default_signed { - $$ = makeRange($1 - 1, 0, $2); + $$ = makeRange(@$, $1 - 1, 0, $2); }; opt_type_vec: @@ -1138,22 +1233,19 @@ opt_signedness_default_unsigned: dpi_function_arg: TOK_ID TOK_ID { - current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); - delete $1; - delete $2; + extra->current_function_or_task->children.push_back(AstNode::mkconst_str(@1, *$1)); } | TOK_ID { - current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); - delete $1; + extra->current_function_or_task->children.push_back(AstNode::mkconst_str(@1, *$1)); }; opt_dpi_function_args: - '(' dpi_function_args ')' | + TOK_LPAREN dpi_function_args TOK_RPAREN | %empty; dpi_function_args: - dpi_function_args ',' dpi_function_arg | - dpi_function_args ',' | + dpi_function_args TOK_COMMA dpi_function_arg | + dpi_function_args TOK_COMMA | dpi_function_arg | %empty; @@ -1162,52 +1254,52 @@ opt_automatic: %empty; task_func_args_opt: - '(' ')' | %empty | '(' { - albuf = nullptr; - astbuf1 = nullptr; - astbuf2 = nullptr; + TOK_LPAREN TOK_RPAREN | %empty | TOK_LPAREN { + extra->albuf = nullptr; + extra->astbuf1 = nullptr; + extra->astbuf2 = nullptr; } task_func_args optional_comma { - delete astbuf1; - if (astbuf2 != NULL) - delete astbuf2; - free_attr(albuf); - } ')'; + (void)extra->astbuf1.reset(); + if (extra->astbuf2 != nullptr) + (void)extra->astbuf2.reset(); + extra->albuf.reset(); + } TOK_RPAREN; task_func_args: - task_func_port | task_func_args ',' task_func_port; + task_func_port | task_func_args TOK_COMMA 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); + if (extra->albuf) { + prev_was_input = extra->astbuf1->is_input; + prev_was_output = extra->astbuf1->is_output; + (void)extra->astbuf1.reset(); + if (extra->astbuf2 != nullptr) + (void)extra->astbuf2.reset(); + extra->albuf.reset(); } - 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; + extra->albuf = std::move($1); + extra->astbuf1 = std::move($2); + extra->astbuf2 = checkRange(extra->astbuf1.get(), std::move($3)); + if (!extra->astbuf1->is_input && !extra->astbuf1->is_output) { + if (!mode->sv) + err_at_loc(@2, "task/function argument direction missing"); + extra->astbuf1->is_input = prev_was_input; + extra->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; + if (!extra->astbuf1) { + if (!mode->sv) + err_at_loc(@$, "task/function argument direction missing"); + extra->albuf = std::make_unique>>(); + extra->astbuf1 = std::make_unique(@$, AST_WIRE); + extra->current_wire_rand = false; + extra->current_wire_const = false; + extra->astbuf1->is_input = true; + extra->astbuf2 = nullptr; } } wire_name; @@ -1225,23 +1317,24 @@ specify_item_list: %empty; specify_item: - specify_if '(' specify_edge expr TOK_SPECIFY_OPER specify_target ')' '=' specify_rise_fall ';' { - AstNode *en_expr = $1; + specify_if TOK_LPAREN specify_edge expr TOK_SPECIFY_OPER specify_target TOK_RPAREN TOK_EQ specify_rise_fall TOK_SEMICOL { + auto en_expr = std::move($1); char specify_edge = $3; - AstNode *src_expr = $4; - string *oper = $5; - specify_target *target = $6; - specify_rise_fall *timing = $9; + auto src_expr = std::move($4); + string *oper = $5.get(); + specify_target_ptr_t target = std::move($6); + specify_rise_fall_ptr_t timing = std::move($9); if (specify_edge != 0 && target->dat == nullptr) - frontend_verilog_yyerror("Found specify edge but no data spec.\n"); + err_at_loc(@3, "Found specify edge but no data spec."); - AstNode *cell = new AstNode(AST_CELL); - ast_stack.back()->children.push_back(cell); + auto cell_owned = std::make_unique(@$, AST_CELL); + auto cell = cell_owned.get(); + extra->ast_stack.back()->children.push_back(std::move(cell_owned)); cell->str = stringf("$specify$%d", autoidx++); - cell->children.push_back(new AstNode(AST_CELLTYPE)); + cell->children.push_back(std::make_unique(@$, AST_CELLTYPE)); cell->children.back()->str = target->dat ? "$specify3" : "$specify2"; - SET_AST_NODE_LOC(cell, en_expr ? @1 : @2, @10); + SET_AST_NODE_LOC(cell, en_expr.get() ? @1 : @2, @10); char oper_polarity = 0; char oper_type = oper->at(0); @@ -1251,148 +1344,141 @@ specify_item: oper_type = oper->at(1); } - cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_type == '*', false, 1))); + cell->children.push_back(std::make_unique(@5, AST_PARASET, AstNode::mkconst_int(@5, 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.push_back(std::make_unique(@5, AST_PARASET, AstNode::mkconst_int(@5, 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.push_back(std::make_unique(@5, AST_PARASET, AstNode::mkconst_int(@5, 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.push_back(std::make_unique(@9, AST_PARASET, std::move(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.push_back(std::make_unique(@9, AST_PARASET, std::move(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.push_back(std::make_unique(@9, AST_PARASET, std::move(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.push_back(std::make_unique(@9, AST_PARASET, std::move(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.push_back(std::make_unique(@9, AST_PARASET, std::move(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.push_back(std::make_unique(@9, AST_PARASET, std::move(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.push_back(std::make_unique(@1, AST_ARGUMENT, en_expr ? std::move(en_expr) : AstNode::mkconst_int(@1, 1, false, 1))); cell->children.back()->str = "\\EN"; - cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr)); + cell->children.push_back(std::make_unique(@4, AST_ARGUMENT, std::move(src_expr))); cell->children.back()->str = "\\SRC"; - cell->children.push_back(new AstNode(AST_ARGUMENT, target->dst)); + cell->children.push_back(std::make_unique(@6, AST_ARGUMENT, std::move(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.push_back(std::make_unique(@3, AST_PARASET, AstNode::mkconst_int(@3, 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.push_back(std::make_unique(@3, AST_PARASET, AstNode::mkconst_int(@3, 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.push_back(std::make_unique(@6, AST_PARASET, AstNode::mkconst_int(@6, 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.push_back(std::make_unique(@6, AST_PARASET, AstNode::mkconst_int(@6, target->polarity_op == '+', false, 1))); cell->children.back()->str = "\\DAT_DST_POL"; - cell->children.push_back(new AstNode(AST_ARGUMENT, target->dat)); + cell->children.push_back(std::make_unique(@6, AST_ARGUMENT, std::move(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 ')' ';' { + TOK_ID TOK_LPAREN specify_edge expr specify_condition TOK_COMMA specify_edge expr specify_condition TOK_COMMA specify_triple specify_opt_triple TOK_RPAREN TOK_SEMICOL { 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()); + err_at_loc(@1, "Unsupported specify rule type: %s", *$1); - 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); + auto src_pen = AstNode::mkconst_int(@3, $3 != 0, false, 1); + auto src_pol = AstNode::mkconst_int(@3, $3 == 'p', false, 1); + auto src_expr = std::move($4), src_en = $5 ? std::move($5) : AstNode::mkconst_int(@5, 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); + auto dst_pen = AstNode::mkconst_int(@7, $7 != 0, false, 1); + auto dst_pol = AstNode::mkconst_int(@7, $7 == 'p', false, 1); + auto dst_expr = std::move($8), dst_en = $9 ? std::move($9) : AstNode::mkconst_int(@5, 1, false, 1); - specify_triple *limit = $11; - specify_triple *limit2 = $12; + specify_triple_ptr_t limit = std::move($11); + specify_triple_ptr_t limit2 = std::move($12); - AstNode *cell = new AstNode(AST_CELL); - ast_stack.back()->children.push_back(cell); + auto cell_owned = std::make_unique(@$, AST_CELL); + auto cell = cell_owned.get(); + extra->ast_stack.back()->children.push_back(std::move(cell_owned)); cell->str = stringf("$specify$%d", autoidx++); - cell->children.push_back(new AstNode(AST_CELLTYPE)); + cell->children.push_back(std::make_unique(@$, 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.push_back(std::make_unique(@1, AST_PARASET, AstNode::mkconst_str(@1, *$1))); cell->children.back()->str = "\\TYPE"; - cell->children.push_back(new AstNode(AST_PARASET, limit->t_min)); + cell->children.push_back(std::make_unique(@11, AST_PARASET, std::move(limit->t_min))); cell->children.back()->str = "\\T_LIMIT_MIN"; - cell->children.push_back(new AstNode(AST_PARASET, limit->t_avg)); + cell->children.push_back(std::make_unique(@11, AST_PARASET, std::move(limit->t_avg))); cell->children.back()->str = "\\T_LIMIT_TYP"; - cell->children.push_back(new AstNode(AST_PARASET, limit->t_max)); + cell->children.push_back(std::make_unique(@11, AST_PARASET, std::move(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.push_back(std::make_unique(@12, AST_PARASET, limit2 ? std::move(limit2->t_min) : AstNode::mkconst_int(@12, 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.push_back(std::make_unique(@12, AST_PARASET, limit2 ? std::move(limit2->t_avg) : AstNode::mkconst_int(@12, 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.push_back(std::make_unique(@12, AST_PARASET, limit2 ? std::move(limit2->t_max) : AstNode::mkconst_int(@12, 0, true))); cell->children.back()->str = "\\T_LIMIT2_MAX"; - cell->children.push_back(new AstNode(AST_PARASET, src_pen)); + cell->children.push_back(std::make_unique(@3, AST_PARASET, std::move(src_pen))); cell->children.back()->str = "\\SRC_PEN"; - cell->children.push_back(new AstNode(AST_PARASET, src_pol)); + cell->children.push_back(std::make_unique(@3, AST_PARASET, std::move(src_pol))); cell->children.back()->str = "\\SRC_POL"; - cell->children.push_back(new AstNode(AST_PARASET, dst_pen)); + cell->children.push_back(std::make_unique(@3, AST_PARASET, std::move(dst_pen))); cell->children.back()->str = "\\DST_PEN"; - cell->children.push_back(new AstNode(AST_PARASET, dst_pol)); + cell->children.push_back(std::make_unique(@7, AST_PARASET, std::move(dst_pol))); cell->children.back()->str = "\\DST_POL"; - cell->children.push_back(new AstNode(AST_ARGUMENT, src_en)); + cell->children.push_back(std::make_unique(@5, AST_ARGUMENT, std::move(src_en))); cell->children.back()->str = "\\SRC_EN"; - cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr)); + cell->children.push_back(std::make_unique(@4, AST_ARGUMENT, std::move(src_expr))); cell->children.back()->str = "\\SRC"; - cell->children.push_back(new AstNode(AST_ARGUMENT, dst_en)); + cell->children.push_back(std::make_unique(@9, AST_ARGUMENT, std::move(dst_en))); cell->children.back()->str = "\\DST_EN"; - cell->children.push_back(new AstNode(AST_ARGUMENT, dst_expr)); + cell->children.push_back(std::make_unique(@8, AST_ARGUMENT, std::move(dst_expr))); cell->children.back()->str = "\\DST"; - - delete $1; - delete limit; - delete limit2; }; specify_opt_triple: - ',' specify_triple { - $$ = $2; + TOK_COMMA specify_triple { + $$ = std::move($2); } | %empty { $$ = nullptr; }; specify_if: - TOK_IF '(' expr ')' { - $$ = $3; + TOK_IF TOK_LPAREN expr TOK_RPAREN { + $$ = std::move($3); } | %empty { $$ = nullptr; @@ -1400,7 +1486,7 @@ specify_if: specify_condition: TOK_SPECIFY_AND expr { - $$ = $2; + $$ = std::move($2); } | %empty { $$ = nullptr; @@ -1408,28 +1494,28 @@ specify_condition: specify_target: expr { - $$ = new specify_target; + $$ = std::make_unique(); $$->polarity_op = 0; - $$->dst = $1; + $$->dst = std::move($1); $$->dat = nullptr; } | - '(' expr ':' expr ')'{ - $$ = new specify_target; + TOK_LPAREN expr TOK_COL expr TOK_RPAREN{ + $$ = std::make_unique(); $$->polarity_op = 0; - $$->dst = $2; - $$->dat = $4; + $$->dst = std::move($2); + $$->dat = std::move($4); } | - '(' expr TOK_NEG_INDEXED expr ')'{ - $$ = new specify_target; + TOK_LPAREN expr TOK_NEG_INDEXED expr TOK_RPAREN{ + $$ = std::make_unique(); $$->polarity_op = '-'; - $$->dst = $2; - $$->dat = $4; + $$->dst = std::move($2); + $$->dat = std::move($4); } | - '(' expr TOK_POS_INDEXED expr ')'{ - $$ = new specify_target; + TOK_LPAREN expr TOK_POS_INDEXED expr TOK_RPAREN{ + $$ = std::make_unique(); $$->polarity_op = '+'; - $$->dst = $2; - $$->dat = $4; + $$->dst = std::move($2); + $$->dat = std::move($4); }; specify_edge: @@ -1439,72 +1525,48 @@ specify_edge: specify_rise_fall: specify_triple { - $$ = new specify_rise_fall; - $$->rise = *$1; + $$ = std::make_unique(); $$->fall.t_min = $1->t_min->clone(); $$->fall.t_avg = $1->t_avg->clone(); $$->fall.t_max = $1->t_max->clone(); - delete $1; + $$->rise = std::move(*$1); } | - '(' specify_triple ',' specify_triple ')' { - $$ = new specify_rise_fall; - $$->rise = *$2; - $$->fall = *$4; - delete $2; - delete $4; + TOK_LPAREN specify_triple TOK_COMMA specify_triple TOK_RPAREN { + $$ = std::make_unique(); + $$->rise = std::move(*$2); + $$->fall = std::move(*$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"); + TOK_LPAREN specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_RPAREN { + $$ = std::make_unique(); + $$->rise = std::move(*$2); + $$->fall = std::move(*$4); + warn_at_loc(@$, "Path delay expressions beyond rise/fall not currently supported. Ignoring."); } | - '(' 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"); + TOK_LPAREN specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_RPAREN { + $$ = std::make_unique(); + $$->rise = std::move(*$2); + $$->fall = std::move(*$4); + warn_at_loc(@$, "Path delay expressions beyond rise/fall not currently supported. Ignoring."); } | - '(' 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"); + TOK_LPAREN specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_COMMA specify_triple TOK_RPAREN { + $$ = std::make_unique(); + $$->rise = std::move(*$2); + $$->fall = std::move(*$4); + warn_at_loc(@$, "Path delay expressions beyond rise/fall not currently supported. Ignoring."); } specify_triple: expr { - $$ = new specify_triple; - $$->t_min = $1; + $$ = std::make_unique(); $$->t_avg = $1->clone(); $$->t_max = $1->clone(); + $$->t_min = std::move($1); } | - expr ':' expr ':' expr { - $$ = new specify_triple; - $$->t_min = $1; - $$->t_avg = $3; - $$->t_max = $5; + expr TOK_COL expr TOK_COL expr { + $$ = std::make_unique(); + $$->t_min = std::move($1); + $$->t_avg = std::move($3); + $$->t_max = std::move($5); }; /******************** ignored specify parser **************************/ @@ -1526,58 +1588,100 @@ ignored_specify_item: ; specparam_declaration: - TOK_SPECPARAM list_of_specparam_assignments ';' | - TOK_SPECPARAM specparam_range list_of_specparam_assignments ';' ; + TOK_SPECPARAM list_of_specparam_assignments TOK_SEMICOL | + TOK_SPECPARAM specparam_range list_of_specparam_assignments TOK_SEMICOL ; // 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 ']' ; + TOK_LBRA ignspec_constant_expression TOK_COL ignspec_constant_expression TOK_RBRA ; list_of_specparam_assignments: - specparam_assignment | list_of_specparam_assignments ',' specparam_assignment; + specparam_assignment | list_of_specparam_assignments TOK_COMMA specparam_assignment; specparam_assignment: - ignspec_id '=' ignspec_expr ; - -ignspec_opt_cond: - TOK_IF '(' ignspec_expr ')' | %empty; + ignspec_id TOK_EQ ignspec_expr ; path_declaration : - simple_path_declaration ';' + simple_path_declaration TOK_SEMICOL // | edge_sensitive_path_declaration - // | state_dependent_path_declaration + | state_dependent_path_declaration TOK_SEMICOL ; simple_path_declaration : - ignspec_opt_cond parallel_path_description '=' path_delay_value | - ignspec_opt_cond full_path_description '=' path_delay_value + parallel_path_description TOK_EQ path_delay_value | + full_path_description TOK_EQ path_delay_value + ; + +state_dependent_path_declaration: + TOK_IF TOK_LPAREN module_path_expression TOK_RPAREN simple_path_declaration + // | TOK_IF TOK_LPAREN module_path_expression TOK_RPAREN edge_sensitive_path_declaration + | TOK_IFNONE simple_path_declaration + ; + +module_path_expression: + module_path_primary + // Flatten out unary_operator to avoid shift/reduce conflict + | TOK_EXCL attr module_path_primary + | TOK_TILDE attr module_path_primary + | TOK_AMP attr module_path_primary + | OP_NAND attr module_path_primary + | TOK_PIPE attr module_path_primary + | OP_NOR attr module_path_primary + | TOK_CARET attr module_path_primary + | OP_XNOR attr module_path_primary + // Flatten out binary_operator to avoid shift/reduce conflict + | module_path_expression OP_EQ attr module_path_expression + | module_path_expression OP_NE attr module_path_expression + | module_path_expression OP_LAND attr module_path_expression + | module_path_expression OP_LOR attr module_path_expression + | module_path_expression TOK_AMP attr module_path_expression + | module_path_expression TOK_PIPE attr module_path_expression + | module_path_expression TOK_CARET attr module_path_expression + | module_path_expression OP_XNOR attr module_path_expression + // | module_path_conditional_expression + ; + +module_path_primary: + number + | TOK_ID + // Deviate from specification: Normally string would not be allowed, however they are necessary for the ecp5 tests + | TOK_STRING + // | module_path_concatenation + // | module_path_multiple_concatenation + // | function_subroutine_call + // | TOK_LPAREN module_path_minmax_expression TOK_RPAREN + ; + +number: + integral_number + | TOK_REALVAL ; path_delay_value : - '(' ignspec_expr list_of_path_delay_extra_expressions ')' + TOK_LPAREN ignspec_expr list_of_path_delay_extra_expressions TOK_RPAREN | 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 + TOK_COMMA ignspec_expr + | TOK_COMMA 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 ')' ')' ; + TOK_LPAREN specify_input_terminal_descriptor opt_polarity_operator TOK_EQ TOK_GT specify_output_terminal_descriptor TOK_RPAREN | + TOK_LPAREN specify_edge_identifier specify_input_terminal_descriptor TOK_EQ TOK_GT TOK_LPAREN specify_output_terminal_descriptor opt_polarity_operator TOK_COL ignspec_expr TOK_RPAREN TOK_RPAREN | + TOK_LPAREN specify_edge_identifier specify_input_terminal_descriptor TOK_EQ TOK_GT TOK_LPAREN specify_output_terminal_descriptor TOK_POS_INDEXED ignspec_expr TOK_RPAREN TOK_RPAREN ; 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 ')' ')' ; + TOK_LPAREN list_of_path_inputs TOK_ASTER TOK_GT list_of_path_outputs TOK_RPAREN | + TOK_LPAREN specify_edge_identifier list_of_path_inputs TOK_ASTER TOK_GT TOK_LPAREN list_of_path_outputs opt_polarity_operator TOK_COL ignspec_expr TOK_RPAREN TOK_RPAREN | + TOK_LPAREN specify_edge_identifier list_of_path_inputs TOK_ASTER TOK_GT TOK_LPAREN list_of_path_outputs TOK_POS_INDEXED ignspec_expr TOK_RPAREN TOK_RPAREN ; // This was broken into 2 rules to solve shift/reduce conflicts list_of_path_inputs : @@ -1585,15 +1689,15 @@ list_of_path_inputs : specify_input_terminal_descriptor more_path_inputs opt_polarity_operator ; more_path_inputs : - ',' specify_input_terminal_descriptor | - more_path_inputs ',' specify_input_terminal_descriptor ; + TOK_COMMA specify_input_terminal_descriptor | + more_path_inputs TOK_COMMA specify_input_terminal_descriptor ; list_of_path_outputs : specify_output_terminal_descriptor | - list_of_path_outputs ',' specify_output_terminal_descriptor ; + list_of_path_outputs TOK_COMMA specify_output_terminal_descriptor ; opt_polarity_operator : - '+' | '-' | %empty; + TOK_PLUS | TOK_MINUS | %empty; // Good enough for the time being specify_input_terminal_descriptor : @@ -1604,7 +1708,7 @@ specify_output_terminal_descriptor : ignspec_id ; system_timing_declaration : - ignspec_id '(' system_timing_args ')' ';' ; + ignspec_id TOK_LPAREN system_timing_args TOK_RPAREN TOK_SEMICOL ; system_timing_arg : TOK_POSEDGE ignspec_id | @@ -1614,140 +1718,135 @@ system_timing_arg : system_timing_args : system_timing_arg | system_timing_args TOK_IGNORED_SPECIFY_AND system_timing_arg | - system_timing_args ',' system_timing_arg ; + system_timing_args TOK_COMMA 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; }; + expr { }; ignspec_expr: - expr { delete $1; } | - expr ':' expr ':' expr { - delete $1; - delete $3; - delete $5; + expr { } | + expr TOK_COL expr TOK_COL expr { }; ignspec_id: - TOK_ID { delete $1; } - range_or_multirange { delete $3; }; + TOK_ID { } + range_or_multirange { }; /**********************************************************************/ param_signed: TOK_SIGNED { - astbuf1->is_signed = true; + extra->astbuf1->is_signed = true; } | TOK_UNSIGNED { - astbuf1->is_signed = false; + extra->astbuf1->is_signed = false; } | %empty; param_integer: type_atom { - astbuf1->is_reg = false; + extra->astbuf1->is_reg = false; }; param_real: TOK_REAL { - astbuf1->children.push_back(new AstNode(AST_REALVALUE)); + extra->astbuf1->children.push_back(std::make_unique(@$, AST_REALVALUE)); }; param_range: range { - if ($1 != NULL) { - astbuf1->children.push_back($1); + if ($1 != nullptr) { + extra->astbuf1->children.push_back(std::move($1)); } }; param_integer_type: param_integer param_signed; param_range_type: type_vec param_signed { - addRange(astbuf1, 0, 0); + addRange(extra->astbuf1.get(), 0, 0); } | type_vec param_signed non_opt_range { - astbuf1->children.push_back($3); + extra->astbuf1->children.push_back(std::move($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); + extra->addWiretypeNode($1.get(), extra->astbuf1.get()); }; 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; + extra->astbuf1 = std::make_unique(@$, AST_PARAMETER); + extra->astbuf1->children.push_back(AstNode::mkconst_int(@$, 0, true)); + append_attr(extra->astbuf1.get(), std::move($1)); + } param_type param_decl_list TOK_SEMICOL { + (void)extra->astbuf1.reset(); }; 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; + extra->astbuf1 = std::make_unique(@$, AST_LOCALPARAM); + extra->astbuf1->children.push_back(AstNode::mkconst_int(@$, 0, true)); + append_attr(extra->astbuf1.get(), std::move($1)); + } param_type param_decl_list TOK_SEMICOL { + (void)extra->astbuf1.reset(); }; param_decl_list: - single_param_decl | param_decl_list ',' single_param_decl; + single_param_decl | param_decl_list TOK_COMMA single_param_decl; single_param_decl: - single_param_decl_ident '=' expr { - AstNode *decl = ast_stack.back()->children.back(); + single_param_decl_ident TOK_EQ expr { + AstNode *decl = extra->ast_stack.back()->children.back().get(); log_assert(decl->type == AST_PARAMETER || decl->type == AST_LOCALPARAM); - delete decl->children[0]; - decl->children[0] = $3; + decl->children[0] = std::move($3); } | single_param_decl_ident { - AstNode *decl = ast_stack.back()->children.back(); + AstNode *decl = extra->ast_stack.back()->children.back().get(); if (decl->type != AST_PARAMETER) { log_assert(decl->type == AST_LOCALPARAM); - frontend_verilog_yyerror("localparam initialization is missing!"); + err_at_loc(@1, "localparam initialization is missing!"); } - if (!sv_mode) - frontend_verilog_yyerror("Parameter defaults can only be omitted in SystemVerilog mode!"); - delete decl->children[0]; + if (!mode->sv) + err_at_loc(@1, "Parameter defaults can only be omitted in SystemVerilog mode!"); 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)); + std::unique_ptr node_owned; + if (extra->astbuf1 == nullptr) { + if (!mode->sv) + err_at_loc(@1, "In pure Verilog (not SystemVerilog), parameter/localparam with an initializer must use the parameter/localparam keyword"); + node_owned = std::make_unique(@$, AST_PARAMETER); + node_owned->children.push_back(AstNode::mkconst_int(@$, 0, true)); } else { - node = astbuf1->clone(); + node_owned = extra->astbuf1->clone(); } - node->str = *$1; - ast_stack.back()->children.push_back(node); - delete $1; + node_owned->str = *$1; + auto node = node_owned.get(); + extra->ast_stack.back()->children.push_back(std::move(node_owned)); SET_AST_NODE_LOC(node, @1, @1); }; defparam_decl: - TOK_DEFPARAM defparam_decl_list ';'; + TOK_DEFPARAM defparam_decl_list TOK_SEMICOL; defparam_decl_list: - single_defparam_decl | defparam_decl_list ',' single_defparam_decl; + single_defparam_decl | defparam_decl_list TOK_COMMA 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); + range rvalue TOK_EQ expr { + auto node = std::make_unique(@$, AST_DEFPARAM); + node->children.push_back(std::move($2)); + node->children.push_back(std::move($4)); + if ($1 != nullptr) + node->children.push_back(std::move($1)); + extra->ast_stack.back()->children.push_back(std::move(node)); }; ///////// @@ -1757,90 +1856,89 @@ single_defparam_decl: 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++); + extra->astbuf2 = std::make_unique(@$, AST_ENUM); + extra->astbuf2->str = std::string("$enum"); + extra->astbuf2->str += std::to_string(enum_count++); + log_assert(!extra->cell_hack); + extra->cell_hack = extra->astbuf2.get(); + extra->ast_stack.back()->children.push_back(std::move(extra->astbuf2)); // 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 '}' { + extra->astbuf1 = std::make_unique(@$, AST_ENUM_ITEM); + extra->astbuf1->children.push_back(AstNode::mkconst_int(@$, 0, true)); + } enum_base_type TOK_LCURL enum_name_list optional_comma TOK_RCURL { // create template for the enum vars - auto tnode = astbuf1->clone(); - delete astbuf1; - astbuf1 = tnode; + log_assert(extra->cell_hack); + auto tnode_owned = extra->astbuf1->clone(); + auto* tnode = tnode_owned.get(); + extra->astbuf1 = std::move(tnode_owned); tnode->type = AST_WIRE; - tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str); + tnode->attributes[ID::enum_type] = AstNode::mkconst_str(@$, extra->cell_hack->str); + extra->cell_hack = nullptr; // drop constant but keep any range - delete tnode->children[0]; tnode->children.erase(tnode->children.begin()); - $$ = astbuf1; + $$ = extra->astbuf1->clone(); }; 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_vec type_signing range { if ($3) extra->astbuf1->children.push_back(std::move($3)); } + | %empty { extra->astbuf1->is_reg = true; addRange(extra->astbuf1.get()); } ; type_atom: integer_atom_type { - astbuf1->is_reg = true; - astbuf1->is_signed = true; - addRange(astbuf1, $1 - 1, 0); + extra->astbuf1->is_reg = true; + extra->astbuf1->is_signed = true; + addRange(extra->astbuf1.get(), $1 - 1, 0); }; -type_vec: TOK_REG { astbuf1->is_reg = true; } // unsigned - | TOK_LOGIC { astbuf1->is_logic = true; } // unsigned +type_vec: TOK_REG { extra->astbuf1->is_reg = true; } // unsigned + | TOK_LOGIC { extra->astbuf1->is_logic = true; } // unsigned ; type_signing: - TOK_SIGNED { astbuf1->is_signed = true; } - | TOK_UNSIGNED { astbuf1->is_signed = false; } + TOK_SIGNED { extra->astbuf1->is_signed = true; } + | TOK_UNSIGNED { extra->astbuf1->is_signed = false; } | %empty ; enum_name_list: enum_name_decl - | enum_name_list ',' enum_name_decl + | enum_name_list TOK_COMMA enum_name_decl ; enum_name_decl: TOK_ID opt_enum_init { // put in fn - log_assert(astbuf1); - log_assert(astbuf2); - auto node = astbuf1->clone(); + log_assert((bool)extra->astbuf1); + log_assert((bool)extra->cell_hack); + auto node = extra->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); + SET_AST_NODE_LOC(node.get(), @1, @1); + node->children[0] = $2 ? std::move($2) : std::make_unique(@$, AST_NONE); + extra->cell_hack->children.push_back(std::move(node)); } ; opt_enum_init: - '=' basic_expr { $$ = $2; } // TODO: restrict this - | %empty { $$ = NULL; } + TOK_EQ basic_expr { $$ = std::move($2); } // TODO: restrict this + | %empty { $$ = nullptr; } ; enum_var_list: enum_var - | enum_var_list ',' enum_var + | enum_var_list TOK_COMMA enum_var ; enum_var: TOK_ID { - log_assert(astbuf1); - log_assert(astbuf2); - auto node = astbuf1->clone(); - ast_stack.back()->children.push_back(node); + log_assert((bool)extra->astbuf1); + auto node = extra->astbuf1->clone(); node->str = *$1; - delete $1; - SET_AST_NODE_LOC(node, @1, @1); + SET_AST_NODE_LOC(node.get(), @1, @1); node->is_enum = true; + extra->ast_stack.back()->children.push_back(std::move(node)); } ; -enum_decl: enum_type enum_var_list ';' { delete $1; } +enum_decl: enum_type enum_var_list TOK_SEMICOL { } ; ////////////////// @@ -1849,30 +1947,36 @@ enum_decl: enum_type enum_var_list ';' { delete $1; } struct_decl: attr struct_type { - append_attr($2, $1); - } struct_var_list ';' { - delete astbuf2; + append_attr(extra->astbuf2.get(), std::move($1)); + } struct_var_list TOK_SEMICOL { + (void)extra->astbuf2.reset(); } ; -struct_type: struct_union { astbuf2 = $1; } struct_body { $$ = astbuf2; } +struct_type: + struct_union { + extra->astbuf2 = std::move($1); + extra->astbuf2->is_custom_type = true; + } struct_body { + $$ = extra->astbuf2->clone(); + } ; struct_union: - TOK_STRUCT { $$ = new AstNode(AST_STRUCT); } - | TOK_UNION { $$ = new AstNode(AST_UNION); } + TOK_STRUCT { $$ = std::make_unique(@$, AST_STRUCT); } + | TOK_UNION { $$ = std::make_unique(@$, AST_UNION); } ; -struct_body: opt_packed '{' struct_member_list '}' +struct_body: opt_packed TOK_LCURL struct_member_list TOK_RCURL ; opt_packed: TOK_PACKED opt_signed_struct | - %empty { frontend_verilog_yyerror("Only PACKED supported at this time"); }; + %empty { err_at_loc(@$, "Only PACKED supported at this time"); }; opt_signed_struct: - TOK_SIGNED { astbuf2->is_signed = true; } - | TOK_UNSIGNED { astbuf2->is_signed = false; } + TOK_SIGNED { extra->astbuf2->is_signed = true; } + | TOK_UNSIGNED { extra->astbuf2->is_signed = false; } | %empty // default is unsigned ; @@ -1880,62 +1984,68 @@ struct_member_list: struct_member | struct_member_list struct_member ; -struct_member: struct_member_type member_name_list ';' { delete astbuf1; } +struct_member: struct_member_type member_name_list TOK_SEMICOL { (void)extra->astbuf1.reset(); } ; member_name_list: member_name - | member_name_list ',' member_name + | member_name_list TOK_COMMA 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); } + extra->astbuf1->str = $1->substr(1); + extra->astbuf3 = extra->astbuf1->clone(); + log_assert(!extra->member_hack); + extra->member_hack = extra->astbuf3.get(); + SET_AST_NODE_LOC(extra->member_hack, @1, @1); + extra->astbuf2->children.push_back(std::move(extra->astbuf3)); + } range { + log_assert((bool)extra->member_hack); + if ($3) extra->member_hack->children.push_back(std::move($3)); + extra->member_hack = nullptr; + } ; -struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_token +struct_member_type: { extra->astbuf1 = std::make_unique(@$, AST_STRUCT_ITEM); } member_type_token ; member_type_token: member_type range_or_multirange { - AstNode *range = checkRange(astbuf1, $2); + auto range = checkRange(extra->astbuf1.get(), std::move($2)); if (range) - astbuf1->children.push_back(range); + extra->astbuf1->children.push_back(std::move(range)); } | { - delete astbuf1; + (void)extra->astbuf1.reset(); } struct_union { - // stash state on ast_stack - ast_stack.push_back(astbuf2); - astbuf2 = $2; + // stash state on extra->ast_stack + // sketchy! + extra->ast_stack.push_back(extra->astbuf2.release()); + extra->astbuf2 = std::move($2); } struct_body { - astbuf1 = astbuf2; + extra->astbuf1 = std::move(extra->astbuf2); // recover state - astbuf2 = ast_stack.back(); - ast_stack.pop_back(); + extra->astbuf2.reset(extra->ast_stack.back()); + extra->ast_stack.pop_back(); } ; member_type: type_atom type_signing | type_vec type_signing - | hierarchical_type_id { addWiretypeNode($1, astbuf1); } + | hierarchical_type_id { extra->addWiretypeNode($1.get(), extra->astbuf1.get()); } ; struct_var_list: struct_var - | struct_var_list ',' struct_var + | struct_var_list TOK_COMMA 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); - } - ; +struct_var: + TOK_ID { + auto var_node = extra->astbuf2->clone(); + var_node->str = *$1; + SET_AST_NODE_LOC(var_node.get(), @1, @1); + extra->ast_stack.back()->children.push_back(std::move(var_node)); + }; ///////// // wire @@ -1943,46 +2053,43 @@ struct_var: TOK_ID { auto *var_node = astbuf2->clone(); wire_decl: attr wire_type range_or_multirange { - albuf = $1; - astbuf1 = $2; - astbuf2 = checkRange(astbuf1, $3); + extra->albuf = std::move($1); + extra->astbuf1 = std::move($2); + extra->astbuf2 = checkRange(extra->astbuf1.get(), std::move($3)); } delay wire_name_list { - delete astbuf1; - if (astbuf2 != NULL) - delete astbuf2; - free_attr(albuf); - } ';' | + (void)extra->astbuf1.reset(); + if (extra->astbuf2 != nullptr) + (void)extra->astbuf2.reset(); + extra->albuf.reset(); + } TOK_SEMICOL | 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 ';' | + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_WIRE)); + extra->ast_stack.back()->children.back()->str = *$3; + append_attr(extra->ast_stack.back()->children.back().get(), std::move($1)); + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_ASSIGN, std::make_unique(@$, AST_IDENTIFIER), AstNode::mkconst_int(@$, 0, false, 1))); + extra->ast_stack.back()->children.back()->children[0]->str = *$3; + } opt_supply_wires TOK_SEMICOL | 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 ';'; + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_WIRE)); + extra->ast_stack.back()->children.back()->str = *$3; + append_attr(extra->ast_stack.back()->children.back().get(), std::move($1)); + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_ASSIGN, std::make_unique(@$, AST_IDENTIFIER), AstNode::mkconst_int(@$, 1, false, 1))); + extra->ast_stack.back()->children.back()->children[0]->str = *$3; + } opt_supply_wires TOK_SEMICOL; 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(); + opt_supply_wires TOK_COMMA TOK_ID { + auto wire_node = extra->ast_stack.back()->children.at(GetSize(extra->ast_stack.back()->children)-2)->clone(); + auto assign_node = extra->ast_stack.back()->children.at(GetSize(extra->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; + extra->ast_stack.back()->children.push_back(std::move(wire_node)); + extra->ast_stack.back()->children.push_back(std::move(assign_node)); }; wire_name_list: - wire_name_and_opt_assign | wire_name_list ',' wire_name_and_opt_assign; + wire_name_and_opt_assign | wire_name_list TOK_COMMA wire_name_and_opt_assign; wire_name_and_opt_assign: wire_name { @@ -1990,31 +2097,27 @@ wire_name_and_opt_assign: 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); + if (extra->ast_stack.back()->children.back()->get_bool_attribute(ID::anyconst)) { + extra->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); + if (extra->ast_stack.back()->children.back()->get_bool_attribute(ID::anyseq)) { + extra->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); + if (extra->ast_stack.back()->children.back()->get_bool_attribute(ID::allconst)) { + extra->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); + if (extra->ast_stack.back()->children.back()->get_bool_attribute(ID::allseq)) { + extra->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 (extra->current_wire_rand || attr_anyconst || attr_anyseq || attr_allconst || attr_allseq) { + auto wire = std::make_unique(@$, AST_IDENTIFIER); + auto fcall = std::make_unique(@$, AST_FCALL); + wire->str = extra->ast_stack.back()->children.back()->str; + fcall->str = extra->current_wire_const ? "\\$anyconst" : "\\$anyseq"; if (attr_anyconst) fcall->str = "\\$anyconst"; if (attr_anyseq) @@ -2023,126 +2126,123 @@ wire_name_and_opt_assign: 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)); + fcall->attributes[ID::reg] = AstNode::mkconst_str(@$, RTLIL::unescape_id(wire->str)); + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_ASSIGN, std::move(wire), std::move(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; + wire_name TOK_EQ expr { + auto wire = std::make_unique(@$, AST_IDENTIFIER); + wire->str = extra->ast_stack.back()->children.back()->str; + if (extra->astbuf1->is_input) { + extra->astbuf1->attributes[ID::defaultvalue] = std::move($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); + else if (extra->astbuf1->is_reg || extra->astbuf1->is_logic){ + auto assign = std::make_unique(@$, AST_ASSIGN_LE, std::move(wire), std::move($3)); + SET_AST_NODE_LOC(assign.get(), @1, @3); + auto block = std::make_unique(@$, AST_BLOCK, std::move(assign)); + SET_AST_NODE_LOC(block.get(), @1, @3); + auto init = std::make_unique(@$, AST_INITIAL, std::move(block)); + SET_AST_NODE_LOC(init.get(), @1, @3); - 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); + extra->ast_stack.back()->children.push_back(std::move(init)); } else { - AstNode *assign = new AstNode(AST_ASSIGN, wire, $3); - SET_AST_NODE_LOC(assign, @1, @3); - ast_stack.back()->children.push_back(assign); + auto assign = std::make_unique(@$, AST_ASSIGN, std::move(wire), std::move($3)); + SET_AST_NODE_LOC(assign.get(), @1, @3); + extra->ast_stack.back()->children.push_back(std::move(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(); + if (extra->astbuf1 == nullptr) + err_at_loc(@1, "Internal error - should not happen - no AST_WIRE node."); + auto node = extra->astbuf1->clone(); node->str = *$1; - append_attr_clone(node, albuf); - if (astbuf2 != NULL) - node->children.push_back(astbuf2->clone()); - if ($2 != NULL) { + append_attr_clone(node.get(), extra->albuf); + if (extra->astbuf2 != nullptr) + node->children.push_back(extra->astbuf2->clone()); + if ($2 != nullptr) { 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); + err_at_loc(@2, "input/output/inout ports cannot have unpacked dimensions."); + if (!extra->astbuf2 && !node->is_custom_type) { + addRange(node.get(), 0, 0, false); } - rewriteAsMemoryNode(node, $2); + rewriteAsMemoryNode(node.get(), std::move($2)); } - if (current_function_or_task) { + if (extra->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) { + node->port_id = extra->current_function_or_task_port_id++; + } else if (extra->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()); + err_at_loc(@1, "Cannot declare module port `%s' within a generate block.", *$1); } else { - if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { - port_stubs[*$1] = ++port_counter; + if (extra->do_not_require_port_stubs && (node->is_input || node->is_output) && extra->port_stubs.count(*$1) == 0) { + extra->port_stubs[*$1] = ++extra->port_counter; } - if (port_stubs.count(*$1) != 0) { + if (extra->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); + err_at_loc(@1, "Module port `%s' is neither input nor output.", *$1); + if (node->is_reg && node->is_input && !node->is_output && !mode->sv) + err_at_loc(@1, "Input port `%s' is declared as register.", *$1); + node->port_id = extra->port_stubs[*$1]; + extra->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()); + err_at_loc(@1, "Module port `%s' is not declared in module header.", *$1); } } //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); + SET_AST_NODE_LOC(node.get(), @1, @1); + extra->ast_stack.back()->children.push_back(std::move(node)); - delete $1; }; assign_stmt: - TOK_ASSIGN delay assign_expr_list ';'; + TOK_ASSIGN delay assign_expr_list TOK_SEMICOL; assign_expr_list: - assign_expr | assign_expr_list ',' assign_expr; + assign_expr | assign_expr_list TOK_COMMA assign_expr; assign_expr: - lvalue '=' expr { - AstNode *node = new AstNode(AST_ASSIGN, $1, $3); + lvalue TOK_EQ expr { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSIGN, std::move($1), std::move($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); } +type_name: TOK_ID { $$ = std::move($1); } // first time seen + | TOK_USER_TYPE { if (extra->isInLocalScope($1.get())) err_at_loc(@1, "Duplicate declaration of TYPEDEF '%s'", $1->c_str()+1); $$ = std::move($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); + TOK_TYPEDEF typedef_base_type range_or_multirange type_name range_or_multirange TOK_SEMICOL { + extra->astbuf1 = std::move($2); + extra->astbuf2 = checkRange(extra->astbuf1.get(), std::move($3)); + bool has_a_range = (bool)extra->astbuf2; + if (extra->astbuf2) { + extra->astbuf1->children.push_back(std::move(extra->astbuf2)); } - addTypedefNode($4, astbuf1); } - | TOK_TYPEDEF enum_struct_type type_name ';' { addTypedefNode($3, $2); } + + if ($5 != nullptr) { + if (!has_a_range && !extra->astbuf1->is_custom_type) { + addRange(extra->astbuf1.get(), 0, 0, false); + } + rewriteAsMemoryNode(extra->astbuf1.get(), std::move($5)); + } + extra->addTypedefNode($4.get(), std::move(extra->astbuf1)); } + | TOK_TYPEDEF enum_struct_type type_name TOK_SEMICOL { extra->addTypedefNode($3.get(), std::move($2)); } ; typedef_base_type: hierarchical_type_id { - $$ = new AstNode(AST_WIRE); + $$ = std::make_unique(@$, AST_WIRE); $$->is_logic = true; - addWiretypeNode($1, $$); + extra->addWiretypeNode($1.get(), $$.get()); } | integer_vector_type opt_signedness_default_unsigned { - $$ = new AstNode(AST_WIRE); - if ($1 == TOK_REG) { + $$ = std::make_unique(@$, AST_WIRE); + if ($1 == token::TOK_REG) { $$->is_reg = true; } else { $$->is_logic = true; @@ -2150,7 +2250,7 @@ typedef_base_type: $$->is_signed = $2; } | integer_atom_type opt_signedness_default_signed { - $$ = new AstNode(AST_WIRE); + $$ = std::make_unique(@$, AST_WIRE); $$->is_logic = true; $$->is_signed = $2; $$->range_left = $1 - 1; @@ -2158,124 +2258,130 @@ typedef_base_type: }; enum_struct_type: - enum_type - | struct_type + enum_type { $$ = std::move($1); } + | struct_type { $$ = std::move($1); } ; 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; + extra->astbuf1 = std::make_unique(@$, AST_CELL); + append_attr(extra->astbuf1.get(), std::move($1)); + extra->astbuf1->children.push_back(std::make_unique(@$, AST_CELLTYPE)); + extra->astbuf1->children[0]->str = *$2; + } cell_parameter_list_opt cell_list TOK_SEMICOL { + (void)extra->astbuf1.reset(); } | attr tok_prim_wrapper delay { - astbuf1 = new AstNode(AST_PRIMITIVE); - astbuf1->str = *$2; - append_attr(astbuf1, $1); - delete $2; - } prim_list ';' { - delete astbuf1; + extra->astbuf1 = std::make_unique(@$, AST_PRIMITIVE); + extra->astbuf1->str = *$2; + append_attr(extra->astbuf1.get(), std::move($1)); + } prim_list TOK_SEMICOL { + (void)extra->astbuf1.reset(); }; tok_prim_wrapper: TOK_PRIMITIVE { - $$ = $1; + $$ = std::move($1); } | TOK_OR { - $$ = new std::string("or"); + $$ = std::make_unique("or"); }; cell_list: single_cell | - cell_list ',' single_cell; + cell_list TOK_COMMA 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, @$); + extra->astbuf2 = extra->astbuf1->clone(); + if (extra->astbuf2->type != AST_PRIMITIVE) + extra->astbuf2->str = *$1; + // TODO optimize again + extra->cell_hack = extra->astbuf2.get(); + extra->ast_stack.back()->children.push_back(std::move(extra->astbuf2)); + } TOK_LPAREN cell_port_list TOK_RPAREN { + log_assert(extra->cell_hack); + SET_AST_NODE_LOC(extra->cell_hack, @1, @$); + extra->cell_hack = nullptr; } 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, @$); + extra->astbuf2 = extra->astbuf1->clone(); + if (extra->astbuf2->type != AST_PRIMITIVE) + extra->astbuf2->str = *$1; + // TODO optimize again + extra->cell_hack = extra->astbuf2.get(); + extra->ast_stack.back()->children.push_back(std::make_unique(@$, AST_CELLARRAY, std::move($2), std::move(extra->astbuf2))); + } TOK_LPAREN cell_port_list TOK_RPAREN{ + log_assert(extra->cell_hack); + SET_AST_NODE_LOC(extra->cell_hack, @1, @$); + extra->cell_hack = nullptr; }; cell_list_no_array: single_cell_no_array | - cell_list_no_array ',' single_cell_no_array; + cell_list_no_array TOK_COMMA single_cell_no_array; prim_list: single_prim | - prim_list ',' single_prim; + prim_list TOK_COMMA 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, @$); + extra->astbuf2 = extra->astbuf1->clone(); + log_assert(!extra->cell_hack); + extra->cell_hack = extra->astbuf2.get(); + // TODO optimize again + extra->ast_stack.back()->children.push_back(std::move(extra->astbuf2)); + } TOK_LPAREN cell_port_list TOK_RPAREN { + log_assert(extra->cell_hack); + SET_AST_NODE_LOC(extra->cell_hack, @1, @$); + extra->cell_hack = nullptr; } cell_parameter_list_opt: - '#' '(' cell_parameter_list ')' | %empty; + TOK_HASH TOK_LPAREN cell_parameter_list TOK_RPAREN | %empty; cell_parameter_list: - cell_parameter | cell_parameter_list ',' cell_parameter; + cell_parameter | cell_parameter_list TOK_COMMA cell_parameter; cell_parameter: %empty | expr { - AstNode *node = new AstNode(AST_PARASET); - astbuf1->children.push_back(node); - node->children.push_back($1); + auto node = std::make_unique(@$, AST_PARASET); + node->children.push_back(std::move($1)); + extra->astbuf1->children.push_back(std::move(node)); } | - '.' TOK_ID '(' ')' { - // delete unused TOK_ID - delete $2; + TOK_DOT TOK_ID TOK_LPAREN TOK_RPAREN { + // just ignore empty parameters } | - '.' TOK_ID '(' expr ')' { - AstNode *node = new AstNode(AST_PARASET); + TOK_DOT TOK_ID TOK_LPAREN expr TOK_RPAREN { + auto node = std::make_unique(@$, AST_PARASET); node->str = *$2; - astbuf1->children.push_back(node); - node->children.push_back($4); - delete $2; + node->children.push_back(std::move($4)); + extra->astbuf1->children.push_back(std::move(node)); }; cell_port_list: cell_port_list_rules { // remove empty args from end of list - while (!astbuf2->children.empty()) { - AstNode *node = astbuf2->children.back(); + while (!extra->cell_hack->children.empty()) { + auto& node = extra->cell_hack->children.back(); if (node->type != AST_ARGUMENT) break; if (!node->children.empty()) break; if (!node->str.empty()) break; - astbuf2->children.pop_back(); - delete node; + extra->cell_hack->children.pop_back(); } // check port types bool has_positional_args = false; bool has_named_args = false; - for (auto node : astbuf2->children) { + for (auto& node : extra->cell_hack->children) { if (node->type != AST_ARGUMENT) continue; if (node->str.empty()) has_positional_args = true; @@ -2284,53 +2390,44 @@ cell_port_list: } if (has_positional_args && has_named_args) - frontend_verilog_yyerror("Mix of positional and named cell ports."); + err_at_loc(@1, "Mix of positional and named cell ports."); }; cell_port_list_rules: - cell_port | cell_port_list_rules ',' cell_port; + cell_port | cell_port_list_rules TOK_COMMA cell_port; cell_port: attr { - AstNode *node = new AstNode(AST_ARGUMENT); - astbuf2->children.push_back(node); - free_attr($1); + auto node = std::make_unique(@$, AST_ARGUMENT); + extra->cell_hack->children.push_back(std::move(node)); } | attr expr { - AstNode *node = new AstNode(AST_ARGUMENT); - astbuf2->children.push_back(node); - node->children.push_back($2); - free_attr($1); + auto node = std::make_unique(@$, AST_ARGUMENT); + node->children.push_back(std::move($2)); + extra->cell_hack->children.push_back(std::move(node)); } | - attr '.' TOK_ID '(' expr ')' { - AstNode *node = new AstNode(AST_ARGUMENT); + attr TOK_DOT TOK_ID TOK_LPAREN expr TOK_RPAREN { + auto node = std::make_unique(@$, AST_ARGUMENT); node->str = *$3; - astbuf2->children.push_back(node); - node->children.push_back($5); - delete $3; - free_attr($1); + node->children.push_back(std::move($5)); + extra->cell_hack->children.push_back(std::move(node)); } | - attr '.' TOK_ID '(' ')' { - AstNode *node = new AstNode(AST_ARGUMENT); + attr TOK_DOT TOK_ID TOK_LPAREN TOK_RPAREN { + auto node = std::make_unique(@$, AST_ARGUMENT); node->str = *$3; - astbuf2->children.push_back(node); - delete $3; - free_attr($1); + extra->cell_hack->children.push_back(std::move(node)); } | - attr '.' TOK_ID { - AstNode *node = new AstNode(AST_ARGUMENT); + attr TOK_DOT TOK_ID { + auto node = std::make_unique(@$, AST_ARGUMENT); node->str = *$3; - astbuf2->children.push_back(node); - node->children.push_back(new AstNode(AST_IDENTIFIER)); + node->children.push_back(std::make_unique(@$, AST_IDENTIFIER)); node->children.back()->str = *$3; - delete $3; - free_attr($1); + extra->cell_hack->children.push_back(std::move(node)); } | 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); + if (!mode->sv) + err_at_loc(@2, "Wildcard port connections are only supported in SystemVerilog mode."); + extra->cell_hack->attributes[ID::wildcard_port_conns] = AstNode::mkconst_int(@2, 1, false); }; always_comb_or_latch: @@ -2351,100 +2448,88 @@ always_or_always_ff: always_stmt: attr always_or_always_ff { - AstNode *node = new AstNode(AST_ALWAYS); - append_attr(node, $1); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_ALWAYS)); + append_attr(node, std::move($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); + node->attributes[ID::always_ff] = AstNode::mkconst_int(@2, 1, false); } always_cond { - AstNode *block = new AstNode(AST_BLOCK); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); + (void)extra->pushChild(std::make_unique(@$, AST_BLOCK)); } behavioral_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @6, @6); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @6, @6); + extra->ast_stack.pop_back(); - SET_AST_NODE_LOC(ast_stack.back(), @2, @$); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @$); + extra->ast_stack.pop_back(); SET_RULE_LOC(@$, @2, @$); } | attr always_comb_or_latch { - AstNode *node = new AstNode(AST_ALWAYS); - append_attr(node, $1); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_ALWAYS)); + append_attr(node, std::move($1)); if ($2) - node->attributes[ID::always_latch] = AstNode::mkconst_int(1, false); + node->attributes[ID::always_latch] = AstNode::mkconst_int(@2, 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); + node->attributes[ID::always_comb] = AstNode::mkconst_int(@2, 1, false); + (void)extra->pushChild(std::make_unique(@$, AST_BLOCK)); } behavioral_stmt { - ast_stack.pop_back(); - ast_stack.pop_back(); + extra->ast_stack.pop_back(); + extra->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); + AstNode* node = extra->pushChild(std::make_unique(@$, AST_INITIAL)); + append_attr(node, std::move($1)); + (void)extra->pushChild(std::make_unique(@$, AST_BLOCK)); } behavioral_stmt { - ast_stack.pop_back(); - ast_stack.pop_back(); + extra->ast_stack.pop_back(); + extra->ast_stack.pop_back(); }; always_cond: - '@' '(' always_events ')' | - '@' '(' '*' ')' | - '@' ATTR_BEGIN ')' | - '@' '(' ATTR_END | - '@' '*' | + TOK_AT TOK_LPAREN always_events TOK_RPAREN | + TOK_AT TOK_LPAREN TOK_ASTER TOK_RPAREN | + TOK_AT ATTR_BEGIN TOK_RPAREN | + TOK_AT TOK_LPAREN ATTR_END | + TOK_AT TOK_ASTER | %empty; always_events: always_event | always_events TOK_OR always_event | - always_events ',' always_event; + always_events TOK_COMMA 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); + auto node = std::make_unique(@$, AST_POSEDGE); + SET_AST_NODE_LOC(node.get(), @1, @1); + node->children.push_back(std::move($2)); + extra->ast_stack.back()->children.push_back(std::move(node)); } | 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); + auto node = std::make_unique(@$, AST_NEGEDGE); + SET_AST_NODE_LOC(node.get(), @1, @1); + node->children.push_back(std::move($2)); + extra->ast_stack.back()->children.push_back(std::move(node)); } | expr { - AstNode *node = new AstNode(AST_EDGE); - ast_stack.back()->children.push_back(node); - node->children.push_back($1); + auto node = std::make_unique(@$, AST_EDGE); + node->children.push_back(std::move($1)); + extra->ast_stack.back()->children.push_back(std::move(node)); }; opt_label: - ':' TOK_ID { - $$ = $2; + TOK_COL TOK_ID { + $$ = std::move($2); } | %empty { - $$ = NULL; + $$ = nullptr; }; opt_sva_label: - TOK_SVA_LABEL ':' { - $$ = $1; + TOK_SVA_LABEL TOK_COL { + $$ = std::move($1); } | %empty { - $$ = NULL; + $$ = nullptr; }; opt_property: @@ -2460,21 +2545,19 @@ opt_property: modport_stmt: TOK_MODPORT TOK_ID { - AstNode *modport = new AstNode(AST_MODPORT); - ast_stack.back()->children.push_back(modport); - ast_stack.push_back(modport); + AstNode* modport = extra->pushChild(std::make_unique(@$, AST_MODPORT)); modport->str = *$2; - delete $2; + } modport_args_opt { - ast_stack.pop_back(); - log_assert(ast_stack.size() == 2); - } ';' + extra->ast_stack.pop_back(); + log_assert(extra->ast_stack.size() == 2); + } TOK_SEMICOL modport_args_opt: - '(' ')' | '(' modport_args optional_comma ')'; + TOK_LPAREN TOK_RPAREN | TOK_LPAREN modport_args optional_comma TOK_RPAREN; modport_args: - modport_arg | modport_args ',' modport_arg; + modport_arg | modport_args TOK_COMMA modport_arg; modport_arg: modport_type_token modport_member | @@ -2482,222 +2565,174 @@ modport_arg: modport_member: TOK_ID { - AstNode *modport_member = new AstNode(AST_MODPORTMEMBER); - ast_stack.back()->children.push_back(modport_member); + AstNode* modport_member = extra->saveChild(std::make_unique(@$, AST_MODPORTMEMBER)); modport_member->str = *$1; - modport_member->is_input = current_modport_input; - modport_member->is_output = current_modport_output; - delete $1; + modport_member->is_input = extra->current_modport_input; + modport_member->is_output = extra->current_modport_output; + } modport_type_token: - TOK_INPUT {current_modport_input = 1; current_modport_output = 0;} | TOK_OUTPUT {current_modport_input = 0; current_modport_output = 1;} + TOK_INPUT {extra->current_modport_input = 1; extra->current_modport_output = 0;} | TOK_OUTPUT {extra->current_modport_input = 0; extra->current_modport_output = 1;} assert: - opt_sva_label TOK_ASSERT opt_property '(' expr ')' ';' { - if (noassert_mode) { - delete $5; + opt_sva_label TOK_ASSERT opt_property TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + if (mode->noassert) { + } else { - AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5); + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assume_asserts ? AST_ASSUME : AST_ASSERT, std::move($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; + opt_sva_label TOK_ASSUME opt_property TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + if (mode->noassume) { } else { - AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5); + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assert_assumes ? AST_ASSERT : AST_ASSUME, std::move($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; + opt_sva_label TOK_ASSERT opt_property TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + if (mode->noassert) { } else { - AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6); + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assume_asserts ? AST_FAIR : AST_LIVE, std::move($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; + opt_sva_label TOK_ASSUME opt_property TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + if (mode->noassume) { } else { - AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6); + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assert_assumes ? AST_LIVE : AST_FAIR, std::move($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); + opt_sva_label TOK_COVER opt_property TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_COVER, std::move($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)); + opt_sva_label TOK_COVER opt_property TOK_LPAREN TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, 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)); + opt_sva_label TOK_COVER TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, 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; + opt_sva_label TOK_RESTRICT opt_property TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + if (mode->norestrict) { } else { - AstNode *node = new AstNode(AST_ASSUME, $5); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSUME, std::move($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; + warn_at_loc(@3, "SystemVerilog does not allow \"restrict\" without \"property\"."); } | - opt_sva_label TOK_RESTRICT opt_property '(' TOK_EVENTUALLY expr ')' ';' { - if (norestrict_mode) { - delete $6; + opt_sva_label TOK_RESTRICT opt_property TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + if (mode->norestrict) { } else { - AstNode *node = new AstNode(AST_FAIR, $6); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_FAIR, std::move($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; + warn_at_loc(@3, "SystemVerilog does not allow \"restrict\" without \"property\"."); }; assert_property: - opt_sva_label TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' { - AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5); + opt_sva_label TOK_ASSERT TOK_PROPERTY TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assume_asserts ? AST_ASSUME : AST_ASSERT, std::move($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; + extra->ast_stack.back()->children.back()->str = *$1; } } | - opt_sva_label TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' { - AstNode *node = new AstNode(AST_ASSUME, $5); + opt_sva_label TOK_ASSUME TOK_PROPERTY TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSUME, std::move($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; + extra->ast_stack.back()->children.back()->str = *$1; } } | - opt_sva_label TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { - AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6); + opt_sva_label TOK_ASSERT TOK_PROPERTY TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, mode->assume_asserts ? AST_FAIR : AST_LIVE, std::move($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; + extra->ast_stack.back()->children.back()->str = *$1; } } | - opt_sva_label TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { - AstNode *node = new AstNode(AST_FAIR, $6); + opt_sva_label TOK_ASSUME TOK_PROPERTY TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_FAIR, std::move($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; + extra->ast_stack.back()->children.back()->str = *$1; } } | - opt_sva_label TOK_COVER TOK_PROPERTY '(' expr ')' ';' { - AstNode *node = new AstNode(AST_COVER, $5); + opt_sva_label TOK_COVER TOK_PROPERTY TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_COVER, std::move($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; + extra->ast_stack.back()->children.back()->str = *$1; } } | - opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' { - if (norestrict_mode) { - delete $5; + opt_sva_label TOK_RESTRICT TOK_PROPERTY TOK_LPAREN expr TOK_RPAREN TOK_SEMICOL { + if (mode->norestrict) { } else { - AstNode *node = new AstNode(AST_ASSUME, $5); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSUME, std::move($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; + extra->ast_stack.back()->children.back()->str = *$1; } } } | - opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { - if (norestrict_mode) { - delete $6; + opt_sva_label TOK_RESTRICT TOK_PROPERTY TOK_LPAREN TOK_EVENTUALLY expr TOK_RPAREN TOK_SEMICOL { + if (mode->norestrict) { } else { - AstNode *node = new AstNode(AST_FAIR, $6); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_FAIR, std::move($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; + extra->ast_stack.back()->children.back()->str = *$1; } } }; simple_behavioral_stmt: - attr lvalue '=' delay expr { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, $5); - ast_stack.back()->children.push_back(node); + attr lvalue TOK_EQ delay expr { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSIGN_EQ, std::move($2), std::move($5))); SET_AST_NODE_LOC(node, @2, @5); - append_attr(node, $1); + append_attr(node, std::move($1)); } | attr lvalue attr inc_or_dec_op { - addIncOrDecStmt($1, $2, $3, $4, @1, @4); + extra->addIncOrDecStmt(std::move($1), std::move($2), std::move($3), $4, location_range(@1, @4)); } | attr inc_or_dec_op attr lvalue { - addIncOrDecStmt($1, $4, $3, $2, @1, @4); + extra->addIncOrDecStmt(std::move($1), std::move($4), std::move($3), $2, location_range(@1, @4)); } | attr lvalue OP_LE delay expr { - AstNode *node = new AstNode(AST_ASSIGN_LE, $2, $5); - ast_stack.back()->children.push_back(node); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSIGN_LE, std::move($2), std::move($5))); SET_AST_NODE_LOC(node, @2, @5); - append_attr(node, $1); + append_attr(node, std::move($1)); } | attr lvalue asgn_binop delay expr { - addAsgnBinopStmt($1, $2, $3, $5, @2, @5); + (void)extra->addAsgnBinopStmt(std::move($1), std::move($2), $3, std::move($5)); }; asgn_binop: @@ -2721,223 +2756,255 @@ inc_or_dec_op: TOK_DECREMENT { $$ = AST_SUB; } ; for_initialization: - TOK_ID '=' expr { - AstNode *ident = new AstNode(AST_IDENTIFIER); + TOK_ID TOK_EQ expr { + auto ident = std::make_unique(@$, 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; + auto node = std::make_unique(@$, AST_ASSIGN_EQ, std::move(ident), std::move($3)); + SET_AST_NODE_LOC(node.get(), @1, @3); + extra->ast_stack.back()->children.push_back(std::move(node)); } | non_io_wire_type range TOK_ID { - frontend_verilog_yyerror("For loop variable declaration is missing initialization!"); + err_at_loc(@3, "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!"); + non_io_wire_type range TOK_ID TOK_EQ expr { + if (!mode->sv) + err_at_loc(@4, "For loop inline variable declaration is only supported in SystemVerilog mode!"); // loop variable declaration - AstNode *wire = $1; - AstNode *range = checkRange(wire, $2); + auto wire = std::move($1); + auto range = checkRange(wire.get(), std::move($2)); + SET_AST_NODE_LOC(wire.get(), @1, @3); + SET_AST_NODE_LOC(range.get(), @2, @2); if (range != nullptr) - wire->children.push_back(range); - SET_AST_NODE_LOC(wire, @1, @3); - SET_AST_NODE_LOC(range, @2, @2); + wire->children.push_back(std::move(range)); - AstNode *ident = new AstNode(AST_IDENTIFIER); + auto ident = std::make_unique(@$, 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); + AstNode *parent = extra->ast_stack.at(extra->ast_stack.size() - 2); + auto& loop = parent->children.back(); + log_assert(extra->ast_stack.back() == loop.get()); // 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); + SET_AST_NODE_LOC(ident.get(), @3, @3); + auto asgn = std::make_unique(@$, AST_ASSIGN_EQ, std::move(ident), std::move($5)); + SET_AST_NODE_LOC(asgn.get(), @3, @5); + loop->children.push_back(std::move(asgn)); // inject a wrapping block to declare the loop variable and // contain the current loop - AstNode *wrapper = new AstNode(AST_BLOCK); + auto wrapper = std::make_unique(@$, 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` + wrapper->children.push_back(std::move(wire)); + wrapper->children.push_back(std::move(loop)); + parent->children.back() = std::move(wrapper); }; // 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); - } | + simple_behavioral_stmt TOK_SEMICOL | + attr TOK_SEMICOL | attr hierarchical_id { - AstNode *node = new AstNode(AST_TCALL); + AstNode* node = extra->pushChild(std::make_unique(@$, 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(); + append_attr(node, std::move($1)); + } opt_arg_list TOK_SEMICOL{ + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @5); + extra->ast_stack.pop_back(); } | attr TOK_MSG_TASKS { - AstNode *node = new AstNode(AST_TCALL); + AstNode* node = extra->pushChild(std::make_unique(@$, 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(); + append_attr(node, std::move($1)); + } opt_arg_list TOK_SEMICOL{ + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @5); + extra->ast_stack.pop_back(); } | attr TOK_BEGIN { - enterTypeScope(); + extra->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) + AstNode* node = extra->pushChild(std::make_unique(@$, AST_BLOCK)); + append_attr(node, std::move($1)); + if ($4 != nullptr) node->str = *$4; } behavioral_stmt_list TOK_END opt_label { - exitTypeScope(); - checkLabelsMatch("Begin label", $4, $8); - AstNode *node = ast_stack.back(); + extra->exitTypeScope(); + checkLabelsMatch(@8, "Begin label", $4.get(), $8.get()); + AstNode *node = extra->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 (mode->sv && node->str.empty()) + for (auto& 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(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @8); + extra->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); + attr TOK_FOR TOK_LPAREN { + AstNode* node = extra->pushChild(std::make_unique(@$, AST_FOR)); + append_attr(node, std::move($1)); + } for_initialization TOK_SEMICOL expr { + extra->ast_stack.back()->children.push_back(std::move($7)); + } TOK_SEMICOL simple_behavioral_stmt TOK_RPAREN { + AstNode* block = extra->pushChild(std::make_unique(@$, 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(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @13, @13); + extra->ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @13); + extra->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); + attr TOK_WHILE TOK_LPAREN expr TOK_RPAREN { + AstNode* node = extra->pushChild(std::make_unique(@$, AST_WHILE)); + append_attr(node, std::move($1)); + auto block_owned = std::make_unique(@$, AST_BLOCK); + auto* block = block_owned.get(); + extra->ast_stack.back()->children.push_back(std::move($4)); + extra->ast_stack.back()->children.push_back(std::move(block_owned)); + extra->ast_stack.push_back(block); } behavioral_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @7, @7); - ast_stack.pop_back(); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @7, @7); + extra->ast_stack.pop_back(); + extra->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); + attr TOK_REPEAT TOK_LPAREN expr TOK_RPAREN { + AstNode* node = extra->pushChild(std::make_unique(@$, AST_REPEAT)); + append_attr(node, std::move($1)); + auto block_owned = std::make_unique(@$, AST_BLOCK); + auto* block = block_owned.get(); + extra->ast_stack.back()->children.push_back(std::move($4)); + extra->ast_stack.back()->children.push_back(std::move(block_owned)); + extra->ast_stack.push_back(block); } behavioral_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @7, @7); - ast_stack.pop_back(); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @7, @7); + extra->ast_stack.pop_back(); + extra->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); + if_attr TOK_IF TOK_LPAREN expr TOK_RPAREN { + std::unique_ptr node_owned; + AstNode* node = nullptr; + AstNode *context = extra->ast_stack.back(); + bool patch_block_on_stack = false; + if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if)) { + AstNode *outer = extra->ast_stack[extra->ast_stack.size() - 2]; + log_assert (outer && outer->type == AST_CASE); + if (outer->get_bool_attribute(ID::parallel_case)) { + // parallel "else if": append condition to outer "if" + node = outer; + log_assert (node->children.size()); + node->children.pop_back(); + // `context` has been killed as a grandchild of `outer` + // we have to undangle it from the stack + patch_block_on_stack = true; + } else if (outer->get_bool_attribute(ID::full_case)) + (*$1)[ID::full_case] = AstNode::mkconst_int(@$, 1, false); + } + auto expr = std::make_unique(@$, AST_REDUCE_BOOL, std::move($4)); + if (!node) { + // not parallel "else if": begin new construction + node_owned = std::make_unique(@$, AST_CASE); + node = node_owned.get(); + append_attr(node, std::move($1)); + node->children.push_back(node->get_bool_attribute(ID::parallel_case) ? AstNode::mkconst_int(@$, 1, false, 1) : expr->clone()); + extra->ast_stack.back()->children.push_back(std::move(node_owned)); + } + auto block_owned = std::make_unique(@$, AST_BLOCK); + auto* block = block_owned.get(); + auto cond_owned = std::make_unique(@$, AST_COND, node->get_bool_attribute(ID::parallel_case) ? std::move(expr) : AstNode::mkconst_int(@$, 1, false, 1), std::move(block_owned)); + SET_AST_NODE_LOC(cond_owned.get(), @4, @4); + node->children.push_back(std::move(cond_owned)); + // Double it and give it to the next person + if (patch_block_on_stack) + extra->ast_stack.back() = block; + extra->ast_stack.push_back(node); + extra->ast_stack.push_back(block); } behavioral_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @7, @7); + SET_AST_NODE_LOC(extra->ast_stack.back(), @7, @7); } optional_else { - ast_stack.pop_back(); - SET_AST_NODE_LOC(ast_stack.back(), @2, @9); - ast_stack.pop_back(); + extra->ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @9); + extra->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); + case_attr case_type TOK_LPAREN expr TOK_RPAREN { + AstNode* node = extra->pushChild(std::make_unique(@$, AST_CASE, std::move($4))); + append_attr(node, std::move($1)); + SET_AST_NODE_LOC(extra->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(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @9); + extra->case_type_stack.pop_back(); + extra->ast_stack.pop_back(); + }; + +if_attr: + attr { + $$ = std::move($1); + } | + attr TOK_UNIQUE0 { + AstNode *context = extra->ast_stack.back(); + if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if)) + err_at_loc(@2, "unique0 keyword cannot be used for 'else if' branch."); + (*$1)[ID::parallel_case] = AstNode::mkconst_int(@$, 1, false); + $$ = std::move($1); + } | + attr TOK_PRIORITY { + AstNode *context = extra->ast_stack.back(); + if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if)) + err_at_loc(@2, "priority keyword cannot be used for 'else if' branch."); + (*$1)[ID::full_case] = AstNode::mkconst_int(@$, 1, false); + $$ = std::move($1); + } | + attr TOK_UNIQUE { + AstNode *context = extra->ast_stack.back(); + if (context && context->type == AST_BLOCK && context->get_bool_attribute(ID::promoted_if)) + err_at_loc(@2, "unique keyword cannot be used for 'else if' branch."); + (*$1)[ID::full_case] = AstNode::mkconst_int(@$, 1, false); + (*$1)[ID::parallel_case] = AstNode::mkconst_int(@$, 1, false); + $$ = std::move($1); }; case_attr: attr { - $$ = $1; + $$ = std::move($1); } | attr TOK_UNIQUE0 { - (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); - $$ = $1; + (*$1)[ID::parallel_case] = AstNode::mkconst_int(@$, 1, false); + $$ = std::move($1); } | attr TOK_PRIORITY { - (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); - $$ = $1; + (*$1)[ID::full_case] = AstNode::mkconst_int(@$, 1, false); + $$ = std::move($1); } | attr TOK_UNIQUE { - (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); - (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); - $$ = $1; + (*$1)[ID::full_case] = AstNode::mkconst_int(@$, 1, false); + (*$1)[ID::parallel_case] = AstNode::mkconst_int(@$, 1, false); + $$ = std::move($1); }; case_type: TOK_CASE { - case_type_stack.push_back(0); + extra->case_type_stack.push_back(0); } | TOK_CASEX { - case_type_stack.push_back('x'); + extra->case_type_stack.push_back('x'); } | TOK_CASEZ { - case_type_stack.push_back('z'); + extra->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); + if (extra->ast_stack.back()->attributes.count(ID::full_case) == 0) + extra->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); + if (extra->ast_stack.back()->attributes.count(ID::parallel_case) == 0) + extra->ast_stack.back()->attributes[ID::parallel_case] = AstNode::mkconst_int(@$, 1, false); } | %empty; @@ -2947,15 +3014,18 @@ behavioral_stmt_list: optional_else: TOK_ELSE { - AstNode *block = new AstNode(AST_BLOCK); - AstNode *cond = new AstNode(AST_COND, new AstNode(AST_DEFAULT), block); + extra->ast_stack.pop_back(); + auto block_owned = std::make_unique(@$, AST_BLOCK); + auto* block = block_owned.get(); + block->attributes[ID::promoted_if] = AstNode::mkconst_int(@$, 1, false); + AstNode* cond = extra->saveChild( + std::make_unique(@$, AST_COND, + std::make_unique(@$, AST_DEFAULT), + std::move(block_owned))); + extra->ast_stack.push_back(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); + SET_AST_NODE_LOC(extra->ast_stack.back(), @3, @3); } | %empty %prec FAKE_THEN; @@ -2965,21 +3035,18 @@ case_body: 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); + (void)extra->pushChild(std::make_unique( + @$, + extra->case_type_stack.size() && extra->case_type_stack.back() == 'x' ? AST_CONDX : + extra->case_type_stack.size() && extra->case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND)); } 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); + (void)extra->pushChild(std::make_unique(@$, AST_BLOCK)); + extra->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(); + extra->case_type_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @4, @4); + extra->ast_stack.pop_back(); + extra->ast_stack.pop_back(); }; gen_case_body: @@ -2988,87 +3055,80 @@ gen_case_body: 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); + (void)extra->pushChild(std::make_unique( + @$, + extra->case_type_stack.size() && extra->case_type_stack.back() == 'x' ? AST_CONDX : + extra->case_type_stack.size() && extra->case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND)); } case_select { - case_type_stack.push_back(0); - SET_AST_NODE_LOC(ast_stack.back(), @2, @2); + extra->case_type_stack.push_back(0); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @2); } gen_stmt_block { - case_type_stack.pop_back(); - ast_stack.pop_back(); + extra->case_type_stack.pop_back(); + extra->ast_stack.pop_back(); }; case_select: - case_expr_list ':' | + case_expr_list TOK_COL | TOK_DEFAULT; case_expr_list: TOK_DEFAULT { - AstNode *node = new AstNode(AST_DEFAULT); + AstNode* node = extra->saveChild(std::make_unique(@$, 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); + AstNode* node = extra->saveChild(std::make_unique(@$, AST_IDENTIFIER)); + node->str = *$1; 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); + extra->ast_stack.back()->children.push_back(std::move($1)); } | - case_expr_list ',' expr { - ast_stack.back()->children.push_back($3); + case_expr_list TOK_COMMA expr { + extra->ast_stack.back()->children.push_back(std::move($3)); }; rvalue: - hierarchical_id '[' expr ']' '.' rvalue { - $$ = new AstNode(AST_PREFIX, $3, $6); + hierarchical_id TOK_LBRA expr TOK_RBRA TOK_DOT rvalue { + $$ = std::make_unique(@$, AST_PREFIX, std::move($3), std::move($6)); $$->str = *$1; - SET_AST_NODE_LOC($$, @1, @6); - delete $1; + SET_AST_NODE_LOC($$.get(), @1, @6); } | hierarchical_id range { - $$ = new AstNode(AST_IDENTIFIER, $2); + $$ = std::make_unique(@$, AST_IDENTIFIER, std::move($2)); $$->str = *$1; - SET_AST_NODE_LOC($$, @1, @1); - delete $1; + SET_AST_NODE_LOC($$.get(), @1, @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); + $$ = std::make_unique(@$, AST_IDENTIFIER, std::move($2)); $$->str = *$1; - SET_AST_NODE_LOC($$, @1, @1); - delete $1; + SET_AST_NODE_LOC($$.get(), @1, @1); }; lvalue: rvalue { - $$ = $1; + $$ = std::move($1); } | - '{' lvalue_concat_list '}' { - $$ = $2; + TOK_LCURL lvalue_concat_list TOK_RCURL { + $$ = std::move($2); }; lvalue_concat_list: expr { - $$ = new AstNode(AST_CONCAT); - $$->children.push_back($1); + $$ = std::make_unique(@$, AST_CONCAT); + $$->children.push_back(std::move($1)); } | - expr ',' lvalue_concat_list { - $$ = $3; - $$->children.push_back($1); + expr TOK_COMMA lvalue_concat_list { + $$ = std::move($3); + $$->children.push_back(std::move($1)); }; opt_arg_list: - '(' arg_list optional_comma ')' | + TOK_LPAREN arg_list optional_comma TOK_RPAREN | %empty; arg_list: @@ -3077,11 +3137,11 @@ arg_list: arg_list2: single_arg | - arg_list ',' single_arg; + arg_list TOK_COMMA single_arg; single_arg: expr { - ast_stack.back()->children.push_back($1); + extra->ast_stack.back()->children.push_back(std::move($1)); }; module_gen_body: @@ -3091,111 +3151,90 @@ module_gen_body: gen_stmt_or_module_body_stmt: gen_stmt | module_body_stmt | - attr ';' { - free_attr($1); - }; + attr TOK_SEMICOL; genvar_identifier: TOK_ID { - $$ = new AstNode(AST_IDENTIFIER); + $$ = std::make_unique(@$, AST_IDENTIFIER); $$->str = *$1; - delete $1; }; genvar_initialization: TOK_GENVAR genvar_identifier { - frontend_verilog_yyerror("Generate for loop variable declaration is missing initialization!"); + err_at_loc(@2, "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); + TOK_GENVAR genvar_identifier TOK_EQ expr { + if (!mode->sv) + err_at_loc(@3, "Generate for loop inline variable declaration is only supported in SystemVerilog mode!"); + AstNode* node = extra->saveChild(std::make_unique(@$, 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); + node = extra->saveChild(std::make_unique(@$, AST_ASSIGN_EQ, std::move($2), std::move($4))); 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); + genvar_identifier TOK_EQ expr { + AstNode* node = extra->saveChild(std::make_unique(@$, AST_ASSIGN_EQ, std::move($1), std::move($3))); 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_FOR TOK_LPAREN { + (void)extra->pushChild(std::make_unique(@$, AST_GENFOR)); + } genvar_initialization TOK_SEMICOL expr { + extra->ast_stack.back()->children.push_back(std::move($6)); + } TOK_SEMICOL simple_behavioral_stmt TOK_RPAREN gen_stmt_block { + SET_AST_NODE_LOC(extra->ast_stack.back(), @1, @11); + extra->rewriteGenForDeclInit(extra->ast_stack.back()); + extra->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); + TOK_IF TOK_LPAREN expr TOK_RPAREN { + (void)extra->pushChild(std::make_unique(@$, AST_GENIF)); + extra->ast_stack.back()->children.push_back(std::move($3)); } gen_stmt_block opt_gen_else { - SET_AST_NODE_LOC(ast_stack.back(), @1, @7); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @1, @7); + extra->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); + case_type TOK_LPAREN expr TOK_RPAREN { + (void)extra->pushChild(std::make_unique(@$, AST_GENCASE, std::move($3))); } gen_case_body TOK_ENDCASE { - case_type_stack.pop_back(); - SET_AST_NODE_LOC(ast_stack.back(), @1, @7); - ast_stack.pop_back(); + extra->case_type_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @1, @7); + extra->ast_stack.pop_back(); } | TOK_MSG_TASKS { - AstNode *node = new AstNode(AST_TECALL); + AstNode* node = extra->pushChild(std::make_unique(@$, 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(); + } opt_arg_list TOK_SEMICOL{ + SET_AST_NODE_LOC(extra->ast_stack.back(), @1, @3); + extra->ast_stack.pop_back(); }; gen_block: TOK_BEGIN { - enterTypeScope(); + extra->enterTypeScope(); } opt_label { - AstNode *node = new AstNode(AST_GENBLOCK); + AstNode* node = extra->pushChild(std::make_unique(@$, 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(); + extra->exitTypeScope(); + checkLabelsMatch(@7, "Begin label", $3.get(), $7.get()); + SET_AST_NODE_LOC(extra->ast_stack.back(), @1, @7); + extra->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); + (void)extra->pushChild(std::make_unique(@$, AST_GENBLOCK)); } gen_stmt_or_module_body_stmt { - SET_AST_NODE_LOC(ast_stack.back(), @2, @2); - ast_stack.pop_back(); + SET_AST_NODE_LOC(extra->ast_stack.back(), @2, @2); + extra->ast_stack.pop_back(); } | gen_block; opt_gen_else: @@ -3203,349 +3242,344 @@ opt_gen_else: expr: basic_expr { - $$ = $1; + $$ = std::move($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); + basic_expr TOK_QUE attr expr TOK_COL expr { + $$ = std::make_unique(@$, AST_TERNARY); + $$->children.push_back(std::move($1)); + $$->children.push_back(std::move($4)); + $$->children.push_back(std::move($6)); + SET_AST_NODE_LOC($$.get(), @1, @$); + append_attr($$.get(), std::move($3)); } | inc_or_dec_op attr rvalue { - $$ = addIncOrDecExpr($3, $2, $1, @1, @3, false); + $$ = extra->addIncOrDecExpr(std::move($3), std::move($2), $1, location_range(@1, @3), false, mode->sv); } | // 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); + $$ = extra->addIncOrDecExpr(std::move($1), nullptr, $2, location_range(@1, @2), true, mode->sv); }; basic_expr: rvalue { - $$ = $1; + $$ = std::move($1); } | - '(' expr ')' integral_number { + TOK_LPAREN expr TOK_RPAREN 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; + err_at_loc(@4, "Cast operation must be applied on sized constants e.g. () , while %s is not a sized constant.", *$4); + ConstParser p{@4}; + auto val = p.const2ast(*$4, extra->case_type_stack.size() == 0 ? 0 : extra->case_type_stack.back(), !mode->lib); + if (val == nullptr) + log_error("Value conversion failed: `%s'\n", *$4); + $$ = std::make_unique(@$, AST_TO_BITS, std::move($2), std::move(val)); } | 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); + err_at_loc(@2, "Cast operation must be applied on sized constants, e.g. \'d0, while %s is not a sized constant.", *$2); + auto bits = std::make_unique(@$, 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; + SET_AST_NODE_LOC(bits.get(), @1, @1); + ConstParser p{@2}; + auto val = p.const2ast(*$2, extra->case_type_stack.size() == 0 ? 0 : extra->case_type_stack.back(), !mode->lib); + SET_AST_NODE_LOC(val.get(), @2, @2); + if (val == nullptr) + log_error("Value conversion failed: `%s'\n", *$2); + $$ = std::make_unique(@$, AST_TO_BITS, std::move(bits), std::move(val)); } | 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; + ConstParser p{@1}; + $$ = p.const2ast(*$1, extra->case_type_stack.size() == 0 ? 0 : extra->case_type_stack.back(), !mode->lib); + SET_AST_NODE_LOC($$.get(), @1, @1); + if ($$ == nullptr) + log_error("Value conversion failed: `%s'\n", *$1); } | TOK_REALVAL { - $$ = new AstNode(AST_REALVALUE); + $$ = std::make_unique(@$, 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); + SET_AST_NODE_LOC($$.get(), @1, @1); log_assert(*q == 0); - delete $1; free(p); } | TOK_STRING { - $$ = AstNode::mkconst_str(*$1); - SET_AST_NODE_LOC($$, @1, @1); - delete $1; + $$ = AstNode::mkconst_str(@1, *$1); + SET_AST_NODE_LOC($$.get(), @1, @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(); + hierarchical_id attr { + // Here we use "Typed Midrule Actions". + // https://www.gnu.org/software/bison/manual/html_node/Typed-Midrule-Actions.html + auto fcall = std::make_unique(@1, AST_FCALL); + AstNode *fcall_node = fcall.get(); + fcall_node->str = *$1; + extra->ast_stack.push_back(fcall_node); + SET_AST_NODE_LOC(fcall_node, @1, @1); + append_attr(fcall_node, std::move($2)); + $$ = std::move(fcall); + } TOK_LPAREN arg_list optional_comma TOK_RPAREN { + log_assert($3 != nullptr); + $$ = std::move($3); + extra->ast_stack.pop_back(); } | - TOK_TO_SIGNED attr '(' expr ')' { - $$ = new AstNode(AST_TO_SIGNED, $4); - append_attr($$, $2); + TOK_TO_SIGNED attr TOK_LPAREN expr TOK_RPAREN { + $$ = std::make_unique(@$, AST_TO_SIGNED, std::move($4)); + append_attr($$.get(), std::move($2)); } | - TOK_TO_UNSIGNED attr '(' expr ')' { - $$ = new AstNode(AST_TO_UNSIGNED, $4); - append_attr($$, $2); + TOK_TO_UNSIGNED attr TOK_LPAREN expr TOK_RPAREN { + $$ = std::make_unique(@$, AST_TO_UNSIGNED, std::move($4)); + append_attr($$.get(), std::move($2)); } | - '(' expr ')' { - $$ = $2; + TOK_LPAREN expr TOK_RPAREN { + $$ = std::move($2); } | - '(' expr ':' expr ':' expr ')' { - delete $2; - $$ = $4; - delete $6; + TOK_LPAREN expr TOK_COL expr TOK_COL expr TOK_RPAREN { + $$ = std::move($4); } | - '{' concat_list '}' { - $$ = $2; + TOK_LCURL concat_list TOK_RCURL { + $$ = std::move($2); } | - '{' expr '{' concat_list '}' '}' { - $$ = new AstNode(AST_REPLICATE, $2, $4); + TOK_LCURL expr TOK_LCURL concat_list TOK_RCURL TOK_RCURL { + $$ = std::make_unique(@$, AST_REPLICATE, std::move($2), std::move($4)); } | - '~' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_BIT_NOT, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_TILDE attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_BIT_NOT, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), std::move($2)); } | - basic_expr '&' attr basic_expr { - $$ = new AstNode(AST_BIT_AND, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_AMP attr basic_expr { + $$ = std::make_unique(@$, AST_BIT_AND, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($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); + $$ = std::make_unique(@$, AST_BIT_NOT, std::make_unique(@$, AST_BIT_AND, std::move($1), std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - basic_expr '|' attr basic_expr { - $$ = new AstNode(AST_BIT_OR, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_PIPE attr basic_expr { + $$ = std::make_unique(@$, AST_BIT_OR, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($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); + $$ = std::make_unique(@$, AST_BIT_NOT, std::make_unique(@$, AST_BIT_OR, std::move($1), std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - basic_expr '^' attr basic_expr { - $$ = new AstNode(AST_BIT_XOR, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_CARET attr basic_expr { + $$ = std::make_unique(@$, AST_BIT_XOR, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | basic_expr OP_XNOR attr basic_expr { - $$ = new AstNode(AST_BIT_XNOR, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_BIT_XNOR, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - '&' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_REDUCE_AND, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_AMP attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_REDUCE_AND, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), std::move($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, $$); + $$ = std::make_unique(@$, AST_REDUCE_AND, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), std::move($2)); + $$ = std::make_unique(@$, AST_LOGIC_NOT, std::move($$)); } | - '|' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_REDUCE_OR, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_PIPE attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_REDUCE_OR, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), std::move($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); + $$ = std::make_unique(@$, AST_REDUCE_OR, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), std::move($2)); + $$ = std::make_unique(@$, AST_LOGIC_NOT, std::move($$)); + SET_AST_NODE_LOC($$.get(), @1, @3); } | - '^' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_REDUCE_XOR, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_CARET attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_REDUCE_XOR, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), std::move($2)); } | OP_XNOR attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_REDUCE_XNOR, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + $$ = std::make_unique(@$, AST_REDUCE_XNOR, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), std::move($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); + $$ = std::make_unique(@$, AST_SHIFT_LEFT, std::move($1), std::make_unique(@$, AST_TO_UNSIGNED, std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($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); + $$ = std::make_unique(@$, AST_SHIFT_RIGHT, std::move($1), std::make_unique(@$, AST_TO_UNSIGNED, std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($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); + $$ = std::make_unique(@$, AST_SHIFT_SLEFT, std::move($1), std::make_unique(@$, AST_TO_UNSIGNED, std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($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); + $$ = std::make_unique(@$, AST_SHIFT_SRIGHT, std::move($1), std::make_unique(@$, AST_TO_UNSIGNED, std::move($4))); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - basic_expr '<' attr basic_expr { - $$ = new AstNode(AST_LT, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_LT attr basic_expr { + $$ = std::make_unique(@$, AST_LT, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | basic_expr OP_LE attr basic_expr { - $$ = new AstNode(AST_LE, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_LE, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | basic_expr OP_EQ attr basic_expr { - $$ = new AstNode(AST_EQ, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_EQ, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | basic_expr OP_NE attr basic_expr { - $$ = new AstNode(AST_NE, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_NE, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | basic_expr OP_EQX attr basic_expr { - $$ = new AstNode(AST_EQX, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_EQX, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | basic_expr OP_NEX attr basic_expr { - $$ = new AstNode(AST_NEX, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_NEX, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | basic_expr OP_GE attr basic_expr { - $$ = new AstNode(AST_GE, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_GE, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - basic_expr '>' attr basic_expr { - $$ = new AstNode(AST_GT, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_GT attr basic_expr { + $$ = std::make_unique(@$, AST_GT, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - basic_expr '+' attr basic_expr { - $$ = new AstNode(AST_ADD, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_PLUS attr basic_expr { + $$ = std::make_unique(@$, AST_ADD, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - basic_expr '-' attr basic_expr { - $$ = new AstNode(AST_SUB, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_MINUS attr basic_expr { + $$ = std::make_unique(@$, AST_SUB, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - basic_expr '*' attr basic_expr { - $$ = new AstNode(AST_MUL, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_ASTER attr basic_expr { + $$ = std::make_unique(@$, AST_MUL, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - basic_expr '/' attr basic_expr { - $$ = new AstNode(AST_DIV, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_SLASH attr basic_expr { + $$ = std::make_unique(@$, AST_DIV, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - basic_expr '%' attr basic_expr { - $$ = new AstNode(AST_MOD, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + basic_expr TOK_PERC attr basic_expr { + $$ = std::make_unique(@$, AST_MOD, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | basic_expr OP_POW attr basic_expr { - $$ = new AstNode(AST_POW, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_POW, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - '+' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_POS, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_PLUS attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_POS, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), std::move($2)); } | - '-' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_NEG, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_MINUS attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_NEG, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), std::move($2)); } | basic_expr OP_LAND attr basic_expr { - $$ = new AstNode(AST_LOGIC_AND, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_LOGIC_AND, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | basic_expr OP_LOR attr basic_expr { - $$ = new AstNode(AST_LOGIC_OR, $1, $4); - SET_AST_NODE_LOC($$, @1, @4); - append_attr($$, $3); + $$ = std::make_unique(@$, AST_LOGIC_OR, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @1, @4); + append_attr($$.get(), std::move($3)); } | - '!' attr basic_expr %prec UNARY_OPS { - $$ = new AstNode(AST_LOGIC_NOT, $3); - SET_AST_NODE_LOC($$, @1, @3); - append_attr($$, $2); + TOK_EXCL attr basic_expr %prec UNARY_OPS { + $$ = std::make_unique(@$, AST_LOGIC_NOT, std::move($3)); + SET_AST_NODE_LOC($$.get(), @1, @3); + append_attr($$.get(), std::move($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_SIGNED OP_CAST TOK_LPAREN expr TOK_RPAREN { + if (!mode->sv) + err_at_loc(@2, "Static cast is only supported in SystemVerilog mode."); + $$ = std::make_unique(@$, AST_TO_SIGNED, std::move($4)); + SET_AST_NODE_LOC($$.get(), @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); + TOK_UNSIGNED OP_CAST TOK_LPAREN expr TOK_RPAREN { + if (!mode->sv) + err_at_loc(@2, "Static cast is only supported in SystemVerilog mode."); + $$ = std::make_unique(@$, AST_TO_UNSIGNED, std::move($4)); + SET_AST_NODE_LOC($$.get(), @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); + basic_expr OP_CAST TOK_LPAREN expr TOK_RPAREN { + if (!mode->sv) + err_at_loc(@2, "Static cast is only supported in SystemVerilog mode."); + $$ = std::make_unique(@$, AST_CAST_SIZE, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @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); + typedef_base_type OP_CAST TOK_LPAREN expr TOK_RPAREN { + if (!mode->sv) + err_at_loc(@2, "Static cast is only supported in SystemVerilog mode."); + $$ = std::make_unique(@$, AST_CAST_SIZE, std::move($1), std::move($4)); + SET_AST_NODE_LOC($$.get(), @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); + TOK_LPAREN expr TOK_EQ expr TOK_RPAREN { + extra->ensureAsgnExprAllowed(@3, mode->sv); $$ = $2->clone(); + auto node = std::make_unique(@$, AST_ASSIGN_EQ, std::move($2), std::move($4)); + SET_AST_NODE_LOC(node.get(), @2, @4); + extra->ast_stack.back()->children.push_back(std::move(node)); } | - '(' expr asgn_binop expr ')' { - ensureAsgnExprAllowed(); - $$ = addAsgnBinopStmt(nullptr, $2, $3, $4, @2, @4)-> clone(); + TOK_LPAREN expr asgn_binop expr TOK_RPAREN { + extra->ensureAsgnExprAllowed(@3, mode->sv); + $$ = extra->addAsgnBinopStmt(nullptr, std::move($2), $3, std::move($4))-> clone(); }; concat_list: expr { - $$ = new AstNode(AST_CONCAT, $1); + $$ = std::make_unique(@$, AST_CONCAT, std::move($1)); } | - expr ',' concat_list { - $$ = $3; - $$->children.push_back($1); + expr TOK_COMMA concat_list { + $$ = std::move($3); + $$->children.push_back(std::move($1)); }; integral_number: - TOK_CONSTVAL { $$ = $1; } | - TOK_UNBASED_UNSIZED_CONSTVAL { $$ = $1; } | + TOK_CONSTVAL { $$ = std::move($1); } | + TOK_UNBASED_UNSIZED_CONSTVAL { $$ = std::move($1); } | TOK_BASE TOK_BASED_CONSTVAL { $1->append(*$2); - $$ = $1; - delete $2; + $$ = std::move($1); } | TOK_CONSTVAL TOK_BASE TOK_BASED_CONSTVAL { $1->append(*$2).append(*$3); - $$ = $1; - delete $2; - delete $3; + $$ = std::move($1); }; diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index 821490dca..e2071436c 100644 --- a/kernel/bitpattern.h +++ b/kernel/bitpattern.h @@ -25,6 +25,18 @@ YOSYS_NAMESPACE_BEGIN +/** + * This file implements BitPatternPool for efficiently storing and querying + * sets of fixed-width 2-valued logic constants compressed as "bit patterns". + * A bit pattern can have don't cares on one or more bit positions (State::Sa). + * + * In terms of logic synthesis: + * A BitPatternPool is a sum of products (SOP). + * BitPatternPool::bits_t is a cube. + * + * BitPatternPool does not permit adding new patterns, only removing. + * Its intended use case is in analysing cases in case/match constructs in HDL. + */ struct BitPatternPool { int width; @@ -67,6 +79,9 @@ struct BitPatternPool } } + /** + * Constructs a pool of all possible patterns (all don't-care bits) + */ BitPatternPool(int width) { this->width = width; @@ -78,16 +93,23 @@ struct BitPatternPool } } + /** + * Convert a constant SigSpec to a pattern. Normalize Yosys many-valued + * to three-valued logic. + */ bits_t sig2bits(RTLIL::SigSpec sig) { bits_t bits; - bits.bitdata = sig.as_const().bits(); + bits.bitdata = sig.as_const().to_bits(); for (auto &b : bits.bitdata) if (b > RTLIL::State::S1) b = RTLIL::State::Sa; return bits; } + /** + * Two cubes match if their intersection is non-empty. + */ bool match(bits_t a, bits_t b) { log_assert(int(a.bitdata.size()) == width); @@ -98,6 +120,15 @@ struct BitPatternPool return true; } + /** + * Does cube sig overlap any cube in the pool? + * For example: + * pool({aaa}).has_any(01a) == true + * pool({01a}).has_any(01a) == true + * pool({011}).has_any(01a) == true + * pool({01a}).has_any(011) == true + * pool({111}).has_any(01a) == false + */ bool has_any(RTLIL::SigSpec sig) { bits_t bits = sig2bits(sig); @@ -107,6 +138,15 @@ struct BitPatternPool return false; } + /** + * Is cube sig covered by a cube in the pool? + * For example: + * pool({aaa}).has_all(01a) == true + * pool({01a}).has_any(01a) == true + * pool({01a}).has_any(011) == true + * pool({011}).has_all(01a) == false + * pool({111}).has_all(01a) == false + */ bool has_all(RTLIL::SigSpec sig) { bits_t bits = sig2bits(sig); @@ -121,6 +161,12 @@ struct BitPatternPool return false; } + /** + * Remove cube sig from the pool, splitting the remaining cubes. True if success. + * For example: + * Taking 011 out of pool({01a}) -> pool({010}), returns true. + * Taking 011 out of pool({010}) does nothing, returns false. + */ bool take(RTLIL::SigSpec sig) { bool status = false; @@ -143,6 +189,9 @@ struct BitPatternPool return status; } + /** + * Remove all patterns. Returns false if already empty. + */ bool take_all() { if (database.empty()) diff --git a/kernel/calc.cc b/kernel/calc.cc index f08c97396..9b0885db9 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -33,10 +33,7 @@ static void extend_u0(RTLIL::Const &arg, int width, bool is_signed) if (arg.size() > 0 && is_signed) padding = arg.back(); - while (GetSize(arg) < width) - arg.bits().push_back(padding); - - arg.bits().resize(width); + arg.resize(width, padding); } static BigInteger const2big(const RTLIL::Const &val, bool as_signed, int &undef_bit_pos) @@ -79,12 +76,12 @@ static RTLIL::Const big2const(const BigInteger &val, int result_len, int undef_b { mag--; for (auto i = 0; i < result_len; i++) - result.bits()[i] = mag.getBit(i) ? RTLIL::State::S0 : RTLIL::State::S1; + result.set(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; + result.set(i, mag.getBit(i) ? RTLIL::State::S1 : RTLIL::State::S0); } } @@ -140,11 +137,11 @@ RTLIL::Const RTLIL::const_not(const RTLIL::Const &arg1, const RTLIL::Const&, boo 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; + result.set(i, RTLIL::State::S0); + else if (arg1_ext[i] == RTLIL::State::S0) + result.set(i, RTLIL::State::S1); + else if (arg1_ext[i] == RTLIL::State::S1) + result.set(i, RTLIL::State::S0); } return result; @@ -161,9 +158,9 @@ static RTLIL::Const logic_wrapper(RTLIL::State(*logic_func)(RTLIL::State, RTLIL: 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); + RTLIL::State a = i < GetSize(arg1) ? arg1[i] : RTLIL::State::S0; + RTLIL::State b = i < GetSize(arg2) ? arg2[i] : RTLIL::State::S0; + result.set(i, logic_func(a, b)); } return result; @@ -197,8 +194,8 @@ static RTLIL::Const logic_reduce_wrapper(RTLIL::State initial, RTLIL::State(*log temp = logic_func(temp, arg1[i]); RTLIL::Const result(temp); - while (GetSize(result) < result_len) - result.bits().push_back(RTLIL::State::S0); + if (GetSize(result) < result_len) + result.resize(result_len, RTLIL::State::S0); return result; } @@ -222,9 +219,9 @@ RTLIL::Const RTLIL::const_reduce_xnor(const RTLIL::Const &arg1, const RTLIL::Con 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; + buffer.set(0, RTLIL::State::S1); else if (buffer.front() == RTLIL::State::S1) - buffer.bits().front() = RTLIL::State::S0; + buffer.set(0, RTLIL::State::S0); } return buffer; } @@ -239,9 +236,8 @@ RTLIL::Const RTLIL::const_logic_not(const RTLIL::Const &arg1, const RTLIL::Const 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); + if (GetSize(result) < result_len) + result.resize(result_len, RTLIL::State::S0); return result; } @@ -254,9 +250,8 @@ RTLIL::Const RTLIL::const_logic_and(const RTLIL::Const &arg1, const RTLIL::Const 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); + if (GetSize(result) < result_len) + result.resize(result_len, RTLIL::State::S0); return result; } @@ -269,9 +264,8 @@ RTLIL::Const RTLIL::const_logic_or(const RTLIL::Const &arg1, const RTLIL::Const 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); + if (GetSize(result) < result_len) + result.resize(result_len, RTLIL::State::S0); return result; } @@ -295,11 +289,11 @@ static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Co for (int i = 0; i < result_len; i++) { BigInteger pos = BigInteger(i) + offset; if (pos < 0) - result.bits()[i] = vacant_bits; + result.set(i, vacant_bits); else if (pos >= BigInteger(GetSize(arg1))) - result.bits()[i] = sign_ext ? arg1.back() : vacant_bits; + result.set(i, sign_ext ? arg1.back() : vacant_bits); else - result.bits()[i] = arg1[pos.toInt()]; + result.set(i, arg1[pos.toInt()]); } return result; @@ -346,9 +340,8 @@ RTLIL::Const RTLIL::const_lt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, 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); + if (GetSize(result) < result_len) + result.resize(result_len, RTLIL::State::S0); return result; } @@ -357,9 +350,8 @@ RTLIL::Const RTLIL::const_le(const RTLIL::Const &arg1, const RTLIL::Const &arg2, 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); + if (GetSize(result) < result_len) + result.resize(result_len, RTLIL::State::S0); return result; } @@ -383,7 +375,7 @@ RTLIL::Const RTLIL::const_eq(const RTLIL::Const &arg1, const RTLIL::Const &arg2, matched_status = RTLIL::State::Sx; } - result.bits().front() = matched_status; + result.set(0, matched_status); return result; } @@ -391,9 +383,9 @@ RTLIL::Const RTLIL::const_ne(const RTLIL::Const &arg1, const RTLIL::Const &arg2, { RTLIL::Const result = RTLIL::const_eq(arg1, arg2, signed1, signed2, result_len); if (result.front() == RTLIL::State::S0) - result.bits().front() = RTLIL::State::S1; + result.set(0, RTLIL::State::S1); else if (result.front() == RTLIL::State::S1) - result.bits().front() = RTLIL::State::S0; + result.set(0, RTLIL::State::S0); return result; } @@ -412,7 +404,7 @@ RTLIL::Const RTLIL::const_eqx(const RTLIL::Const &arg1, const RTLIL::Const &arg2 return result; } - result.bits().front() = RTLIL::State::S1; + result.set(0, RTLIL::State::S1); return result; } @@ -420,9 +412,9 @@ RTLIL::Const RTLIL::const_nex(const RTLIL::Const &arg1, const RTLIL::Const &arg2 { RTLIL::Const result = RTLIL::const_eqx(arg1, arg2, signed1, signed2, result_len); if (result.front() == RTLIL::State::S0) - result.bits().front() = RTLIL::State::S1; + result.set(0, RTLIL::State::S1); else if (result.front() == RTLIL::State::S1) - result.bits().front() = RTLIL::State::S0; + result.set(0, RTLIL::State::S0); return result; } @@ -431,9 +423,8 @@ RTLIL::Const RTLIL::const_ge(const RTLIL::Const &arg1, const RTLIL::Const &arg2, 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); + if (GetSize(result) < result_len) + result.resize(result_len, RTLIL::State::S0); return result; } @@ -442,9 +433,8 @@ RTLIL::Const RTLIL::const_gt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, 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); + if (GetSize(result) < result_len) + result.resize(result_len, RTLIL::State::S0); return result; } @@ -628,7 +618,7 @@ RTLIL::Const RTLIL::const_mux(const RTLIL::Const &arg1, const RTLIL::Const &arg2 RTLIL::Const ret = arg1; for (auto i = 0; i < ret.size(); i++) if (ret[i] != arg2[i]) - ret.bits()[i] = State::Sx; + ret.set(i, State::Sx); return ret; } @@ -703,7 +693,7 @@ RTLIL::Const RTLIL::const_bweqx(const RTLIL::Const &arg1, const RTLIL::Const &ar 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; + result.set(i, arg1[i] == arg2[i] ? State::S1 : State::S0); return result; } @@ -715,7 +705,7 @@ RTLIL::Const RTLIL::const_bwmux(const RTLIL::Const &arg1, const RTLIL::Const &ar 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]; + result.set(i, arg3[i] == State::S1 ? arg2[i] : arg1[i]); } return result; diff --git a/kernel/celledges.cc b/kernel/celledges.cc index 8e52d0380..c39ced95a 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -447,7 +447,7 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL return true; } - if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + if (cell->is_builtin_ff()) { ff_op(this, cell); return true; } diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 0ce5db54d..2c3535eac 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -111,6 +111,8 @@ struct CellTypes setup_type(ID($original_tag), {ID::A}, {ID::Y}); setup_type(ID($future_ff), {ID::A}, {ID::Y}); setup_type(ID($scopeinfo), {}, {}); + setup_type(ID($input_port), {}, {ID::Y}); + setup_type(ID($connect), {ID::A, ID::B}, {}); } void setup_internals_eval() @@ -303,24 +305,34 @@ struct CellTypes cell_types.clear(); } - bool cell_known(RTLIL::IdString type) const + bool cell_known(const RTLIL::IdString &type) const { return cell_types.count(type) != 0; } - bool cell_output(RTLIL::IdString type, RTLIL::IdString port) const + bool cell_output(const RTLIL::IdString &type, const 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 + bool cell_input(const RTLIL::IdString &type, const 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 + RTLIL::PortDir cell_port_dir(RTLIL::IdString type, RTLIL::IdString port) const + { + auto it = cell_types.find(type); + if (it == cell_types.end()) + return RTLIL::PD_UNKNOWN; + bool is_input = it->second.inputs.count(port); + bool is_output = it->second.outputs.count(port); + return RTLIL::PortDir(is_input + is_output * 2); + } + + bool cell_evaluable(const RTLIL::IdString &type) const { auto it = cell_types.find(type); return it != cell_types.end() && it->second.is_evaluable; @@ -328,12 +340,13 @@ struct CellTypes static RTLIL::Const eval_not(RTLIL::Const v) { - for (auto &bit : v.bits()) + for (auto bit : v) if (bit == State::S0) bit = State::S1; else if (bit == State::S1) bit = State::S0; return v; } + // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs 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) @@ -416,19 +429,18 @@ struct CellTypes log_abort(); } + // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs 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; + return arg1.extract(offset, width); } if (cell->type == ID($concat)) { RTLIL::Const ret = arg1; - ret.bits().insert(ret.bits().end(), arg2.begin(), arg2.end()); + ret.append(arg2); return ret; } @@ -503,10 +515,13 @@ struct CellTypes return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len, errp); } + // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs 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($_NMUX_)) + return eval_not(const_mux(arg1, arg2, arg3)); if (cell->type == ID($bwmux)) return const_bwmux(arg1, arg2, arg3); if (cell->type == ID($pmux)) @@ -520,6 +535,7 @@ struct CellTypes return eval(cell, arg1, arg2, errp); } + // Consider using the ConstEval struct instead if you need named ports and/or multiple outputs 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_)) diff --git a/kernel/consteval.h b/kernel/consteval.h index 844120ef0..b13c7ea5c 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -115,7 +115,7 @@ struct ConstEval 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; + coval.set(i, carry ? State::S1 : State::S0); } set(sig_co, coval); @@ -249,7 +249,7 @@ struct ConstEval for (int i = 0; i < GetSize(val_y); i++) if (val_y[i] == RTLIL::Sx) - val_x.bits()[i] = RTLIL::Sx; + val_x.set(i, RTLIL::Sx); set(sig_y, val_y); set(sig_x, val_x); @@ -349,7 +349,11 @@ struct ConstEval 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); + RTLIL::Const eval_ret; + if (sig_s.size() > 0 && eval(sig_s, undef, cell)) { + eval_ret = CellTypes::eval(cell, sig_a.as_const(), sig_b.as_const(), sig_s.as_const(), &eval_err); + } else + 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; diff --git a/kernel/constids.inc b/kernel/constids.inc index 4fdbb3dc8..c99aa788d 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -1,163 +1,686 @@ +// These must be in perfect ASCII order!!! + +// Workaround for macos's math.h defining an OVERFLOW macro +#ifdef OVERFLOW +#undef OVERFLOW +#endif + +// Workaround for windows defining IN and OUT macros in minwindef.h which ends +// up getting included for visual studio builds +#ifdef IN +#undef IN +#endif +#ifdef OUT +#undef OUT +#endif + +X($_ALDFFE_NNN_) +X($_ALDFFE_NNP_) +X($_ALDFFE_NPN_) +X($_ALDFFE_NPP_) +X($_ALDFFE_PNN_) +X($_ALDFFE_PNP_) +X($_ALDFFE_PPN_) +X($_ALDFFE_PPP_) +X($_ALDFF_NN_) +X($_ALDFF_NP_) +X($_ALDFF_PN_) +X($_ALDFF_PP_) +X($_ANDNOT_) +X($_AND_) +X($_AOI3_) +X($_AOI4_) +X($_BUF_) +X($_DFFE_NN0N_) +X($_DFFE_NN0P_) +X($_DFFE_NN1N_) +X($_DFFE_NN1P_) +X($_DFFE_NN_) +X($_DFFE_NP0N_) +X($_DFFE_NP0P_) +X($_DFFE_NP1N_) +X($_DFFE_NP1P_) +X($_DFFE_NP_) +X($_DFFE_PN0N_) +X($_DFFE_PN0P_) +X($_DFFE_PN1N_) +X($_DFFE_PN1P_) +X($_DFFE_PN_) +X($_DFFE_PP0N_) +X($_DFFE_PP0P_) +X($_DFFE_PP1N_) +X($_DFFE_PP1P_) +X($_DFFE_PP_) +X($_DFFSRE_NNNN_) +X($_DFFSRE_NNNP_) +X($_DFFSRE_NNPN_) +X($_DFFSRE_NNPP_) +X($_DFFSRE_NPNN_) +X($_DFFSRE_NPNP_) +X($_DFFSRE_NPPN_) +X($_DFFSRE_NPPP_) +X($_DFFSRE_PNNN_) +X($_DFFSRE_PNNP_) +X($_DFFSRE_PNPN_) +X($_DFFSRE_PNPP_) +X($_DFFSRE_PPNN_) +X($_DFFSRE_PPNP_) +X($_DFFSRE_PPPN_) +X($_DFFSRE_PPPP_) +X($_DFFSR_NNN_) +X($_DFFSR_NNP_) +X($_DFFSR_NPN_) +X($_DFFSR_NPP_) +X($_DFFSR_PNN_) +X($_DFFSR_PNP_) +X($_DFFSR_PPN_) +X($_DFFSR_PPP_) +X($_DFF_N) +X($_DFF_NN0_) +X($_DFF_NN1_) +X($_DFF_NP0_) +X($_DFF_NP1_) +X($_DFF_N_) +X($_DFF_PN0_) +X($_DFF_PN1_) +X($_DFF_PP0_) +X($_DFF_PP1_) +X($_DFF_P_) +X($_DLATCHSR_NNN_) +X($_DLATCHSR_NNP_) +X($_DLATCHSR_NPN_) +X($_DLATCHSR_NPP_) +X($_DLATCHSR_PNN_) +X($_DLATCHSR_PNP_) +X($_DLATCHSR_PPN_) +X($_DLATCHSR_PPP_) +X($_DLATCH_NN0_) +X($_DLATCH_NN1_) +X($_DLATCH_NP0_) +X($_DLATCH_NP1_) +X($_DLATCH_N_) +X($_DLATCH_PN0_) +X($_DLATCH_PN1_) +X($_DLATCH_PP0_) +X($_DLATCH_PP1_) +X($_DLATCH_P_) +X($_FF_) +X($_MUX16_) +X($_MUX4_) +X($_MUX8_) +X($_MUX_) +X($_NAND_) +X($_NMUX_) +X($_NOR_) +X($_NOT_) +X($_OAI3_) +X($_OAI4_) +X($_ORNOT_) +X($_OR_) +X($_SDFFCE_NN0N_) +X($_SDFFCE_NN0P_) +X($_SDFFCE_NN1N_) +X($_SDFFCE_NN1P_) +X($_SDFFCE_NP0N_) +X($_SDFFCE_NP0P_) +X($_SDFFCE_NP1N_) +X($_SDFFCE_NP1P_) +X($_SDFFCE_PN0N_) +X($_SDFFCE_PN0P_) +X($_SDFFCE_PN1N_) +X($_SDFFCE_PN1P_) +X($_SDFFCE_PP0N_) +X($_SDFFCE_PP0P_) +X($_SDFFCE_PP1N_) +X($_SDFFCE_PP1P_) +X($_SDFFE_NN0N_) +X($_SDFFE_NN0P_) +X($_SDFFE_NN1N_) +X($_SDFFE_NN1P_) +X($_SDFFE_NP0N_) +X($_SDFFE_NP0P_) +X($_SDFFE_NP1N_) +X($_SDFFE_NP1P_) +X($_SDFFE_PN0N_) +X($_SDFFE_PN0P_) +X($_SDFFE_PN1N_) +X($_SDFFE_PN1P_) +X($_SDFFE_PP0N_) +X($_SDFFE_PP0P_) +X($_SDFFE_PP1N_) +X($_SDFFE_PP1P_) +X($_SDFF_NN0_) +X($_SDFF_NN1_) +X($_SDFF_NP0_) +X($_SDFF_NP1_) +X($_SDFF_PN0_) +X($_SDFF_PN1_) +X($_SDFF_PP0_) +X($_SDFF_PP1_) +X($_SR_NN_) +X($_SR_NP_) +X($_SR_PN_) +X($_SR_PP_) +X($_TBUF_) +X($_XNOR_) +X($_XOR_) +X($__ABC9_DELAY) +X($__ABC9_SCC_BREAKER) +X($__CC_NOT) +X($__COUNT_) +X($__ICE40_CARRY_WRAPPER) +X($__QLF_TDP36K) +X($__QLF_TDP36K_MERGED) +X($__XILINX_SHREG_) +X($abc9_flops) +X($add) +X($adff) +X($adffe) +X($adlatch) +X($aldff) +X($aldffe) +X($allconst) +X($allseq) +X($alu) +X($and) +X($anyconst) +X($anyinit) +X($anyseq) +X($assert) +X($assume) +X($bmux) +X($buf) +X($bugpoint) +X($bweq) +X($bweqx) +X($bwmux) +X($check) +X($concat) +X($connect) +X($cover) +X($demux) +X($dff) +X($dffe) +X($dffsr) +X($dffsre) +X($div) +X($divfloor) +X($dlatch) +X($dlatchsr) +X($eq) +X($equiv) +X($eqx) +X($fa) +X($fair) +X($false) +X($ff) +X($flowmap_level) +X($fsm) +X($fullskew) +X($future_ff) +X($ge) +X($get_tag) +X($gt) +X($initstate) +X($input) +X($input_port) +X($lcu) +X($le) +X($live) +X($logic_and) +X($logic_not) +X($logic_or) +X($lt) +X($lut) +X($macc) +X($macc_v2) +X($mem) +X($mem_v2) +X($meminit) +X($meminit_v2) +X($memrd) +X($memrd_v2) +X($memwr) +X($memwr_v2) +X($mod) +X($modfloor) +X($mul) +X($mux) +X($ne) +X($neg) +X($nex) +X($not) +X($or) +X($original_tag) +X($output) +X($overwrite_tag) +X($pending) +X($pmux) +X($pos) +X($pow) +X($print) +X($recrem) +X($reduce_and) +X($reduce_bool) +X($reduce_nand) +X($reduce_or) +X($reduce_xnor) +X($reduce_xor) +X($scopeinfo) +X($sdff) +X($sdffce) +X($sdffe) +X($set_tag) +X($setup) +X($setuphold) +X($shift) +X($shiftx) +X($shl) +X($shr) +X($slice) +X($sop) +X($specify2) +X($specify3) +X($specrule) +X($sr) +X($sshl) +X($sshr) +X($state) +X($sub) +X($tribuf) +X($true) +X($undef) +X($xnor) +X($xor) X(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(A0REG) +X(A1) +X(A1REG) +X(A2) +X(A3) +X(A4) X(ABITS) +X(ACASCREG) +X(ACCUMCI) +X(ACCUMCO) +X(ACIN) X(AD) +X(ADDEND_NEGATED) X(ADDR) -X(allconst) -X(allseq) +X(ADDSUBBOT) +X(ADDSUBTOP) +X(ADREG) +X(AHOLD) X(ALOAD) X(ALOAD_POLARITY) -X(always_comb) -X(always_ff) -X(always_latch) -X(anyconst) -X(anyseq) +X(ALUMODE) +X(ALUMODEREG) +X(ALUTYPE) +X(AL_MAP_ADDER) +X(AL_MAP_LUT1) +X(AL_MAP_LUT2) +X(AL_MAP_LUT3) +X(AL_MAP_LUT4) +X(AL_MAP_LUT5) +X(AL_MAP_LUT6) +X(ALn) +X(AND) +X(ANDNOT) +X(ANDTERM) +X(AOI3) +X(AOI4) +X(AREG) X(ARGS) X(ARGS_WIDTH) X(ARST) X(ARST_POLARITY) X(ARST_VALUE) +X(A_BYPASS) +X(A_EN) +X(A_INPUT) +X(A_REG) X(A_SIGNED) +X(A_SRST_N) X(A_WIDTH) +X(A_WIDTHS) X(B) +X(B0REG) +X(B1) +X(B1REG) +X(B2) +X(B3) +X(B4) +X(BCASCREG) +X(BCIN) +X(BHOLD) X(BI) X(BITS_USED) -X(blackbox) +X(BOTADDSUB_CARRYSELECT) +X(BOTADDSUB_LOWERINPUT) +X(BOTADDSUB_UPPERINPUT) +X(BOTOUTPUT_SELECT) +X(BOT_8x8_MULT_REG) +X(BREG) +X(BUF) +X(BUFG) +X(BUFGSR) +X(BUFGTS) +X(B_BYPASS) +X(B_EN) +X(B_INPUT) +X(B_REG) X(B_SIGNED) -X(bugpoint_keep) +X(B_SRST_N) X(B_WIDTH) -X(BYTE) +X(B_WIDTHS) X(C) -X(cells_not_processed) +X(CARRYIN) +X(CARRYINREG) +X(CARRYINSEL) +X(CARRYINSELREG) +X(CARRYOUT) +X(CC_L2T4) +X(CC_L2T5) +X(CC_LUT2) +X(CDIN_FDBK_SEL) +X(CE) +X(CEA) +X(CEA1) +X(CEA2) +X(CEAD) +X(CEB) +X(CEB1) +X(CEB2) +X(CEC) +X(CED) +X(CEM) +X(CEP) X(CE_OVER_SRST) +X(CFG1) +X(CFG2) +X(CFG3) +X(CFG4) X(CFG_ABITS) X(CFG_DBITS) X(CFG_INIT) -X(chain) +X(CHOLD) X(CI) X(CLK) -X(clkbuf_driver) -X(clkbuf_inhibit) -X(clkbuf_inv) -X(clkbuf_sink) +X(CLKIN_DIVIDE) +X(CLKPOL) X(CLK_ENABLE) X(CLK_POLARITY) X(CLR) X(CLR_POLARITY) X(CO) X(COLLISION_X_MASK) +X(COMP_INP) X(CONFIG) X(CONFIG_WIDTH) +X(COUNT_EXTRACT) +X(COUNT_TO) +X(CREG) X(CTRL_IN) X(CTRL_IN_WIDTH) X(CTRL_OUT) X(CTRL_OUT_WIDTH) +X(C_ARST_N) +X(C_BYPASS) +X(C_EN) +X(C_REG) +X(C_SIGNED) +X(C_SRST_N) +X(C_WIDTHS) X(D) X(DAT) X(DATA) X(DAT_DST_PEN) X(DAT_DST_POL) -X(defaultvalue) X(DELAY) X(DEPTH) +X(DFF) +X(DHOLD) +X(DIRECTION) +X(DREG) +X(DSP48E1) X(DST) X(DST_EN) X(DST_PEN) X(DST_POL) X(DST_WIDTH) -X(dynports) +X(D_ARST_N) +X(D_BYPASS) +X(D_EN) +X(D_REG) +X(D_SRST_N) X(E) X(EDGE_EN) X(EDGE_POL) +X(EFX_ADD) X(EN) +X(ENPOL) 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(EQN) X(F) +X(FDCE) +X(FDCE_1) +X(FDCP) +X(FDCPE) +X(FDCPE_1) +X(FDCPE_N) +X(FDCP_N) +X(FDDCP) +X(FDDCPE) +X(FDPE) +X(FDPE_1) +X(FDRE) +X(FDRE_1) +X(FDRSE) +X(FDRSE_1) +X(FDSE) +X(FDSE_1) X(FLAVOR) X(FORMAT) -X(force_downto) -X(force_upto) -X(fsm_encoding) -X(fsm_export) +X(FTCP) +X(FTCP_N) +X(FTDCP) X(FULL) -X(full_case) X(G) -X(gclk) -X(gentb_clock) -X(gentb_constant) -X(gentb_skip) +X(GP_DFF) +X(GP_DFFI) +X(GP_DFFR) +X(GP_DFFRI) +X(GP_DFFS) +X(GP_DFFSI) +X(GP_DFFSR) +X(GP_DFFSRI) +X(GP_DLATCH) +X(GP_DLATCHI) +X(GP_DLATCHR) +X(GP_DLATCHRI) +X(GP_DLATCHS) +X(GP_DLATCHSI) +X(GP_DLATCHSR) +X(GP_DLATCHSRI) +X(GP_INV) +X(GP_SHREG) +X(GSR) X(H) -X(hdlname) -X(hierconn) +X(HAS_CE) +X(HAS_POUT) X(I) +X(I0) +X(I0_POLARITY) +X(I1) +X(I1_POLARITY) +X(I2) +X(I3) +X(I3_IS_CI) +X(I4) +X(I5) +X(IBUF) +X(IN) X(INIT) +X(INIT1) +X(INIT2) +X(INIT_FILE) +X(INIT_L00) +X(INIT_L01) +X(INIT_L02) +X(INIT_L03) +X(INIT_L10) +X(INIT_L20) 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(INMODE) +X(INMODEREG) +X(INV) +X(INVERT_OUT) +X(IN_B) +X(IN_ORTERM) +X(IN_PTC) +X(IOBUFE) +X(IRSTBOT) +X(IRSTTOP) +X(IS_C_INVERTED) +X(IS_D_INVERTED) +X(IS_R_INVERTED) +X(IS_S_INVERTED) X(J) X(K) -X(keep) -X(keep_hierarchy) X(L) -X(lib_whitebox) -X(localparam) -X(logic_block) -X(lram) +X(LAT) +X(LDCP) +X(LDCP_N) +X(LSR) X(LUT) -X(lut_keep) +X(LUT1) +X(LUT2) +X(LUT3) +X(LUT4) +X(LUT5) +X(LUT6) +X(LUT_INIT) X(M) -X(maximize) -X(mem2reg) +X(MACROCELL_XOR) +X(MASK) X(MEMID) -X(minimize) -X(module_not_derived) +X(MODE_8x8) +X(MODE_BITS) +X(MREG) +X(MUX) +X(MUX16) +X(MUX4) +X(MUX8) X(N) +X(NADDENDS) X(NAME) -X(noblackbox) -X(nolatches) -X(nomem2init) -X(nomem2reg) -X(nomeminit) -X(nosync) -X(nowrshmsk) -X(no_ram) -X(no_rw_check) +X(NAND) +X(NEG_TRIGGER) +X(NMUX) +X(NOR) +X(NOT) +X(NPRODUCTS) +X(NX_CY) +X(NX_CY_1BIT) X(O) +X(OAI3) +X(OAI4) X(OFFSET) -X(onehot) +X(OHOLDBOT) +X(OHOLDTOP) +X(OLOADBOT) +X(OLOADTOP) +X(ONE) +X(OPMODE) +X(OPMODEREG) +X(OPTION_SPLIT) +X(OR) +X(ORNOT) +X(ORSTBOT) +X(ORSTTOP) +X(ORTERM) +X(OUT) +X(OUTA) +X(OUTA_INVERT) +X(OUTA_TAP) +X(OUTB) +X(OUTB_TAP) +X(OVERFLOW) X(P) -X(parallel_case) -X(parameter) +X(PASUB) +X(PATTERN) +X(PCIN) +X(PIPELINE_16x16_MULT_REG1) +X(PIPELINE_16x16_MULT_REG2) X(PORTID) +X(PORT_A1_ADDR) +X(PORT_A1_CLK) +X(PORT_A1_CLK_EN) +X(PORT_A1_RD_DATA) +X(PORT_A1_WIDTH) +X(PORT_A1_WR_BE) +X(PORT_A1_WR_BE_WIDTH) +X(PORT_A1_WR_DATA) +X(PORT_A1_WR_EN) +X(PORT_A2_ADDR) +X(PORT_A2_CLK) +X(PORT_A2_CLK_EN) +X(PORT_A2_RD_DATA) +X(PORT_A2_WIDTH) +X(PORT_A2_WR_BE) +X(PORT_A2_WR_BE_WIDTH) +X(PORT_A2_WR_DATA) +X(PORT_A2_WR_EN) +X(PORT_A_ADDR) +X(PORT_A_CLK) +X(PORT_A_CLK_EN) +X(PORT_A_RD_DATA) +X(PORT_A_WIDTH) +X(PORT_A_WR_BE) +X(PORT_A_WR_BE_WIDTH) +X(PORT_A_WR_DATA) +X(PORT_A_WR_EN) +X(PORT_B1_ADDR) +X(PORT_B1_CLK) +X(PORT_B1_CLK_EN) +X(PORT_B1_RD_DATA) +X(PORT_B1_WIDTH) +X(PORT_B1_WR_BE) +X(PORT_B1_WR_BE_WIDTH) +X(PORT_B1_WR_DATA) +X(PORT_B1_WR_EN) +X(PORT_B2_ADDR) +X(PORT_B2_CLK) +X(PORT_B2_CLK_EN) +X(PORT_B2_RD_DATA) +X(PORT_B2_WIDTH) +X(PORT_B2_WR_BE) +X(PORT_B2_WR_BE_WIDTH) +X(PORT_B2_WR_DATA) +X(PORT_B2_WR_EN) +X(PORT_B_ADDR) +X(PORT_B_CLK) +X(PORT_B_CLK_EN) +X(PORT_B_RD_DATA) +X(PORT_B_WIDTH) +X(PORT_B_WR_BE) +X(PORT_B_WR_BE_WIDTH) +X(PORT_B_WR_DATA) +X(PORT_B_WR_EN) +X(POUT) +X(PRE) +X(PREG) X(PRIORITY) X(PRIORITY_MASK) +X(PRODUCT_NEGATED) +X(P_BYPASS) +X(P_EN) +X(P_SRST_N) X(Q) +X(QL_DSP2) X(R) -X(ram_block) -X(ram_style) -X(ramstyle) X(RD_ADDR) X(RD_ARST) X(RD_ARST_VALUE) @@ -175,91 +698,97 @@ 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(RESET_MODE) +X(RESET_TO_MAX) +X(RST) +X(RSTA) +X(RSTB) +X(RSTC) +X(RSTD) +X(RSTM) +X(RSTP) X(S) +X(S1) +X(S2) +X(S3) +X(S4) +X(SB_CARRY) +X(SB_LUT4) +X(SB_MAC16) +X(SB_RAM40_4K) +X(SB_RAM40_4KNR) +X(SB_RAM40_4KNRNW) +X(SB_RAM40_4KNW) +X(SD) +X(SEL_MASK) +X(SEL_PATTERN) X(SET) X(SET_POLARITY) +X(SGSR) +X(SIGNEXTIN) +X(SIGNEXTOUT) X(SIZE) +X(SLE) +X(SLn) 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(SRMODE) 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(SUB) 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(TDP36K) +X(TOPADDSUB_CARRYSELECT) +X(TOPADDSUB_LOWERINPUT) +X(TOPADDSUB_UPPERINPUT) +X(TOPOUTPUT_SELECT) +X(TOP_8x8_MULT_REG) +X(TRANSPARENCY_MASK) +X(TRANSPARENT) +X(TRANS_NUM) +X(TRANS_TABLE) +X(TRELLIS_FF) +X(TRG) +X(TRG_ENABLE) +X(TRG_POLARITY) +X(TRG_WIDTH) +X(TRUE_INP) +X(TYPE) 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(UP) +X(USE_DPORT) +X(USE_MULT) +X(USE_PATTERN_DETECT) +X(USE_SIMD) +X(UUT) 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) @@ -271,16 +800,214 @@ X(WR_PORTS) X(WR_PRIORITY_MASK) X(WR_WIDE_CONTINUATION) X(X) -X(xprop_decoder) +X(XNOR) +X(XOR) X(Y) X(Y_WIDTH) +X(Z) +X(ZERO) +X(_TECHMAP_BITS_CONNMAP_) +X(_TECHMAP_CELLNAME_) +X(_TECHMAP_CELLTYPE_) +X(_TECHMAP_PLACEHOLDER_) +X(_TECHMAP_REPLACE_) +X(__glift_weight) +X(_const0_) +X(_const1_) +X(_dff_) +X(_id) +X(_techmap_special_) +X(a) +X(a_i) +X(abc9_box) +X(abc9_box_id) +X(abc9_box_seq) +X(abc9_bypass) +X(abc9_carry) +X(abc9_deferred_box) +X(abc9_flop) +X(abc9_keep) +X(abc9_lut) +X(abc9_mergeability) +X(abc9_scc_id) +X(abc9_script) +X(abcgroup) +X(acc_fir) +X(acc_fir_i) +X(add_carry) +X(allconst) +X(allseq) +X(always_comb) +X(always_ff) +X(always_latch) +X(anyconst) +X(anyseq) +X(architecture) X(area) +X(b) +X(b_i) +X(blackbox) +X(bottom_bound) +X(bugpoint_keep) +X(c) X(capacitance) -X(NPRODUCTS) -X(NADDENDS) -X(PRODUCT_NEGATED) -X(ADDEND_NEGATED) -X(A_WIDTHS) -X(B_WIDTHS) -X(C_WIDTHS) -X(C_SIGNED) +X(cells_not_processed) +X(chain) +X(clk) +X(clk2fflogic) +X(clkbuf_driver) +X(clkbuf_inhibit) +X(clkbuf_inv) +X(clkbuf_sink) +X(clock_i) +X(cxxrtl_blackbox) +X(cxxrtl_comb) +X(cxxrtl_edge) +X(cxxrtl_sync) +X(cxxrtl_template) +X(cxxrtl_width) +X(defaultvalue) +X(dff) +X(dffsre) +X(dft_tag) +X(dly_b) +X(dly_b_o) +X(dsp_t1_10x9x32) +X(dynports) +X(enum_base_type) +X(enum_type) +X(equiv_merged) +X(equiv_region) +X(extract_order) +X(f_mode) +X(feedback) +X(feedback_i) +X(first) +X(force_downto) +X(force_upto) +X(fsm_encoding) +X(fsm_export) +X(full_case) +X(gate) +X(gate_cost_equivalent) +X(gclk) +X(gentb_clock) +X(gentb_constant) +X(gentb_skip) +X(glift) +X(gold) +X(hdlname) +X(hierconn) +X(i) +X(init) +X(initial_top) +X(interface_modport) +X(interface_type) +X(interfaces_replaced_in_module) +X(invertible_pin) +X(iopad_external_pin) +X(is_inferred) +X(is_interface) +X(it) +X(keep) +X(keep_hierarchy) +X(lib_whitebox) +X(library) +X(load_acc) +X(load_acc_i) +X(localparam) +X(logic_block) +X(lram) +X(lut) +X(lut_keep) +X(maximize) +X(mem2reg) +X(minimize) +X(module) +X(module_not_derived) +X(nQ) +X(nRST) +X(nSET) +X(netlist) +X(no_ram) +X(no_rw_check) +X(noblackbox) +X(nogsr) +X(nolatches) +X(nomem2reg) +X(nomeminit) +X(nosync) +X(nowrshmsk) +X(o) +X(offset) +X(onehot) +X(output_select) +X(output_select_i) +X(p_class) +X(parallel_case) +X(parameter) +X(promoted_if) +X(raise_error) +X(ram_block) +X(ram_style) +X(ramstyle) +X(reg) +X(register_inputs) +X(register_inputs_i) +X(replaced_by_gclk) +X(reprocess_after) +X(reset) +X(reset_i) +X(rom_block) +X(rom_style) +X(romstyle) +X(round) +X(round_i) +X(rtlil) +X(saturate_enable) +X(saturate_enable_i) +X(scopename) +X(sdffsre) +X(shift_right) +X(shift_right_i) +X(single_bit_vector) +X(smtlib2_comb_expr) +X(smtlib2_module) +X(src) +X(sta_arrival) +X(submod) +X(subtract) +X(subtract_i) +X(syn_ramstyle) +X(syn_romstyle) +X(techmap_autopurge) +X(techmap_celltype) +X(techmap_chtype) +X(techmap_maccmap) +X(techmap_simplemap) +X(techmap_wrap) +X(test) +X(to_delete) +X(top) +X(top_bound) +X(trg_on_gclk) +X(trigger) +X(unique) +X(unsigned_a) +X(unsigned_a_i) +X(unsigned_b) +X(unsigned_b_i) +X(unused_bits) +X(use_dsp) +X(value) +X(via_celltype) +X(wand) +X(whitebox) +X(width) +X(wildcard_port_conns) +X(wiretype) +X(wor) +X(xprop_decoder) +X(y) +X(z) +X(z_o) diff --git a/kernel/cost.cc b/kernel/cost.cc index 985220f14..4942823d3 100644 --- a/kernel/cost.cc +++ b/kernel/cost.cc @@ -143,14 +143,14 @@ unsigned int CellCosts::get(RTLIL::Cell *cell) return 1; if (design_ && design_->module(cell->type) && cell->parameters.empty()) { - log_debug("%s is a module, recurse\n", cell->name.c_str()); + log_debug("%s is a module, recurse\n", cell->name); return get(design_->module(cell->type)); - } else if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + } else if (cell->is_builtin_ff()) { log_assert(cell->hasPort(ID::Q) && "Weird flip flop"); - log_debug("%s is ff\n", cell->name.c_str()); + log_debug("%s is ff\n", cell->name); return cell->getParam(ID::WIDTH).as_int(); } else if (cell->type.in(ID($mem), ID($mem_v2))) { - log_debug("%s is mem\n", cell->name.c_str()); + log_debug("%s is mem\n", cell->name); return cell->getParam(ID::WIDTH).as_int() * cell->getParam(ID::SIZE).as_int(); } else if (y_coef(cell->type)) { // linear with Y_WIDTH or WIDTH @@ -159,23 +159,23 @@ unsigned int CellCosts::get(RTLIL::Cell *cell) 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)); + log_debug("%s Y*coef %d * %d\n", cell->name, 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)); + log_debug("%s sum*coef %d * %d\n", cell->name, 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)); + log_debug("%s max*coef %d * %d\n", cell->name, 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); + log_debug("%s coef*(sum**2) %d * %d\n", cell->name, coef, sum * sum); return coef * sum * sum; } else if (cell->type.in(ID($macc), ID($macc_v2))) { // quadratic per term @@ -196,15 +196,15 @@ unsigned int CellCosts::get(RTLIL::Cell *cell) } 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); + log_debug("%s is 2**%d\n", cell->name, 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); + log_debug("%s is (2*%d + 1)*%d\n", cell->name, width, depth); return (2 * width + 1) * depth; } else if (is_free(cell->type)) { - log_debug("%s is free\n", cell->name.c_str()); + log_debug("%s is free\n", cell->name); return 0; } // TODO: $fsm diff --git a/kernel/driver.cc b/kernel/driver.cc index 9acc58dc4..4097411b4 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -20,6 +20,7 @@ #include "kernel/yosys.h" #include "kernel/hashlib.h" #include "libs/sha1/sha1.h" +#define CXXOPTS_VECTOR_DELIMITER '\0' #include "libs/cxxopts/include/cxxopts.hpp" #include @@ -36,6 +37,12 @@ # include #endif +#ifdef YOSYS_ENABLE_PYTHON +# include +# include +namespace py = pybind11; +#endif + #include #include #include @@ -90,9 +97,10 @@ int main(int argc, char **argv) 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()); +#ifdef YOSYS_ENABLE_PYTHON + py::object sys = py::module_::import("sys"); + sys.attr("path").attr("append")(proc_self_dirname()); + sys.attr("path").attr("append")(proc_share_dirname()); #endif if (argc == 2) @@ -180,7 +188,7 @@ extern char yosys_path[PATH_MAX]; #endif #ifdef YOSYS_ENABLE_TCL namespace Yosys { - extern int yosys_tcl_iterp_init(Tcl_Interp *interp); + extern int yosys_tcl_interp_init(Tcl_Interp *interp); extern void yosys_tcl_activate_repl(); }; #endif @@ -225,10 +233,10 @@ int main(int argc, char **argv) cxxopts::value(),"") ("C,tcl-interactive", "enters TCL interactive shell mode") #endif // YOSYS_ENABLE_TCL -#ifdef WITH_PYTHON +#ifdef YOSYS_ENABLE_PYTHON ("y,py-scriptfile", "execute the Python