merge upstream master and resolve conflicts
This commit is contained in:
commit
0019a967b4
|
|
@ -28,6 +28,38 @@ concurrency:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
|
build-2604-gcc:
|
||||||
|
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||||
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
|
with:
|
||||||
|
sha: ${{ github.sha }}
|
||||||
|
os: ${{ matrix.os }}
|
||||||
|
os-name: linux
|
||||||
|
cc: ${{ matrix.cc }}
|
||||||
|
dev-asan: ${{ matrix.asan }}
|
||||||
|
dev-gcov: 0
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- {os: ubuntu-26.04, cc: gcc, asan: 0}
|
||||||
|
|
||||||
|
build-2604-clang:
|
||||||
|
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||||
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
|
with:
|
||||||
|
sha: ${{ github.sha }}
|
||||||
|
os: ${{ matrix.os }}
|
||||||
|
os-name: linux
|
||||||
|
cc: ${{ matrix.cc }}
|
||||||
|
dev-asan: ${{ matrix.asan }}
|
||||||
|
dev-gcov: 0
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- {os: ubuntu-26.04, cc: clang, asan: 1}
|
||||||
|
|
||||||
build-2404-gcc:
|
build-2404-gcc:
|
||||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||||
uses: ./.github/workflows/reusable-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
|
|
@ -58,7 +90,7 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- {os: ubuntu-24.04, cc: clang, asan: 1}
|
- {os: ubuntu-24.04, cc: clang, asan: 0}
|
||||||
|
|
||||||
build-2204-gcc:
|
build-2204-gcc:
|
||||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||||
|
|
@ -76,22 +108,6 @@ jobs:
|
||||||
include:
|
include:
|
||||||
- {os: ubuntu-22.04, cc: gcc, asan: 0}
|
- {os: ubuntu-22.04, cc: gcc, asan: 0}
|
||||||
|
|
||||||
build-2204-clang:
|
|
||||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
|
||||||
uses: ./.github/workflows/reusable-build.yml
|
|
||||||
with:
|
|
||||||
sha: ${{ github.sha }}
|
|
||||||
os: ${{ matrix.os }}
|
|
||||||
os-name: linux
|
|
||||||
cc: ${{ matrix.cc }}
|
|
||||||
dev-asan: ${{ matrix.asan }}
|
|
||||||
dev-gcov: 0
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- {os: ubuntu-22.04, cc: clang, asan: 0}
|
|
||||||
|
|
||||||
build-osx-gcc:
|
build-osx-gcc:
|
||||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||||
uses: ./.github/workflows/reusable-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
|
|
@ -138,11 +154,11 @@ jobs:
|
||||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
with:
|
with:
|
||||||
path: repo
|
path: repo
|
||||||
- name: Cache $CCACHE_DIR
|
- name: Cache $CCACHE_DIR
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||||
with:
|
with:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: msbuild-msvc-cmake
|
key: msbuild-msvc-cmake
|
||||||
|
|
@ -155,11 +171,59 @@ jobs:
|
||||||
- name: Zip up repository
|
- name: Zip up repository
|
||||||
run: Compress-Archive -LiteralPath install -DestinationPath verilator.zip
|
run: Compress-Archive -LiteralPath install -DestinationPath verilator.zip
|
||||||
- name: Upload zip archive
|
- name: Upload zip archive
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/repo/verilator.zip
|
path: ${{ github.workspace }}/repo/verilator.zip
|
||||||
name: verilator-win.zip
|
name: verilator-win.zip
|
||||||
|
|
||||||
|
test-2604-gcc:
|
||||||
|
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||||
|
needs: build-2604-gcc
|
||||||
|
uses: ./.github/workflows/reusable-test.yml
|
||||||
|
with:
|
||||||
|
archive: ${{ needs.build-2604-gcc.outputs.archive }}
|
||||||
|
os: ${{ matrix.os }}
|
||||||
|
cc: ${{ matrix.cc }}
|
||||||
|
reloc: ${{ matrix.reloc }}
|
||||||
|
suite: ${{ matrix.suite }}
|
||||||
|
dev-gcov: 0
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
# Ubuntu 26.04 gcc
|
||||||
|
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
|
||||||
|
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
|
||||||
|
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
|
||||||
|
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
|
||||||
|
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-0}
|
||||||
|
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-1}
|
||||||
|
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-2}
|
||||||
|
|
||||||
|
test-2604-clang:
|
||||||
|
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||||
|
needs: build-2604-clang
|
||||||
|
uses: ./.github/workflows/reusable-test.yml
|
||||||
|
with:
|
||||||
|
archive: ${{ needs.build-2604-clang.outputs.archive }}
|
||||||
|
os: ${{ matrix.os }}
|
||||||
|
cc: ${{ matrix.cc }}
|
||||||
|
reloc: ${{ matrix.reloc }}
|
||||||
|
suite: ${{ matrix.suite }}
|
||||||
|
dev-gcov: 0
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
# Ubuntu 26.04 clang
|
||||||
|
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-0}
|
||||||
|
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-1}
|
||||||
|
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-2}
|
||||||
|
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-3}
|
||||||
|
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-0}
|
||||||
|
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-1}
|
||||||
|
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-2}
|
||||||
|
|
||||||
test-2404-gcc:
|
test-2404-gcc:
|
||||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||||
needs: build-2404-gcc
|
needs: build-2404-gcc
|
||||||
|
|
@ -232,55 +296,6 @@ jobs:
|
||||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-1}
|
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-1}
|
||||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-2}
|
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-2}
|
||||||
|
|
||||||
test-2204-clang:
|
|
||||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
|
||||||
needs: build-2204-clang
|
|
||||||
uses: ./.github/workflows/reusable-test.yml
|
|
||||||
with:
|
|
||||||
archive: ${{ needs.build-2204-clang.outputs.archive }}
|
|
||||||
os: ${{ matrix.os }}
|
|
||||||
cc: ${{ matrix.cc }}
|
|
||||||
reloc: ${{ matrix.reloc }}
|
|
||||||
suite: ${{ matrix.suite }}
|
|
||||||
dev-gcov: 0
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# Ubuntu 22.04 clang, also test relocation
|
|
||||||
- {os: ubuntu-22.04, cc: clang, reloc: 1, suite: dist-vlt-0}
|
|
||||||
- {os: ubuntu-22.04, cc: clang, reloc: 1, suite: dist-vlt-1}
|
|
||||||
- {os: ubuntu-22.04, cc: clang, reloc: 1, suite: dist-vlt-2}
|
|
||||||
- {os: ubuntu-22.04, cc: clang, reloc: 1, suite: dist-vlt-3}
|
|
||||||
- {os: ubuntu-22.04, cc: clang, reloc: 1, suite: vltmt-0}
|
|
||||||
- {os: ubuntu-22.04, cc: clang, reloc: 1, suite: vltmt-1}
|
|
||||||
- {os: ubuntu-22.04, cc: clang, reloc: 1, suite: vltmt-2}
|
|
||||||
|
|
||||||
lint-py:
|
lint-py:
|
||||||
name: Lint Python
|
name: Lint Python
|
||||||
uses: ./.github/workflows/reusable-lint-py.yml
|
uses: ./.github/workflows/reusable-lint-py.yml
|
||||||
|
|
||||||
passed:
|
|
||||||
name: Test suite passed
|
|
||||||
if: always()
|
|
||||||
needs:
|
|
||||||
- build-2404-gcc
|
|
||||||
- build-2404-clang
|
|
||||||
- build-2204-gcc
|
|
||||||
- build-2204-clang
|
|
||||||
- build-osx-gcc
|
|
||||||
- build-osx-clang
|
|
||||||
- build-windows
|
|
||||||
- test-2404-gcc
|
|
||||||
- test-2404-clang
|
|
||||||
- test-2204-gcc
|
|
||||||
- test-2204-clang
|
|
||||||
- lint-py
|
|
||||||
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Decide whether the needed jobs succeeded or failed
|
|
||||||
uses: re-actors/alls-green@release/v1
|
|
||||||
with:
|
|
||||||
jobs: ${{ toJSON(needs) }}
|
|
||||||
|
|
|
||||||
|
|
@ -16,5 +16,5 @@ jobs:
|
||||||
name: "'docs/CONTRIBUTORS' was signed"
|
name: "'docs/CONTRIBUTORS' was signed"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
- run: test_regress/t/t_dist_contributors.py
|
- run: test_regress/t/t_dist_contributors.py
|
||||||
|
|
|
||||||
|
|
@ -74,10 +74,10 @@ jobs:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
|
|
||||||
- name: Download code coverage data
|
- name: Download code coverage data
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||||
with:
|
with:
|
||||||
pattern: code-coverage-*
|
pattern: code-coverage-*
|
||||||
path: obj_coverage
|
path: obj_coverage
|
||||||
|
|
@ -90,7 +90,7 @@ jobs:
|
||||||
find obj_coverage -type f | paste -sd, | sed "s/^/files=/" >> "$GITHUB_OUTPUT"
|
find obj_coverage -type f | paste -sd, | sed "s/^/files=/" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Upload to codecov.io
|
- name: Upload to codecov.io
|
||||||
uses: codecov/codecov-action@v6
|
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7
|
||||||
with:
|
with:
|
||||||
disable_file_fixes: true
|
disable_file_fixes: true
|
||||||
disable_search: true
|
disable_search: true
|
||||||
|
|
@ -114,7 +114,7 @@ jobs:
|
||||||
sudo apt install lcov
|
sudo apt install lcov
|
||||||
|
|
||||||
- name: Download repository archive
|
- name: Download repository archive
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||||
with:
|
with:
|
||||||
name: ${{ needs.build.outputs.archive }}
|
name: ${{ needs.build.outputs.archive }}
|
||||||
path: ${{ github.workspace }}
|
path: ${{ github.workspace }}
|
||||||
|
|
@ -125,7 +125,7 @@ jobs:
|
||||||
ls -lsha
|
ls -lsha
|
||||||
|
|
||||||
- name: Download code coverage data
|
- name: Download code coverage data
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||||
with:
|
with:
|
||||||
pattern: code-coverage-*
|
pattern: code-coverage-*
|
||||||
path: repo/obj_coverage
|
path: repo/obj_coverage
|
||||||
|
|
@ -172,14 +172,14 @@ jobs:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Upload report
|
- name: Upload report
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: repo/obj_coverage
|
path: repo/obj_coverage
|
||||||
name: coverage-report
|
name: coverage-report
|
||||||
|
|
||||||
- name: Upload notification
|
- name: Upload notification
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: repo/notification
|
path: repo/notification
|
||||||
name: pr-notification
|
name: pr-notification
|
||||||
|
|
@ -195,9 +195,9 @@ jobs:
|
||||||
# Creating issues requires elevated privilege
|
# Creating issues requires elevated privilege
|
||||||
- name: Generate access token
|
- name: Generate access token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@v3.2.0
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||||
with:
|
with:
|
||||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
client-id: ${{ vars.VERILATOR_CI_ID }}
|
||||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||||
owner: verilator
|
owner: verilator
|
||||||
repositories: verilator
|
repositories: verilator
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
|
|
||||||
- name: Extract context variables
|
- name: Extract context variables
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -54,7 +54,7 @@ jobs:
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: docker_meta
|
id: docker_meta
|
||||||
uses: docker/metadata-action@v6
|
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
||||||
|
|
@ -64,21 +64,21 @@ jobs:
|
||||||
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
|
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v4
|
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v4
|
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||||
with:
|
with:
|
||||||
buildkitd-flags: --debug
|
buildkitd-flags: --debug
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v4
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Build and Push to Docker
|
- name: Build and Push to Docker
|
||||||
uses: docker/build-push-action@v7
|
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
||||||
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
|
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
|
||||||
with:
|
with:
|
||||||
context: ${{ env.build_context }}
|
context: ${{ env.build_context }}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ jobs:
|
||||||
CI_COMMIT: ${{ github.sha }}
|
CI_COMMIT: ${{ github.sha }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Install packages for build
|
- name: Install packages for build
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ jobs:
|
||||||
pr-run-ids: ${{ steps.build.outputs.pr-run-ids }}
|
pr-run-ids: ${{ steps.build.outputs.pr-run-ids }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
- name: Build pages
|
- name: Build pages
|
||||||
id: build
|
id: build
|
||||||
env:
|
env:
|
||||||
|
|
@ -48,7 +48,7 @@ jobs:
|
||||||
ls -lsha
|
ls -lsha
|
||||||
tree -L 3 pages
|
tree -L 3 pages
|
||||||
- name: Upload pages artifact
|
- name: Upload pages artifact
|
||||||
uses: actions/upload-pages-artifact@v5
|
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
|
||||||
with:
|
with:
|
||||||
path: pages
|
path: pages
|
||||||
|
|
||||||
|
|
@ -61,7 +61,7 @@ jobs:
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
uses: actions/deploy-pages@v5
|
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
|
||||||
|
|
||||||
notify:
|
notify:
|
||||||
name: Notify
|
name: Notify
|
||||||
|
|
@ -70,13 +70,13 @@ jobs:
|
||||||
if: ${{ github.repository == 'verilator/verilator' }}
|
if: ${{ github.repository == 'verilator/verilator' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
# Use the Verilator CI app to post the comment
|
# Use the Verilator CI app to post the comment
|
||||||
- name: Generate access token
|
- name: Generate access token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@v3.2.0
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||||
with:
|
with:
|
||||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
client-id: ${{ vars.VERILATOR_CI_ID }}
|
||||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||||
permission-actions: write
|
permission-actions: write
|
||||||
permission-pull-requests: write
|
permission-pull-requests: write
|
||||||
|
|
|
||||||
|
|
@ -61,14 +61,14 @@ jobs:
|
||||||
CCACHE_MAXSIZE: 1000M # Per build matrix entry (* 5 = 5000M in total)
|
CCACHE_MAXSIZE: 1000M # Per build matrix entry (* 5 = 5000M in total)
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
with:
|
with:
|
||||||
path: repo
|
path: repo
|
||||||
ref: ${{ inputs.sha }}
|
ref: ${{ inputs.sha }}
|
||||||
fetch-depth: ${{ inputs.dev-gcov && '0' || '1' }} # Coverage flow needs full history
|
fetch-depth: ${{ inputs.dev-gcov && '0' || '1' }} # Coverage flow needs full history
|
||||||
|
|
||||||
- name: Cache $CCACHE_DIR
|
- name: Cache $CCACHE_DIR
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||||
env:
|
env:
|
||||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||||
with:
|
with:
|
||||||
|
|
@ -93,7 +93,7 @@ jobs:
|
||||||
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
|
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Upload repository archive
|
- name: Upload repository archive
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
|
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
|
||||||
name: ${{ steps.create-archive.outputs.archive }}
|
name: ${{ steps.create-archive.outputs.archive }}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ jobs:
|
||||||
name: Sub-lint | Python
|
name: Sub-lint | Python
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
with:
|
with:
|
||||||
path: repo
|
path: repo
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ jobs:
|
||||||
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev
|
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev
|
||||||
|
|
||||||
- name: Use saved ccache
|
- name: Use saved ccache
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||||
with:
|
with:
|
||||||
path: ccache
|
path: ccache
|
||||||
key: rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
key: rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
|
@ -60,7 +60,7 @@ jobs:
|
||||||
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}
|
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
with:
|
with:
|
||||||
path: repo
|
path: repo
|
||||||
ref: ${{ inputs.sha }}
|
ref: ${{ inputs.sha }}
|
||||||
|
|
@ -89,7 +89,7 @@ jobs:
|
||||||
echo "archive=$ARCHIVE" >> $GITHUB_OUTPUT
|
echo "archive=$ARCHIVE" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Upload Verilator installation archive
|
- name: Upload Verilator installation archive
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.create-archive.outputs.archive }}
|
path: ${{ steps.create-archive.outputs.archive }}
|
||||||
name: ${{ steps.create-archive.outputs.archive }}
|
name: ${{ steps.create-archive.outputs.archive }}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ jobs:
|
||||||
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev
|
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev
|
||||||
|
|
||||||
- name: Checkout RTLMeter
|
- name: Checkout RTLMeter
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
with:
|
with:
|
||||||
repository: "verilator/rtlmeter"
|
repository: "verilator/rtlmeter"
|
||||||
path: rtlmeter
|
path: rtlmeter
|
||||||
|
|
@ -84,7 +84,7 @@ jobs:
|
||||||
|
|
||||||
- name: Use saved ccache
|
- name: Use saved ccache
|
||||||
if: ${{ env.CCACHE_DISABLE == 0 }}
|
if: ${{ env.CCACHE_DISABLE == 0 }}
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||||
with:
|
with:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: rtlmeter-run-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.cases }}-${{ inputs.compileArgs }}-${{ github.run_id }}-${{ github.run_attempt }}
|
key: rtlmeter-run-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.cases }}-${{ inputs.compileArgs }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
|
@ -95,7 +95,7 @@ jobs:
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
- name: Download Verilator installation archive - new
|
- name: Download Verilator installation archive - new
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||||
with:
|
with:
|
||||||
name: ${{ inputs.verilator-archive-new }}
|
name: ${{ inputs.verilator-archive-new }}
|
||||||
|
|
||||||
|
|
@ -135,7 +135,7 @@ jobs:
|
||||||
./rtlmeter report --steps '*' --metrics '*' ../results-${{ steps.results.outputs.hash }}.json
|
./rtlmeter report --steps '*' --metrics '*' ../results-${{ steps.results.outputs.hash }}.json
|
||||||
|
|
||||||
- name: Upload results - new
|
- name: Upload results - new
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: results-${{ steps.results.outputs.hash }}.json
|
path: results-${{ steps.results.outputs.hash }}.json
|
||||||
name: rtlmeter-${{ inputs.tag }}-results-${{ steps.results.outputs.hash }}
|
name: rtlmeter-${{ inputs.tag }}-results-${{ steps.results.outputs.hash }}
|
||||||
|
|
@ -157,7 +157,7 @@ jobs:
|
||||||
|
|
||||||
- name: Download Verilator installation archive - old
|
- name: Download Verilator installation archive - old
|
||||||
if: ${{ inputs.verilator-archive-old != '' }}
|
if: ${{ inputs.verilator-archive-old != '' }}
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||||
with:
|
with:
|
||||||
name: ${{ inputs.verilator-archive-old }}
|
name: ${{ inputs.verilator-archive-old }}
|
||||||
|
|
||||||
|
|
@ -198,7 +198,7 @@ jobs:
|
||||||
|
|
||||||
- name: Upload results - old
|
- name: Upload results - old
|
||||||
if: ${{ inputs.verilator-archive-old != '' }}
|
if: ${{ inputs.verilator-archive-old != '' }}
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: reference-${{ steps.results.outputs.hash }}.json
|
path: reference-${{ steps.results.outputs.hash }}.json
|
||||||
name: rtlmeter-${{ inputs.tag }}-reference-${{ steps.results.outputs.hash }}
|
name: rtlmeter-${{ inputs.tag }}-reference-${{ steps.results.outputs.hash }}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Download repository archive
|
- name: Download repository archive
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||||
with:
|
with:
|
||||||
name: ${{ inputs.archive }}
|
name: ${{ inputs.archive }}
|
||||||
path: ${{ github.workspace }}
|
path: ${{ github.workspace }}
|
||||||
|
|
@ -67,7 +67,7 @@ jobs:
|
||||||
ls -lsha
|
ls -lsha
|
||||||
|
|
||||||
- name: Cache $CCACHE_DIR
|
- name: Cache $CCACHE_DIR
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||||
env:
|
env:
|
||||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2
|
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2
|
||||||
with:
|
with:
|
||||||
|
|
@ -99,7 +99,7 @@ jobs:
|
||||||
|
|
||||||
- name: Upload code coverage data
|
- name: Upload code coverage data
|
||||||
if: ${{ inputs.dev-gcov }}
|
if: ${{ inputs.dev-gcov }}
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/repo/obj_coverage/verilator-${{ inputs.suite }}.info
|
path: ${{ github.workspace }}/repo/obj_coverage/verilator-${{ inputs.suite }}.info
|
||||||
name: code-coverage-${{ inputs.suite }}
|
name: code-coverage-${{ inputs.suite }}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ jobs:
|
||||||
cases: ${{ steps.cases.outputs.cases }}
|
cases: ${{ steps.cases.outputs.cases }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
|
|
||||||
- name: Startup
|
- name: Startup
|
||||||
id: start
|
id: start
|
||||||
|
|
@ -208,7 +208,7 @@ jobs:
|
||||||
run: echo "tags=$(jq -r 'keys | map(sub("^run-"; "")) | join(" ")' <<< '${{ toJSON(needs) }}')" >> "$GITHUB_OUTPUT"
|
run: echo "tags=$(jq -r 'keys | map(sub("^run-"; "")) | join(" ")' <<< '${{ toJSON(needs) }}')" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Checkout RTLMeter
|
- name: Checkout RTLMeter
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
with:
|
with:
|
||||||
repository: "verilator/rtlmeter"
|
repository: "verilator/rtlmeter"
|
||||||
path: rtlmeter
|
path: rtlmeter
|
||||||
|
|
@ -232,7 +232,7 @@ jobs:
|
||||||
./rtlmeter collate ../all-results-$tag/*.json > ../all-results-$tag.json
|
./rtlmeter collate ../all-results-$tag/*.json > ../all-results-$tag.json
|
||||||
done
|
done
|
||||||
- name: Upload combined results
|
- name: Upload combined results
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: all-results-*.json
|
path: all-results-*.json
|
||||||
name: all-results
|
name: all-results
|
||||||
|
|
@ -259,7 +259,7 @@ jobs:
|
||||||
done
|
done
|
||||||
- name: Upload reference results
|
- name: Upload reference results
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: all-reference-*.json
|
path: all-reference-*.json
|
||||||
name: all-reference
|
name: all-reference
|
||||||
|
|
@ -278,27 +278,27 @@ jobs:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Download combined results
|
- name: Download combined results
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||||
with:
|
with:
|
||||||
name: all-results
|
name: all-results
|
||||||
path: results
|
path: results
|
||||||
- name: Upload published results
|
- name: Upload published results
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: results/*.json
|
path: results/*.json
|
||||||
name: published-results
|
name: published-results
|
||||||
# Pushing to verilator/verilator-rtlmeter-results requires elevated permissions
|
# Pushing to verilator/verilator-rtlmeter-results requires elevated permissions
|
||||||
- name: Generate access token
|
- name: Generate access token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@v3.2.0
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||||
with:
|
with:
|
||||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
client-id: ${{ vars.VERILATOR_CI_ID }}
|
||||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||||
owner: verilator
|
owner: verilator
|
||||||
repositories: verilator-rtlmeter-results
|
repositories: verilator-rtlmeter-results
|
||||||
permission-contents: write
|
permission-contents: write
|
||||||
- name: Checkout verilator-rtlmeter-results
|
- name: Checkout verilator-rtlmeter-results
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
with:
|
with:
|
||||||
repository: "verilator/verilator-rtlmeter-results"
|
repository: "verilator/verilator-rtlmeter-results"
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
@ -331,7 +331,7 @@ jobs:
|
||||||
actions: read
|
actions: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout RTLMeter
|
- name: Checkout RTLMeter
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
with:
|
with:
|
||||||
repository: "verilator/rtlmeter"
|
repository: "verilator/rtlmeter"
|
||||||
path: rtlmeter
|
path: rtlmeter
|
||||||
|
|
@ -341,7 +341,7 @@ jobs:
|
||||||
run: make venv
|
run: make venv
|
||||||
|
|
||||||
- name: Checkout Verilator
|
- name: Checkout Verilator
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
with:
|
with:
|
||||||
path: verilator
|
path: verilator
|
||||||
|
|
||||||
|
|
@ -367,13 +367,13 @@ jobs:
|
||||||
echo ${{ github.event.number }} > ../notification-artifact/pr-number.txt
|
echo ${{ github.event.number }} > ../notification-artifact/pr-number.txt
|
||||||
|
|
||||||
- name: Upload report
|
- name: Upload report
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: report-artifact
|
path: report-artifact
|
||||||
name: rtlmeter-report
|
name: rtlmeter-report
|
||||||
|
|
||||||
- name: Upload notification
|
- name: Upload notification
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: notification-artifact
|
path: notification-artifact
|
||||||
name: pr-notification
|
name: pr-notification
|
||||||
|
|
@ -392,9 +392,9 @@ jobs:
|
||||||
# Creating issues requires elevated privilege
|
# Creating issues requires elevated privilege
|
||||||
- name: Generate access token
|
- name: Generate access token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@v3.2.0
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||||
with:
|
with:
|
||||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
client-id: ${{ vars.VERILATOR_CI_ID }}
|
||||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||||
owner: verilator
|
owner: verilator
|
||||||
repositories: verilator
|
repositories: verilator
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
<!-- DESCRIPTION: Verilator: Repository-wide guidelines for AI coding agents
|
||||||
|
SPDX-FileCopyrightText: 2026-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -->
|
||||||
|
|
||||||
|
# Verilator Guidelines for AI Coding Agents
|
||||||
|
|
||||||
|
These files are the general layer an agent loads first -- nearest file wins, so
|
||||||
|
you read this repository-root file plus the one for the directory you are
|
||||||
|
editing. They stay deliberately high-level: where to start, how the tree is laid
|
||||||
|
out, and the conventions reviewers otherwise enforce by hand. They are an index,
|
||||||
|
not the architecture reference -- for depth (how a pass works internally, the
|
||||||
|
algorithms, node lifetime) they point you to `docs/internals.rst`. When the
|
||||||
|
guidance here is not enough, that is where to look next.
|
||||||
|
|
||||||
|
This file has two parts. **Orientation** gets you productive in the codebase from
|
||||||
|
a cold start. **Before you open a PR** is the checklist of conventions reviewers
|
||||||
|
otherwise have to enforce by hand -- read it before submitting any change.
|
||||||
|
|
||||||
|
Then read the directory guide for the area you are editing:
|
||||||
|
|
||||||
|
- [src/AGENTS.md](src/AGENTS.md) -- compiler C++ sources: AST, visitors, passes, parser, style
|
||||||
|
- [include/AGENTS.md](include/AGENTS.md) -- runtime library (`verilated*`): C++14, MT-safety, fixed-width types
|
||||||
|
- [test_regress/AGENTS.md](test_regress/AGENTS.md) -- regression tests: harness, drivers, golden files
|
||||||
|
- [docs/AGENTS.md](docs/AGENTS.md) -- documentation (`*.rst`)
|
||||||
|
|
||||||
|
______________________________________________________________________
|
||||||
|
|
||||||
|
# Orientation
|
||||||
|
|
||||||
|
## What Verilator is
|
||||||
|
|
||||||
|
Verilator is a *compiler*, not an interpreter. It translates synthesizable (and
|
||||||
|
much behavioral) SystemVerilog into a cycle-accurate C++ model that you then
|
||||||
|
compile and run. Almost every decision is made at compile ("verilation") time;
|
||||||
|
the generated C++ just advances state each evaluation. Optimize for verilation-
|
||||||
|
time work over runtime work.
|
||||||
|
|
||||||
|
## The pipeline is the spine
|
||||||
|
|
||||||
|
A run is an ordered sequence of passes over one shared AST (abstract syntax
|
||||||
|
tree). In source order:
|
||||||
|
|
||||||
|
| Stage | What it does | Key files |
|
||||||
|
|---|---|---|
|
||||||
|
| Preprocess + parse | Lex and parse text into a raw AST -- builds nodes only, no semantic checks | `verilog.l`, `verilog.y` |
|
||||||
|
| Link / elaborate | Resolve names, scopes, parameters; instantiate the hierarchy | `V3LinkParse`, `V3LinkDot`, `V3Param` |
|
||||||
|
| Width / type | Assign and check data types and bit widths | `V3Width` |
|
||||||
|
| Transform / optimize / schedule | Constant fold, lower language features, schedule events | `V3Const`, `V3Randomize`, `V3Assert*`, `V3Sched`, `V3Timing`, `V3Dfg` |
|
||||||
|
| Emit | Lower the final AST to generated C++ | `V3EmitC*` |
|
||||||
|
| Runtime | Library the generated model links against | `include/verilated*` |
|
||||||
|
|
||||||
|
This table is the map; `docs/internals.rst` has the detail behind each stage.
|
||||||
|
|
||||||
|
## Where to make a change
|
||||||
|
|
||||||
|
Map the symptom to the pass that owns it, then start by reading that pass's
|
||||||
|
top-of-file comment.
|
||||||
|
|
||||||
|
| Symptom or feature area | Start in |
|
||||||
|
|---|---|
|
||||||
|
| Type/width error, "what type is this", implicit conversion | `V3Width` |
|
||||||
|
| Name/scope/parameter resolution ("Can't find...", hierarchy) | `V3LinkDot`, `V3Param` |
|
||||||
|
| `randomize` / `constraint` / `rand` / `randc` | `V3Randomize` |
|
||||||
|
| `assert` / `property` / `sequence` / `cover` | `V3Assert`, `V3AssertPre`, `V3AssertNfa` |
|
||||||
|
| `fork` / timing / `#delay` / NBA / event scheduling | `V3Sched`, `V3Timing`, `V3Fork` |
|
||||||
|
| Syntax wrongly accepted or rejected | `verilog.y`, `verilog.l` |
|
||||||
|
| Wrong generated C++ | `V3EmitC*` |
|
||||||
|
| Runtime model behavior | `include/verilated*` |
|
||||||
|
|
||||||
|
## Build and run a test
|
||||||
|
|
||||||
|
- Build in the source tree: `autoconf && ./configure && make -j8`. Configure with
|
||||||
|
`--enable-ccwarn` so a new compiler warning stops the build.
|
||||||
|
- Run one test from the repository root: `test_regress/t/t_<name>.py`.
|
||||||
|
- Run the full regression with `make test`. The complete suite requires
|
||||||
|
configuring with `--enable-longtests` (works on every OS, including macOS).
|
||||||
|
|
||||||
|
______________________________________________________________________
|
||||||
|
|
||||||
|
# Before you open a PR
|
||||||
|
|
||||||
|
## Scope and process
|
||||||
|
|
||||||
|
- [ ] Searched open PRs and issues -- duplicating in-flight work wastes review time.
|
||||||
|
- [ ] Fixed the general root cause, not just the reported case -- if it also
|
||||||
|
affects other modules/classes/interfaces, cover them or expect rejection.
|
||||||
|
- [ ] PR is single-purpose. Refactors, drive-by fixes found along the way, and new
|
||||||
|
features each go in separate PRs; land standalone cleanups first.
|
||||||
|
- [ ] Every bug fix has a test that fails *without* the fix; include the issue's
|
||||||
|
own reproducer when possible.
|
||||||
|
- [ ] New code aims for 100% line coverage; branch coverage far below line coverage
|
||||||
|
signals guards callers never violate -- justify or remove them.
|
||||||
|
- [ ] Ran `make format` (clang-format), `make cppcheck`, and `make lint-py`;
|
||||||
|
self-reviewed the diff for leftover debug code, stale comments, and
|
||||||
|
copy-paste errors.
|
||||||
|
- [ ] Ran the full regression on at least one OS before submitting. Partial runs
|
||||||
|
are fine during development, but the submitted PR is expected to pass every
|
||||||
|
test.
|
||||||
|
- [ ] Did not edit `docs/CONTRIBUTORS` (humans only) or `Changes` (maintainer
|
||||||
|
updates it near release).
|
||||||
|
|
||||||
|
## Pick the right diagnostic (and its required test)
|
||||||
|
|
||||||
|
The API you choose determines which test must accompany the change.
|
||||||
|
|
||||||
|
| API | Output | Meaning | Required test |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `v3error("...")` | `%Error:` | User wrote invalid SystemVerilog | `t_*_bad*.v` + `.out` golden |
|
||||||
|
| `v3error("Unsupported: ...")` | `%Error-UNSUPPORTED:` | Legal SV that Verilator does not yet support | `t_*_unsup*.v` + `.out` golden |
|
||||||
|
| `v3warn(CODE, "...")` | `%Warning-CODE:` | Legal but suspicious code | warning test + `.out` golden |
|
||||||
|
| `v3fatalSrc("...")` | `%Error: Internal Error` | Should-never-happen internal assertion | none -- not user-triggerable |
|
||||||
|
|
||||||
|
- Every `v3error`/`v3warn` needs a test in `test_regress/t/` -- enforced by the
|
||||||
|
warn-coverage distribution test. `v3fatalSrc` is exempt.
|
||||||
|
- Reserve "Unsupported:" for not-yet-implemented features, never for user mistakes.
|
||||||
|
- When an error enforces a spec-defined restriction, cite the clause
|
||||||
|
(`IEEE 1800-2023 11.4.7`) so it is verifiable. Update `docs/guide/warnings.rst`
|
||||||
|
when adding or changing a warning.
|
||||||
|
- On error paths, clean up or replace invalid AST (e.g. `AstConst::BitFalse`) so
|
||||||
|
later passes do not crash after the error.
|
||||||
|
|
||||||
|
## Cross-cutting code rules
|
||||||
|
|
||||||
|
- [ ] No non-ASCII characters in C++ sources or headers: write `--` (two ASCII
|
||||||
|
hyphens) rather than a Unicode em-dash, and a plain `'` rather than a smart
|
||||||
|
quote. At write time, not when CI complains.
|
||||||
|
- [ ] Lists stay sorted: lexer/parser tokens, option declarations, enum values,
|
||||||
|
configure feature lists, documented option lists.
|
||||||
|
- [ ] `bin/` scripts are Python (distributed cross-platform); `nodist/` may use
|
||||||
|
bash and platform-specific code (developer-only, not packaged).
|
||||||
|
- [ ] Runtime code in `include/` targets C++14 (`--no-timing` builds must work);
|
||||||
|
C++20 only in timing code paths.
|
||||||
|
- [ ] In `include/` public headers, prefix public classes with `Verilated`/`Vl`
|
||||||
|
and document the API with `///` comments.
|
||||||
|
- [ ] A new code pattern is applied globally or not at all -- no one-off
|
||||||
|
convention in a single file.
|
||||||
|
|
||||||
|
## Commits
|
||||||
|
|
||||||
|
- Subject line is short and imperative and conventionally ends with the PR number:
|
||||||
|
`Support property case (#7721)`. A body is optional and common for non-trivial
|
||||||
|
changes.
|
||||||
51
Changes
51
Changes
|
|
@ -15,7 +15,7 @@ Verilator 5.049 devel
|
||||||
|
|
||||||
**Important:**
|
**Important:**
|
||||||
|
|
||||||
* Support covergroups, coverpoints, and bins (#784) (#7117). [Matthew Ballance]
|
* Support covergroups, coverpoints, and bins (#784) (#7117) (#7728). [Matthew Ballance]
|
||||||
* Support new FST writer API (#6871) (#6992). [Yu-Sheng Lin]
|
* Support new FST writer API (#6871) (#6992). [Yu-Sheng Lin]
|
||||||
Use of FST may requiring installing liblz4 and/or liblz4-dev packages, see docs/install.rst.
|
Use of FST may requiring installing liblz4 and/or liblz4-dev packages, see docs/install.rst.
|
||||||
|
|
||||||
|
|
@ -27,9 +27,12 @@ Verilator 5.049 devel
|
||||||
* Add `--coverage-per-instance` (#7636). [Yogish Sekhar]
|
* Add `--coverage-per-instance` (#7636). [Yogish Sekhar]
|
||||||
* Add NOTREDOP error on reduction and negation operators (#7417) (#7623) (#7624).
|
* Add NOTREDOP error on reduction and negation operators (#7417) (#7623) (#7624).
|
||||||
* Add hierarchy-aware reporting to `verilator_coverage` (#7657). [Yogish Sekhar]
|
* Add hierarchy-aware reporting to `verilator_coverage` (#7657). [Yogish Sekhar]
|
||||||
|
* Add FINALDLY error (#7754). [Igor Zaworski, Antmicro Ltd.]
|
||||||
|
* Deprecate isolate_assignments attribute (#7774) (#7144). [Geza Lore, Testorrent USA, Inc.]
|
||||||
* Improve `--coverage-fsm` (#7490) (#7529) (#7561) (#7573) (#7619). [Yogish Sekhar]
|
* Improve `--coverage-fsm` (#7490) (#7529) (#7561) (#7573) (#7619). [Yogish Sekhar]
|
||||||
* Change `+verilator+seed` to default to 1, and 0 to randomly select (#7325) (#7516). [Miguel]
|
* Change `+verilator+seed` to default to 1, and 0 to randomly select (#7325) (#7516). [Miguel]
|
||||||
* Change JSON to include parameter constant mnemonics for FSM Coverage (#7531). [Yogish Sekhar]
|
* Change JSON to include parameter constant mnemonics for FSM Coverage (#7531). [Yogish Sekhar]
|
||||||
|
* Support assert property 'default disable iff` (#4848) (#7723). [Artur Bieniek, Antmicro Ltd.]
|
||||||
* Support printing enum names for %p and %s (#5523) (#7338 repair) (#7521) (#7527). [Nick Brereton]
|
* Support printing enum names for %p and %s (#5523) (#7338 repair) (#7521) (#7527). [Nick Brereton]
|
||||||
* Support weak `until` / `until_with` property operators (#7290) (#7548) (#7685). [Yilou Wang]
|
* Support weak `until` / `until_with` property operators (#7290) (#7548) (#7685). [Yilou Wang]
|
||||||
* Support `s_eventually` (#7291) (#7508). [Bartłomiej Chmiel, Antmicro Ltd.]
|
* Support `s_eventually` (#7291) (#7508). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||||
|
|
@ -50,6 +53,7 @@ Verilator 5.049 devel
|
||||||
* Support procedural concurrent assertions with inferred clock (#7581). [Yilou Wang]
|
* Support procedural concurrent assertions with inferred clock (#7581). [Yilou Wang]
|
||||||
* Support calling interface functions without parens (#7584). [Krzysztof Bieganski, Antmicro Ltd.]
|
* Support calling interface functions without parens (#7584). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||||
* Support streaming on queues (#7597). [Benjamin Collier, Secturion Systems, Inc.]
|
* Support streaming on queues (#7597). [Benjamin Collier, Secturion Systems, Inc.]
|
||||||
|
* Support clocking event on a sequence declaration body (#7598) (#7793). [Yilou Wang]
|
||||||
* Support generic interface arrays (#7604). [Krzysztof Bieganski, Antmicro Ltd.]
|
* Support generic interface arrays (#7604). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||||
* Support FSM detection in primitive wrappers (#7607). [Yogish Sekhar]
|
* Support FSM detection in primitive wrappers (#7607). [Yogish Sekhar]
|
||||||
* Support busses with mix of pullup/pulldown (#7632). [Lucas Amaral]
|
* Support busses with mix of pullup/pulldown (#7632). [Lucas Amaral]
|
||||||
|
|
@ -59,6 +63,18 @@ Verilator 5.049 devel
|
||||||
* Support if/if-else in properties (#7692). [Artur Bieniek, Antmicro Ltd.]
|
* Support if/if-else in properties (#7692). [Artur Bieniek, Antmicro Ltd.]
|
||||||
* Support process::self().srand() (#7695). [Igor Zaworski, Antmicro Ltd.]
|
* Support process::self().srand() (#7695). [Igor Zaworski, Antmicro Ltd.]
|
||||||
* Support MacOS lldb (#7697). [Tracy Narine]
|
* Support MacOS lldb (#7697). [Tracy Narine]
|
||||||
|
* Support assoc array methods with wide value types (#7680). [pawelktk]
|
||||||
|
* Support property case (#7682) (#7721). [Artur Bieniek, Antmicro Ltd.]
|
||||||
|
* Support `s_until` and `s_until_with`(#7722). [Artur Bieniek, Antmicro Ltd.]
|
||||||
|
* Support covergroup runtime model Phase A1 (#7728). [Matthew Ballance]
|
||||||
|
* Support reduction XOR/AND operations in constraints (#7753). [Kornel Uriasz, Antmicro Ltd.]
|
||||||
|
* Support NBAs in initial blocks (#7754). [Igor Zaworski, Antmicro Ltd.]
|
||||||
|
* Support assertion control system tasks in classes and interfaces (#7761). [Yilou Wang]
|
||||||
|
* Support cover sequence statement (#7764). [Yilou Wang]
|
||||||
|
* Support unpacked struct stream (#7767). [Nick Brereton]
|
||||||
|
* Support $assertcontrol control_type from lock to kill (#7788). [Yilou Wang]
|
||||||
|
* Support unbounded always [m:$] and strong s_always liveness (#7798). [Yilou Wang]
|
||||||
|
* Support method calls on a sub-interface via a virtual interface (#7800). [Yilou Wang]
|
||||||
* Optimize emitting to_string() for compiler speedup (#7468). [Jakub Michalski, Antmicro Ltd.]
|
* Optimize emitting to_string() for compiler speedup (#7468). [Jakub Michalski, Antmicro Ltd.]
|
||||||
* Optimize additional DFG peephole cases (#7553). [Varun Koyyalagunta, Testorrent USA, Inc.]
|
* Optimize additional DFG peephole cases (#7553). [Varun Koyyalagunta, Testorrent USA, Inc.]
|
||||||
* Optimize forced signal handling (#7554 partial) (#7572) (#7594) (#7596). [Krzysztof Bieganski, Artur Bieniek, Antmicro Ltd.]
|
* Optimize forced signal handling (#7554 partial) (#7572) (#7594) (#7596). [Krzysztof Bieganski, Artur Bieniek, Antmicro Ltd.]
|
||||||
|
|
@ -71,9 +87,22 @@ Verilator 5.049 devel
|
||||||
* Optimize runtime assertOn() checks (#7707). [Geza Lore, Testorrent USA, Inc.]
|
* Optimize runtime assertOn() checks (#7707). [Geza Lore, Testorrent USA, Inc.]
|
||||||
* Optimize $countones and $onehot in DFG. [Geza Lore, Testorrent USA, Inc.]
|
* Optimize $countones and $onehot in DFG. [Geza Lore, Testorrent USA, Inc.]
|
||||||
* Optimize procedural loop unrolling. [Geza Lore, Testorrent USA, Inc.]
|
* Optimize procedural loop unrolling. [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Optimize V3Gate inlining heuristic (#7716). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Optimize reset in DFG (#7737). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Optimize DFG with relaxed live variable analysis (#7739). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Optimize conditional patterns sharing common MBSs/LSBs in DfgPeephole (#7760). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Optimize bit select removal earlier in DFG (#7762). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Optimize away proven redundant case statement assertions (#7771). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Optimize table lookups in DFG (#7772). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Optimize input combinational logic by change detection (#7784). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Optimize decoder case statements into lookup tables (#7795). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Optimize wide decoder case statements into decoder expressions (#7804). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Optimize DFG cycle breaking to do less work (#7210). [Geza Lore, Testorrent USA, Inc.]
|
||||||
* Fix TSP variable ordering for mtasks (#5342) (#7610). [Muzaffer Kal]
|
* Fix TSP variable ordering for mtasks (#5342) (#7610). [Muzaffer Kal]
|
||||||
* Fix inlining static initializer in V3Gate (#5381) (#7503). [Andrew Nolte] [Geza Lore, Testorrent USA, Inc.]
|
* Fix inlining static initializer in V3Gate (#5381) (#7503). [Andrew Nolte] [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Fix timed nested fork block with disable (#6720) (#7743). [Marco Bartoli]
|
||||||
* Fix segmentation fault when using --trace with --lib-create (#7299) (#7518). [anonkey]
|
* Fix segmentation fault when using --trace with --lib-create (#7299) (#7518). [anonkey]
|
||||||
|
* Fix cross-hierarchy tristate drivers of interface nets (#7339) (#7801). [Yilou Wang]
|
||||||
* Fix destructive event state before dynamic waits (#7340). [Nick Brereton]
|
* Fix destructive event state before dynamic waits (#7340). [Nick Brereton]
|
||||||
* Fix ALWCOMBORDER on variable ordering (#7350) (#7608). [Cookie]
|
* Fix ALWCOMBORDER on variable ordering (#7350) (#7608). [Cookie]
|
||||||
* Fix false MULTIDRIVEN warning on always_ff variables (#7351) (#7621) (#7672).
|
* Fix false MULTIDRIVEN warning on always_ff variables (#7351) (#7621) (#7672).
|
||||||
|
|
@ -128,13 +157,33 @@ Verilator 5.049 devel
|
||||||
* Fix loss of events due to bit shift (#7670). [Artur Bieniek, Antmicro Ltd.]
|
* Fix loss of events due to bit shift (#7670). [Artur Bieniek, Antmicro Ltd.]
|
||||||
* Fix parameter read through locally-declared interface instance (#7679). [Nick Brereton]
|
* Fix parameter read through locally-declared interface instance (#7679). [Nick Brereton]
|
||||||
* Fix skipping nulls in $sscanf (#7689).
|
* Fix skipping nulls in $sscanf (#7689).
|
||||||
|
* Fix bounds checks in expressions with read/write references (#7694). [Ryszard Rozak, Antmicro Ltd.]
|
||||||
* Fix (const) ref default task argument handling (#7698). [Nick Brereton]
|
* Fix (const) ref default task argument handling (#7698). [Nick Brereton]
|
||||||
* Fix `ref` argument type check for packed arrays with differing range directions (#7700). [Nick Brereton]
|
* Fix `ref` argument type check for packed arrays with differing range directions (#7700). [Nick Brereton]
|
||||||
* Fix ignoring not-found modules with encoded names (#7706). [Igor Zaworski, Antmicro Ltd.]
|
* Fix ignoring not-found modules with encoded names (#7706). [Igor Zaworski, Antmicro Ltd.]
|
||||||
* Fix MULTIDRIVEN in generates (#7709). [Todd Strader]
|
* Fix MULTIDRIVEN in generates (#7709). [Todd Strader]
|
||||||
|
* Fix parameter pollution when using class parameters (#7711) (#7763). [em2machine]
|
||||||
* Fix Makefile action to not write to ${srcdir} (#7715). [Larry Doolittle]
|
* Fix Makefile action to not write to ${srcdir} (#7715). [Larry Doolittle]
|
||||||
* Fix splitting functions containing fork logic (#7717). [Mateusz Gancarz, Antmicro Ltd.]
|
* Fix splitting functions containing fork logic (#7717). [Mateusz Gancarz, Antmicro Ltd.]
|
||||||
* Fix optimizations of assignments with timing controls (#7718). [Ryszard Rozak, Antmicro Ltd.]
|
* Fix optimizations of assignments with timing controls (#7718). [Ryszard Rozak, Antmicro Ltd.]
|
||||||
|
* Fix s_eventually on interface (#7731) (#7733). [Marco Bartoli]
|
||||||
|
* Fix parameter values in coverage bins widths (#7732) (#7734). [Marco Bartoli]
|
||||||
|
* Fix configure fall back on dynamic malloc libraries (#7736). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Fix crash on overlapping priority case. [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Fix s_eventually in parameterized interfaces (#7741). [Nick Brereton]
|
||||||
|
* Fix dpi export pointers (#7742) (#7751). [Yilin Li]
|
||||||
|
* Fix force on unpacked bit select (#7744) (#7745). [Nikolai Kumar]
|
||||||
|
* Fix FSM detect unchecked casts and variable redeclaration (#7758). [Adam Kostrzewski, Antmicro Ltd.]
|
||||||
|
* Fix no-scope internal error on virtual interface method calls (#7759). [Yilou Wang]
|
||||||
|
* Fix 'case (_) inside' with x wildcards (#7766). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Fix not failing assertion when RHS of a range window rejects once (#7773). [Artur Bieniek, Antmicro Ltd.]
|
||||||
|
* Fix $fflush and autoflush with --threads (#7782).
|
||||||
|
* Fix out-of-bounds read value for 2-state types (#7785). [Jakub Michalski]
|
||||||
|
* Fix `cover property` of an implication counting vacuous matches (#7789). [Yilou Wang]
|
||||||
|
* Fix randomization of dynamic arrays of objects (#7790). [Ryszard Rozak, Antmicro Ltd.]
|
||||||
|
* Fix `$bits` on unpacked structs (#4521) (#7796). [Nick Brereton]
|
||||||
|
* Fix randomize() with skipping derived pre/post_randomize (#7799). [Yilou Wang]
|
||||||
|
* Fix assertion when loop unrolling failed (#7810). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
|
||||||
|
|
||||||
Verilator 5.048 2026-04-26
|
Verilator 5.048 2026-04-26
|
||||||
|
|
|
||||||
28
Makefile.in
28
Makefile.in
|
|
@ -495,6 +495,11 @@ MAKE_FILES = \
|
||||||
src/Makefile*.in \
|
src/Makefile*.in \
|
||||||
test_regress/Makefile* \
|
test_regress/Makefile* \
|
||||||
|
|
||||||
|
# Markdown
|
||||||
|
MD_FILES = \
|
||||||
|
*.md \
|
||||||
|
*/*.md \
|
||||||
|
|
||||||
# Perl programs
|
# Perl programs
|
||||||
PERL_PROGRAMS = \
|
PERL_PROGRAMS = \
|
||||||
bin/redirect \
|
bin/redirect \
|
||||||
|
|
@ -541,6 +546,13 @@ PY_FILES = \
|
||||||
# Python files, test_regress tests
|
# Python files, test_regress tests
|
||||||
PY_TEST_FILES = test_regress/t/*.py
|
PY_TEST_FILES = test_regress/t/*.py
|
||||||
|
|
||||||
|
# reStructuredText Sphinx files
|
||||||
|
RST_FILES = \
|
||||||
|
*.rst \
|
||||||
|
*/*.rst \
|
||||||
|
ci/docker/*/*.rst \
|
||||||
|
docs/guide/*.rst \
|
||||||
|
|
||||||
# YAML files
|
# YAML files
|
||||||
YAML_FILES = \
|
YAML_FILES = \
|
||||||
.*.yaml \
|
.*.yaml \
|
||||||
|
|
@ -553,7 +565,7 @@ YAML_FILES = \
|
||||||
# Format
|
# Format
|
||||||
|
|
||||||
format:
|
format:
|
||||||
$(MAKE) -j 5 format-c format-cmake format-exec format-py format-yaml
|
$(MAKE) -j 5 format-c format-cmake format-exec format-md format-py format-rst format-yaml
|
||||||
|
|
||||||
BEAUTYSH = beautysh
|
BEAUTYSH = beautysh
|
||||||
BEAUTYSH_FLAGS = --indent-size 2
|
BEAUTYSH_FLAGS = --indent-size 2
|
||||||
|
|
@ -590,6 +602,13 @@ format-make mbake:
|
||||||
$(MBAKE) --version
|
$(MBAKE) --version
|
||||||
$(MBAKE) $(MBAKE_FLAGS) $(MAKE_FILES)
|
$(MBAKE) $(MBAKE_FLAGS) $(MAKE_FILES)
|
||||||
|
|
||||||
|
MDFORMAT = mdformat
|
||||||
|
MDFORMAT_FLAGS =
|
||||||
|
|
||||||
|
format-md:
|
||||||
|
$(MDFORMAT) --version
|
||||||
|
$(MDFORMAT) $(MDFORMAT_FLAGS) $(MD_FILES)
|
||||||
|
|
||||||
YAPF = yapf
|
YAPF = yapf
|
||||||
YAPF_FLAGS = -i --parallel
|
YAPF_FLAGS = -i --parallel
|
||||||
|
|
||||||
|
|
@ -597,6 +616,13 @@ format-py yapf:
|
||||||
$(YAPF) --version
|
$(YAPF) --version
|
||||||
$(YAPF) $(YAPF_FLAGS) $(PY_FILES)
|
$(YAPF) $(YAPF_FLAGS) $(PY_FILES)
|
||||||
|
|
||||||
|
DOCSTRFMT = docstrfmt
|
||||||
|
DOCSTRFMT_FLAGS = --line-length 75 --indent-width 3 --keep-blanks --ordered-marker "\#" --preserve-adornments --no-center-section-titles
|
||||||
|
|
||||||
|
format-rst:
|
||||||
|
$(DOCSTRFMT) --version
|
||||||
|
$(DOCSTRFMT) $(DOCSTRFMT_FLAGS) $(RST_FILES)
|
||||||
|
|
||||||
YAMLFIX = YAMLFIX_WHITELINES=1 YAMLFIX_LINE_LENGTH=200 YAMLFIX_preserve_quotes=true yamlfix
|
YAMLFIX = YAMLFIX_WHITELINES=1 YAMLFIX_LINE_LENGTH=200 YAMLFIX_preserve_quotes=true yamlfix
|
||||||
YAMLFIX_FLAGS =
|
YAMLFIX_FLAGS =
|
||||||
|
|
||||||
|
|
|
||||||
69
README.rst
69
README.rst
|
|
@ -1,58 +1,63 @@
|
||||||
.. Github doesn't render images unless absolute URL
|
..
|
||||||
.. Do not know of a conditional tag, "only: github" nor "github display" works
|
Github doesn't render images unless absolute URL
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
Do not know of a conditional tag, "only: github" nor "github display" works
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|badge1| |badge2| |badge3| |badge4| |badge5| |badge7| |badge8|
|
|badge1| |badge2| |badge3| |badge4| |badge5| |badge7| |badge8|
|
||||||
|
|
||||||
.. |badge1| image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|
.. |badge1| image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|
||||||
:target: https://verilator.org
|
:target: https://verilator.org
|
||||||
|
|
||||||
.. |badge2| image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
|
.. |badge2| image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
|
||||||
:target: https://www.gnu.org/licenses/lgpl-3.0
|
:target: https://www.gnu.org/licenses/lgpl-3.0
|
||||||
|
|
||||||
.. |badge3| image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
.. |badge3| image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
||||||
:target: https://opensource.org/licenses/Artistic-2.0
|
:target: https://opensource.org/licenses/Artistic-2.0
|
||||||
|
|
||||||
.. |badge4| image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
.. |badge4| image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
||||||
:target: https://repology.org/project/verilator/versions
|
:target: https://repology.org/project/verilator/versions
|
||||||
|
|
||||||
.. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator
|
.. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator
|
||||||
:target: https://hub.docker.com/r/verilator/verilator
|
:target: https://hub.docker.com/r/verilator/verilator
|
||||||
|
|
||||||
.. |badge7| image:: https://github.com/verilator/verilator/workflows/build/badge.svg
|
.. |badge7| image:: https://github.com/verilator/verilator/workflows/build/badge.svg
|
||||||
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
|
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
|
||||||
|
|
||||||
.. |badge8| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/rtlmeter.yml?branch=master&event=schedule&label=benchmarks
|
.. |badge8| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/rtlmeter.yml?branch=master&event=schedule&label=benchmarks
|
||||||
:target: https://verilator.github.io/verilator-rtlmeter-results
|
:target: https://verilator.github.io/verilator-rtlmeter-results
|
||||||
|
|
||||||
|
|
||||||
Welcome to Verilator
|
Welcome to Verilator
|
||||||
====================
|
====================
|
||||||
|
|
||||||
.. list-table::
|
.. list-table::
|
||||||
|
|
||||||
* - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.**
|
- - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.**
|
||||||
* Accepts Verilog or SystemVerilog
|
- Accepts Verilog or SystemVerilog
|
||||||
* Performs lint code-quality checks
|
- Performs lint code-quality checks
|
||||||
* Compiles into multithreaded C++, or SystemC
|
- Compiles into multithreaded C++, or SystemC
|
||||||
* Creates JSON to front-end your own tools
|
- Creates JSON to front-end your own tools
|
||||||
- |Logo|
|
- |Logo|
|
||||||
* - |verilator multithreaded performance|
|
- - |verilator multithreaded performance|
|
||||||
- **Fast**
|
- **Fast**
|
||||||
* Outperforms many closed-source commercial simulators
|
- Outperforms many closed-source commercial simulators
|
||||||
* Single- and multithreaded output models
|
- Single- and multithreaded output models
|
||||||
* - **Widely Used**
|
- - **Widely Used**
|
||||||
* Wide industry and academic deployment
|
- Wide industry and academic deployment
|
||||||
* Out-of-the-box support from Arm and RISC-V vendor IP
|
- Out-of-the-box support from Arm and RISC-V vendor IP
|
||||||
* Over 700 contributors
|
- Over 700 contributors
|
||||||
- |verilator usage|
|
- |verilator usage|
|
||||||
* - |verilator community|
|
- - |verilator community|
|
||||||
- **Community Driven & Openly Licensed**
|
- **Community Driven & Openly Licensed**
|
||||||
* Guided by the `CHIPS Alliance`_ and `Linux Foundation`_
|
- Guided by the `CHIPS Alliance`_ and `Linux Foundation`_
|
||||||
* Open, and free as in both speech and beer
|
- Open, and free as in both speech and beer
|
||||||
* More simulation for your verification budget
|
- More simulation for your verification budget
|
||||||
* - **Commercial Support Available**
|
- - **Commercial Support Available**
|
||||||
* Commercial support contracts
|
- Commercial support contracts
|
||||||
* Design support contracts
|
- Design support contracts
|
||||||
* Enhancement contracts
|
- Enhancement contracts
|
||||||
- |verilator support|
|
- |verilator support|
|
||||||
|
|
||||||
|
|
||||||
What Verilator Does
|
What Verilator Does
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
|
@ -77,7 +82,6 @@ SDF annotation, or mixed-signal simulation. However, if you are looking for
|
||||||
a path to migrate SystemVerilog to C++/SystemC, or want high-speed
|
a path to migrate SystemVerilog to C++/SystemC, or want high-speed
|
||||||
simulation, Verilator is the tool for you.
|
simulation, Verilator is the tool for you.
|
||||||
|
|
||||||
|
|
||||||
Performance
|
Performance
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
|
@ -96,7 +100,6 @@ Mentor ModelSim/Questa, Synopsys VCS, VTOC, and Pragmatic CVer/CVC). But,
|
||||||
Verilator is open-sourced, so you can spend on computes rather than
|
Verilator is open-sourced, so you can spend on computes rather than
|
||||||
licenses. Thus, Verilator gives you the best simulation cycles/dollar.
|
licenses. Thus, Verilator gives you the best simulation cycles/dollar.
|
||||||
|
|
||||||
|
|
||||||
Installation & Documentation
|
Installation & Documentation
|
||||||
============================
|
============================
|
||||||
|
|
||||||
|
|
@ -115,7 +118,6 @@ For more information:
|
||||||
|
|
||||||
- `Verilator issues <https://verilator.org/issues>`_
|
- `Verilator issues <https://verilator.org/issues>`_
|
||||||
|
|
||||||
|
|
||||||
Support
|
Support
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
|
@ -132,7 +134,6 @@ Verilator also supports and encourages commercial support models and
|
||||||
organizations; please see `Verilator Commercial Support
|
organizations; please see `Verilator Commercial Support
|
||||||
<https://verilator.org/verilator_commercial_support>`_.
|
<https://verilator.org/verilator_commercial_support>`_.
|
||||||
|
|
||||||
|
|
||||||
Related Projects
|
Related Projects
|
||||||
================
|
================
|
||||||
|
|
||||||
|
|
@ -149,7 +150,6 @@ Related Projects
|
||||||
- `Surfer <https://surfer-project.org/>`_ - Web or offline waveform viewer
|
- `Surfer <https://surfer-project.org/>`_ - Web or offline waveform viewer
|
||||||
for Verilator traces.
|
for Verilator traces.
|
||||||
|
|
||||||
|
|
||||||
Open License
|
Open License
|
||||||
============
|
============
|
||||||
|
|
||||||
|
|
@ -161,10 +161,17 @@ the terms of either the GNU Lesser General Public License Version 3 or the
|
||||||
Perl Artistic License Version 2.0. See the documentation for more details.
|
Perl Artistic License Version 2.0. See the documentation for more details.
|
||||||
|
|
||||||
.. _chips alliance: https://chipsalliance.org
|
.. _chips alliance: https://chipsalliance.org
|
||||||
|
|
||||||
.. _icarus verilog: https://steveicarus.github.io/iverilog
|
.. _icarus verilog: https://steveicarus.github.io/iverilog
|
||||||
|
|
||||||
.. _linux foundation: https://www.linuxfoundation.org
|
.. _linux foundation: https://www.linuxfoundation.org
|
||||||
|
|
||||||
.. |Logo| image:: https://www.veripool.org/img/verilator_256_200_min.png
|
.. |Logo| image:: https://www.veripool.org/img/verilator_256_200_min.png
|
||||||
|
|
||||||
.. |verilator multithreaded performance| image:: https://www.veripool.org/img/verilator_multithreaded_performance_bg-min.png
|
.. |verilator multithreaded performance| image:: https://www.veripool.org/img/verilator_multithreaded_performance_bg-min.png
|
||||||
|
|
||||||
.. |verilator usage| image:: https://www.veripool.org/img/verilator_usage_400x200-min.png
|
.. |verilator usage| image:: https://www.veripool.org/img/verilator_usage_400x200-min.png
|
||||||
|
|
||||||
.. |verilator community| image:: https://www.veripool.org/img/verilator_community_400x125-min.png
|
.. |verilator community| image:: https://www.veripool.org/img/verilator_community_400x125-min.png
|
||||||
|
|
||||||
.. |verilator support| image:: https://www.veripool.org/img/verilator_support_400x125-min.png
|
.. |verilator support| image:: https://www.veripool.org/img/verilator_support_400x125-min.png
|
||||||
|
|
|
||||||
|
|
@ -438,6 +438,7 @@ detailed descriptions of these arguments.
|
||||||
--diagnostics-sarif-output <filename> Set SARIF diagnostics output file
|
--diagnostics-sarif-output <filename> Set SARIF diagnostics output file
|
||||||
--dpi-hdr-only Only produce the DPI header file
|
--dpi-hdr-only Only produce the DPI header file
|
||||||
--dump-<srcfile> Enable dumping everything in source file
|
--dump-<srcfile> Enable dumping everything in source file
|
||||||
|
--dump-ast-patterns Enable dumping Ast pattern statistics
|
||||||
--dump-defines Show preprocessor defines with -E
|
--dump-defines Show preprocessor defines with -E
|
||||||
--dump-dfg Enable dumping DfgGraphs to .dot files
|
--dump-dfg Enable dumping DfgGraphs to .dot files
|
||||||
--dump-dfg-patterns Enable dumping Dfg pattern statistics
|
--dump-dfg-patterns Enable dumping Dfg pattern statistics
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _verilator build docker container:
|
.. _verilator build docker container:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
Verilator Executable Docker Container
|
Verilator Executable Docker Container
|
||||||
=====================================
|
=====================================
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
<!-- DESCRIPTION: Verilator: docs/ guidelines for AI coding agents
|
||||||
|
SPDX-FileCopyrightText: 2026-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -->
|
||||||
|
|
||||||
|
# docs/ Guidelines -- Verilator documentation (\*.rst)
|
||||||
|
|
||||||
|
When to check: before editing anything under `docs/`.
|
||||||
|
Read the repository-root [AGENTS.md](../AGENTS.md) first for process and cross-cutting rules.
|
||||||
|
|
||||||
|
## Writing rules
|
||||||
|
|
||||||
|
- Rewrap paragraphs after editing -- keep consistent line length in `*.rst` files.
|
||||||
|
|
||||||
|
- Document only stable, implemented features -- never planned or in-development capabilities; prevents user confusion and support burden.
|
||||||
|
|
||||||
|
- Explain WHAT and WHEN, not HOW -- conceptual purpose and practical use cases over implementation mechanics; describe behavior ("optimized as pure", not "treated as pure") and clarify ambiguous referents ("in the internals of Verilator").
|
||||||
|
|
||||||
|
- Keep terminology consistent -- one term per concept; update docs when renaming code constructs; spell out full terms, avoiding abbreviations like "sim"/"sims".
|
||||||
|
|
||||||
|
- Use "how many" for countable nouns (threads, tasks, workers) and "how much" for uncountable quantities.
|
||||||
|
|
||||||
|
- Mark internal or experimental features "for internal use only" -- prevents user dependence and forced deprecation cycles later.
|
||||||
|
|
||||||
|
- Use specific IEEE references: `IEEE {number}-{year}` plus the section (e.g. `Annex I`) -- a vague "IEEE spec requires" is unverifiable.
|
||||||
|
|
||||||
|
- Document all flags with descriptions, not just syntax.
|
||||||
|
|
||||||
|
## reStructuredText mechanics
|
||||||
|
|
||||||
|
- Use the `:vlopt:` role for Verilator option references -- makes cross-references clickable and consistent.
|
||||||
|
|
||||||
|
- Escape angle brackets (`\<`, `\>`) in link targets -- prevents broken links to command-line options.
|
||||||
|
|
||||||
|
- Generate documentation examples with `test.extract` from `test_regress` test files -- examples stay synced with actually tested behavior.
|
||||||
|
|
||||||
|
## Hard constraint
|
||||||
|
|
||||||
|
- Never edit `docs/CONTRIBUTORS` -- only humans may, after reading and agreeing to its statement; remind the user instead.
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
Contributing to Verilator
|
Contributing to Verilator
|
||||||
=========================
|
=========================
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
||||||
|
|
||||||
404allen404
|
404allen404
|
||||||
Adam Bagley
|
Adam Bagley
|
||||||
|
Adam Kostrzewski
|
||||||
Adrian Sampson
|
Adrian Sampson
|
||||||
Adrien Le Masle
|
Adrien Le Masle
|
||||||
أحمد المحمودي (Ahmed El-Mahmoudy)
|
أحمد المحمودي (Ahmed El-Mahmoudy)
|
||||||
|
|
@ -158,6 +159,7 @@ Kefa Chen
|
||||||
Keith Colbert
|
Keith Colbert
|
||||||
Kevin Kiningham
|
Kevin Kiningham
|
||||||
Kevin Nygaard
|
Kevin Nygaard
|
||||||
|
Kornel Uriasz
|
||||||
Kritik Bhimani
|
Kritik Bhimani
|
||||||
Krzysztof Bieganski
|
Krzysztof Bieganski
|
||||||
Krzysztof Boronski
|
Krzysztof Boronski
|
||||||
|
|
@ -250,6 +252,7 @@ Rowan Goemans
|
||||||
Rupert Swarbrick
|
Rupert Swarbrick
|
||||||
Ryan Ziegler
|
Ryan Ziegler
|
||||||
Ryszard Rozak
|
Ryszard Rozak
|
||||||
|
Saksham Gupta
|
||||||
Samuel Riedel
|
Samuel Riedel
|
||||||
Sean Cross
|
Sean Cross
|
||||||
Sebastien Van Cauwenberghe
|
Sebastien Van Cauwenberghe
|
||||||
|
|
@ -270,12 +273,14 @@ Teng Huang
|
||||||
Thomas Aldrian
|
Thomas Aldrian
|
||||||
Thomas Brown
|
Thomas Brown
|
||||||
Thomas Dybdahl Ahle
|
Thomas Dybdahl Ahle
|
||||||
|
Thomas Santerre
|
||||||
Tim Hutt
|
Tim Hutt
|
||||||
Tim Snyder
|
Tim Snyder
|
||||||
Tobias Jensen
|
Tobias Jensen
|
||||||
Tobias Rosenkranz
|
Tobias Rosenkranz
|
||||||
Tobias Wölfel
|
Tobias Wölfel
|
||||||
Todd Strader
|
Todd Strader
|
||||||
|
Tom Jackson
|
||||||
Tom Manner
|
Tom Manner
|
||||||
Tomasz Gorochowik
|
Tomasz Gorochowik
|
||||||
Topa Topino
|
Topa Topino
|
||||||
|
|
@ -297,6 +302,7 @@ Vito Gamberini
|
||||||
Wei-Lun Chiu
|
Wei-Lun Chiu
|
||||||
William D. Jones
|
William D. Jones
|
||||||
Wilson Snyder
|
Wilson Snyder
|
||||||
|
Wolfgang Mayerwieser
|
||||||
Xi Zhang
|
Xi Zhang
|
||||||
Yan Xu
|
Yan Xu
|
||||||
Yangyu Chen
|
Yangyu Chen
|
||||||
|
|
@ -326,3 +332,4 @@ Yogish Sekhar
|
||||||
Zubin Jain
|
Zubin Jain
|
||||||
Muzaffer Kal
|
Muzaffer Kal
|
||||||
Yilin Li
|
Yilin Li
|
||||||
|
Shashvat Prabhu
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
Verilator Documentation
|
Verilator Documentation
|
||||||
=======================
|
=======================
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
.. comment: generated by t_finaldly_bad
|
||||||
|
.. code-block:: sv
|
||||||
|
:linenos:
|
||||||
|
:emphasize-lines: 2
|
||||||
|
|
||||||
|
bit foo;
|
||||||
|
final foo <= 1; // <--- Error
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
.. comment: generated by t_finaldly_bad
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
%Error-FINALDLY: example.v:1:13 Non-blocking assignment '<=' in final block
|
||||||
|
9 | final foo <= 1;
|
||||||
|
| ^~
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
****************
|
****************
|
||||||
Revision History
|
Revision History
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _connecting:
|
.. _connecting:
|
||||||
|
|
||||||
|
|
@ -28,13 +29,13 @@ The generated model class file manages all internal state required by the
|
||||||
model, and exposes the following interface that allows interaction with the
|
model, and exposes the following interface that allows interaction with the
|
||||||
model:
|
model:
|
||||||
|
|
||||||
* Top level IO ports are exposed as references to the appropriate internal
|
- Top level IO ports are exposed as references to the appropriate internal
|
||||||
equivalents.
|
equivalents.
|
||||||
|
|
||||||
* Public top level module instances are exposed as pointers to allow access
|
- Public top level module instances are exposed as pointers to allow access
|
||||||
to ``/* verilator public */`` items.
|
to ``/* verilator public */`` items.
|
||||||
|
|
||||||
* The root of the design hierarchy (as in SystemVerilog ``$root``) is
|
- The root of the design hierarchy (as in SystemVerilog ``$root``) is
|
||||||
exposed via the ``rootp`` member pointer to allow access to model
|
exposed via the ``rootp`` member pointer to allow access to model
|
||||||
internals, including ``/* verilator public_flat */`` items.
|
internals, including ``/* verilator public_flat */`` items.
|
||||||
|
|
||||||
|
|
@ -58,22 +59,22 @@ This means that user code that accesses internal signals in the model
|
||||||
(likely including ``/* verilator public_flat */`` signals, as they are
|
(likely including ``/* verilator public_flat */`` signals, as they are
|
||||||
often inlined into the root scope) will need to be updated as follows:
|
often inlined into the root scope) will need to be updated as follows:
|
||||||
|
|
||||||
* No change required for accessing top level IO signals. These are directly
|
- No change required for accessing top level IO signals. These are directly
|
||||||
accessible in the model class via references.
|
accessible in the model class via references.
|
||||||
|
|
||||||
* No change required for accessing ``/* verilator public */`` items. These
|
- No change required for accessing ``/* verilator public */`` items. These
|
||||||
are directly accessible via sub-module pointers in the model class.
|
are directly accessible via sub-module pointers in the model class.
|
||||||
|
|
||||||
* Accessing any other internal members, including
|
- Accessing any other internal members, including ``/* verilator
|
||||||
``/* verilator public_flat */`` items requires the following changes:
|
public_flat */`` items requires the following changes:
|
||||||
|
|
||||||
* Additionally include :file:`{prefix}___024root.h`. This header defines
|
- Additionally include :file:`{prefix}___024root.h`. This header defines
|
||||||
type of the ``rootp`` pointer within the model class. Note the
|
type of the ``rootp`` pointer within the model class. Note the
|
||||||
``__024`` substring is the Verilator escape sequence for the ``$``
|
``__024`` substring is the Verilator escape sequence for the ``$``
|
||||||
character, i.e.: ``rootp`` points to the Verilated SystemVerilog
|
character, i.e.: ``rootp`` points to the Verilated SystemVerilog
|
||||||
``$root`` scope.
|
``$root`` scope.
|
||||||
|
|
||||||
* Replace ``modelp->internal->member`` references with
|
- Replace ``modelp->internal->member`` references with
|
||||||
``modelp->rootp->internal->member`` references, which contain one
|
``modelp->rootp->internal->member`` references, which contain one
|
||||||
additional indirection via the ``rootp`` pointer.
|
additional indirection via the ``rootp`` pointer.
|
||||||
|
|
||||||
|
|
@ -178,7 +179,7 @@ DPI Example
|
||||||
In the SYSTEMC example above, if you wanted to import C++ functions into
|
In the SYSTEMC example above, if you wanted to import C++ functions into
|
||||||
Verilog, put in our.v:
|
Verilog, put in our.v:
|
||||||
|
|
||||||
.. code-block::
|
.. code-block:: sv
|
||||||
|
|
||||||
import "DPI-C" function int add (input int a, input int b);
|
import "DPI-C" function int add (input int a, input int b);
|
||||||
|
|
||||||
|
|
@ -212,7 +213,7 @@ function name for the import, but note it must be escaped.
|
||||||
|
|
||||||
.. code-block:: sv
|
.. code-block:: sv
|
||||||
|
|
||||||
export "DPI-C" function integer \$myRand;
|
import "DPI-C" function integer \$myRand;
|
||||||
|
|
||||||
initial $display("myRand=%d", $myRand());
|
initial $display("myRand=%d", $myRand());
|
||||||
|
|
||||||
|
|
@ -502,9 +503,9 @@ described above is just a wrapper which calls these two functions.
|
||||||
3. If using delays and :vlopt:`--timing`, there are two additional methods
|
3. If using delays and :vlopt:`--timing`, there are two additional methods
|
||||||
the user should call:
|
the user should call:
|
||||||
|
|
||||||
* ``designp->eventsPending()``, which returns ``true`` if there are any
|
- ``designp->eventsPending()``, which returns ``true`` if there are any
|
||||||
delayed events pending,
|
delayed events pending,
|
||||||
* ``designp->nextTimeSlot()``, which returns the simulation time of the
|
- ``designp->nextTimeSlot()``, which returns the simulation time of the
|
||||||
next delayed event. This method can only be called if
|
next delayed event. This method can only be called if
|
||||||
``designp->eventsPending()`` returned ``true``.
|
``designp->eventsPending()`` returned ``true``.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
*******************************
|
*******************************
|
||||||
Contributing and Reporting Bugs
|
Contributing and Reporting Bugs
|
||||||
|
|
@ -86,7 +87,8 @@ Please refer to `sv-bugpoint README
|
||||||
<https://github.com/antmicro/sv-bugpoint/blob/main/README.md>`_ for more
|
<https://github.com/antmicro/sv-bugpoint/blob/main/README.md>`_ for more
|
||||||
information on how to use `sv-bugpoint`.
|
information on how to use `sv-bugpoint`.
|
||||||
|
|
||||||
.. Contributing
|
..
|
||||||
.. ============
|
Contributing
|
||||||
|
============
|
||||||
|
|
||||||
.. include:: ../CONTRIBUTING.rst
|
.. include:: ../CONTRIBUTING.rst
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
************************
|
************************
|
||||||
Contributors and Origins
|
Contributors and Origins
|
||||||
|
|
@ -29,10 +30,10 @@ Alliance <https://chipsalliance.org>`_, and `Antmicro Ltd
|
||||||
Previous major corporate sponsors of Verilator, by providing significant
|
Previous major corporate sponsors of Verilator, by providing significant
|
||||||
contributions of time or funds include: Antmicro Ltd., Atmel Corporation,
|
contributions of time or funds include: Antmicro Ltd., Atmel Corporation,
|
||||||
Compaq Corporation, Digital Equipment Corporation, Embecosm Ltd., Fractile
|
Compaq Corporation, Digital Equipment Corporation, Embecosm Ltd., Fractile
|
||||||
Ltd., Hicamp Systems, Intel Corporation, Marvell Inc., Mindspeed Technologies
|
Ltd., Hicamp Systems, Intel Corporation, Marvell Inc., Mindspeed
|
||||||
Inc., MicroTune Inc., picoChip Designs Ltd., Sun Microsystems Inc., Nauticus
|
Technologies Inc., MicroTune Inc., picoChip Designs Ltd., Sun Microsystems
|
||||||
Networks Inc., SiCortex Inc, Shunyao CAD, Tenstorrent USA, Inc. and Western
|
Inc., Nauticus Networks Inc., SiCortex Inc, Shunyao CAD, Tenstorrent USA,
|
||||||
Digital Inc.
|
Inc. and Western Digital Inc.
|
||||||
|
|
||||||
The contributors of major functionality are: Jeremy Bennett, Krzysztof
|
The contributors of major functionality are: Jeremy Bennett, Krzysztof
|
||||||
Bieganski, Byron Bradley, Lane Brooks, John Coiner, Duane Galbi, Arkadiusz
|
Bieganski, Byron Bradley, Lane Brooks, John Coiner, Duane Galbi, Arkadiusz
|
||||||
|
|
@ -142,18 +143,18 @@ Krzysztof Obłonczek, Danny Oler, Andreas Olofsson, Baltazar Ortiz,
|
||||||
Aleksander Osman, Don Owen, Tim Paine, Deepa Palaniappan, James Pallister,
|
Aleksander Osman, Don Owen, Tim Paine, Deepa Palaniappan, James Pallister,
|
||||||
Vassilis Papaefstathiou, Sanggyu Park, Brad Parker, Risto Pejašinović, Seth
|
Vassilis Papaefstathiou, Sanggyu Park, Brad Parker, Risto Pejašinović, Seth
|
||||||
Pellegrino, Joel Peltonen, Morten Borup Petersen, Dan Petrisko, Thanh Tung
|
Pellegrino, Joel Peltonen, Morten Borup Petersen, Dan Petrisko, Thanh Tung
|
||||||
Pham, Wesley Piard, Maciej Piechotka, David Pierce, Cody Piersall,
|
Pham, Wesley Piard, Maciej Piechotka, David Pierce, Cody Piersall, T.
|
||||||
T. Platz, Michael Platzer, Dominic Plunkett, Nolan Poe, Tuomas Poikela,
|
Platz, Michael Platzer, Dominic Plunkett, Nolan Poe, Tuomas Poikela, George
|
||||||
George Polack, David Poole, Michael Popoloski, Roman Popov, Aylon Chaim
|
Polack, David Poole, Michael Popoloski, Roman Popov, Aylon Chaim Porat,
|
||||||
Porat, Oron Port, Rich Porter, Rick Porter, Stefan Post, Niranjan Prabhu,
|
Oron Port, Rich Porter, Rick Porter, Stefan Post, Niranjan Prabhu, Damien
|
||||||
Damien Pretet, Harald Pretl, Bill Pringlemeir, Usha Priyadharshini, Mark
|
Pretet, Harald Pretl, Bill Pringlemeir, Usha Priyadharshini, Mark Jackson
|
||||||
Jackson Pulver, Prateek Puri, Nikolay Puzanov, Han Qi, Jiacheng Qian,
|
Pulver, Prateek Puri, Nikolay Puzanov, Han Qi, Jiacheng Qian, Marshal Qiao,
|
||||||
Marshal Qiao, Raynard Qiao, Yujia Qiao, Jasen Qin, Frank Qiu, Nandu Raj,
|
Raynard Qiao, Yujia Qiao, Jasen Qin, Frank Qiu, Nandu Raj, Kamil Rakoczy,
|
||||||
Kamil Rakoczy, Danilo Ramos, Drew Ranck, Chris Randall, Anton Rapp, Josh
|
Danilo Ramos, Drew Ranck, Chris Randall, Anton Rapp, Josh Redford, Odd
|
||||||
Redford, Odd Magne Reitan, Frédéric Requin, Wajahat Riaz, Dustin Richmond,
|
Magne Reitan, Frédéric Requin, Wajahat Riaz, Dustin Richmond, Samuel
|
||||||
Samuel Riedel, Alberto Del Rio, Eric Rippey, Narcis Rodas, Oleg Rodionov,
|
Riedel, Alberto Del Rio, Eric Rippey, Narcis Rodas, Oleg Rodionov, Ludwig
|
||||||
Ludwig Rogiers, Paul Rolfe, Michail Rontionov, Arjen Roodselaar, Arthur
|
Rogiers, Paul Rolfe, Michail Rontionov, Arjen Roodselaar, Arthur Rosa,
|
||||||
Rosa, Tobias Rosenkranz, Yernagula Roshit, Diego Roux, Ryszard Rozak, Dan
|
Tobias Rosenkranz, Yernagula Roshit, Diego Roux, Ryszard Rozak, Dan
|
||||||
Ruelas-Petrisko, Luca Rufer, Huang Rui, Graham Rushton, Jan Egil Ruud,
|
Ruelas-Petrisko, Luca Rufer, Huang Rui, Graham Rushton, Jan Egil Ruud,
|
||||||
Denis Rystsov, Pawel Sagan, Robert Sammelson, Adrian Sampson, John
|
Denis Rystsov, Pawel Sagan, Robert Sammelson, Adrian Sampson, John
|
||||||
Sanguinetti, Josep Sans, Dave Sargeant, Luca Sasselli, Philippe Sauter,
|
Sanguinetti, Josep Sans, Dave Sargeant, Luca Sasselli, Philippe Sauter,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _verilator control files:
|
.. _verilator control files:
|
||||||
|
|
||||||
|
|
@ -187,6 +188,10 @@ The grammar of control commands is as follows:
|
||||||
|
|
||||||
.. option:: isolate_assignments -module "<modulename>" [-task "<taskname>"] -var "<signame>"
|
.. option:: isolate_assignments -module "<modulename>" [-task "<taskname>"] -var "<signame>"
|
||||||
|
|
||||||
|
Deprecated and has no effect (ignored).
|
||||||
|
|
||||||
|
In versions before 5.050:
|
||||||
|
|
||||||
Used to indicate that the assignments to this signal in any blocks
|
Used to indicate that the assignments to this signal in any blocks
|
||||||
should be isolated into new blocks. Same as
|
should be isolated into new blocks. Same as
|
||||||
:option:`/*verilator&32;isolate_assignments*/` metacomment.
|
:option:`/*verilator&32;isolate_assignments*/` metacomment.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
*********
|
*********
|
||||||
Copyright
|
Copyright
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
Deprecations
|
Deprecations
|
||||||
============
|
============
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _environment:
|
.. _environment:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _example create-binary execution:
|
.. _example create-binary execution:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _example c++ execution:
|
.. _example c++ execution:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
First you need Verilator installed, see :ref:`Installation`. In brief, if
|
First you need Verilator installed, see :ref:`Installation`. In brief, if
|
||||||
you installed Verilator using the package manager of your operating system,
|
you installed Verilator using the package manager of your operating system,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _examples in the distribution:
|
.. _examples in the distribution:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _example systemc execution:
|
.. _example systemc execution:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _examples:
|
.. _examples:
|
||||||
|
|
||||||
|
|
@ -9,10 +10,10 @@ Examples
|
||||||
|
|
||||||
This section covers the following examples:
|
This section covers the following examples:
|
||||||
|
|
||||||
* :ref:`Example Create-Binary Execution`
|
- :ref:`Example Create-Binary Execution`
|
||||||
* :ref:`Example C++ Execution`
|
- :ref:`Example C++ Execution`
|
||||||
* :ref:`Example SystemC Execution`
|
- :ref:`Example SystemC Execution`
|
||||||
* :ref:`Examples in the Distribution`
|
- :ref:`Examples in the Distribution`
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _simulation runtime arguments:
|
.. _simulation runtime arguments:
|
||||||
|
|
||||||
|
|
@ -50,8 +51,8 @@ Options:
|
||||||
|
|
||||||
.. option:: +verilator+log+file+<filename>
|
.. option:: +verilator+log+file+<filename>
|
||||||
|
|
||||||
Log all stdout and stderr to the specified output filename. If not specified
|
Log all stdout and stderr to the specified output filename. If not specified
|
||||||
the normal stdout/stderr streams are used.
|
the normal stdout/stderr streams are used.
|
||||||
|
|
||||||
.. option:: +verilator+noassert
|
.. option:: +verilator+noassert
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
===================
|
===================
|
||||||
verilator Arguments
|
verilator Arguments
|
||||||
|
|
@ -490,6 +491,10 @@ Summary:
|
||||||
Rarely needed - for developer use. Enable all dumping in the given
|
Rarely needed - for developer use. Enable all dumping in the given
|
||||||
source file at level 3.
|
source file at level 3.
|
||||||
|
|
||||||
|
.. option:: --dump-ast-patterns
|
||||||
|
|
||||||
|
Rarely needed. Enable dumping AstNodeExpr pattern statistics.
|
||||||
|
|
||||||
.. option:: --dump-defines
|
.. option:: --dump-defines
|
||||||
|
|
||||||
With :vlopt:`-E`, suppress normal output, and instead print a list of
|
With :vlopt:`-E`, suppress normal output, and instead print a list of
|
||||||
|
|
@ -657,8 +662,28 @@ Summary:
|
||||||
|
|
||||||
.. option:: -fno-assemble
|
.. option:: -fno-assemble
|
||||||
|
|
||||||
|
.. option:: -fno-bit-scan-loops
|
||||||
|
|
||||||
|
Rarely needed. Disable converting bit counting loops into built-in operations.
|
||||||
|
|
||||||
.. option:: -fno-case
|
.. option:: -fno-case
|
||||||
|
|
||||||
|
Rarely needed. Disable all case statement optimizations.
|
||||||
|
|
||||||
|
Alias for all other `-fno-case-*` options.
|
||||||
|
|
||||||
|
.. option:: -fno-case-decoder
|
||||||
|
|
||||||
|
Rarely needed. Disable converting case statements into decoder tables.
|
||||||
|
|
||||||
|
.. option:: -fno-case-table
|
||||||
|
|
||||||
|
Rarely needed. Disable converting case statements into table lookups.
|
||||||
|
|
||||||
|
.. option:: -fno-case-tree
|
||||||
|
|
||||||
|
Rarely needed. Disable converting case statements into bit-wise branch trees.
|
||||||
|
|
||||||
.. option:: -fno-combine
|
.. option:: -fno-combine
|
||||||
|
|
||||||
.. option:: -fno-const
|
.. option:: -fno-const
|
||||||
|
|
@ -737,12 +762,41 @@ Summary:
|
||||||
this is not recommended as may cause additional warnings and ordering
|
this is not recommended as may cause additional warnings and ordering
|
||||||
issues.
|
issues.
|
||||||
|
|
||||||
|
.. option:: -fno-ico-change-detect
|
||||||
|
|
||||||
|
Rarely needed. Disable input change detection in the input combinational
|
||||||
|
('ico') region. With change detection enabled (the default, unless
|
||||||
|
:vlopt:`--vpi` is passed), the input combinational logic is evaluated only
|
||||||
|
when a top level input has actually changed, rather than unconditionally on
|
||||||
|
the first scheduling iteration.
|
||||||
|
|
||||||
|
The change detection logic assumes a top level input only ever changes
|
||||||
|
externally between evaluations. The optimization is automatically disabled
|
||||||
|
for top level input signals that are written within the design. Accesses via
|
||||||
|
the VPI cannot be analyzed at compile time, therefore :vlopt:`--vpi`
|
||||||
|
disables this optimization for all inputs; it may be turned back on by
|
||||||
|
explicitly passing :vlopt:`-fico-change-detect <-fno-ico-change-detect>`.
|
||||||
|
|
||||||
.. option:: -fno-inline
|
.. option:: -fno-inline
|
||||||
|
|
||||||
|
Rarely needed. Disable module inlining.
|
||||||
|
|
||||||
|
.. option:: -fno-inline-cfuncs
|
||||||
|
|
||||||
|
Rarely needed. Disable inlining of small generated C++ functions into their
|
||||||
|
callers.
|
||||||
|
|
||||||
|
This optimization is automatically disabled when :vlopt:`--prof-cfuncs` is
|
||||||
|
used.
|
||||||
|
|
||||||
.. option:: -fno-inline-funcs
|
.. option:: -fno-inline-funcs
|
||||||
|
|
||||||
|
Rarely needed. Disable inlining of SystemVerilog functions and tasks.
|
||||||
|
|
||||||
.. option:: -fno-inline-funcs-eager
|
.. option:: -fno-inline-funcs-eager
|
||||||
|
|
||||||
|
Rarely needed. Disable eager inlining of SystemVerilog functions and tasks.
|
||||||
|
|
||||||
.. option:: -fno-life
|
.. option:: -fno-life
|
||||||
|
|
||||||
.. option:: -fno-life-post
|
.. option:: -fno-life-post
|
||||||
|
|
@ -944,27 +998,21 @@ Summary:
|
||||||
|
|
||||||
.. option:: --inline-cfuncs <value>
|
.. option:: --inline-cfuncs <value>
|
||||||
|
|
||||||
Inline small C++ function (internal AstCFunc) calls directly into their
|
Tune the inlining of small generated C++ function. Functions no bigger than
|
||||||
callers when the function has at most <value> nodes. This reduces
|
<value> nodes will be inlined if possible. The default is 20.
|
||||||
function call overhead when :vlopt:`--output-split-cfuncs` places
|
|
||||||
functions in separate compilation units that the C++ compiler cannot
|
|
||||||
inline.
|
|
||||||
|
|
||||||
Set to 0 to disable this optimization. The default is 20.
|
See also :vlopt:`--inline-cfuncs-product` and :vlopt:`-fno-inline-cfuncs`.
|
||||||
|
|
||||||
This optimization is automatically disabled when :vlopt:`--prof-cfuncs`
|
|
||||||
or :vlopt:`--trace` is used.
|
|
||||||
|
|
||||||
.. option:: --inline-cfuncs-product <value>
|
.. option:: --inline-cfuncs-product <value>
|
||||||
|
|
||||||
Tune the inlining of C++ function (internal AstCFunc) calls for larger
|
Tune the inlining of small generated C++ function. If a function's node
|
||||||
functions. When a function is too large to always inline (exceeds
|
count multiplied by the number of calls is not bigger than <value>, the
|
||||||
:vlopt:`--inline-cfuncs` threshold), it may still be inlined if the
|
function will be inlined if possible.
|
||||||
function size multiplied by the number of call sites is at most <value>.
|
|
||||||
|
|
||||||
This allows functions that are called only once or twice to be inlined
|
This allows functions that are called only once or twice to be inlined even
|
||||||
even if they exceed the small function threshold. Set to 0 to only inline
|
if they exceed the small function threshold. The default is 200.
|
||||||
functions below the :vlopt:`--inline-cfuncs` threshold. The default is 200.
|
|
||||||
|
See also :vlopt:`--inline-cfuncs` and :vlopt:`-fno-inline-cfuncs`.
|
||||||
|
|
||||||
.. option:: --inline-mult <value>
|
.. option:: --inline-mult <value>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
verilator_coverage
|
verilator_coverage
|
||||||
==================
|
==================
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
verilator_gantt
|
verilator_gantt
|
||||||
===============
|
===============
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
verilator_profcfunc
|
verilator_profcfunc
|
||||||
===================
|
===================
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
*********************************
|
*********************************
|
||||||
Executable and Argument Reference
|
Executable and Argument Reference
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
===================
|
===================
|
||||||
Language Extensions
|
Language Extensions
|
||||||
|
|
@ -341,6 +342,10 @@ or "`ifdef`"'s may break other tools.
|
||||||
|
|
||||||
.. option:: /*verilator&32;isolate_assignments*/
|
.. option:: /*verilator&32;isolate_assignments*/
|
||||||
|
|
||||||
|
Deprecated and has no effect (ignored).
|
||||||
|
|
||||||
|
In versions before 5.050:
|
||||||
|
|
||||||
Used after a signal declaration to indicate the assignments to this
|
Used after a signal declaration to indicate the assignments to this
|
||||||
signal in any blocks should be isolated into new blocks. When large
|
signal in any blocks should be isolated into new blocks. When large
|
||||||
combinatorial block results in a :option:`UNOPTFLAT` warning, attaching
|
combinatorial block results in a :option:`UNOPTFLAT` warning, attaching
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
******************************
|
******************************
|
||||||
FAQ/Frequently Asked Questions
|
FAQ/Frequently Asked Questions
|
||||||
|
|
@ -82,17 +83,17 @@ the licenses for details.
|
||||||
|
|
||||||
Some examples:
|
Some examples:
|
||||||
|
|
||||||
* Any SystemVerilog or other input fed into Verilator remains your own.
|
- Any SystemVerilog or other input fed into Verilator remains your own.
|
||||||
|
|
||||||
* Any of your VPI/DPI C++ routines that Verilator calls remain your own.
|
- Any of your VPI/DPI C++ routines that Verilator calls remain your own.
|
||||||
|
|
||||||
* Any of your main() C++ code that calls into Verilator remains your own.
|
- Any of your main() C++ code that calls into Verilator remains your own.
|
||||||
|
|
||||||
* If you change Verilator itself, for example, changing or adding a file
|
- If you change Verilator itself, for example, changing or adding a file
|
||||||
under the src/ directory in the repository, you must make the source code
|
under the src/ directory in the repository, you must make the source code
|
||||||
available under the GNU Lesser Public License.
|
available under the GNU Lesser Public License.
|
||||||
|
|
||||||
* If you change a header Verilator provides, for example, under include/ in
|
- If you change a header Verilator provides, for example, under include/ in
|
||||||
the repository, you must make the source code available under the GNU
|
the repository, you must make the source code available under the GNU
|
||||||
Lesser Public License.
|
Lesser Public License.
|
||||||
|
|
||||||
|
|
@ -384,33 +385,33 @@ example of how to do this.
|
||||||
How do I get faster build times?
|
How do I get faster build times?
|
||||||
""""""""""""""""""""""""""""""""
|
""""""""""""""""""""""""""""""""
|
||||||
|
|
||||||
* When running make, pass the make variable VM_PARALLEL_BUILDS=1, so that
|
- When running make, pass the make variable VM_PARALLEL_BUILDS=1, so that
|
||||||
builds occur in parallel. Note this is now set by default if an output
|
builds occur in parallel. Note this is now set by default if an output
|
||||||
file is large enough to be split due to the :vlopt:`--output-split`
|
file is large enough to be split due to the :vlopt:`--output-split`
|
||||||
option.
|
option.
|
||||||
|
|
||||||
* Verilator emits any infrequently executed "cold" routines into separate
|
- Verilator emits any infrequently executed "cold" routines into separate
|
||||||
__Slow.cpp files. This can accelerate compilation as optimization can be
|
__Slow.cpp files. This can accelerate compilation as optimization can be
|
||||||
disabled on these routines. See the OPT_FAST and OPT_SLOW make variables
|
disabled on these routines. See the OPT_FAST and OPT_SLOW make variables
|
||||||
and :ref:`Benchmarking & Optimization`.
|
and :ref:`Benchmarking & Optimization`.
|
||||||
|
|
||||||
* Use a recent compiler. Newer compilers tend to be faster.
|
- Use a recent compiler. Newer compilers tend to be faster.
|
||||||
|
|
||||||
* Compile in parallel on many machines and use caching; see the web for the
|
- Compile in parallel on many machines and use caching; see the web for the
|
||||||
ccache, sccache, distcc, or icecream packages. ccache will skip GCC runs
|
ccache, sccache, distcc, or icecream packages. ccache will skip GCC runs
|
||||||
between identical source builds, even across different users. If ccache
|
between identical source builds, even across different users. If ccache
|
||||||
was installed when Verilator was built, it is used, or see OBJCACHE
|
was installed when Verilator was built, it is used, or see OBJCACHE
|
||||||
environment variable to override this. Also see the
|
environment variable to override this. Also see the
|
||||||
:vlopt:`--output-split` option and :ref: `Profiling ccache efficiency`.
|
:vlopt:`--output-split` option and :ref: `Profiling ccache efficiency`.
|
||||||
|
|
||||||
* To reduce the compile time of classes that use a Verilated module (e.g.,
|
- To reduce the compile time of classes that use a Verilated module (e.g.,
|
||||||
a top CPP file) you may wish to add a
|
a top CPP file) you may wish to add a
|
||||||
:option:`/*verilator&32;no_inline_module*/` metacomment to your top-level
|
:option:`/*verilator&32;no_inline_module*/` metacomment to your top-level
|
||||||
module. This will decrease the amount of code in the model's Verilated
|
module. This will decrease the amount of code in the model's Verilated
|
||||||
class, improving compile times of any instantiating top-level C++ code,
|
class, improving compile times of any instantiating top-level C++ code,
|
||||||
at a relatively small cost of execution performance.
|
at a relatively small cost of execution performance.
|
||||||
|
|
||||||
* Use :ref:`hierarchical verilation`.
|
- Use :ref:`hierarchical verilation`.
|
||||||
|
|
||||||
|
|
||||||
Why do so many files need to recompile when I add a signal?
|
Why do so many files need to recompile when I add a signal?
|
||||||
|
|
@ -498,12 +499,12 @@ equal, the best performance is when Verilator sees all of the design. So,
|
||||||
look at the hierarchy of your design, labeling instances as to if they are
|
look at the hierarchy of your design, labeling instances as to if they are
|
||||||
SystemC or Verilog. Then:
|
SystemC or Verilog. Then:
|
||||||
|
|
||||||
* A module with only SystemC instances below must be SystemC.
|
- A module with only SystemC instances below must be SystemC.
|
||||||
|
|
||||||
* A module with a mix of Verilog and SystemC instances below must be
|
- A module with a mix of Verilog and SystemC instances below must be
|
||||||
SystemC. (As Verilator cannot connect to lower-level SystemC instances.)
|
SystemC. (As Verilator cannot connect to lower-level SystemC instances.)
|
||||||
|
|
||||||
* A module with only Verilog instances below can be either, but for best
|
- A module with only Verilog instances below can be either, but for best
|
||||||
performance should be Verilog. (The exception is if you have a design
|
performance should be Verilog. (The exception is if you have a design
|
||||||
that is instantiated many times; in this case, Verilating one of the
|
that is instantiated many times; in this case, Verilating one of the
|
||||||
lower modules and instantiating that Verilated instances multiple times
|
lower modules and instantiating that Verilated instances multiple times
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
*****
|
*****
|
||||||
Files
|
Files
|
||||||
|
|
@ -42,138 +43,141 @@ For --cc/--sc, it creates:
|
||||||
|
|
||||||
.. list-table::
|
.. list-table::
|
||||||
|
|
||||||
* - *{prefix}*.json
|
- - *{prefix}*.json
|
||||||
- JSON build definition compiling (from --make json)
|
- JSON build definition compiling (from --make json)
|
||||||
* - *{prefix}*.mk
|
- - *{prefix}*.mk
|
||||||
- Make include file for compiling (from --make gmake)
|
- Make include file for compiling (from --make gmake)
|
||||||
* - *{prefix}*\ _classes.mk
|
- - *{prefix}*\ _classes.mk
|
||||||
- Make include file with class names (from --make gmake)
|
- Make include file with class names (from --make gmake)
|
||||||
* - *{prefix}*.h
|
- - *{prefix}*.h
|
||||||
- Model header
|
- Model header
|
||||||
* - *{prefix}*.cpp
|
- - *{prefix}*.cpp
|
||||||
- Model C++ file
|
- Model C++ file
|
||||||
* - *{prefix}*\ ___024root.h
|
- - *{prefix}*\ ___024root.h
|
||||||
- Top-level internal header file (from SystemVerilog $root)
|
- Top-level internal header file (from SystemVerilog $root)
|
||||||
* - *{prefix}*\ ___024root.cpp
|
- - *{prefix}*\ ___024root.cpp
|
||||||
- Top-level internal C++ file (from SystemVerilog $root)
|
- Top-level internal C++ file (from SystemVerilog $root)
|
||||||
* - *{prefix}*\ ___024root\ *{__n}*.cpp
|
- - *{prefix}*\ ___024root\ *{__n}*.cpp
|
||||||
- Additional top-level internal C++ files
|
- Additional top-level internal C++ files
|
||||||
* - *{prefix}*\ ___024root__Slow\ *{__n}*.cpp
|
- - *{prefix}*\ ___024root__Slow\ *{__n}*.cpp
|
||||||
- Infrequent cold routines
|
- Infrequent cold routines
|
||||||
* - *{prefix}*\ ___024root__Trace\ *{__n}*.cpp
|
- - *{prefix}*\ ___024root__Trace\ *{__n}*.cpp
|
||||||
- Wave file generation code (from --trace-\*)
|
- Wave file generation code (from --trace-\*)
|
||||||
* - *{prefix}*\ ___024root__Trace__Slow\ *{__n}*.cpp
|
- - *{prefix}*\ ___024root__Trace__Slow\ *{__n}*.cpp
|
||||||
- Wave file generation code (from --trace-\*)
|
- Wave file generation code (from --trace-\*)
|
||||||
* - *{prefix}*\ __Dpi.h
|
- - *{prefix}*\ __Dpi.h
|
||||||
- DPI import and export declarations (from --dpi)
|
- DPI import and export declarations (from --dpi)
|
||||||
* - *{prefix}*\ __Dpi.cpp
|
- - *{prefix}*\ __Dpi.cpp
|
||||||
- Global DPI export wrappers (from --dpi)
|
- Global DPI export wrappers (from --dpi)
|
||||||
* - *{prefix}*\ __Dpi_Export\ *{__n}*.cpp
|
- - *{prefix}*\ __Dpi_Export\ *{__n}*.cpp
|
||||||
- DPI export wrappers scoped to this particular model (from --dpi)
|
- DPI export wrappers scoped to this particular model (from --dpi)
|
||||||
* - *{prefix}*\ __Inlines.h
|
- - *{prefix}*\ __Inlines.h
|
||||||
- Inline support functions
|
- Inline support functions
|
||||||
* - *{prefix}*\ __Syms.h
|
- - *{prefix}*\ __Syms.h
|
||||||
- Global symbol table header
|
- Global symbol table header
|
||||||
* - *{prefix}*\ __Syms.cpp
|
- - *{prefix}*\ __Syms.cpp
|
||||||
- Global symbol table C++
|
- Global symbol table C++
|
||||||
* - *{prefix}{each_verilog_module}*.h
|
- - *{prefix}{each_verilog_module}*.h
|
||||||
- Lower level internal header files
|
- Lower level internal header files
|
||||||
* - *{prefix}{each_verilog_module}*.cpp
|
- - *{prefix}{each_verilog_module}*.cpp
|
||||||
- Lower level internal C++ files
|
- Lower level internal C++ files
|
||||||
* - *{prefix}{each_verilog_module}{__n}*.cpp
|
- - *{prefix}{each_verilog_module}{__n}*.cpp
|
||||||
- Additional lower C++ files
|
- Additional lower C++ files
|
||||||
|
|
||||||
For --hierarchical mode, it creates:
|
For --hierarchical mode, it creates:
|
||||||
|
|
||||||
.. list-table::
|
.. list-table::
|
||||||
|
|
||||||
* - V\ *{hier_block}*\ /
|
- - V\ *{hier_block}*/
|
||||||
- Directory to Verilate each hierarchical block (from --hierarchical)
|
- Directory to Verilate each hierarchical block (from --hierarchical)
|
||||||
* - *{prefix}*\ __hierVer.d
|
- - *{prefix}*\ __hierVer.d
|
||||||
- Make dependencies of the top module (from --hierarchical)
|
- Make dependencies of the top module (from --hierarchical)
|
||||||
* - *{prefix}*\ _hier.mk
|
- - *{prefix}*\ _hier.mk
|
||||||
- Make file for hierarchical blocks (from --make gmake)
|
- Make file for hierarchical blocks (from --make gmake)
|
||||||
* - *{prefix}*\ __hierMkJsonArgs.f
|
- - *{prefix}*\ __hierMkJsonArgs.f
|
||||||
- Arguments for hierarchical Verilation (from --make json)
|
- Arguments for hierarchical Verilation (from --make json)
|
||||||
* - *{prefix}*\ __hierMkArgs.f
|
- - *{prefix}*\ __hierMkArgs.f
|
||||||
- Arguments for hierarchical Verilation (from --make gmake)
|
- Arguments for hierarchical Verilation (from --make gmake)
|
||||||
* - *{prefix}*\ __hierParameters.v
|
- - *{prefix}*\ __hierParameters.v
|
||||||
- Module parameters for hierarchical blocks
|
- Module parameters for hierarchical blocks
|
||||||
* - *{prefix}*\ __hier.dir
|
- - *{prefix}*\ __hier.dir
|
||||||
- Directory to store .dot, .vpp, .tree of top module (from --hierarchical)
|
- Directory to store .dot, .vpp, .tree of top module (from
|
||||||
|
--hierarchical)
|
||||||
|
|
||||||
In specific debug and other modes, it also creates:
|
In specific debug and other modes, it also creates:
|
||||||
|
|
||||||
.. list-table::
|
.. list-table::
|
||||||
|
|
||||||
* - *{prefix}*.sarif
|
- - *{prefix}*.sarif
|
||||||
- SARIF diagnostics (from --diagnostics-sarif)
|
- SARIF diagnostics (from --diagnostics-sarif)
|
||||||
* - *{prefix}*.tree.json
|
- - *{prefix}*.tree.json
|
||||||
- JSON tree information (from --json-only)
|
- JSON tree information (from --json-only)
|
||||||
* - *{prefix}*.tree.meta.json
|
- - *{prefix}*.tree.meta.json
|
||||||
- JSON tree metadata (from --json-only)
|
- JSON tree metadata (from --json-only)
|
||||||
* - *{prefix}*\ __cdc.txt
|
- - *{prefix}*\ __cdc.txt
|
||||||
- Clock Domain Crossing checks (from --cdc)
|
- Clock Domain Crossing checks (from --cdc)
|
||||||
* - *{prefix}*\ __stats.txt
|
- - *{prefix}*\ __stats.txt
|
||||||
- Statistics (from --stats)
|
- Statistics (from --stats)
|
||||||
* - *{prefix}*\ __idmap.txt
|
- - *{prefix}*\ __idmap.txt
|
||||||
- Symbol demangling (from --protect-ids)
|
- Symbol demangling (from --protect-ids)
|
||||||
* - *{prefix}*\ __ver.d
|
- - *{prefix}*\ __ver.d
|
||||||
- Make dependencies (from -MMD)
|
- Make dependencies (from -MMD)
|
||||||
* - *{prefix}*\ __verFiles.dat
|
- - *{prefix}*\ __verFiles.dat
|
||||||
- Timestamps (from --skip-identical)
|
- Timestamps (from --skip-identical)
|
||||||
* - *{prefix}{misc}*.dot
|
- - *{prefix}{misc}*.dot
|
||||||
- Debugging graph files (from --debug)
|
- Debugging graph files (from --debug)
|
||||||
* - *{prefix}{misc}*.tree
|
- - *{prefix}{misc}*.tree
|
||||||
- Debugging files (from --debug)
|
- Debugging files (from --debug)
|
||||||
* - *{prefix}*\ __inputs.vpp
|
- - *{prefix}*\ __inputs.vpp
|
||||||
- Pre-processed verilog for all files (from --debug)
|
- Pre-processed verilog for all files (from --debug)
|
||||||
* - *{prefix}*\ _ *{each_verilog_base_filename}*.vpp
|
- - *{prefix}*\ _ *{each_verilog_base_filename}*.vpp
|
||||||
- Pre-processed verilog for each file (from --debug)
|
- Pre-processed verilog for each file (from --debug)
|
||||||
|
|
||||||
After running Make, the C++ compiler may produce the following:
|
After running Make, the C++ compiler may produce the following:
|
||||||
|
|
||||||
.. list-table::
|
.. list-table::
|
||||||
|
|
||||||
* - verilated{misc}*.d
|
- - verilated{misc}*.d
|
||||||
- Intermediate dependencies
|
- Intermediate dependencies
|
||||||
* - verilated{misc}*.o
|
- - verilated{misc}*.o
|
||||||
- Intermediate objects
|
- Intermediate objects
|
||||||
* - {mod_prefix}{misc}*.d
|
- - {mod_prefix}{misc}*.d
|
||||||
- Intermediate dependencies
|
- Intermediate dependencies
|
||||||
* - {mod_prefix}{misc}*.o
|
- - {mod_prefix}{misc}*.o
|
||||||
- Intermediate objects
|
- Intermediate objects
|
||||||
* - *{prefix}*\
|
- - *{prefix}*\
|
||||||
- Final executable (from --exe)
|
- Final executable (from --exe)
|
||||||
* - lib\ *{prefix}*.a
|
- - lib\ *{prefix}*.a
|
||||||
- Final archive (default lib mode)
|
- Final archive (default lib mode)
|
||||||
* - libverilated.a
|
- - libverilated.a
|
||||||
- Runtime for verilated model (default lib mode)
|
- Runtime for verilated model (default lib mode)
|
||||||
* - *{prefix}*\ __ALL.a
|
- - *{prefix}*\ __ALL.a
|
||||||
- Library of all Verilated objects
|
- Library of all Verilated objects
|
||||||
* - *{prefix}*\ __ALL.cpp
|
- - *{prefix}*\ __ALL.cpp
|
||||||
- Include of all code for single compile
|
- Include of all code for single compile
|
||||||
* - *{prefix}{misc}*.d
|
- - *{prefix}{misc}*.d
|
||||||
- Intermediate dependencies
|
- Intermediate dependencies
|
||||||
* - *{prefix}{misc}*.o
|
- - *{prefix}{misc}*.o
|
||||||
- Intermediate objects
|
- Intermediate objects
|
||||||
|
|
||||||
The Verilated executable may produce the following:
|
The Verilated executable may produce the following:
|
||||||
|
|
||||||
.. list-table::
|
.. list-table::
|
||||||
|
|
||||||
* - coverage.dat
|
- - coverage.dat
|
||||||
- Code coverage output, and default input filename for :command:`verilator_coverage`
|
- Code coverage output, and default input filename for
|
||||||
* - gmon.out
|
:command:`verilator_coverage`
|
||||||
- GCC/clang code profiler output, often fed into :command:`verilator_profcfunc`
|
- - gmon.out
|
||||||
* - profile.vlt
|
- GCC/clang code profiler output, often fed into
|
||||||
|
:command:`verilator_profcfunc`
|
||||||
|
- - profile.vlt
|
||||||
- --prof-pgo data file for :ref:`Thread PGO`
|
- --prof-pgo data file for :ref:`Thread PGO`
|
||||||
* - profile_exec.dat
|
- - profile_exec.dat
|
||||||
- --prof-exec data file for :command:`verilator_gantt`
|
- --prof-exec data file for :command:`verilator_gantt`
|
||||||
|
|
||||||
Verilator_gantt may produce the following:
|
Verilator_gantt may produce the following:
|
||||||
|
|
||||||
.. list-table::
|
.. list-table::
|
||||||
|
|
||||||
* - profile_exec.vcd
|
- - profile_exec.vcd
|
||||||
- Gantt report waveform output
|
- Gantt report waveform output
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
######################
|
######################
|
||||||
Verilator User's Guide
|
Verilator User's Guide
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _cmakeinstallation:
|
.. _cmakeinstallation:
|
||||||
|
|
||||||
|
|
@ -16,16 +17,16 @@ Linux).
|
||||||
Quick Install
|
Quick Install
|
||||||
=============
|
=============
|
||||||
|
|
||||||
1. Install Python for your platform from https://www.python.org/downloads/.
|
#. Install Python for your platform from https://www.python.org/downloads/.
|
||||||
2. Install CMake for your platform from https://cmake.org/download/ or
|
#. Install CMake for your platform from https://cmake.org/download/ or
|
||||||
build it from source.
|
build it from source.
|
||||||
3. If the compiler of your choice is MSVC, then install
|
#. If the compiler of your choice is MSVC, then install
|
||||||
https://visualstudio.microsoft.com/downloads/. If the compiler of your
|
https://visualstudio.microsoft.com/downloads/. If the compiler of your
|
||||||
choice is Clang, then install https://releases.llvm.org/download.html or
|
choice is Clang, then install https://releases.llvm.org/download.html or
|
||||||
build it from source.
|
build it from source.
|
||||||
4. For flex and bison use https://github.com/lexxmark/winflexbison to build
|
#. For flex and bison use https://github.com/lexxmark/winflexbison to build
|
||||||
and install.
|
and install.
|
||||||
5. For build on Windows using MSVC set environment variable WIN_FLEX_BISON
|
#. For build on Windows using MSVC set environment variable WIN_FLEX_BISON
|
||||||
to install directory. For build on Windows/Linux/OS-X using ninja set
|
to install directory. For build on Windows/Linux/OS-X using ninja set
|
||||||
the environment variable FLEX_INCLUDE to the directory containing
|
the environment variable FLEX_INCLUDE to the directory containing
|
||||||
FlexLexer.h and ensure that flex/bison is available within the PATH.
|
FlexLexer.h and ensure that flex/bison is available within the PATH.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _installation:
|
.. _installation:
|
||||||
|
|
||||||
|
|
@ -384,12 +385,10 @@ the files:
|
||||||
|
|
||||||
make install
|
make install
|
||||||
|
|
||||||
|
|
||||||
.. Docker Build Environment
|
.. Docker Build Environment
|
||||||
|
|
||||||
.. include:: ../../ci/docker/buildenv/README.rst
|
.. include:: ../../ci/docker/buildenv/README.rst
|
||||||
|
|
||||||
|
|
||||||
.. Docker Run Environment
|
.. Docker Run Environment
|
||||||
|
|
||||||
.. include:: ../../ci/docker/run/README.rst
|
.. include:: ../../ci/docker/run/README.rst
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
***************
|
***************
|
||||||
Input Languages
|
Input Languages
|
||||||
|
|
@ -102,11 +103,11 @@ Time
|
||||||
|
|
||||||
With :vlopt:`--timing`, all timing controls are supported:
|
With :vlopt:`--timing`, all timing controls are supported:
|
||||||
|
|
||||||
* delay statements,
|
- delay statements,
|
||||||
* event control statements not only at the top of a process,
|
- event control statements not only at the top of a process,
|
||||||
* intra-assignment timing controls,
|
- intra-assignment timing controls,
|
||||||
* net delays,
|
- net delays,
|
||||||
* ``wait`` statements,
|
- ``wait`` statements,
|
||||||
|
|
||||||
as well as all flavors of ``fork``.
|
as well as all flavors of ``fork``.
|
||||||
|
|
||||||
|
|
@ -128,26 +129,26 @@ simulation (perhaps using :vlopt:`--build`) and run it.
|
||||||
With :vlopt:`--no-timing`, all timing controls cause the :option:`NOTIMING`
|
With :vlopt:`--no-timing`, all timing controls cause the :option:`NOTIMING`
|
||||||
error, except:
|
error, except:
|
||||||
|
|
||||||
* delay statements - they are ignored (as they are in synthesis), though they
|
- delay statements - they are ignored (as they are in synthesis), though
|
||||||
do issue a :option:`STMTDLY` warning,
|
they do issue a :option:`STMTDLY` warning,
|
||||||
* intra-assignment timing controls - they are ignored, though they do issue
|
- intra-assignment timing controls - they are ignored, though they do issue
|
||||||
an :option:`ASSIGNDLY` warning,
|
an :option:`ASSIGNDLY` warning,
|
||||||
* net delays - they are ignored,
|
- net delays - they are ignored,
|
||||||
* event controls at the top of the procedure,
|
- event controls at the top of the procedure,
|
||||||
|
|
||||||
Forks cause this error as well, except:
|
Forks cause this error as well, except:
|
||||||
|
|
||||||
* forks with no statements,
|
- forks with no statements,
|
||||||
* ``fork..join`` or ``fork..join_any`` with one statement,
|
- ``fork..join`` or ``fork..join_any`` with one statement,
|
||||||
* forks with :vlopt:`--bbox-unsup`.
|
- forks with :vlopt:`--bbox-unsup`.
|
||||||
|
|
||||||
If neither :vlopt:`--timing` nor :vlopt:`--no-timing` is specified, all
|
If neither :vlopt:`--timing` nor :vlopt:`--no-timing` is specified, all
|
||||||
timing controls cause the :option:`NEEDTIMINGOPT` error, except event
|
timing controls cause the :option:`NEEDTIMINGOPT` error, except event
|
||||||
controls at the top of the process. Forks cause this error as well, except:
|
controls at the top of the process. Forks cause this error as well, except:
|
||||||
|
|
||||||
* forks with no statements,
|
- forks with no statements,
|
||||||
* ``fork..join`` or ``fork..join_any`` with one statement,
|
- ``fork..join`` or ``fork..join_any`` with one statement,
|
||||||
* forks with :vlopt:`--bbox-unsup`.
|
- forks with :vlopt:`--bbox-unsup`.
|
||||||
|
|
||||||
Timing controls and forks can also be ignored in specific files or parts of
|
Timing controls and forks can also be ignored in specific files or parts of
|
||||||
files. The :option:`/*verilator&32;timing_off*/` and
|
files. The :option:`/*verilator&32;timing_off*/` and
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
********
|
********
|
||||||
Overview
|
Overview
|
||||||
|
|
@ -47,9 +48,9 @@ The best place to get started is to try the :ref:`Examples`.
|
||||||
uses the shorthand, e.g., "IEEE 1364-2005", to refer to the, e.g., 2005
|
uses the shorthand, e.g., "IEEE 1364-2005", to refer to the, e.g., 2005
|
||||||
version of this standard.
|
version of this standard.
|
||||||
|
|
||||||
.. [#] SystemVerilog is defined by the `Institute of Electrical and
|
.. [#] SystemVerilog is defined by the `Institute of Electrical and Electronics
|
||||||
Electronics Engineers (IEEE) Standard for SystemVerilog - Unified
|
Engineers (IEEE) Standard for SystemVerilog - Unified Hardware Design,
|
||||||
Hardware Design, Specification, and Verification Language`, Standard
|
Specification, and Verification Language`, Standard 1800, released in
|
||||||
1800, released in 2005, 2009, 2012, 2017, and 2023. The Verilator
|
2005, 2009, 2012, 2017, and 2023. The Verilator documentation uses the
|
||||||
documentation uses the shorthand e.g., "IEEE 1800-2023", to refer to
|
shorthand e.g., "IEEE 1800-2023", to refer to the, e.g., 2023 version of
|
||||||
the, e.g., 2023 version of this standard.
|
this standard.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
.. _simulating:
|
.. _simulating:
|
||||||
|
|
||||||
|
|
@ -209,7 +210,8 @@ For simple coverage points, use the ``cover property`` construct:
|
||||||
|
|
||||||
DefaultClock: cover property (@(posedge clk) cyc==3);
|
DefaultClock: cover property (@(posedge clk) cyc==3);
|
||||||
|
|
||||||
This adds a coverage point that tracks whether the condition has been observed.
|
This adds a coverage point that tracks whether the condition has been
|
||||||
|
observed.
|
||||||
|
|
||||||
.. _covergroup coverage:
|
.. _covergroup coverage:
|
||||||
|
|
||||||
|
|
@ -218,8 +220,8 @@ Covergroup Coverage
|
||||||
|
|
||||||
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
|
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
|
||||||
translate covergroup coverage points the user has inserted manually in
|
translate covergroup coverage points the user has inserted manually in
|
||||||
SystemVerilog code into the Verilated model. Verilator supports
|
SystemVerilog code into the Verilated model. Verilator supports coverpoints
|
||||||
coverpoints with value and transition bins, and cross points.
|
with value and transition bins, and cross points.
|
||||||
|
|
||||||
|
|
||||||
.. _fsm coverage:
|
.. _fsm coverage:
|
||||||
|
|
@ -244,15 +246,15 @@ encodings in these common forms:
|
||||||
- Single-process FSMs, whose state dispatch is written as ``case (state)``
|
- Single-process FSMs, whose state dispatch is written as ``case (state)``
|
||||||
or as a top-level ``if`` / ``else if`` chain comparing the same state
|
or as a top-level ``if`` / ``else if`` chain comparing the same state
|
||||||
variable against known state values
|
variable against known state values
|
||||||
- Two-process and three-block FSMs, where a clocked state register is paired
|
- Two-process and three-block FSMs, where a clocked state register is
|
||||||
with a combinational next-state block using the same supported
|
paired with a combinational next-state block using the same supported
|
||||||
``case`` or top-level ``if`` / ``else if`` dispatch forms
|
``case`` or top-level ``if`` / ``else if`` dispatch forms
|
||||||
|
|
||||||
Scalar state encodings may be wider than 32 bits. This allows sparse
|
Scalar state encodings may be wider than 32 bits. This allows sparse state
|
||||||
state encodings, such as high-Hamming-distance enum or localparam values,
|
encodings, such as high-Hamming-distance enum or localparam values, to be
|
||||||
to be preserved in the detected FSM model. Verilator uses the declared
|
preserved in the detected FSM model. Verilator uses the declared enum item
|
||||||
enum item name, parameter name, or localparam name as the reported state
|
name, parameter name, or localparam name as the reported state label where
|
||||||
label where possible.
|
possible.
|
||||||
|
|
||||||
Simple input guards are supported when they appear inside a recognized
|
Simple input guards are supported when they appear inside a recognized
|
||||||
state branch, or as a top-level conjunction containing exactly one state
|
state branch, or as a top-level conjunction containing exactly one state
|
||||||
|
|
@ -267,18 +269,17 @@ deeply nested control recovery, or cross-module state alias tracing.
|
||||||
The following metacomments may be attached to the state variable to steer
|
The following metacomments may be attached to the state variable to steer
|
||||||
the extracted coverage model:
|
the extracted coverage model:
|
||||||
|
|
||||||
- ``/*verilator fsm_state*/`` forces the variable to be treated as
|
- ``/*verilator fsm_state*/`` forces the variable to be treated as FSM
|
||||||
FSM state.
|
state.
|
||||||
- ``/*verilator fsm_reset_arc*/`` marks reset transitions as
|
- ``/*verilator fsm_reset_arc*/`` marks reset transitions as user-visible
|
||||||
user-visible reset arcs instead of defaulting to a hidden reset-only
|
reset arcs instead of defaulting to a hidden reset-only summary.
|
||||||
summary.
|
- ``/*verilator fsm_arc_include_cond*/`` keeps conditional branch arcs that
|
||||||
- ``/*verilator fsm_arc_include_cond*/`` keeps conditional branch
|
would otherwise be skipped by the conservative extractor.
|
||||||
arcs that would otherwise be skipped by the conservative extractor.
|
|
||||||
|
|
||||||
State registers may also be wrapped by a transparent instance, for
|
State registers may also be wrapped by a transparent instance, for example
|
||||||
example a project flop wrapper or primitive. Such wrappers must be
|
a project flop wrapper or primitive. Such wrappers must be described
|
||||||
described explicitly with a VLT command file action before Verilator will
|
explicitly with a VLT command file action before Verilator will use their
|
||||||
use their data, state, clock, or reset connections for FSM extraction:
|
data, state, clock, or reset connections for FSM extraction:
|
||||||
|
|
||||||
.. code-block:: sv
|
.. code-block:: sv
|
||||||
|
|
||||||
|
|
@ -298,11 +299,10 @@ Optional reset metadata may also be supplied:
|
||||||
fsm_register_wrapper -module "my_fsm_flop" -d "state_i" -q "state_o" -clock "clk_i" \
|
fsm_register_wrapper -module "my_fsm_flop" -d "state_i" -q "state_o" -clock "clk_i" \
|
||||||
-reset "rst_ni" -reset_value "ResetValue"
|
-reset "rst_ni" -reset_value "ResetValue"
|
||||||
|
|
||||||
Reset arcs are emitted only when the configured reset port has an
|
Reset arcs are emitted only when the configured reset port has an inferable
|
||||||
inferable edge in the wrapper and the configured reset value parameter is
|
edge in the wrapper and the configured reset value parameter is statically
|
||||||
statically resolvable. If reset metadata is incomplete, Verilator warns
|
resolvable. If reset metadata is incomplete, Verilator warns and may still
|
||||||
and may still emit FSM state and transition coverage, but reset arcs are
|
emit FSM state and transition coverage, but reset arcs are omitted.
|
||||||
omitted.
|
|
||||||
|
|
||||||
Reset transitions are included in the collected data either way. By
|
Reset transitions are included in the collected data either way. By
|
||||||
default, :command:`verilator_coverage` summarizes reset-only arcs rather
|
default, :command:`verilator_coverage` summarizes reset-only arcs rather
|
||||||
|
|
@ -433,11 +433,11 @@ coverage point insertions into the model and collect the coverage data.
|
||||||
|
|
||||||
To get the coverage data from the model, write the coverage with either:
|
To get the coverage data from the model, write the coverage with either:
|
||||||
|
|
||||||
1. Using :vlopt:`--binary` or :vlopt:`--main`, and Verilator will dump
|
#. Using :vlopt:`--binary` or :vlopt:`--main`, and Verilator will dump
|
||||||
coverage when the test completes to the filename specified with
|
coverage when the test completes to the filename specified with
|
||||||
:vlopt:`+verilator+coverage+file+\<filename\>`.
|
:vlopt:`+verilator+coverage+file+\<filename\>`.
|
||||||
|
|
||||||
2. In the user wrapper code, typically at the end once a test passes, call
|
#. In the user wrapper code, typically at the end once a test passes, call
|
||||||
``Verilated::threadContextp()->coveragep()->write`` with an argument of
|
``Verilated::threadContextp()->coveragep()->write`` with an argument of
|
||||||
the filename for the coverage data file to write coverage data to
|
the filename for the coverage data file to write coverage data to
|
||||||
(typically "logs/coverage.dat").
|
(typically "logs/coverage.dat").
|
||||||
|
|
@ -502,12 +502,12 @@ how execution time is distributed in a verilated model.
|
||||||
|
|
||||||
With the :vlopt:`--prof-exec` option, Verilator will:
|
With the :vlopt:`--prof-exec` option, Verilator will:
|
||||||
|
|
||||||
* Add code to the Verilated model to record execution flow.
|
- Add code to the Verilated model to record execution flow.
|
||||||
|
|
||||||
* Add code to save profiling data in non-human-friendly form to the file
|
- Add code to save profiling data in non-human-friendly form to the file
|
||||||
specified with :vlopt:`+verilator+prof+exec+file+\<filename\>`.
|
specified with :vlopt:`+verilator+prof+exec+file+\<filename\>`.
|
||||||
|
|
||||||
* In multithreaded models, add code to record each macro-task's start and
|
- In multithreaded models, add code to record each macro-task's start and
|
||||||
end time across several calls to eval. (What is a macro-task? See the
|
end time across several calls to eval. (What is a macro-task? See the
|
||||||
Verilator internals document (:file:`docs/internals.rst` in the
|
Verilator internals document (:file:`docs/internals.rst` in the
|
||||||
distribution.)
|
distribution.)
|
||||||
|
|
@ -607,8 +607,8 @@ There are two forms of profile-guided optimizations. Unfortunately, for
|
||||||
best results, they must each be performed from the highest level code to
|
best results, they must each be performed from the highest level code to
|
||||||
the lowest, which means performing them separately and in this order:
|
the lowest, which means performing them separately and in this order:
|
||||||
|
|
||||||
* :ref:`Thread PGO`
|
- :ref:`Thread PGO`
|
||||||
* :ref:`Compiler PGO`
|
- :ref:`Compiler PGO`
|
||||||
|
|
||||||
Other forms of PGO may be supported in the future, such as clock and reset
|
Other forms of PGO may be supported in the future, such as clock and reset
|
||||||
toggle rate PGO, branch prediction PGO, statement execution time PGO, or
|
toggle rate PGO, branch prediction PGO, statement execution time PGO, or
|
||||||
|
|
@ -674,7 +674,7 @@ multithreaded models.
|
||||||
Please see the appropriate compiler documentation to use PGO with GCC or
|
Please see the appropriate compiler documentation to use PGO with GCC or
|
||||||
Clang. The process in GCC 10 was as follows:
|
Clang. The process in GCC 10 was as follows:
|
||||||
|
|
||||||
1. Compile the Verilated model with the compiler's "-fprofile-generate"
|
#. Compile the Verilated model with the compiler's "-fprofile-generate"
|
||||||
flag:
|
flag:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
@ -685,10 +685,10 @@ Clang. The process in GCC 10 was as follows:
|
||||||
Or, if calling make yourself, add -fprofile-generate appropriately to
|
Or, if calling make yourself, add -fprofile-generate appropriately to
|
||||||
your Makefile.
|
your Makefile.
|
||||||
|
|
||||||
2. Run your simulation. This will create \*.gcda file(s) in the same
|
#. Run your simulation. This will create \*.gcda file(s) in the same
|
||||||
directory as the source files.
|
directory as the source files.
|
||||||
|
|
||||||
3. Recompile the model with -fprofile-use. The compiler will read the
|
#. Recompile the model with -fprofile-use. The compiler will read the
|
||||||
\*.gcda file(s).
|
\*.gcda file(s).
|
||||||
|
|
||||||
For GCC:
|
For GCC:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
**********
|
**********
|
||||||
Verilating
|
Verilating
|
||||||
|
|
@ -7,21 +8,21 @@ Verilating
|
||||||
|
|
||||||
Verilator may be used in five major ways:
|
Verilator may be used in five major ways:
|
||||||
|
|
||||||
* With the :vlopt:`--binary` option, Verilator will translate the design
|
- With the :vlopt:`--binary` option, Verilator will translate the design
|
||||||
into an executable, via generating C++ and compiling it. See
|
into an executable, via generating C++ and compiling it. See
|
||||||
:ref:`Binary, C++ and SystemC Generation`.
|
:ref:`Binary, C++ and SystemC Generation`.
|
||||||
|
|
||||||
* With the :vlopt:`--cc` or :vlopt:`--sc` options, Verilator will translate
|
- With the :vlopt:`--cc` or :vlopt:`--sc` options, Verilator will translate
|
||||||
the design into C++ or SystemC code, respectively. See :ref:`Binary, C++
|
the design into C++ or SystemC code, respectively. See :ref:`Binary, C++
|
||||||
and SystemC Generation`.
|
and SystemC Generation`.
|
||||||
|
|
||||||
* With the :vlopt:`--lint-only` option, Verilator will lint the design to
|
- With the :vlopt:`--lint-only` option, Verilator will lint the design to
|
||||||
check for warnings but will not typically create any output files.
|
check for warnings but will not typically create any output files.
|
||||||
|
|
||||||
* With the :vlopt:`--json-only` option, Verilator will create JSON output
|
- With the :vlopt:`--json-only` option, Verilator will create JSON output
|
||||||
that may be used to feed into other user-designed tools.
|
that may be used to feed into other user-designed tools.
|
||||||
|
|
||||||
* With the :vlopt:`-E` option, Verilator will preprocess the code according
|
- With the :vlopt:`-E` option, Verilator will preprocess the code according
|
||||||
to IEEE preprocessing rules and write the output to standard out. This is
|
to IEEE preprocessing rules and write the output to standard out. This is
|
||||||
useful to feed other tools and to debug how "\`define" statements are
|
useful to feed other tools and to debug how "\`define" statements are
|
||||||
expanded.
|
expanded.
|
||||||
|
|
@ -127,9 +128,9 @@ Usage
|
||||||
Users need to mark one or more moderate-size modules as hierarchy block(s).
|
Users need to mark one or more moderate-size modules as hierarchy block(s).
|
||||||
There are two ways to mark a module:
|
There are two ways to mark a module:
|
||||||
|
|
||||||
* Write :option:`/*verilator&32;hier_block*/` metacomment in HDL code.
|
- Write :option:`/*verilator&32;hier_block*/` metacomment in HDL code.
|
||||||
|
|
||||||
* Add a :option:`hier_block` line in the :ref:`Verilator Control Files`.
|
- Add a :option:`hier_block` line in the :ref:`Verilator Control Files`.
|
||||||
|
|
||||||
Then pass the :vlopt:`--hierarchical` option to Verilator.
|
Then pass the :vlopt:`--hierarchical` option to Verilator.
|
||||||
|
|
||||||
|
|
@ -145,28 +146,28 @@ Limitations
|
||||||
|
|
||||||
Hierarchy blocks have some limitations, including:
|
Hierarchy blocks have some limitations, including:
|
||||||
|
|
||||||
* Internals of the hierarchy block cannot be accessed using dot (.) from
|
- Internals of the hierarchy block cannot be accessed using dot (.) from
|
||||||
the upper module(s) or other hierarchy blocks, except that ports of a
|
the upper module(s) or other hierarchy blocks, except that ports of a
|
||||||
hierarchy block instance can be accessed from the directly enclosing
|
hierarchy block instance can be accessed from the directly enclosing
|
||||||
nested hierarchy block, or from the top level non-hierarchical portions
|
nested hierarchy block, or from the top level non-hierarchical portions
|
||||||
of the design if not a nested hierarchy block.
|
of the design if not a nested hierarchy block.
|
||||||
|
|
||||||
* Modport cannot be used at the hierarchical block boundary.
|
- Modport cannot be used at the hierarchical block boundary.
|
||||||
|
|
||||||
* The simulation speed is likely not as fast as flat Verilation, in which
|
- The simulation speed is likely not as fast as flat Verilation, in which
|
||||||
all modules are globally scheduled.
|
all modules are globally scheduled.
|
||||||
|
|
||||||
* Generated clocks may not work correctly if generated in the hierarchical
|
- Generated clocks may not work correctly if generated in the hierarchical
|
||||||
model and passed into another hierarchical model or the top module.
|
model and passed into another hierarchical model or the top module.
|
||||||
|
|
||||||
* Delays are not allowed in hierarchy blocks.
|
- Delays are not allowed in hierarchy blocks.
|
||||||
|
|
||||||
But, the following usage is supported:
|
But, the following usage is supported:
|
||||||
|
|
||||||
* Nested hierarchy blocks. A hierarchy block may instantiate other
|
- Nested hierarchy blocks. A hierarchy block may instantiate other
|
||||||
hierarchy blocks.
|
hierarchy blocks.
|
||||||
|
|
||||||
* Parameterized hierarchy block. Parameters of a hierarchy block can be
|
- Parameterized hierarchy block. Parameters of a hierarchy block can be
|
||||||
overridden using ``#(.param_name(value))`` construct.
|
overridden using ``#(.param_name(value))`` construct.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
..
|
||||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
===================
|
===================
|
||||||
Errors and Warnings
|
Errors and Warnings
|
||||||
|
|
@ -882,6 +883,22 @@ List Of Warnings
|
||||||
with a newline."
|
with a newline."
|
||||||
|
|
||||||
|
|
||||||
|
.. option:: FINALDLY
|
||||||
|
|
||||||
|
Error issued when a non-blocking assignment `<=` is used in a
|
||||||
|
`final` block.
|
||||||
|
|
||||||
|
This error can be disabled. If disabled, the assignment will be
|
||||||
|
executed as a `=` blocking assignment.
|
||||||
|
|
||||||
|
Faulty example:
|
||||||
|
|
||||||
|
.. include:: ../../docs/gen/ex_FINALDLY_faulty.rst
|
||||||
|
|
||||||
|
Results in:
|
||||||
|
|
||||||
|
.. include:: ../../docs/gen/ex_FINALDLY_msg.rst
|
||||||
|
|
||||||
.. option:: FSMMULTI
|
.. option:: FSMMULTI
|
||||||
|
|
||||||
Warns that the same always block contains multiple enum-typed case
|
Warns that the same always block contains multiple enum-typed case
|
||||||
|
|
@ -1178,7 +1195,7 @@ List Of Warnings
|
||||||
|
|
||||||
.. option:: INITIALDLY
|
.. option:: INITIALDLY
|
||||||
|
|
||||||
.. TODO better example
|
Historical, never issued since version 5.050.
|
||||||
|
|
||||||
Warns that the code has a delayed assignment inside of an ``initial`` or
|
Warns that the code has a delayed assignment inside of an ``initial`` or
|
||||||
``final`` block. If this message is suppressed, Verilator will convert
|
``final`` block. If this message is suppressed, Verilator will convert
|
||||||
|
|
@ -2366,11 +2383,6 @@ List Of Warnings
|
||||||
the conflict. If you run with :vlopt:`--report-unoptflat`, Verilator will
|
the conflict. If you run with :vlopt:`--report-unoptflat`, Verilator will
|
||||||
suggest possible candidates for :option:`/*verilator&32;split_var*/`.
|
suggest possible candidates for :option:`/*verilator&32;split_var*/`.
|
||||||
|
|
||||||
The UNOPTFLAT warning may also occur where outputs from a block of logic
|
|
||||||
are independent, but occur in the same always block. To fix this, use
|
|
||||||
the :option:`/*verilator&32;isolate_assignments*/` metacomment described
|
|
||||||
above.
|
|
||||||
|
|
||||||
Before version 5.000, the UNOPTFLAT warning may also have been due to
|
Before version 5.000, the UNOPTFLAT warning may also have been due to
|
||||||
clock enables, identified from the reported path going through a clock
|
clock enables, identified from the reported path going through a clock
|
||||||
gating instance. To fix these, the clock_enable meta comment was used.
|
gating instance. To fix these, the clock_enable meta comment was used.
|
||||||
|
|
|
||||||
|
|
@ -113,9 +113,9 @@ pointer to the ``AstNode`` currently being processed.
|
||||||
|
|
||||||
There are notable sub-hierarchies of the ``AstNode`` sub-types, namely:
|
There are notable sub-hierarchies of the ``AstNode`` sub-types, namely:
|
||||||
|
|
||||||
1. All AST nodes representing data types derive from ``AstNodeDType``.
|
#. All AST nodes representing data types derive from ``AstNodeDType``.
|
||||||
|
|
||||||
2. All AST nodes representing expressions (i.e.: anything that stands for,
|
#. All AST nodes representing expressions (i.e.: anything that stands for,
|
||||||
or evaluates to a value) derive from ``AstNodeExpr``.
|
or evaluates to a value) derive from ``AstNodeExpr``.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -666,23 +666,23 @@ The second visitor in ``V3Timing.cpp``, ``TimingControlVisitor``, uses the
|
||||||
information provided by ``TimingSuspendableVisitor`` and transforms each
|
information provided by ``TimingSuspendableVisitor`` and transforms each
|
||||||
timing control into a ``co_await``.
|
timing control into a ``co_await``.
|
||||||
|
|
||||||
* event controls are turned into ``co_await`` on a trigger scheduler's
|
- event controls are turned into ``co_await`` on a trigger scheduler's
|
||||||
``trigger`` method. The awaited trigger scheduler is the one
|
``trigger`` method. The awaited trigger scheduler is the one
|
||||||
corresponding to the sentree referenced by the event control. This
|
corresponding to the sentree referenced by the event control. This
|
||||||
sentree is also referenced by the ``AstCAwait`` node, to be used later by
|
sentree is also referenced by the ``AstCAwait`` node, to be used later by
|
||||||
the static scheduling code.
|
the static scheduling code.
|
||||||
|
|
||||||
* if an event control waits on a local variable or class member, it uses a
|
- if an event control waits on a local variable or class member, it uses a
|
||||||
local trigger which it evaluates inline. It awaits a dynamic trigger
|
local trigger which it evaluates inline. It awaits a dynamic trigger
|
||||||
scheduler multiple times: for trigger evaluation, updates, and
|
scheduler multiple times: for trigger evaluation, updates, and
|
||||||
resumption. The dynamic trigger scheduler is responsible for resuming the
|
resumption. The dynamic trigger scheduler is responsible for resuming the
|
||||||
coroutine at the correct point of evaluation.
|
coroutine at the correct point of evaluation.
|
||||||
|
|
||||||
* delays are turned into ``co_await`` on a delay scheduler's ``delay``
|
- delays are turned into ``co_await`` on a delay scheduler's ``delay``
|
||||||
method. The created ``AstCAwait`` nodes also reference a special sentree
|
method. The created ``AstCAwait`` nodes also reference a special sentree
|
||||||
related to delays, to be used later by the static scheduling code.
|
related to delays, to be used later by the static scheduling code.
|
||||||
|
|
||||||
* ``join`` and ``join_any`` are turned into ``co_await`` on a
|
- ``join`` and ``join_any`` are turned into ``co_await`` on a
|
||||||
``VlForkSync``'s ``join`` method. Each forked process gets a
|
``VlForkSync``'s ``join`` method. Each forked process gets a
|
||||||
``VlForkSync::done`` call at the end.
|
``VlForkSync::done`` call at the end.
|
||||||
|
|
||||||
|
|
@ -732,24 +732,24 @@ event `a` was called first - which is necessary to know.
|
||||||
|
|
||||||
There are two functions for managing timing logic called by ``_eval()``:
|
There are two functions for managing timing logic called by ``_eval()``:
|
||||||
|
|
||||||
* ``_timing_ready()``, which commits all coroutines whose triggers were
|
- ``_timing_ready()``, which commits all coroutines whose triggers were not
|
||||||
not set in the current iteration,
|
set in the current iteration,
|
||||||
* ``_timing_resume()``, which calls `resume()` on all trigger and delay
|
- ``_timing_resume()``, which calls `resume()` on all trigger and delay
|
||||||
schedulers whose triggers were set in the current iteration.
|
schedulers whose triggers were set in the current iteration.
|
||||||
|
|
||||||
Thanks to this separation a coroutine:
|
Thanks to this separation a coroutine:
|
||||||
|
|
||||||
* awaiting a trigger cannot be suspended and resumed in the same iteration
|
- awaiting a trigger cannot be suspended and resumed in the same iteration
|
||||||
(``test_regress/t/t_timing_eval_act.v``) - which is necessary to make
|
(``test_regress/t/t_timing_eval_act.v``) - which is necessary to make
|
||||||
Verilator more predictable; this is the reason for introduction of 3rd
|
Verilator more predictable; this is the reason for introduction of 3rd
|
||||||
stage in `VlTriggerScheduler` and thanks to this it is guaranteed that
|
stage in `VlTriggerScheduler` and thanks to this it is guaranteed that
|
||||||
downstream logic will be evaluated before resumption (assuming that the
|
downstream logic will be evaluated before resumption (assuming that the
|
||||||
coroutine wasn't already triggered in previous iteration);
|
coroutine wasn't already triggered in previous iteration);
|
||||||
* cannot be resumed before it is suspended -
|
- cannot be resumed before it is suspended -
|
||||||
``test_regress/t/t_event_control_double_excessive.v``;
|
``test_regress/t/t_event_control_double_excessive.v``;
|
||||||
* firing cannot cannot be lost
|
- firing cannot cannot be lost
|
||||||
(``test_regress/t/t_event_control_double_lost.v``) - which is possible when
|
(``test_regress/t/t_event_control_double_lost.v``) - which is possible
|
||||||
triggers are not evaluated right before awaiting.
|
when triggers are not evaluated right before awaiting.
|
||||||
|
|
||||||
All coroutines are committed and resumed in the 'act' eval loop. With
|
All coroutines are committed and resumed in the 'act' eval loop. With
|
||||||
timing features enabled, the ``_eval()`` function takes this form:
|
timing features enabled, the ``_eval()`` function takes this form:
|
||||||
|
|
@ -937,14 +937,15 @@ macro-task's dataset fits in one core's local caches.
|
||||||
|
|
||||||
To achieve spatial locality, we tag each variable with the set of
|
To achieve spatial locality, we tag each variable with the set of
|
||||||
macro-tasks that access it. Let's call this set the "footprint" of that
|
macro-tasks that access it. Let's call this set the "footprint" of that
|
||||||
variable. The variables in a given module have a set of footprints. We group
|
variable. The variables in a given module have a set of footprints. We
|
||||||
variables with identical non-empty footprints, emit those groups in deterministic
|
group variables with identical non-empty footprints, emit those groups in
|
||||||
footprint-key order, then emit variables with no footprint information last.
|
deterministic footprint-key order, then emit variables with no footprint
|
||||||
|
information last.
|
||||||
|
|
||||||
The first emitted variable in each footprint group is aligned to a cache-line
|
The first emitted variable in each footprint group is aligned to a
|
||||||
boundary. This avoids false sharing between different macro-task footprints
|
cache-line boundary. This avoids false sharing between different macro-task
|
||||||
without building a complete pairwise-distance graph over all footprints, which
|
footprints without building a complete pairwise-distance graph over all
|
||||||
would use excessive memory on very large models.
|
footprints, which would use excessive memory on very large models.
|
||||||
|
|
||||||
This is an old idea. Simulators designed at DEC in the early 1990s used
|
This is an old idea. Simulators designed at DEC in the early 1990s used
|
||||||
similar techniques to optimize both single-thread and multithread modes.
|
similar techniques to optimize both single-thread and multithread modes.
|
||||||
|
|
@ -1299,15 +1300,15 @@ the ``<description>`` field is ``<identifier> : <type>``, where
|
||||||
``<identifier>`` will be used as the base name of the generated operand
|
``<identifier>`` will be used as the base name of the generated operand
|
||||||
accessors, and ``<type>`` is one of:
|
accessors, and ``<type>`` is one of:
|
||||||
|
|
||||||
1. An ``AstNode`` sub-class, defining the operand to be of that type,
|
#. An ``AstNode`` sub-class, defining the operand to be of that type,
|
||||||
always no-null, and with an always null ``nextp()``. That is, the child
|
always no-null, and with an always null ``nextp()``. That is, the child
|
||||||
node is always present, and is a single ``AstNode`` (as opposed to a
|
node is always present, and is a single ``AstNode`` (as opposed to a
|
||||||
list).
|
list).
|
||||||
|
|
||||||
2. ``Optional[<AstNode sub-class>]``. This is just like in point 1 above,
|
#. ``Optional[<AstNode sub-class>]``. This is just like in point 1 above,
|
||||||
but defines the child node to be optional, meaning it may be null.
|
but defines the child node to be optional, meaning it may be null.
|
||||||
|
|
||||||
3. ``List[AstNode sub-class]`` describes a list operand, which means the
|
#. ``List[AstNode sub-class]`` describes a list operand, which means the
|
||||||
child node may have a non-null ``nextp()`` and in addition the child
|
child node may have a non-null ``nextp()`` and in addition the child
|
||||||
itself may be null, representing an empty list.
|
itself may be null, representing an empty list.
|
||||||
|
|
||||||
|
|
@ -1394,7 +1395,7 @@ calling ``accept`` on ``AstIf`` will look in turn for:
|
||||||
|
|
||||||
There are three ways data is passed between visitor functions.
|
There are three ways data is passed between visitor functions.
|
||||||
|
|
||||||
1. A visitor-class member variable. This is generally for passing "parent"
|
#. A visitor-class member variable. This is generally for passing "parent"
|
||||||
information down to children. ``m_modp`` is a common example. It's set
|
information down to children. ``m_modp`` is a common example. It's set
|
||||||
to NULL in the constructor, where that node (``AstModule`` visitor) sets
|
to NULL in the constructor, where that node (``AstModule`` visitor) sets
|
||||||
it, then the children are iterated, then it's cleared. Children under an
|
it, then the children are iterated, then it's cleared. Children under an
|
||||||
|
|
@ -1404,7 +1405,7 @@ There are three ways data is passed between visitor functions.
|
||||||
visitor; otherwise exiting the lower for will lose the upper for's
|
visitor; otherwise exiting the lower for will lose the upper for's
|
||||||
setting.
|
setting.
|
||||||
|
|
||||||
2. User attributes. Each ``AstNode`` (**Note.** The AST node, not the
|
#. User attributes. Each ``AstNode`` (**Note.** The AST node, not the
|
||||||
visitor) has five user attributes, which may be accessed as an integer
|
visitor) has five user attributes, which may be accessed as an integer
|
||||||
using the ``user1()`` through ``user4()`` methods, or as a pointer (of
|
using the ``user1()`` through ``user4()`` methods, or as a pointer (of
|
||||||
type ``AstNUser``) using the ``user1p()`` through ``user4p()`` methods
|
type ``AstNUser``) using the ``user1p()`` through ``user4p()`` methods
|
||||||
|
|
@ -1435,7 +1436,7 @@ There are three ways data is passed between visitor functions.
|
||||||
so it's ok to call fairly often. For example, it's commonly called on
|
so it's ok to call fairly often. For example, it's commonly called on
|
||||||
every module.
|
every module.
|
||||||
|
|
||||||
3. Parameters can be passed between the visitors in close to the "normal"
|
#. Parameters can be passed between the visitors in close to the "normal"
|
||||||
function caller to callee way. This is the second ``vup`` parameter of
|
function caller to callee way. This is the second ``vup`` parameter of
|
||||||
type ``AstNUser`` that is ignored on most of the visitor functions.
|
type ``AstNUser`` that is ignored on most of the visitor functions.
|
||||||
V3Width does this, but it proved messier than the above and is
|
V3Width does this, but it proved messier than the above and is
|
||||||
|
|
@ -2125,19 +2126,19 @@ To print a node:
|
||||||
``src/.gdbinit`` and ``src/.gdbinit.py`` define handy utilities for working
|
``src/.gdbinit`` and ``src/.gdbinit.py`` define handy utilities for working
|
||||||
with JSON AST dumps. For example:
|
with JSON AST dumps. For example:
|
||||||
|
|
||||||
* ``jstash nodep`` - Perform a JSON AST dump and save it into GDB value
|
- ``jstash nodep`` - Perform a JSON AST dump and save it into GDB value
|
||||||
history (e.g. ``$1``)
|
history (e.g. ``$1``)
|
||||||
|
|
||||||
* ``jtree nodep`` - Perform a JSON AST dump and pretty print it using
|
- ``jtree nodep`` - Perform a JSON AST dump and pretty print it using
|
||||||
``astsee_verilator``.
|
``astsee_verilator``.
|
||||||
* ``jtree $1`` - Pretty print a dump that was previously saved by
|
- ``jtree $1`` - Pretty print a dump that was previously saved by
|
||||||
``jstash``.
|
``jstash``.
|
||||||
* ``jtree nodep -d '.file, .timeunit'`` - Perform a JSON AST dump, filter
|
- ``jtree nodep -d '.file, .timeunit'`` - Perform a JSON AST dump, filter
|
||||||
out some fields and pretty print it.
|
out some fields and pretty print it.
|
||||||
|
|
||||||
* ``jtree 0x55555613dca0`` - Pretty print using address literal (rather
|
- ``jtree 0x55555613dca0`` - Pretty print using address literal (rather
|
||||||
than actual pointer).
|
than actual pointer).
|
||||||
* ``jtree $1 nodep`` - Diff ``nodep`` against an older dump.
|
- ``jtree $1 nodep`` - Diff ``nodep`` against an older dump.
|
||||||
|
|
||||||
A detailed description of ``jstash`` and ``jtree`` can be displayed using
|
A detailed description of ``jstash`` and ``jtree`` can be displayed using
|
||||||
``gdb``'s ``help`` command.
|
``gdb``'s ``help`` command.
|
||||||
|
|
@ -2243,25 +2244,25 @@ Adding a New Feature
|
||||||
|
|
||||||
Generally, what would you do to add a new feature?
|
Generally, what would you do to add a new feature?
|
||||||
|
|
||||||
1. File an issue (if there isn't already) so others know what you're
|
#. File an issue (if there isn't already) so others know what you're
|
||||||
working on.
|
working on.
|
||||||
|
|
||||||
2. Make a testcase in the test_regress/t/t_EXAMPLE format, see `Testing`.
|
#. Make a testcase in the test_regress/t/t_EXAMPLE format, see `Testing`.
|
||||||
|
|
||||||
3. If grammar changes are needed, look at the IEEE 1800-2023 Appendix A, as
|
#. If grammar changes are needed, look at the IEEE 1800-2023 Appendix A, as
|
||||||
src/verilog.y generally follows the same rule layout.
|
src/verilog.y generally follows the same rule layout.
|
||||||
|
|
||||||
4. If a new Ast type is needed, add it to the appropriate V3AstNode*.h.
|
#. If a new Ast type is needed, add it to the appropriate V3AstNode*.h.
|
||||||
Follow the convention described above about the AstNode type hierarchy.
|
Follow the convention described above about the AstNode type hierarchy.
|
||||||
Ordering of definitions is enforced by ``astgen``.
|
Ordering of definitions is enforced by ``astgen``.
|
||||||
|
|
||||||
5. Now you can run ``test_regress/t/t_<newtestcase>.py --debug`` and it'll
|
#. Now you can run ``test_regress/t/t_<newtestcase>.py --debug`` and it'll
|
||||||
probably fail, but you'll see a
|
probably fail, but you'll see a
|
||||||
``test_regress/obj_dir/t_<newtestcase>/*.tree`` file which you can
|
``test_regress/obj_dir/t_<newtestcase>/*.tree`` file which you can
|
||||||
examine to see if the parsing worked. See also the sections above on
|
examine to see if the parsing worked. See also the sections above on
|
||||||
debugging.
|
debugging.
|
||||||
|
|
||||||
6. Modify the later visitor functions to process the new feature as needed.
|
#. Modify the later visitor functions to process the new feature as needed.
|
||||||
|
|
||||||
|
|
||||||
Adding a New Pass
|
Adding a New Pass
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
.. for github, vim: syntax=reStructuredText
|
..
|
||||||
.. SPDX-FileCopyrightText: 2025-2026 Wilson Snyder
|
for github, vim: syntax=reStructuredText
|
||||||
.. SPDX-License-Identifier: CC0-1.0
|
SPDX-FileCopyrightText: 2025-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
Security Policy
|
Security Policy
|
||||||
===============
|
===============
|
||||||
|
|
|
||||||
|
|
@ -861,6 +861,7 @@ hx
|
||||||
hyperthreading
|
hyperthreading
|
||||||
hyperthreads
|
hyperthreads
|
||||||
icecream
|
icecream
|
||||||
|
ico
|
||||||
idmap
|
idmap
|
||||||
ifdef
|
ifdef
|
||||||
ifdefed
|
ifdefed
|
||||||
|
|
@ -916,6 +917,7 @@ linters
|
||||||
linux
|
linux
|
||||||
liu
|
liu
|
||||||
livelock
|
livelock
|
||||||
|
liveness
|
||||||
lldb
|
lldb
|
||||||
ln
|
ln
|
||||||
loc
|
loc
|
||||||
|
|
@ -924,6 +926,7 @@ localparams
|
||||||
localtime
|
localtime
|
||||||
logicals
|
logicals
|
||||||
longint
|
longint
|
||||||
|
lookups
|
||||||
lossy
|
lossy
|
||||||
lsb
|
lsb
|
||||||
lubc
|
lubc
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
<!-- DESCRIPTION: Verilator: include/ guidelines for AI coding agents
|
||||||
|
SPDX-FileCopyrightText: 2026-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -->
|
||||||
|
|
||||||
|
# include/ -- Verilator runtime library
|
||||||
|
|
||||||
|
Covers the C++ runtime under `include/` (`verilated.h/.cpp`, `verilated_*.h/.cpp`)
|
||||||
|
that every generated model links against. Read the repository-root
|
||||||
|
[AGENTS.md](../AGENTS.md) first. The rules here differ from `src/`: this code
|
||||||
|
ships to users, runs every simulation cycle, and must stay portable and fast.
|
||||||
|
|
||||||
|
______________________________________________________________________
|
||||||
|
|
||||||
|
# Orientation
|
||||||
|
|
||||||
|
- **This is the runtime, not the compiler.** The passes in `src/` *emit* C++ that
|
||||||
|
calls into this library; this code then *runs* during simulation. Optimize it
|
||||||
|
for execution speed and portability, not for compile-time clarity.
|
||||||
|
- **Key files:** `verilated.h` (core model API), `verilated_types.h` (data
|
||||||
|
types), `verilated_random.cpp` (constrained-random runtime), `verilated_cov.*`
|
||||||
|
(coverage), `verilated_threads.*` (MT runtime), `verilated_timing.*`
|
||||||
|
(`--timing` runtime), `verilated_vcd_c.*`/`verilated_fst_c.*` (tracing).
|
||||||
|
- A runtime-only fix lives entirely here and does not rebuild `verilator_bin`.
|
||||||
|
|
||||||
|
______________________________________________________________________
|
||||||
|
|
||||||
|
# Before you open a PR
|
||||||
|
|
||||||
|
- **C++14 baseline.** The runtime must build under `--no-timing` with C++14; C++20
|
||||||
|
features are allowed only in `--timing` code paths.
|
||||||
|
- **Public API naming and docs.** Prefix public classes and types with
|
||||||
|
`Verilated`/`Vl` to avoid collisions with user code, and document the API with
|
||||||
|
`///` comments -- they feed doc generation.
|
||||||
|
- **No exceptions in runtime code** -- use error returns or assertions; exceptions
|
||||||
|
add overhead on every path.
|
||||||
|
- **Use fixed-width model types** (`CData`/`SData`/`IData`/`QData`/`VlWide`), never
|
||||||
|
`size_t`, for model data. Process wide data word-by-word (`VL_ZERO_W`,
|
||||||
|
`VL_MEMCPY_W`), never bit-by-bit or byte-by-byte.
|
||||||
|
- **Do all string parsing at verilation time** -- never parse strings during
|
||||||
|
simulation; emit structured data or compile-time hints instead.
|
||||||
|
- **Keep per-cycle code lean.** Do not add vtables to high-frequency objects
|
||||||
|
(8 bytes per instance); guard optional features behind
|
||||||
|
`hasClasses()`/`hasEvents()`-style checks; functions called per cycle must avoid
|
||||||
|
mutexes -- use atomics or lockless designs.
|
||||||
|
- **Emit no runtime loops** the compiler could have expanded at verilation time;
|
||||||
|
prefer a single runtime call.
|
||||||
|
- **Thread safety.** Annotate with the hierarchy `VL_PURE` > `VL_MT_SAFE` >
|
||||||
|
`VL_MT_STABLE`; annotations must match the implementation. Annotate
|
||||||
|
mutex-protected members with `VL_GUARDED_BY` and document acquisition ordering.
|
||||||
|
Prefer has-a over is-a: a guarded class wrapping the unguarded internal one, with
|
||||||
|
the guarded version as the default public API.
|
||||||
|
- **Keep runtime and compiler headers separate** -- never include `verilated.h`
|
||||||
|
into the compiler in `src/`.
|
||||||
|
|
||||||
|
## File-specific rules
|
||||||
|
|
||||||
|
| File | Rule |
|
||||||
|
|---|---|
|
||||||
|
| `verilated_random.cpp` | Emit only portable SMT-LIB 2.6 -- no solver-specific or MaxSMT extensions; the generated solver text is the model's runtime constraint interface |
|
||||||
|
| `verilated_cov.cpp` | Coverage runtime is shared by all models -- keep per-point overhead minimal and the on-disk format stable for `verilator_coverage` |
|
||||||
|
|
@ -81,6 +81,12 @@
|
||||||
std::abort(); \
|
std::abort(); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define FST_FAIL_STRING(s) \
|
||||||
|
do { \
|
||||||
|
std::cerr << (s) << std::endl; \
|
||||||
|
std::abort(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
// We turn on all DCHECKs to CHECKs temporarily for better safety.
|
// We turn on all DCHECKs to CHECKs temporarily for better safety.
|
||||||
#if 1
|
#if 1
|
||||||
# define FST_DCHECK(a) FST_CHECK(a)
|
# define FST_DCHECK(a) FST_CHECK(a)
|
||||||
|
|
|
||||||
|
|
@ -335,10 +335,10 @@ public:
|
||||||
// Double variables should not use these array-based emitValueChange overloads.
|
// Double variables should not use these array-based emitValueChange overloads.
|
||||||
// We implement them to satisfy the VairableInfo::dispatchHelper template instantiation.
|
// We implement them to satisfy the VairableInfo::dispatchHelper template instantiation.
|
||||||
void emitValueChange(uint64_t, const uint32_t *, EncodingType) {
|
void emitValueChange(uint64_t, const uint32_t *, EncodingType) {
|
||||||
throw std::runtime_error("emitValueChange(uint32_t*) not supported for Double");
|
FST_FAIL_STRING("emitValueChange(uint32_t*) not supported for Double");
|
||||||
}
|
}
|
||||||
void emitValueChange(uint64_t, const uint64_t *, EncodingType) {
|
void emitValueChange(uint64_t, const uint64_t *, EncodingType) {
|
||||||
throw std::runtime_error("emitValueChange(uint64_t*) not supported for Double");
|
FST_FAIL_STRING("emitValueChange(uint64_t*) not supported for Double");
|
||||||
}
|
}
|
||||||
|
|
||||||
void dumpInitialBits(std::vector<uint8_t> &buf) const {
|
void dumpInitialBits(std::vector<uint8_t> &buf) const {
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ Handle Writer::createVar(
|
||||||
// (void)type;
|
// (void)type;
|
||||||
// (void)svt;
|
// (void)svt;
|
||||||
// (void)sdt;
|
// (void)sdt;
|
||||||
// throw std::runtime_error("TODO");
|
// FST_FAIL_STRING("TODO");
|
||||||
// return 0;
|
// return 0;
|
||||||
// }
|
// }
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
@ -353,11 +353,7 @@ void compressUsingZlib(
|
||||||
uncompressed_size,
|
uncompressed_size,
|
||||||
level
|
level
|
||||||
);
|
);
|
||||||
if (z_status != Z_OK) {
|
FST_CHECK_EQ(z_status, Z_OK);
|
||||||
throw std::runtime_error(
|
|
||||||
"Failed to compress data with zlib, error code: " + std::to_string(z_status)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
compressed_data.resize(compressed_bound);
|
compressed_data.resize(compressed_bound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -318,6 +318,13 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VL_FFLUSH_MT() VL_MT_SAFE {
|
||||||
|
va_list ap;
|
||||||
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||||
|
Verilated::runFlushCallbacks();
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
template <typename... snprintf_args_ts>
|
template <typename... snprintf_args_ts>
|
||||||
static size_t _vl_snprintf_string(std::string& str, const char* format,
|
static size_t _vl_snprintf_string(std::string& str, const char* format,
|
||||||
snprintf_args_ts... args) VL_MT_SAFE {
|
snprintf_args_ts... args) VL_MT_SAFE {
|
||||||
|
|
@ -3028,40 +3035,97 @@ void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
||||||
}
|
}
|
||||||
bool VerilatedContext::assertOnGet(VerilatedAssertType_t type,
|
bool VerilatedContext::assertOnGet(VerilatedAssertType_t type,
|
||||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
|
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
|
||||||
// Check if selected directive type bit in the assertOn is enabled for assertion type.
|
return assertCtlGet(VerilatedAssertCtlQuery::ASSERT_CTL_ON, type, directive);
|
||||||
// Note: it is assumed that this is checked only for one type at the time.
|
}
|
||||||
|
uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
|
||||||
// Flag unspecified assertion types as disabled.
|
VerilatedAssertDirectiveType_t directives) VL_PURE {
|
||||||
if (type == 0) return false;
|
// Place the directive bits at each selected assertion type's 3-bit group.
|
||||||
|
uint32_t mask = 0;
|
||||||
// Get index of 3-bit group guarding assertion type status.
|
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
|
||||||
// Since the assertOnGet is generated __always__ for a single assert type, we assume that only
|
if (VL_BITISSET_I(types, i)) mask |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH);
|
||||||
// a single bit will be set. Thus, ceil log2 will work fine.
|
}
|
||||||
VL_DEBUG_IFDEF(assert((type & (type - 1)) == 0););
|
return mask;
|
||||||
const IData typeMaskPosition = VL_CLOG2_I(type);
|
|
||||||
|
|
||||||
// Check if directive type bit is enabled in corresponding assertion type bits.
|
|
||||||
return m_s.m_assertOn & (directive << (typeMaskPosition * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH));
|
|
||||||
}
|
}
|
||||||
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||||
// For each assertion type, set directive bits.
|
m_s.m_assertOn |= assertOnMask(types, directives);
|
||||||
|
|
||||||
// Iterate through all positions of assertion type bits. If bit for this assertion type is set,
|
|
||||||
// set directive type bits mask at this group index.
|
|
||||||
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
|
|
||||||
if (VL_BITISSET_I(types, i))
|
|
||||||
m_s.m_assertOn |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||||
// Iterate through all positions of assertion type bits. If bit for this assertion type is set,
|
m_s.m_assertOn &= ~assertOnMask(types, directives);
|
||||||
// clear directive type bits mask at this group index.
|
}
|
||||||
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
|
void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||||
if (VL_BITISSET_I(types, i))
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||||
m_s.m_assertOn &= ~(directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH));
|
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the
|
||||||
|
// selected bits until Unlock; On/Off/Kill leave locked bits unchanged.
|
||||||
|
const uint32_t mask = assertOnMask(types, directives);
|
||||||
|
const uint32_t lockedMask = mask & ~m_s.m_assertLock;
|
||||||
|
switch (controlType) {
|
||||||
|
case 1: // Lock
|
||||||
|
m_s.m_assertLock |= mask;
|
||||||
|
break;
|
||||||
|
case 2: // Unlock
|
||||||
|
m_s.m_assertLock &= ~mask;
|
||||||
|
break;
|
||||||
|
case 3: // On
|
||||||
|
m_s.m_assertOn |= lockedMask;
|
||||||
|
break;
|
||||||
|
case 4: // Off
|
||||||
|
m_s.m_assertOn &= ~lockedMask;
|
||||||
|
break;
|
||||||
|
case 5: { // Kill
|
||||||
|
m_s.m_assertOn &= ~lockedMask;
|
||||||
|
for (int slot = 0; slot < static_cast<int>(ASSERT_CONTROL_SLOT_COUNT); ++slot) {
|
||||||
|
if (VL_BITISSET_I(lockedMask, slot)) { m_s.m_assertKill[slot]++; }
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case 6: // PassOn
|
||||||
|
m_s.m_assertPassOnVacuous |= lockedMask;
|
||||||
|
m_s.m_assertPassOnNonvacuous |= lockedMask;
|
||||||
|
break;
|
||||||
|
case 7: // PassOff
|
||||||
|
m_s.m_assertPassOnVacuous &= ~lockedMask;
|
||||||
|
m_s.m_assertPassOnNonvacuous &= ~lockedMask;
|
||||||
|
break;
|
||||||
|
case 8: // FailOn
|
||||||
|
m_s.m_assertFailOn |= lockedMask;
|
||||||
|
break;
|
||||||
|
case 9: // FailOff
|
||||||
|
m_s.m_assertFailOn &= ~lockedMask;
|
||||||
|
break;
|
||||||
|
case 10: // NonvacuousOn
|
||||||
|
m_s.m_assertPassOnNonvacuous |= lockedMask;
|
||||||
|
break;
|
||||||
|
case 11: // VacuousOff
|
||||||
|
m_s.m_assertPassOnVacuous &= ~lockedMask;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
VL_WARN_MT("", 0, "",
|
||||||
|
("Bad $assertcontrol control_type '" + std::to_string(controlType)
|
||||||
|
+ "' (IEEE 1800-2023 Table 20-5)")
|
||||||
|
.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t
|
||||||
|
VerilatedContext::assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
|
||||||
|
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
|
||||||
|
const uint32_t mask = assertOnMask(type, directive);
|
||||||
|
if (!mask) return 0;
|
||||||
|
switch (query) { // LCOV_EXCL_BR_LINE
|
||||||
|
case VerilatedAssertCtlQuery::ASSERT_CTL_ON: return (m_s.m_assertOn & mask) != 0;
|
||||||
|
case VerilatedAssertCtlQuery::ASSERT_CTL_KILL:
|
||||||
|
assert(mask && (mask & (mask - 1)) == 0);
|
||||||
|
return m_s.m_assertKill[VL_CLOG2_I(mask)];
|
||||||
|
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
|
||||||
|
return (m_s.m_assertPassOnVacuous & mask) != 0;
|
||||||
|
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_NONVACUOUS:
|
||||||
|
return (m_s.m_assertPassOnNonvacuous & mask) != 0;
|
||||||
|
case VerilatedAssertCtlQuery::ASSERT_CTL_FAIL_ON: return (m_s.m_assertFailOn & mask) != 0;
|
||||||
|
default: // LCOV_EXCL_START
|
||||||
|
VL_FATAL_MT("", 0, "", "Internal: Bad assertCtlGet query");
|
||||||
|
VL_UNREACHABLE;
|
||||||
|
} // LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
|
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
|
||||||
const VerilatedLockGuard lock{m_mutex};
|
const VerilatedLockGuard lock{m_mutex};
|
||||||
|
|
@ -3893,6 +3957,7 @@ std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { retu
|
||||||
|
|
||||||
// cppcheck-suppress unusedFunction // Used by applications
|
// cppcheck-suppress unusedFunction // Used by applications
|
||||||
uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
|
uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
|
||||||
|
if (m_entSize) return m_entSize;
|
||||||
uint32_t size = 1;
|
uint32_t size = 1;
|
||||||
switch (vltype()) {
|
switch (vltype()) {
|
||||||
case VLVT_PTR: size = sizeof(void*); break;
|
case VLVT_PTR: size = sizeof(void*); break;
|
||||||
|
|
@ -4009,6 +4074,27 @@ VerilatedVar* VerilatedScope::varInsert(const char* namep, void* datap, bool isP
|
||||||
return &(m_varsp->find(namep)->second);
|
return &(m_varsp->find(namep)->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
|
||||||
|
VerilatedVarType vltype, int vlflags, int udims,
|
||||||
|
uint32_t entSize...) VL_MT_UNSAFE {
|
||||||
|
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
|
||||||
|
VerilatedVar var(namep, datap, vltype, static_cast<VerilatedVarFlags>(vlflags), udims, 0,
|
||||||
|
isParam, entSize);
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, entSize);
|
||||||
|
for (int i = 0; i < udims; ++i) {
|
||||||
|
const int msb = va_arg(ap, int);
|
||||||
|
const int lsb = va_arg(ap, int);
|
||||||
|
var.m_unpacked[i].m_left = msb;
|
||||||
|
var.m_unpacked[i].m_right = lsb;
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
m_varsp->emplace(namep, std::move(var));
|
||||||
|
return &(m_varsp->find(namep)->second);
|
||||||
|
}
|
||||||
|
|
||||||
VerilatedVar*
|
VerilatedVar*
|
||||||
VerilatedScope::forceableVarInsert(const char* namep, void* datap, bool isParam,
|
VerilatedScope::forceableVarInsert(const char* namep, void* datap, bool isParam,
|
||||||
VerilatedVarType vltype, int vlflags, void* forceReadSignalData,
|
VerilatedVarType vltype, int vlflags, void* forceReadSignalData,
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,9 @@ enum VerilatedVarType : uint8_t {
|
||||||
VLVT_UINT64, // AKA QData
|
VLVT_UINT64, // AKA QData
|
||||||
VLVT_WDATA, // AKA VlWide
|
VLVT_WDATA, // AKA VlWide
|
||||||
VLVT_STRING, // C++ string
|
VLVT_STRING, // C++ string
|
||||||
VLVT_REAL // AKA double
|
VLVT_REAL, // AKA double
|
||||||
|
VLVT_STRUCT, // SystemVerilog unpacked struct
|
||||||
|
VLVT_UNION // SystemVerilog unpacked union
|
||||||
};
|
};
|
||||||
|
|
||||||
enum VerilatedVarFlags : uint32_t {
|
enum VerilatedVarFlags : uint32_t {
|
||||||
|
|
@ -154,7 +156,8 @@ enum VerilatedVarFlags : uint32_t {
|
||||||
VLVF_CONTINUOUSLY = (1 << 11), // Is continously assigned
|
VLVF_CONTINUOUSLY = (1 << 11), // Is continously assigned
|
||||||
VLVF_FORCEABLE = (1 << 12), // Forceable
|
VLVF_FORCEABLE = (1 << 12), // Forceable
|
||||||
VLVF_SIGNED = (1 << 13), // Signed integer
|
VLVF_SIGNED = (1 << 13), // Signed integer
|
||||||
VLVF_BITVAR = (1 << 14) // Four state bit (vs two state logic)
|
VLVF_BITVAR = (1 << 14), // Four state bit (vs two state logic)
|
||||||
|
VLVF_NET = (1 << 15) // Net object
|
||||||
};
|
};
|
||||||
|
|
||||||
// IEEE 1800-2023 Table 20-6
|
// IEEE 1800-2023 Table 20-6
|
||||||
|
|
@ -175,6 +178,15 @@ enum class VerilatedAssertDirectiveType : uint8_t {
|
||||||
DIRECTIVE_TYPE_COVER = (1 << 1),
|
DIRECTIVE_TYPE_COVER = (1 << 1),
|
||||||
DIRECTIVE_TYPE_ASSUME = (1 << 2),
|
DIRECTIVE_TYPE_ASSUME = (1 << 2),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Runtime query selector for assertion-control state
|
||||||
|
enum class VerilatedAssertCtlQuery : uint8_t {
|
||||||
|
ASSERT_CTL_ON,
|
||||||
|
ASSERT_CTL_KILL,
|
||||||
|
ASSERT_CTL_PASS_ON_VACUOUS,
|
||||||
|
ASSERT_CTL_PASS_ON_NONVACUOUS,
|
||||||
|
ASSERT_CTL_FAIL_ON,
|
||||||
|
};
|
||||||
using VerilatedAssertType_t = std::underlying_type<VerilatedAssertType>::type;
|
using VerilatedAssertType_t = std::underlying_type<VerilatedAssertType>::type;
|
||||||
using VerilatedAssertDirectiveType_t = std::underlying_type<VerilatedAssertDirectiveType>::type;
|
using VerilatedAssertDirectiveType_t = std::underlying_type<VerilatedAssertDirectiveType>::type;
|
||||||
|
|
||||||
|
|
@ -356,6 +368,10 @@ private:
|
||||||
static constexpr size_t ASSERT_ON_WIDTH
|
static constexpr size_t ASSERT_ON_WIDTH
|
||||||
= ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits
|
= ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits
|
||||||
+ 1;
|
+ 1;
|
||||||
|
// Build the assertion-control bit mask for the given assertion x directive types.
|
||||||
|
static uint32_t assertOnMask(VerilatedAssertType_t types,
|
||||||
|
VerilatedAssertDirectiveType_t directives) VL_PURE;
|
||||||
|
static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// TYPES
|
// TYPES
|
||||||
|
|
@ -375,6 +391,16 @@ protected:
|
||||||
// for each VerilatedAssertType we store
|
// for each VerilatedAssertType we store
|
||||||
// 3-bits, one for each directive type. Last
|
// 3-bits, one for each directive type. Last
|
||||||
// bit guards internal directive types.
|
// bit guards internal directive types.
|
||||||
|
std::atomic<uint32_t> m_assertLock{0}; // Locked assertion bits (IEEE 1800-2023 20.11
|
||||||
|
// Lock/Unlock); same layout as m_assertOn. While
|
||||||
|
// a bit is locked, On/Off/Kill leave it unchanged.
|
||||||
|
std::atomic<uint32_t> m_assertPassOnVacuous{
|
||||||
|
std::numeric_limits<uint32_t>::max()}; // Enabled vacuous pass actions
|
||||||
|
std::atomic<uint32_t> m_assertPassOnNonvacuous{
|
||||||
|
std::numeric_limits<uint32_t>::max()}; // Enabled nonvacuous pass actions
|
||||||
|
std::atomic<uint32_t> m_assertFailOn{
|
||||||
|
std::numeric_limits<uint32_t>::max()}; // Enabled fail actions
|
||||||
|
std::array<std::atomic<uint32_t>, ASSERT_CONTROL_SLOT_COUNT> m_assertKill{};
|
||||||
bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated
|
bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated
|
||||||
bool m_fatalOnError = true; // Fatal on $stop/non-fatal error
|
bool m_fatalOnError = true; // Fatal on $stop/non-fatal error
|
||||||
bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported
|
bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported
|
||||||
|
|
@ -484,6 +510,13 @@ public:
|
||||||
/// Clear enabled status for given assertion types
|
/// Clear enabled status for given assertion types
|
||||||
void assertOnClear(VerilatedAssertType_t types,
|
void assertOnClear(VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||||
|
/// Apply assertion control for given control, assertion, and directive types
|
||||||
|
void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||||
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||||
|
/// Get assertion-control runtime state. Boolean queries return 0/1, Kill returns
|
||||||
|
/// the generation count.
|
||||||
|
uint32_t assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
|
||||||
|
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
|
||||||
/// Return if calculating of unused signals (for traces)
|
/// Return if calculating of unused signals (for traces)
|
||||||
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
|
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
|
||||||
/// Enable calculation of unused signals (for traces)
|
/// Enable calculation of unused signals (for traces)
|
||||||
|
|
@ -751,6 +784,9 @@ public: // But internals only - called from verilated modules, VerilatedSyms
|
||||||
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
|
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
|
||||||
VerilatedVar* varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype,
|
VerilatedVar* varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype,
|
||||||
int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE;
|
int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE;
|
||||||
|
VerilatedVar* varInsertSized(const char* namep, void* datap, bool isParam,
|
||||||
|
VerilatedVarType vltype, int vlflags, int udims, uint32_t entSize,
|
||||||
|
...) VL_MT_UNSAFE;
|
||||||
VerilatedVar* forceableVarInsert(const char* namep, void* datap, bool isParam,
|
VerilatedVar* forceableVarInsert(const char* namep, void* datap, bool isParam,
|
||||||
VerilatedVarType vltype, int vlflags,
|
VerilatedVarType vltype, int vlflags,
|
||||||
void* forceReadSignalData, const char* forceReadSignalName,
|
void* forceReadSignalData, const char* forceReadSignalName,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Code available from: https://verilator.org
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of either the GNU Lesser General Public License Version 3
|
||||||
|
// or the Perl Artistic License Version 2.0.
|
||||||
|
// SPDX-FileCopyrightText: 2024-2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
///
|
||||||
|
/// \file
|
||||||
|
/// \brief Verilated functional-coverage model interfaces
|
||||||
|
///
|
||||||
|
/// Defines interface classes to runtime covergroup coverage-collection classes.
|
||||||
|
/// These are used to query coverage achievement at runtime, and (future)
|
||||||
|
/// when writing coverage to the coverage database.
|
||||||
|
///
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
#ifndef VERILATOR_VERILATED_COV_MODEL_H_
|
||||||
|
#define VERILATOR_VERILATED_COV_MODEL_H_
|
||||||
|
|
||||||
|
#include "verilatedos.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Per-bin classification. A bin's kind is which set it lives in (structural),
|
||||||
|
// not a per-bin field. Only Normal feeds coverage(); the rest are recorded.
|
||||||
|
// Enumerators are 'KIND_'-prefixed because the bare LRM terms collide with
|
||||||
|
// <windows.h> macros (e.g. IGNORE), which the preprocessor would expand.
|
||||||
|
enum class VlCovBinKind : uint8_t {
|
||||||
|
KIND_NORMAL = 0, // Base coverage-collecting bin
|
||||||
|
KIND_DEFAULT = 1, // Bin declared with 'default' range (which is excluded per LRM)
|
||||||
|
KIND_IGNORE = 2, // Ignore bin
|
||||||
|
KIND_ILLEGAL = 3 // Illegal bin
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// VlCoverpointIf
|
||||||
|
/// Read-side view of a coverpoint. The writer queries bins by index; the
|
||||||
|
/// implementor computes names/kinds on demand. Bounded bin count, so random
|
||||||
|
/// access by index is the primary usage.
|
||||||
|
|
||||||
|
class VlCoverpointIf VL_NOT_FINAL {
|
||||||
|
public:
|
||||||
|
// CONSTRUCTORS
|
||||||
|
virtual ~VlCoverpointIf() = default;
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
// All bins, across every set; index range [0, binCount())
|
||||||
|
virtual int binCount() const = 0;
|
||||||
|
// Bin name in declaration order (e.g. "myBin" or "b[3]")
|
||||||
|
virtual std::string binName(int i) const = 0;
|
||||||
|
virtual VlCovBinKind binKind(int i) const = 0;
|
||||||
|
// Bins covered / effective total (Normal set only) for the coverage calc
|
||||||
|
virtual void coverageParts(double& covered, double& total) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // Guard
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Code available from: https://verilator.org
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of either the GNU Lesser General Public License Version 3
|
||||||
|
// or the Perl Artistic License Version 2.0.
|
||||||
|
// SPDX-FileCopyrightText: 2024-2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
///
|
||||||
|
/// \file
|
||||||
|
/// \brief Verilated functional-coverage collection runtime implementation
|
||||||
|
///
|
||||||
|
/// Compiled and linked when "verilator --coverage" is used with covergroups.
|
||||||
|
///
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
#include "verilatedos.h"
|
||||||
|
|
||||||
|
#include "verilated_covergroup.h"
|
||||||
|
|
||||||
|
#include "verilated_cov.h"
|
||||||
|
|
||||||
|
void VlCoverpoint::init(const char* hier, uint32_t atLeast, int nBins) {
|
||||||
|
m_hier = hier;
|
||||||
|
m_atLeast = atLeast;
|
||||||
|
m_total = nBins;
|
||||||
|
m_counts.assign(nBins, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VlCoverpoint::addNamer(VlCovBinKind set, int count, VlCovBinNaming naming, const char* name,
|
||||||
|
const char* file, int line, int col) {
|
||||||
|
m_namers.emplace_back(set, count, m_nextBase, naming, name, file, line, col);
|
||||||
|
m_nextBase += count;
|
||||||
|
if (set == VlCovBinKind::KIND_NORMAL) m_normal += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VlCovNamer& VlCoverpoint::namerFor(int i) const {
|
||||||
|
// Namers are appended in ascending, contiguous index order covering [0, m_total),
|
||||||
|
// and i is always a valid bin index, so the matching namer always exists.
|
||||||
|
for (const VlCovNamer& nm : m_namers) {
|
||||||
|
if (i < nm.base() + nm.count()) return nm;
|
||||||
|
}
|
||||||
|
VL_UNREACHABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VlCoverpoint::binName(int i) const {
|
||||||
|
const VlCovNamer& nm = namerFor(i);
|
||||||
|
std::string name = nm.name();
|
||||||
|
if (nm.naming() == VlCovBinNaming::Array) name += '[' + std::to_string(i - nm.base()) + ']';
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* page) {
|
||||||
|
for (int i = 0; i < binCount(); ++i) {
|
||||||
|
const VlCovNamer& nm = namerFor(i);
|
||||||
|
const VlCovBinKind kind = binKind(i);
|
||||||
|
const std::string binp = binName(i);
|
||||||
|
const std::string full = m_hier + "." + binp;
|
||||||
|
const std::string lineStr = std::to_string(nm.line());
|
||||||
|
const std::string colStr = std::to_string(nm.col());
|
||||||
|
if (kind == VlCovBinKind::KIND_NORMAL) {
|
||||||
|
VL_COVER_INSERT(covcontextp, full.c_str(), &m_counts[i], "page", page, "filename",
|
||||||
|
nm.file(), "lineno", lineStr.c_str(), "column", colStr.c_str(), "bin",
|
||||||
|
binp.c_str());
|
||||||
|
} else {
|
||||||
|
const char* const binType = kind == VlCovBinKind::KIND_IGNORE ? "ignore"
|
||||||
|
: kind == VlCovBinKind::KIND_ILLEGAL ? "illegal"
|
||||||
|
: "default";
|
||||||
|
VL_COVER_INSERT(covcontextp, full.c_str(), &m_counts[i], "page", page, "filename",
|
||||||
|
nm.file(), "lineno", lineStr.c_str(), "column", colStr.c_str(), "bin",
|
||||||
|
binp.c_str(), "bin_type", binType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
|
//=============================================================================
|
||||||
|
//
|
||||||
|
// Code available from: https://verilator.org
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of either the GNU Lesser General Public License Version 3
|
||||||
|
// or the Perl Artistic License Version 2.0.
|
||||||
|
// SPDX-FileCopyrightText: 2024-2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
///
|
||||||
|
/// \file
|
||||||
|
/// \brief Verilated functional-coverage collection runtime
|
||||||
|
///
|
||||||
|
/// VlCoverpoint owns per-instance bin-count storage for one coverpoint,
|
||||||
|
/// computes coverage, builds bin names on demand, and registers bins with the
|
||||||
|
/// coverage database. It implements the VlCoverpointIf read interface.
|
||||||
|
///
|
||||||
|
/// Generated covergroup code holds one VlCoverpoint per coverpoint, configures
|
||||||
|
/// it in the constructor (init + add*Namer), increments bins from sample(),
|
||||||
|
/// and registers via registerBins().
|
||||||
|
///
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
#ifndef VERILATOR_VERILATED_COVERGROUP_H_
|
||||||
|
#define VERILATOR_VERILATED_COVERGROUP_H_
|
||||||
|
|
||||||
|
#include "verilatedos.h"
|
||||||
|
|
||||||
|
#include "verilated_cov_model.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class VerilatedCovContext;
|
||||||
|
|
||||||
|
// How a namer builds the names of the bins it covers.
|
||||||
|
enum class VlCovBinNaming : uint8_t {
|
||||||
|
Single, // "<name>" one bin
|
||||||
|
Array, // "<name>[i]" bins b[N] value array
|
||||||
|
};
|
||||||
|
|
||||||
|
// Specifies the naming scheme for a range of bins, allowing the
|
||||||
|
// specific name to be computed on-demand.
|
||||||
|
// All name strings are borrowed literals from the generated code.
|
||||||
|
class VlCovNamer final {
|
||||||
|
// MEMBERS
|
||||||
|
VlCovBinKind m_set; // which set the bins belong to
|
||||||
|
int m_count; // bins this namer covers (1 for Single)
|
||||||
|
int m_base; // first bin index (declaration order), assigned on append
|
||||||
|
VlCovBinNaming m_naming; // how bin names are built
|
||||||
|
const char* m_name; // bin name (Single) or array base name (Array)
|
||||||
|
const char* m_file; // declaration file
|
||||||
|
int m_line; // declaration line
|
||||||
|
int m_col; // declaration column
|
||||||
|
|
||||||
|
public:
|
||||||
|
// CONSTRUCTORS
|
||||||
|
VlCovNamer(VlCovBinKind set, int count, int base, VlCovBinNaming naming, const char* name,
|
||||||
|
const char* file, int line, int col)
|
||||||
|
: m_set{set}
|
||||||
|
, m_count{count}
|
||||||
|
, m_base{base}
|
||||||
|
, m_naming{naming}
|
||||||
|
, m_name{name}
|
||||||
|
, m_file{file}
|
||||||
|
, m_line{line}
|
||||||
|
, m_col{col} {}
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
VlCovBinKind set() const { return m_set; }
|
||||||
|
int count() const { return m_count; }
|
||||||
|
int base() const { return m_base; }
|
||||||
|
VlCovBinNaming naming() const { return m_naming; }
|
||||||
|
const char* name() const { return m_name; }
|
||||||
|
const char* file() const { return m_file; }
|
||||||
|
int line() const { return m_line; }
|
||||||
|
int col() const { return m_col; }
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// VlCoverpoint
|
||||||
|
/// Per-instance coverpoint runtime. Bins are stored in declaration order; a
|
||||||
|
/// bin's set/name come from the owning namer. coverage() is computed on demand
|
||||||
|
/// by scanning bin counts, keeping the sample() hot path a plain counter bump.
|
||||||
|
|
||||||
|
class VlCoverpoint final : public VlCoverpointIf {
|
||||||
|
// MEMBERS
|
||||||
|
std::string m_hier; // "covergroup.coverpoint"
|
||||||
|
uint32_t m_atLeast = 1; // option.at_least (coverpoint-wide)
|
||||||
|
int m_total = 0; // bins across all sets
|
||||||
|
int m_normal = 0; // Normal bins (coverage denominator)
|
||||||
|
int m_nextBase = 0; // running append cursor
|
||||||
|
std::vector<uint32_t> m_counts; // [m_total], one per bin
|
||||||
|
std::vector<VlCovNamer> m_namers; // appended in declaration order
|
||||||
|
|
||||||
|
// PRIVATE METHODS
|
||||||
|
const VlCovNamer& namerFor(int i) const; // obtain the bin-specific name producer
|
||||||
|
void addNamer(VlCovBinKind set, int count, VlCovBinNaming naming, const char* name,
|
||||||
|
const char* file, int line, int col);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// CONSTRUCTORS
|
||||||
|
VlCoverpoint() = default;
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
// ---- configuration (from generated constructor) ----
|
||||||
|
void init(const char* hier, uint32_t atLeast, int nBins);
|
||||||
|
void addSingleNamer(VlCovBinKind set, const char* name, const char* file, int line, int col) {
|
||||||
|
addNamer(set, 1, VlCovBinNaming::Single, name, file, line, col);
|
||||||
|
}
|
||||||
|
void addArrayNamer(VlCovBinKind set, int count, const char* name, const char* file, int line,
|
||||||
|
int col) {
|
||||||
|
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
|
||||||
|
}
|
||||||
|
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||||
|
|
||||||
|
// ---- hot path (from generated sample()) ----
|
||||||
|
void incrementBin(int i) { ++m_counts[i]; } // Normal bin: count only
|
||||||
|
void recordHit(int i) { ++m_counts[i]; } // Ignore/Illegal/Default: count only
|
||||||
|
|
||||||
|
// ---- VlCoverpointIf ----
|
||||||
|
int binCount() const override { return m_total; }
|
||||||
|
std::string binName(int i) const override;
|
||||||
|
VlCovBinKind binKind(int i) const override { return namerFor(i).set(); }
|
||||||
|
void coverageParts(double& covered, double& total) const override {
|
||||||
|
// Count Normal bins that reached option.at_least on demand, so the hot
|
||||||
|
// path (incrementBin) stays a plain counter bump.
|
||||||
|
int numCovered = 0;
|
||||||
|
for (const VlCovNamer& nm : m_namers) {
|
||||||
|
if (nm.set() != VlCovBinKind::KIND_NORMAL) continue;
|
||||||
|
for (int i = nm.base(); i < nm.base() + nm.count(); ++i) {
|
||||||
|
if (m_counts[i] >= m_atLeast) ++numCovered;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
covered = numCovered;
|
||||||
|
total = m_normal;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // Guard
|
||||||
|
|
@ -101,12 +101,13 @@ using VlForceStorageType = typename VlForceStorageTypeOf<VlForceBaseType<T>>::ty
|
||||||
class VlForceVec final {
|
class VlForceVec final {
|
||||||
private:
|
private:
|
||||||
struct Entry final {
|
struct Entry final {
|
||||||
int m_lsb; // Inclusive lower bit
|
int m_lsb; // Inclusive lower bit for scalar path or element index for unpacked
|
||||||
int m_msb; // Inclusive upper bit
|
int m_msb; // Inclusive upper bit for scalar path or element index for unpacked
|
||||||
int m_rhsLsb; // Destination index that maps to RHS index 0
|
int m_rhsLsb; // Destination index that maps to RHS index 0
|
||||||
const void* m_rhsDatap; // Pointer to RHS storage
|
const void* m_rhsDatap; // Pointer to RHS storage
|
||||||
|
int m_bitLsb = 0;
|
||||||
bool operator<(const Entry& other) const { return m_msb < other.m_msb; }
|
int m_bitMsb = 0;
|
||||||
|
int m_elemWidth = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Entry> m_entries; // Sorted by msb, non-overlapping
|
std::vector<Entry> m_entries; // Sorted by msb, non-overlapping
|
||||||
|
|
@ -134,6 +135,41 @@ private:
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t trimElementBitRange(int elem, int bitLsb, int bitMsb) {
|
||||||
|
auto it = std::lower_bound(m_entries.begin(), m_entries.end(), elem,
|
||||||
|
[](const Entry& e, int idx) { return e.m_msb < idx; });
|
||||||
|
while (it != m_entries.end() && it->m_lsb <= elem) {
|
||||||
|
if (it->m_elemWidth == 0 || it->m_bitMsb < bitLsb || it->m_bitLsb > bitMsb) {
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (it->m_bitLsb < bitLsb && it->m_bitMsb > bitMsb) {
|
||||||
|
Entry high = *it;
|
||||||
|
high.m_bitLsb = bitMsb + 1;
|
||||||
|
it->m_bitMsb = bitLsb - 1;
|
||||||
|
m_entries.insert(it + 1, high);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (it->m_bitLsb < bitLsb) {
|
||||||
|
it->m_bitMsb = bitLsb - 1;
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (it->m_bitMsb > bitMsb) {
|
||||||
|
it->m_bitLsb = bitMsb + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
it = m_entries.erase(it);
|
||||||
|
}
|
||||||
|
auto ins = std::lower_bound(m_entries.begin(), m_entries.end(), elem,
|
||||||
|
[](const Entry& e, int idx) { return e.m_msb < idx; });
|
||||||
|
while (ins != m_entries.end() && ins->m_lsb <= elem
|
||||||
|
&& (ins->m_elemWidth == 0 || ins->m_bitLsb <= bitLsb)) {
|
||||||
|
++ins;
|
||||||
|
}
|
||||||
|
return static_cast<std::size_t>(ins - m_entries.begin());
|
||||||
|
}
|
||||||
|
|
||||||
static QData extractRhsChunk(const Entry& entry, int rhsLsb, int width) {
|
static QData extractRhsChunk(const Entry& entry, int rhsLsb, int width) {
|
||||||
assert(width > 0 && width <= VL_QUADSIZE);
|
assert(width > 0 && width <= VL_QUADSIZE);
|
||||||
assert(rhsLsb >= 0);
|
assert(rhsLsb >= 0);
|
||||||
|
|
@ -195,6 +231,22 @@ private:
|
||||||
return *static_cast<const VlForceBaseType<T>*>(entry.m_rhsDatap);
|
return *static_cast<const VlForceBaseType<T>*>(entry.m_rhsDatap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Elem>
|
||||||
|
static typename std::enable_if<!VlIsVlWide<Elem>::value, Elem>::type
|
||||||
|
blendElem(Elem cur, const Entry& e) {
|
||||||
|
const Entry bitEntry{e.m_bitLsb, e.m_bitMsb, e.m_rhsLsb, e.m_rhsDatap, 0, 0, 0};
|
||||||
|
return applyEntry(cur, bitEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Elem>
|
||||||
|
static typename std::enable_if<VlIsVlWide<Elem>::value, Elem>::type blendElem(Elem cur,
|
||||||
|
const Entry& e) {
|
||||||
|
Elem res = cur;
|
||||||
|
const Entry bitEntry{e.m_bitLsb, e.m_bitMsb, e.m_rhsLsb, e.m_rhsDatap, 0, 0, 0};
|
||||||
|
applyEntry(res, bitEntry, e.m_bitLsb, e.m_bitMsb, 0);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<VlIsVlWide<T>::value>::type applyEntries(T& val) const {
|
typename std::enable_if<VlIsVlWide<T>::value>::type applyEntries(T& val) const {
|
||||||
for (const auto& entry : m_entries) {
|
for (const auto& entry : m_entries) {
|
||||||
|
|
@ -229,15 +281,19 @@ public:
|
||||||
using ElemRef
|
using ElemRef
|
||||||
= decltype(VlForceArrayIndexer<T>::elem(result, static_cast<std::size_t>(0)));
|
= decltype(VlForceArrayIndexer<T>::elem(result, static_cast<std::size_t>(0)));
|
||||||
using Elem = VlForceBaseType<ElemRef>;
|
using Elem = VlForceBaseType<ElemRef>;
|
||||||
const int total = static_cast<int>(VlForceArrayIndexer<T>::size);
|
|
||||||
for (const auto& entry : m_entries) {
|
for (const auto& entry : m_entries) {
|
||||||
const Elem* const rhsBasep = static_cast<const Elem*>(entry.m_rhsDatap);
|
|
||||||
const int startIdx = entry.m_lsb;
|
const int startIdx = entry.m_lsb;
|
||||||
const int endIdx = entry.m_msb;
|
const int endIdx = entry.m_msb;
|
||||||
for (int idx = startIdx; idx <= endIdx; idx++) {
|
for (int idx = startIdx; idx <= endIdx; idx++) {
|
||||||
const int rhsIndex = idx - entry.m_rhsLsb;
|
|
||||||
const std::size_t uidx = static_cast<std::size_t>(idx);
|
const std::size_t uidx = static_cast<std::size_t>(idx);
|
||||||
VlForceArrayIndexer<T>::elem(result, uidx) = rhsBasep[rhsIndex];
|
Elem& dst = VlForceArrayIndexer<T>::elem(result, uidx);
|
||||||
|
if (entry.m_elemWidth == 0) {
|
||||||
|
const Elem* const rhsBasep = static_cast<const Elem*>(entry.m_rhsDatap);
|
||||||
|
const int rhsIndex = idx - entry.m_rhsLsb;
|
||||||
|
dst = rhsBasep[rhsIndex];
|
||||||
|
} else {
|
||||||
|
dst = blendElem<Elem>(dst, entry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -250,14 +306,18 @@ public:
|
||||||
T readIndex(T origVal, int index) const {
|
T readIndex(T origVal, int index) const {
|
||||||
if (m_entries.empty()) return origVal;
|
if (m_entries.empty()) return origVal;
|
||||||
|
|
||||||
const auto it = std::lower_bound(m_entries.begin(), m_entries.end(), index,
|
T result = origVal;
|
||||||
[](const Entry& e, int idx) { return e.m_msb < idx; });
|
for (auto it = std::lower_bound(m_entries.begin(), m_entries.end(), index,
|
||||||
if (it != m_entries.end() && it->m_lsb <= index) {
|
[](const Entry& e, int idx) { return e.m_msb < idx; });
|
||||||
const int rhsIndex = index - it->m_rhsLsb;
|
it != m_entries.end() && it->m_lsb <= index; ++it) {
|
||||||
const T* const rhsBasep = static_cast<const T*>(it->m_rhsDatap);
|
if (it->m_elemWidth == 0) {
|
||||||
return rhsBasep[rhsIndex];
|
const int rhsIndex = index - it->m_rhsLsb;
|
||||||
|
result = static_cast<const T*>(it->m_rhsDatap)[rhsIndex];
|
||||||
|
} else {
|
||||||
|
result = blendElem<T>(result, *it);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return origVal;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
IData readSelI(int lbits, WDataInP valp, int lsb, int width) const {
|
IData readSelI(int lbits, WDataInP valp, int lsb, int width) const {
|
||||||
|
|
@ -291,11 +351,28 @@ public:
|
||||||
m_entries.insert(it, {lsb, msb, rhsLsb, rhsDatap});
|
m_entries.insert(it, {lsb, msb, rhsLsb, rhsDatap});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addForce(int lsb, int msb, const void* rhsDatap, int rhsLsb, int bitLsb, int bitMsb,
|
||||||
|
int elemWidth) {
|
||||||
|
assert(lsb == msb);
|
||||||
|
assert(rhsDatap);
|
||||||
|
assert(elemWidth > 0);
|
||||||
|
assert(0 <= bitLsb && bitLsb <= bitMsb && bitMsb < elemWidth);
|
||||||
|
const std::size_t at = trimElementBitRange(lsb, bitLsb, bitMsb);
|
||||||
|
m_entries.insert(m_entries.begin() + at,
|
||||||
|
Entry{lsb, msb, rhsLsb, rhsDatap, bitLsb, bitMsb, elemWidth});
|
||||||
|
}
|
||||||
|
|
||||||
void release(int lsb, int msb) {
|
void release(int lsb, int msb) {
|
||||||
assert(lsb <= msb);
|
assert(lsb <= msb);
|
||||||
trimEntries(lsb, msb);
|
trimEntries(lsb, msb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void release(int lsb, int msb, int bitLsb, int bitMsb) {
|
||||||
|
assert(lsb == msb);
|
||||||
|
assert(bitLsb <= bitMsb);
|
||||||
|
trimElementBitRange(lsb, bitLsb, bitMsb);
|
||||||
|
}
|
||||||
|
|
||||||
void touch() {}
|
void touch() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,10 +74,8 @@ extern void VL_FATAL_MT(const char* filename, int linenum, const char* hier,
|
||||||
extern void VL_WARN_MT(const char* filename, int linenum, const char* hier,
|
extern void VL_WARN_MT(const char* filename, int linenum, const char* hier,
|
||||||
const char* msg) VL_MT_SAFE;
|
const char* msg) VL_MT_SAFE;
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
/// Print a string, multithread safe. Eventually VL_PRINTF will get called.
|
/// Print a string, multithread safe. Eventually VL_PRINTF will get called.
|
||||||
extern void VL_PRINTF_MT(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
extern void VL_PRINTF_MT(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
/// Print a debug message from internals with standard prefix, with printf style format
|
/// Print a debug message from internals with standard prefix, with printf style format
|
||||||
extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
||||||
|
|
@ -85,6 +83,9 @@ extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
||||||
/// Print a debug message from string via VL_DBG_MSGF
|
/// Print a debug message from string via VL_DBG_MSGF
|
||||||
inline void VL_DBG_MSGS(const std::string& str) VL_MT_SAFE { VL_DBG_MSGF("%s", str.c_str()); }
|
inline void VL_DBG_MSGS(const std::string& str) VL_MT_SAFE { VL_DBG_MSGF("%s", str.c_str()); }
|
||||||
|
|
||||||
|
/// Flush stdout
|
||||||
|
extern void VL_FFLUSH_MT() VL_MT_SAFE;
|
||||||
|
|
||||||
// EMIT_RULE: VL_RANDOM: oclean=dirty
|
// EMIT_RULE: VL_RANDOM: oclean=dirty
|
||||||
inline IData VL_RANDOM_I() VL_MT_SAFE { return vl_rand64(); }
|
inline IData VL_RANDOM_I() VL_MT_SAFE { return vl_rand64(); }
|
||||||
inline QData VL_RANDOM_Q() VL_MT_SAFE { return vl_rand64(); }
|
inline QData VL_RANDOM_Q() VL_MT_SAFE { return vl_rand64(); }
|
||||||
|
|
@ -268,6 +269,41 @@ static inline VlQueue<T> VL_CVT_UNPACK_TO_Q(const VlUnpacked<T, N_Depth>& q) VL_
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Masked match functions
|
||||||
|
static inline IData VL_MATCHMASKED_I(int, IData lhs, WDataInP matchp) VL_PURE {
|
||||||
|
size_t i = 0;
|
||||||
|
while (true) {
|
||||||
|
const IData mask = matchp[i * 2];
|
||||||
|
const IData bits = matchp[i * 2 + 1];
|
||||||
|
if ((mask & lhs) == bits) break;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
static inline IData VL_MATCHMASKED_Q(int, QData lhs, WDataInP matchp) VL_PURE {
|
||||||
|
size_t i = 0;
|
||||||
|
while (true) {
|
||||||
|
const QData mask = VL_SET_QW(matchp + i * 4);
|
||||||
|
const QData bits = VL_SET_QW(matchp + i * 4 + 2);
|
||||||
|
if ((mask & lhs) == bits) break;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
static inline IData VL_MATCHMASKED_W(int lbits, WDataInP lhsp, WDataInP matchp) VL_MT_SAFE {
|
||||||
|
const int iwords = VL_WORDS_I(lbits);
|
||||||
|
size_t i = 0;
|
||||||
|
while (true) {
|
||||||
|
const WDataInP maskp = matchp + (i * iwords * 2);
|
||||||
|
const WDataInP bitsp = matchp + (i * iwords * 2 + iwords);
|
||||||
|
EData diff = 0;
|
||||||
|
for (int j = 0; j < iwords; ++j) diff |= (maskp[j] & lhsp[j]) ^ bitsp[j];
|
||||||
|
if (!diff) break;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
// Return double from lhs (numeric) unsigned
|
// Return double from lhs (numeric) unsigned
|
||||||
double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
|
double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
|
||||||
static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE {
|
static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE {
|
||||||
|
|
@ -867,15 +903,31 @@ static inline IData VL_CLOG2_W(int words, WDataInP const lwp) VL_PURE {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline IData VL_MOSTSETBITP1_I(IData lhs) VL_PURE {
|
||||||
|
if (VL_UNLIKELY(!lhs)) return 0; // __builtin_clz is undefined for 0
|
||||||
|
#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS)
|
||||||
|
return VL_EDATASIZE - __builtin_clz(lhs);
|
||||||
|
#else
|
||||||
|
for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) {
|
||||||
|
if (VL_BITISSET_E(lhs, bit)) return bit + 1;
|
||||||
|
}
|
||||||
|
return 0; // LCOV_EXCL_LINE // Can't get here - one bit must be set
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
static inline IData VL_MOSTSETBITP1_Q(QData lhs) VL_PURE {
|
||||||
|
if (VL_UNLIKELY(!lhs)) return 0;
|
||||||
|
#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS)
|
||||||
|
return 64 - __builtin_clzll(static_cast<unsigned long long>(lhs));
|
||||||
|
#else
|
||||||
|
const IData hi = static_cast<IData>(lhs >> 32ULL);
|
||||||
|
return hi ? (VL_EDATASIZE + VL_MOSTSETBITP1_I(hi))
|
||||||
|
: VL_MOSTSETBITP1_I(static_cast<IData>(lhs));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
static inline IData VL_MOSTSETBITP1_W(int words, WDataInP const lwp) VL_PURE {
|
static inline IData VL_MOSTSETBITP1_W(int words, WDataInP const lwp) VL_PURE {
|
||||||
// MSB set bit plus one; similar to FLS. 0=value is zero
|
|
||||||
for (int i = words - 1; i >= 0; --i) {
|
for (int i = words - 1; i >= 0; --i) {
|
||||||
if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken
|
// Shorter worst case if predict not taken
|
||||||
for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) {
|
if (VL_UNLIKELY(lwp[i])) return i * VL_EDATASIZE + VL_MOSTSETBITP1_I(lwp[i]);
|
||||||
if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) return i * VL_EDATASIZE + bit + 1;
|
|
||||||
}
|
|
||||||
// Can't get here - one bit must be set
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ class VerilatedVarProps VL_NOT_FINAL {
|
||||||
const uint32_t m_magic; // Magic number
|
const uint32_t m_magic; // Magic number
|
||||||
const VerilatedVarType m_vltype; // Data type
|
const VerilatedVarType m_vltype; // Data type
|
||||||
const VerilatedVarFlags m_vlflags; // Direction
|
const VerilatedVarFlags m_vlflags; // Direction
|
||||||
|
const uint32_t m_entSize; // Element size in bytes, or 0 to derive from type
|
||||||
std::vector<VerilatedRange> m_unpacked; // Unpacked array ranges
|
std::vector<VerilatedRange> m_unpacked; // Unpacked array ranges
|
||||||
std::vector<VerilatedRange> m_packed; // Packed array ranges
|
std::vector<VerilatedRange> m_packed; // Packed array ranges
|
||||||
VerilatedRange m_packedDpi; // Flattened packed array range
|
VerilatedRange m_packedDpi; // Flattened packed array range
|
||||||
|
|
@ -104,10 +105,12 @@ class VerilatedVarProps VL_NOT_FINAL {
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
protected:
|
protected:
|
||||||
friend class VerilatedScope;
|
friend class VerilatedScope;
|
||||||
VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims, int pdims)
|
VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims, int pdims,
|
||||||
|
uint32_t entSize = 0)
|
||||||
: m_magic{MAGIC}
|
: m_magic{MAGIC}
|
||||||
, m_vltype{vltype}
|
, m_vltype{vltype}
|
||||||
, m_vlflags{vlflags} {
|
, m_vlflags{vlflags}
|
||||||
|
, m_entSize{entSize} {
|
||||||
// Only preallocate the ranges
|
// Only preallocate the ranges
|
||||||
initUnpacked(udims, nullptr);
|
initUnpacked(udims, nullptr);
|
||||||
initPacked(pdims, nullptr);
|
initPacked(pdims, nullptr);
|
||||||
|
|
@ -119,12 +122,14 @@ public:
|
||||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags)
|
VerilatedVarProps(VerilatedVarType vltype, int vlflags)
|
||||||
: m_magic{MAGIC}
|
: m_magic{MAGIC}
|
||||||
, m_vltype{vltype}
|
, m_vltype{vltype}
|
||||||
, m_vlflags(VerilatedVarFlags(vlflags)) {} // Need () or GCC 4.8 false warning
|
, m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
|
||||||
|
, m_entSize{0} {}
|
||||||
|
|
||||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims)
|
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims)
|
||||||
: m_magic{MAGIC}
|
: m_magic{MAGIC}
|
||||||
, m_vltype{vltype}
|
, m_vltype{vltype}
|
||||||
, m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning
|
, m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
|
||||||
|
, m_entSize{0} {
|
||||||
initUnpacked(udims, ulims);
|
initUnpacked(udims, ulims);
|
||||||
}
|
}
|
||||||
// With packed
|
// With packed
|
||||||
|
|
@ -132,14 +137,16 @@ public:
|
||||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pdims, const int* plims)
|
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pdims, const int* plims)
|
||||||
: m_magic{MAGIC}
|
: m_magic{MAGIC}
|
||||||
, m_vltype{vltype}
|
, m_vltype{vltype}
|
||||||
, m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning
|
, m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
|
||||||
|
, m_entSize{0} {
|
||||||
initPacked(pdims, plims);
|
initPacked(pdims, plims);
|
||||||
}
|
}
|
||||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims,
|
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims,
|
||||||
Packed, int pdims, const int* plims)
|
Packed, int pdims, const int* plims)
|
||||||
: m_magic{MAGIC}
|
: m_magic{MAGIC}
|
||||||
, m_vltype{vltype}
|
, m_vltype{vltype}
|
||||||
, m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning
|
, m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning
|
||||||
|
, m_entSize{0} {
|
||||||
initUnpacked(udims, ulims);
|
initUnpacked(udims, ulims);
|
||||||
initPacked(pdims, plims);
|
initPacked(pdims, plims);
|
||||||
}
|
}
|
||||||
|
|
@ -164,6 +171,7 @@ public:
|
||||||
bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
|
bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
|
||||||
bool isSigned() const { return ((m_vlflags & VLVF_SIGNED) != 0); }
|
bool isSigned() const { return ((m_vlflags & VLVF_SIGNED) != 0); }
|
||||||
bool isBitVar() const { return ((m_vlflags & VLVF_BITVAR) != 0); }
|
bool isBitVar() const { return ((m_vlflags & VLVF_BITVAR) != 0); }
|
||||||
|
bool isNet() const { return ((m_vlflags & VLVF_NET) != 0); }
|
||||||
int udims() const VL_MT_SAFE { return m_unpacked.size(); }
|
int udims() const VL_MT_SAFE { return m_unpacked.size(); }
|
||||||
int pdims() const VL_MT_SAFE { return m_packed.size(); }
|
int pdims() const VL_MT_SAFE { return m_packed.size(); }
|
||||||
int dims() const VL_MT_SAFE { return pdims() + udims(); }
|
int dims() const VL_MT_SAFE { return pdims() + udims(); }
|
||||||
|
|
@ -265,6 +273,8 @@ protected:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
||||||
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam);
|
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam);
|
||||||
|
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
||||||
|
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam, uint32_t entSize);
|
||||||
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
||||||
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam,
|
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam,
|
||||||
std::unique_ptr<const VerilatedForceControlSignals> forceControlSignals);
|
std::unique_ptr<const VerilatedForceControlSignals> forceControlSignals);
|
||||||
|
|
@ -296,6 +306,13 @@ inline VerilatedVar::VerilatedVar(const char* namep, void* datap, VerilatedVarTy
|
||||||
, m_datap{datap}
|
, m_datap{datap}
|
||||||
, m_namep{namep}
|
, m_namep{namep}
|
||||||
, m_isParam{isParam} {}
|
, m_isParam{isParam} {}
|
||||||
|
inline VerilatedVar::VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
||||||
|
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam,
|
||||||
|
uint32_t entSize)
|
||||||
|
: VerilatedVarProps{vltype, vlflags, udims, pdims, entSize}
|
||||||
|
, m_datap{datap}
|
||||||
|
, m_namep{namep}
|
||||||
|
, m_isParam{isParam} {}
|
||||||
inline VerilatedVar::VerilatedVar(
|
inline VerilatedVar::VerilatedVar(
|
||||||
const char* namep, void* datap, VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims,
|
const char* namep, void* datap, VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims,
|
||||||
int pdims, bool isParam,
|
int pdims, bool isParam,
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
@ -68,6 +69,25 @@ constexpr unsigned VL_VPI_LINE_SIZE_ = 8192;
|
||||||
//======================================================================
|
//======================================================================
|
||||||
// Implementation
|
// Implementation
|
||||||
|
|
||||||
|
static const char* _vl_vpi_find_unescaped_dot(const char* posp) {
|
||||||
|
for (; *posp; ++posp) {
|
||||||
|
if (*posp == '\\') {
|
||||||
|
while (*posp && *posp != ' ') ++posp;
|
||||||
|
if (!*posp) return nullptr;
|
||||||
|
} else if (*posp == '.') {
|
||||||
|
return posp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string _vl_vpi_member_local_name(const char* const namep) {
|
||||||
|
const char* localp = namep;
|
||||||
|
const char* posp = namep;
|
||||||
|
while ((posp = _vl_vpi_find_unescaped_dot(posp))) localp = ++posp;
|
||||||
|
return localp;
|
||||||
|
}
|
||||||
|
|
||||||
// Base VPI handled object
|
// Base VPI handled object
|
||||||
class VerilatedVpio VL_NOT_FINAL {
|
class VerilatedVpio VL_NOT_FINAL {
|
||||||
// CONSTANTS
|
// CONSTANTS
|
||||||
|
|
@ -201,6 +221,9 @@ public:
|
||||||
}
|
}
|
||||||
const VerilatedVar* varp() const { return m_varp; }
|
const VerilatedVar* varp() const { return m_varp; }
|
||||||
const VerilatedScope* scopep() const { return m_scopep; }
|
const VerilatedScope* scopep() const { return m_scopep; }
|
||||||
|
bool isStructOrUnion() const {
|
||||||
|
return varp()->vltype() == VLVT_STRUCT || varp()->vltype() == VLVT_UNION;
|
||||||
|
}
|
||||||
// Returns the number of the currently indexed dimension (starting at -1 for none).
|
// Returns the number of the currently indexed dimension (starting at -1 for none).
|
||||||
int32_t indexedDim() const { return m_indexedDim; }
|
int32_t indexedDim() const { return m_indexedDim; }
|
||||||
// Returns whether the currently indexed dimension is unpacked.
|
// Returns whether the currently indexed dimension is unpacked.
|
||||||
|
|
@ -363,6 +386,8 @@ class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase {
|
||||||
uint32_t m_entSize = 0; // memoized variable size
|
uint32_t m_entSize = 0; // memoized variable size
|
||||||
uint32_t m_bitOffset = 0;
|
uint32_t m_bitOffset = 0;
|
||||||
int32_t m_partselBits = -1; // Part-select width, -1 means no part-select active
|
int32_t m_partselBits = -1; // Part-select width, -1 means no part-select active
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_fullNameOverride;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries
|
void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries
|
||||||
|
|
@ -373,6 +398,17 @@ public:
|
||||||
: VerilatedVpioVarBase{varp, scopep} {
|
: VerilatedVpioVarBase{varp, scopep} {
|
||||||
m_entSize = varp->entSize();
|
m_entSize = varp->entSize();
|
||||||
m_varDatap = varp->datap();
|
m_varDatap = varp->datap();
|
||||||
|
if (_vl_vpi_find_unescaped_dot(varp->name())) {
|
||||||
|
m_name = _vl_vpi_member_local_name(varp->name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep, void* datap,
|
||||||
|
const std::string& name, const std::string& fullname)
|
||||||
|
: VerilatedVpioVarBase{varp, scopep} {
|
||||||
|
m_entSize = varp->entSize();
|
||||||
|
m_varDatap = datap;
|
||||||
|
m_name = name;
|
||||||
|
m_fullNameOverride = fullname;
|
||||||
}
|
}
|
||||||
explicit VerilatedVpioVar(const VerilatedVpioVar* vop)
|
explicit VerilatedVpioVar(const VerilatedVpioVar* vop)
|
||||||
: VerilatedVpioVarBase{vop} {
|
: VerilatedVpioVarBase{vop} {
|
||||||
|
|
@ -380,6 +416,8 @@ public:
|
||||||
m_entSize = vop->m_entSize;
|
m_entSize = vop->m_entSize;
|
||||||
m_varDatap = vop->m_varDatap;
|
m_varDatap = vop->m_varDatap;
|
||||||
m_index = vop->m_index;
|
m_index = vop->m_index;
|
||||||
|
m_name = vop->m_name;
|
||||||
|
m_fullNameOverride = vop->m_fullNameOverride;
|
||||||
m_partselBits = vop->m_partselBits;
|
m_partselBits = vop->m_partselBits;
|
||||||
m_bitOffset = vop->m_bitOffset;
|
m_bitOffset = vop->m_bitOffset;
|
||||||
// Not copying m_prevDatap, must be nullptr
|
// Not copying m_prevDatap, must be nullptr
|
||||||
|
|
@ -395,15 +433,22 @@ public:
|
||||||
uint32_t bitOffset() const override { return m_bitOffset; }
|
uint32_t bitOffset() const override { return m_bitOffset; }
|
||||||
int32_t partselBits() const { return m_partselBits; }
|
int32_t partselBits() const { return m_partselBits; }
|
||||||
uint32_t bitSize() const {
|
uint32_t bitSize() const {
|
||||||
|
if (isStructOrUnion() && !isIndexedDimUnpacked()) return 0;
|
||||||
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
|
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
|
||||||
return VerilatedVpioVarBase::bitSize();
|
return VerilatedVpioVarBase::bitSize();
|
||||||
}
|
}
|
||||||
uint32_t size() const override {
|
uint32_t size() const override {
|
||||||
|
if (isStructOrUnion() && !isIndexedDimUnpacked()) return 0;
|
||||||
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
|
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
|
||||||
return VerilatedVpioVarBase::size();
|
return VerilatedVpioVarBase::size();
|
||||||
}
|
}
|
||||||
|
const VerilatedRange* rangep() const override {
|
||||||
|
if (isStructOrUnion() && !isIndexedDimUnpacked()) return nullptr;
|
||||||
|
return VerilatedVpioVarBase::rangep();
|
||||||
|
}
|
||||||
uint32_t entSize() const { return m_entSize; }
|
uint32_t entSize() const { return m_entSize; }
|
||||||
const std::vector<int32_t>& index() const { return m_index; }
|
const std::vector<int32_t>& index() const { return m_index; }
|
||||||
|
const char* name() const override { return m_name.empty() ? varp()->name() : m_name.c_str(); }
|
||||||
// Create a part-selected view of this variable with the given bit range [hi:lo].
|
// Create a part-selected view of this variable with the given bit range [hi:lo].
|
||||||
VerilatedVpioVar* withPartSelect(int32_t hi, int32_t lo) const {
|
VerilatedVpioVar* withPartSelect(int32_t hi, int32_t lo) const {
|
||||||
if (isIndexedDimUnpacked()) return nullptr;
|
if (isIndexedDimUnpacked()) return nullptr;
|
||||||
|
|
@ -456,22 +501,43 @@ public:
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
VerilatedVpioVar* withMember(const VerilatedVar* memberVarp) const {
|
||||||
|
const char* const parentName = varp()->name();
|
||||||
|
const std::string memberName = memberVarp->name();
|
||||||
|
const size_t parentLen = std::strlen(parentName);
|
||||||
|
|
||||||
|
void* const parentDatap = varp()->datap();
|
||||||
|
void* const memberDatap = memberVarp->datap();
|
||||||
|
if (VL_UNLIKELY(!parentDatap) || VL_UNLIKELY(!memberDatap)) return nullptr;
|
||||||
|
const auto offset
|
||||||
|
= static_cast<uint8_t*>(memberDatap) - static_cast<uint8_t*>(parentDatap);
|
||||||
|
|
||||||
|
const std::string localName = _vl_vpi_member_local_name(memberVarp->name());
|
||||||
|
|
||||||
|
return new VerilatedVpioVar{memberVarp, scopep(),
|
||||||
|
static_cast<uint8_t*>(varDatap()) + offset, localName,
|
||||||
|
std::string{fullname()} + memberName.substr(parentLen)};
|
||||||
|
}
|
||||||
uint32_t type() const override {
|
uint32_t type() const override {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
// TODO have V3EmitCSyms.cpp put vpiType directly into constant table
|
// TODO have V3EmitCSyms.cpp put vpiType directly into constant table
|
||||||
switch (varp()->vltype()) {
|
switch (varp()->vltype()) {
|
||||||
case VLVT_REAL: type = vpiRealVar; break;
|
case VLVT_REAL: type = vpiRealVar; break;
|
||||||
case VLVT_STRING: type = vpiStringVar; break;
|
case VLVT_STRING: type = vpiStringVar; break;
|
||||||
|
case VLVT_STRUCT: type = varp()->isNet() ? vpiStructNet : vpiStructVar; break;
|
||||||
|
case VLVT_UNION: type = varp()->isNet() ? vpiUnionNet : vpiUnionVar; break;
|
||||||
default: type = varp()->isBitVar() ? vpiBitVar : vpiReg; break;
|
default: type = varp()->isBitVar() ? vpiBitVar : vpiReg; break;
|
||||||
}
|
}
|
||||||
if (isIndexedDimUnpacked()) return vpiRegArray;
|
if (isIndexedDimUnpacked())
|
||||||
|
return isStructOrUnion() && varp()->isNet() ? vpiNetArray : vpiRegArray;
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
const char* fullname() const override {
|
const char* fullname() const override {
|
||||||
static thread_local std::string t_out;
|
m_fullname = m_fullNameOverride.empty()
|
||||||
t_out = std::string{scopep()->name()} + "." + name();
|
? std::string{scopep()->name()} + "." + varp()->name()
|
||||||
for (auto idx : index()) t_out += "[" + std::to_string(idx) + "]";
|
: m_fullNameOverride;
|
||||||
return t_out.c_str();
|
for (auto idx : index()) m_fullname += "[" + std::to_string(idx) + "]";
|
||||||
|
return m_fullname.c_str();
|
||||||
}
|
}
|
||||||
void* prevDatap() const { return m_prevDatap; }
|
void* prevDatap() const { return m_prevDatap; }
|
||||||
void* varDatap() const override { return m_varDatap; }
|
void* varDatap() const override { return m_varDatap; }
|
||||||
|
|
@ -590,25 +656,63 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class VerilatedVpioMemberIter final : public VerilatedVpio {
|
||||||
|
const VerilatedScope* const m_scopep;
|
||||||
|
const VerilatedVarNameMap* const m_varsp;
|
||||||
|
VerilatedVarNameMap::const_iterator m_it;
|
||||||
|
VerilatedVpioVar* m_varp;
|
||||||
|
const std::string m_namePrefix;
|
||||||
|
bool m_started = false;
|
||||||
|
|
||||||
|
static std::string namePrefix(const VerilatedVpioVar* vop) {
|
||||||
|
return std::string{vop->varp()->name()} + ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
vpiHandle atEnd() {
|
||||||
|
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VerilatedVpioMemberIter(const VerilatedVpioVar* vop)
|
||||||
|
: m_scopep{vop->scopep()}
|
||||||
|
, m_varsp{m_scopep->varsp()}
|
||||||
|
, m_varp{new VerilatedVpioVar{vop}}
|
||||||
|
, m_namePrefix{namePrefix(vop)} {}
|
||||||
|
~VerilatedVpioMemberIter() override { VL_DO_CLEAR(delete m_varp, m_varp = nullptr); }
|
||||||
|
// cppcheck-suppress duplInheritedMember
|
||||||
|
static VerilatedVpioMemberIter* castp(vpiHandle h) {
|
||||||
|
return dynamic_cast<VerilatedVpioMemberIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||||
|
}
|
||||||
|
uint32_t type() const override { return vpiIterator; }
|
||||||
|
vpiHandle dovpi_scan() override {
|
||||||
|
if (VL_UNLIKELY(!m_varsp)) return atEnd();
|
||||||
|
if (VL_UNLIKELY(!m_started)) {
|
||||||
|
m_it = m_varsp->begin();
|
||||||
|
m_started = true;
|
||||||
|
} else if (VL_LIKELY(m_it != m_varsp->end())) {
|
||||||
|
++m_it;
|
||||||
|
}
|
||||||
|
for (; m_it != m_varsp->end(); ++m_it) {
|
||||||
|
const char* const name = m_it->second.name();
|
||||||
|
if (std::strncmp(name, m_namePrefix.c_str(), m_namePrefix.length()) != 0) continue;
|
||||||
|
// Only direct members, not grandchildren
|
||||||
|
if (_vl_vpi_find_unescaped_dot(name + m_namePrefix.length())) continue;
|
||||||
|
VerilatedVpioVar* const memberp = m_varp->withMember(&(m_it->second));
|
||||||
|
if (VL_UNLIKELY(!memberp)) continue;
|
||||||
|
return memberp->castVpiHandle();
|
||||||
|
}
|
||||||
|
return atEnd();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class VerilatedVpioModule final : public VerilatedVpioScope {
|
class VerilatedVpioModule final : public VerilatedVpioScope {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit VerilatedVpioModule(const VerilatedScope* modulep)
|
explicit VerilatedVpioModule(const VerilatedScope* modulep)
|
||||||
: VerilatedVpioScope{modulep} {
|
: VerilatedVpioScope{modulep} {
|
||||||
// Look for '.' not inside escaped identifier
|
// Look for '.' not inside escaped identifier
|
||||||
const std::string scopename = m_fullname;
|
if (VL_UNLIKELY(!_vl_vpi_find_unescaped_dot(m_fullname))) m_toplevel = true;
|
||||||
std::string::size_type pos = std::string::npos;
|
|
||||||
size_t i = 0;
|
|
||||||
while (i < scopename.length()) {
|
|
||||||
if (scopename[i] == '\\') {
|
|
||||||
while (i < scopename.length() && scopename[i] != ' ') ++i;
|
|
||||||
++i; // Proc ' ', it should always be there. Then grab '.' on next cycle
|
|
||||||
} else {
|
|
||||||
while (i < scopename.length() && scopename[i] != '.') ++i;
|
|
||||||
if (i < scopename.length()) pos = i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true;
|
|
||||||
}
|
}
|
||||||
// cppcheck-suppress duplInheritedMember
|
// cppcheck-suppress duplInheritedMember
|
||||||
static VerilatedVpioModule* castp(vpiHandle h) {
|
static VerilatedVpioModule* castp(vpiHandle h) {
|
||||||
|
|
@ -1770,6 +1874,8 @@ const char* VerilatedVpiError::strFromVpiObjType(PLI_INT32 vpiVal) VL_PURE {
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
if (VL_UNCOVERABLE(vpiVal < 0)) return names[0];
|
if (VL_UNCOVERABLE(vpiVal < 0)) return names[0];
|
||||||
|
// vpiUnionNet is outside the otherwise contiguous SystemVerilog object type range.
|
||||||
|
if (vpiVal == vpiUnionNet) return "vpiUnionNet";
|
||||||
if (vpiVal <= vpiAutomatics) return names[vpiVal];
|
if (vpiVal <= vpiAutomatics) return names[vpiVal];
|
||||||
if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl)
|
if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl)
|
||||||
return sv_names1[(vpiVal - vpiPackage)];
|
return sv_names1[(vpiVal - vpiPackage)];
|
||||||
|
|
@ -2135,6 +2241,7 @@ void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumVar);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumVar);
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructVar);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructVar);
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionVar);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionVar);
|
||||||
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionNet);
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitVar);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitVar);
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassObj);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassObj);
|
||||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleVar);
|
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleVar);
|
||||||
|
|
@ -2404,6 +2511,162 @@ static bool vl_vpi_parse_indices(std::string& name, std::vector<PLI_INT32>& indi
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const VerilatedScope* _vl_vpi_top_port_scopep(const VerilatedScope* const scopep) {
|
||||||
|
if (VL_UNLIKELY(!scopep)) return nullptr;
|
||||||
|
if (scopep->type() != VerilatedScope::SCOPE_MODULE) return nullptr;
|
||||||
|
if (std::strcmp(scopep->name(), "TOP") == 0) return nullptr;
|
||||||
|
if (_vl_vpi_find_unescaped_dot(scopep->name())) return nullptr;
|
||||||
|
return Verilated::threadContextp()->scopeFind("TOP");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _vl_vpi_find_dotted_var(const std::string& scopename, const std::string& basename,
|
||||||
|
const VerilatedScope*& scopep, const VerilatedVar*& varp,
|
||||||
|
std::string& fullname) {
|
||||||
|
if (scopename.empty()) return false;
|
||||||
|
|
||||||
|
// Unpacked struct/union members are exposed as synthetic vars whose names contain dots
|
||||||
|
// (e.g. "mystruct.member"), so the boundary between the scope and the variable name is
|
||||||
|
// ambiguous. Walk the boundary leftward, moving one scope segment at a time onto the front
|
||||||
|
// of the dotted variable name, and try each candidate scope. An exhausted scope means the
|
||||||
|
// variable lives in the toplevel "TOP" scope.
|
||||||
|
std::string dottedName = basename;
|
||||||
|
std::string dottedScope = scopename;
|
||||||
|
while (true) {
|
||||||
|
const std::string::size_type lastDot = dottedScope.rfind('.');
|
||||||
|
dottedName = (lastDot == std::string::npos ? dottedScope : dottedScope.substr(lastDot + 1))
|
||||||
|
+ "." + dottedName;
|
||||||
|
dottedScope.resize(lastDot == std::string::npos ? 0 : lastDot);
|
||||||
|
scopep = Verilated::threadContextp()->scopeFind(dottedScope.empty() ? "TOP"
|
||||||
|
: dottedScope.c_str());
|
||||||
|
if (scopep) {
|
||||||
|
if (const VerilatedScope* const topScopep = _vl_vpi_top_port_scopep(scopep)) {
|
||||||
|
if (const VerilatedVar* const topVarp = topScopep->varFind(dottedName.c_str())) {
|
||||||
|
scopep = topScopep;
|
||||||
|
varp = topVarp;
|
||||||
|
fullname = dottedScope.empty() ? dottedName : dottedScope + "." + dottedName;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
varp = scopep->varFind(dottedName.c_str());
|
||||||
|
if (varp) return true;
|
||||||
|
}
|
||||||
|
if (lastDot == std::string::npos) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VerilatedVpioVar* _vl_vpi_handle_member_by_name(const std::string& name,
|
||||||
|
const VerilatedVpioVar* vop) {
|
||||||
|
const VerilatedScope* const scopep = vop->scopep();
|
||||||
|
if (VL_UNLIKELY(!scopep)) return nullptr;
|
||||||
|
const std::string memberName = std::string{vop->varp()->name()} + "." + name;
|
||||||
|
const VerilatedVar* const memberVarp = scopep->varFind(memberName.c_str());
|
||||||
|
if (!memberVarp) return nullptr;
|
||||||
|
return vop->withMember(memberVarp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static VerilatedVpioVar* _vl_vpi_handle_apply_indices(VerilatedVpioVar* vop,
|
||||||
|
const std::vector<PLI_INT32>& indices) {
|
||||||
|
for (const PLI_INT32 index : indices) {
|
||||||
|
VerilatedVpioVar* const nextVop = vop->withIndex(index);
|
||||||
|
VL_DO_CLEAR(delete vop, vop = nullptr);
|
||||||
|
if (!nextVop) return nullptr;
|
||||||
|
vop = nextVop;
|
||||||
|
}
|
||||||
|
return vop;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _vl_vpi_parse_optional_indices(std::string& name, std::vector<PLI_INT32>& indices) {
|
||||||
|
indices.clear();
|
||||||
|
if (name.empty()) return false;
|
||||||
|
if (name.back() != ']') return true;
|
||||||
|
|
||||||
|
VlVpiBitRange bitRange;
|
||||||
|
return vl_vpi_parse_indices(name, indices, &bitRange) && !bitRange.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _vl_vpi_split_dotted_name(const std::string& name, std::vector<std::string>& parts) {
|
||||||
|
parts.clear();
|
||||||
|
const char* const namep = name.c_str();
|
||||||
|
const char* beginp = namep;
|
||||||
|
const char* posp = beginp;
|
||||||
|
while ((posp = _vl_vpi_find_unescaped_dot(posp))) {
|
||||||
|
parts.emplace_back(beginp, posp - beginp);
|
||||||
|
beginp = ++posp;
|
||||||
|
}
|
||||||
|
parts.emplace_back(beginp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static VerilatedVpioVar*
|
||||||
|
_vl_vpi_handle_indexed_member_from_scope(const VerilatedScope* const scopep,
|
||||||
|
const std::vector<std::string>& parts,
|
||||||
|
const size_t firstPart) {
|
||||||
|
if (VL_UNLIKELY(!scopep) || VL_UNLIKELY(firstPart >= parts.size())) return nullptr;
|
||||||
|
|
||||||
|
std::string baseName = parts[firstPart];
|
||||||
|
std::vector<PLI_INT32> indices;
|
||||||
|
if (!_vl_vpi_parse_optional_indices(baseName, indices)) return nullptr;
|
||||||
|
|
||||||
|
const VerilatedScope* varScopep = scopep;
|
||||||
|
const VerilatedVar* baseVarp = nullptr;
|
||||||
|
std::string fullnameOverride;
|
||||||
|
if (const VerilatedScope* const topScopep = _vl_vpi_top_port_scopep(scopep)) {
|
||||||
|
if (const VerilatedVar* const topVarp = topScopep->varFind(baseName.c_str())) {
|
||||||
|
varScopep = topScopep;
|
||||||
|
baseVarp = topVarp;
|
||||||
|
fullnameOverride = std::string{scopep->name()} + "." + baseName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!baseVarp) baseVarp = scopep->varFind(baseName.c_str());
|
||||||
|
if (!baseVarp) return nullptr;
|
||||||
|
|
||||||
|
VerilatedVpioVar* baseVop
|
||||||
|
= fullnameOverride.empty()
|
||||||
|
? new VerilatedVpioVar{baseVarp, varScopep}
|
||||||
|
: new VerilatedVpioVar{baseVarp, varScopep, baseVarp->datap(),
|
||||||
|
_vl_vpi_member_local_name(baseVarp->name()),
|
||||||
|
fullnameOverride};
|
||||||
|
VerilatedVpioVar* vop = _vl_vpi_handle_apply_indices(baseVop, indices);
|
||||||
|
if (!vop) return nullptr;
|
||||||
|
|
||||||
|
for (size_t i = firstPart + 1; i < parts.size(); ++i) {
|
||||||
|
std::string memberName = parts[i];
|
||||||
|
if (!_vl_vpi_parse_optional_indices(memberName, indices)) {
|
||||||
|
VL_DO_CLEAR(delete vop, vop = nullptr);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VerilatedVpioVar* const memberVop = _vl_vpi_handle_member_by_name(memberName, vop);
|
||||||
|
VL_DO_CLEAR(delete vop, vop = nullptr);
|
||||||
|
if (!memberVop) return nullptr;
|
||||||
|
|
||||||
|
vop = _vl_vpi_handle_apply_indices(memberVop, indices);
|
||||||
|
if (!vop) return nullptr;
|
||||||
|
}
|
||||||
|
return vop;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VerilatedVpioVar*
|
||||||
|
_vl_vpi_handle_dotted_indexed_member_by_name(const std::string& scopeAndName) {
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
_vl_vpi_split_dotted_name(scopeAndName, parts);
|
||||||
|
if (parts.size() < 2) return nullptr;
|
||||||
|
|
||||||
|
for (size_t firstPart = parts.size() - 1; firstPart > 0; --firstPart) {
|
||||||
|
std::string scopeName = parts[0];
|
||||||
|
for (size_t i = 1; i < firstPart; ++i) scopeName += "." + parts[i];
|
||||||
|
const VerilatedScope* const scopep
|
||||||
|
= Verilated::threadContextp()->scopeFind(scopeName.c_str());
|
||||||
|
if (!scopep) continue;
|
||||||
|
if (VerilatedVpioVar* const vop
|
||||||
|
= _vl_vpi_handle_indexed_member_from_scope(scopep, parts, firstPart)) {
|
||||||
|
return vop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VerilatedScope* const topScopep = Verilated::threadContextp()->scopeFind("TOP");
|
||||||
|
return _vl_vpi_handle_indexed_member_from_scope(topScopep, parts, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// for obtaining handles
|
// for obtaining handles
|
||||||
|
|
||||||
vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||||
|
|
@ -2425,7 +2688,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||||
|
|
||||||
const VerilatedVar* varp = nullptr;
|
const VerilatedVar* varp = nullptr;
|
||||||
const VerilatedScope* scopep;
|
const VerilatedScope* scopep;
|
||||||
|
std::string fullnameOverride;
|
||||||
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
|
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
|
||||||
|
const VerilatedVpioVar* const voVarp = VerilatedVpioVar::castp(scope);
|
||||||
|
|
||||||
if (0 == std::strncmp(scopeAndName.c_str(), "$root.", std::strlen("$root."))) {
|
if (0 == std::strncmp(scopeAndName.c_str(), "$root.", std::strlen("$root."))) {
|
||||||
scopeAndName.erase(0, std::strlen("$root."));
|
scopeAndName.erase(0, std::strlen("$root."));
|
||||||
|
|
@ -2433,6 +2698,12 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||||
const bool scopeIsPackage = VerilatedVpioPackage::castp(scope) != nullptr;
|
const bool scopeIsPackage = VerilatedVpioPackage::castp(scope) != nullptr;
|
||||||
scopeAndName
|
scopeAndName
|
||||||
= std::string{voScopep->fullname()} + (scopeIsPackage ? "" : ".") + scopeAndName;
|
= std::string{voScopep->fullname()} + (scopeIsPackage ? "" : ".") + scopeAndName;
|
||||||
|
} else if (voVarp && voVarp->isStructOrUnion()) {
|
||||||
|
if (VerilatedVpioVar* const memberp
|
||||||
|
= _vl_vpi_handle_member_by_name(scopeAndName, voVarp)) {
|
||||||
|
return memberp->castVpiHandle();
|
||||||
|
}
|
||||||
|
scopeAndName = std::string{voVarp->fullname()} + "." + scopeAndName;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// This doesn't yet follow the hierarchy in the proper way
|
// This doesn't yet follow the hierarchy in the proper way
|
||||||
|
|
@ -2491,8 +2762,18 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||||
}
|
}
|
||||||
if (!varp) {
|
if (!varp) {
|
||||||
scopep = Verilated::threadContextp()->scopeFind(scopename.c_str());
|
scopep = Verilated::threadContextp()->scopeFind(scopename.c_str());
|
||||||
if (!scopep) return nullptr;
|
if (scopep) { varp = scopep->varFind(basename.c_str()); }
|
||||||
varp = scopep->varFind(basename.c_str());
|
// Unpacked struct members are exposed as synthetic variables with dotted names.
|
||||||
|
// Exact unindexed member names can be found directly; indexed member paths need the
|
||||||
|
// component walker so array indices are applied before member offsets.
|
||||||
|
if (!varp
|
||||||
|
&& !_vl_vpi_find_dotted_var(scopename, basename, scopep, varp, fullnameOverride)) {
|
||||||
|
if (VerilatedVpioVar* const memberp
|
||||||
|
= _vl_vpi_handle_dotted_indexed_member_by_name(scopeAndName)) {
|
||||||
|
return memberp->castVpiHandle();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!varp) return nullptr;
|
if (!varp) return nullptr;
|
||||||
|
|
@ -2501,6 +2782,11 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||||
vpiHandle resultHandle;
|
vpiHandle resultHandle;
|
||||||
if (varp->isParam()) {
|
if (varp->isParam()) {
|
||||||
resultHandle = (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
|
resultHandle = (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
|
||||||
|
} else if (!fullnameOverride.empty()) {
|
||||||
|
resultHandle
|
||||||
|
= (new VerilatedVpioVar{varp, scopep, varp->datap(),
|
||||||
|
_vl_vpi_member_local_name(varp->name()), fullnameOverride})
|
||||||
|
->castVpiHandle();
|
||||||
} else {
|
} else {
|
||||||
resultHandle = (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
|
resultHandle = (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
|
||||||
}
|
}
|
||||||
|
|
@ -2642,6 +2928,11 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||||
if (vop) return ((new VerilatedVpioRegIter{vop})->castVpiHandle());
|
if (vop) return ((new VerilatedVpioRegIter{vop})->castVpiHandle());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
case vpiMember: {
|
||||||
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||||
|
if (!vop || !vop->isStructOrUnion()) return nullptr;
|
||||||
|
return ((new VerilatedVpioMemberIter{vop})->castVpiHandle());
|
||||||
|
}
|
||||||
case vpiParameter: {
|
case vpiParameter: {
|
||||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||||
|
|
@ -2734,6 +3025,11 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
|
||||||
if (VL_UNLIKELY(!vop)) return vpiUndefined;
|
if (VL_UNLIKELY(!vop)) return vpiUndefined;
|
||||||
return vop->varp()->isSigned();
|
return vop->varp()->isSigned();
|
||||||
}
|
}
|
||||||
|
case vpiPacked: {
|
||||||
|
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
||||||
|
if (VL_LIKELY(vop && vop->isStructOrUnion())) return 0;
|
||||||
|
[[fallthrough]];
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
|
||||||
__func__, VerilatedVpiError::strFromVpiProp(property));
|
__func__, VerilatedVpiError::strFromVpiProp(property));
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@
|
||||||
breathe==4.36.0
|
breathe==4.36.0
|
||||||
compiledb==0.10.7
|
compiledb==0.10.7
|
||||||
distro==1.9.0
|
distro==1.9.0
|
||||||
|
docstrfmt==2.2.0
|
||||||
gersemi==0.23.1
|
gersemi==0.23.1
|
||||||
mbake==1.4.3
|
mbake==1.4.3
|
||||||
|
mdformat==1.0.0
|
||||||
mypy==1.19.0
|
mypy==1.19.0
|
||||||
pylint==3.0.2
|
pylint==3.0.2
|
||||||
ruff==0.14.8
|
ruff==0.14.8
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,241 @@
|
||||||
|
<!-- DESCRIPTION: Verilator: src/ guidelines for AI coding agents
|
||||||
|
SPDX-FileCopyrightText: 2026-2026 Wilson Snyder
|
||||||
|
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -->
|
||||||
|
|
||||||
|
# src/ -- Verilator compiler sources
|
||||||
|
|
||||||
|
Covers all C++ under `src/`, including astgen inputs and the parser/lexer
|
||||||
|
(`verilog.y`, `verilog.l`). Read the repository-root [AGENTS.md](../AGENTS.md)
|
||||||
|
first. This file has two parts: **Orientation** explains the AST and pass model;
|
||||||
|
**Before you open a PR** is the style and correctness checklist.
|
||||||
|
|
||||||
|
______________________________________________________________________
|
||||||
|
|
||||||
|
# Orientation: the AST and the visitor model
|
||||||
|
|
||||||
|
- **Everything is an `AstNode`.** Each construct is an `Ast*` subclass (`AstAdd`,
|
||||||
|
`AstVar`, `AstIf`). The design under analysis is one tree, with statement lists
|
||||||
|
threaded by sibling `nextp()` links.
|
||||||
|
- **Children sit in numbered slots `op1p()`..`op4p()`, accessed by name.** Use the
|
||||||
|
named accessors (`lhsp()`, `condp()`, `thensp()`), not the raw slots -- the
|
||||||
|
numbering is an implementation detail.
|
||||||
|
- **`astgen` generates node boilerplate.** Declare children and cross-node
|
||||||
|
pointers with `@astgen op` / `@astgen ptr` annotations in the `V3AstNode*.h`
|
||||||
|
headers; it emits accessors, clone, and broken-check code. Do not hand-write
|
||||||
|
what astgen can generate.
|
||||||
|
- **A pass is a visitor.** Convention: a class with a private constructor and a
|
||||||
|
static `apply()` entry point, named after its file (`TimingSuspendableVisitor`
|
||||||
|
in `V3Timing.cpp`). It walks the tree through `visit(AstFoo*)` handlers and
|
||||||
|
`iterateChildren()`. To understand a pass, read its top-of-file comment first --
|
||||||
|
every `.cpp` opens with one describing the algorithm.
|
||||||
|
- **Scratch state lives on nodes.** Passes stash data in `user1p()`..`user5p()`
|
||||||
|
(and `user1()`..`user5()`), claimed for the pass lifetime with a `VNUser*InUse`
|
||||||
|
guard. Save and restore visitor members across recursion with `VL_RESTORER`.
|
||||||
|
- **Three downcasts, three null behaviors:** `VN_IS` returns bool, `VN_CAST`
|
||||||
|
returns nullptr on mismatch, `VN_AS` asserts the type. `V3Broken` re-validates
|
||||||
|
tree invariants between passes, so trust resolved pointers (`dtypep()`,
|
||||||
|
`varp()`) instead of adding defensive null checks for impossible cases.
|
||||||
|
- `docs/internals.rst` is the authoritative reference for the AST, the pass list,
|
||||||
|
and node lifetime.
|
||||||
|
|
||||||
|
______________________________________________________________________
|
||||||
|
|
||||||
|
# Before you open a PR
|
||||||
|
|
||||||
|
## Code style
|
||||||
|
|
||||||
|
- Mark every variable, parameter, pointer, and member function `const` where possible.
|
||||||
|
- Pointer variables take a `p` suffix and pointer locals are doubly const where
|
||||||
|
possible: `AstVar* const varp`; non-pointers never use the `p` suffix.
|
||||||
|
- Do not use `auto` except for iterators or genuinely unwieldy types.
|
||||||
|
- Use pre-increment (`++i`) unless you specifically need post-increment's old value.
|
||||||
|
- Brace-initialize node and struct construction: `new AstIf{fl, condp, thenp, elsep}`.
|
||||||
|
- Never use C-style casts; instead use `static_cast<T>` for non-AST types and
|
||||||
|
`VN_AS`/`VN_CAST` for AST downcasts.
|
||||||
|
- `static constexpr` for compile-time constants, not `#define` or file-scope const.
|
||||||
|
- Mark every `class`/`struct` `final` or `VL_NOT_FINAL` -- a distribution test
|
||||||
|
scans all definitions.
|
||||||
|
- Keep functions under roughly 100-150 lines; thread shared state through a
|
||||||
|
context struct rather than long parameter lists.
|
||||||
|
- Keep headers lean: move implementation to `.cpp`; convert large lambdas into
|
||||||
|
named member functions -- lambdas are opaque in stack traces and block reuse.
|
||||||
|
- Start every new `.cpp` with a top-of-file comment explaining the algorithm.
|
||||||
|
- Comments are capitalized sentences written for an unknown future reader, without
|
||||||
|
"I"/"we"/"our"; remove commented-out code -- version control preserves history.
|
||||||
|
- No `using namespace`; prefix non-namespaced symbols with `VL`/`Vl`.
|
||||||
|
- Prefer semantic predicates over enum comparisons: `varp->isClassMember()`, not
|
||||||
|
`varp->varType() == VVarType::MEMBER`.
|
||||||
|
- `new*` functions return a `new` object; `make*` functions do something more
|
||||||
|
complex -- pick the prefix accordingly.
|
||||||
|
- Name compiler-created temporaries with a `__V` prefix plus a context suffix
|
||||||
|
(`__VInside`, `__VCase`); runtime utility functions use a `vl_` prefix.
|
||||||
|
- Use `VL_*` bit/word macros from `verilatedos.h` (`VL_WORDS_I`, `VL_MASK_I`); do
|
||||||
|
not include `<cstdint>` directly.
|
||||||
|
- Replace magic numbers with named `static constexpr` constants.
|
||||||
|
|
||||||
|
## AST construction and manipulation
|
||||||
|
|
||||||
|
- Build logic as AST nodes, never as raw C text in `AstCStmt` -- later passes
|
||||||
|
(V3Name, `--protect`) rename AST identifiers but cannot see into raw strings.
|
||||||
|
|
||||||
|
- Know the cast forms (above) and never pair `VN_IS` with `VN_AS` on the same
|
||||||
|
node -- use a single `VN_CAST`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// BAD: redundant double check
|
||||||
|
if (VN_IS(nodep, VarRef)) { AstVarRef* const refp = VN_AS(nodep, VarRef); }
|
||||||
|
// GOOD: single conditional cast
|
||||||
|
if (const AstVarRef* const refp = VN_CAST(nodep, VarRef)) { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
- Use `UASSERT_OBJ(cond, nodep, "...")` over `UASSERT` when a node is in scope;
|
||||||
|
use `v3fatalSrc("...")` for unreachable paths, never a silent `if (!ptr) return;`.
|
||||||
|
|
||||||
|
- Use `VL_DO_DANGLING(pushDeletep(nodep), nodep)` instead of `deleteTree()` in
|
||||||
|
visitors -- deferred deletion is safe against re-entry and unlinking order.
|
||||||
|
`deleteTree()` is only for fresh nodes that never entered the tree.
|
||||||
|
|
||||||
|
- Every new AST member needs both `dump()` and `dumpJson()` support -- never wrap
|
||||||
|
these in `LCOV_EXCL`; cover them by adding a construct to `t_debug_emitv.v`.
|
||||||
|
Override `isSame()` to include any new semantically meaningful field.
|
||||||
|
|
||||||
|
- Pointers to nodes outside op1p-op4p require a `broken()` override and
|
||||||
|
`cloneRelink()` support -- avoid storing out-of-tree node pointers when possible.
|
||||||
|
|
||||||
|
- Never allocate AstNode objects on the stack or by value -- always pointers.
|
||||||
|
|
||||||
|
- Prefer a new `visit()` in an existing visitor over `nodep->foreach(...)` --
|
||||||
|
better for runtime, and handles diverse node types better. Prefer named
|
||||||
|
accessors over `op1p()`..`op4p()`, and verify traversal order is preserved
|
||||||
|
when converting manual iteration.
|
||||||
|
|
||||||
|
- Prefer `AstForeach` over generating unrolled loop bodies -- constant-size code
|
||||||
|
instead of O(N); wrap the body in `AstBegin` for scope isolation.
|
||||||
|
|
||||||
|
- Always `skipRefp()` when comparing or resolving dtypes -- missing it breaks
|
||||||
|
typedef support; prove the paths with typedef tests.
|
||||||
|
|
||||||
|
- Use `num().isOpaque()` rather than `isDouble() || isString()` when guarding
|
||||||
|
V3Number comparisons against non-integer types.
|
||||||
|
|
||||||
|
- Use `FileLine::operatorCompare` for source-position ordering -- never hand-roll
|
||||||
|
filename/lineno comparisons.
|
||||||
|
|
||||||
|
- Identify compiler-generated constructs by an attribute flag on the node (with
|
||||||
|
dump support), never by name-pattern matching -- magic names break with escaped
|
||||||
|
identifiers.
|
||||||
|
|
||||||
|
- Use `V3Number` arithmetic for `AstConst` values wider than 32 bits -- `1 << i`
|
||||||
|
silently overflows at `i >= 32`.
|
||||||
|
|
||||||
|
- Use `VMemberMap`/`findMember()` for name lookups in structs, classes, modules,
|
||||||
|
and packages -- O(1) versus quadratic linear scans.
|
||||||
|
|
||||||
|
- Never emit raw source filenames or identifiers in generated code -- pass them
|
||||||
|
through `protect()`/`putsQuoted` so `--protect` does not leak source.
|
||||||
|
|
||||||
|
## Visitors and passes
|
||||||
|
|
||||||
|
- `VL_RESTORER` on every visitor member a `visit()` modifies before iterating
|
||||||
|
children -- even when nesting "cannot happen" today.
|
||||||
|
- Every pass using `userNp()` needs a `VNUserNInUse` guard, and the header
|
||||||
|
documents which user fields it uses.
|
||||||
|
- Use `iterateAndNextNull()` rather than `iterate()` -- the null-safe form
|
||||||
|
prevents copy-paste errors during refactors.
|
||||||
|
- Derive read-only visitors from `VNVisitorConst` with `iterateChildrenConst`.
|
||||||
|
- Reset per-module visitor state in `visit(AstNodeModule*)`, including numeric ID
|
||||||
|
counters, to keep generated names stable.
|
||||||
|
- Capture first-occurrence module state inside the node's own `visit()` handler,
|
||||||
|
not via a `foreach` pre-scan from `visit(AstNodeModule)` -- source order already
|
||||||
|
matches IEEE declaration-before-use.
|
||||||
|
- Avoid `backp()` -- it returns parent or prior sibling depending on position and
|
||||||
|
causes O(n^2) hunts; build maps or capture context during forward traversal.
|
||||||
|
- When raw node pointers key a map or set, erase entries when the node is deleted
|
||||||
|
-- allocators reuse addresses, so stale entries alias new nodes.
|
||||||
|
- Derive graph-shaped passes from V3Graph (`V3GraphVertex`/`V3GraphEdge`) -- it
|
||||||
|
gives dump, color, rank, topological sort, and reachability for free.
|
||||||
|
- When a change outgrows local rewrites, create a dedicated pass instead of
|
||||||
|
growing an existing one.
|
||||||
|
- State explicitly how side effects are preserved in optimizations involving
|
||||||
|
purity, expression lifting, or simplification.
|
||||||
|
|
||||||
|
## Errors and warnings
|
||||||
|
|
||||||
|
(See the root checklist for choosing the diagnostic API and its required test.)
|
||||||
|
|
||||||
|
- Append `nodep->prettyNameQ()` for user-facing names; use `name()` only in
|
||||||
|
debug/UINFO output. Enclose specific values in single quotes: `'value'`.
|
||||||
|
- End messages with periods, never exclamation marks; do not write "Error:" in the
|
||||||
|
text -- the macro prints the prefix.
|
||||||
|
- State what was attempted and what was found: "Instance attempts to connect to
|
||||||
|
'PARAM' as a parameter, but it is a variable". Add a `warnMore()` suggestion
|
||||||
|
where possible.
|
||||||
|
- Name warning codes object-first and short (`ASCRANGE`, not `RANGEASC`); rename
|
||||||
|
via `renamedTo()` so old suppressions keep working.
|
||||||
|
- Set warning suppression on `AstVar`, not `AstVarRef` -- VarRefs get recreated
|
||||||
|
and lose `warnIsOff`.
|
||||||
|
- "Unsupported:" messages describe the specific unsupported context, not just the
|
||||||
|
construct name -- each message must be distinct.
|
||||||
|
- When replacing or refactoring a pass, keep existing user-facing error strings --
|
||||||
|
`.out` goldens and documentation depend on the wording.
|
||||||
|
|
||||||
|
## Performance and memory
|
||||||
|
|
||||||
|
- O(n^2) is never acceptable -- build maps for batch lookups; any quadratic loop
|
||||||
|
needs explicit justification in a comment.
|
||||||
|
- Prefer `std::map` for per-module structures (many small instances); use
|
||||||
|
`unordered_map` only for one-per-netlist data, and never let `unordered_*`
|
||||||
|
iteration order reach generated output.
|
||||||
|
- Prefer `emplace` over `insert` and check the returned `.second` instead of a
|
||||||
|
separate `find()`. `reserve()` strings and vectors when the size is estimable.
|
||||||
|
- Add no new static or global mutable data -- statics are being eliminated for
|
||||||
|
future parallelism.
|
||||||
|
- Use Verilator's fixed-width data types for model data (`CData`/`SData`/`IData`/
|
||||||
|
`QData`/`VlWide`), not `size_t`. Process wide data word-by-word
|
||||||
|
(`VL_ZERO_W`, `VL_MEMCPY_W`), never bit-by-bit.
|
||||||
|
- No exceptions in verilated runtime code; do string parsing at verilation time,
|
||||||
|
never during simulation.
|
||||||
|
- Wrap unlikely hot-path branches in `VL_UNLIKELY`/`VL_LIKELY`.
|
||||||
|
- Count what every new pass does via V3Stats -- stats become deterministic
|
||||||
|
regression anchors.
|
||||||
|
|
||||||
|
## Thread safety
|
||||||
|
|
||||||
|
- Annotate with the hierarchy `VL_PURE` > `VL_MT_SAFE` > `VL_MT_STABLE`: PURE has
|
||||||
|
no side effects and calls only PURE; MT_SAFE is safe under locks; MT_STABLE is
|
||||||
|
safe only while tree topology is stable. Annotations must match the
|
||||||
|
implementation.
|
||||||
|
- Never include `verilated.h` in the compiler itself -- use `verilatedos.h`.
|
||||||
|
- Annotate mutex-protected members with `VL_GUARDED_BY` and document acquisition
|
||||||
|
ordering. `++` on shared state and container `empty()` are not thread-safe.
|
||||||
|
|
||||||
|
## Parser and lexer (verilog.y, verilog.l)
|
||||||
|
|
||||||
|
- Preserve IEEE Appendix A BNF comments (`// IEEE: {rule}`); comment explicitly
|
||||||
|
when accepting syntax beyond IEEE as an extension.
|
||||||
|
- The parser only builds AST nodes -- defer semantic validation, `VN_IS` checks,
|
||||||
|
and context-dependent logic to V3LinkParse/V3Width and later passes.
|
||||||
|
- Represent hierarchical paths as structured nodes (`AstDot`/parse-ref chains via
|
||||||
|
`idDotted`), never concatenated strings -- preserves per-segment FileLine.
|
||||||
|
- Prefer tightening a grammar rule's operand type over a runtime cast-chain guard
|
||||||
|
in a later visitor -- illegal operands then fail with a clean syntax error.
|
||||||
|
- Solve ambiguities with token-pipeline look-ahead (`tokenPipeScan*`) rather than
|
||||||
|
limiting grammar rules; mark unsupported rules with `//UNSUP`.
|
||||||
|
- Sort token declarations alphabetically by string literal; sort `yD_*`
|
||||||
|
productions by token name.
|
||||||
|
- Add a test for every `|` alternative and optional clause of a new or changed
|
||||||
|
grammar rule -- untested alternatives are where parse regressions hide.
|
||||||
|
|
||||||
|
## File-specific rules
|
||||||
|
|
||||||
|
| File | Rule |
|
||||||
|
|---|---|
|
||||||
|
| `src/V3Options.cpp` | Chain `.notForRerun()` onto `DECL_OPTION()` for options that do not affect semantic output |
|
||||||
|
| `src/V3Ast.cpp` | For composite types (queues, dynamic arrays) use `computeCastableImp()` on subtypes -- shallow `width()`/`similarDType()` checks miss nested incompatibility |
|
||||||
|
| `src/V3AstNode*.h` | Every node class gets a what-construct comment and every member a semantic-purpose comment; put enum type definitions in `V3AstAttr.h` |
|
||||||
|
| `src/V3AstNodeExpr.h` | `CCast` is only for basic C types (char/short/int/QData) -- never 4-state logic or structs |
|
||||||
|
| `src/V3AstNodeOther.h` | `cloneRelink` must propagate all stateful flags (e.g. `maybePointedTo`) and update internal references |
|
||||||
|
| `src/V3Const.cpp` | Check `keepIfEmpty` before removing empty functions -- flagged functions must survive for codegen/side effects |
|
||||||
|
| `src/V3Coverage.cpp` | Instrumentation contexts are opt-in (allowlist), never blocklist -- blocklists silently break when new contexts appear |
|
||||||
|
| `src/V3Inline.cpp` | Preserve `VarXRef::varp()` during passes -- pin-reconnection needs it before V3LinkDot re-resolves |
|
||||||
|
| `src/V3Sched*.cpp` | Every change needs a test proving necessity; isolate unrelated scheduler changes into separate PRs -- high-risk area |
|
||||||
|
|
@ -51,6 +51,7 @@ set(HEADERS
|
||||||
V3AstNodeExpr.h
|
V3AstNodeExpr.h
|
||||||
V3AstNodeOther.h
|
V3AstNodeOther.h
|
||||||
V3AstNodeStmt.h
|
V3AstNodeStmt.h
|
||||||
|
V3AstPatterns.h
|
||||||
V3AstUserAllocator.h
|
V3AstUserAllocator.h
|
||||||
V3Begin.h
|
V3Begin.h
|
||||||
V3Branch.h
|
V3Branch.h
|
||||||
|
|
@ -175,7 +176,6 @@ set(HEADERS
|
||||||
V3Simulate.h
|
V3Simulate.h
|
||||||
V3Slice.h
|
V3Slice.h
|
||||||
V3Split.h
|
V3Split.h
|
||||||
V3SplitAs.h
|
|
||||||
V3SplitVar.h
|
V3SplitVar.h
|
||||||
V3StackCount.h
|
V3StackCount.h
|
||||||
V3Stats.h
|
V3Stats.h
|
||||||
|
|
@ -183,7 +183,6 @@ set(HEADERS
|
||||||
V3String.h
|
V3String.h
|
||||||
V3Subst.h
|
V3Subst.h
|
||||||
V3SymTable.h
|
V3SymTable.h
|
||||||
V3TSP.h
|
|
||||||
V3Table.h
|
V3Table.h
|
||||||
V3Task.h
|
V3Task.h
|
||||||
V3ThreadPool.h
|
V3ThreadPool.h
|
||||||
|
|
@ -203,7 +202,6 @@ set(HEADERS
|
||||||
V3WidthCommit.h
|
V3WidthCommit.h
|
||||||
V3WidthRemove.h
|
V3WidthRemove.h
|
||||||
VlcBucket.h
|
VlcBucket.h
|
||||||
VlcCovergroup.h
|
|
||||||
VlcOptions.h
|
VlcOptions.h
|
||||||
VlcPoint.h
|
VlcPoint.h
|
||||||
VlcSource.h
|
VlcSource.h
|
||||||
|
|
@ -253,7 +251,6 @@ set(COMMON_SOURCES
|
||||||
V3DfgDataType.cpp
|
V3DfgDataType.cpp
|
||||||
V3DfgDecomposition.cpp
|
V3DfgDecomposition.cpp
|
||||||
V3DfgDfgToAst.cpp
|
V3DfgDfgToAst.cpp
|
||||||
V3DfgDumpPatterns.cpp
|
|
||||||
V3DfgOptimizer.cpp
|
V3DfgOptimizer.cpp
|
||||||
V3DfgPasses.cpp
|
V3DfgPasses.cpp
|
||||||
V3DfgPeephole.cpp
|
V3DfgPeephole.cpp
|
||||||
|
|
@ -330,6 +327,7 @@ set(COMMON_SOURCES
|
||||||
V3ParseGrammar.cpp
|
V3ParseGrammar.cpp
|
||||||
V3ParseImp.cpp
|
V3ParseImp.cpp
|
||||||
V3ParseLex.cpp
|
V3ParseLex.cpp
|
||||||
|
V3PatternStats.cpp
|
||||||
V3PreProc.cpp
|
V3PreProc.cpp
|
||||||
V3PreShell.cpp
|
V3PreShell.cpp
|
||||||
V3Premit.cpp
|
V3Premit.cpp
|
||||||
|
|
@ -351,14 +349,12 @@ set(COMMON_SOURCES
|
||||||
V3Scoreboard.cpp
|
V3Scoreboard.cpp
|
||||||
V3Slice.cpp
|
V3Slice.cpp
|
||||||
V3Split.cpp
|
V3Split.cpp
|
||||||
V3SplitAs.cpp
|
|
||||||
V3SplitVar.cpp
|
V3SplitVar.cpp
|
||||||
V3StackCount.cpp
|
V3StackCount.cpp
|
||||||
V3Stats.cpp
|
V3Stats.cpp
|
||||||
V3StatsReport.cpp
|
V3StatsReport.cpp
|
||||||
V3String.cpp
|
V3String.cpp
|
||||||
V3Subst.cpp
|
V3Subst.cpp
|
||||||
V3TSP.cpp
|
|
||||||
V3Table.cpp
|
V3Table.cpp
|
||||||
V3Task.cpp
|
V3Task.cpp
|
||||||
V3ThreadPool.cpp
|
V3ThreadPool.cpp
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,6 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||||
V3DfgDataType.o \
|
V3DfgDataType.o \
|
||||||
V3DfgDecomposition.o \
|
V3DfgDecomposition.o \
|
||||||
V3DfgDfgToAst.o \
|
V3DfgDfgToAst.o \
|
||||||
V3DfgDumpPatterns.o \
|
|
||||||
V3DfgOptimizer.o \
|
V3DfgOptimizer.o \
|
||||||
V3DfgPasses.o \
|
V3DfgPasses.o \
|
||||||
V3DfgPeephole.o \
|
V3DfgPeephole.o \
|
||||||
|
|
@ -314,6 +313,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||||
V3OrderProcessDomains.o \
|
V3OrderProcessDomains.o \
|
||||||
V3OrderSerial.o \
|
V3OrderSerial.o \
|
||||||
V3Param.o \
|
V3Param.o \
|
||||||
|
V3PatternStats.o \
|
||||||
V3Premit.o \
|
V3Premit.o \
|
||||||
V3ProtectLib.o \
|
V3ProtectLib.o \
|
||||||
V3RandSequence.o \
|
V3RandSequence.o \
|
||||||
|
|
@ -333,11 +333,9 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||||
V3Scoreboard.o \
|
V3Scoreboard.o \
|
||||||
V3Slice.o \
|
V3Slice.o \
|
||||||
V3Split.o \
|
V3Split.o \
|
||||||
V3SplitAs.o \
|
|
||||||
V3SplitVar.o \
|
V3SplitVar.o \
|
||||||
V3StackCount.o \
|
V3StackCount.o \
|
||||||
V3Subst.o \
|
V3Subst.o \
|
||||||
V3TSP.o \
|
|
||||||
V3Table.o \
|
V3Table.o \
|
||||||
V3Task.o \
|
V3Task.o \
|
||||||
V3Timing.o \
|
V3Timing.o \
|
||||||
|
|
|
||||||
|
|
@ -351,7 +351,7 @@ public:
|
||||||
|
|
||||||
class ActiveDlyVisitor final : public VNVisitor {
|
class ActiveDlyVisitor final : public VNVisitor {
|
||||||
public:
|
public:
|
||||||
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL, CT_SUSPENDABLE };
|
enum CheckType : uint8_t { CT_COMB, CT_FINAL };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// MEMBERS
|
// MEMBERS
|
||||||
|
|
@ -359,15 +359,9 @@ private:
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
void visit(AstAssignDly* nodep) override {
|
void visit(AstAssignDly* nodep) override {
|
||||||
// Non-blocking assignments are OK in sequential processes
|
|
||||||
if (m_check == CT_SEQ || m_check == CT_SUSPENDABLE) return;
|
|
||||||
|
|
||||||
// Issue appropriate warning
|
// Issue appropriate warning
|
||||||
if (m_check == CT_INITIAL) {
|
if (m_check == CT_FINAL) {
|
||||||
nodep->v3warn(INITIALDLY,
|
nodep->v3warn(FINALDLY, "Non-blocking assignment '<=' in final block");
|
||||||
"Non-blocking assignment '<=' in initial/final block\n"
|
|
||||||
<< nodep->warnMore()
|
|
||||||
<< "... This will be executed as a blocking assignment '='!");
|
|
||||||
} else {
|
} else {
|
||||||
nodep->v3warn(COMBDLY,
|
nodep->v3warn(COMBDLY,
|
||||||
"Non-blocking assignment '<=' in combinational logic process\n"
|
"Non-blocking assignment '<=' in combinational logic process\n"
|
||||||
|
|
@ -465,11 +459,7 @@ class ActiveVisitor final : public VNVisitor {
|
||||||
wantactivep->addStmtsp(nodep);
|
wantactivep->addStmtsp(nodep);
|
||||||
|
|
||||||
// Warn and convert any delayed assignments
|
// Warn and convert any delayed assignments
|
||||||
{
|
if (!m_clockedProcess) ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB};
|
||||||
ActiveDlyVisitor{nodep, !m_clockedProcess ? ActiveDlyVisitor::CT_COMB
|
|
||||||
: oldsentreep ? ActiveDlyVisitor::CT_SEQ
|
|
||||||
: ActiveDlyVisitor::CT_SUSPENDABLE};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete sensitivity list
|
// Delete sensitivity list
|
||||||
if (oldsentreep) VL_DO_DANGLING(oldsentreep->deleteTree(), oldsentreep);
|
if (oldsentreep) VL_DO_DANGLING(oldsentreep->deleteTree(), oldsentreep);
|
||||||
|
|
@ -509,17 +499,11 @@ class ActiveVisitor final : public VNVisitor {
|
||||||
|
|
||||||
void visit(AstInitialStatic* nodep) override { moveUnderSpecial<AstSenItem::Static>(nodep); }
|
void visit(AstInitialStatic* nodep) override { moveUnderSpecial<AstSenItem::Static>(nodep); }
|
||||||
void visit(AstInitial* nodep) override {
|
void visit(AstInitial* nodep) override {
|
||||||
const bool timedInitial
|
|
||||||
= v3Global.opt.timing().isSetTrue() && nodep->exists([](const AstNode* const subp) {
|
|
||||||
return VN_IS(subp, Delay) || VN_IS(subp, EventControl);
|
|
||||||
});
|
|
||||||
const ActiveDlyVisitor dlyvisitor{nodep, timedInitial ? ActiveDlyVisitor::CT_SUSPENDABLE
|
|
||||||
: ActiveDlyVisitor::CT_INITIAL};
|
|
||||||
visitSenItems(nodep);
|
visitSenItems(nodep);
|
||||||
moveUnderSpecial<AstSenItem::Initial>(nodep);
|
moveUnderSpecial<AstSenItem::Initial>(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstFinal* nodep) override {
|
void visit(AstFinal* nodep) override {
|
||||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_FINAL};
|
||||||
moveUnderSpecial<AstSenItem::Final>(nodep);
|
moveUnderSpecial<AstSenItem::Final>(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstCoverToggle* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
|
void visit(AstCoverToggle* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
|
||||||
|
|
|
||||||
331
src/V3Assert.cpp
331
src/V3Assert.cpp
|
|
@ -23,6 +23,79 @@
|
||||||
|
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class DefaultDisableLocalVisitor final : public VNVisitor {
|
||||||
|
// STATE
|
||||||
|
AstNode* m_scopep = nullptr;
|
||||||
|
|
||||||
|
// VISITORS
|
||||||
|
void visit(AstNodeModule* nodep) override {
|
||||||
|
VL_RESTORER(m_scopep);
|
||||||
|
m_scopep = nodep;
|
||||||
|
nodep->defaultDisablep(nullptr);
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
|
void visit(AstGenBlock* nodep) override {
|
||||||
|
VL_RESTORER(m_scopep);
|
||||||
|
m_scopep = nodep;
|
||||||
|
nodep->defaultDisablep(nullptr);
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
|
void visit(AstDefaultDisable* nodep) override {
|
||||||
|
UASSERT_OBJ(nodep, m_scopep,
|
||||||
|
"default disable iff must be inside a module or generate block");
|
||||||
|
AstDefaultDisable* defaultp = nullptr;
|
||||||
|
if (const AstNodeModule* const modp = VN_CAST(m_scopep, NodeModule)) {
|
||||||
|
defaultp = modp->defaultDisablep();
|
||||||
|
} else {
|
||||||
|
defaultp = VN_AS(m_scopep, GenBlock)->defaultDisablep();
|
||||||
|
}
|
||||||
|
if (VL_UNLIKELY(defaultp)) {
|
||||||
|
nodep->v3error("Only one 'default disable iff' allowed per "
|
||||||
|
<< (VN_IS(m_scopep, NodeModule) ? "module" : "generate block")
|
||||||
|
<< " (IEEE 1800-2023 16.15)");
|
||||||
|
} else if (AstNodeModule* const modp = VN_CAST(m_scopep, NodeModule)) {
|
||||||
|
modp->defaultDisablep(nodep);
|
||||||
|
} else {
|
||||||
|
VN_AS(m_scopep, GenBlock)->defaultDisablep(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DefaultDisableLocalVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class DefaultDisablePropagateVisitor final : public VNVisitor {
|
||||||
|
// STATE
|
||||||
|
AstDefaultDisable* m_defaultDisablep = nullptr;
|
||||||
|
|
||||||
|
// VISITORS
|
||||||
|
void visit(AstNodeModule* nodep) override {
|
||||||
|
VL_RESTORER(m_defaultDisablep);
|
||||||
|
m_defaultDisablep = nodep->defaultDisablep();
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
|
void visit(AstGenBlock* nodep) override {
|
||||||
|
VL_RESTORER(m_defaultDisablep);
|
||||||
|
if (!nodep->defaultDisablep()) nodep->defaultDisablep(m_defaultDisablep);
|
||||||
|
m_defaultDisablep = nodep->defaultDisablep();
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DefaultDisablePropagateVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) {
|
||||||
|
{ DefaultDisableLocalVisitor{nodep}; }
|
||||||
|
{ DefaultDisablePropagateVisitor{nodep}; }
|
||||||
|
}
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// AssertDeFutureVisitor
|
// AssertDeFutureVisitor
|
||||||
// If any AstFuture, then move all non-future varrefs to be one cycle behind,
|
// If any AstFuture, then move all non-future varrefs to be one cycle behind,
|
||||||
|
|
@ -153,12 +226,23 @@ class AssertVisitor final : public VNVisitor {
|
||||||
bool m_inRestrict = false; // True inside restrict assertion
|
bool m_inRestrict = false; // True inside restrict assertion
|
||||||
AstNode* m_passsp = nullptr; // Current pass statement
|
AstNode* m_passsp = nullptr; // Current pass statement
|
||||||
AstNode* m_failsp = nullptr; // Current fail statement
|
AstNode* m_failsp = nullptr; // Current fail statement
|
||||||
|
AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion
|
||||||
AstFinal* m_finalp = nullptr; // Current final block
|
AstFinal* m_finalp = nullptr; // Current final block
|
||||||
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
|
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
|
||||||
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
|
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
|
||||||
m_modExpr2Sen2DelayedAlwaysp;
|
m_modExpr2Sen2DelayedAlwaysp;
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
static string assertCtlGetCall(const char* query, VAssertType type,
|
||||||
|
VAssertDirectiveType directiveType) {
|
||||||
|
return "vlSymsp->_vm_contextp__->assertCtlGet(VerilatedAssertCtlQuery::"s + query + ", "s
|
||||||
|
+ std::to_string(type) + ", "s + std::to_string(directiveType) + ")"s;
|
||||||
|
}
|
||||||
|
static const char* assertPassOnQuery(bool vacuous) {
|
||||||
|
static constexpr const char* queries[2]
|
||||||
|
= {"ASSERT_CTL_PASS_ON_NONVACUOUS", "ASSERT_CTL_PASS_ON_VACUOUS"};
|
||||||
|
return queries[vacuous];
|
||||||
|
}
|
||||||
static AstNodeExpr* assertOnCond(FileLine* fl, VAssertType type,
|
static AstNodeExpr* assertOnCond(FileLine* fl, VAssertType type,
|
||||||
VAssertDirectiveType directiveType) {
|
VAssertDirectiveType directiveType) {
|
||||||
// cppcheck-suppress missingReturn
|
// cppcheck-suppress missingReturn
|
||||||
|
|
@ -178,9 +262,7 @@ class AssertVisitor final : public VNVisitor {
|
||||||
case VAssertDirectiveType::ASSUME: {
|
case VAssertDirectiveType::ASSUME: {
|
||||||
if (v3Global.opt.assertOn()) {
|
if (v3Global.opt.assertOn()) {
|
||||||
return new AstCExpr{fl, AstCExpr::Pure{},
|
return new AstCExpr{fl, AstCExpr::Pure{},
|
||||||
"vlSymsp->_vm_contextp__->assertOnGet("s + std::to_string(type)
|
assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
|
||||||
+ ", "s + std::to_string(directiveType) + ")"s,
|
|
||||||
1};
|
|
||||||
}
|
}
|
||||||
return new AstConst{fl, AstConst::BitFalse{}};
|
return new AstConst{fl, AstConst::BitFalse{}};
|
||||||
}
|
}
|
||||||
|
|
@ -196,6 +278,31 @@ class AssertVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
VL_UNREACHABLE;
|
VL_UNREACHABLE;
|
||||||
}
|
}
|
||||||
|
static string assertActionControlPrefix(VAssertDirectiveType directiveType) {
|
||||||
|
const int controlled = !!(static_cast<int>(directiveType)
|
||||||
|
& (static_cast<int>(VAssertDirectiveType::ASSERT)
|
||||||
|
| static_cast<int>(VAssertDirectiveType::COVER)
|
||||||
|
| static_cast<int>(VAssertDirectiveType::ASSUME)));
|
||||||
|
const int checkRuntime = controlled & static_cast<int>(v3Global.opt.assertOn());
|
||||||
|
return "("s + std::to_string(controlled ^ 1) + " || ("s + std::to_string(checkRuntime)
|
||||||
|
+ " && "s;
|
||||||
|
}
|
||||||
|
static AstNodeExpr* assertPassOnCond(FileLine* fl, VAssertType type,
|
||||||
|
VAssertDirectiveType directiveType, bool vacuous) {
|
||||||
|
return new AstCExpr{fl, AstCExpr::Pure{},
|
||||||
|
assertActionControlPrefix(directiveType)
|
||||||
|
+ assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType)
|
||||||
|
+ "))"s,
|
||||||
|
1};
|
||||||
|
}
|
||||||
|
static AstNodeExpr* assertFailOnCond(FileLine* fl, VAssertType type,
|
||||||
|
VAssertDirectiveType directiveType) {
|
||||||
|
return new AstCExpr{fl, AstCExpr::Pure{},
|
||||||
|
assertActionControlPrefix(directiveType)
|
||||||
|
+ assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType)
|
||||||
|
+ "))"s,
|
||||||
|
1};
|
||||||
|
}
|
||||||
string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message,
|
string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message,
|
||||||
VDisplayType severity) {
|
VDisplayType severity) {
|
||||||
if (severity == VDisplayType::DT_ERROR || severity == VDisplayType::DT_FATAL) {
|
if (severity == VDisplayType::DT_ERROR || severity == VDisplayType::DT_FATAL) {
|
||||||
|
|
@ -207,36 +314,6 @@ class AssertVisitor final : public VNVisitor {
|
||||||
+ cvtToStr(nodep->fileline()->lineno()) + ": %m" + ((message != "") ? ": " : "")
|
+ cvtToStr(nodep->fileline()->lineno()) + ": %m" + ((message != "") ? ": " : "")
|
||||||
+ message + "\n");
|
+ message + "\n");
|
||||||
}
|
}
|
||||||
static bool resolveAssertType(AstAssertCtl* nodep) {
|
|
||||||
if (!nodep->assertTypesp()) {
|
|
||||||
nodep->ctlAssertTypes(VAssertType{ALL_ASSERT_TYPES});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (const AstConst* const assertTypesp = VN_CAST(nodep->assertTypesp(), Const)) {
|
|
||||||
nodep->ctlAssertTypes(VAssertType{assertTypesp->toSInt()});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
static bool resolveControlType(AstAssertCtl* nodep) {
|
|
||||||
if (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
|
|
||||||
nodep->ctlType(constp->toSInt());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
static bool resolveDirectiveType(AstAssertCtl* nodep) {
|
|
||||||
if (!nodep->directiveTypesp()) {
|
|
||||||
nodep->ctlDirectiveTypes(VAssertDirectiveType::ASSERT | VAssertDirectiveType::ASSUME
|
|
||||||
| VAssertDirectiveType::COVER);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (const AstConst* const directiveTypesp = VN_CAST(nodep->directiveTypesp(), Const)) {
|
|
||||||
nodep->ctlDirectiveTypes(VAssertDirectiveType{directiveTypesp->toSInt()});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
|
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
|
||||||
nodep->fmtp()->text(
|
nodep->fmtp()->text(
|
||||||
assertDisplayMessage(nodep, prefix, nodep->fmtp()->text(), nodep->displayType()));
|
assertDisplayMessage(nodep, prefix, nodep->fmtp()->text(), nodep->displayType()));
|
||||||
|
|
@ -278,8 +355,8 @@ class AssertVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
static AstIf* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
|
static AstIf* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
|
||||||
VAssertType type = VAssertType::INTERNAL) {
|
VAssertType type = VAssertType::INTERNAL) {
|
||||||
// Add a internal if to check assertions are on.
|
// Add an internal if to check assertions are on.
|
||||||
// Don't make this a AND term, as it's unlikely to need to test this.
|
// Don't make this an AND term, as it's unlikely to need to test this.
|
||||||
FileLine* const fl = bodyp->fileline();
|
FileLine* const fl = bodyp->fileline();
|
||||||
|
|
||||||
AstNodeExpr* const condp = assertOnCond(fl, type, directiveType);
|
AstNodeExpr* const condp = assertOnCond(fl, type, directiveType);
|
||||||
|
|
@ -288,6 +365,28 @@ class AssertVisitor final : public VNVisitor {
|
||||||
newp->user2(true); // Mark as an assertOn() check
|
newp->user2(true); // Mark as an assertOn() check
|
||||||
return newp;
|
return newp;
|
||||||
}
|
}
|
||||||
|
static AstNodeStmt* newIfAssertPassOn(AstNode* bodyp, VAssertDirectiveType directiveType,
|
||||||
|
VAssertType type, bool vacuous) {
|
||||||
|
// Add an internal if to check assertion passOn is enabled.
|
||||||
|
// Don't make this an AND term, as it's unlikely to need to test this.
|
||||||
|
FileLine* const fl = bodyp->fileline();
|
||||||
|
AstNodeExpr* const condp = assertPassOnCond(fl, type, directiveType, vacuous);
|
||||||
|
AstNodeIf* const newp = new AstIf{fl, condp, bodyp};
|
||||||
|
newp->isBoundsCheck(true); // To avoid LATCH warning
|
||||||
|
newp->user1(true); // Don't assert/cover this if
|
||||||
|
return newp;
|
||||||
|
}
|
||||||
|
static AstNodeStmt* newIfAssertFailOn(AstNode* bodyp, VAssertDirectiveType directiveType,
|
||||||
|
VAssertType type) {
|
||||||
|
// Add an internal if to check assertion failOn is enabled.
|
||||||
|
// Don't make this an AND term, as it's unlikely to need to test this.
|
||||||
|
FileLine* const fl = bodyp->fileline();
|
||||||
|
AstNodeExpr* const condp = assertFailOnCond(fl, type, directiveType);
|
||||||
|
AstNodeIf* const newp = new AstIf{fl, condp, bodyp};
|
||||||
|
newp->isBoundsCheck(true); // To avoid LATCH warning
|
||||||
|
newp->user1(true); // Don't assert/cover this if
|
||||||
|
return newp;
|
||||||
|
}
|
||||||
|
|
||||||
static AstIf* assertCond(const AstNodeCoverOrAssert* nodep, AstNodeExpr* propp,
|
static AstIf* assertCond(const AstNodeCoverOrAssert* nodep, AstNodeExpr* propp,
|
||||||
AstNode* passsp, AstNode* failsp) {
|
AstNode* passsp, AstNode* failsp) {
|
||||||
|
|
@ -444,6 +543,7 @@ class AssertVisitor final : public VNVisitor {
|
||||||
if (failsp) failsp->unlinkFrBackWithNext();
|
if (failsp) failsp->unlinkFrBackWithNext();
|
||||||
|
|
||||||
bool selfDestruct = false;
|
bool selfDestruct = false;
|
||||||
|
bool passspGated = false;
|
||||||
if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
|
if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
|
||||||
++m_statCover;
|
++m_statCover;
|
||||||
if (!v3Global.opt.coverageUser()) {
|
if (!v3Global.opt.coverageUser()) {
|
||||||
|
|
@ -455,6 +555,9 @@ class AssertVisitor final : public VNVisitor {
|
||||||
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
||||||
if (message != "") covincp->declp()->comment(message);
|
if (message != "") covincp->declp()->comment(message);
|
||||||
if (passsp) {
|
if (passsp) {
|
||||||
|
passsp = newIfAssertPassOn(passsp, nodep->directive(), nodep->userType(),
|
||||||
|
/*vacuous=*/false);
|
||||||
|
passspGated = true;
|
||||||
passsp = AstNode::addNext<AstNode, AstNode>(covincp, passsp);
|
passsp = AstNode::addNext<AstNode, AstNode>(covincp, passsp);
|
||||||
} else {
|
} else {
|
||||||
passsp = covincp;
|
passsp = covincp;
|
||||||
|
|
@ -475,8 +578,10 @@ class AssertVisitor final : public VNVisitor {
|
||||||
|
|
||||||
VL_RESTORER(m_passsp);
|
VL_RESTORER(m_passsp);
|
||||||
VL_RESTORER(m_failsp);
|
VL_RESTORER(m_failsp);
|
||||||
|
VL_RESTORER(m_assertp);
|
||||||
m_passsp = passsp;
|
m_passsp = passsp;
|
||||||
m_failsp = failsp;
|
m_failsp = failsp;
|
||||||
|
m_assertp = nodep;
|
||||||
iterate(nodep->propp());
|
iterate(nodep->propp());
|
||||||
|
|
||||||
AstNode* propExprp;
|
AstNode* propExprp;
|
||||||
|
|
@ -488,6 +593,15 @@ class AssertVisitor final : public VNVisitor {
|
||||||
propExprp = nodep->propp()->unlinkFrBack();
|
propExprp = nodep->propp()->unlinkFrBack();
|
||||||
}
|
}
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
|
bool passspAlreadyGated = false;
|
||||||
|
if (passsp && VN_IS(passsp, If)) passspAlreadyGated = VN_AS(passsp, If)->user1();
|
||||||
|
if (passsp && !passspGated && !passspAlreadyGated && !VN_IS(propExprp, PExpr)) {
|
||||||
|
passsp = newIfAssertPassOn(passsp, nodep->directive(), nodep->userType(),
|
||||||
|
/*vacuous=*/false);
|
||||||
|
}
|
||||||
|
if (failsp && !VN_IS(propExprp, PExpr)) {
|
||||||
|
failsp = newIfAssertFailOn(failsp, nodep->directive(), nodep->userType());
|
||||||
|
}
|
||||||
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
|
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
|
||||||
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
|
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
|
||||||
// Add assertOn check last, for better combining
|
// Add assertOn check last, for better combining
|
||||||
|
|
@ -586,6 +700,34 @@ class AssertVisitor final : public VNVisitor {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nodep->user2()) {
|
||||||
|
// Combine consecutive assertOn checks if possible
|
||||||
|
if (AstIf* const backp = VN_CAST(nodep->backp(), If)) {
|
||||||
|
if (backp->nextp() == nodep //
|
||||||
|
&& backp->user2() //
|
||||||
|
&& backp->condp()->sameTree(nodep->condp())) {
|
||||||
|
++m_statAssertOnCombined;
|
||||||
|
backp->addThensp(nodep->thensp()->unlinkFrBackWithNext());
|
||||||
|
nodep->unlinkFrBack();
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Combine nested assertOn checks if possible
|
||||||
|
if (nodep->thensp() && !nodep->thensp()->nextp() && isEmptyStmt(nodep->elsesp())) {
|
||||||
|
AstIf* const checkp = VN_CAST(nodep->thensp(), If);
|
||||||
|
if (checkp //
|
||||||
|
&& checkp->user2() //
|
||||||
|
&& checkp->condp()->sameTree(nodep->condp())) {
|
||||||
|
++m_statAssertOnCombined;
|
||||||
|
nodep->addThensp(checkp->thensp()->unlinkFrBackWithNext());
|
||||||
|
VL_DO_DANGLING(pushDeletep(checkp->unlinkFrBack()), checkp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Swap assertOn check with single statement 'if' statement to bubble up for combining
|
// Swap assertOn check with single statement 'if' statement to bubble up for combining
|
||||||
// Note we can't just swap the conditions as they two Ifs have different flags,
|
// Note we can't just swap the conditions as they two Ifs have different flags,
|
||||||
// so swapping the Ifs themeselves then swapping back the bodies.
|
// so swapping the Ifs themeselves then swapping back the bodies.
|
||||||
|
|
@ -611,20 +753,6 @@ class AssertVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine consecutive assertOn checks if possible
|
|
||||||
if (nodep->user2()) {
|
|
||||||
if (AstIf* const backp = VN_CAST(nodep->backp(), If)) {
|
|
||||||
if (backp->nextp() == nodep //
|
|
||||||
&& backp->user2() //
|
|
||||||
&& backp->condp()->sameTree(nodep->condp())) {
|
|
||||||
++m_statAssertOnCombined;
|
|
||||||
backp->addThensp(nodep->thensp()->unlinkFrBackWithNext());
|
|
||||||
nodep->unlinkFrBack();
|
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//========== Case assertions
|
//========== Case assertions
|
||||||
|
|
@ -807,8 +935,11 @@ class AssertVisitor final : public VNVisitor {
|
||||||
if (nodep->pass() && m_passsp) {
|
if (nodep->pass() && m_passsp) {
|
||||||
// Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
|
// Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
|
||||||
stmtsp = m_passsp->cloneTree(true);
|
stmtsp = m_passsp->cloneTree(true);
|
||||||
|
stmtsp = newIfAssertPassOn(stmtsp, m_assertp->directive(), m_assertp->userType(),
|
||||||
|
nodep->vacuous());
|
||||||
} else if (!nodep->pass() && m_failsp) {
|
} else if (!nodep->pass() && m_failsp) {
|
||||||
stmtsp = m_failsp->cloneTree(true);
|
stmtsp = m_failsp->cloneTree(true);
|
||||||
|
stmtsp = newIfAssertFailOn(stmtsp, m_assertp->directive(), m_assertp->userType());
|
||||||
}
|
}
|
||||||
if (stmtsp) {
|
if (stmtsp) {
|
||||||
stmtsp->foreachAndNext([](AstNodeVarRef* const refp) {
|
stmtsp->foreachAndNext([](AstNodeVarRef* const refp) {
|
||||||
|
|
@ -914,34 +1045,51 @@ class AssertVisitor final : public VNVisitor {
|
||||||
visitAssertionIterate(nodep, nodep->failsp());
|
visitAssertionIterate(nodep, nodep->failsp());
|
||||||
}
|
}
|
||||||
void visit(AstAssertCtl* nodep) override {
|
void visit(AstAssertCtl* nodep) override {
|
||||||
if (VN_IS(m_modp, Class) || VN_IS(m_modp, Iface)) {
|
|
||||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assertcontrols in classes or interfaces");
|
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
|
||||||
if (!resolveAssertType(nodep)) {
|
bool assertTypeConst = true;
|
||||||
nodep->v3warn(E_UNSUPPORTED,
|
if (!nodep->assertTypesp()) {
|
||||||
"Unsupported: non-constant assert assertion-type expression");
|
nodep->ctlAssertTypes(VAssertType{ALL_ASSERT_TYPES});
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
} else if (const AstConst* const assertTypesp = VN_CAST(nodep->assertTypesp(), Const)) {
|
||||||
|
nodep->ctlAssertTypes(VAssertType{assertTypesp->toSInt()});
|
||||||
|
} else {
|
||||||
|
assertTypeConst = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool controlTypeConst = false;
|
||||||
|
if (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
|
||||||
|
nodep->ctlType(constp->toSInt());
|
||||||
|
controlTypeConst = true;
|
||||||
|
}
|
||||||
|
if (controlTypeConst
|
||||||
|
&& (nodep->ctlType() < VAssertCtlType::LOCK
|
||||||
|
|| nodep->ctlType() > VAssertCtlType::VACUOUS_OFF)) {
|
||||||
|
nodep->unlinkFrBack();
|
||||||
|
nodep->v3error("Bad $assertcontrol control_type '"
|
||||||
|
<< cvtToStr(static_cast<int>(nodep->ctlType()))
|
||||||
|
<< "' (IEEE 1800-2023 Table 20-5)");
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (nodep->ctlAssertTypes() != ALL_ASSERT_TYPES
|
if (assertTypeConst && nodep->ctlAssertTypes() != ALL_ASSERT_TYPES
|
||||||
&& nodep->ctlAssertTypes().containsAny(VAssertType::EXPECT | VAssertType::UNIQUE
|
&& nodep->ctlAssertTypes().containsAny(VAssertType::UNIQUE | VAssertType::UNIQUE0
|
||||||
| VAssertType::UNIQUE0
|
|
||||||
| VAssertType::PRIORITY)) {
|
| VAssertType::PRIORITY)) {
|
||||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assert control assertion_type");
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assert control assertion_type");
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!resolveControlType(nodep)) {
|
|
||||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: non-const assert control type expression");
|
bool directiveTypeConst = true;
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
if (!nodep->directiveTypesp()) {
|
||||||
return;
|
nodep->ctlDirectiveTypes(VAssertDirectiveType::ASSERT | VAssertDirectiveType::ASSUME
|
||||||
|
| VAssertDirectiveType::COVER);
|
||||||
|
} else if (const AstConst* const directiveTypesp
|
||||||
|
= VN_CAST(nodep->directiveTypesp(), Const)) {
|
||||||
|
nodep->ctlDirectiveTypes(VAssertDirectiveType{directiveTypesp->toSInt()});
|
||||||
|
} else {
|
||||||
|
directiveTypeConst = false;
|
||||||
}
|
}
|
||||||
if (!resolveDirectiveType(nodep)) {
|
if (!directiveTypeConst) {
|
||||||
nodep->v3warn(E_UNSUPPORTED,
|
nodep->v3warn(E_UNSUPPORTED,
|
||||||
"Unsupported: non-const assert directive type expression");
|
"Unsupported: non-const assert directive type expression");
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
|
|
@ -949,43 +1097,18 @@ class AssertVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
FileLine* const fl = nodep->fileline();
|
FileLine* const fl = nodep->fileline();
|
||||||
switch (nodep->ctlType()) {
|
UINFO(9, "Generating assertctl for a module: " << m_modp);
|
||||||
case VAssertCtlType::ON:
|
AstCStmt* const newp = new AstCStmt{fl};
|
||||||
UINFO(9, "Generating assertctl for a module: " << m_modp);
|
newp->add("vlSymsp->_vm_contextp__->assertCtl(");
|
||||||
nodep->replaceWith(
|
newp->add(nodep->controlTypep()->unlinkFrBack());
|
||||||
new AstCStmt{fl, "vlSymsp->_vm_contextp__->assertOnSet("s
|
newp->add(", ");
|
||||||
+ std::to_string(nodep->ctlAssertTypes()) + ", "s
|
if (nodep->assertTypesp()) {
|
||||||
+ std::to_string(nodep->ctlDirectiveTypes()) + ");\n"s});
|
newp->add(nodep->assertTypesp()->unlinkFrBack());
|
||||||
break;
|
} else {
|
||||||
case VAssertCtlType::OFF:
|
newp->add(std::to_string(ALL_ASSERT_TYPES));
|
||||||
case VAssertCtlType::KILL: {
|
|
||||||
UINFO(9, "Generating assertctl for a module: " << m_modp);
|
|
||||||
nodep->replaceWith(
|
|
||||||
new AstCStmt{fl, "vlSymsp->_vm_contextp__->assertOnClear("s
|
|
||||||
+ std::to_string(nodep->ctlAssertTypes()) + " ,"s
|
|
||||||
+ std::to_string(nodep->ctlDirectiveTypes()) + ");\n"s});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VAssertCtlType::LOCK:
|
|
||||||
case VAssertCtlType::UNLOCK:
|
|
||||||
case VAssertCtlType::PASS_ON:
|
|
||||||
case VAssertCtlType::PASS_OFF:
|
|
||||||
case VAssertCtlType::FAIL_ON:
|
|
||||||
case VAssertCtlType::FAIL_OFF:
|
|
||||||
case VAssertCtlType::NONVACUOUS_ON:
|
|
||||||
case VAssertCtlType::VACUOUS_OFF: {
|
|
||||||
nodep->unlinkFrBack();
|
|
||||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: $assertcontrol control_type '" << cvtToStr(
|
|
||||||
static_cast<int>(nodep->ctlType())) << "'");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
nodep->unlinkFrBack();
|
|
||||||
nodep->v3warn(EC_ERROR, "Bad $assertcontrol control_type '"
|
|
||||||
<< cvtToStr(static_cast<int>(nodep->ctlType()))
|
|
||||||
<< "' (IEEE 1800-2023 Table 20-5)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
newp->add(", " + std::to_string(nodep->ctlDirectiveTypes()) + ");\n");
|
||||||
|
nodep->replaceWith(newp);
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
}
|
}
|
||||||
void visit(AstAssertIntrinsic* nodep) override { //
|
void visit(AstAssertIntrinsic* nodep) override { //
|
||||||
|
|
@ -1007,10 +1130,12 @@ class AssertVisitor final : public VNVisitor {
|
||||||
VL_RESTORER(m_modPastNum);
|
VL_RESTORER(m_modPastNum);
|
||||||
VL_RESTORER(m_modStrobeNum);
|
VL_RESTORER(m_modStrobeNum);
|
||||||
VL_RESTORER(m_modExpr2Sen2DelayedAlwaysp);
|
VL_RESTORER(m_modExpr2Sen2DelayedAlwaysp);
|
||||||
|
VL_RESTORER(m_finalp);
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
m_modPastNum = 0;
|
m_modPastNum = 0;
|
||||||
m_modStrobeNum = 0;
|
m_modStrobeNum = 0;
|
||||||
m_modExpr2Sen2DelayedAlwaysp.clear();
|
m_modExpr2Sen2DelayedAlwaysp.clear();
|
||||||
|
m_finalp = nullptr;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstNodeProcedure* nodep) override {
|
void visit(AstNodeProcedure* nodep) override {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,11 @@
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
|
class V3AssertCommon final {
|
||||||
|
public:
|
||||||
|
static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED;
|
||||||
|
};
|
||||||
|
|
||||||
class V3Assert final {
|
class V3Assert final {
|
||||||
public:
|
public:
|
||||||
static void assertAll(AstNetlist* nodep) VL_MT_DISABLED;
|
static void assertAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include "V3AssertPre.h"
|
#include "V3AssertPre.h"
|
||||||
|
|
||||||
|
#include "V3Assert.h"
|
||||||
#include "V3Const.h"
|
#include "V3Const.h"
|
||||||
#include "V3Task.h"
|
#include "V3Task.h"
|
||||||
#include "V3UniqueNames.h"
|
#include "V3UniqueNames.h"
|
||||||
|
|
@ -60,6 +61,7 @@ private:
|
||||||
AstNodeExpr* m_disablep = nullptr; // Last disable
|
AstNodeExpr* m_disablep = nullptr; // Last disable
|
||||||
AstIf* m_disableSeqIfp = nullptr; // Used for handling disable iff in sequences
|
AstIf* m_disableSeqIfp = nullptr; // Used for handling disable iff in sequences
|
||||||
AstPExpr* m_pexprp = nullptr; // Last AstPExpr
|
AstPExpr* m_pexprp = nullptr; // Last AstPExpr
|
||||||
|
bool m_underCover = false; // True if the enclosing assertion is a cover
|
||||||
// Other:
|
// Other:
|
||||||
V3UniqueNames m_cycleDlyNames{"__VcycleDly"}; // Cycle delay counter name generator
|
V3UniqueNames m_cycleDlyNames{"__VcycleDly"}; // Cycle delay counter name generator
|
||||||
V3UniqueNames m_consRepNames{"__VconsRep"}; // Consecutive repetition counter name generator
|
V3UniqueNames m_consRepNames{"__VconsRep"}; // Consecutive repetition counter name generator
|
||||||
|
|
@ -91,7 +93,10 @@ private:
|
||||||
fromAlways = true;
|
fromAlways = true;
|
||||||
}
|
}
|
||||||
if (!senip) {
|
if (!senip) {
|
||||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Unclocked assertion");
|
nodep->v3error("Concurrent assertion has no clock (IEEE 1800-2023 16.16)\n"
|
||||||
|
<< nodep->warnMore()
|
||||||
|
<< "... Suggest provide a clocking event, a default"
|
||||||
|
" clocking, or a clocked procedural context");
|
||||||
newp = new AstSenTree{nodep->fileline(), nullptr};
|
newp = new AstSenTree{nodep->fileline(), nullptr};
|
||||||
} else {
|
} else {
|
||||||
if (cassertp && fromAlways) cassertp->senFromAlways(true);
|
if (cassertp && fromAlways) cassertp->senFromAlways(true);
|
||||||
|
|
@ -1281,7 +1286,10 @@ private:
|
||||||
// Don't iterate pexprp here -- it was already iterated when created
|
// Don't iterate pexprp here -- it was already iterated when created
|
||||||
// (in visit(AstSExpr*)), so delays and disable iff are already processed.
|
// (in visit(AstSExpr*)), so delays and disable iff are already processed.
|
||||||
} else if (nodep->isOverlapped()) {
|
} else if (nodep->isOverlapped()) {
|
||||||
nodep->replaceWith(new AstLogOr{flp, new AstLogNot{flp, lhsp}, rhsp});
|
AstNodeExpr* const exprp
|
||||||
|
= m_underCover ? static_cast<AstNodeExpr*>(new AstLogAnd{flp, lhsp, rhsp})
|
||||||
|
: new AstLogOr{flp, new AstLogNot{flp, lhsp}, rhsp};
|
||||||
|
nodep->replaceWith(exprp);
|
||||||
} else {
|
} else {
|
||||||
if (m_disablep) {
|
if (m_disablep) {
|
||||||
lhsp = new AstAnd{flp, new AstNot{flp, m_disablep->cloneTreePure(false)}, lhsp};
|
lhsp = new AstAnd{flp, new AstNot{flp, m_disablep->cloneTreePure(false)}, lhsp};
|
||||||
|
|
@ -1290,7 +1298,9 @@ private:
|
||||||
AstPast* const pastp = new AstPast{flp, lhsp};
|
AstPast* const pastp = new AstPast{flp, lhsp};
|
||||||
pastp->dtypeFrom(lhsp);
|
pastp->dtypeFrom(lhsp);
|
||||||
pastp->sentreep(newSenTree(nodep));
|
pastp->sentreep(newSenTree(nodep));
|
||||||
AstNodeExpr* const exprp = new AstOr{flp, new AstNot{flp, pastp}, rhsp};
|
AstNodeExpr* const exprp
|
||||||
|
= m_underCover ? static_cast<AstNodeExpr*>(new AstAnd{flp, pastp, rhsp})
|
||||||
|
: new AstOr{flp, new AstNot{flp, pastp}, rhsp};
|
||||||
exprp->dtypeSetBit();
|
exprp->dtypeSetBit();
|
||||||
nodep->replaceWith(exprp);
|
nodep->replaceWith(exprp);
|
||||||
}
|
}
|
||||||
|
|
@ -1438,12 +1448,6 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(AstDefaultDisable* nodep) override {
|
void visit(AstDefaultDisable* nodep) override {
|
||||||
if (m_defaultDisablep) {
|
|
||||||
nodep->v3error("Only one 'default disable iff' allowed per module"
|
|
||||||
" (IEEE 1800-2023 16.15)");
|
|
||||||
} else {
|
|
||||||
m_defaultDisablep = nodep;
|
|
||||||
}
|
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
}
|
}
|
||||||
void visit(AstInferredDisable* nodep) override {
|
void visit(AstInferredDisable* nodep) override {
|
||||||
|
|
@ -1474,6 +1478,10 @@ private:
|
||||||
nodep->propp(new AstSampled{nodep->fileline(), nodep->propp()->unlinkFrBack(),
|
nodep->propp(new AstSampled{nodep->fileline(), nodep->propp()->unlinkFrBack(),
|
||||||
propDtp->dtypep()});
|
propDtp->dtypep()});
|
||||||
}
|
}
|
||||||
|
// cover counts non-vacuous matches only (IEEE 1800-2023 16.15.2), so an
|
||||||
|
// implication antecedent must hold; assert passes vacuously instead.
|
||||||
|
VL_RESTORER(m_underCover);
|
||||||
|
m_underCover = VN_IS(nodep->backp(), Cover);
|
||||||
iterate(nodep->propp());
|
iterate(nodep->propp());
|
||||||
}
|
}
|
||||||
void visit(AstPExpr* nodep) override {
|
void visit(AstPExpr* nodep) override {
|
||||||
|
|
@ -1565,10 +1573,15 @@ private:
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
m_defaultClockingp = nullptr;
|
m_defaultClockingp = nullptr;
|
||||||
m_defaultClkEvtVarp = nullptr;
|
m_defaultClkEvtVarp = nullptr;
|
||||||
m_defaultDisablep = nullptr;
|
m_defaultDisablep = nodep->defaultDisablep();
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
void visit(AstGenBlock* nodep) override {
|
||||||
|
VL_RESTORER(m_defaultDisablep);
|
||||||
|
m_defaultDisablep = nodep->defaultDisablep();
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
void visit(AstProperty* nodep) override {
|
void visit(AstProperty* nodep) override {
|
||||||
// The body will be visited when will be substituted in place of property reference
|
// The body will be visited when will be substituted in place of property reference
|
||||||
// (AstFuncRef)
|
// (AstFuncRef)
|
||||||
|
|
|
||||||
150
src/V3Ast.cpp
150
src/V3Ast.cpp
|
|
@ -1745,6 +1745,156 @@ AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
// Renders the canonical pattern S-expression for a single AstNode
|
||||||
|
|
||||||
|
class VNPatternString final {
|
||||||
|
std::ostream& m_os;
|
||||||
|
|
||||||
|
std::map<std::string, std::string> m_internedConsts; // Interned constants
|
||||||
|
std::map<int, std::string> m_internedWordWidths; // Interned widths
|
||||||
|
std::map<int, std::string> m_internedWideWidths; // Interned widths
|
||||||
|
|
||||||
|
// Whether to dump the widhtMin as well
|
||||||
|
const bool m_dumpWidthMin = v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH;
|
||||||
|
|
||||||
|
static std::string toLetters(size_t value, bool lowerCase = false) {
|
||||||
|
const char base = lowerCase ? 'a' : 'A';
|
||||||
|
std::string s;
|
||||||
|
do { s += static_cast<char>(base + value % 26); } while (value /= 26);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& internConst(const AstConst& nodep) {
|
||||||
|
const auto pair = m_internedConsts.emplace(nodep.num().ascii(false), "");
|
||||||
|
if (pair.second) pair.first->second += toLetters(m_internedConsts.size() - 1);
|
||||||
|
return pair.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& internWordWidth(int value) {
|
||||||
|
const auto pair = m_internedWordWidths.emplace(value, "");
|
||||||
|
if (pair.second) pair.first->second += toLetters(m_internedWordWidths.size() - 1, true);
|
||||||
|
return pair.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& internWideWidth(int value) {
|
||||||
|
const auto pair = m_internedWideWidths.emplace(value, "");
|
||||||
|
if (pair.second) pair.first->second += toLetters(m_internedWideWidths.size() - 1);
|
||||||
|
return pair.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if the node has no operands (e.g. a VarRef or Const)
|
||||||
|
static bool isLeaf(const AstNode* nodep) {
|
||||||
|
const VNTypeInfo& typeInfo = VNType::typeInfo(nodep->type());
|
||||||
|
for (const VNTypeInfo::OpEn& opType : typeInfo.m_opType) {
|
||||||
|
if (opType != VNTypeInfo::OP_UNUSED) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render operand, return true if not unused
|
||||||
|
void renderOperand(VNTypeInfo::OpEn opType, const AstNode* const opp, uint32_t depth) {
|
||||||
|
switch (opType) {
|
||||||
|
case VNTypeInfo::OP_UNUSED: //
|
||||||
|
break;
|
||||||
|
case VNTypeInfo::OP_USED: //
|
||||||
|
m_os << ' ';
|
||||||
|
render(opp, depth);
|
||||||
|
break;
|
||||||
|
case VNTypeInfo::OP_LIST:
|
||||||
|
m_os << " [";
|
||||||
|
for (const AstNode* nodep = opp; nodep; nodep = nodep->nextp()) {
|
||||||
|
if (nodep != opp) m_os << ", ";
|
||||||
|
render(opp, depth);
|
||||||
|
}
|
||||||
|
m_os << ']';
|
||||||
|
break;
|
||||||
|
case VNTypeInfo::OP_OPTIONAL:
|
||||||
|
if (opp) {
|
||||||
|
m_os << " ";
|
||||||
|
render(opp, depth);
|
||||||
|
} else {
|
||||||
|
m_os << " nil";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the node into the stream.
|
||||||
|
void render(const AstNode* nodep, uint32_t depth) {
|
||||||
|
if (const AstConst* const constp = VN_CAST(nodep, Const)) {
|
||||||
|
// Base case 1: constant
|
||||||
|
if (constp->isZero()) {
|
||||||
|
m_os << "(CONST ZERO)";
|
||||||
|
} else if (constp->isEqAllOnes()) {
|
||||||
|
m_os << "(CONST ONES)";
|
||||||
|
} else {
|
||||||
|
m_os << "(CONST #" << internConst(*constp) << ')';
|
||||||
|
}
|
||||||
|
} else if (isLeaf(nodep)) {
|
||||||
|
// Base case 2: expression with no operands (e.g. a variable reference)
|
||||||
|
m_os << '(' << nodep->typeName() << ')';
|
||||||
|
} else if (depth == 0) {
|
||||||
|
// Base case 3: deep expression
|
||||||
|
m_os << '_';
|
||||||
|
} else {
|
||||||
|
// Recursively print an S-expression for the expression
|
||||||
|
m_os << '(';
|
||||||
|
// Name
|
||||||
|
m_os << nodep->typeName();
|
||||||
|
// Operands
|
||||||
|
const VNTypeInfo& typeInfo = VNType::typeInfo(nodep->type());
|
||||||
|
renderOperand(typeInfo.m_opType[0], nodep->op1p(), depth - 1);
|
||||||
|
renderOperand(typeInfo.m_opType[1], nodep->op2p(), depth - 1);
|
||||||
|
renderOperand(typeInfo.m_opType[2], nodep->op3p(), depth - 1);
|
||||||
|
renderOperand(typeInfo.m_opType[3], nodep->op4p(), depth - 1);
|
||||||
|
// S-expression end
|
||||||
|
m_os << ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Annotate type
|
||||||
|
m_os << ':';
|
||||||
|
const AstNodeDType* const dtypep = nodep->dtypep() ? nodep->dtypep()->skipRefp() : nullptr;
|
||||||
|
if (!dtypep) {
|
||||||
|
m_os << '?';
|
||||||
|
} else if (dtypep->isCompound() || VN_IS(dtypep, UnpackArrayDType)) {
|
||||||
|
dtypep->dumpSmall(m_os);
|
||||||
|
} else {
|
||||||
|
const int width = nodep->width();
|
||||||
|
if (width == 1) {
|
||||||
|
m_os << '1';
|
||||||
|
} else if (width <= VL_QUADSIZE) {
|
||||||
|
m_os << internWordWidth(width);
|
||||||
|
} else {
|
||||||
|
m_os << internWideWidth(width);
|
||||||
|
}
|
||||||
|
if (m_dumpWidthMin) {
|
||||||
|
m_os << '/';
|
||||||
|
const int widthMin = nodep->widthMin();
|
||||||
|
if (widthMin == 1) {
|
||||||
|
m_os << '1';
|
||||||
|
} else if (widthMin <= VL_QUADSIZE) {
|
||||||
|
m_os << internWordWidth(widthMin);
|
||||||
|
} else {
|
||||||
|
m_os << internWideWidth(widthMin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
VNPatternString(std::ostream& os, const AstNode* nodep, uint32_t depth)
|
||||||
|
: m_os{os} {
|
||||||
|
render(nodep, depth);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string AstNodeExpr::patternString(uint32_t depth) const {
|
||||||
|
std::ostringstream oss;
|
||||||
|
VNPatternString{oss, this, depth};
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// VNDeleter
|
// VNDeleter
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -342,7 +342,6 @@ public:
|
||||||
VAR_PUBLIC_FLAT, // V3LinkParse moves to AstVar::sigPublic
|
VAR_PUBLIC_FLAT, // V3LinkParse moves to AstVar::sigPublic
|
||||||
VAR_PUBLIC_FLAT_RD, // V3LinkParse moves to AstVar::sigPublic
|
VAR_PUBLIC_FLAT_RD, // V3LinkParse moves to AstVar::sigPublic
|
||||||
VAR_PUBLIC_FLAT_RW, // V3LinkParse moves to AstVar::sigPublic
|
VAR_PUBLIC_FLAT_RW, // V3LinkParse moves to AstVar::sigPublic
|
||||||
VAR_ISOLATE_ASSIGNMENTS, // V3LinkParse moves to AstVar::attrIsolateAssign
|
|
||||||
VAR_SC_BIGUINT, // V3LinkParse moves to AstVar::attrScBigUint
|
VAR_SC_BIGUINT, // V3LinkParse moves to AstVar::attrScBigUint
|
||||||
VAR_SC_BV, // V3LinkParse moves to AstVar::attrScBv
|
VAR_SC_BV, // V3LinkParse moves to AstVar::attrScBv
|
||||||
VAR_SFORMAT, // V3LinkParse moves to AstVar::attrSFormat
|
VAR_SFORMAT, // V3LinkParse moves to AstVar::attrSFormat
|
||||||
|
|
@ -364,7 +363,7 @@ public:
|
||||||
"TYPEID", "TYPENAME",
|
"TYPEID", "TYPENAME",
|
||||||
"VAR_BASE", "VAR_FORCEABLE", "VAR_FSM_ARC_INCLUDE_COND", "VAR_FSM_RESET_ARC",
|
"VAR_BASE", "VAR_FORCEABLE", "VAR_FSM_ARC_INCLUDE_COND", "VAR_FSM_RESET_ARC",
|
||||||
"VAR_FSM_STATE", "VAR_PORT_DTYPE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT",
|
"VAR_FSM_STATE", "VAR_PORT_DTYPE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT",
|
||||||
"VAR_PUBLIC_FLAT_RD", "VAR_PUBLIC_FLAT_RW", "VAR_ISOLATE_ASSIGNMENTS",
|
"VAR_PUBLIC_FLAT_RD", "VAR_PUBLIC_FLAT_RW",
|
||||||
"VAR_SC_BIGUINT", "VAR_SC_BV", "VAR_SFORMAT", "VAR_SPLIT_VAR"
|
"VAR_SC_BIGUINT", "VAR_SC_BV", "VAR_SFORMAT", "VAR_SPLIT_VAR"
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
@ -1191,6 +1190,19 @@ public:
|
||||||
= {"user", "array", "auto", "ignore", "illegal", "default", "wildcard", "transition"};
|
= {"user", "array", "auto", "ignore", "illegal", "default", "wildcard", "transition"};
|
||||||
return names[m_e];
|
return names[m_e];
|
||||||
}
|
}
|
||||||
|
// VlCovBinKind enumerator naming the bin's set
|
||||||
|
const char* binSetEnum() const {
|
||||||
|
switch (m_e) {
|
||||||
|
case BINS_IGNORE: return "VlCovBinKind::KIND_IGNORE";
|
||||||
|
case BINS_ILLEGAL: return "VlCovBinKind::KIND_ILLEGAL";
|
||||||
|
case BINS_DEFAULT: return "VlCovBinKind::KIND_DEFAULT";
|
||||||
|
default: return "VlCovBinKind::KIND_NORMAL";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Normal bins (feed coverage) are anything but ignore/illegal/default
|
||||||
|
bool binIsNormal() const {
|
||||||
|
return m_e != BINS_IGNORE && m_e != BINS_ILLEGAL && m_e != BINS_DEFAULT;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
constexpr bool operator==(const VCoverBinsType& lhs, VCoverBinsType::en rhs) {
|
constexpr bool operator==(const VCoverBinsType& lhs, VCoverBinsType::en rhs) {
|
||||||
return lhs.m_e == rhs;
|
return lhs.m_e == rhs;
|
||||||
|
|
@ -1416,6 +1428,7 @@ public:
|
||||||
ET_EVENT, // VlEventBase::isFired
|
ET_EVENT, // VlEventBase::isFired
|
||||||
// Involving an expression
|
// Involving an expression
|
||||||
ET_TRUE,
|
ET_TRUE,
|
||||||
|
ET_INITIAL_NBA, // Event that is fired initially and never again
|
||||||
//
|
//
|
||||||
ET_COMBO, // Sensitive to all combo inputs to this block
|
ET_COMBO, // Sensitive to all combo inputs to this block
|
||||||
ET_COMBO_STAR, // Sensitive to all combo inputs to this block (from .*)
|
ET_COMBO_STAR, // Sensitive to all combo inputs to this block (from .*)
|
||||||
|
|
@ -1434,6 +1447,7 @@ public:
|
||||||
true, // ET_NEGEDGE
|
true, // ET_NEGEDGE
|
||||||
true, // ET_EVENT
|
true, // ET_EVENT
|
||||||
true, // ET_TRUE
|
true, // ET_TRUE
|
||||||
|
true, // ET_INITIAL_NBA
|
||||||
|
|
||||||
false, // ET_COMBO
|
false, // ET_COMBO
|
||||||
false, // ET_COMBO_STAR
|
false, // ET_COMBO_STAR
|
||||||
|
|
@ -1457,14 +1471,14 @@ public:
|
||||||
}
|
}
|
||||||
const char* ascii() const {
|
const char* ascii() const {
|
||||||
static const char* const names[]
|
static const char* const names[]
|
||||||
= {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE", "COMBO",
|
= {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE", "ET_INITIAL_NBA",
|
||||||
"COMBO_STAR", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
|
"COMBO", "COMBO_STAR", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
|
||||||
return names[m_e];
|
return names[m_e];
|
||||||
}
|
}
|
||||||
const char* verilogKwd() const {
|
const char* verilogKwd() const {
|
||||||
static const char* const names[]
|
static const char* const names[] = {
|
||||||
= {"[changed]", "edge", "posedge", "negedge", "[event]", "[true]", "*",
|
"[changed]", "edge", "posedge", "negedge", "[event]", "[true]", "[initial_nba]",
|
||||||
"*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
|
"*", "*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
|
||||||
return names[m_e];
|
return names[m_e];
|
||||||
}
|
}
|
||||||
// Return true iff this and the other have mutually exclusive transitions
|
// Return true iff this and the other have mutually exclusive transitions
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,13 @@ bool AstVar::sameNode(const AstNode* samep) const {
|
||||||
return m_name == asamep->m_name && varType() == asamep->varType();
|
return m_name == asamep->m_name && varType() == asamep->varType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp)
|
||||||
|
: ASTGEN_SUPER_MatchMasked(fl) {
|
||||||
|
this->lhsp(lhsp);
|
||||||
|
this->matchp(new AstVarRef{fl, matchp, VAccess::READ});
|
||||||
|
dtypeSetUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
|
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
|
||||||
: ASTGEN_SUPER_VarRef(fl, varp, access) {
|
: ASTGEN_SUPER_VarRef(fl, varp, access) {
|
||||||
if (v3Global.assertDTypesResolved()) {
|
if (v3Global.assertDTypesResolved()) {
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,11 @@ public:
|
||||||
void generic(bool flag) { m_generic = flag; }
|
void generic(bool flag) { m_generic = flag; }
|
||||||
std::pair<uint32_t, uint32_t> dimensions(bool includeBasic) const;
|
std::pair<uint32_t, uint32_t> dimensions(bool includeBasic) const;
|
||||||
uint32_t arrayUnpackedElements() const; // 1, or total multiplication of all dimensions
|
uint32_t arrayUnpackedElements() const; // 1, or total multiplication of all dimensions
|
||||||
|
// Fixed aggregate streaming properties
|
||||||
|
bool isStreamableFixedAggregate() const;
|
||||||
|
bool containsUnpackedStruct() const;
|
||||||
|
int widthStream() const;
|
||||||
|
string vlEnumType() const; // Return VerilatedVarType: VLVT_UINT32, etc
|
||||||
static int uniqueNumInc() { return ++s_uniqueNum; }
|
static int uniqueNumInc() { return ++s_uniqueNum; }
|
||||||
const char* charIQWN() const {
|
const char* charIQWN() const {
|
||||||
return (isString() ? "N" : isWide() ? "W" : isDouble() ? "D" : isQuad() ? "Q" : "I");
|
return (isString() ? "N" : isWide() ? "W" : isDouble() ? "D" : isQuad() ? "Q" : "I");
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,9 @@ public:
|
||||||
// Returns an error message if widthMin() is not correct otherwise returns nullptr like
|
// Returns an error message if widthMin() is not correct otherwise returns nullptr like
|
||||||
// broken()
|
// broken()
|
||||||
virtual const char* widthMismatch() const VL_MT_STABLE { return nullptr; }
|
virtual const char* widthMismatch() const VL_MT_STABLE { return nullptr; }
|
||||||
|
|
||||||
|
// S-expression inspired dump of node and operands for debugging
|
||||||
|
std::string patternString(uint32_t depth = 0) const;
|
||||||
};
|
};
|
||||||
class AstNodeBiop VL_NOT_FINAL : public AstNodeExpr {
|
class AstNodeBiop VL_NOT_FINAL : public AstNodeExpr {
|
||||||
// Binary expression
|
// Binary expression
|
||||||
|
|
@ -1855,6 +1858,22 @@ public:
|
||||||
bool index() const { return m_index; }
|
bool index() const { return m_index; }
|
||||||
bool isExprCoverageEligible() const override { return false; }
|
bool isExprCoverageEligible() const override { return false; }
|
||||||
};
|
};
|
||||||
|
class AstMatchMasked final : public AstNodeExpr {
|
||||||
|
// This is a non-source construct, created internally to represent
|
||||||
|
// some case statements. It is a '(mask & _) == bits' matching loop
|
||||||
|
// where {mask, bits} pairs are packed into a single wide 'matchp',
|
||||||
|
// and the result is the index of the first matching entry.
|
||||||
|
// See VL_DECODER_* runtime functions.
|
||||||
|
// @astgen op1 := lhsp : AstNodeExpr
|
||||||
|
// @astgen op2 := matchp : AstVarRef
|
||||||
|
public:
|
||||||
|
inline AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp);
|
||||||
|
ASTGEN_MEMBERS_AstMatchMasked;
|
||||||
|
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||||
|
string emitC() override { return "VL_MATCHMASKED_%lq(%lw, %li, %ri)"; }
|
||||||
|
bool cleanOut() const override { return true; }
|
||||||
|
static uint32_t fold(const V3Number& lhs, AstVar* matchVarp);
|
||||||
|
};
|
||||||
class AstMatches final : public AstNodeExpr {
|
class AstMatches final : public AstNodeExpr {
|
||||||
// "matches" operator: "expr matches pattern"
|
// "matches" operator: "expr matches pattern"
|
||||||
// @astgen op1 := lhsp : AstNodeExpr // Expression to match
|
// @astgen op1 := lhsp : AstNodeExpr // Expression to match
|
||||||
|
|
@ -2225,6 +2244,23 @@ public:
|
||||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||||
bool isSystemFunc() const override { return true; }
|
bool isSystemFunc() const override { return true; }
|
||||||
};
|
};
|
||||||
|
class AstSClocked final : public AstNodeExpr {
|
||||||
|
// Sequence expression with an explicit leading clocking event
|
||||||
|
// IEEE 1800-2023 16.7: sequence_expr ::= clocking_event sequence_expr
|
||||||
|
// The clocking event is hoisted to the enclosing assertion clock by V3AssertNfa.
|
||||||
|
// @astgen op1 := sensesp : AstSenItem
|
||||||
|
// @astgen op2 := exprp : AstNodeExpr
|
||||||
|
public:
|
||||||
|
AstSClocked(FileLine* fl, AstSenItem* sensesp, AstNodeExpr* exprp)
|
||||||
|
: ASTGEN_SUPER_SClocked(fl) {
|
||||||
|
this->sensesp(sensesp);
|
||||||
|
this->exprp(exprp);
|
||||||
|
}
|
||||||
|
ASTGEN_MEMBERS_AstSClocked;
|
||||||
|
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||||
|
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||||
|
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
||||||
|
};
|
||||||
class AstSConsRep final : public AstNodeExpr {
|
class AstSConsRep final : public AstNodeExpr {
|
||||||
// Consecutive repetition [*N], [*N:M], [+], [*] (IEEE 1800-2023 16.9.2)
|
// Consecutive repetition [*N], [*N:M], [+], [*] (IEEE 1800-2023 16.9.2)
|
||||||
// op1 := exprp -- the repeated expression
|
// op1 := exprp -- the repeated expression
|
||||||
|
|
@ -5701,6 +5737,22 @@ public:
|
||||||
void dump(std::ostream& str) const override;
|
void dump(std::ostream& str) const override;
|
||||||
void dumpJson(std::ostream& str) const override;
|
void dumpJson(std::ostream& str) const override;
|
||||||
};
|
};
|
||||||
|
class AstMostSetBitP1 final : public AstNodeUniop {
|
||||||
|
// Most-significant set bit plus one (bit-width); 0 if value is zero
|
||||||
|
public:
|
||||||
|
AstMostSetBitP1(FileLine* fl, AstNodeExpr* lhsp)
|
||||||
|
: ASTGEN_SUPER_MostSetBitP1(fl, lhsp) {
|
||||||
|
dtypeSetInteger2State();
|
||||||
|
}
|
||||||
|
ASTGEN_MEMBERS_AstMostSetBitP1;
|
||||||
|
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opMostSetBitP1(lhs); }
|
||||||
|
string emitVerilog() override { return "%f$mostsetbitp1(%l)"; }
|
||||||
|
string emitC() override { return "VL_MOSTSETBITP1_%lq(%lW, %P, %li)"; }
|
||||||
|
bool cleanOut() const override { return true; }
|
||||||
|
bool cleanLhs() const override { return true; }
|
||||||
|
bool sizeMattersLhs() const override { return false; }
|
||||||
|
int instrCount() const override { return widthInstrs() * 16; }
|
||||||
|
};
|
||||||
class AstNToI final : public AstNodeUniop {
|
class AstNToI final : public AstNodeUniop {
|
||||||
// String to any-size integral
|
// String to any-size integral
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,6 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
||||||
string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8)
|
string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8)
|
||||||
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
|
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
|
||||||
bool m_taskPublic : 1; // Public task
|
bool m_taskPublic : 1; // Public task
|
||||||
bool m_attrIsolateAssign : 1; // User isolate_assignments attribute
|
|
||||||
bool m_classMethod : 1; // Class method
|
bool m_classMethod : 1; // Class method
|
||||||
bool m_didProto : 1; // Did prototype processing
|
bool m_didProto : 1; // Did prototype processing
|
||||||
bool m_prototype : 1; // Just a prototype
|
bool m_prototype : 1; // Just a prototype
|
||||||
|
|
@ -130,7 +129,6 @@ protected:
|
||||||
: AstNode{t, fl}
|
: AstNode{t, fl}
|
||||||
, m_name{name}
|
, m_name{name}
|
||||||
, m_taskPublic{false}
|
, m_taskPublic{false}
|
||||||
, m_attrIsolateAssign{false}
|
|
||||||
, m_classMethod{false}
|
, m_classMethod{false}
|
||||||
, m_didProto{false}
|
, m_didProto{false}
|
||||||
, m_prototype{false}
|
, m_prototype{false}
|
||||||
|
|
@ -179,8 +177,6 @@ public:
|
||||||
uint64_t dpiOpenParent() const { return m_dpiOpenParent; }
|
uint64_t dpiOpenParent() const { return m_dpiOpenParent; }
|
||||||
bool taskPublic() const { return m_taskPublic; }
|
bool taskPublic() const { return m_taskPublic; }
|
||||||
void taskPublic(bool flag) { m_taskPublic = flag; }
|
void taskPublic(bool flag) { m_taskPublic = flag; }
|
||||||
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
|
|
||||||
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
|
|
||||||
bool classMethod() const { return m_classMethod; }
|
bool classMethod() const { return m_classMethod; }
|
||||||
void classMethod(bool flag) { m_classMethod = flag; }
|
void classMethod(bool flag) { m_classMethod = flag; }
|
||||||
bool didProto() const { return m_didProto; }
|
bool didProto() const { return m_didProto; }
|
||||||
|
|
@ -301,6 +297,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
|
||||||
VLifetime m_lifetime; // Lifetime
|
VLifetime m_lifetime; // Lifetime
|
||||||
VTimescale m_timeunit; // Global time unit
|
VTimescale m_timeunit; // Global time unit
|
||||||
VOptionBool m_unconnectedDrive; // State of `unconnected_drive
|
VOptionBool m_unconnectedDrive; // State of `unconnected_drive
|
||||||
|
AstDefaultDisable* m_defaultDisablep = nullptr; // Default disable iff in this scope
|
||||||
|
|
||||||
bool m_modPublic : 1; // Module has public references
|
bool m_modPublic : 1; // Module has public references
|
||||||
bool m_modTrace : 1; // Tracing this module
|
bool m_modTrace : 1; // Tracing this module
|
||||||
|
|
@ -351,6 +348,8 @@ public:
|
||||||
string origName() const override { return m_origName; }
|
string origName() const override { return m_origName; }
|
||||||
string someInstanceName() const VL_MT_SAFE { return m_someInstanceName; }
|
string someInstanceName() const VL_MT_SAFE { return m_someInstanceName; }
|
||||||
void someInstanceName(const string& name) { m_someInstanceName = name; }
|
void someInstanceName(const string& name) { m_someInstanceName = name; }
|
||||||
|
AstDefaultDisable* defaultDisablep() const { return m_defaultDisablep; }
|
||||||
|
void defaultDisablep(AstDefaultDisable* nodep) { m_defaultDisablep = nodep; }
|
||||||
bool inLibrary() const { return m_inLibrary; }
|
bool inLibrary() const { return m_inLibrary; }
|
||||||
void inLibrary(bool flag) { m_inLibrary = flag; }
|
void inLibrary(bool flag) { m_inLibrary = flag; }
|
||||||
void depth(int value) { m_depth = value; }
|
void depth(int value) { m_depth = value; }
|
||||||
|
|
@ -1001,6 +1000,8 @@ public:
|
||||||
// this matters, the caller must handle the dtype difference as appropriate. If 'mergeDType' is
|
// this matters, the caller must handle the dtype difference as appropriate. If 'mergeDType' is
|
||||||
// false, the returned VarScope will have _->dtypep()->sameTree(initp->dtypep()) return true.
|
// false, the returned VarScope will have _->dtypep()->sameTree(initp->dtypep()) return true.
|
||||||
AstVarScope* findConst(AstConst* initp, bool mergeDType);
|
AstVarScope* findConst(AstConst* initp, bool mergeDType);
|
||||||
|
// Rebuild hashes after potential removals
|
||||||
|
void reCache();
|
||||||
};
|
};
|
||||||
class AstConstraint final : public AstNode {
|
class AstConstraint final : public AstNode {
|
||||||
// Constraint
|
// Constraint
|
||||||
|
|
@ -1184,12 +1185,21 @@ public:
|
||||||
};
|
};
|
||||||
class AstCoverpointRef final : public AstNode {
|
class AstCoverpointRef final : public AstNode {
|
||||||
// Reference to a coverpoint used in a cross
|
// Reference to a coverpoint used in a cross
|
||||||
const string m_name; // coverpoint name
|
// @astgen op1 := exprp : Optional[AstNodeExpr] // Non-standard: hierarchical/dotted
|
||||||
|
// // reference (implicit coverpoint), e.g.
|
||||||
|
// // 'cross a.b'; nullptr for a plain coverpoint
|
||||||
|
// // name. An AstDot at parse time, resolved
|
||||||
|
// // by the time V3Covergroup runs.
|
||||||
|
const string m_name; // coverpoint name; empty when exprp() carries a hierarchical reference
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AstCoverpointRef(FileLine* fl, const string& name)
|
AstCoverpointRef(FileLine* fl, const string& name)
|
||||||
: ASTGEN_SUPER_CoverpointRef(fl)
|
: ASTGEN_SUPER_CoverpointRef(fl)
|
||||||
, m_name{name} {}
|
, m_name{name} {}
|
||||||
|
AstCoverpointRef(FileLine* fl, AstNodeExpr* exprp)
|
||||||
|
: ASTGEN_SUPER_CoverpointRef(fl) {
|
||||||
|
this->exprp(exprp);
|
||||||
|
}
|
||||||
ASTGEN_MEMBERS_AstCoverpointRef;
|
ASTGEN_MEMBERS_AstCoverpointRef;
|
||||||
void dump(std::ostream& str) const override;
|
void dump(std::ostream& str) const override;
|
||||||
void dumpJson(std::ostream& str) const override;
|
void dumpJson(std::ostream& str) const override;
|
||||||
|
|
@ -1752,6 +1762,7 @@ public:
|
||||||
class Combo {}; // for constructor type-overload selection
|
class Combo {}; // for constructor type-overload selection
|
||||||
class Static {}; // for constructor type-overload selection
|
class Static {}; // for constructor type-overload selection
|
||||||
class Initial {}; // for constructor type-overload selection
|
class Initial {}; // for constructor type-overload selection
|
||||||
|
class InitialNBA {}; // for constructor type-overload selection
|
||||||
class Final {}; // for constructor type-overload selection
|
class Final {}; // for constructor type-overload selection
|
||||||
class Never {}; // for constructor type-overload selection
|
class Never {}; // for constructor type-overload selection
|
||||||
AstSenItem(FileLine* fl, VEdgeType edgeType, AstNodeExpr* senp, AstNodeExpr* condp = nullptr)
|
AstSenItem(FileLine* fl, VEdgeType edgeType, AstNodeExpr* senp, AstNodeExpr* condp = nullptr)
|
||||||
|
|
@ -1769,6 +1780,9 @@ public:
|
||||||
AstSenItem(FileLine* fl, Initial)
|
AstSenItem(FileLine* fl, Initial)
|
||||||
: ASTGEN_SUPER_SenItem(fl)
|
: ASTGEN_SUPER_SenItem(fl)
|
||||||
, m_edgeType{VEdgeType::ET_INITIAL} {}
|
, m_edgeType{VEdgeType::ET_INITIAL} {}
|
||||||
|
AstSenItem(FileLine* fl, InitialNBA)
|
||||||
|
: ASTGEN_SUPER_SenItem(fl)
|
||||||
|
, m_edgeType{VEdgeType::ET_INITIAL_NBA} {}
|
||||||
AstSenItem(FileLine* fl, Final)
|
AstSenItem(FileLine* fl, Final)
|
||||||
: ASTGEN_SUPER_SenItem(fl)
|
: ASTGEN_SUPER_SenItem(fl)
|
||||||
, m_edgeType{VEdgeType::ET_FINAL} {}
|
, m_edgeType{VEdgeType::ET_FINAL} {}
|
||||||
|
|
@ -2131,15 +2145,16 @@ class AstVar final : public AstNode {
|
||||||
bool m_funcReturn : 1; // Return variable for a function
|
bool m_funcReturn : 1; // Return variable for a function
|
||||||
bool m_attrScBv : 1; // User force bit vector attribute
|
bool m_attrScBv : 1; // User force bit vector attribute
|
||||||
bool m_attrScBigUint : 1; // User force sc_biguint attribute
|
bool m_attrScBigUint : 1; // User force sc_biguint attribute
|
||||||
bool m_attrIsolateAssign : 1; // User isolate_assignments attribute
|
|
||||||
bool m_attrSFormat : 1; // User sformat attribute
|
bool m_attrSFormat : 1; // User sformat attribute
|
||||||
bool m_attrSplitVar : 1; // declared with split_var metacomment
|
bool m_attrSplitVar : 1; // declared with split_var metacomment
|
||||||
bool m_attrFsmState : 1; // declared with fsm_state metacomment
|
bool m_attrFsmState : 1; // declared with fsm_state metacomment
|
||||||
bool m_attrFsmRegisterWrapper : 1; // connected to an fsm_register_wrapper instance
|
bool m_attrFsmRegisterWrapper : 1; // connected to an fsm_register_wrapper instance
|
||||||
bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment
|
bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment
|
||||||
bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment
|
bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment
|
||||||
|
bool m_constPoolEntry : 1; // Constant pool variable
|
||||||
bool m_fileDescr : 1; // File descriptor
|
bool m_fileDescr : 1; // File descriptor
|
||||||
bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration
|
bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration
|
||||||
|
bool m_icoMaybeWritten : 1; // Design might write this input signal - for ico change detect
|
||||||
bool m_isConst : 1; // Table contains constant data
|
bool m_isConst : 1; // Table contains constant data
|
||||||
bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
|
bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
|
||||||
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
|
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
|
||||||
|
|
@ -2194,15 +2209,16 @@ class AstVar final : public AstNode {
|
||||||
m_funcReturn = false;
|
m_funcReturn = false;
|
||||||
m_attrScBv = false;
|
m_attrScBv = false;
|
||||||
m_attrScBigUint = false;
|
m_attrScBigUint = false;
|
||||||
m_attrIsolateAssign = false;
|
|
||||||
m_attrSFormat = false;
|
m_attrSFormat = false;
|
||||||
m_attrSplitVar = false;
|
m_attrSplitVar = false;
|
||||||
m_attrFsmState = false;
|
m_attrFsmState = false;
|
||||||
m_attrFsmRegisterWrapper = false;
|
m_attrFsmRegisterWrapper = false;
|
||||||
m_attrFsmResetArc = false;
|
m_attrFsmResetArc = false;
|
||||||
m_attrFsmArcInclCond = false;
|
m_attrFsmArcInclCond = false;
|
||||||
|
m_constPoolEntry = false;
|
||||||
m_fileDescr = false;
|
m_fileDescr = false;
|
||||||
m_gotNansiType = false;
|
m_gotNansiType = false;
|
||||||
|
m_icoMaybeWritten = false;
|
||||||
m_isConst = false;
|
m_isConst = false;
|
||||||
m_isContinuously = false;
|
m_isContinuously = false;
|
||||||
m_hasStrengthAssignment = false;
|
m_hasStrengthAssignment = false;
|
||||||
|
|
@ -2343,13 +2359,14 @@ public:
|
||||||
void attrFileDescr(bool flag) { m_fileDescr = flag; }
|
void attrFileDescr(bool flag) { m_fileDescr = flag; }
|
||||||
void attrScBv(bool flag) { m_attrScBv = flag; }
|
void attrScBv(bool flag) { m_attrScBv = flag; }
|
||||||
void attrScBigUint(bool flag) { m_attrScBigUint = flag; }
|
void attrScBigUint(bool flag) { m_attrScBigUint = flag; }
|
||||||
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
|
|
||||||
void attrSFormat(bool flag) { m_attrSFormat = flag; }
|
void attrSFormat(bool flag) { m_attrSFormat = flag; }
|
||||||
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
|
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
|
||||||
void attrFsmState(bool flag) { m_attrFsmState = flag; }
|
void attrFsmState(bool flag) { m_attrFsmState = flag; }
|
||||||
void attrFsmRegisterWrapper(bool flag) { m_attrFsmRegisterWrapper = flag; }
|
void attrFsmRegisterWrapper(bool flag) { m_attrFsmRegisterWrapper = flag; }
|
||||||
void attrFsmResetArc(bool flag) { m_attrFsmResetArc = flag; }
|
void attrFsmResetArc(bool flag) { m_attrFsmResetArc = flag; }
|
||||||
void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; }
|
void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; }
|
||||||
|
bool constPoolEntry() const { return m_constPoolEntry; }
|
||||||
|
void setConstPoolEntry() { m_constPoolEntry = true; }
|
||||||
void rand(const VRandAttr flag) { m_rand = flag; }
|
void rand(const VRandAttr flag) { m_rand = flag; }
|
||||||
void usedParam(bool flag) { m_usedParam = flag; }
|
void usedParam(bool flag) { m_usedParam = flag; }
|
||||||
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
|
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
|
||||||
|
|
@ -2383,6 +2400,8 @@ public:
|
||||||
void hasStrengthAssignment(bool flag) { m_hasStrengthAssignment = flag; }
|
void hasStrengthAssignment(bool flag) { m_hasStrengthAssignment = flag; }
|
||||||
bool hasUserInit() const { return m_hasUserInit; }
|
bool hasUserInit() const { return m_hasUserInit; }
|
||||||
void hasUserInit(bool flag) { m_hasUserInit = flag; }
|
void hasUserInit(bool flag) { m_hasUserInit = flag; }
|
||||||
|
void icoMaybeWritten(bool flag) { m_icoMaybeWritten = flag; }
|
||||||
|
bool icoMaybeWritten() const { return m_icoMaybeWritten; }
|
||||||
bool isDpiOpenArray() const VL_MT_SAFE { return m_isDpiOpenArray; }
|
bool isDpiOpenArray() const VL_MT_SAFE { return m_isDpiOpenArray; }
|
||||||
void isDpiOpenArray(bool flag) { m_isDpiOpenArray = flag; }
|
void isDpiOpenArray(bool flag) { m_isDpiOpenArray = flag; }
|
||||||
bool isHideLocal() const { return m_isHideLocal; }
|
bool isHideLocal() const { return m_isHideLocal; }
|
||||||
|
|
@ -2462,12 +2481,6 @@ public:
|
||||||
bool isWor() const { return varType().isWor(); }
|
bool isWor() const { return varType().isWor(); }
|
||||||
bool isWiredNet() const { return varType().isWiredNet(); }
|
bool isWiredNet() const { return varType().isWiredNet(); }
|
||||||
bool isTemp() const { return varType().isTemp(); }
|
bool isTemp() const { return varType().isTemp(); }
|
||||||
bool isToggleCoverable() const {
|
|
||||||
return ((isIO() || isSignal())
|
|
||||||
&& (isIO() || isBitLogic())
|
|
||||||
// Wrapper would otherwise duplicate wrapped module's coverage
|
|
||||||
&& !isSc() && !isPrimaryIO() && !isConst() && !isDouble() && !isString());
|
|
||||||
}
|
|
||||||
bool isClassMember() const { return varType() == VVarType::MEMBER; }
|
bool isClassMember() const { return varType() == VVarType::MEMBER; }
|
||||||
bool isVirtIface() const {
|
bool isVirtIface() const {
|
||||||
if (AstIfaceRefDType* const dtp = VN_CAST(dtypep(), IfaceRefDType)) {
|
if (AstIfaceRefDType* const dtp = VN_CAST(dtypep(), IfaceRefDType)) {
|
||||||
|
|
@ -2519,7 +2532,6 @@ public:
|
||||||
bool attrFsmRegisterWrapper() const { return m_attrFsmRegisterWrapper; }
|
bool attrFsmRegisterWrapper() const { return m_attrFsmRegisterWrapper; }
|
||||||
bool attrFsmResetArc() const { return m_attrFsmResetArc; }
|
bool attrFsmResetArc() const { return m_attrFsmResetArc; }
|
||||||
bool attrFsmArcInclCond() const { return m_attrFsmArcInclCond; }
|
bool attrFsmArcInclCond() const { return m_attrFsmArcInclCond; }
|
||||||
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
|
|
||||||
AstIface* sensIfacep() const { return m_sensIfacep; }
|
AstIface* sensIfacep() const { return m_sensIfacep; }
|
||||||
VRandAttr rand() const { return m_rand; }
|
VRandAttr rand() const { return m_rand; }
|
||||||
string verilogKwd() const override;
|
string verilogKwd() const override;
|
||||||
|
|
@ -2531,7 +2543,6 @@ public:
|
||||||
// This is getting connected to fromp; keep attributes
|
// This is getting connected to fromp; keep attributes
|
||||||
// Note the method below too
|
// Note the method below too
|
||||||
if (fromp->attrFileDescr()) attrFileDescr(true);
|
if (fromp->attrFileDescr()) attrFileDescr(true);
|
||||||
if (fromp->attrIsolateAssign()) attrIsolateAssign(true);
|
|
||||||
if (fromp->isContinuously()) isContinuously(true);
|
if (fromp->isContinuously()) isContinuously(true);
|
||||||
}
|
}
|
||||||
void propagateWrapAttrFrom(const AstVar* fromp) {
|
void propagateWrapAttrFrom(const AstVar* fromp) {
|
||||||
|
|
@ -2810,6 +2821,7 @@ class AstGenBlock final : public AstNodeGen {
|
||||||
std::string m_name; // Name of block
|
std::string m_name; // Name of block
|
||||||
const bool m_unnamed; // Originally unnamed (name change does not affect this)
|
const bool m_unnamed; // Originally unnamed (name change does not affect this)
|
||||||
const bool m_implied; // Not inserted by user
|
const bool m_implied; // Not inserted by user
|
||||||
|
AstDefaultDisable* m_defaultDisablep = nullptr; // Default disable iff in this scope
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AstGenBlock(FileLine* fl, const string& name, AstNode* itemsp, bool implied)
|
AstGenBlock(FileLine* fl, const string& name, AstNode* itemsp, bool implied)
|
||||||
|
|
@ -2826,6 +2838,8 @@ public:
|
||||||
void name(const std::string& name) override { m_name = name; }
|
void name(const std::string& name) override { m_name = name; }
|
||||||
bool unnamed() const { return m_unnamed; }
|
bool unnamed() const { return m_unnamed; }
|
||||||
bool implied() const { return m_implied; }
|
bool implied() const { return m_implied; }
|
||||||
|
AstDefaultDisable* defaultDisablep() const { return m_defaultDisablep; }
|
||||||
|
void defaultDisablep(AstDefaultDisable* nodep) { m_defaultDisablep = nodep; }
|
||||||
};
|
};
|
||||||
class AstGenCase final : public AstNodeGen {
|
class AstGenCase final : public AstNodeGen {
|
||||||
// Generate 'case'
|
// Generate 'case'
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ protected:
|
||||||
public:
|
public:
|
||||||
ASTGEN_MEMBERS_AstNodeAssign;
|
ASTGEN_MEMBERS_AstNodeAssign;
|
||||||
// Clone single node, just get same type back.
|
// Clone single node, just get same type back.
|
||||||
|
void dump(std::ostream& str) const override;
|
||||||
virtual AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) = 0;
|
virtual AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) = 0;
|
||||||
bool hasDType() const override VL_MT_SAFE { return true; }
|
bool hasDType() const override VL_MT_SAFE { return true; }
|
||||||
virtual bool cleanRhs() const { return true; }
|
virtual bool cleanRhs() const { return true; }
|
||||||
|
|
@ -941,13 +942,16 @@ public:
|
||||||
class AstPExprClause final : public AstNodeStmt {
|
class AstPExprClause final : public AstNodeStmt {
|
||||||
const bool m_pass; // True if will be replaced by passing assertion clause, false for
|
const bool m_pass; // True if will be replaced by passing assertion clause, false for
|
||||||
// assertion failure clause
|
// assertion failure clause
|
||||||
|
const bool m_vacuous; // True if pass is vacuous
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ASTGEN_MEMBERS_AstPExprClause;
|
ASTGEN_MEMBERS_AstPExprClause;
|
||||||
explicit AstPExprClause(FileLine* fl, bool pass = true)
|
explicit AstPExprClause(FileLine* fl, bool pass = true, bool vacuous = false)
|
||||||
: ASTGEN_SUPER_PExprClause(fl)
|
: ASTGEN_SUPER_PExprClause(fl)
|
||||||
, m_pass{pass} {}
|
, m_pass{pass}
|
||||||
|
, m_vacuous{vacuous} {}
|
||||||
bool pass() const { return m_pass; }
|
bool pass() const { return m_pass; }
|
||||||
|
bool vacuous() const { return m_vacuous; }
|
||||||
};
|
};
|
||||||
class AstPrintTimeScale final : public AstNodeStmt {
|
class AstPrintTimeScale final : public AstNodeStmt {
|
||||||
// Parents: stmtlist
|
// Parents: stmtlist
|
||||||
|
|
@ -1606,12 +1610,18 @@ public:
|
||||||
};
|
};
|
||||||
class AstCover final : public AstNodeCoverOrAssert {
|
class AstCover final : public AstNodeCoverOrAssert {
|
||||||
// @astgen op3 := coverincsp: List[AstNode] // Coverage node
|
// @astgen op3 := coverincsp: List[AstNode] // Coverage node
|
||||||
|
bool m_isCoverSeq = false; // 'cover sequence' (IEEE 1800-2023 16.14.3): fires per
|
||||||
|
// end-of-match, not per property success
|
||||||
public:
|
public:
|
||||||
ASTGEN_MEMBERS_AstCover;
|
ASTGEN_MEMBERS_AstCover;
|
||||||
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type,
|
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type,
|
||||||
const string& name = "")
|
const string& name = "")
|
||||||
: ASTGEN_SUPER_Cover(fl, propp, stmtsp, type, VAssertDirectiveType::COVER, name) {}
|
: ASTGEN_SUPER_Cover(fl, propp, stmtsp, type, VAssertDirectiveType::COVER, name) {}
|
||||||
string verilogKwd() const override { return "cover"; }
|
string verilogKwd() const override { return "cover"; }
|
||||||
|
void dump(std::ostream& str) const override;
|
||||||
|
void dumpJson(std::ostream& str) const override;
|
||||||
|
bool isCoverSeq() const { return m_isCoverSeq; }
|
||||||
|
void isCoverSeq(bool flag) { m_isCoverSeq = flag; }
|
||||||
};
|
};
|
||||||
class AstRestrict final : public AstNodeCoverOrAssert {
|
class AstRestrict final : public AstNodeCoverOrAssert {
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -546,6 +546,26 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
|
||||||
|
|
||||||
string AstConstraintRef::name() const { return constrp()->name(); }
|
string AstConstraintRef::name() const { return constrp()->name(); }
|
||||||
|
|
||||||
|
uint32_t AstMatchMasked::fold(const V3Number& lhs, AstVar* matchVarp) {
|
||||||
|
const V3Number& numTable = VN_AS(matchVarp->valuep(), Const)->num();
|
||||||
|
V3Number numMask{matchVarp, lhs.width(), 0};
|
||||||
|
V3Number numBits{matchVarp, lhs.width(), 0};
|
||||||
|
V3Number numAnd{matchVarp, lhs.width(), 0};
|
||||||
|
const int width = lhs.width();
|
||||||
|
const int entryWidth = VL_WORDS_I(width) * VL_EDATASIZE;
|
||||||
|
uint32_t i = 0;
|
||||||
|
while (true) {
|
||||||
|
const int lsb = 2 * i * entryWidth;
|
||||||
|
const int msb = lsb + width - 1;
|
||||||
|
numMask.opSel(numTable, msb, lsb);
|
||||||
|
numBits.opSel(numTable, msb + entryWidth, lsb + entryWidth);
|
||||||
|
numAnd.opAnd(numMask, lhs);
|
||||||
|
if (numAnd.isCaseEq(numBits)) break;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
AstNetlist::AstNetlist()
|
AstNetlist::AstNetlist()
|
||||||
: ASTGEN_SUPER_Netlist(new FileLine{FileLine::builtInFilename()})
|
: ASTGEN_SUPER_Netlist(new FileLine{FileLine::builtInFilename()})
|
||||||
, m_typeTablep{new AstTypeTable{fileline()}}
|
, m_typeTablep{new AstTypeTable{fileline()}}
|
||||||
|
|
@ -630,6 +650,7 @@ void AstVar::combineType(const AstVar* otherp) {
|
||||||
varType(otherp->varType());
|
varType(otherp->varType());
|
||||||
direction(otherp->direction());
|
direction(otherp->direction());
|
||||||
}
|
}
|
||||||
|
if (otherp->icoMaybeWritten()) icoMaybeWritten(true);
|
||||||
}
|
}
|
||||||
void AstVar::combineType(VVarType type) {
|
void AstVar::combineType(VVarType type) {
|
||||||
// These flags get combined with the existing settings of the flags.
|
// These flags get combined with the existing settings of the flags.
|
||||||
|
|
@ -682,9 +703,14 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
||||||
return ostatic + dtypep()->cType(oname, forFunc, asRef);
|
return ostatic + dtypep()->cType(oname, forFunc, asRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
string AstVar::vlEnumType() const {
|
string AstNodeDType::vlEnumType() const {
|
||||||
string arg;
|
string arg;
|
||||||
const AstBasicDType* const bdtypep = basicp();
|
const AstNodeDType* dtypep = skipRefp();
|
||||||
|
while (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||||
|
dtypep = adtypep->subDTypep()->skipRefp();
|
||||||
|
}
|
||||||
|
const AstBasicDType* const bdtypep = dtypep->basicp();
|
||||||
|
const AstNodeUOrStructDType* const sdtypep = VN_CAST(dtypep, NodeUOrStructDType);
|
||||||
const bool strtype = bdtypep && bdtypep->keyword() == VBasicDTypeKwd::STRING;
|
const bool strtype = bdtypep && bdtypep->keyword() == VBasicDTypeKwd::STRING;
|
||||||
if (bdtypep && bdtypep->keyword() == VBasicDTypeKwd::CHARPTR) {
|
if (bdtypep && bdtypep->keyword() == VBasicDTypeKwd::CHARPTR) {
|
||||||
return "VLVT_PTR";
|
return "VLVT_PTR";
|
||||||
|
|
@ -694,6 +720,8 @@ string AstVar::vlEnumType() const {
|
||||||
arg += "VLVT_STRING";
|
arg += "VLVT_STRING";
|
||||||
} else if (isDouble()) {
|
} else if (isDouble()) {
|
||||||
arg += "VLVT_REAL";
|
arg += "VLVT_REAL";
|
||||||
|
} else if (sdtypep && !sdtypep->packed()) {
|
||||||
|
arg += VN_IS(sdtypep, StructDType) ? "VLVT_STRUCT" : "VLVT_UNION";
|
||||||
} else if (widthMin() <= 8) {
|
} else if (widthMin() <= 8) {
|
||||||
arg += "VLVT_UINT8";
|
arg += "VLVT_UINT8";
|
||||||
} else if (widthMin() <= 16) {
|
} else if (widthMin() <= 16) {
|
||||||
|
|
@ -709,6 +737,8 @@ string AstVar::vlEnumType() const {
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string AstVar::vlEnumType() const { return dtypep()->vlEnumType(); }
|
||||||
|
|
||||||
string AstVar::vlEnumDir() const {
|
string AstVar::vlEnumDir() const {
|
||||||
string out;
|
string out;
|
||||||
if (isInout()) {
|
if (isInout()) {
|
||||||
|
|
@ -738,6 +768,7 @@ string AstVar::vlEnumDir() const {
|
||||||
if (AstBasicDType* const basicp = dtypep()->skipRefp()->basicp()) {
|
if (AstBasicDType* const basicp = dtypep()->skipRefp()->basicp()) {
|
||||||
if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR";
|
if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR";
|
||||||
}
|
}
|
||||||
|
if (isNet()) out += "|VLVF_NET";
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1259,6 +1290,47 @@ uint32_t AstNodeDType::arrayUnpackedElements() const {
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AstNodeDType::isStreamableFixedAggregate() const {
|
||||||
|
const AstNodeDType* const dtypep = skipRefp();
|
||||||
|
if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||||
|
return adtypep->subDTypep()->isStreamableFixedAggregate();
|
||||||
|
} else if (const AstNodeUOrStructDType* const sdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||||
|
if (sdtypep->packed()) return true;
|
||||||
|
if (!VN_IS(sdtypep, StructDType)) return false;
|
||||||
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||||
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||||
|
if (!itemp->dtypep()->isStreamableFixedAggregate()) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return dtypep->isIntegralOrPacked() || dtypep->isDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AstNodeDType::containsUnpackedStruct() const {
|
||||||
|
const AstNodeDType* const dtypep = skipRefp();
|
||||||
|
if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||||
|
return adtypep->subDTypep()->containsUnpackedStruct();
|
||||||
|
}
|
||||||
|
const AstStructDType* const sdtypep = VN_CAST(dtypep, StructDType);
|
||||||
|
return sdtypep && !sdtypep->packed();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AstNodeDType::widthStream() const {
|
||||||
|
const AstNodeDType* const dtypep = skipRefp();
|
||||||
|
if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||||
|
return adtypep->subDTypep()->widthStream() * adtypep->elementsConst();
|
||||||
|
} else if (const AstNodeUOrStructDType* const sdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||||
|
if (!VN_IS(sdtypep, StructDType) || sdtypep->packed()) return width();
|
||||||
|
int width = 0;
|
||||||
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||||
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||||
|
width += itemp->dtypep()->widthStream();
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
return dtypep->width();
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) const {
|
std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) const {
|
||||||
// How many array dimensions (packed,unpacked) does this Var have?
|
// How many array dimensions (packed,unpacked) does this Var have?
|
||||||
uint32_t packed = 0;
|
uint32_t packed = 0;
|
||||||
|
|
@ -1322,6 +1394,12 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
|
||||||
} else if (VN_IS(nodep, Sel)) {
|
} else if (VN_IS(nodep, Sel)) {
|
||||||
nodep = VN_AS(nodep, Sel)->fromp();
|
nodep = VN_AS(nodep, Sel)->fromp();
|
||||||
continue;
|
continue;
|
||||||
|
} else if (VN_IS(nodep, AssocSel)) {
|
||||||
|
nodep = VN_AS(nodep, AssocSel)->fromp();
|
||||||
|
continue;
|
||||||
|
} else if (VN_IS(nodep, WildcardSel)) {
|
||||||
|
nodep = VN_AS(nodep, WildcardSel)->fromp();
|
||||||
|
continue;
|
||||||
} else if (overMembers && VN_IS(nodep, MemberSel)) {
|
} else if (overMembers && VN_IS(nodep, MemberSel)) {
|
||||||
nodep = VN_AS(nodep, MemberSel)->fromp();
|
nodep = VN_AS(nodep, MemberSel)->fromp();
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1581,6 +1659,7 @@ AstConstPool::AstConstPool(FileLine* fl)
|
||||||
AstVarScope* AstConstPool::createNewEntry(const string& name, AstNodeExpr* initp) {
|
AstVarScope* AstConstPool::createNewEntry(const string& name, AstNodeExpr* initp) {
|
||||||
FileLine* const fl = initp->fileline();
|
FileLine* const fl = initp->fileline();
|
||||||
AstVar* const varp = new AstVar{fl, VVarType::MODULETEMP, name, initp->dtypep()};
|
AstVar* const varp = new AstVar{fl, VVarType::MODULETEMP, name, initp->dtypep()};
|
||||||
|
varp->setConstPoolEntry();
|
||||||
varp->isConst(true);
|
varp->isConst(true);
|
||||||
varp->isStatic(true);
|
varp->isStatic(true);
|
||||||
varp->valuep(initp->cloneTree(false));
|
varp->valuep(initp->cloneTree(false));
|
||||||
|
|
@ -1700,6 +1779,17 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
|
||||||
return varScopep;
|
return varScopep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AstConstPool::reCache() {
|
||||||
|
m_tables.clear();
|
||||||
|
m_consts.clear();
|
||||||
|
for (AstVarScope* vscp = m_scopep->varsp(); vscp; vscp = VN_CAST(vscp->nextp(), VarScope)) {
|
||||||
|
AstNode* const valuep = vscp->varp()->valuep();
|
||||||
|
const V3Hash hash = V3Hasher::uncachedHash(valuep);
|
||||||
|
if (VN_IS(valuep, InitArray)) m_tables.emplace(hash.value(), vscp);
|
||||||
|
if (VN_IS(valuep, Const)) m_consts.emplace(hash.value(), vscp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//======================================================================
|
//======================================================================
|
||||||
// Per-type Debugging
|
// Per-type Debugging
|
||||||
|
|
||||||
|
|
@ -2133,6 +2223,14 @@ void AstNodeCoverOrAssert::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonBoolFuncIf(str, immediate);
|
dumpJsonBoolFuncIf(str, immediate);
|
||||||
dumpJsonBoolFuncIf(str, senFromAlways);
|
dumpJsonBoolFuncIf(str, senFromAlways);
|
||||||
}
|
}
|
||||||
|
void AstCover::dump(std::ostream& str) const {
|
||||||
|
this->AstNodeCoverOrAssert::dump(str);
|
||||||
|
if (isCoverSeq()) str << " [COVERSEQ]";
|
||||||
|
}
|
||||||
|
void AstCover::dumpJson(std::ostream& str) const {
|
||||||
|
dumpJsonBoolFuncIf(str, isCoverSeq);
|
||||||
|
this->AstNodeCoverOrAssert::dumpJson(str);
|
||||||
|
}
|
||||||
void AstClocking::dump(std::ostream& str) const {
|
void AstClocking::dump(std::ostream& str) const {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
if (isDefault()) str << " [DEFAULT]";
|
if (isDefault()) str << " [DEFAULT]";
|
||||||
|
|
@ -2724,6 +2822,10 @@ void AstNodeArrayDType::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonStr(str, "declRange", cvtToStr(declRange()));
|
dumpJsonStr(str, "declRange", cvtToStr(declRange()));
|
||||||
dumpJsonGen(str);
|
dumpJsonGen(str);
|
||||||
}
|
}
|
||||||
|
void AstNodeAssign::dump(std::ostream& str) const {
|
||||||
|
this->AstNode::dump(str);
|
||||||
|
if (timingControlp()) str << " [TIMING=" << nodeAddr(timingControlp()) << "]";
|
||||||
|
}
|
||||||
string AstPackArrayDType::prettyDTypeName(bool full) const {
|
string AstPackArrayDType::prettyDTypeName(bool full) const {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
if (const auto subp = subDTypep()) os << subp->prettyDTypeName(full);
|
if (const auto subp = subDTypep()) os << subp->prettyDTypeName(full);
|
||||||
|
|
@ -3138,6 +3240,7 @@ int AstVarRef::instrCount() const {
|
||||||
}
|
}
|
||||||
void AstVar::dump(std::ostream& str) const {
|
void AstVar::dump(std::ostream& str) const {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
|
if (constPoolEntry()) str << " [CONSTPOOL]";
|
||||||
if (isSc()) str << " [SC]";
|
if (isSc()) str << " [SC]";
|
||||||
if (isPrimaryIO()) str << (isInout() ? " [PIO]" : (isWritable() ? " [PO]" : " [PI]"));
|
if (isPrimaryIO()) str << (isInout() ? " [PIO]" : (isWritable() ? " [PO]" : " [PI]"));
|
||||||
if (isPrimaryClock()) str << " [PCLK]";
|
if (isPrimaryClock()) str << " [PCLK]";
|
||||||
|
|
@ -3158,7 +3261,6 @@ void AstVar::dump(std::ostream& str) const {
|
||||||
if (noReset()) str << " [!RST]";
|
if (noReset()) str << " [!RST]";
|
||||||
if (processQueue()) str << " [PROCQ]";
|
if (processQueue()) str << " [PROCQ]";
|
||||||
if (sampled()) str << " [SAMPLED]";
|
if (sampled()) str << " [SAMPLED]";
|
||||||
if (attrIsolateAssign()) str << " [aISO]";
|
|
||||||
if (attrFsmState()) str << " [aFSMSTATE]";
|
if (attrFsmState()) str << " [aFSMSTATE]";
|
||||||
if (attrFsmResetArc()) str << " [aFSMRESETARC]";
|
if (attrFsmResetArc()) str << " [aFSMRESETARC]";
|
||||||
if (attrFsmArcInclCond()) str << " [aFSMARCCOND]";
|
if (attrFsmArcInclCond()) str << " [aFSMARCCOND]";
|
||||||
|
|
@ -3169,6 +3271,7 @@ void AstVar::dump(std::ostream& str) const {
|
||||||
str << " [FUNC]";
|
str << " [FUNC]";
|
||||||
}
|
}
|
||||||
if (hasUserInit()) str << " [UINIT]";
|
if (hasUserInit()) str << " [UINIT]";
|
||||||
|
if (icoMaybeWritten()) str << " [ICOMAYBEWRITTEN]";
|
||||||
if (isDpiOpenArray()) str << " [DPIOPENA]";
|
if (isDpiOpenArray()) str << " [DPIOPENA]";
|
||||||
if (ignorePostWrite()) str << " [IGNPWR]";
|
if (ignorePostWrite()) str << " [IGNPWR]";
|
||||||
if (ignoreSchedWrite()) str << " [IGNWR]";
|
if (ignoreSchedWrite()) str << " [IGNWR]";
|
||||||
|
|
@ -3179,6 +3282,7 @@ void AstVar::dump(std::ostream& str) const {
|
||||||
void AstVar::dumpJson(std::ostream& str) const {
|
void AstVar::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonStrFunc(str, origName);
|
dumpJsonStrFunc(str, origName);
|
||||||
dumpJsonStrFunc(str, verilogName);
|
dumpJsonStrFunc(str, verilogName);
|
||||||
|
dumpJsonBoolFuncIf(str, constPoolEntry);
|
||||||
dumpJsonBoolFuncIf(str, isSc);
|
dumpJsonBoolFuncIf(str, isSc);
|
||||||
dumpJsonBoolFuncIf(str, isPrimaryIO);
|
dumpJsonBoolFuncIf(str, isPrimaryIO);
|
||||||
dumpJsonBoolFuncIf(str, isPrimaryClock);
|
dumpJsonBoolFuncIf(str, isPrimaryClock);
|
||||||
|
|
@ -3193,11 +3297,11 @@ void AstVar::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonBoolFuncIf(str, noReset);
|
dumpJsonBoolFuncIf(str, noReset);
|
||||||
dumpJsonBoolFuncIf(str, processQueue);
|
dumpJsonBoolFuncIf(str, processQueue);
|
||||||
dumpJsonBoolFuncIf(str, sampled);
|
dumpJsonBoolFuncIf(str, sampled);
|
||||||
dumpJsonBoolFuncIf(str, attrIsolateAssign);
|
|
||||||
dumpJsonBoolFuncIf(str, attrFsmState);
|
dumpJsonBoolFuncIf(str, attrFsmState);
|
||||||
dumpJsonBoolFuncIf(str, attrFsmResetArc);
|
dumpJsonBoolFuncIf(str, attrFsmResetArc);
|
||||||
dumpJsonBoolFuncIf(str, attrFsmArcInclCond);
|
dumpJsonBoolFuncIf(str, attrFsmArcInclCond);
|
||||||
dumpJsonBoolFuncIf(str, attrFileDescr);
|
dumpJsonBoolFuncIf(str, attrFileDescr);
|
||||||
|
dumpJsonBoolFuncIf(str, icoMaybeWritten);
|
||||||
dumpJsonBoolFuncIf(str, isDpiOpenArray);
|
dumpJsonBoolFuncIf(str, isDpiOpenArray);
|
||||||
dumpJsonBoolFuncIf(str, isFuncReturn);
|
dumpJsonBoolFuncIf(str, isFuncReturn);
|
||||||
dumpJsonBoolFuncIf(str, isFuncLocal);
|
dumpJsonBoolFuncIf(str, isFuncLocal);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
// DESCRIPTION: Verilator: Break always into separate statements to reduce temps
|
// DESCRIPTION: Verilator: Dump AstNodeExpr patterns for analysis
|
||||||
//
|
//
|
||||||
// Code available from: https://verilator.org
|
// Code available from: https://verilator.org
|
||||||
//
|
//
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
//
|
//
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#ifndef VERILATOR_V3SPLITAS_H_
|
#ifndef VERILATOR_V3ASTPATTERNS_H_
|
||||||
#define VERILATOR_V3SPLITAS_H_
|
#define VERILATOR_V3ASTPATTERNS_H_
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
|
@ -24,9 +24,11 @@ class AstNetlist;
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
class V3SplitAs final {
|
class V3AstPatterns final {
|
||||||
public:
|
public:
|
||||||
static void splitAsAll(AstNetlist* nodep) VL_MT_DISABLED;
|
// Accumulate the patterns in the netlist, and dump the collected
|
||||||
|
// statistics to '<prefix>__ast_patterns_<suffix>.txt'.
|
||||||
|
static void dumpAll(const AstNetlist* nodep, const std::string& suffix) VL_MT_DISABLED;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|
@ -61,7 +61,6 @@ class BeginVisitor final : public VNVisitor {
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// AstCase::user1 -> bool, if already purified
|
// AstCase::user1 -> bool, if already purified
|
||||||
|
|
||||||
V3UniqueNames m_caseTempNames; // For generating unique temporary variable names used by cases
|
|
||||||
// STATE - across all visitors
|
// STATE - across all visitors
|
||||||
BeginState* const m_statep; // Current global state
|
BeginState* const m_statep; // Current global state
|
||||||
|
|
||||||
|
|
@ -75,7 +74,6 @@ class BeginVisitor final : public VNVisitor {
|
||||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||||
int m_ifDepth = 0; // Current if depth
|
int m_ifDepth = 0; // Current if depth
|
||||||
bool m_keepBegins = false; // True if begins should not be inlined
|
bool m_keepBegins = false; // True if begins should not be inlined
|
||||||
VDouble0 m_statPurifiedCaseExpr; // Count of purified case expressions
|
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
||||||
|
|
@ -132,7 +130,6 @@ class BeginVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
void visit(AstNodeModule* nodep) override {
|
void visit(AstNodeModule* nodep) override {
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
VL_RESTORER(m_caseTempNames);
|
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
// Rename it (e.g. class under a generate)
|
// Rename it (e.g. class under a generate)
|
||||||
if (m_unnamedScope != "") {
|
if (m_unnamedScope != "") {
|
||||||
|
|
@ -171,7 +168,6 @@ class BeginVisitor final : public VNVisitor {
|
||||||
VL_RESTORER(m_liftedp);
|
VL_RESTORER(m_liftedp);
|
||||||
VL_RESTORER(m_namedScope);
|
VL_RESTORER(m_namedScope);
|
||||||
VL_RESTORER(m_unnamedScope);
|
VL_RESTORER(m_unnamedScope);
|
||||||
VL_RESTORER(m_caseTempNames);
|
|
||||||
m_displayScope = dot(m_displayScope, nodep->name());
|
m_displayScope = dot(m_displayScope, nodep->name());
|
||||||
m_namedScope = "";
|
m_namedScope = "";
|
||||||
m_unnamedScope = "";
|
m_unnamedScope = "";
|
||||||
|
|
@ -415,29 +411,7 @@ class BeginVisitor final : public VNVisitor {
|
||||||
// any BEGINs, but V3Coverage adds them all under the module itself.
|
// any BEGINs, but V3Coverage adds them all under the module itself.
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstCase* nodep) override {
|
void visit(AstCase* nodep) override { iterateChildren(nodep); }
|
||||||
// Introduce temporary variable for AstCase if needed - it is done here and not in V3Case
|
|
||||||
// because this phase is before V3Scope and V3Case is not. Doing it before V3Scope ensures
|
|
||||||
// that V3Scope will take care of a scope creation
|
|
||||||
if (!nodep->exprp()->isPure() && !nodep->user1SetOnce()) {
|
|
||||||
++m_statPurifiedCaseExpr;
|
|
||||||
FileLine* const fl = nodep->exprp()->fileline();
|
|
||||||
AstVar* const varp = new AstVar{fl, VVarType::XTEMP, m_caseTempNames.get(nodep),
|
|
||||||
nodep->exprp()->dtypep()};
|
|
||||||
nodep->exprp(new AstExprStmt{fl,
|
|
||||||
new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
|
||||||
nodep->exprp()->unlinkFrBack()},
|
|
||||||
new AstVarRef{fl, varp, VAccess::READ}});
|
|
||||||
if (m_ftaskp) {
|
|
||||||
varp->funcLocal(true);
|
|
||||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
|
||||||
m_ftaskp->stmtsp()->addHereThisAsNext(varp);
|
|
||||||
} else {
|
|
||||||
m_modp->stmtsp()->addHereThisAsNext(varp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iterateChildren(nodep);
|
|
||||||
}
|
|
||||||
// VISITORS - LINT CHECK
|
// VISITORS - LINT CHECK
|
||||||
void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
||||||
VL_RESTORER(m_keepBegins);
|
VL_RESTORER(m_keepBegins);
|
||||||
|
|
@ -464,10 +438,8 @@ class BeginVisitor final : public VNVisitor {
|
||||||
public:
|
public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
BeginVisitor(AstNetlist* nodep, BeginState* statep)
|
BeginVisitor(AstNetlist* nodep, BeginState* statep)
|
||||||
: m_caseTempNames{"__VCase"}
|
: m_statep{statep} {
|
||||||
, m_statep{statep} {
|
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
V3Stats::addStatSum("Impure case expressions", m_statPurifiedCaseExpr);
|
|
||||||
}
|
}
|
||||||
~BeginVisitor() override = default;
|
~BeginVisitor() override = default;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ class CUseVisitor final : public VNVisitorConst {
|
||||||
iterateConst(nodep->lhsp()->dtypep());
|
iterateConst(nodep->lhsp()->dtypep());
|
||||||
}
|
}
|
||||||
void visit(AstNodeDType* nodep) override {
|
void visit(AstNodeDType* nodep) override {
|
||||||
|
if (nodep->user1SetOnce()) return; // Process once
|
||||||
if (nodep->virtRefDTypep()) iterateConst(nodep->virtRefDTypep());
|
if (nodep->virtRefDTypep()) iterateConst(nodep->virtRefDTypep());
|
||||||
if (nodep->virtRefDType2p()) iterateConst(nodep->virtRefDType2p());
|
if (nodep->virtRefDType2p()) iterateConst(nodep->virtRefDType2p());
|
||||||
|
|
||||||
|
|
|
||||||
1339
src/V3Case.cpp
1339
src/V3Case.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -232,6 +232,11 @@ class CleanVisitor final : public VNVisitor {
|
||||||
ensureClean(nodep->rhsp());
|
ensureClean(nodep->rhsp());
|
||||||
setClean(nodep, true);
|
setClean(nodep, true);
|
||||||
}
|
}
|
||||||
|
void visit(AstMatchMasked* nodep) override {
|
||||||
|
iterateChildren(nodep);
|
||||||
|
ensureClean(nodep->lhsp());
|
||||||
|
setClean(nodep, true);
|
||||||
|
}
|
||||||
void visit(AstSel* nodep) override {
|
void visit(AstSel* nodep) override {
|
||||||
operandBiop(nodep);
|
operandBiop(nodep);
|
||||||
setClean(nodep, nodep->cleanOut());
|
setClean(nodep, nodep->cleanOut());
|
||||||
|
|
|
||||||
241
src/V3Const.cpp
241
src/V3Const.cpp
|
|
@ -286,7 +286,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
||||||
|
|
||||||
// Get the mask that selects the bits that are relevant in this term
|
// Get the mask that selects the bits that are relevant in this term
|
||||||
V3Number maskNum{srcp, m_width, 0};
|
V3Number maskNum{srcp, m_width, 0};
|
||||||
maskNum.opBitsNonX(m_bitPolarity); // 'x' -> 0, 0->1, 1->1
|
maskNum.opBitsNonXZ(m_bitPolarity); // 'x' -> 0, 0->1, 1->1
|
||||||
const uint64_t maskVal = maskNum.toUQuad();
|
const uint64_t maskVal = maskNum.toUQuad();
|
||||||
UASSERT_OBJ(maskVal != 0, m_refp,
|
UASSERT_OBJ(maskVal != 0, m_refp,
|
||||||
"Should have been recognized as having const 0 result");
|
"Should have been recognized as having const 0 result");
|
||||||
|
|
@ -973,6 +973,96 @@ class ConstVisitor final : public VNVisitor {
|
||||||
return !numv.isNumber() ? numv : V3Number{nodep, nodep->width(), numv};
|
return !numv.isNumber() ? numv : V3Number{nodep, nodep->width(), numv};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool lowerAsFixedAggregate(const AstNodeDType* const dtypep) {
|
||||||
|
return dtypep->isStreamableFixedAggregate() && dtypep->containsUnpackedStruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
AstStructSel* newStructSel(AstNodeExpr* const fromp, const AstMemberDType* const itemp) {
|
||||||
|
AstStructSel* const selp = new AstStructSel{fromp->fileline(), fromp, itemp->name()};
|
||||||
|
selp->dtypeFrom(itemp->dtypep());
|
||||||
|
return selp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void collectFixedAggregateTerms(AstNodeExpr* const fromp, std::vector<AstNodeExpr*>& termps,
|
||||||
|
const bool packReal) {
|
||||||
|
const AstNodeDType* const dtypep = fromp->dtypep()->skipRefp();
|
||||||
|
if (const AstUnpackArrayDType* const unpackDtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||||
|
const int left = unpackDtypep->left();
|
||||||
|
const int right = unpackDtypep->right();
|
||||||
|
const int step = left <= right ? 1 : -1;
|
||||||
|
for (int idx = left;; idx += step) {
|
||||||
|
AstArraySel* const selp
|
||||||
|
= new AstArraySel{fromp->fileline(), fromp->cloneTreePure(false), idx};
|
||||||
|
collectFixedAggregateTerms(selp, termps, packReal);
|
||||||
|
if (idx == right) break;
|
||||||
|
}
|
||||||
|
VL_DO_DANGLING(pushDeletep(fromp), fromp);
|
||||||
|
} else if (const AstNodeUOrStructDType* const sdtypep
|
||||||
|
= VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||||
|
if (sdtypep->packed()) {
|
||||||
|
termps.push_back(fromp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||||
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||||
|
collectFixedAggregateTerms(newStructSel(fromp->cloneTreePure(false), itemp),
|
||||||
|
termps, packReal);
|
||||||
|
}
|
||||||
|
VL_DO_DANGLING(pushDeletep(fromp), fromp);
|
||||||
|
} else if (packReal && dtypep->isDouble()) {
|
||||||
|
termps.push_back(new AstRealToBits{fromp->fileline(), fromp});
|
||||||
|
} else {
|
||||||
|
termps.push_back(fromp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNodeExpr* packFixedAggregate(AstNodeExpr* const fromp) {
|
||||||
|
std::vector<AstNodeExpr*> termps;
|
||||||
|
collectFixedAggregateTerms(fromp, termps, true);
|
||||||
|
UASSERT(!termps.empty(), "No stream terms");
|
||||||
|
AstNodeExpr* resultp = termps[0];
|
||||||
|
for (size_t i = 1; i < termps.size(); ++i) {
|
||||||
|
resultp = new AstConcat{resultp->fileline(), resultp, termps[i]};
|
||||||
|
}
|
||||||
|
return resultp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void replaceAssignToFixedAggregate(AstNodeAssign* const nodep, AstNodeExpr* const dstp,
|
||||||
|
AstNodeExpr* srcp) {
|
||||||
|
const int dstWidth = dstp->dtypep()->widthStream();
|
||||||
|
std::vector<AstNodeExpr*> termps;
|
||||||
|
collectFixedAggregateTerms(dstp, termps, false);
|
||||||
|
int srcWidth = srcp->width();
|
||||||
|
if (srcWidth < dstWidth) {
|
||||||
|
AstExtend* const extendp = new AstExtend{srcp->fileline(), srcp};
|
||||||
|
extendp->dtypeSetLogicSized(dstWidth, VSigning::UNSIGNED);
|
||||||
|
srcp = new AstShiftL{
|
||||||
|
extendp->fileline(), extendp,
|
||||||
|
new AstConst{extendp->fileline(), static_cast<uint32_t>(dstWidth - srcWidth)},
|
||||||
|
dstWidth};
|
||||||
|
srcWidth = dstWidth;
|
||||||
|
}
|
||||||
|
AstNodeAssign* newp = nullptr;
|
||||||
|
int offset = 0;
|
||||||
|
for (size_t i = 0; i < termps.size(); ++i) {
|
||||||
|
AstNodeExpr* const termp = termps[i];
|
||||||
|
const int width = termp->dtypep()->widthStream();
|
||||||
|
const int lsb = srcWidth - offset - width;
|
||||||
|
offset += width;
|
||||||
|
AstNodeExpr* rhsp
|
||||||
|
= new AstSel{srcp->fileline(), srcp->cloneTreePure(false), lsb, width};
|
||||||
|
if (termp->dtypep()->skipRefp()->isDouble()) {
|
||||||
|
rhsp = new AstBitsToRealD{rhsp->fileline(), rhsp};
|
||||||
|
}
|
||||||
|
AstNodeAssign* const assignp = nodep->cloneType(termp, rhsp);
|
||||||
|
assignp->dtypeFrom(termp);
|
||||||
|
newp = AstNode::addNext(newp, assignp);
|
||||||
|
}
|
||||||
|
nodep->addNextHere(newp);
|
||||||
|
VL_DO_DANGLING(pushDeletep(srcp), srcp);
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
|
}
|
||||||
|
|
||||||
bool operandConst(const AstNode* nodep) { return VN_IS(nodep, Const); }
|
bool operandConst(const AstNode* nodep) { return VN_IS(nodep, Const); }
|
||||||
bool operandAsvConst(const AstNode* nodep) {
|
bool operandAsvConst(const AstNode* nodep) {
|
||||||
// BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...)
|
// BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...)
|
||||||
|
|
@ -1327,6 +1417,45 @@ class ConstVisitor final : public VNVisitor {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool matchMaskedZero(const AstAnd* nodep) {
|
||||||
|
// Turn masking of known zero bits into constant zero. Commonly appears after V3Expand.
|
||||||
|
const AstConst* const maskp = VN_AS(nodep->lhsp(), Const);
|
||||||
|
const AstNodeExpr* const rhsp = nodep->rhsp();
|
||||||
|
const uint32_t msbP1 = maskp->num().mostSetBitP1();
|
||||||
|
if (!msbP1) return false; // Don't rewrite, separate rule matches for this
|
||||||
|
const uint32_t msb = msbP1 - 1;
|
||||||
|
const uint32_t lsb = maskp->num().leastSetBitP1() - 1;
|
||||||
|
|
||||||
|
if (const AstShiftL* const shiftp = VN_CAST(rhsp, ShiftL)) {
|
||||||
|
// 'a << S' forces the low S bits to zero
|
||||||
|
if (AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) {
|
||||||
|
return scp->num().fitsInUInt() //
|
||||||
|
&& (scp->num().toUInt() > msb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (const AstShiftR* const shiftp = VN_CAST(rhsp, ShiftR)) {
|
||||||
|
// 'a >> S' forces the high S bits to zero. Check against the width of the shifted
|
||||||
|
// operand, V3Expand can create shifts wider than their inputs
|
||||||
|
if (AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) {
|
||||||
|
return scp->num().fitsInUInt()
|
||||||
|
&& (lsb + scp->num().toUInt()
|
||||||
|
>= static_cast<uint32_t>(shiftp->lhsp()->widthMin()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (const AstMul* const mulp = VN_CAST(rhsp, Mul)) {
|
||||||
|
// 'C * a' forces the low N bits to zero where 'C' has low zero bits
|
||||||
|
if (AstConst* const cp = VN_CAST(mulp->lhsp(), Const)) {
|
||||||
|
return cp->num().leastSetBitP1() > msb + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (const AstExtend* const extendp = VN_CAST(rhsp, Extend)) {
|
||||||
|
// Zero-extension forces the bits above the source width to zero
|
||||||
|
return lsb >= static_cast<uint32_t>(extendp->lhsp()->width());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool matchBitOpTree(AstNodeExpr* nodep) {
|
bool matchBitOpTree(AstNodeExpr* nodep) {
|
||||||
if (nodep->widthMin() != 1) return false;
|
if (nodep->widthMin() != 1) return false;
|
||||||
if (!v3Global.opt.fConstBitOpTree()) return false;
|
if (!v3Global.opt.fConstBitOpTree()) return false;
|
||||||
|
|
@ -1474,16 +1603,46 @@ class ConstVisitor final : public VNVisitor {
|
||||||
&& static_cast<int>(nodep->widthConst()) == nodep->fromp()->width());
|
&& static_cast<int>(nodep->widthConst()) == nodep->fromp()->width());
|
||||||
}
|
}
|
||||||
bool operandSelExtend(AstSel* nodep) {
|
bool operandSelExtend(AstSel* nodep) {
|
||||||
// A pattern created by []'s after offsets have been removed
|
if (!m_doV) return false;
|
||||||
// SEL(EXTEND(any,width,...),(width-1),0) -> ...
|
|
||||||
// Since select's return unsigned, this is always an extend
|
|
||||||
// cppcheck-suppress constVariablePointer // children unlinked below
|
|
||||||
AstExtend* const extendp = VN_CAST(nodep->fromp(), Extend);
|
AstExtend* const extendp = VN_CAST(nodep->fromp(), Extend);
|
||||||
if (!(m_doV && extendp && VN_IS(nodep->lsbp(), Const) && nodep->lsbConst() == 0
|
if (!extendp) return false;
|
||||||
&& static_cast<int>(nodep->widthConst()) == extendp->lhsp()->width()))
|
AstConst* const lsbp = VN_CAST(nodep->lsbp(), Const);
|
||||||
return false;
|
if (!lsbp) return false;
|
||||||
VL_DO_DANGLING(replaceWChild(nodep, extendp->lhsp()), nodep);
|
const int width = nodep->widthConst();
|
||||||
return true;
|
const int lsb = lsbp->toSInt();
|
||||||
|
const int msb = lsb + width - 1;
|
||||||
|
AstNodeExpr* const lhsp = extendp->lhsp();
|
||||||
|
if (!lhsp->isPure()) return false;
|
||||||
|
|
||||||
|
// Selecting the entire extended expression, replace with that
|
||||||
|
if (lsb == 0 && msb == lhsp->width() - 1) {
|
||||||
|
lhsp->unlinkFrBack();
|
||||||
|
nodep->replaceWithKeepDType(lhsp);
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Select entirely in the extended part - replace with zero
|
||||||
|
if (lsb >= lhsp->width()) {
|
||||||
|
replaceZero(nodep);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Select entirely in the extended expression - replace with select from that
|
||||||
|
if (msb < lhsp->width()) {
|
||||||
|
lhsp->unlinkFrBack();
|
||||||
|
lsbp->unlinkFrBack();
|
||||||
|
nodep->replaceWithKeepDType(new AstSel{nodep->fileline(), lhsp, lsbp, width});
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Select straddles both sides, but is just a shorter extend
|
||||||
|
if (lsb == 0) {
|
||||||
|
lhsp->unlinkFrBack();
|
||||||
|
nodep->replaceWithKeepDType(new AstExtend{nodep->fileline(), lhsp});
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
bool operandSelBiLower(AstSel* nodep) {
|
bool operandSelBiLower(AstSel* nodep) {
|
||||||
// SEL(ADD(a,b),(width-1),0) -> ADD(SEL(a),SEL(b))
|
// SEL(ADD(a,b),(width-1),0) -> ADD(SEL(a),SEL(b))
|
||||||
|
|
@ -2393,6 +2552,25 @@ class ConstVisitor final : public VNVisitor {
|
||||||
VL_DO_DANGLING(pushDeletep(conp), conp);
|
VL_DO_DANGLING(pushDeletep(conp), conp);
|
||||||
// Further reduce, either node may have more reductions.
|
// Further reduce, either node may have more reductions.
|
||||||
return true;
|
return true;
|
||||||
|
} else if (m_doV && VN_IS(nodep->rhsp(), CvtPackedToArray)
|
||||||
|
&& lowerAsFixedAggregate(nodep->lhsp()->dtypep())) {
|
||||||
|
AstCvtPackedToArray* const cvtp
|
||||||
|
= VN_AS(nodep->rhsp(), CvtPackedToArray)->unlinkFrBack();
|
||||||
|
AstNodeExpr* srcp = cvtp->fromp()->unlinkFrBack();
|
||||||
|
if (lowerAsFixedAggregate(srcp->dtypep())) {
|
||||||
|
srcp = packFixedAggregate(srcp);
|
||||||
|
} else if (AstNodeStream* const streamp = VN_CAST(srcp, NodeStream)) {
|
||||||
|
AstNodeExpr* const streamSrcp = streamp->lhsp();
|
||||||
|
if (lowerAsFixedAggregate(streamSrcp->dtypep())) {
|
||||||
|
AstNodeExpr* const packedp = packFixedAggregate(streamSrcp->unlinkFrBack());
|
||||||
|
streamp->lhsp(packedp);
|
||||||
|
streamp->dtypeSetLogicUnsized(packedp->width(), packedp->widthMin(),
|
||||||
|
VSigning::UNSIGNED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VL_DO_DANGLING(pushDeletep(cvtp), cvtp);
|
||||||
|
replaceAssignToFixedAggregate(nodep, nodep->lhsp()->unlinkFrBack(), srcp);
|
||||||
|
return true;
|
||||||
} else if (m_doV && VN_IS(nodep->rhsp(), StreamR)
|
} else if (m_doV && VN_IS(nodep->rhsp(), StreamR)
|
||||||
&& !VN_IS(nodep->lhsp()->dtypep()->skipRefp(), QueueDType)) {
|
&& !VN_IS(nodep->lhsp()->dtypep()->skipRefp(), QueueDType)) {
|
||||||
// The right-streaming operator on rhs of assignment does not
|
// The right-streaming operator on rhs of assignment does not
|
||||||
|
|
@ -2402,7 +2580,9 @@ class ConstVisitor final : public VNVisitor {
|
||||||
AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack();
|
AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack();
|
||||||
AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
||||||
const AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp();
|
const AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp();
|
||||||
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
|
if (lowerAsFixedAggregate(srcDTypep)) {
|
||||||
|
srcp = packFixedAggregate(srcp);
|
||||||
|
} else if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
|
||||||
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
||||||
int srcElementBits = 0;
|
int srcElementBits = 0;
|
||||||
if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
|
if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
|
||||||
|
|
@ -2429,6 +2609,19 @@ class ConstVisitor final : public VNVisitor {
|
||||||
srcp = new AstShiftL{srcp->fileline(), srcp,
|
srcp = new AstShiftL{srcp->fileline(), srcp,
|
||||||
new AstConst{srcp->fileline(), offset}, packedBits};
|
new AstConst{srcp->fileline(), offset}, packedBits};
|
||||||
}
|
}
|
||||||
|
if (!VN_IS(dstDTypep, UnpackArrayDType) && !VN_IS(dstDTypep, QueueDType)
|
||||||
|
&& !VN_IS(dstDTypep, DynArrayDType)) {
|
||||||
|
const int sWidth = srcp->width();
|
||||||
|
const int dWidth = nodep->lhsp()->width();
|
||||||
|
if (sWidth < dWidth) {
|
||||||
|
AstExtend* const extendp = new AstExtend{srcp->fileline(), srcp};
|
||||||
|
extendp->dtypeSetLogicSized(dWidth, VSigning::UNSIGNED);
|
||||||
|
srcp = new AstShiftL{
|
||||||
|
srcp->fileline(), extendp,
|
||||||
|
new AstConst{srcp->fileline(), static_cast<uint32_t>(dWidth - sWidth)},
|
||||||
|
dWidth};
|
||||||
|
}
|
||||||
|
}
|
||||||
nodep->rhsp(srcp);
|
nodep->rhsp(srcp);
|
||||||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||||
// Further reduce, any of the nodes may have more reductions.
|
// Further reduce, any of the nodes may have more reductions.
|
||||||
|
|
@ -2438,7 +2631,7 @@ class ConstVisitor final : public VNVisitor {
|
||||||
AstNodeExpr* streamp = nodep->lhsp()->unlinkFrBack();
|
AstNodeExpr* streamp = nodep->lhsp()->unlinkFrBack();
|
||||||
AstNodeExpr* const dstp = VN_AS(streamp, StreamL)->lhsp()->unlinkFrBack();
|
AstNodeExpr* const dstp = VN_AS(streamp, StreamL)->lhsp()->unlinkFrBack();
|
||||||
AstNodeDType* const dstDTypep = dstp->dtypep()->skipRefp();
|
AstNodeDType* const dstDTypep = dstp->dtypep()->skipRefp();
|
||||||
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
|
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
|
||||||
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
||||||
// Handle unpacked/queue/dynarray source -> queue/dynarray dest via
|
// Handle unpacked/queue/dynarray source -> queue/dynarray dest via
|
||||||
// CvtArrayToArray (StreamL reverses, so reverse=true)
|
// CvtArrayToArray (StreamL reverses, so reverse=true)
|
||||||
|
|
@ -2466,6 +2659,7 @@ class ConstVisitor final : public VNVisitor {
|
||||||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (lowerAsFixedAggregate(srcDTypep)) srcp = packFixedAggregate(srcp);
|
||||||
const int sWidth = srcp->width();
|
const int sWidth = srcp->width();
|
||||||
const int dWidth = dstp->width();
|
const int dWidth = dstp->width();
|
||||||
// Connect the rhs to the stream operator and update its width
|
// Connect the rhs to the stream operator and update its width
|
||||||
|
|
@ -2556,7 +2750,7 @@ class ConstVisitor final : public VNVisitor {
|
||||||
} else {
|
} else {
|
||||||
// Source narrower than destination: left-justify by shifting left.
|
// Source narrower than destination: left-justify by shifting left.
|
||||||
// The right stream operator packs left-to-right, so remaining
|
// The right stream operator packs left-to-right, so remaining
|
||||||
// LSBs are zero-filled (IEEE 1800-2023 11.4.14.2).
|
// LSBs are zero-filled (IEEE 1800-2023 11.4.14.3).
|
||||||
if (!VN_IS(srcp->dtypep()->skipRefp(), QueueDType)) {
|
if (!VN_IS(srcp->dtypep()->skipRefp(), QueueDType)) {
|
||||||
AstExtend* const extendp = new AstExtend{srcp->fileline(), srcp};
|
AstExtend* const extendp = new AstExtend{srcp->fileline(), srcp};
|
||||||
extendp->dtypeSetLogicSized(dWidth, VSigning::UNSIGNED);
|
extendp->dtypeSetLogicSized(dWidth, VSigning::UNSIGNED);
|
||||||
|
|
@ -2580,6 +2774,13 @@ class ConstVisitor final : public VNVisitor {
|
||||||
AstNodeExpr* srcp = streamp->lhsp();
|
AstNodeExpr* srcp = streamp->lhsp();
|
||||||
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
||||||
AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp();
|
AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp();
|
||||||
|
if (lowerAsFixedAggregate(srcDTypep)) {
|
||||||
|
AstNodeExpr* const packedp = packFixedAggregate(srcp->unlinkFrBack());
|
||||||
|
streamp->lhsp(packedp);
|
||||||
|
streamp->dtypeSetLogicUnsized(packedp->width(), packedp->widthMin(),
|
||||||
|
VSigning::UNSIGNED);
|
||||||
|
srcp = packedp;
|
||||||
|
}
|
||||||
if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|
if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|
||||||
|| VN_IS(srcDTypep, UnpackArrayDType))) {
|
|| VN_IS(srcDTypep, UnpackArrayDType))) {
|
||||||
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
||||||
|
|
@ -2887,6 +3088,15 @@ class ConstVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void visit(AstClassOrPackageRef* nodep) override { iterateChildren(nodep); }
|
void visit(AstClassOrPackageRef* nodep) override { iterateChildren(nodep); }
|
||||||
|
|
||||||
|
void visit(AstMatchMasked* nodep) override {
|
||||||
|
// Do not iterate the tables, they must be constant pool entries
|
||||||
|
iterate(nodep->lhsp());
|
||||||
|
if (AstConst* const constp = VN_CAST(nodep->lhsp(), Const)) {
|
||||||
|
replaceNum(nodep, AstMatchMasked::fold(constp->num(), nodep->matchp()->varp()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void visit(AstPin* nodep) override { iterateChildren(nodep); }
|
void visit(AstPin* nodep) override { iterateChildren(nodep); }
|
||||||
|
|
||||||
void replaceLogEq(AstLogEq* nodep) {
|
void replaceLogEq(AstLogEq* nodep) {
|
||||||
|
|
@ -3149,7 +3359,8 @@ class ConstVisitor final : public VNVisitor {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
UASSERT_OBJ(nodep->varp(), nodep, "Not linked");
|
UASSERT_OBJ(nodep->varp(), nodep, "Not linked");
|
||||||
bool did = false;
|
bool did = false;
|
||||||
if (m_doV && nodep->varp()->valuep() && !m_attrp) {
|
if (m_doV && (!nodep->varp()->constPoolEntry() || m_selp) && nodep->varp()->valuep()
|
||||||
|
&& !m_attrp) {
|
||||||
// UINFOTREE(1, valuep, "", "visitvaref");
|
// UINFOTREE(1, valuep, "", "visitvaref");
|
||||||
iterateAndNextNull(nodep->varp()->valuep()); // May change nodep->varp()->valuep()
|
iterateAndNextNull(nodep->varp()->valuep()); // May change nodep->varp()->valuep()
|
||||||
AstNode* const valuep = nodep->varp()->valuep();
|
AstNode* const valuep = nodep->varp()->valuep();
|
||||||
|
|
@ -4081,6 +4292,8 @@ class ConstVisitor final : public VNVisitor {
|
||||||
// Zero on one side or the other
|
// Zero on one side or the other
|
||||||
TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
||||||
TREEOP ("AstAnd {$lhsp.isZero, $rhsp, $rhsp.isPure}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure
|
TREEOP ("AstAnd {$lhsp.isZero, $rhsp, $rhsp.isPure}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure
|
||||||
|
// Masking that always yields zero
|
||||||
|
TREEOP ("AstAnd {$lhsp.castConst, matchMaskedZero(nodep)}", "replaceZeroChkPure(nodep, $rhsp)");
|
||||||
// This visit function here must allow for short-circuiting.
|
// This visit function here must allow for short-circuiting.
|
||||||
TREEOPS("AstLogAnd {$lhsp.isZero}", "replaceZero(nodep)");
|
TREEOPS("AstLogAnd {$lhsp.isZero}", "replaceZero(nodep)");
|
||||||
TREEOP ("AstLogAnd{$lhsp.isZero, $rhsp}", "replaceZero(nodep)");
|
TREEOP ("AstLogAnd{$lhsp.isZero, $rhsp}", "replaceZero(nodep)");
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,6 @@ class V3ControlFTask final {
|
||||||
V3ControlVarResolver m_params; // Parameters in function/task
|
V3ControlVarResolver m_params; // Parameters in function/task
|
||||||
V3ControlVarResolver m_ports; // Ports in function/task
|
V3ControlVarResolver m_ports; // Ports in function/task
|
||||||
V3ControlVarResolver m_vars; // Variables in function/task
|
V3ControlVarResolver m_vars; // Variables in function/task
|
||||||
bool m_isolate = false; // Isolate function return
|
|
||||||
bool m_noinline = false; // Don't inline function/task
|
bool m_noinline = false; // Don't inline function/task
|
||||||
bool m_public = false; // Public function/task
|
bool m_public = false; // Public function/task
|
||||||
|
|
||||||
|
|
@ -190,7 +189,6 @@ public:
|
||||||
V3ControlFTask() = default;
|
V3ControlFTask() = default;
|
||||||
void update(const V3ControlFTask& f) {
|
void update(const V3ControlFTask& f) {
|
||||||
// Don't overwrite true with false
|
// Don't overwrite true with false
|
||||||
if (f.m_isolate) m_isolate = true;
|
|
||||||
if (f.m_noinline) m_noinline = true;
|
if (f.m_noinline) m_noinline = true;
|
||||||
if (f.m_public) m_public = true;
|
if (f.m_public) m_public = true;
|
||||||
m_params.update(f.m_params);
|
m_params.update(f.m_params);
|
||||||
|
|
@ -203,7 +201,6 @@ public:
|
||||||
V3ControlVarResolver& ports() { return m_ports; }
|
V3ControlVarResolver& ports() { return m_ports; }
|
||||||
V3ControlVarResolver& vars() { return m_vars; }
|
V3ControlVarResolver& vars() { return m_vars; }
|
||||||
|
|
||||||
void setIsolate(bool set) { m_isolate = set; }
|
|
||||||
void setNoInline(bool set) { m_noinline = set; }
|
void setNoInline(bool set) { m_noinline = set; }
|
||||||
void setPublic(bool set) { m_public = set; }
|
void setPublic(bool set) { m_public = set; }
|
||||||
|
|
||||||
|
|
@ -212,8 +209,6 @@ public:
|
||||||
ftaskp->addStmtsp(new AstPragma{ftaskp->fileline(), VPragmaType::NO_INLINE_TASK});
|
ftaskp->addStmtsp(new AstPragma{ftaskp->fileline(), VPragmaType::NO_INLINE_TASK});
|
||||||
if (m_public)
|
if (m_public)
|
||||||
ftaskp->addStmtsp(new AstPragma{ftaskp->fileline(), VPragmaType::PUBLIC_TASK});
|
ftaskp->addStmtsp(new AstPragma{ftaskp->fileline(), VPragmaType::PUBLIC_TASK});
|
||||||
// Only functions can have isolate (return value)
|
|
||||||
if (VN_IS(ftaskp, Func)) ftaskp->attrIsolateAssign(m_isolate);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -981,13 +976,7 @@ void V3Control::addVarAttr(FileLine* fl, const string& module, const string& fta
|
||||||
|
|
||||||
// Semantics: Most of the attributes operate on signals
|
// Semantics: Most of the attributes operate on signals
|
||||||
if (pattern.empty()) {
|
if (pattern.empty()) {
|
||||||
if (attr == VAttrType::VAR_ISOLATE_ASSIGNMENTS) {
|
if (attr == VAttrType::VAR_PUBLIC) {
|
||||||
if (ftask.empty()) {
|
|
||||||
fl->v3error("isolate_assignments only applies to signals or functions/tasks");
|
|
||||||
} else {
|
|
||||||
V3ControlResolver::s().modules().at(module).ftasks().at(ftask).setIsolate(true);
|
|
||||||
}
|
|
||||||
} else if (attr == VAttrType::VAR_PUBLIC) {
|
|
||||||
if (ftask.empty()) {
|
if (ftask.empty()) {
|
||||||
// public module, this is the only exception from var here
|
// public module, this is the only exception from var here
|
||||||
V3ControlResolver::s().modules().at(module).addModulePragma(
|
V3ControlResolver::s().modules().at(module).addModulePragma(
|
||||||
|
|
|
||||||
|
|
@ -166,10 +166,13 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
||||||
|
// Return non-nullptr reason if this variable shouldn't have toggle coverage
|
||||||
const char* varIgnoreToggle(const AstVar* nodep) {
|
const char* varIgnoreToggle(const AstVar* nodep) {
|
||||||
// Return true if this shouldn't be traced
|
const bool cover = nodep->isIO() || (nodep->isSignal() && nodep->isBitLogic());
|
||||||
// See also similar rule in V3TraceDecl::varIgnoreTrace
|
if (!cover) return "Not relevant signal";
|
||||||
if (!nodep->isToggleCoverable()) return "Not relevant signal type";
|
if (nodep->isConst()) return "Signal is constant";
|
||||||
|
if (nodep->isDouble()) return "Signal is double";
|
||||||
|
if (nodep->isString()) return "Signal is string";
|
||||||
if (!v3Global.opt.coverageUnderscore()) {
|
if (!v3Global.opt.coverageUnderscore()) {
|
||||||
const string prettyName = nodep->prettyName();
|
const string prettyName = nodep->prettyName();
|
||||||
if (prettyName[0] == '_') return "Leading underscore";
|
if (prettyName[0] == '_') return "Leading underscore";
|
||||||
|
|
@ -372,6 +375,8 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
} else {
|
} else {
|
||||||
itemp->addElsesp(stmtp);
|
itemp->addElsesp(stmtp);
|
||||||
}
|
}
|
||||||
|
} else if (AstBegin* const itemp = VN_CAST(nodep, Begin)) {
|
||||||
|
itemp->addStmtsp(stmtp);
|
||||||
} else {
|
} else {
|
||||||
nodep->v3fatalSrc("Bad node type");
|
nodep->v3fatalSrc("Bad node type");
|
||||||
}
|
}
|
||||||
|
|
@ -530,8 +535,10 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
newent.cleanup();
|
newent.cleanup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (VN_IS(dtypep, QueueDType)) {
|
} else if (VN_IS(dtypep, QueueDType) || VN_IS(dtypep, AssocArrayDType)
|
||||||
|
|| VN_IS(dtypep, WildcardArrayDType)) {
|
||||||
// Not covered
|
// Not covered
|
||||||
|
varp->v3warn(COVERIGN, "Coverage ignored for type " << dtypep->prettyTypeName());
|
||||||
} else {
|
} else {
|
||||||
dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "
|
dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "
|
||||||
<< dtypep->prettyTypeName());
|
<< dtypep->prettyTypeName());
|
||||||
|
|
@ -771,6 +778,8 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
// covers the code in that line.)
|
// covers the code in that line.)
|
||||||
VL_RESTORER(m_beginHier);
|
VL_RESTORER(m_beginHier);
|
||||||
VL_RESTORER(m_inToggleOff);
|
VL_RESTORER(m_inToggleOff);
|
||||||
|
VL_RESTORER(m_exprStmtsp);
|
||||||
|
m_exprStmtsp = nodep;
|
||||||
m_inToggleOff = true;
|
m_inToggleOff = true;
|
||||||
if (nodep->name() != "") {
|
if (nodep->name() != "") {
|
||||||
m_beginHier = m_beginHier + (m_beginHier != "" ? "__DOT__" : "") + nodep->name();
|
m_beginHier = m_beginHier + (m_beginHier != "" ? "__DOT__" : "") + nodep->name();
|
||||||
|
|
@ -815,6 +824,7 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
if (pair.second) {
|
if (pair.second) {
|
||||||
varp = new AstVar{fl, VVarType::MODULETEMP, m_exprTempNames.get(frefp),
|
varp = new AstVar{fl, VVarType::MODULETEMP, m_exprTempNames.get(frefp),
|
||||||
dtypep};
|
dtypep};
|
||||||
|
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||||
pair.first->second = varp;
|
pair.first->second = varp;
|
||||||
if (m_ftaskp) {
|
if (m_ftaskp) {
|
||||||
varp->funcLocal(true);
|
varp->funcLocal(true);
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,9 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
, crossBins{cb} {}
|
, crossBins{cb} {}
|
||||||
};
|
};
|
||||||
std::vector<BinInfo> m_binInfos; // All bins in current covergroup
|
std::vector<BinInfo> m_binInfos; // All bins in current covergroup
|
||||||
|
std::set<std::string> m_crossedCpNames; // Coverpoints referenced by a cross (kept legacy)
|
||||||
|
std::vector<AstVar*> m_convCpVars; // VlCoverpoint members of converted coverpoints
|
||||||
|
AstCDType* m_vlCoverpointDTypep = nullptr; // Shared "VlCoverpoint" C++ member type
|
||||||
|
|
||||||
VMemberMap m_memberMap; // Member names cached for fast lookup
|
VMemberMap m_memberMap; // Member names cached for fast lookup
|
||||||
|
|
||||||
|
|
@ -88,6 +91,17 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
// Clear bin info for this covergroup (deleting any orphaned cross pseudo-bins)
|
// Clear bin info for this covergroup (deleting any orphaned cross pseudo-bins)
|
||||||
clearBinInfos();
|
clearBinInfos();
|
||||||
|
|
||||||
|
// Coverpoints referenced by a cross keep the legacy per-bin-member path (the cross
|
||||||
|
// reads those members); collect their names before they are consumed by the cross.
|
||||||
|
m_crossedCpNames.clear();
|
||||||
|
m_convCpVars.clear();
|
||||||
|
for (AstCoverCross* crossp : m_coverCrosses) {
|
||||||
|
for (AstNode* itemp = crossp->itemsp(); itemp; itemp = itemp->nextp()) {
|
||||||
|
if (const AstCoverpointRef* const refp = VN_CAST(itemp, CoverpointRef))
|
||||||
|
if (!refp->exprp()) m_crossedCpNames.insert(refp->name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For each coverpoint, generate sampling code
|
// For each coverpoint, generate sampling code
|
||||||
for (AstCoverpoint* cpp : m_coverpoints) generateCoverpointCode(cpp);
|
for (AstCoverpoint* cpp : m_coverpoints) generateCoverpointCode(cpp);
|
||||||
|
|
||||||
|
|
@ -504,6 +518,13 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
// Create implicit automatic bins if no regular bins exist
|
// Create implicit automatic bins if no regular bins exist
|
||||||
createImplicitAutoBins(coverpointp, exprp, autoBinMax);
|
createImplicitAutoBins(coverpointp, exprp, autoBinMax);
|
||||||
|
|
||||||
|
// Eligible coverpoints route through the VlCoverpoint runtime; the rest (cross-fed or
|
||||||
|
// transition-bearing) keep the legacy per-bin-member path below.
|
||||||
|
if (coverpointConvertible(coverpointp)) {
|
||||||
|
generateConvertedCoverpoint(coverpointp, exprp, atLeastValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Generate member variables and matching code for each bin
|
// Generate member variables and matching code for each bin
|
||||||
// Process in two passes: first non-default bins, then default bins
|
// Process in two passes: first non-default bins, then default bins
|
||||||
std::vector<AstCoverBin*> defaultBins;
|
std::vector<AstCoverBin*> defaultBins;
|
||||||
|
|
@ -595,47 +616,213 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
UINFO(4, " Successfully added if statement for bin: " << binp->name());
|
UINFO(4, " Successfully added if statement for bin: " << binp->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build the condition under which a default bin matches: NOT(OR of all normal bins).
|
||||||
|
AstNodeExpr* buildDefaultCondition(AstCoverpoint* coverpointp, AstNodeExpr* exprp,
|
||||||
|
FileLine* fl) {
|
||||||
|
AstNodeExpr* anyBinMatchp = nullptr;
|
||||||
|
for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) {
|
||||||
|
AstCoverBin* const cbinp = VN_AS(binp, CoverBin);
|
||||||
|
if (cbinp->binsType() == VCoverBinsType::BINS_DEFAULT
|
||||||
|
|| cbinp->binsType() == VCoverBinsType::BINS_IGNORE
|
||||||
|
|| cbinp->binsType() == VCoverBinsType::BINS_ILLEGAL)
|
||||||
|
continue;
|
||||||
|
AstNodeExpr* const binCondp = buildBinCondition(cbinp, exprp);
|
||||||
|
UASSERT_OBJ(binCondp, cbinp,
|
||||||
|
"buildBinCondition returned nullptr for non-ignore/non-illegal bin");
|
||||||
|
anyBinMatchp = anyBinMatchp ? new AstOr{fl, anyBinMatchp, binCondp} : binCondp;
|
||||||
|
}
|
||||||
|
return anyBinMatchp ? static_cast<AstNodeExpr*>(new AstNot{fl, anyBinMatchp})
|
||||||
|
: static_cast<AstNodeExpr*>(new AstConst{fl, AstConst::BitTrue{}});
|
||||||
|
}
|
||||||
|
|
||||||
|
//====================================================================
|
||||||
|
// VlCoverpoint conversion (eligible coverpoints)
|
||||||
|
|
||||||
|
// True if a coverpoint routes through the VlCoverpoint runtime. Cross-fed coverpoints
|
||||||
|
// (the cross reads their per-bin members) and transition-bearing ones stay legacy.
|
||||||
|
bool coverpointConvertible(AstCoverpoint* coverpointp) {
|
||||||
|
if (m_crossedCpNames.count(coverpointp->name())) return false;
|
||||||
|
for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) {
|
||||||
|
if (VN_AS(binp, CoverBin)->transp()) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 'this->m_member' reference for embedding in an AstCStmt
|
||||||
|
AstVarRef* memberRef(FileLine* fl, AstVar* varp) {
|
||||||
|
AstVarRef* const refp = new AstVarRef{fl, varp, VAccess::READ};
|
||||||
|
refp->selfPointer(VSelfPointerText{VSelfPointerText::This{}});
|
||||||
|
return refp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Individual equality targets of an array bin (bins b[] = {values/ranges}), in order.
|
||||||
|
// An open-ended bound ('$', AstUnbounded) resolves to the coverpoint domain: '[lo:$]'
|
||||||
|
// covers [lo:maxVal] and '[$:hi]' covers [0:hi]. One target is produced per value; a
|
||||||
|
// range whose resolved size would exceed COVER_BINS_LIMIT (e.g. an open '[lo:$]' over a
|
||||||
|
// wide coverpoint) is unsupported -- emits COVERIGN, sets unsupportedOut, yields nothing.
|
||||||
|
std::vector<AstNodeExpr*> extractArrayValues(AstCoverBin* arrayBinp, AstNodeExpr* exprp,
|
||||||
|
bool& unsupportedOut) {
|
||||||
|
unsupportedOut = false;
|
||||||
|
const int width = exprp->width();
|
||||||
|
const uint64_t maxVal = (width >= 64) ? UINT64_MAX : ((1ULL << width) - 1);
|
||||||
|
std::vector<AstNodeExpr*> values;
|
||||||
|
for (AstNode* rangep = arrayBinp->rangesp(); rangep; rangep = rangep->nextp()) {
|
||||||
|
if (AstInsideRange* const irp = VN_CAST(rangep, InsideRange)) {
|
||||||
|
AstNodeExpr* const lhsp = V3Const::constifyEdit(irp->lhsp());
|
||||||
|
AstNodeExpr* const rhsp = V3Const::constifyEdit(irp->rhsp());
|
||||||
|
const bool loUnb = VN_IS(lhsp, Unbounded);
|
||||||
|
const bool hiUnb = VN_IS(rhsp, Unbounded);
|
||||||
|
AstConst* const minp = VN_CAST(lhsp, Const);
|
||||||
|
AstConst* const maxp = VN_CAST(rhsp, Const);
|
||||||
|
if ((!minp && !loUnb) || (!maxp && !hiUnb)) {
|
||||||
|
arrayBinp->v3error("Non-constant expression in array bins range; "
|
||||||
|
"range bounds must be constants");
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
if ((minp && minp->num().isFourState()) || (maxp && maxp->num().isFourState())) {
|
||||||
|
arrayBinp->v3error("Four-state (x/z) value in array bins range bound; "
|
||||||
|
"range bounds must be two-state constants");
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
const uint64_t lo = loUnb ? 0 : minp->toUQuad();
|
||||||
|
const uint64_t hi = hiUnb ? maxVal : maxp->toUQuad();
|
||||||
|
if (hi < lo) continue; // empty range contributes no bins
|
||||||
|
// Guard against a '$'-bounded or otherwise huge range exploding the bin count.
|
||||||
|
const uint64_t span = hi - lo; // == valueCount - 1 (no overflow: hi >= lo)
|
||||||
|
if (span >= static_cast<uint64_t>(COVER_BINS_LIMIT)
|
||||||
|
|| values.size() + span + 1 > static_cast<uint64_t>(COVER_BINS_LIMIT)) {
|
||||||
|
arrayBinp->v3warn(COVERIGN, "Unsupported: array 'bins' covering more than "
|
||||||
|
<< COVER_BINS_LIMIT
|
||||||
|
<< " values (e.g. an open '[lo:$]' range over "
|
||||||
|
"a wide coverpoint); bin ignored");
|
||||||
|
unsupportedOut = true;
|
||||||
|
for (AstNodeExpr* const vp : values) VL_DO_DANGLING(pushDeletep(vp), vp);
|
||||||
|
values.clear();
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
for (uint64_t v = lo; v <= hi; ++v)
|
||||||
|
values.push_back(new AstConst{irp->fileline(), AstConst::WidthedValue{}, width,
|
||||||
|
static_cast<uint32_t>(v)});
|
||||||
|
} else {
|
||||||
|
values.push_back(VN_AS(rangep->cloneTree(false), NodeExpr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit a 'this->m_cp.addSingleNamer/addArrayNamer(...)' statement for one bin
|
||||||
|
AstCStmt* makeNamer(AstVar* cpVarp, AstCoverBin* binp, int count) {
|
||||||
|
FileLine* const fl = binp->fileline();
|
||||||
|
AstCStmt* const cs = new AstCStmt{fl};
|
||||||
|
cs->add(memberRef(fl, cpVarp));
|
||||||
|
const std::string loc = "\"" + std::string{fl->filename()} + "\", "
|
||||||
|
+ std::to_string(fl->lineno()) + ", "
|
||||||
|
+ std::to_string(fl->firstColumn()) + ");";
|
||||||
|
if (count < 0) { // single bin
|
||||||
|
cs->add(".addSingleNamer(" + std::string{binp->binsType().binSetEnum()} + ", \""
|
||||||
|
+ binp->name() + "\", " + loc);
|
||||||
|
} else { // value array bin
|
||||||
|
cs->add(".addArrayNamer(" + std::string{binp->binsType().binSetEnum()} + ", "
|
||||||
|
+ std::to_string(count) + ", \"" + binp->name() + "\", " + loc);
|
||||||
|
}
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit 'if (iff && cond) m_cp.incrementBin(idx);' (or recordHit, + illegal action) in sample()
|
||||||
|
void emitConvHitIf(AstCoverpoint* coverpointp, AstCoverBin* binp, AstVar* cpVarp, int idx,
|
||||||
|
AstNodeExpr* condp) {
|
||||||
|
FileLine* const fl = binp->fileline();
|
||||||
|
AstCStmt* const hitp = new AstCStmt{fl};
|
||||||
|
hitp->add(memberRef(fl, cpVarp));
|
||||||
|
hitp->add((binp->binsType().binIsNormal() ? ".incrementBin(" : ".recordHit(")
|
||||||
|
+ std::to_string(idx) + ");");
|
||||||
|
AstNode* actionp = hitp;
|
||||||
|
if (binp->binsType() == VCoverBinsType::BINS_ILLEGAL) {
|
||||||
|
actionp->addNext(makeIllegalBinAction(fl, "Illegal bin " + binp->prettyNameQ()
|
||||||
|
+ " hit in coverpoint "
|
||||||
|
+ coverpointp->prettyNameQ()));
|
||||||
|
}
|
||||||
|
AstNodeExpr* const guardedp = applyCoverpointIffCondition(coverpointp, fl, condp);
|
||||||
|
UASSERT_OBJ(m_sampleFuncp, binp, "sample() CFunc not set in converted coverpoint");
|
||||||
|
m_sampleFuncp->addStmtsp(new AstIf{fl, guardedp, actionp, nullptr});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route an eligible coverpoint through a VlCoverpoint member: emit the member, its
|
||||||
|
// sample() increments, the constructor configuration (init + namers), and registration.
|
||||||
|
void generateConvertedCoverpoint(AstCoverpoint* coverpointp, AstNodeExpr* exprp,
|
||||||
|
int atLeastValue) {
|
||||||
|
FileLine* const fl = coverpointp->fileline();
|
||||||
|
UINFO(4, " Converting coverpoint to VlCoverpoint: " << coverpointp->name());
|
||||||
|
|
||||||
|
if (!m_vlCoverpointDTypep) {
|
||||||
|
m_vlCoverpointDTypep = new AstCDType{fl, "VlCoverpoint"};
|
||||||
|
v3Global.rootp()->typeTablep()->addTypesp(m_vlCoverpointDTypep);
|
||||||
|
}
|
||||||
|
AstVar* const cpVarp = new AstVar{fl, VVarType::MEMBER, "__Vcp_" + coverpointp->name(),
|
||||||
|
m_vlCoverpointDTypep};
|
||||||
|
cpVarp->isStatic(false);
|
||||||
|
m_covergroupp->addMembersp(cpVarp);
|
||||||
|
m_convCpVars.push_back(cpVarp);
|
||||||
|
|
||||||
|
// Walk bins (non-default, then default), assigning sequential indices that match the
|
||||||
|
// namer append order; emit sample increments and collect namer statements.
|
||||||
|
std::vector<AstCStmt*> namerStmts;
|
||||||
|
std::vector<AstCoverBin*> defaultBins;
|
||||||
|
int idx = 0;
|
||||||
|
for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) {
|
||||||
|
AstCoverBin* const cbinp = VN_AS(binp, CoverBin);
|
||||||
|
if (cbinp->binsType() == VCoverBinsType::BINS_DEFAULT) {
|
||||||
|
defaultBins.push_back(cbinp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cbinp->isArray()) { // value array: bins b[N] = {...} -> b[0]..b[N-1]
|
||||||
|
bool unsupported = false;
|
||||||
|
std::vector<AstNodeExpr*> values = extractArrayValues(cbinp, exprp, unsupported);
|
||||||
|
if (unsupported) continue; // bin ignored (COVERIGN emitted); reserve no slot
|
||||||
|
namerStmts.push_back(makeNamer(cpVarp, cbinp, static_cast<int>(values.size())));
|
||||||
|
for (AstNodeExpr* valuep : values) {
|
||||||
|
emitConvHitIf(coverpointp, cbinp, cpVarp, idx++,
|
||||||
|
new AstEq{cbinp->fileline(), exprp->cloneTree(false), valuep});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
namerStmts.push_back(makeNamer(cpVarp, cbinp, -1));
|
||||||
|
// buildBinCondition is null for 'ignore_bins = default' (no ranges); the bin
|
||||||
|
// still gets a reserved slot (recorded, never incremented).
|
||||||
|
if (AstNodeExpr* const condp = buildBinCondition(cbinp, exprp))
|
||||||
|
emitConvHitIf(coverpointp, cbinp, cpVarp, idx, condp);
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (AstCoverBin* const defBinp : defaultBins) {
|
||||||
|
namerStmts.push_back(makeNamer(cpVarp, defBinp, -1));
|
||||||
|
emitConvHitIf(coverpointp, defBinp, cpVarp, idx++,
|
||||||
|
buildDefaultCondition(coverpointp, exprp, defBinp->fileline()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor: init (allocates), namers, then registration (under --coverage)
|
||||||
|
const std::string hier = m_covergroupp->name() + "." + coverpointp->name();
|
||||||
|
AstCStmt* const initp = new AstCStmt{fl};
|
||||||
|
initp->add(memberRef(fl, cpVarp));
|
||||||
|
initp->add(".init(\"" + hier + "\", " + std::to_string(atLeastValue) + ", "
|
||||||
|
+ std::to_string(idx) + ");");
|
||||||
|
m_constructorp->addStmtsp(initp);
|
||||||
|
for (AstCStmt* const ns : namerStmts) m_constructorp->addStmtsp(ns);
|
||||||
|
if (v3Global.opt.coverage()) {
|
||||||
|
AstCStmt* const regp = new AstCStmt{fl};
|
||||||
|
regp->add(memberRef(fl, cpVarp));
|
||||||
|
regp->add(".registerBins(vlSymsp->_vm_contextp__->coveragep(), \"v_covergroup/"
|
||||||
|
+ m_covergroupp->name() + "\");");
|
||||||
|
m_constructorp->addStmtsp(regp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate matching code for default bins
|
// Generate matching code for default bins
|
||||||
// Default bins match when value doesn't match any other explicit bin
|
// Default bins match when value doesn't match any other explicit bin
|
||||||
void generateDefaultBinMatchCode(AstCoverpoint* coverpointp, AstCoverBin* defBinp,
|
void generateDefaultBinMatchCode(AstCoverpoint* coverpointp, AstCoverBin* defBinp,
|
||||||
AstNodeExpr* exprp, AstVar* hitVarp) {
|
AstNodeExpr* exprp, AstVar* hitVarp) {
|
||||||
UINFO(4, " Generating default bin match for: " << defBinp->name());
|
UINFO(4, " Generating default bin match for: " << defBinp->name());
|
||||||
|
|
||||||
// Build OR of all non-default, non-ignore bins
|
AstNodeExpr* defaultCondp = buildDefaultCondition(coverpointp, exprp, defBinp->fileline());
|
||||||
AstNodeExpr* anyBinMatchp = nullptr;
|
|
||||||
|
|
||||||
for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) {
|
|
||||||
AstCoverBin* const cbinp = VN_AS(binp, CoverBin);
|
|
||||||
|
|
||||||
// Skip default, ignore, and illegal bins
|
|
||||||
if (cbinp->binsType() == VCoverBinsType::BINS_DEFAULT
|
|
||||||
|| cbinp->binsType() == VCoverBinsType::BINS_IGNORE
|
|
||||||
|| cbinp->binsType() == VCoverBinsType::BINS_ILLEGAL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build condition for this bin
|
|
||||||
AstNodeExpr* const binCondp = buildBinCondition(cbinp, exprp);
|
|
||||||
UASSERT_OBJ(binCondp, cbinp,
|
|
||||||
"buildBinCondition returned nullptr for non-ignore/non-illegal bin");
|
|
||||||
|
|
||||||
// OR with previous conditions
|
|
||||||
if (anyBinMatchp) {
|
|
||||||
anyBinMatchp = new AstOr{defBinp->fileline(), anyBinMatchp, binCondp};
|
|
||||||
} else {
|
|
||||||
anyBinMatchp = binCondp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default matches when NO explicit bin matches
|
|
||||||
AstNodeExpr* defaultCondp = nullptr;
|
|
||||||
if (anyBinMatchp) {
|
|
||||||
// NOT (bin1 OR bin2 OR ... OR binN)
|
|
||||||
defaultCondp = new AstNot{defBinp->fileline(), anyBinMatchp};
|
|
||||||
} else {
|
|
||||||
// No other bins - default always matches (shouldn't happen in practice)
|
|
||||||
defaultCondp = new AstConst{defBinp->fileline(), AstConst::BitTrue{}};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply iff condition if present
|
// Apply iff condition if present
|
||||||
if (AstNodeExpr* iffp = coverpointp->iffp()) {
|
if (AstNodeExpr* iffp = coverpointp->iffp()) {
|
||||||
|
|
@ -903,34 +1090,10 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
int atLeastValue) {
|
int atLeastValue) {
|
||||||
UINFO(4, " Generating array bins for: " << arrayBinp->name());
|
UINFO(4, " Generating array bins for: " << arrayBinp->name());
|
||||||
|
|
||||||
// Extract all values from the range list
|
// Extract all values from the range list (resolves '$', caps/ignores huge ranges)
|
||||||
std::vector<AstNodeExpr*> values;
|
bool unsupported = false;
|
||||||
for (AstNode* rangep = arrayBinp->rangesp(); rangep; rangep = rangep->nextp()) {
|
std::vector<AstNodeExpr*> values = extractArrayValues(arrayBinp, exprp, unsupported);
|
||||||
if (AstInsideRange* const insideRangep = VN_CAST(rangep, InsideRange)) {
|
if (unsupported) return; // bin ignored (COVERIGN emitted)
|
||||||
// For InsideRange [min:max], create bins for each value
|
|
||||||
AstNodeExpr* const minp = V3Const::constifyEdit(insideRangep->lhsp());
|
|
||||||
AstNodeExpr* const maxp = V3Const::constifyEdit(insideRangep->rhsp());
|
|
||||||
AstConst* const minConstp = VN_CAST(minp, Const);
|
|
||||||
AstConst* const maxConstp = VN_CAST(maxp, Const);
|
|
||||||
if (minConstp && maxConstp) {
|
|
||||||
const int minVal = minConstp->toSInt();
|
|
||||||
const int maxVal = maxConstp->toSInt();
|
|
||||||
UINFO(6, " Expanding InsideRange [" << minVal << ":" << maxVal << "]");
|
|
||||||
for (int val = minVal; val <= maxVal; ++val) {
|
|
||||||
values.push_back(new AstConst{insideRangep->fileline(),
|
|
||||||
AstConst::WidthedValue{},
|
|
||||||
(int)exprp->width(), (uint32_t)val});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
arrayBinp->v3error("Non-constant expression in array bins range; "
|
|
||||||
"range bounds must be constants");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Single value - should be an expression
|
|
||||||
values.push_back(VN_AS(rangep->cloneTree(false), NodeExpr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a separate bin for each value
|
// Create a separate bin for each value
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
@ -1132,6 +1295,18 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
while (itemp) {
|
while (itemp) {
|
||||||
AstNode* const nextp = itemp->nextp();
|
AstNode* const nextp = itemp->nextp();
|
||||||
AstCoverpointRef* const refp = VN_AS(itemp, CoverpointRef);
|
AstCoverpointRef* const refp = VN_AS(itemp, CoverpointRef);
|
||||||
|
if (refp->exprp()) {
|
||||||
|
// Non-standard hierarchical/dotted cross item (e.g. 'cross a.b'): an implicit
|
||||||
|
// coverpoint over the referenced expression (carried in refp->exprp()). The
|
||||||
|
// grammar already warned NONSTD; implicit coverpoints are not yet implemented, so
|
||||||
|
// generate no sampling code for this cross. When support is added the implicit
|
||||||
|
// coverpoint should be synthesized upstream (V3LinkParse) as a real AstCoverpoint
|
||||||
|
// so it flows through the normal coverpoint path - by here coverpoint lowering has
|
||||||
|
// already run.
|
||||||
|
refp->v3warn(COVERIGN,
|
||||||
|
"Unsupported: cross of hierarchical reference (implicit coverpoint)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Find the referenced coverpoint via name map (O(log n) vs O(n) linear scan)
|
// Find the referenced coverpoint via name map (O(log n) vs O(n) linear scan)
|
||||||
const auto it = m_coverpointMap.find(refp->name());
|
const auto it = m_coverpointMap.find(refp->name());
|
||||||
AstCoverpoint* const foundCpp = (it != m_coverpointMap.end()) ? it->second : nullptr;
|
AstCoverpoint* const foundCpp = (it != m_coverpointMap.end()) ? it->second : nullptr;
|
||||||
|
|
@ -1320,15 +1495,50 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
|
|
||||||
void generateCoverageMethodBody(AstFunc* funcp) {
|
void generateCoverageMethodBody(AstFunc* funcp) {
|
||||||
FileLine* const fl = funcp->fileline();
|
FileLine* const fl = funcp->fileline();
|
||||||
|
AstVar* const returnVarp = VN_AS(funcp->fvarp(), Var);
|
||||||
|
|
||||||
// Count total bins (excluding ignore_bins and illegal_bins)
|
// Converted coverpoints hold their bins in VlCoverpoint. Combine their contributions
|
||||||
|
// (via coverageParts) with any remaining legacy cross/cross-fed bins as the same flat
|
||||||
|
// covered/total ratio the all-legacy path below computes. Normal bins only: ignore,
|
||||||
|
// illegal, and default are excluded (LRM 19.5).
|
||||||
|
if (!m_convCpVars.empty()) {
|
||||||
|
AstCStmt* const headp = new AstCStmt{fl};
|
||||||
|
headp->add("double __Vcov = 0.0; double __Vtot = 0.0;");
|
||||||
|
funcp->addStmtsp(headp);
|
||||||
|
for (AstVar* const cpVarp : m_convCpVars) {
|
||||||
|
AstCStmt* const cs = new AstCStmt{fl};
|
||||||
|
cs->add("{ double __Vc = 0.0; double __Vt = 0.0; ");
|
||||||
|
cs->add(memberRef(fl, cpVarp));
|
||||||
|
cs->add(".coverageParts(__Vc, __Vt); __Vcov += __Vc; __Vtot += __Vt; }");
|
||||||
|
funcp->addStmtsp(cs);
|
||||||
|
}
|
||||||
|
int legacyRegular = 0;
|
||||||
|
for (const BinInfo& bi : m_binInfos) {
|
||||||
|
if (!bi.binp->binsType().binIsNormal()) continue;
|
||||||
|
++legacyRegular;
|
||||||
|
AstCStmt* const cs = new AstCStmt{fl};
|
||||||
|
cs->add("if (");
|
||||||
|
cs->add(memberRef(fl, bi.varp));
|
||||||
|
cs->add(" >= " + std::to_string(bi.atLeast) + ") __Vcov += 1.0;");
|
||||||
|
funcp->addStmtsp(cs);
|
||||||
|
}
|
||||||
|
if (legacyRegular) {
|
||||||
|
AstCStmt* const cs = new AstCStmt{fl};
|
||||||
|
cs->add("__Vtot += " + std::to_string(legacyRegular) + ".0;");
|
||||||
|
funcp->addStmtsp(cs);
|
||||||
|
}
|
||||||
|
AstCStmt* const retp = new AstCStmt{fl};
|
||||||
|
retp->add(new AstVarRef{fl, returnVarp, VAccess::WRITE});
|
||||||
|
retp->add(" = (__Vtot != 0.0) ? (100.0 * __Vcov / __Vtot) : 100.0;");
|
||||||
|
funcp->addStmtsp(retp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count total bins (Normal only: excludes ignore/illegal/default)
|
||||||
int totalBins = 0;
|
int totalBins = 0;
|
||||||
for (const BinInfo& bi : m_binInfos) {
|
for (const BinInfo& bi : m_binInfos) {
|
||||||
UINFO(6, " Bin: " << bi.binp->name() << " type=" << bi.binp->binsType().ascii());
|
UINFO(6, " Bin: " << bi.binp->name() << " type=" << bi.binp->binsType().ascii());
|
||||||
if (bi.binp->binsType() != VCoverBinsType::BINS_IGNORE
|
if (bi.binp->binsType().binIsNormal()) totalBins++;
|
||||||
&& bi.binp->binsType() != VCoverBinsType::BINS_ILLEGAL) {
|
|
||||||
totalBins++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UINFO(4, " Total regular bins: " << totalBins << " of " << m_binInfos.size());
|
UINFO(4, " Total regular bins: " << totalBins << " of " << m_binInfos.size());
|
||||||
|
|
@ -1337,7 +1547,6 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
// No coverage to compute - return 100%.
|
// No coverage to compute - return 100%.
|
||||||
// Any parser-generated initialization of returnVar is overridden by our assignment.
|
// Any parser-generated initialization of returnVar is overridden by our assignment.
|
||||||
UINFO(4, " Empty covergroup, returning 100.0");
|
UINFO(4, " Empty covergroup, returning 100.0");
|
||||||
AstVar* const returnVarp = VN_AS(funcp->fvarp(), Var);
|
|
||||||
funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, returnVarp, VAccess::WRITE},
|
funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, returnVarp, VAccess::WRITE},
|
||||||
new AstConst{fl, AstConst::RealDouble{}, 100.0}});
|
new AstConst{fl, AstConst::RealDouble{}, 100.0}});
|
||||||
UINFO(4, " Added assignment to return 100.0");
|
UINFO(4, " Added assignment to return 100.0");
|
||||||
|
|
@ -1356,11 +1565,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
|
|
||||||
// For each regular bin, if count > 0, increment covered_count
|
// For each regular bin, if count > 0, increment covered_count
|
||||||
for (const BinInfo& bi : m_binInfos) {
|
for (const BinInfo& bi : m_binInfos) {
|
||||||
// Skip ignore_bins and illegal_bins in coverage calculation
|
// Skip ignore/illegal/default bins in coverage calculation
|
||||||
if (bi.binp->binsType() == VCoverBinsType::BINS_IGNORE
|
if (!bi.binp->binsType().binIsNormal()) continue;
|
||||||
|| bi.binp->binsType() == VCoverBinsType::BINS_ILLEGAL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (bin_count >= at_least) covered_count++;
|
// if (bin_count >= at_least) covered_count++;
|
||||||
AstIf* ifp = new AstIf{
|
AstIf* ifp = new AstIf{
|
||||||
|
|
@ -1375,9 +1581,6 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||||
funcp->addStmtsp(ifp);
|
funcp->addStmtsp(ifp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the return variable
|
|
||||||
AstVar* const returnVarp = VN_AS(funcp->fvarp(), Var);
|
|
||||||
|
|
||||||
// Calculate coverage: (covered_count / total_bins) * 100.0
|
// Calculate coverage: (covered_count / total_bins) * 100.0
|
||||||
// return_var = (double)covered_count / (double)total_bins * 100.0
|
// return_var = (double)covered_count / (double)total_bins * 100.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -597,6 +597,7 @@ public:
|
||||||
// We may have removed some datatypes, cleanup
|
// We may have removed some datatypes, cleanup
|
||||||
nodep->typeTablep()->repairCache();
|
nodep->typeTablep()->repairCache();
|
||||||
VIsCached::clearCacheTree(); // Removing assignments may affect isPure
|
VIsCached::clearCacheTree(); // Removing assignments may affect isPure
|
||||||
|
nodep->constPoolp()->reCache();
|
||||||
}
|
}
|
||||||
~DeadVisitor() override {
|
~DeadVisitor() override {
|
||||||
V3Stats::addStatSum("Optimizations, deadified FTasks", m_statFTasksDeadified);
|
V3Stats::addStatSum("Optimizations, deadified FTasks", m_statFTasksDeadified);
|
||||||
|
|
|
||||||
|
|
@ -275,6 +275,7 @@ class DelayedVisitor final : public VNVisitor {
|
||||||
bool m_inSuspendableOrFork = false; // True in suspendable processes and forks
|
bool m_inSuspendableOrFork = false; // True in suspendable processes and forks
|
||||||
bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
|
bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
|
||||||
bool m_inNonCombLogic = false; // We are in non-combinational logic
|
bool m_inNonCombLogic = false; // We are in non-combinational logic
|
||||||
|
bool m_needsInitialTrigger = false; // Whether a NodeProcedure needs a initial trigger
|
||||||
AstVarRef* m_currNbaLhsRefp = nullptr; // Current NBA LHS variable reference
|
AstVarRef* m_currNbaLhsRefp = nullptr; // Current NBA LHS variable reference
|
||||||
|
|
||||||
// STATE - during NBA conversion (after visit)
|
// STATE - during NBA conversion (after visit)
|
||||||
|
|
@ -291,6 +292,7 @@ class DelayedVisitor final : public VNVisitor {
|
||||||
VDouble0 m_nSchemeValueQueuesWhole; // Number of variables using Scheme::ValueQueueWhole
|
VDouble0 m_nSchemeValueQueuesWhole; // Number of variables using Scheme::ValueQueueWhole
|
||||||
VDouble0 m_nSchemeValueQueuesPartial; // Number of variables using Scheme::ValueQueuePartial
|
VDouble0 m_nSchemeValueQueuesPartial; // Number of variables using Scheme::ValueQueuePartial
|
||||||
VDouble0 m_nSharedSetFlags; // "Set" flags actually shared by Scheme::FlagShared variables
|
VDouble0 m_nSharedSetFlags; // "Set" flags actually shared by Scheme::FlagShared variables
|
||||||
|
VDouble0 m_nInitialNBA; // Number of procedural blocks with initial NBA
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
||||||
|
|
@ -999,6 +1001,12 @@ class DelayedVisitor final : public VNVisitor {
|
||||||
m_writeRefs(nodep->varScopep()).emplace_back(nodep, nonBlocking, m_inNonCombLogic);
|
m_writeRefs(nodep->varScopep()).emplace_back(nodep, nonBlocking, m_inNonCombLogic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Procedure_T>
|
||||||
|
static bool isProcedureWithSentreep(const AstNodeProcedure* const nodep) {
|
||||||
|
const Procedure_T* const procedurep = AstNode::cast<Procedure_T>(nodep);
|
||||||
|
return procedurep && procedurep->sentreep();
|
||||||
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
void visit(AstNetlist* nodep) override {
|
void visit(AstNetlist* nodep) override {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
|
@ -1112,21 +1120,36 @@ class DelayedVisitor final : public VNVisitor {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstNodeProcedure* nodep) override {
|
void visit(AstNodeProcedure* nodep) override {
|
||||||
|
VL_RESTORER(m_needsInitialTrigger);
|
||||||
const size_t firstNBAAddedIndex = m_nbas.size();
|
const size_t firstNBAAddedIndex = m_nbas.size();
|
||||||
{
|
{
|
||||||
VL_RESTORER(m_inSuspendableOrFork);
|
VL_RESTORER(m_inSuspendableOrFork);
|
||||||
VL_RESTORER(m_procp);
|
VL_RESTORER(m_procp);
|
||||||
VL_RESTORER(m_ignoreBlkAndNBlk);
|
VL_RESTORER(m_ignoreBlkAndNBlk);
|
||||||
VL_RESTORER(m_inNonCombLogic);
|
VL_RESTORER(m_inNonCombLogic);
|
||||||
m_inSuspendableOrFork = nodep->isSuspendable();
|
// When we are dealing with initial block we need to
|
||||||
|
// treat it as suspendable when we meet a NBA
|
||||||
|
m_inSuspendableOrFork = nodep->isSuspendable() || VN_IS(nodep, Initial);
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
if (m_inSuspendableOrFork) {
|
if (nodep->isSuspendable()) {
|
||||||
m_ignoreBlkAndNBlk = false;
|
m_ignoreBlkAndNBlk = false;
|
||||||
m_inNonCombLogic = true;
|
m_inNonCombLogic = true;
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
if (m_timingDomains.empty()) return;
|
auto containsClocled = [](const AstSenItem* itemp) {
|
||||||
|
while (itemp) {
|
||||||
|
if (itemp->edgeType().clockedStmt()) return true;
|
||||||
|
itemp = VN_AS(itemp->nextp(), SenItem);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
const bool addInitialTrigger = m_needsInitialTrigger
|
||||||
|
&& !(isProcedureWithSentreep<AstAlways>(nodep)
|
||||||
|
|| isProcedureWithSentreep<AstAlwaysObserved>(nodep)
|
||||||
|
|| isProcedureWithSentreep<AstAlwaysReactive>(nodep))
|
||||||
|
&& !containsClocled(m_activep->sentreep()->sensesp());
|
||||||
|
if (m_timingDomains.empty() && !addInitialTrigger) return;
|
||||||
|
|
||||||
// There were some timing domains involved in the process. Add all of them as sensitivities
|
// There were some timing domains involved in the process. Add all of them as sensitivities
|
||||||
// of all NBA targets in this process. Note this is a bit of a sledgehammer, we should only
|
// of all NBA targets in this process. Note this is a bit of a sledgehammer, we should only
|
||||||
|
|
@ -1135,6 +1158,11 @@ class DelayedVisitor final : public VNVisitor {
|
||||||
|
|
||||||
// First gather all senItems
|
// First gather all senItems
|
||||||
AstSenItem* senItemp = nullptr;
|
AstSenItem* senItemp = nullptr;
|
||||||
|
if (addInitialTrigger) {
|
||||||
|
senItemp = new AstSenItem{nodep->fileline(), AstSenItem::InitialNBA{}};
|
||||||
|
++m_nInitialNBA;
|
||||||
|
}
|
||||||
|
|
||||||
for (const AstSenTree* const domainp : m_timingDomains) {
|
for (const AstSenTree* const domainp : m_timingDomains) {
|
||||||
if (domainp->sensesp())
|
if (domainp->sensesp())
|
||||||
senItemp = AstNode::addNext(senItemp, domainp->sensesp()->cloneTree(true));
|
senItemp = AstNode::addNext(senItemp, domainp->sensesp()->cloneTree(true));
|
||||||
|
|
@ -1218,6 +1246,8 @@ class DelayedVisitor final : public VNVisitor {
|
||||||
UASSERT_OBJ(m_inSuspendableOrFork || m_activep->hasClocked(), nodep,
|
UASSERT_OBJ(m_inSuspendableOrFork || m_activep->hasClocked(), nodep,
|
||||||
"<= assignment in non-clocked block, should have been converted in V3Active");
|
"<= assignment in non-clocked block, should have been converted in V3Active");
|
||||||
|
|
||||||
|
m_needsInitialTrigger |= m_timingDomains.empty();
|
||||||
|
|
||||||
// Record scope of this NBA
|
// Record scope of this NBA
|
||||||
nodep->user2p(m_scopep);
|
nodep->user2p(m_scopep);
|
||||||
|
|
||||||
|
|
@ -1327,6 +1357,7 @@ public:
|
||||||
V3Stats::addStat("NBA, variables using ValueQueuePartial scheme",
|
V3Stats::addStat("NBA, variables using ValueQueuePartial scheme",
|
||||||
m_nSchemeValueQueuesPartial);
|
m_nSchemeValueQueuesPartial);
|
||||||
V3Stats::addStat("Optimizations, NBA flags shared", m_nSharedSetFlags);
|
V3Stats::addStat("Optimizations, NBA flags shared", m_nSharedSetFlags);
|
||||||
|
V3Stats::addStat("Procedures needing initial NBA trigger", m_nInitialNBA);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue