merge upstream master and resolve conflicts
This commit is contained in:
commit
0019a967b4
|
|
@ -28,6 +28,38 @@ concurrency:
|
|||
|
||||
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:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
|
|
@ -58,7 +90,7 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: ubuntu-24.04, cc: clang, asan: 1}
|
||||
- {os: ubuntu-24.04, cc: clang, asan: 0}
|
||||
|
||||
build-2204-gcc:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
|
|
@ -76,22 +108,6 @@ jobs:
|
|||
include:
|
||||
- {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:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
|
|
@ -138,11 +154,11 @@ jobs:
|
|||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
path: repo
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: msbuild-msvc-cmake
|
||||
|
|
@ -155,11 +171,59 @@ jobs:
|
|||
- name: Zip up repository
|
||||
run: Compress-Archive -LiteralPath install -DestinationPath verilator.zip
|
||||
- name: Upload zip archive
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: ${{ github.workspace }}/repo/verilator.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:
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
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-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:
|
||||
name: Lint Python
|
||||
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"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- run: test_regress/t/t_dist_contributors.py
|
||||
|
|
|
|||
|
|
@ -74,10 +74,10 @@ jobs:
|
|||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
|
||||
- name: Download code coverage data
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
pattern: code-coverage-*
|
||||
path: obj_coverage
|
||||
|
|
@ -90,7 +90,7 @@ jobs:
|
|||
find obj_coverage -type f | paste -sd, | sed "s/^/files=/" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v6
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7
|
||||
with:
|
||||
disable_file_fixes: true
|
||||
disable_search: true
|
||||
|
|
@ -114,7 +114,7 @@ jobs:
|
|||
sudo apt install lcov
|
||||
|
||||
- name: Download repository archive
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ needs.build.outputs.archive }}
|
||||
path: ${{ github.workspace }}
|
||||
|
|
@ -125,7 +125,7 @@ jobs:
|
|||
ls -lsha
|
||||
|
||||
- name: Download code coverage data
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
pattern: code-coverage-*
|
||||
path: repo/obj_coverage
|
||||
|
|
@ -172,14 +172,14 @@ jobs:
|
|||
fi
|
||||
|
||||
- name: Upload report
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: repo/obj_coverage
|
||||
name: coverage-report
|
||||
|
||||
- name: Upload notification
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: repo/notification
|
||||
name: pr-notification
|
||||
|
|
@ -195,9 +195,9 @@ jobs:
|
|||
# Creating issues requires elevated privilege
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v3.2.0
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
client-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||
owner: verilator
|
||||
repositories: verilator
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
|
||||
- name: Extract context variables
|
||||
run: |
|
||||
|
|
@ -54,7 +54,7 @@ jobs:
|
|||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@v6
|
||||
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
|
||||
with:
|
||||
images: |
|
||||
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
||||
|
|
@ -64,21 +64,21 @@ jobs:
|
|||
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
with:
|
||||
buildkitd-flags: --debug
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- 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'
|
||||
with:
|
||||
context: ${{ env.build_context }}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
CI_COMMIT: ${{ github.sha }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Install packages for build
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
pr-run-ids: ${{ steps.build.outputs.pr-run-ids }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- name: Build pages
|
||||
id: build
|
||||
env:
|
||||
|
|
@ -48,7 +48,7 @@ jobs:
|
|||
ls -lsha
|
||||
tree -L 3 pages
|
||||
- name: Upload pages artifact
|
||||
uses: actions/upload-pages-artifact@v5
|
||||
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
|
||||
with:
|
||||
path: pages
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ jobs:
|
|||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: actions/deploy-pages@v5
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
|
||||
|
||||
notify:
|
||||
name: Notify
|
||||
|
|
@ -70,13 +70,13 @@ jobs:
|
|||
if: ${{ github.repository == 'verilator/verilator' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
# Use the Verilator CI app to post the comment
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v3.2.0
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
client-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||
permission-actions: write
|
||||
permission-pull-requests: write
|
||||
|
|
|
|||
|
|
@ -61,14 +61,14 @@ jobs:
|
|||
CCACHE_MAXSIZE: 1000M # Per build matrix entry (* 5 = 5000M in total)
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
path: repo
|
||||
ref: ${{ inputs.sha }}
|
||||
fetch-depth: ${{ inputs.dev-gcov && '0' || '1' }} # Coverage flow needs full history
|
||||
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||
with:
|
||||
|
|
@ -93,7 +93,7 @@ jobs:
|
|||
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload repository archive
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
|
||||
name: ${{ steps.create-archive.outputs.archive }}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
name: Sub-lint | Python
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
path: repo
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ jobs:
|
|||
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev
|
||||
|
||||
- name: Use saved ccache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: ccache
|
||||
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 }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
path: repo
|
||||
ref: ${{ inputs.sha }}
|
||||
|
|
@ -89,7 +89,7 @@ jobs:
|
|||
echo "archive=$ARCHIVE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Verilator installation archive
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: ${{ 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
|
||||
|
||||
- name: Checkout RTLMeter
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
repository: "verilator/rtlmeter"
|
||||
path: rtlmeter
|
||||
|
|
@ -84,7 +84,7 @@ jobs:
|
|||
|
||||
- name: Use saved ccache
|
||||
if: ${{ env.CCACHE_DISABLE == 0 }}
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
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
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.verilator-archive-new }}
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ jobs:
|
|||
./rtlmeter report --steps '*' --metrics '*' ../results-${{ steps.results.outputs.hash }}.json
|
||||
|
||||
- name: Upload results - new
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: results-${{ steps.results.outputs.hash }}.json
|
||||
name: rtlmeter-${{ inputs.tag }}-results-${{ steps.results.outputs.hash }}
|
||||
|
|
@ -157,7 +157,7 @@ jobs:
|
|||
|
||||
- name: Download Verilator installation archive - old
|
||||
if: ${{ inputs.verilator-archive-old != '' }}
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.verilator-archive-old }}
|
||||
|
||||
|
|
@ -198,7 +198,7 @@ jobs:
|
|||
|
||||
- name: Upload results - old
|
||||
if: ${{ inputs.verilator-archive-old != '' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: reference-${{ steps.results.outputs.hash }}.json
|
||||
name: rtlmeter-${{ inputs.tag }}-reference-${{ steps.results.outputs.hash }}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ jobs:
|
|||
steps:
|
||||
|
||||
- name: Download repository archive
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: ${{ inputs.archive }}
|
||||
path: ${{ github.workspace }}
|
||||
|
|
@ -67,7 +67,7 @@ jobs:
|
|||
ls -lsha
|
||||
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2
|
||||
with:
|
||||
|
|
@ -99,7 +99,7 @@ jobs:
|
|||
|
||||
- name: Upload code coverage data
|
||||
if: ${{ inputs.dev-gcov }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: ${{ github.workspace }}/repo/obj_coverage/verilator-${{ inputs.suite }}.info
|
||||
name: code-coverage-${{ inputs.suite }}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ jobs:
|
|||
cases: ${{ steps.cases.outputs.cases }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
|
||||
- name: Startup
|
||||
id: start
|
||||
|
|
@ -208,7 +208,7 @@ jobs:
|
|||
run: echo "tags=$(jq -r 'keys | map(sub("^run-"; "")) | join(" ")' <<< '${{ toJSON(needs) }}')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout RTLMeter
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
repository: "verilator/rtlmeter"
|
||||
path: rtlmeter
|
||||
|
|
@ -232,7 +232,7 @@ jobs:
|
|||
./rtlmeter collate ../all-results-$tag/*.json > ../all-results-$tag.json
|
||||
done
|
||||
- name: Upload combined results
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: all-results-*.json
|
||||
name: all-results
|
||||
|
|
@ -259,7 +259,7 @@ jobs:
|
|||
done
|
||||
- name: Upload reference results
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: all-reference-*.json
|
||||
name: all-reference
|
||||
|
|
@ -278,27 +278,27 @@ jobs:
|
|||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Download combined results
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: all-results
|
||||
path: results
|
||||
- name: Upload published results
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: results/*.json
|
||||
name: published-results
|
||||
# Pushing to verilator/verilator-rtlmeter-results requires elevated permissions
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v3.2.0
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
client-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||
owner: verilator
|
||||
repositories: verilator-rtlmeter-results
|
||||
permission-contents: write
|
||||
- name: Checkout verilator-rtlmeter-results
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
repository: "verilator/verilator-rtlmeter-results"
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
|
|
@ -331,7 +331,7 @@ jobs:
|
|||
actions: read
|
||||
steps:
|
||||
- name: Checkout RTLMeter
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
repository: "verilator/rtlmeter"
|
||||
path: rtlmeter
|
||||
|
|
@ -341,7 +341,7 @@ jobs:
|
|||
run: make venv
|
||||
|
||||
- name: Checkout Verilator
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
path: verilator
|
||||
|
||||
|
|
@ -367,13 +367,13 @@ jobs:
|
|||
echo ${{ github.event.number }} > ../notification-artifact/pr-number.txt
|
||||
|
||||
- name: Upload report
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: report-artifact
|
||||
name: rtlmeter-report
|
||||
|
||||
- name: Upload notification
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: notification-artifact
|
||||
name: pr-notification
|
||||
|
|
@ -392,9 +392,9 @@ jobs:
|
|||
# Creating issues requires elevated privilege
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v3.2.0
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
client-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||
owner: 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:**
|
||||
|
||||
* 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]
|
||||
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 NOTREDOP error on reduction and negation operators (#7417) (#7623) (#7624).
|
||||
* 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]
|
||||
* 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]
|
||||
* 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 weak `until` / `until_with` property operators (#7290) (#7548) (#7685). [Yilou Wang]
|
||||
* 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 calling interface functions without parens (#7584). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* 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 FSM detection in primitive wrappers (#7607). [Yogish Sekhar]
|
||||
* 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 process::self().srand() (#7695). [Igor Zaworski, Antmicro Ltd.]
|
||||
* 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 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.]
|
||||
|
|
@ -71,9 +87,22 @@ Verilator 5.049 devel
|
|||
* Optimize runtime assertOn() checks (#7707). [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 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 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 cross-hierarchy tristate drivers of interface nets (#7339) (#7801). [Yilou Wang]
|
||||
* Fix destructive event state before dynamic waits (#7340). [Nick Brereton]
|
||||
* Fix ALWCOMBORDER on variable ordering (#7350) (#7608). [Cookie]
|
||||
* 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 parameter read through locally-declared interface instance (#7679). [Nick Brereton]
|
||||
* 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 `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 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 splitting functions containing fork logic (#7717). [Mateusz Gancarz, 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
|
||||
|
|
|
|||
28
Makefile.in
28
Makefile.in
|
|
@ -495,6 +495,11 @@ MAKE_FILES = \
|
|||
src/Makefile*.in \
|
||||
test_regress/Makefile* \
|
||||
|
||||
# Markdown
|
||||
MD_FILES = \
|
||||
*.md \
|
||||
*/*.md \
|
||||
|
||||
# Perl programs
|
||||
PERL_PROGRAMS = \
|
||||
bin/redirect \
|
||||
|
|
@ -541,6 +546,13 @@ PY_FILES = \
|
|||
# Python files, test_regress tests
|
||||
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 \
|
||||
|
|
@ -553,7 +565,7 @@ YAML_FILES = \
|
|||
# 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_FLAGS = --indent-size 2
|
||||
|
|
@ -590,6 +602,13 @@ format-make mbake:
|
|||
$(MBAKE) --version
|
||||
$(MBAKE) $(MBAKE_FLAGS) $(MAKE_FILES)
|
||||
|
||||
MDFORMAT = mdformat
|
||||
MDFORMAT_FLAGS =
|
||||
|
||||
format-md:
|
||||
$(MDFORMAT) --version
|
||||
$(MDFORMAT) $(MDFORMAT_FLAGS) $(MD_FILES)
|
||||
|
||||
YAPF = yapf
|
||||
YAPF_FLAGS = -i --parallel
|
||||
|
||||
|
|
@ -597,6 +616,13 @@ format-py yapf:
|
|||
$(YAPF) --version
|
||||
$(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_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
|
||||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
..
|
||||
Github doesn't render images unless absolute URL
|
||||
Do not know of a conditional tag, "only: github" nor "github display" works
|
||||
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| image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|
||||
:target: https://verilator.org
|
||||
|
||||
.. |badge2| image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0
|
||||
|
||||
.. |badge3| image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
||||
:target: https://opensource.org/licenses/Artistic-2.0
|
||||
|
||||
.. |badge4| image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
||||
:target: https://repology.org/project/verilator/versions
|
||||
|
||||
.. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator
|
||||
:target: https://hub.docker.com/r/verilator/verilator
|
||||
|
||||
.. |badge7| image:: https://github.com/verilator/verilator/workflows/build/badge.svg
|
||||
: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
|
||||
:target: https://verilator.github.io/verilator-rtlmeter-results
|
||||
|
||||
|
||||
Welcome to Verilator
|
||||
====================
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.**
|
||||
* Accepts Verilog or SystemVerilog
|
||||
* Performs lint code-quality checks
|
||||
* Compiles into multithreaded C++, or SystemC
|
||||
* Creates JSON to front-end your own tools
|
||||
- - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.**
|
||||
- Accepts Verilog or SystemVerilog
|
||||
- Performs lint code-quality checks
|
||||
- Compiles into multithreaded C++, or SystemC
|
||||
- Creates JSON to front-end your own tools
|
||||
- |Logo|
|
||||
* - |verilator multithreaded performance|
|
||||
- - |verilator multithreaded performance|
|
||||
- **Fast**
|
||||
* Outperforms many closed-source commercial simulators
|
||||
* Single- and multithreaded output models
|
||||
* - **Widely Used**
|
||||
* Wide industry and academic deployment
|
||||
* Out-of-the-box support from Arm and RISC-V vendor IP
|
||||
* Over 700 contributors
|
||||
- Outperforms many closed-source commercial simulators
|
||||
- Single- and multithreaded output models
|
||||
- - **Widely Used**
|
||||
- Wide industry and academic deployment
|
||||
- Out-of-the-box support from Arm and RISC-V vendor IP
|
||||
- Over 700 contributors
|
||||
- |verilator usage|
|
||||
* - |verilator community|
|
||||
- - |verilator community|
|
||||
- **Community Driven & Openly Licensed**
|
||||
* Guided by the `CHIPS Alliance`_ and `Linux Foundation`_
|
||||
* Open, and free as in both speech and beer
|
||||
* More simulation for your verification budget
|
||||
* - **Commercial Support Available**
|
||||
* Commercial support contracts
|
||||
* Design support contracts
|
||||
* Enhancement contracts
|
||||
- Guided by the `CHIPS Alliance`_ and `Linux Foundation`_
|
||||
- Open, and free as in both speech and beer
|
||||
- More simulation for your verification budget
|
||||
- - **Commercial Support Available**
|
||||
- Commercial support contracts
|
||||
- Design support contracts
|
||||
- Enhancement contracts
|
||||
- |verilator support|
|
||||
|
||||
|
||||
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
|
||||
simulation, Verilator is the tool for you.
|
||||
|
||||
|
||||
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
|
||||
licenses. Thus, Verilator gives you the best simulation cycles/dollar.
|
||||
|
||||
|
||||
Installation & Documentation
|
||||
============================
|
||||
|
||||
|
|
@ -115,7 +118,6 @@ For more information:
|
|||
|
||||
- `Verilator issues <https://verilator.org/issues>`_
|
||||
|
||||
|
||||
Support
|
||||
=======
|
||||
|
||||
|
|
@ -132,7 +134,6 @@ Verilator also supports and encourages commercial support models and
|
|||
organizations; please see `Verilator Commercial Support
|
||||
<https://verilator.org/verilator_commercial_support>`_.
|
||||
|
||||
|
||||
Related Projects
|
||||
================
|
||||
|
||||
|
|
@ -149,7 +150,6 @@ Related Projects
|
|||
- `Surfer <https://surfer-project.org/>`_ - Web or offline waveform viewer
|
||||
for Verilator traces.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
.. _chips alliance: https://chipsalliance.org
|
||||
|
||||
.. _icarus verilog: https://steveicarus.github.io/iverilog
|
||||
|
||||
.. _linux foundation: https://www.linuxfoundation.org
|
||||
|
||||
.. |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 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 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
|
||||
--dpi-hdr-only Only produce the DPI header 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-dfg Enable dumping DfgGraphs to .dot files
|
||||
--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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
=====================================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
=========================
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
|||
|
||||
404allen404
|
||||
Adam Bagley
|
||||
Adam Kostrzewski
|
||||
Adrian Sampson
|
||||
Adrien Le Masle
|
||||
أحمد المحمودي (Ahmed El-Mahmoudy)
|
||||
|
|
@ -158,6 +159,7 @@ Kefa Chen
|
|||
Keith Colbert
|
||||
Kevin Kiningham
|
||||
Kevin Nygaard
|
||||
Kornel Uriasz
|
||||
Kritik Bhimani
|
||||
Krzysztof Bieganski
|
||||
Krzysztof Boronski
|
||||
|
|
@ -250,6 +252,7 @@ Rowan Goemans
|
|||
Rupert Swarbrick
|
||||
Ryan Ziegler
|
||||
Ryszard Rozak
|
||||
Saksham Gupta
|
||||
Samuel Riedel
|
||||
Sean Cross
|
||||
Sebastien Van Cauwenberghe
|
||||
|
|
@ -270,12 +273,14 @@ Teng Huang
|
|||
Thomas Aldrian
|
||||
Thomas Brown
|
||||
Thomas Dybdahl Ahle
|
||||
Thomas Santerre
|
||||
Tim Hutt
|
||||
Tim Snyder
|
||||
Tobias Jensen
|
||||
Tobias Rosenkranz
|
||||
Tobias Wölfel
|
||||
Todd Strader
|
||||
Tom Jackson
|
||||
Tom Manner
|
||||
Tomasz Gorochowik
|
||||
Topa Topino
|
||||
|
|
@ -297,6 +302,7 @@ Vito Gamberini
|
|||
Wei-Lun Chiu
|
||||
William D. Jones
|
||||
Wilson Snyder
|
||||
Wolfgang Mayerwieser
|
||||
Xi Zhang
|
||||
Yan Xu
|
||||
Yangyu Chen
|
||||
|
|
@ -326,3 +332,4 @@ Yogish Sekhar
|
|||
Zubin Jain
|
||||
Muzaffer Kal
|
||||
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
|
||||
=======================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
@ -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:
|
||||
|
||||
* 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.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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
|
||||
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
|
||||
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.
|
||||
|
||||
* 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.
|
||||
|
||||
* Accessing any other internal members, including
|
||||
``/* verilator public_flat */`` items requires the following changes:
|
||||
- Accessing any other internal members, including ``/* verilator
|
||||
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
|
||||
``__024`` substring is the Verilator escape sequence for the ``$``
|
||||
character, i.e.: ``rootp`` points to the Verilated SystemVerilog
|
||||
``$root`` scope.
|
||||
|
||||
* Replace ``modelp->internal->member`` references with
|
||||
- Replace ``modelp->internal->member`` references with
|
||||
``modelp->rootp->internal->member`` references, which contain one
|
||||
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
|
||||
Verilog, put in our.v:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: sv
|
||||
|
||||
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
|
||||
|
||||
export "DPI-C" function integer \$myRand;
|
||||
import "DPI-C" function integer \$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
|
||||
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,
|
||||
* ``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
|
||||
``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
|
||||
|
|
@ -86,7 +87,8 @@ Please refer to `sv-bugpoint README
|
|||
<https://github.com/antmicro/sv-bugpoint/blob/main/README.md>`_ for more
|
||||
information on how to use `sv-bugpoint`.
|
||||
|
||||
.. Contributing
|
||||
.. ============
|
||||
..
|
||||
Contributing
|
||||
============
|
||||
|
||||
.. 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
|
||||
|
|
@ -29,10 +30,10 @@ Alliance <https://chipsalliance.org>`_, and `Antmicro Ltd
|
|||
Previous major corporate sponsors of Verilator, by providing significant
|
||||
contributions of time or funds include: Antmicro Ltd., Atmel Corporation,
|
||||
Compaq Corporation, Digital Equipment Corporation, Embecosm Ltd., Fractile
|
||||
Ltd., Hicamp Systems, Intel Corporation, Marvell Inc., Mindspeed Technologies
|
||||
Inc., MicroTune Inc., picoChip Designs Ltd., Sun Microsystems Inc., Nauticus
|
||||
Networks Inc., SiCortex Inc, Shunyao CAD, Tenstorrent USA, Inc. and Western
|
||||
Digital Inc.
|
||||
Ltd., Hicamp Systems, Intel Corporation, Marvell Inc., Mindspeed
|
||||
Technologies Inc., MicroTune Inc., picoChip Designs Ltd., Sun Microsystems
|
||||
Inc., Nauticus Networks Inc., SiCortex Inc, Shunyao CAD, Tenstorrent USA,
|
||||
Inc. and Western Digital Inc.
|
||||
|
||||
The contributors of major functionality are: Jeremy Bennett, Krzysztof
|
||||
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,
|
||||
Vassilis Papaefstathiou, Sanggyu Park, Brad Parker, Risto Pejašinović, Seth
|
||||
Pellegrino, Joel Peltonen, Morten Borup Petersen, Dan Petrisko, Thanh Tung
|
||||
Pham, Wesley Piard, Maciej Piechotka, David Pierce, Cody Piersall,
|
||||
T. Platz, Michael Platzer, Dominic Plunkett, Nolan Poe, Tuomas Poikela,
|
||||
George Polack, David Poole, Michael Popoloski, Roman Popov, Aylon Chaim
|
||||
Porat, Oron Port, Rich Porter, Rick Porter, Stefan Post, Niranjan Prabhu,
|
||||
Damien Pretet, Harald Pretl, Bill Pringlemeir, Usha Priyadharshini, Mark
|
||||
Jackson Pulver, Prateek Puri, Nikolay Puzanov, Han Qi, Jiacheng Qian,
|
||||
Marshal Qiao, Raynard Qiao, Yujia Qiao, Jasen Qin, Frank Qiu, Nandu Raj,
|
||||
Kamil Rakoczy, Danilo Ramos, Drew Ranck, Chris Randall, Anton Rapp, Josh
|
||||
Redford, Odd Magne Reitan, Frédéric Requin, Wajahat Riaz, Dustin Richmond,
|
||||
Samuel Riedel, Alberto Del Rio, Eric Rippey, Narcis Rodas, Oleg Rodionov,
|
||||
Ludwig Rogiers, Paul Rolfe, Michail Rontionov, Arjen Roodselaar, Arthur
|
||||
Rosa, Tobias Rosenkranz, Yernagula Roshit, Diego Roux, Ryszard Rozak, Dan
|
||||
Pham, Wesley Piard, Maciej Piechotka, David Pierce, Cody Piersall, T.
|
||||
Platz, Michael Platzer, Dominic Plunkett, Nolan Poe, Tuomas Poikela, George
|
||||
Polack, David Poole, Michael Popoloski, Roman Popov, Aylon Chaim Porat,
|
||||
Oron Port, Rich Porter, Rick Porter, Stefan Post, Niranjan Prabhu, Damien
|
||||
Pretet, Harald Pretl, Bill Pringlemeir, Usha Priyadharshini, Mark Jackson
|
||||
Pulver, Prateek Puri, Nikolay Puzanov, Han Qi, Jiacheng Qian, Marshal Qiao,
|
||||
Raynard Qiao, Yujia Qiao, Jasen Qin, Frank Qiu, Nandu Raj, Kamil Rakoczy,
|
||||
Danilo Ramos, Drew Ranck, Chris Randall, Anton Rapp, Josh Redford, Odd
|
||||
Magne Reitan, Frédéric Requin, Wajahat Riaz, Dustin Richmond, Samuel
|
||||
Riedel, Alberto Del Rio, Eric Rippey, Narcis Rodas, Oleg Rodionov, Ludwig
|
||||
Rogiers, Paul Rolfe, Michail Rontionov, Arjen Roodselaar, Arthur Rosa,
|
||||
Tobias Rosenkranz, Yernagula Roshit, Diego Roux, Ryszard Rozak, Dan
|
||||
Ruelas-Petrisko, Luca Rufer, Huang Rui, Graham Rushton, Jan Egil Ruud,
|
||||
Denis Rystsov, Pawel Sagan, Robert Sammelson, Adrian Sampson, John
|
||||
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:
|
||||
|
||||
|
|
@ -187,6 +188,10 @@ The grammar of control commands is as follows:
|
|||
|
||||
.. 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
|
||||
should be isolated into new blocks. Same as
|
||||
: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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
============
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
@ -9,10 +10,10 @@ Examples
|
|||
|
||||
This section covers the following examples:
|
||||
|
||||
* :ref:`Example Create-Binary Execution`
|
||||
* :ref:`Example C++ Execution`
|
||||
* :ref:`Example SystemC Execution`
|
||||
* :ref:`Examples in the Distribution`
|
||||
- :ref:`Example Create-Binary Execution`
|
||||
- :ref:`Example C++ Execution`
|
||||
- :ref:`Example SystemC Execution`
|
||||
- :ref:`Examples in the Distribution`
|
||||
|
||||
.. toctree::
|
||||
: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:
|
||||
|
||||
|
|
@ -50,8 +51,8 @@ Options:
|
|||
|
||||
.. option:: +verilator+log+file+<filename>
|
||||
|
||||
Log all stdout and stderr to the specified output filename. If not specified
|
||||
the normal stdout/stderr streams are used.
|
||||
Log all stdout and stderr to the specified output filename. If not specified
|
||||
the normal stdout/stderr streams are used.
|
||||
|
||||
.. 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
|
||||
|
|
@ -490,6 +491,10 @@ Summary:
|
|||
Rarely needed - for developer use. Enable all dumping in the given
|
||||
source file at level 3.
|
||||
|
||||
.. option:: --dump-ast-patterns
|
||||
|
||||
Rarely needed. Enable dumping AstNodeExpr pattern statistics.
|
||||
|
||||
.. option:: --dump-defines
|
||||
|
||||
With :vlopt:`-E`, suppress normal output, and instead print a list of
|
||||
|
|
@ -657,8 +662,28 @@ Summary:
|
|||
|
||||
.. option:: -fno-assemble
|
||||
|
||||
.. option:: -fno-bit-scan-loops
|
||||
|
||||
Rarely needed. Disable converting bit counting loops into built-in operations.
|
||||
|
||||
.. 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-const
|
||||
|
|
@ -737,12 +762,41 @@ Summary:
|
|||
this is not recommended as may cause additional warnings and ordering
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
Rarely needed. Disable inlining of SystemVerilog functions and tasks.
|
||||
|
||||
.. option:: -fno-inline-funcs-eager
|
||||
|
||||
Rarely needed. Disable eager inlining of SystemVerilog functions and tasks.
|
||||
|
||||
.. option:: -fno-life
|
||||
|
||||
.. option:: -fno-life-post
|
||||
|
|
@ -944,27 +998,21 @@ Summary:
|
|||
|
||||
.. option:: --inline-cfuncs <value>
|
||||
|
||||
Inline small C++ function (internal AstCFunc) calls directly into their
|
||||
callers when the function has at most <value> nodes. This reduces
|
||||
function call overhead when :vlopt:`--output-split-cfuncs` places
|
||||
functions in separate compilation units that the C++ compiler cannot
|
||||
inline.
|
||||
Tune the inlining of small generated C++ function. Functions no bigger than
|
||||
<value> nodes will be inlined if possible. The default is 20.
|
||||
|
||||
Set to 0 to disable this optimization. The default is 20.
|
||||
|
||||
This optimization is automatically disabled when :vlopt:`--prof-cfuncs`
|
||||
or :vlopt:`--trace` is used.
|
||||
See also :vlopt:`--inline-cfuncs-product` and :vlopt:`-fno-inline-cfuncs`.
|
||||
|
||||
.. option:: --inline-cfuncs-product <value>
|
||||
|
||||
Tune the inlining of C++ function (internal AstCFunc) calls for larger
|
||||
functions. When a function is too large to always inline (exceeds
|
||||
:vlopt:`--inline-cfuncs` threshold), it may still be inlined if the
|
||||
function size multiplied by the number of call sites is at most <value>.
|
||||
Tune the inlining of small generated C++ function. If a function's node
|
||||
count multiplied by the number of calls is not bigger than <value>, the
|
||||
function will be inlined if possible.
|
||||
|
||||
This allows functions that are called only once or twice to be inlined
|
||||
even if they exceed the small function threshold. Set to 0 to only inline
|
||||
functions below the :vlopt:`--inline-cfuncs` threshold. The default is 200.
|
||||
This allows functions that are called only once or twice to be inlined even
|
||||
if they exceed the small function threshold. The default is 200.
|
||||
|
||||
See also :vlopt:`--inline-cfuncs` and :vlopt:`-fno-inline-cfuncs`.
|
||||
|
||||
.. 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
|
||||
==================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
===============
|
||||
|
|
|
|||
|
|
@ -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
|
||||
===================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -341,6 +342,10 @@ or "`ifdef`"'s may break other tools.
|
|||
|
||||
.. 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
|
||||
signal in any blocks should be isolated into new blocks. When large
|
||||
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
|
||||
|
|
@ -82,17 +83,17 @@ the licenses for details.
|
|||
|
||||
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
|
||||
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
|
||||
Lesser Public License.
|
||||
|
||||
|
|
@ -384,33 +385,33 @@ example of how to do this.
|
|||
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
|
||||
file is large enough to be split due to the :vlopt:`--output-split`
|
||||
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
|
||||
disabled on these routines. See the OPT_FAST and OPT_SLOW make variables
|
||||
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
|
||||
between identical source builds, even across different users. If ccache
|
||||
was installed when Verilator was built, it is used, or see OBJCACHE
|
||||
environment variable to override this. Also see the
|
||||
: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
|
||||
:option:`/*verilator&32;no_inline_module*/` metacomment to your top-level
|
||||
module. This will decrease the amount of code in the model's Verilated
|
||||
class, improving compile times of any instantiating top-level C++ code,
|
||||
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?
|
||||
|
|
@ -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
|
||||
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.)
|
||||
|
||||
* 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
|
||||
that is instantiated many times; in this case, Verilating one of the
|
||||
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
|
||||
|
|
@ -42,138 +43,141 @@ For --cc/--sc, it creates:
|
|||
|
||||
.. list-table::
|
||||
|
||||
* - *{prefix}*.json
|
||||
- - *{prefix}*.json
|
||||
- JSON build definition compiling (from --make json)
|
||||
* - *{prefix}*.mk
|
||||
- - *{prefix}*.mk
|
||||
- Make include file for compiling (from --make gmake)
|
||||
* - *{prefix}*\ _classes.mk
|
||||
- - *{prefix}*\ _classes.mk
|
||||
- Make include file with class names (from --make gmake)
|
||||
* - *{prefix}*.h
|
||||
- - *{prefix}*.h
|
||||
- Model header
|
||||
* - *{prefix}*.cpp
|
||||
- - *{prefix}*.cpp
|
||||
- Model C++ file
|
||||
* - *{prefix}*\ ___024root.h
|
||||
- - *{prefix}*\ ___024root.h
|
||||
- Top-level internal header file (from SystemVerilog $root)
|
||||
* - *{prefix}*\ ___024root.cpp
|
||||
- - *{prefix}*\ ___024root.cpp
|
||||
- Top-level internal C++ file (from SystemVerilog $root)
|
||||
* - *{prefix}*\ ___024root\ *{__n}*.cpp
|
||||
- - *{prefix}*\ ___024root\ *{__n}*.cpp
|
||||
- Additional top-level internal C++ files
|
||||
* - *{prefix}*\ ___024root__Slow\ *{__n}*.cpp
|
||||
- - *{prefix}*\ ___024root__Slow\ *{__n}*.cpp
|
||||
- Infrequent cold routines
|
||||
* - *{prefix}*\ ___024root__Trace\ *{__n}*.cpp
|
||||
- - *{prefix}*\ ___024root__Trace\ *{__n}*.cpp
|
||||
- Wave file generation code (from --trace-\*)
|
||||
* - *{prefix}*\ ___024root__Trace__Slow\ *{__n}*.cpp
|
||||
- - *{prefix}*\ ___024root__Trace__Slow\ *{__n}*.cpp
|
||||
- Wave file generation code (from --trace-\*)
|
||||
* - *{prefix}*\ __Dpi.h
|
||||
- - *{prefix}*\ __Dpi.h
|
||||
- DPI import and export declarations (from --dpi)
|
||||
* - *{prefix}*\ __Dpi.cpp
|
||||
- - *{prefix}*\ __Dpi.cpp
|
||||
- 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)
|
||||
* - *{prefix}*\ __Inlines.h
|
||||
- - *{prefix}*\ __Inlines.h
|
||||
- Inline support functions
|
||||
* - *{prefix}*\ __Syms.h
|
||||
- - *{prefix}*\ __Syms.h
|
||||
- Global symbol table header
|
||||
* - *{prefix}*\ __Syms.cpp
|
||||
- - *{prefix}*\ __Syms.cpp
|
||||
- Global symbol table C++
|
||||
* - *{prefix}{each_verilog_module}*.h
|
||||
- - *{prefix}{each_verilog_module}*.h
|
||||
- Lower level internal header files
|
||||
* - *{prefix}{each_verilog_module}*.cpp
|
||||
- - *{prefix}{each_verilog_module}*.cpp
|
||||
- Lower level internal C++ files
|
||||
* - *{prefix}{each_verilog_module}{__n}*.cpp
|
||||
- - *{prefix}{each_verilog_module}{__n}*.cpp
|
||||
- Additional lower C++ files
|
||||
|
||||
For --hierarchical mode, it creates:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - V\ *{hier_block}*\ /
|
||||
- - V\ *{hier_block}*/
|
||||
- Directory to Verilate each hierarchical block (from --hierarchical)
|
||||
* - *{prefix}*\ __hierVer.d
|
||||
- - *{prefix}*\ __hierVer.d
|
||||
- Make dependencies of the top module (from --hierarchical)
|
||||
* - *{prefix}*\ _hier.mk
|
||||
- - *{prefix}*\ _hier.mk
|
||||
- Make file for hierarchical blocks (from --make gmake)
|
||||
* - *{prefix}*\ __hierMkJsonArgs.f
|
||||
- - *{prefix}*\ __hierMkJsonArgs.f
|
||||
- Arguments for hierarchical Verilation (from --make json)
|
||||
* - *{prefix}*\ __hierMkArgs.f
|
||||
- - *{prefix}*\ __hierMkArgs.f
|
||||
- Arguments for hierarchical Verilation (from --make gmake)
|
||||
* - *{prefix}*\ __hierParameters.v
|
||||
- - *{prefix}*\ __hierParameters.v
|
||||
- Module parameters for hierarchical blocks
|
||||
* - *{prefix}*\ __hier.dir
|
||||
- Directory to store .dot, .vpp, .tree of top module (from --hierarchical)
|
||||
- - *{prefix}*\ __hier.dir
|
||||
- Directory to store .dot, .vpp, .tree of top module (from
|
||||
--hierarchical)
|
||||
|
||||
In specific debug and other modes, it also creates:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - *{prefix}*.sarif
|
||||
- - *{prefix}*.sarif
|
||||
- SARIF diagnostics (from --diagnostics-sarif)
|
||||
* - *{prefix}*.tree.json
|
||||
- - *{prefix}*.tree.json
|
||||
- JSON tree information (from --json-only)
|
||||
* - *{prefix}*.tree.meta.json
|
||||
- - *{prefix}*.tree.meta.json
|
||||
- JSON tree metadata (from --json-only)
|
||||
* - *{prefix}*\ __cdc.txt
|
||||
- - *{prefix}*\ __cdc.txt
|
||||
- Clock Domain Crossing checks (from --cdc)
|
||||
* - *{prefix}*\ __stats.txt
|
||||
- - *{prefix}*\ __stats.txt
|
||||
- Statistics (from --stats)
|
||||
* - *{prefix}*\ __idmap.txt
|
||||
- - *{prefix}*\ __idmap.txt
|
||||
- Symbol demangling (from --protect-ids)
|
||||
* - *{prefix}*\ __ver.d
|
||||
- - *{prefix}*\ __ver.d
|
||||
- Make dependencies (from -MMD)
|
||||
* - *{prefix}*\ __verFiles.dat
|
||||
- - *{prefix}*\ __verFiles.dat
|
||||
- Timestamps (from --skip-identical)
|
||||
* - *{prefix}{misc}*.dot
|
||||
- - *{prefix}{misc}*.dot
|
||||
- Debugging graph files (from --debug)
|
||||
* - *{prefix}{misc}*.tree
|
||||
- - *{prefix}{misc}*.tree
|
||||
- Debugging files (from --debug)
|
||||
* - *{prefix}*\ __inputs.vpp
|
||||
- - *{prefix}*\ __inputs.vpp
|
||||
- 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)
|
||||
|
||||
After running Make, the C++ compiler may produce the following:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - verilated{misc}*.d
|
||||
- - verilated{misc}*.d
|
||||
- Intermediate dependencies
|
||||
* - verilated{misc}*.o
|
||||
- - verilated{misc}*.o
|
||||
- Intermediate objects
|
||||
* - {mod_prefix}{misc}*.d
|
||||
- - {mod_prefix}{misc}*.d
|
||||
- Intermediate dependencies
|
||||
* - {mod_prefix}{misc}*.o
|
||||
- - {mod_prefix}{misc}*.o
|
||||
- Intermediate objects
|
||||
* - *{prefix}*\
|
||||
- - *{prefix}*\
|
||||
- Final executable (from --exe)
|
||||
* - lib\ *{prefix}*.a
|
||||
- - lib\ *{prefix}*.a
|
||||
- Final archive (default lib mode)
|
||||
* - libverilated.a
|
||||
- - libverilated.a
|
||||
- Runtime for verilated model (default lib mode)
|
||||
* - *{prefix}*\ __ALL.a
|
||||
- - *{prefix}*\ __ALL.a
|
||||
- Library of all Verilated objects
|
||||
* - *{prefix}*\ __ALL.cpp
|
||||
- - *{prefix}*\ __ALL.cpp
|
||||
- Include of all code for single compile
|
||||
* - *{prefix}{misc}*.d
|
||||
- - *{prefix}{misc}*.d
|
||||
- Intermediate dependencies
|
||||
* - *{prefix}{misc}*.o
|
||||
- - *{prefix}{misc}*.o
|
||||
- Intermediate objects
|
||||
|
||||
The Verilated executable may produce the following:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - coverage.dat
|
||||
- Code coverage output, and default input filename for :command:`verilator_coverage`
|
||||
* - gmon.out
|
||||
- GCC/clang code profiler output, often fed into :command:`verilator_profcfunc`
|
||||
* - profile.vlt
|
||||
- - coverage.dat
|
||||
- Code coverage output, and default input filename for
|
||||
:command:`verilator_coverage`
|
||||
- - gmon.out
|
||||
- GCC/clang code profiler output, often fed into
|
||||
:command:`verilator_profcfunc`
|
||||
- - profile.vlt
|
||||
- --prof-pgo data file for :ref:`Thread PGO`
|
||||
* - profile_exec.dat
|
||||
- - profile_exec.dat
|
||||
- --prof-exec data file for :command:`verilator_gantt`
|
||||
|
||||
Verilator_gantt may produce the following:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - profile_exec.vcd
|
||||
- - profile_exec.vcd
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
@ -16,16 +17,16 @@ Linux).
|
|||
Quick Install
|
||||
=============
|
||||
|
||||
1. Install Python for your platform from https://www.python.org/downloads/.
|
||||
2. Install CMake for your platform from https://cmake.org/download/ or
|
||||
#. Install Python for your platform from https://www.python.org/downloads/.
|
||||
#. Install CMake for your platform from https://cmake.org/download/ or
|
||||
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
|
||||
choice is Clang, then install https://releases.llvm.org/download.html or
|
||||
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.
|
||||
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
|
||||
the environment variable FLEX_INCLUDE to the directory containing
|
||||
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:
|
||||
|
||||
|
|
@ -384,12 +385,10 @@ the files:
|
|||
|
||||
make install
|
||||
|
||||
|
||||
.. Docker Build Environment
|
||||
|
||||
.. include:: ../../ci/docker/buildenv/README.rst
|
||||
|
||||
|
||||
.. Docker Run Environment
|
||||
|
||||
.. 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
|
||||
|
|
@ -102,11 +103,11 @@ Time
|
|||
|
||||
With :vlopt:`--timing`, all timing controls are supported:
|
||||
|
||||
* delay statements,
|
||||
* event control statements not only at the top of a process,
|
||||
* intra-assignment timing controls,
|
||||
* net delays,
|
||||
* ``wait`` statements,
|
||||
- delay statements,
|
||||
- event control statements not only at the top of a process,
|
||||
- intra-assignment timing controls,
|
||||
- net delays,
|
||||
- ``wait`` statements,
|
||||
|
||||
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`
|
||||
error, except:
|
||||
|
||||
* delay statements - they are ignored (as they are in synthesis), though they
|
||||
do issue a :option:`STMTDLY` warning,
|
||||
* intra-assignment timing controls - they are ignored, though they do issue
|
||||
- delay statements - they are ignored (as they are in synthesis), though
|
||||
they do issue a :option:`STMTDLY` warning,
|
||||
- intra-assignment timing controls - they are ignored, though they do issue
|
||||
an :option:`ASSIGNDLY` warning,
|
||||
* net delays - they are ignored,
|
||||
* event controls at the top of the procedure,
|
||||
- net delays - they are ignored,
|
||||
- event controls at the top of the procedure,
|
||||
|
||||
Forks cause this error as well, except:
|
||||
|
||||
* forks with no statements,
|
||||
* ``fork..join`` or ``fork..join_any`` with one statement,
|
||||
* forks with :vlopt:`--bbox-unsup`.
|
||||
- forks with no statements,
|
||||
- ``fork..join`` or ``fork..join_any`` with one statement,
|
||||
- forks with :vlopt:`--bbox-unsup`.
|
||||
|
||||
If neither :vlopt:`--timing` nor :vlopt:`--no-timing` is specified, all
|
||||
timing controls cause the :option:`NEEDTIMINGOPT` error, except event
|
||||
controls at the top of the process. Forks cause this error as well, except:
|
||||
|
||||
* forks with no statements,
|
||||
* ``fork..join`` or ``fork..join_any`` with one statement,
|
||||
* forks with :vlopt:`--bbox-unsup`.
|
||||
- forks with no statements,
|
||||
- ``fork..join`` or ``fork..join_any`` with one statement,
|
||||
- forks with :vlopt:`--bbox-unsup`.
|
||||
|
||||
Timing controls and forks can also be ignored in specific files or parts of
|
||||
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
|
||||
|
|
@ -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
|
||||
version of this standard.
|
||||
|
||||
.. [#] SystemVerilog is defined by the `Institute of Electrical and
|
||||
Electronics Engineers (IEEE) Standard for SystemVerilog - Unified
|
||||
Hardware Design, Specification, and Verification Language`, Standard
|
||||
1800, released in 2005, 2009, 2012, 2017, and 2023. The Verilator
|
||||
documentation uses the shorthand e.g., "IEEE 1800-2023", to refer to
|
||||
the, e.g., 2023 version of this standard.
|
||||
.. [#] SystemVerilog is defined by the `Institute of Electrical and Electronics
|
||||
Engineers (IEEE) Standard for SystemVerilog - Unified Hardware Design,
|
||||
Specification, and Verification Language`, Standard 1800, released in
|
||||
2005, 2009, 2012, 2017, and 2023. The Verilator documentation uses the
|
||||
shorthand e.g., "IEEE 1800-2023", to refer to the, e.g., 2023 version of
|
||||
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:
|
||||
|
||||
|
|
@ -209,7 +210,8 @@ For simple coverage points, use the ``cover property`` construct:
|
|||
|
||||
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:
|
||||
|
||||
|
|
@ -218,8 +220,8 @@ Covergroup Coverage
|
|||
|
||||
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
|
||||
translate covergroup coverage points the user has inserted manually in
|
||||
SystemVerilog code into the Verilated model. Verilator supports
|
||||
coverpoints with value and transition bins, and cross points.
|
||||
SystemVerilog code into the Verilated model. Verilator supports coverpoints
|
||||
with value and transition bins, and cross points.
|
||||
|
||||
|
||||
.. _fsm coverage:
|
||||
|
|
@ -244,15 +246,15 @@ encodings in these common forms:
|
|||
- Single-process FSMs, whose state dispatch is written as ``case (state)``
|
||||
or as a top-level ``if`` / ``else if`` chain comparing the same state
|
||||
variable against known state values
|
||||
- Two-process and three-block FSMs, where a clocked state register is paired
|
||||
with a combinational next-state block using the same supported
|
||||
- Two-process and three-block FSMs, where a clocked state register is
|
||||
paired with a combinational next-state block using the same supported
|
||||
``case`` or top-level ``if`` / ``else if`` dispatch forms
|
||||
|
||||
Scalar state encodings may be wider than 32 bits. This allows sparse
|
||||
state encodings, such as high-Hamming-distance enum or localparam values,
|
||||
to be preserved in the detected FSM model. Verilator uses the declared
|
||||
enum item name, parameter name, or localparam name as the reported state
|
||||
label where possible.
|
||||
Scalar state encodings may be wider than 32 bits. This allows sparse state
|
||||
encodings, such as high-Hamming-distance enum or localparam values, to be
|
||||
preserved in the detected FSM model. Verilator uses the declared enum item
|
||||
name, parameter name, or localparam name as the reported state label where
|
||||
possible.
|
||||
|
||||
Simple input guards are supported when they appear inside a recognized
|
||||
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 extracted coverage model:
|
||||
|
||||
- ``/*verilator fsm_state*/`` forces the variable to be treated as
|
||||
FSM state.
|
||||
- ``/*verilator fsm_reset_arc*/`` marks reset transitions as
|
||||
user-visible reset arcs instead of defaulting to a hidden reset-only
|
||||
summary.
|
||||
- ``/*verilator fsm_arc_include_cond*/`` keeps conditional branch
|
||||
arcs that would otherwise be skipped by the conservative extractor.
|
||||
- ``/*verilator fsm_state*/`` forces the variable to be treated as FSM
|
||||
state.
|
||||
- ``/*verilator fsm_reset_arc*/`` marks reset transitions as user-visible
|
||||
reset arcs instead of defaulting to a hidden reset-only summary.
|
||||
- ``/*verilator fsm_arc_include_cond*/`` keeps conditional branch arcs that
|
||||
would otherwise be skipped by the conservative extractor.
|
||||
|
||||
State registers may also be wrapped by a transparent instance, for
|
||||
example a project flop wrapper or primitive. Such wrappers must be
|
||||
described explicitly with a VLT command file action before Verilator will
|
||||
use their data, state, clock, or reset connections for FSM extraction:
|
||||
State registers may also be wrapped by a transparent instance, for example
|
||||
a project flop wrapper or primitive. Such wrappers must be described
|
||||
explicitly with a VLT command file action before Verilator will use their
|
||||
data, state, clock, or reset connections for FSM extraction:
|
||||
|
||||
.. 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" \
|
||||
-reset "rst_ni" -reset_value "ResetValue"
|
||||
|
||||
Reset arcs are emitted only when the configured reset port has an
|
||||
inferable edge in the wrapper and the configured reset value parameter is
|
||||
statically resolvable. If reset metadata is incomplete, Verilator warns
|
||||
and may still emit FSM state and transition coverage, but reset arcs are
|
||||
omitted.
|
||||
Reset arcs are emitted only when the configured reset port has an inferable
|
||||
edge in the wrapper and the configured reset value parameter is statically
|
||||
resolvable. If reset metadata is incomplete, Verilator warns and may still
|
||||
emit FSM state and transition coverage, but reset arcs are omitted.
|
||||
|
||||
Reset transitions are included in the collected data either way. By
|
||||
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:
|
||||
|
||||
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
|
||||
: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
|
||||
the filename for the coverage data file to write coverage data to
|
||||
(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:
|
||||
|
||||
* 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\>`.
|
||||
|
||||
* 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
|
||||
Verilator internals document (:file:`docs/internals.rst` in the
|
||||
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
|
||||
the lowest, which means performing them separately and in this order:
|
||||
|
||||
* :ref:`Thread PGO`
|
||||
* :ref:`Compiler PGO`
|
||||
- :ref:`Thread PGO`
|
||||
- :ref:`Compiler PGO`
|
||||
|
||||
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
|
||||
|
|
@ -674,7 +674,7 @@ multithreaded models.
|
|||
Please see the appropriate compiler documentation to use PGO with GCC or
|
||||
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:
|
||||
|
||||
.. 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
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
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
|
||||
|
|
@ -7,21 +8,21 @@ Verilating
|
|||
|
||||
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
|
||||
: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++
|
||||
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.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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
|
||||
useful to feed other tools and to debug how "\`define" statements are
|
||||
expanded.
|
||||
|
|
@ -127,9 +128,9 @@ Usage
|
|||
Users need to mark one or more moderate-size modules as hierarchy block(s).
|
||||
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.
|
||||
|
||||
|
|
@ -145,28 +146,28 @@ Limitations
|
|||
|
||||
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
|
||||
hierarchy block instance can be accessed from the directly enclosing
|
||||
nested hierarchy block, or from the top level non-hierarchical portions
|
||||
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.
|
||||
|
||||
* 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.
|
||||
|
||||
* Delays are not allowed in hierarchy blocks.
|
||||
- Delays are not allowed in hierarchy blocks.
|
||||
|
||||
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.
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -882,6 +883,22 @@ List Of Warnings
|
|||
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
|
||||
|
||||
Warns that the same always block contains multiple enum-typed case
|
||||
|
|
@ -1178,7 +1195,7 @@ List Of Warnings
|
|||
|
||||
.. 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
|
||||
``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
|
||||
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
|
||||
clock enables, identified from the reported path going through a clock
|
||||
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:
|
||||
|
||||
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``.
|
||||
|
||||
|
||||
|
|
@ -666,23 +666,23 @@ The second visitor in ``V3Timing.cpp``, ``TimingControlVisitor``, uses the
|
|||
information provided by ``TimingSuspendableVisitor`` and transforms each
|
||||
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
|
||||
corresponding to the sentree referenced by the event control. This
|
||||
sentree is also referenced by the ``AstCAwait`` node, to be used later by
|
||||
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
|
||||
scheduler multiple times: for trigger evaluation, updates, and
|
||||
resumption. The dynamic trigger scheduler is responsible for resuming the
|
||||
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
|
||||
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::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()``:
|
||||
|
||||
* ``_timing_ready()``, which commits all coroutines whose triggers were
|
||||
not set in the current iteration,
|
||||
* ``_timing_resume()``, which calls `resume()` on all trigger and delay
|
||||
- ``_timing_ready()``, which commits all coroutines whose triggers were not
|
||||
set in the current iteration,
|
||||
- ``_timing_resume()``, which calls `resume()` on all trigger and delay
|
||||
schedulers whose triggers were set in the current iteration.
|
||||
|
||||
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
|
||||
Verilator more predictable; this is the reason for introduction of 3rd
|
||||
stage in `VlTriggerScheduler` and thanks to this it is guaranteed that
|
||||
downstream logic will be evaluated before resumption (assuming that the
|
||||
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``;
|
||||
* firing cannot cannot be lost
|
||||
(``test_regress/t/t_event_control_double_lost.v``) - which is possible when
|
||||
triggers are not evaluated right before awaiting.
|
||||
- firing cannot cannot be lost
|
||||
(``test_regress/t/t_event_control_double_lost.v``) - which is possible
|
||||
when triggers are not evaluated right before awaiting.
|
||||
|
||||
All coroutines are committed and resumed in the 'act' eval loop. With
|
||||
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
|
||||
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
|
||||
variables with identical non-empty footprints, emit those groups in deterministic
|
||||
footprint-key order, then emit variables with no footprint information last.
|
||||
variable. The variables in a given module have a set of footprints. We
|
||||
group variables with identical non-empty footprints, emit those groups in
|
||||
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
|
||||
boundary. This avoids false sharing between different macro-task footprints
|
||||
without building a complete pairwise-distance graph over all footprints, which
|
||||
would use excessive memory on very large models.
|
||||
The first emitted variable in each footprint group is aligned to a
|
||||
cache-line boundary. This avoids false sharing between different macro-task
|
||||
footprints without building a complete pairwise-distance graph over all
|
||||
footprints, which would use excessive memory on very large models.
|
||||
|
||||
This is an old idea. Simulators designed at DEC in the early 1990s used
|
||||
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
|
||||
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
|
||||
node is always present, and is a single ``AstNode`` (as opposed to a
|
||||
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.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
to NULL in the constructor, where that node (``AstModule`` visitor) sets
|
||||
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
|
||||
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
|
||||
using the ``user1()`` through ``user4()`` methods, or as a pointer (of
|
||||
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
|
||||
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
|
||||
type ``AstNUser`` that is ignored on most of the visitor functions.
|
||||
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
|
||||
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``)
|
||||
|
||||
* ``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``.
|
||||
* ``jtree $1`` - Pretty print a dump that was previously saved by
|
||||
- ``jtree $1`` - Pretty print a dump that was previously saved by
|
||||
``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.
|
||||
|
||||
* ``jtree 0x55555613dca0`` - Pretty print using address literal (rather
|
||||
- ``jtree 0x55555613dca0`` - Pretty print using address literal (rather
|
||||
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
|
||||
``gdb``'s ``help`` command.
|
||||
|
|
@ -2243,25 +2244,25 @@ Adding 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
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
|
||||
``test_regress/obj_dir/t_<newtestcase>/*.tree`` file which you can
|
||||
examine to see if the parsing worked. See also the sections above on
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
.. for github, vim: syntax=reStructuredText
|
||||
.. SPDX-FileCopyrightText: 2025-2026 Wilson Snyder
|
||||
.. SPDX-License-Identifier: CC0-1.0
|
||||
..
|
||||
for github, vim: syntax=reStructuredText
|
||||
SPDX-FileCopyrightText: 2025-2026 Wilson Snyder
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
Security Policy
|
||||
===============
|
||||
|
|
|
|||
|
|
@ -861,6 +861,7 @@ hx
|
|||
hyperthreading
|
||||
hyperthreads
|
||||
icecream
|
||||
ico
|
||||
idmap
|
||||
ifdef
|
||||
ifdefed
|
||||
|
|
@ -916,6 +917,7 @@ linters
|
|||
linux
|
||||
liu
|
||||
livelock
|
||||
liveness
|
||||
lldb
|
||||
ln
|
||||
loc
|
||||
|
|
@ -924,6 +926,7 @@ localparams
|
|||
localtime
|
||||
logicals
|
||||
longint
|
||||
lookups
|
||||
lossy
|
||||
lsb
|
||||
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(); \
|
||||
}
|
||||
|
||||
#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.
|
||||
#if 1
|
||||
# define FST_DCHECK(a) FST_CHECK(a)
|
||||
|
|
|
|||
|
|
@ -335,10 +335,10 @@ public:
|
|||
// Double variables should not use these array-based emitValueChange overloads.
|
||||
// We implement them to satisfy the VairableInfo::dispatchHelper template instantiation.
|
||||
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) {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ Handle Writer::createVar(
|
|||
// (void)type;
|
||||
// (void)svt;
|
||||
// (void)sdt;
|
||||
// throw std::runtime_error("TODO");
|
||||
// FST_FAIL_STRING("TODO");
|
||||
// return 0;
|
||||
// }
|
||||
// LCOV_EXCL_STOP
|
||||
|
|
@ -353,11 +353,7 @@ void compressUsingZlib(
|
|||
uncompressed_size,
|
||||
level
|
||||
);
|
||||
if (z_status != Z_OK) {
|
||||
throw std::runtime_error(
|
||||
"Failed to compress data with zlib, error code: " + std::to_string(z_status)
|
||||
);
|
||||
}
|
||||
FST_CHECK_EQ(z_status, Z_OK);
|
||||
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>
|
||||
static size_t _vl_snprintf_string(std::string& str, const char* format,
|
||||
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,
|
||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
|
||||
// Check if selected directive type bit in the assertOn is enabled for assertion type.
|
||||
// Note: it is assumed that this is checked only for one type at the time.
|
||||
|
||||
// Flag unspecified assertion types as disabled.
|
||||
if (type == 0) return false;
|
||||
|
||||
// Get index of 3-bit group guarding assertion type status.
|
||||
// Since the assertOnGet is generated __always__ for a single assert type, we assume that only
|
||||
// a single bit will be set. Thus, ceil log2 will work fine.
|
||||
VL_DEBUG_IFDEF(assert((type & (type - 1)) == 0););
|
||||
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));
|
||||
return assertCtlGet(VerilatedAssertCtlQuery::ASSERT_CTL_ON, type, directive);
|
||||
}
|
||||
uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_PURE {
|
||||
// Place the directive bits at each selected assertion type's 3-bit group.
|
||||
uint32_t mask = 0;
|
||||
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
|
||||
if (VL_BITISSET_I(types, i)) mask |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
// For each assertion type, set directive bits.
|
||||
|
||||
// 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);
|
||||
}
|
||||
m_s.m_assertOn |= assertOnMask(types, directives);
|
||||
}
|
||||
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
// Iterate through all positions of assertion type bits. If bit for this assertion type is set,
|
||||
// clear 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));
|
||||
m_s.m_assertOn &= ~assertOnMask(types, directives);
|
||||
}
|
||||
void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
// 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 {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
|
|
@ -3893,6 +3957,7 @@ std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { retu
|
|||
|
||||
// cppcheck-suppress unusedFunction // Used by applications
|
||||
uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
|
||||
if (m_entSize) return m_entSize;
|
||||
uint32_t size = 1;
|
||||
switch (vltype()) {
|
||||
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);
|
||||
}
|
||||
|
||||
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*
|
||||
VerilatedScope::forceableVarInsert(const char* namep, void* datap, bool isParam,
|
||||
VerilatedVarType vltype, int vlflags, void* forceReadSignalData,
|
||||
|
|
|
|||
|
|
@ -137,7 +137,9 @@ enum VerilatedVarType : uint8_t {
|
|||
VLVT_UINT64, // AKA QData
|
||||
VLVT_WDATA, // AKA VlWide
|
||||
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 {
|
||||
|
|
@ -154,7 +156,8 @@ enum VerilatedVarFlags : uint32_t {
|
|||
VLVF_CONTINUOUSLY = (1 << 11), // Is continously assigned
|
||||
VLVF_FORCEABLE = (1 << 12), // Forceable
|
||||
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
|
||||
|
|
@ -175,6 +178,15 @@ enum class VerilatedAssertDirectiveType : uint8_t {
|
|||
DIRECTIVE_TYPE_COVER = (1 << 1),
|
||||
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 VerilatedAssertDirectiveType_t = std::underlying_type<VerilatedAssertDirectiveType>::type;
|
||||
|
||||
|
|
@ -356,6 +368,10 @@ private:
|
|||
static constexpr size_t ASSERT_ON_WIDTH
|
||||
= ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits
|
||||
+ 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:
|
||||
// TYPES
|
||||
|
|
@ -375,6 +391,16 @@ protected:
|
|||
// for each VerilatedAssertType we store
|
||||
// 3-bits, one for each directive type. Last
|
||||
// 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_fatalOnError = true; // Fatal on $stop/non-fatal error
|
||||
bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported
|
||||
|
|
@ -484,6 +510,13 @@ public:
|
|||
/// Clear enabled status for given assertion types
|
||||
void assertOnClear(VerilatedAssertType_t types,
|
||||
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)
|
||||
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
|
||||
/// 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;
|
||||
VerilatedVar* varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype,
|
||||
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,
|
||||
VerilatedVarType vltype, int vlflags,
|
||||
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 {
|
||||
private:
|
||||
struct Entry final {
|
||||
int m_lsb; // Inclusive lower bit
|
||||
int m_msb; // Inclusive upper bit
|
||||
int m_lsb; // Inclusive lower bit for scalar path or element index for unpacked
|
||||
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
|
||||
const void* m_rhsDatap; // Pointer to RHS storage
|
||||
|
||||
bool operator<(const Entry& other) const { return m_msb < other.m_msb; }
|
||||
int m_bitLsb = 0;
|
||||
int m_bitMsb = 0;
|
||||
int m_elemWidth = 0;
|
||||
};
|
||||
|
||||
std::vector<Entry> m_entries; // Sorted by msb, non-overlapping
|
||||
|
|
@ -134,6 +135,41 @@ private:
|
|||
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) {
|
||||
assert(width > 0 && width <= VL_QUADSIZE);
|
||||
assert(rhsLsb >= 0);
|
||||
|
|
@ -195,6 +231,22 @@ private:
|
|||
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>
|
||||
typename std::enable_if<VlIsVlWide<T>::value>::type applyEntries(T& val) const {
|
||||
for (const auto& entry : m_entries) {
|
||||
|
|
@ -229,15 +281,19 @@ public:
|
|||
using ElemRef
|
||||
= decltype(VlForceArrayIndexer<T>::elem(result, static_cast<std::size_t>(0)));
|
||||
using Elem = VlForceBaseType<ElemRef>;
|
||||
const int total = static_cast<int>(VlForceArrayIndexer<T>::size);
|
||||
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 endIdx = entry.m_msb;
|
||||
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);
|
||||
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;
|
||||
|
|
@ -250,14 +306,18 @@ public:
|
|||
T readIndex(T origVal, int index) const {
|
||||
if (m_entries.empty()) return origVal;
|
||||
|
||||
const auto it = std::lower_bound(m_entries.begin(), m_entries.end(), index,
|
||||
[](const Entry& e, int idx) { return e.m_msb < idx; });
|
||||
if (it != m_entries.end() && it->m_lsb <= index) {
|
||||
const int rhsIndex = index - it->m_rhsLsb;
|
||||
const T* const rhsBasep = static_cast<const T*>(it->m_rhsDatap);
|
||||
return rhsBasep[rhsIndex];
|
||||
T result = origVal;
|
||||
for (auto it = std::lower_bound(m_entries.begin(), m_entries.end(), index,
|
||||
[](const Entry& e, int idx) { return e.m_msb < idx; });
|
||||
it != m_entries.end() && it->m_lsb <= index; ++it) {
|
||||
if (it->m_elemWidth == 0) {
|
||||
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 {
|
||||
|
|
@ -291,11 +351,28 @@ public:
|
|||
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) {
|
||||
assert(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() {}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
const char* msg) VL_MT_SAFE;
|
||||
|
||||
// clang-format off
|
||||
/// 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;
|
||||
// clang-format on
|
||||
|
||||
/// 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;
|
||||
|
|
@ -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
|
||||
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
|
||||
inline IData VL_RANDOM_I() 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;
|
||||
}
|
||||
|
||||
// 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
|
||||
double VL_ITOR_D_W(int lbits, WDataInP const lwp) 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;
|
||||
}
|
||||
|
||||
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 {
|
||||
// MSB set bit plus one; similar to FLS. 0=value is zero
|
||||
for (int i = words - 1; i >= 0; --i) {
|
||||
if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken
|
||||
for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) {
|
||||
if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) return i * VL_EDATASIZE + bit + 1;
|
||||
}
|
||||
// Can't get here - one bit must be set
|
||||
}
|
||||
// Shorter worst case if predict not taken
|
||||
if (VL_UNLIKELY(lwp[i])) return i * VL_EDATASIZE + VL_MOSTSETBITP1_I(lwp[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ class VerilatedVarProps VL_NOT_FINAL {
|
|||
const uint32_t m_magic; // Magic number
|
||||
const VerilatedVarType m_vltype; // Data type
|
||||
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_packed; // Packed array ranges
|
||||
VerilatedRange m_packedDpi; // Flattened packed array range
|
||||
|
|
@ -104,10 +105,12 @@ class VerilatedVarProps VL_NOT_FINAL {
|
|||
// CONSTRUCTORS
|
||||
protected:
|
||||
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_vltype{vltype}
|
||||
, m_vlflags{vlflags} {
|
||||
, m_vlflags{vlflags}
|
||||
, m_entSize{entSize} {
|
||||
// Only preallocate the ranges
|
||||
initUnpacked(udims, nullptr);
|
||||
initPacked(pdims, nullptr);
|
||||
|
|
@ -119,12 +122,14 @@ public:
|
|||
VerilatedVarProps(VerilatedVarType vltype, int vlflags)
|
||||
: m_magic{MAGIC}
|
||||
, 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)
|
||||
: m_magic{MAGIC}
|
||||
, 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);
|
||||
}
|
||||
// With packed
|
||||
|
|
@ -132,14 +137,16 @@ public:
|
|||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pdims, const int* plims)
|
||||
: m_magic{MAGIC}
|
||||
, 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);
|
||||
}
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims,
|
||||
Packed, int pdims, const int* plims)
|
||||
: m_magic{MAGIC}
|
||||
, 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);
|
||||
initPacked(pdims, plims);
|
||||
}
|
||||
|
|
@ -164,6 +171,7 @@ public:
|
|||
bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
|
||||
bool isSigned() const { return ((m_vlflags & VLVF_SIGNED) != 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 pdims() const VL_MT_SAFE { return m_packed.size(); }
|
||||
int dims() const VL_MT_SAFE { return pdims() + udims(); }
|
||||
|
|
@ -265,6 +273,8 @@ protected:
|
|||
// CONSTRUCTORS
|
||||
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
||||
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,
|
||||
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam,
|
||||
std::unique_ptr<const VerilatedForceControlSignals> forceControlSignals);
|
||||
|
|
@ -296,6 +306,13 @@ inline VerilatedVar::VerilatedVar(const char* namep, void* datap, VerilatedVarTy
|
|||
, m_datap{datap}
|
||||
, m_namep{namep}
|
||||
, 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(
|
||||
const char* namep, void* datap, VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims,
|
||||
int pdims, bool isParam,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
|
@ -68,6 +69,25 @@ constexpr unsigned VL_VPI_LINE_SIZE_ = 8192;
|
|||
//======================================================================
|
||||
// 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
|
||||
class VerilatedVpio VL_NOT_FINAL {
|
||||
// CONSTANTS
|
||||
|
|
@ -201,6 +221,9 @@ public:
|
|||
}
|
||||
const VerilatedVar* varp() const { return m_varp; }
|
||||
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).
|
||||
int32_t indexedDim() const { return m_indexedDim; }
|
||||
// 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_bitOffset = 0;
|
||||
int32_t m_partselBits = -1; // Part-select width, -1 means no part-select active
|
||||
std::string m_name;
|
||||
std::string m_fullNameOverride;
|
||||
|
||||
protected:
|
||||
void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries
|
||||
|
|
@ -373,6 +398,17 @@ public:
|
|||
: VerilatedVpioVarBase{varp, scopep} {
|
||||
m_entSize = varp->entSize();
|
||||
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)
|
||||
: VerilatedVpioVarBase{vop} {
|
||||
|
|
@ -380,6 +416,8 @@ public:
|
|||
m_entSize = vop->m_entSize;
|
||||
m_varDatap = vop->m_varDatap;
|
||||
m_index = vop->m_index;
|
||||
m_name = vop->m_name;
|
||||
m_fullNameOverride = vop->m_fullNameOverride;
|
||||
m_partselBits = vop->m_partselBits;
|
||||
m_bitOffset = vop->m_bitOffset;
|
||||
// Not copying m_prevDatap, must be nullptr
|
||||
|
|
@ -395,15 +433,22 @@ public:
|
|||
uint32_t bitOffset() const override { return m_bitOffset; }
|
||||
int32_t partselBits() const { return m_partselBits; }
|
||||
uint32_t bitSize() const {
|
||||
if (isStructOrUnion() && !isIndexedDimUnpacked()) return 0;
|
||||
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
|
||||
return VerilatedVpioVarBase::bitSize();
|
||||
}
|
||||
uint32_t size() const override {
|
||||
if (isStructOrUnion() && !isIndexedDimUnpacked()) return 0;
|
||||
if (m_partselBits >= 0) return static_cast<uint32_t>(m_partselBits);
|
||||
return VerilatedVpioVarBase::size();
|
||||
}
|
||||
const VerilatedRange* rangep() const override {
|
||||
if (isStructOrUnion() && !isIndexedDimUnpacked()) return nullptr;
|
||||
return VerilatedVpioVarBase::rangep();
|
||||
}
|
||||
uint32_t entSize() const { return m_entSize; }
|
||||
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].
|
||||
VerilatedVpioVar* withPartSelect(int32_t hi, int32_t lo) const {
|
||||
if (isIndexedDimUnpacked()) return nullptr;
|
||||
|
|
@ -456,22 +501,43 @@ public:
|
|||
|
||||
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;
|
||||
// TODO have V3EmitCSyms.cpp put vpiType directly into constant table
|
||||
switch (varp()->vltype()) {
|
||||
case VLVT_REAL: type = vpiRealVar; 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;
|
||||
}
|
||||
if (isIndexedDimUnpacked()) return vpiRegArray;
|
||||
if (isIndexedDimUnpacked())
|
||||
return isStructOrUnion() && varp()->isNet() ? vpiNetArray : vpiRegArray;
|
||||
return type;
|
||||
}
|
||||
const char* fullname() const override {
|
||||
static thread_local std::string t_out;
|
||||
t_out = std::string{scopep()->name()} + "." + name();
|
||||
for (auto idx : index()) t_out += "[" + std::to_string(idx) + "]";
|
||||
return t_out.c_str();
|
||||
m_fullname = m_fullNameOverride.empty()
|
||||
? std::string{scopep()->name()} + "." + varp()->name()
|
||||
: m_fullNameOverride;
|
||||
for (auto idx : index()) m_fullname += "[" + std::to_string(idx) + "]";
|
||||
return m_fullname.c_str();
|
||||
}
|
||||
void* prevDatap() const { return m_prevDatap; }
|
||||
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 {
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioModule(const VerilatedScope* modulep)
|
||||
: VerilatedVpioScope{modulep} {
|
||||
// Look for '.' not inside escaped identifier
|
||||
const std::string scopename = m_fullname;
|
||||
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;
|
||||
if (VL_UNLIKELY(!_vl_vpi_find_unescaped_dot(m_fullname))) m_toplevel = true;
|
||||
}
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
static VerilatedVpioModule* castp(vpiHandle h) {
|
||||
|
|
@ -1770,6 +1874,8 @@ const char* VerilatedVpiError::strFromVpiObjType(PLI_INT32 vpiVal) VL_PURE {
|
|||
};
|
||||
// clang-format on
|
||||
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 >= vpiPackage && vpiVal <= vpiPropFormalDecl)
|
||||
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, vpiStructVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionNet);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassObj);
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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 VerilatedScope* scopep;
|
||||
std::string fullnameOverride;
|
||||
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."))) {
|
||||
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;
|
||||
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
|
||||
|
|
@ -2491,8 +2762,18 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
|||
}
|
||||
if (!varp) {
|
||||
scopep = Verilated::threadContextp()->scopeFind(scopename.c_str());
|
||||
if (!scopep) return nullptr;
|
||||
varp = scopep->varFind(basename.c_str());
|
||||
if (scopep) { 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;
|
||||
|
|
@ -2501,6 +2782,11 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
|||
vpiHandle resultHandle;
|
||||
if (varp->isParam()) {
|
||||
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 {
|
||||
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());
|
||||
return nullptr;
|
||||
}
|
||||
case vpiMember: {
|
||||
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
if (!vop || !vop->isStructOrUnion()) return nullptr;
|
||||
return ((new VerilatedVpioMemberIter{vop})->castVpiHandle());
|
||||
}
|
||||
case vpiParameter: {
|
||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||
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;
|
||||
return vop->varp()->isSigned();
|
||||
}
|
||||
case vpiPacked: {
|
||||
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
||||
if (VL_LIKELY(vop && vop->isStructOrUnion())) return 0;
|
||||
[[fallthrough]];
|
||||
}
|
||||
default:
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
|
||||
__func__, VerilatedVpiError::strFromVpiProp(property));
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@
|
|||
breathe==4.36.0
|
||||
compiledb==0.10.7
|
||||
distro==1.9.0
|
||||
docstrfmt==2.2.0
|
||||
gersemi==0.23.1
|
||||
mbake==1.4.3
|
||||
mdformat==1.0.0
|
||||
mypy==1.19.0
|
||||
pylint==3.0.2
|
||||
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
|
||||
V3AstNodeOther.h
|
||||
V3AstNodeStmt.h
|
||||
V3AstPatterns.h
|
||||
V3AstUserAllocator.h
|
||||
V3Begin.h
|
||||
V3Branch.h
|
||||
|
|
@ -175,7 +176,6 @@ set(HEADERS
|
|||
V3Simulate.h
|
||||
V3Slice.h
|
||||
V3Split.h
|
||||
V3SplitAs.h
|
||||
V3SplitVar.h
|
||||
V3StackCount.h
|
||||
V3Stats.h
|
||||
|
|
@ -183,7 +183,6 @@ set(HEADERS
|
|||
V3String.h
|
||||
V3Subst.h
|
||||
V3SymTable.h
|
||||
V3TSP.h
|
||||
V3Table.h
|
||||
V3Task.h
|
||||
V3ThreadPool.h
|
||||
|
|
@ -203,7 +202,6 @@ set(HEADERS
|
|||
V3WidthCommit.h
|
||||
V3WidthRemove.h
|
||||
VlcBucket.h
|
||||
VlcCovergroup.h
|
||||
VlcOptions.h
|
||||
VlcPoint.h
|
||||
VlcSource.h
|
||||
|
|
@ -253,7 +251,6 @@ set(COMMON_SOURCES
|
|||
V3DfgDataType.cpp
|
||||
V3DfgDecomposition.cpp
|
||||
V3DfgDfgToAst.cpp
|
||||
V3DfgDumpPatterns.cpp
|
||||
V3DfgOptimizer.cpp
|
||||
V3DfgPasses.cpp
|
||||
V3DfgPeephole.cpp
|
||||
|
|
@ -330,6 +327,7 @@ set(COMMON_SOURCES
|
|||
V3ParseGrammar.cpp
|
||||
V3ParseImp.cpp
|
||||
V3ParseLex.cpp
|
||||
V3PatternStats.cpp
|
||||
V3PreProc.cpp
|
||||
V3PreShell.cpp
|
||||
V3Premit.cpp
|
||||
|
|
@ -351,14 +349,12 @@ set(COMMON_SOURCES
|
|||
V3Scoreboard.cpp
|
||||
V3Slice.cpp
|
||||
V3Split.cpp
|
||||
V3SplitAs.cpp
|
||||
V3SplitVar.cpp
|
||||
V3StackCount.cpp
|
||||
V3Stats.cpp
|
||||
V3StatsReport.cpp
|
||||
V3String.cpp
|
||||
V3Subst.cpp
|
||||
V3TSP.cpp
|
||||
V3Table.cpp
|
||||
V3Task.cpp
|
||||
V3ThreadPool.cpp
|
||||
|
|
|
|||
|
|
@ -264,7 +264,6 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3DfgDataType.o \
|
||||
V3DfgDecomposition.o \
|
||||
V3DfgDfgToAst.o \
|
||||
V3DfgDumpPatterns.o \
|
||||
V3DfgOptimizer.o \
|
||||
V3DfgPasses.o \
|
||||
V3DfgPeephole.o \
|
||||
|
|
@ -314,6 +313,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3OrderProcessDomains.o \
|
||||
V3OrderSerial.o \
|
||||
V3Param.o \
|
||||
V3PatternStats.o \
|
||||
V3Premit.o \
|
||||
V3ProtectLib.o \
|
||||
V3RandSequence.o \
|
||||
|
|
@ -333,11 +333,9 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3Scoreboard.o \
|
||||
V3Slice.o \
|
||||
V3Split.o \
|
||||
V3SplitAs.o \
|
||||
V3SplitVar.o \
|
||||
V3StackCount.o \
|
||||
V3Subst.o \
|
||||
V3TSP.o \
|
||||
V3Table.o \
|
||||
V3Task.o \
|
||||
V3Timing.o \
|
||||
|
|
|
|||
|
|
@ -351,7 +351,7 @@ public:
|
|||
|
||||
class ActiveDlyVisitor final : public VNVisitor {
|
||||
public:
|
||||
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL, CT_SUSPENDABLE };
|
||||
enum CheckType : uint8_t { CT_COMB, CT_FINAL };
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
|
|
@ -359,15 +359,9 @@ private:
|
|||
|
||||
// VISITORS
|
||||
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
|
||||
if (m_check == CT_INITIAL) {
|
||||
nodep->v3warn(INITIALDLY,
|
||||
"Non-blocking assignment '<=' in initial/final block\n"
|
||||
<< nodep->warnMore()
|
||||
<< "... This will be executed as a blocking assignment '='!");
|
||||
if (m_check == CT_FINAL) {
|
||||
nodep->v3warn(FINALDLY, "Non-blocking assignment '<=' in final block");
|
||||
} else {
|
||||
nodep->v3warn(COMBDLY,
|
||||
"Non-blocking assignment '<=' in combinational logic process\n"
|
||||
|
|
@ -465,11 +459,7 @@ class ActiveVisitor final : public VNVisitor {
|
|||
wantactivep->addStmtsp(nodep);
|
||||
|
||||
// Warn and convert any delayed assignments
|
||||
{
|
||||
ActiveDlyVisitor{nodep, !m_clockedProcess ? ActiveDlyVisitor::CT_COMB
|
||||
: oldsentreep ? ActiveDlyVisitor::CT_SEQ
|
||||
: ActiveDlyVisitor::CT_SUSPENDABLE};
|
||||
}
|
||||
if (!m_clockedProcess) ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB};
|
||||
|
||||
// Delete sensitivity list
|
||||
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(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);
|
||||
moveUnderSpecial<AstSenItem::Initial>(nodep);
|
||||
}
|
||||
void visit(AstFinal* nodep) override {
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_FINAL};
|
||||
moveUnderSpecial<AstSenItem::Final>(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;
|
||||
|
||||
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
|
||||
// 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
|
||||
AstNode* m_passsp = nullptr; // Current pass statement
|
||||
AstNode* m_failsp = nullptr; // Current fail statement
|
||||
AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion
|
||||
AstFinal* m_finalp = nullptr; // Current final block
|
||||
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
|
||||
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
|
||||
m_modExpr2Sen2DelayedAlwaysp;
|
||||
|
||||
// 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,
|
||||
VAssertDirectiveType directiveType) {
|
||||
// cppcheck-suppress missingReturn
|
||||
|
|
@ -178,9 +262,7 @@ class AssertVisitor final : public VNVisitor {
|
|||
case VAssertDirectiveType::ASSUME: {
|
||||
if (v3Global.opt.assertOn()) {
|
||||
return new AstCExpr{fl, AstCExpr::Pure{},
|
||||
"vlSymsp->_vm_contextp__->assertOnGet("s + std::to_string(type)
|
||||
+ ", "s + std::to_string(directiveType) + ")"s,
|
||||
1};
|
||||
assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
|
||||
}
|
||||
return new AstConst{fl, AstConst::BitFalse{}};
|
||||
}
|
||||
|
|
@ -196,6 +278,31 @@ class AssertVisitor final : public VNVisitor {
|
|||
}
|
||||
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,
|
||||
VDisplayType severity) {
|
||||
if (severity == VDisplayType::DT_ERROR || severity == VDisplayType::DT_FATAL) {
|
||||
|
|
@ -207,36 +314,6 @@ class AssertVisitor final : public VNVisitor {
|
|||
+ cvtToStr(nodep->fileline()->lineno()) + ": %m" + ((message != "") ? ": " : "")
|
||||
+ 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) {
|
||||
nodep->fmtp()->text(
|
||||
assertDisplayMessage(nodep, prefix, nodep->fmtp()->text(), nodep->displayType()));
|
||||
|
|
@ -278,8 +355,8 @@ class AssertVisitor final : public VNVisitor {
|
|||
}
|
||||
static AstIf* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
|
||||
VAssertType type = VAssertType::INTERNAL) {
|
||||
// Add a internal if to check assertions are on.
|
||||
// Don't make this a AND term, as it's unlikely to need to test this.
|
||||
// Add an internal if to check assertions are on.
|
||||
// Don't make this an AND term, as it's unlikely to need to test this.
|
||||
FileLine* const fl = bodyp->fileline();
|
||||
|
||||
AstNodeExpr* const condp = assertOnCond(fl, type, directiveType);
|
||||
|
|
@ -288,6 +365,28 @@ class AssertVisitor final : public VNVisitor {
|
|||
newp->user2(true); // Mark as an assertOn() check
|
||||
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,
|
||||
AstNode* passsp, AstNode* failsp) {
|
||||
|
|
@ -444,6 +543,7 @@ class AssertVisitor final : public VNVisitor {
|
|||
if (failsp) failsp->unlinkFrBackWithNext();
|
||||
|
||||
bool selfDestruct = false;
|
||||
bool passspGated = false;
|
||||
if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
|
||||
++m_statCover;
|
||||
if (!v3Global.opt.coverageUser()) {
|
||||
|
|
@ -455,6 +555,9 @@ class AssertVisitor final : public VNVisitor {
|
|||
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
||||
if (message != "") covincp->declp()->comment(message);
|
||||
if (passsp) {
|
||||
passsp = newIfAssertPassOn(passsp, nodep->directive(), nodep->userType(),
|
||||
/*vacuous=*/false);
|
||||
passspGated = true;
|
||||
passsp = AstNode::addNext<AstNode, AstNode>(covincp, passsp);
|
||||
} else {
|
||||
passsp = covincp;
|
||||
|
|
@ -475,8 +578,10 @@ class AssertVisitor final : public VNVisitor {
|
|||
|
||||
VL_RESTORER(m_passsp);
|
||||
VL_RESTORER(m_failsp);
|
||||
VL_RESTORER(m_assertp);
|
||||
m_passsp = passsp;
|
||||
m_failsp = failsp;
|
||||
m_assertp = nodep;
|
||||
iterate(nodep->propp());
|
||||
|
||||
AstNode* propExprp;
|
||||
|
|
@ -488,6 +593,15 @@ class AssertVisitor final : public VNVisitor {
|
|||
propExprp = nodep->propp()->unlinkFrBack();
|
||||
}
|
||||
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);
|
||||
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
|
||||
// Add assertOn check last, for better combining
|
||||
|
|
@ -586,6 +700,34 @@ class AssertVisitor final : public VNVisitor {
|
|||
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
|
||||
// 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.
|
||||
|
|
@ -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
|
||||
|
|
@ -807,8 +935,11 @@ class AssertVisitor final : public VNVisitor {
|
|||
if (nodep->pass() && m_passsp) {
|
||||
// Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
|
||||
stmtsp = m_passsp->cloneTree(true);
|
||||
stmtsp = newIfAssertPassOn(stmtsp, m_assertp->directive(), m_assertp->userType(),
|
||||
nodep->vacuous());
|
||||
} else if (!nodep->pass() && m_failsp) {
|
||||
stmtsp = m_failsp->cloneTree(true);
|
||||
stmtsp = newIfAssertFailOn(stmtsp, m_assertp->directive(), m_assertp->userType());
|
||||
}
|
||||
if (stmtsp) {
|
||||
stmtsp->foreachAndNext([](AstNodeVarRef* const refp) {
|
||||
|
|
@ -914,34 +1045,51 @@ class AssertVisitor final : public VNVisitor {
|
|||
visitAssertionIterate(nodep, nodep->failsp());
|
||||
}
|
||||
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);
|
||||
|
||||
if (!resolveAssertType(nodep)) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: non-constant assert assertion-type expression");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
bool assertTypeConst = true;
|
||||
if (!nodep->assertTypesp()) {
|
||||
nodep->ctlAssertTypes(VAssertType{ALL_ASSERT_TYPES});
|
||||
} 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;
|
||||
}
|
||||
if (nodep->ctlAssertTypes() != ALL_ASSERT_TYPES
|
||||
&& nodep->ctlAssertTypes().containsAny(VAssertType::EXPECT | VAssertType::UNIQUE
|
||||
| VAssertType::UNIQUE0
|
||||
if (assertTypeConst && nodep->ctlAssertTypes() != ALL_ASSERT_TYPES
|
||||
&& nodep->ctlAssertTypes().containsAny(VAssertType::UNIQUE | VAssertType::UNIQUE0
|
||||
| VAssertType::PRIORITY)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assert control assertion_type");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
if (!resolveControlType(nodep)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: non-const assert control type expression");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
|
||||
bool directiveTypeConst = true;
|
||||
if (!nodep->directiveTypesp()) {
|
||||
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,
|
||||
"Unsupported: non-const assert directive type expression");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
|
|
@ -949,43 +1097,18 @@ class AssertVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
FileLine* const fl = nodep->fileline();
|
||||
switch (nodep->ctlType()) {
|
||||
case VAssertCtlType::ON:
|
||||
UINFO(9, "Generating assertctl for a module: " << m_modp);
|
||||
nodep->replaceWith(
|
||||
new AstCStmt{fl, "vlSymsp->_vm_contextp__->assertOnSet("s
|
||||
+ std::to_string(nodep->ctlAssertTypes()) + ", "s
|
||||
+ std::to_string(nodep->ctlDirectiveTypes()) + ");\n"s});
|
||||
break;
|
||||
case VAssertCtlType::OFF:
|
||||
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)");
|
||||
}
|
||||
UINFO(9, "Generating assertctl for a module: " << m_modp);
|
||||
AstCStmt* const newp = new AstCStmt{fl};
|
||||
newp->add("vlSymsp->_vm_contextp__->assertCtl(");
|
||||
newp->add(nodep->controlTypep()->unlinkFrBack());
|
||||
newp->add(", ");
|
||||
if (nodep->assertTypesp()) {
|
||||
newp->add(nodep->assertTypesp()->unlinkFrBack());
|
||||
} else {
|
||||
newp->add(std::to_string(ALL_ASSERT_TYPES));
|
||||
}
|
||||
newp->add(", " + std::to_string(nodep->ctlDirectiveTypes()) + ");\n");
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstAssertIntrinsic* nodep) override { //
|
||||
|
|
@ -1007,10 +1130,12 @@ class AssertVisitor final : public VNVisitor {
|
|||
VL_RESTORER(m_modPastNum);
|
||||
VL_RESTORER(m_modStrobeNum);
|
||||
VL_RESTORER(m_modExpr2Sen2DelayedAlwaysp);
|
||||
VL_RESTORER(m_finalp);
|
||||
m_modp = nodep;
|
||||
m_modPastNum = 0;
|
||||
m_modStrobeNum = 0;
|
||||
m_modExpr2Sen2DelayedAlwaysp.clear();
|
||||
m_finalp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
|
||||
//============================================================================
|
||||
|
||||
class V3AssertCommon final {
|
||||
public:
|
||||
static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
class V3Assert final {
|
||||
public:
|
||||
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 "V3Assert.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Task.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
|
@ -60,6 +61,7 @@ private:
|
|||
AstNodeExpr* m_disablep = nullptr; // Last disable
|
||||
AstIf* m_disableSeqIfp = nullptr; // Used for handling disable iff in sequences
|
||||
AstPExpr* m_pexprp = nullptr; // Last AstPExpr
|
||||
bool m_underCover = false; // True if the enclosing assertion is a cover
|
||||
// Other:
|
||||
V3UniqueNames m_cycleDlyNames{"__VcycleDly"}; // Cycle delay counter name generator
|
||||
V3UniqueNames m_consRepNames{"__VconsRep"}; // Consecutive repetition counter name generator
|
||||
|
|
@ -91,7 +93,10 @@ private:
|
|||
fromAlways = true;
|
||||
}
|
||||
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};
|
||||
} else {
|
||||
if (cassertp && fromAlways) cassertp->senFromAlways(true);
|
||||
|
|
@ -1281,7 +1286,10 @@ private:
|
|||
// Don't iterate pexprp here -- it was already iterated when created
|
||||
// (in visit(AstSExpr*)), so delays and disable iff are already processed.
|
||||
} 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 {
|
||||
if (m_disablep) {
|
||||
lhsp = new AstAnd{flp, new AstNot{flp, m_disablep->cloneTreePure(false)}, lhsp};
|
||||
|
|
@ -1290,7 +1298,9 @@ private:
|
|||
AstPast* const pastp = new AstPast{flp, lhsp};
|
||||
pastp->dtypeFrom(lhsp);
|
||||
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();
|
||||
nodep->replaceWith(exprp);
|
||||
}
|
||||
|
|
@ -1438,12 +1448,6 @@ private:
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
void visit(AstInferredDisable* nodep) override {
|
||||
|
|
@ -1474,6 +1478,10 @@ private:
|
|||
nodep->propp(new AstSampled{nodep->fileline(), nodep->propp()->unlinkFrBack(),
|
||||
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());
|
||||
}
|
||||
void visit(AstPExpr* nodep) override {
|
||||
|
|
@ -1565,10 +1573,15 @@ private:
|
|||
VL_RESTORER(m_modp);
|
||||
m_defaultClockingp = nullptr;
|
||||
m_defaultClkEvtVarp = nullptr;
|
||||
m_defaultDisablep = nullptr;
|
||||
m_defaultDisablep = nodep->defaultDisablep();
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstGenBlock* nodep) override {
|
||||
VL_RESTORER(m_defaultDisablep);
|
||||
m_defaultDisablep = nodep->defaultDisablep();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstProperty* nodep) override {
|
||||
// The body will be visited when will be substituted in place of property reference
|
||||
// (AstFuncRef)
|
||||
|
|
|
|||
150
src/V3Ast.cpp
150
src/V3Ast.cpp
|
|
@ -1745,6 +1745,156 @@ AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) {
|
|||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -342,7 +342,6 @@ public:
|
|||
VAR_PUBLIC_FLAT, // V3LinkParse moves to AstVar::sigPublic
|
||||
VAR_PUBLIC_FLAT_RD, // 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_BV, // V3LinkParse moves to AstVar::attrScBv
|
||||
VAR_SFORMAT, // V3LinkParse moves to AstVar::attrSFormat
|
||||
|
|
@ -364,7 +363,7 @@ public:
|
|||
"TYPEID", "TYPENAME",
|
||||
"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_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"
|
||||
};
|
||||
// clang-format on
|
||||
|
|
@ -1191,6 +1190,19 @@ public:
|
|||
= {"user", "array", "auto", "ignore", "illegal", "default", "wildcard", "transition"};
|
||||
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) {
|
||||
return lhs.m_e == rhs;
|
||||
|
|
@ -1416,6 +1428,7 @@ public:
|
|||
ET_EVENT, // VlEventBase::isFired
|
||||
// Involving an expression
|
||||
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_STAR, // Sensitive to all combo inputs to this block (from .*)
|
||||
|
|
@ -1434,6 +1447,7 @@ public:
|
|||
true, // ET_NEGEDGE
|
||||
true, // ET_EVENT
|
||||
true, // ET_TRUE
|
||||
true, // ET_INITIAL_NBA
|
||||
|
||||
false, // ET_COMBO
|
||||
false, // ET_COMBO_STAR
|
||||
|
|
@ -1457,14 +1471,14 @@ public:
|
|||
}
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
= {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE", "COMBO",
|
||||
"COMBO_STAR", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
|
||||
= {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE", "ET_INITIAL_NBA",
|
||||
"COMBO", "COMBO_STAR", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* verilogKwd() const {
|
||||
static const char* const names[]
|
||||
= {"[changed]", "edge", "posedge", "negedge", "[event]", "[true]", "*",
|
||||
"*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
|
||||
static const char* const names[] = {
|
||||
"[changed]", "edge", "posedge", "negedge", "[event]", "[true]", "[initial_nba]",
|
||||
"*", "*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
|
||||
return names[m_e];
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
|
||||
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)
|
||||
: ASTGEN_SUPER_VarRef(fl, varp, access) {
|
||||
if (v3Global.assertDTypesResolved()) {
|
||||
|
|
|
|||
|
|
@ -170,6 +170,11 @@ public:
|
|||
void generic(bool flag) { m_generic = flag; }
|
||||
std::pair<uint32_t, uint32_t> dimensions(bool includeBasic) const;
|
||||
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; }
|
||||
const char* charIQWN() const {
|
||||
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
|
||||
// broken()
|
||||
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 {
|
||||
// Binary expression
|
||||
|
|
@ -1855,6 +1858,22 @@ public:
|
|||
bool index() const { return m_index; }
|
||||
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 {
|
||||
// "matches" operator: "expr matches pattern"
|
||||
// @astgen op1 := lhsp : AstNodeExpr // Expression to match
|
||||
|
|
@ -2225,6 +2244,23 @@ public:
|
|||
bool sameNode(const AstNode* /*samep*/) 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 {
|
||||
// Consecutive repetition [*N], [*N:M], [+], [*] (IEEE 1800-2023 16.9.2)
|
||||
// op1 := exprp -- the repeated expression
|
||||
|
|
@ -5701,6 +5737,22 @@ public:
|
|||
void dump(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 {
|
||||
// String to any-size integral
|
||||
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)
|
||||
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
|
||||
bool m_taskPublic : 1; // Public task
|
||||
bool m_attrIsolateAssign : 1; // User isolate_assignments attribute
|
||||
bool m_classMethod : 1; // Class method
|
||||
bool m_didProto : 1; // Did prototype processing
|
||||
bool m_prototype : 1; // Just a prototype
|
||||
|
|
@ -130,7 +129,6 @@ protected:
|
|||
: AstNode{t, fl}
|
||||
, m_name{name}
|
||||
, m_taskPublic{false}
|
||||
, m_attrIsolateAssign{false}
|
||||
, m_classMethod{false}
|
||||
, m_didProto{false}
|
||||
, m_prototype{false}
|
||||
|
|
@ -179,8 +177,6 @@ public:
|
|||
uint64_t dpiOpenParent() const { return m_dpiOpenParent; }
|
||||
bool taskPublic() const { return m_taskPublic; }
|
||||
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; }
|
||||
void classMethod(bool flag) { m_classMethod = flag; }
|
||||
bool didProto() const { return m_didProto; }
|
||||
|
|
@ -301,6 +297,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
|
|||
VLifetime m_lifetime; // Lifetime
|
||||
VTimescale m_timeunit; // Global time unit
|
||||
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_modTrace : 1; // Tracing this module
|
||||
|
|
@ -351,6 +348,8 @@ public:
|
|||
string origName() const override { return m_origName; }
|
||||
string someInstanceName() const VL_MT_SAFE { return m_someInstanceName; }
|
||||
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; }
|
||||
void inLibrary(bool flag) { m_inLibrary = flag; }
|
||||
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
|
||||
// false, the returned VarScope will have _->dtypep()->sameTree(initp->dtypep()) return true.
|
||||
AstVarScope* findConst(AstConst* initp, bool mergeDType);
|
||||
// Rebuild hashes after potential removals
|
||||
void reCache();
|
||||
};
|
||||
class AstConstraint final : public AstNode {
|
||||
// Constraint
|
||||
|
|
@ -1184,12 +1185,21 @@ public:
|
|||
};
|
||||
class AstCoverpointRef final : public AstNode {
|
||||
// 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:
|
||||
AstCoverpointRef(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_CoverpointRef(fl)
|
||||
, m_name{name} {}
|
||||
AstCoverpointRef(FileLine* fl, AstNodeExpr* exprp)
|
||||
: ASTGEN_SUPER_CoverpointRef(fl) {
|
||||
this->exprp(exprp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverpointRef;
|
||||
void dump(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 Static {}; // 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 Never {}; // for constructor type-overload selection
|
||||
AstSenItem(FileLine* fl, VEdgeType edgeType, AstNodeExpr* senp, AstNodeExpr* condp = nullptr)
|
||||
|
|
@ -1769,6 +1780,9 @@ public:
|
|||
AstSenItem(FileLine* fl, Initial)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, m_edgeType{VEdgeType::ET_INITIAL} {}
|
||||
AstSenItem(FileLine* fl, InitialNBA)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, m_edgeType{VEdgeType::ET_INITIAL_NBA} {}
|
||||
AstSenItem(FileLine* fl, Final)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, 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_attrScBv : 1; // User force bit vector 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_attrSplitVar : 1; // declared with split_var metacomment
|
||||
bool m_attrFsmState : 1; // declared with fsm_state metacomment
|
||||
bool m_attrFsmRegisterWrapper : 1; // connected to an fsm_register_wrapper instance
|
||||
bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc 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_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_isContinuously : 1; // Ever assigned continuously (for force/release)
|
||||
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_attrScBv = false;
|
||||
m_attrScBigUint = false;
|
||||
m_attrIsolateAssign = false;
|
||||
m_attrSFormat = false;
|
||||
m_attrSplitVar = false;
|
||||
m_attrFsmState = false;
|
||||
m_attrFsmRegisterWrapper = false;
|
||||
m_attrFsmResetArc = false;
|
||||
m_attrFsmArcInclCond = false;
|
||||
m_constPoolEntry = false;
|
||||
m_fileDescr = false;
|
||||
m_gotNansiType = false;
|
||||
m_icoMaybeWritten = false;
|
||||
m_isConst = false;
|
||||
m_isContinuously = false;
|
||||
m_hasStrengthAssignment = false;
|
||||
|
|
@ -2343,13 +2359,14 @@ public:
|
|||
void attrFileDescr(bool flag) { m_fileDescr = flag; }
|
||||
void attrScBv(bool flag) { m_attrScBv = flag; }
|
||||
void attrScBigUint(bool flag) { m_attrScBigUint = flag; }
|
||||
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
|
||||
void attrSFormat(bool flag) { m_attrSFormat = flag; }
|
||||
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
|
||||
void attrFsmState(bool flag) { m_attrFsmState = flag; }
|
||||
void attrFsmRegisterWrapper(bool flag) { m_attrFsmRegisterWrapper = flag; }
|
||||
void attrFsmResetArc(bool flag) { m_attrFsmResetArc = 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 usedParam(bool flag) { m_usedParam = flag; }
|
||||
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
|
||||
|
|
@ -2383,6 +2400,8 @@ public:
|
|||
void hasStrengthAssignment(bool flag) { m_hasStrengthAssignment = flag; }
|
||||
bool hasUserInit() const { return m_hasUserInit; }
|
||||
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; }
|
||||
void isDpiOpenArray(bool flag) { m_isDpiOpenArray = flag; }
|
||||
bool isHideLocal() const { return m_isHideLocal; }
|
||||
|
|
@ -2462,12 +2481,6 @@ public:
|
|||
bool isWor() const { return varType().isWor(); }
|
||||
bool isWiredNet() const { return varType().isWiredNet(); }
|
||||
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 isVirtIface() const {
|
||||
if (AstIfaceRefDType* const dtp = VN_CAST(dtypep(), IfaceRefDType)) {
|
||||
|
|
@ -2519,7 +2532,6 @@ public:
|
|||
bool attrFsmRegisterWrapper() const { return m_attrFsmRegisterWrapper; }
|
||||
bool attrFsmResetArc() const { return m_attrFsmResetArc; }
|
||||
bool attrFsmArcInclCond() const { return m_attrFsmArcInclCond; }
|
||||
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
|
||||
AstIface* sensIfacep() const { return m_sensIfacep; }
|
||||
VRandAttr rand() const { return m_rand; }
|
||||
string verilogKwd() const override;
|
||||
|
|
@ -2531,7 +2543,6 @@ public:
|
|||
// This is getting connected to fromp; keep attributes
|
||||
// Note the method below too
|
||||
if (fromp->attrFileDescr()) attrFileDescr(true);
|
||||
if (fromp->attrIsolateAssign()) attrIsolateAssign(true);
|
||||
if (fromp->isContinuously()) isContinuously(true);
|
||||
}
|
||||
void propagateWrapAttrFrom(const AstVar* fromp) {
|
||||
|
|
@ -2810,6 +2821,7 @@ class AstGenBlock final : public AstNodeGen {
|
|||
std::string m_name; // Name of block
|
||||
const bool m_unnamed; // Originally unnamed (name change does not affect this)
|
||||
const bool m_implied; // Not inserted by user
|
||||
AstDefaultDisable* m_defaultDisablep = nullptr; // Default disable iff in this scope
|
||||
|
||||
public:
|
||||
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; }
|
||||
bool unnamed() const { return m_unnamed; }
|
||||
bool implied() const { return m_implied; }
|
||||
AstDefaultDisable* defaultDisablep() const { return m_defaultDisablep; }
|
||||
void defaultDisablep(AstDefaultDisable* nodep) { m_defaultDisablep = nodep; }
|
||||
};
|
||||
class AstGenCase final : public AstNodeGen {
|
||||
// Generate 'case'
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ protected:
|
|||
public:
|
||||
ASTGEN_MEMBERS_AstNodeAssign;
|
||||
// Clone single node, just get same type back.
|
||||
void dump(std::ostream& str) const override;
|
||||
virtual AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) = 0;
|
||||
bool hasDType() const override VL_MT_SAFE { return true; }
|
||||
virtual bool cleanRhs() const { return true; }
|
||||
|
|
@ -941,13 +942,16 @@ public:
|
|||
class AstPExprClause final : public AstNodeStmt {
|
||||
const bool m_pass; // True if will be replaced by passing assertion clause, false for
|
||||
// assertion failure clause
|
||||
const bool m_vacuous; // True if pass is vacuous
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstPExprClause;
|
||||
explicit AstPExprClause(FileLine* fl, bool pass = true)
|
||||
explicit AstPExprClause(FileLine* fl, bool pass = true, bool vacuous = false)
|
||||
: ASTGEN_SUPER_PExprClause(fl)
|
||||
, m_pass{pass} {}
|
||||
, m_pass{pass}
|
||||
, m_vacuous{vacuous} {}
|
||||
bool pass() const { return m_pass; }
|
||||
bool vacuous() const { return m_vacuous; }
|
||||
};
|
||||
class AstPrintTimeScale final : public AstNodeStmt {
|
||||
// Parents: stmtlist
|
||||
|
|
@ -1606,12 +1610,18 @@ public:
|
|||
};
|
||||
class AstCover final : public AstNodeCoverOrAssert {
|
||||
// @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:
|
||||
ASTGEN_MEMBERS_AstCover;
|
||||
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type,
|
||||
const string& name = "")
|
||||
: ASTGEN_SUPER_Cover(fl, propp, stmtsp, type, VAssertDirectiveType::COVER, name) {}
|
||||
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 {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -546,6 +546,26 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
|
|||
|
||||
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()
|
||||
: ASTGEN_SUPER_Netlist(new FileLine{FileLine::builtInFilename()})
|
||||
, m_typeTablep{new AstTypeTable{fileline()}}
|
||||
|
|
@ -630,6 +650,7 @@ void AstVar::combineType(const AstVar* otherp) {
|
|||
varType(otherp->varType());
|
||||
direction(otherp->direction());
|
||||
}
|
||||
if (otherp->icoMaybeWritten()) icoMaybeWritten(true);
|
||||
}
|
||||
void AstVar::combineType(VVarType type) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
string AstVar::vlEnumType() const {
|
||||
string AstNodeDType::vlEnumType() const {
|
||||
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;
|
||||
if (bdtypep && bdtypep->keyword() == VBasicDTypeKwd::CHARPTR) {
|
||||
return "VLVT_PTR";
|
||||
|
|
@ -694,6 +720,8 @@ string AstVar::vlEnumType() const {
|
|||
arg += "VLVT_STRING";
|
||||
} else if (isDouble()) {
|
||||
arg += "VLVT_REAL";
|
||||
} else if (sdtypep && !sdtypep->packed()) {
|
||||
arg += VN_IS(sdtypep, StructDType) ? "VLVT_STRUCT" : "VLVT_UNION";
|
||||
} else if (widthMin() <= 8) {
|
||||
arg += "VLVT_UINT8";
|
||||
} else if (widthMin() <= 16) {
|
||||
|
|
@ -709,6 +737,8 @@ string AstVar::vlEnumType() const {
|
|||
return arg;
|
||||
}
|
||||
|
||||
string AstVar::vlEnumType() const { return dtypep()->vlEnumType(); }
|
||||
|
||||
string AstVar::vlEnumDir() const {
|
||||
string out;
|
||||
if (isInout()) {
|
||||
|
|
@ -738,6 +768,7 @@ string AstVar::vlEnumDir() const {
|
|||
if (AstBasicDType* const basicp = dtypep()->skipRefp()->basicp()) {
|
||||
if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR";
|
||||
}
|
||||
if (isNet()) out += "|VLVF_NET";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -1259,6 +1290,47 @@ uint32_t AstNodeDType::arrayUnpackedElements() const {
|
|||
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 {
|
||||
// How many array dimensions (packed,unpacked) does this Var have?
|
||||
uint32_t packed = 0;
|
||||
|
|
@ -1322,6 +1394,12 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
|
|||
} else if (VN_IS(nodep, Sel)) {
|
||||
nodep = VN_AS(nodep, Sel)->fromp();
|
||||
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)) {
|
||||
nodep = VN_AS(nodep, MemberSel)->fromp();
|
||||
continue;
|
||||
|
|
@ -1581,6 +1659,7 @@ AstConstPool::AstConstPool(FileLine* fl)
|
|||
AstVarScope* AstConstPool::createNewEntry(const string& name, AstNodeExpr* initp) {
|
||||
FileLine* const fl = initp->fileline();
|
||||
AstVar* const varp = new AstVar{fl, VVarType::MODULETEMP, name, initp->dtypep()};
|
||||
varp->setConstPoolEntry();
|
||||
varp->isConst(true);
|
||||
varp->isStatic(true);
|
||||
varp->valuep(initp->cloneTree(false));
|
||||
|
|
@ -1700,6 +1779,17 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
|
|||
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
|
||||
|
||||
|
|
@ -2133,6 +2223,14 @@ void AstNodeCoverOrAssert::dumpJson(std::ostream& str) const {
|
|||
dumpJsonBoolFuncIf(str, immediate);
|
||||
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 {
|
||||
this->AstNode::dump(str);
|
||||
if (isDefault()) str << " [DEFAULT]";
|
||||
|
|
@ -2724,6 +2822,10 @@ void AstNodeArrayDType::dumpJson(std::ostream& str) const {
|
|||
dumpJsonStr(str, "declRange", cvtToStr(declRange()));
|
||||
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 {
|
||||
std::ostringstream os;
|
||||
if (const auto subp = subDTypep()) os << subp->prettyDTypeName(full);
|
||||
|
|
@ -3138,6 +3240,7 @@ int AstVarRef::instrCount() const {
|
|||
}
|
||||
void AstVar::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (constPoolEntry()) str << " [CONSTPOOL]";
|
||||
if (isSc()) str << " [SC]";
|
||||
if (isPrimaryIO()) str << (isInout() ? " [PIO]" : (isWritable() ? " [PO]" : " [PI]"));
|
||||
if (isPrimaryClock()) str << " [PCLK]";
|
||||
|
|
@ -3158,7 +3261,6 @@ void AstVar::dump(std::ostream& str) const {
|
|||
if (noReset()) str << " [!RST]";
|
||||
if (processQueue()) str << " [PROCQ]";
|
||||
if (sampled()) str << " [SAMPLED]";
|
||||
if (attrIsolateAssign()) str << " [aISO]";
|
||||
if (attrFsmState()) str << " [aFSMSTATE]";
|
||||
if (attrFsmResetArc()) str << " [aFSMRESETARC]";
|
||||
if (attrFsmArcInclCond()) str << " [aFSMARCCOND]";
|
||||
|
|
@ -3169,6 +3271,7 @@ void AstVar::dump(std::ostream& str) const {
|
|||
str << " [FUNC]";
|
||||
}
|
||||
if (hasUserInit()) str << " [UINIT]";
|
||||
if (icoMaybeWritten()) str << " [ICOMAYBEWRITTEN]";
|
||||
if (isDpiOpenArray()) str << " [DPIOPENA]";
|
||||
if (ignorePostWrite()) str << " [IGNPWR]";
|
||||
if (ignoreSchedWrite()) str << " [IGNWR]";
|
||||
|
|
@ -3179,6 +3282,7 @@ void AstVar::dump(std::ostream& str) const {
|
|||
void AstVar::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, origName);
|
||||
dumpJsonStrFunc(str, verilogName);
|
||||
dumpJsonBoolFuncIf(str, constPoolEntry);
|
||||
dumpJsonBoolFuncIf(str, isSc);
|
||||
dumpJsonBoolFuncIf(str, isPrimaryIO);
|
||||
dumpJsonBoolFuncIf(str, isPrimaryClock);
|
||||
|
|
@ -3193,11 +3297,11 @@ void AstVar::dumpJson(std::ostream& str) const {
|
|||
dumpJsonBoolFuncIf(str, noReset);
|
||||
dumpJsonBoolFuncIf(str, processQueue);
|
||||
dumpJsonBoolFuncIf(str, sampled);
|
||||
dumpJsonBoolFuncIf(str, attrIsolateAssign);
|
||||
dumpJsonBoolFuncIf(str, attrFsmState);
|
||||
dumpJsonBoolFuncIf(str, attrFsmResetArc);
|
||||
dumpJsonBoolFuncIf(str, attrFsmArcInclCond);
|
||||
dumpJsonBoolFuncIf(str, attrFileDescr);
|
||||
dumpJsonBoolFuncIf(str, icoMaybeWritten);
|
||||
dumpJsonBoolFuncIf(str, isDpiOpenArray);
|
||||
dumpJsonBoolFuncIf(str, isFuncReturn);
|
||||
dumpJsonBoolFuncIf(str, isFuncLocal);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// -*- 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
|
||||
//
|
||||
|
|
@ -14,8 +14,8 @@
|
|||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3SPLITAS_H_
|
||||
#define VERILATOR_V3SPLITAS_H_
|
||||
#ifndef VERILATOR_V3ASTPATTERNS_H_
|
||||
#define VERILATOR_V3ASTPATTERNS_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
|
@ -24,9 +24,11 @@ class AstNetlist;
|
|||
|
||||
//============================================================================
|
||||
|
||||
class V3SplitAs final {
|
||||
class V3AstPatterns final {
|
||||
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
|
||||
|
|
@ -61,7 +61,6 @@ class BeginVisitor final : public VNVisitor {
|
|||
// NODE STATE
|
||||
// AstCase::user1 -> bool, if already purified
|
||||
|
||||
V3UniqueNames m_caseTempNames; // For generating unique temporary variable names used by cases
|
||||
// STATE - across all visitors
|
||||
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
|
||||
int m_ifDepth = 0; // Current if depth
|
||||
bool m_keepBegins = false; // True if begins should not be inlined
|
||||
VDouble0 m_statPurifiedCaseExpr; // Count of purified case expressions
|
||||
|
||||
// METHODS
|
||||
|
||||
|
|
@ -132,7 +130,6 @@ class BeginVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_caseTempNames);
|
||||
m_modp = nodep;
|
||||
// Rename it (e.g. class under a generate)
|
||||
if (m_unnamedScope != "") {
|
||||
|
|
@ -171,7 +168,6 @@ class BeginVisitor final : public VNVisitor {
|
|||
VL_RESTORER(m_liftedp);
|
||||
VL_RESTORER(m_namedScope);
|
||||
VL_RESTORER(m_unnamedScope);
|
||||
VL_RESTORER(m_caseTempNames);
|
||||
m_displayScope = dot(m_displayScope, nodep->name());
|
||||
m_namedScope = "";
|
||||
m_unnamedScope = "";
|
||||
|
|
@ -415,29 +411,7 @@ class BeginVisitor final : public VNVisitor {
|
|||
// any BEGINs, but V3Coverage adds them all under the module itself.
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCase* nodep) override {
|
||||
// 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);
|
||||
}
|
||||
void visit(AstCase* nodep) override { iterateChildren(nodep); }
|
||||
// VISITORS - LINT CHECK
|
||||
void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
||||
VL_RESTORER(m_keepBegins);
|
||||
|
|
@ -464,10 +438,8 @@ class BeginVisitor final : public VNVisitor {
|
|||
public:
|
||||
// CONSTRUCTORS
|
||||
BeginVisitor(AstNetlist* nodep, BeginState* statep)
|
||||
: m_caseTempNames{"__VCase"}
|
||||
, m_statep{statep} {
|
||||
: m_statep{statep} {
|
||||
iterate(nodep);
|
||||
V3Stats::addStatSum("Impure case expressions", m_statPurifiedCaseExpr);
|
||||
}
|
||||
~BeginVisitor() override = default;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ class CUseVisitor final : public VNVisitorConst {
|
|||
iterateConst(nodep->lhsp()->dtypep());
|
||||
}
|
||||
void visit(AstNodeDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->virtRefDTypep()) iterateConst(nodep->virtRefDTypep());
|
||||
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());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstMatchMasked* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureClean(nodep->lhsp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstSel* nodep) override {
|
||||
operandBiop(nodep);
|
||||
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
|
||||
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();
|
||||
UASSERT_OBJ(maskVal != 0, m_refp,
|
||||
"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};
|
||||
}
|
||||
|
||||
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 operandAsvConst(const AstNode* nodep) {
|
||||
// BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...)
|
||||
|
|
@ -1327,6 +1417,45 @@ class ConstVisitor final : public VNVisitor {
|
|||
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) {
|
||||
if (nodep->widthMin() != 1) 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());
|
||||
}
|
||||
bool operandSelExtend(AstSel* nodep) {
|
||||
// A pattern created by []'s after offsets have been removed
|
||||
// SEL(EXTEND(any,width,...),(width-1),0) -> ...
|
||||
// Since select's return unsigned, this is always an extend
|
||||
// cppcheck-suppress constVariablePointer // children unlinked below
|
||||
if (!m_doV) return false;
|
||||
AstExtend* const extendp = VN_CAST(nodep->fromp(), Extend);
|
||||
if (!(m_doV && extendp && VN_IS(nodep->lsbp(), Const) && nodep->lsbConst() == 0
|
||||
&& static_cast<int>(nodep->widthConst()) == extendp->lhsp()->width()))
|
||||
return false;
|
||||
VL_DO_DANGLING(replaceWChild(nodep, extendp->lhsp()), nodep);
|
||||
return true;
|
||||
if (!extendp) return false;
|
||||
AstConst* const lsbp = VN_CAST(nodep->lsbp(), Const);
|
||||
if (!lsbp) return false;
|
||||
const int width = nodep->widthConst();
|
||||
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) {
|
||||
// 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);
|
||||
// Further reduce, either node may have more reductions.
|
||||
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)
|
||||
&& !VN_IS(nodep->lhsp()->dtypep()->skipRefp(), QueueDType)) {
|
||||
// The right-streaming operator on rhs of assignment does not
|
||||
|
|
@ -2402,7 +2580,9 @@ class ConstVisitor final : public VNVisitor {
|
|||
AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack();
|
||||
AstNodeDType* const srcDTypep = srcp->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)) {
|
||||
int srcElementBits = 0;
|
||||
if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
|
||||
|
|
@ -2429,6 +2609,19 @@ class ConstVisitor final : public VNVisitor {
|
|||
srcp = new AstShiftL{srcp->fileline(), srcp,
|
||||
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);
|
||||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||
// 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* const dstp = VN_AS(streamp, StreamL)->lhsp()->unlinkFrBack();
|
||||
AstNodeDType* const dstDTypep = dstp->dtypep()->skipRefp();
|
||||
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
|
||||
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
||||
// Handle unpacked/queue/dynarray source -> queue/dynarray dest via
|
||||
// CvtArrayToArray (StreamL reverses, so reverse=true)
|
||||
|
|
@ -2466,6 +2659,7 @@ class ConstVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||
return true;
|
||||
}
|
||||
if (lowerAsFixedAggregate(srcDTypep)) srcp = packFixedAggregate(srcp);
|
||||
const int sWidth = srcp->width();
|
||||
const int dWidth = dstp->width();
|
||||
// Connect the rhs to the stream operator and update its width
|
||||
|
|
@ -2556,7 +2750,7 @@ class ConstVisitor final : public VNVisitor {
|
|||
} else {
|
||||
// Source narrower than destination: left-justify by shifting left.
|
||||
// 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)) {
|
||||
AstExtend* const extendp = new AstExtend{srcp->fileline(), srcp};
|
||||
extendp->dtypeSetLogicSized(dWidth, VSigning::UNSIGNED);
|
||||
|
|
@ -2580,6 +2774,13 @@ class ConstVisitor final : public VNVisitor {
|
|||
AstNodeExpr* srcp = streamp->lhsp();
|
||||
const AstNodeDType* const srcDTypep = srcp->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)
|
||||
|| VN_IS(srcDTypep, UnpackArrayDType))) {
|
||||
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(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 replaceLogEq(AstLogEq* nodep) {
|
||||
|
|
@ -3149,7 +3359,8 @@ class ConstVisitor final : public VNVisitor {
|
|||
iterateChildren(nodep);
|
||||
UASSERT_OBJ(nodep->varp(), nodep, "Not linked");
|
||||
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");
|
||||
iterateAndNextNull(nodep->varp()->valuep()); // May change 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
|
||||
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
|
||||
// Masking that always yields zero
|
||||
TREEOP ("AstAnd {$lhsp.castConst, matchMaskedZero(nodep)}", "replaceZeroChkPure(nodep, $rhsp)");
|
||||
// This visit function here must allow for short-circuiting.
|
||||
TREEOPS("AstLogAnd {$lhsp.isZero}", "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_ports; // Ports 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_public = false; // Public function/task
|
||||
|
||||
|
|
@ -190,7 +189,6 @@ public:
|
|||
V3ControlFTask() = default;
|
||||
void update(const V3ControlFTask& f) {
|
||||
// Don't overwrite true with false
|
||||
if (f.m_isolate) m_isolate = true;
|
||||
if (f.m_noinline) m_noinline = true;
|
||||
if (f.m_public) m_public = true;
|
||||
m_params.update(f.m_params);
|
||||
|
|
@ -203,7 +201,6 @@ public:
|
|||
V3ControlVarResolver& ports() { return m_ports; }
|
||||
V3ControlVarResolver& vars() { return m_vars; }
|
||||
|
||||
void setIsolate(bool set) { m_isolate = set; }
|
||||
void setNoInline(bool set) { m_noinline = set; }
|
||||
void setPublic(bool set) { m_public = set; }
|
||||
|
||||
|
|
@ -212,8 +209,6 @@ public:
|
|||
ftaskp->addStmtsp(new AstPragma{ftaskp->fileline(), VPragmaType::NO_INLINE_TASK});
|
||||
if (m_public)
|
||||
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
|
||||
if (pattern.empty()) {
|
||||
if (attr == VAttrType::VAR_ISOLATE_ASSIGNMENTS) {
|
||||
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 (attr == VAttrType::VAR_PUBLIC) {
|
||||
if (ftask.empty()) {
|
||||
// public module, this is the only exception from var here
|
||||
V3ControlResolver::s().modules().at(module).addModulePragma(
|
||||
|
|
|
|||
|
|
@ -166,10 +166,13 @@ class CoverageVisitor final : public VNVisitor {
|
|||
|
||||
// METHODS
|
||||
|
||||
// Return non-nullptr reason if this variable shouldn't have toggle coverage
|
||||
const char* varIgnoreToggle(const AstVar* nodep) {
|
||||
// Return true if this shouldn't be traced
|
||||
// See also similar rule in V3TraceDecl::varIgnoreTrace
|
||||
if (!nodep->isToggleCoverable()) return "Not relevant signal type";
|
||||
const bool cover = nodep->isIO() || (nodep->isSignal() && nodep->isBitLogic());
|
||||
if (!cover) return "Not relevant signal";
|
||||
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()) {
|
||||
const string prettyName = nodep->prettyName();
|
||||
if (prettyName[0] == '_') return "Leading underscore";
|
||||
|
|
@ -372,6 +375,8 @@ class CoverageVisitor final : public VNVisitor {
|
|||
} else {
|
||||
itemp->addElsesp(stmtp);
|
||||
}
|
||||
} else if (AstBegin* const itemp = VN_CAST(nodep, Begin)) {
|
||||
itemp->addStmtsp(stmtp);
|
||||
} else {
|
||||
nodep->v3fatalSrc("Bad node type");
|
||||
}
|
||||
|
|
@ -530,8 +535,10 @@ class CoverageVisitor final : public VNVisitor {
|
|||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
} else if (VN_IS(dtypep, QueueDType)) {
|
||||
} else if (VN_IS(dtypep, QueueDType) || VN_IS(dtypep, AssocArrayDType)
|
||||
|| VN_IS(dtypep, WildcardArrayDType)) {
|
||||
// Not covered
|
||||
varp->v3warn(COVERIGN, "Coverage ignored for type " << dtypep->prettyTypeName());
|
||||
} else {
|
||||
dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "
|
||||
<< dtypep->prettyTypeName());
|
||||
|
|
@ -771,6 +778,8 @@ class CoverageVisitor final : public VNVisitor {
|
|||
// covers the code in that line.)
|
||||
VL_RESTORER(m_beginHier);
|
||||
VL_RESTORER(m_inToggleOff);
|
||||
VL_RESTORER(m_exprStmtsp);
|
||||
m_exprStmtsp = nodep;
|
||||
m_inToggleOff = true;
|
||||
if (nodep->name() != "") {
|
||||
m_beginHier = m_beginHier + (m_beginHier != "" ? "__DOT__" : "") + nodep->name();
|
||||
|
|
@ -815,6 +824,7 @@ class CoverageVisitor final : public VNVisitor {
|
|||
if (pair.second) {
|
||||
varp = new AstVar{fl, VVarType::MODULETEMP, m_exprTempNames.get(frefp),
|
||||
dtypep};
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
pair.first->second = varp;
|
||||
if (m_ftaskp) {
|
||||
varp->funcLocal(true);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
, crossBins{cb} {}
|
||||
};
|
||||
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
|
||||
|
||||
|
|
@ -88,6 +91,17 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
// Clear bin info for this covergroup (deleting any orphaned cross pseudo-bins)
|
||||
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 (AstCoverpoint* cpp : m_coverpoints) generateCoverpointCode(cpp);
|
||||
|
||||
|
|
@ -504,6 +518,13 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
// Create implicit automatic bins if no regular bins exist
|
||||
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
|
||||
// Process in two passes: first non-default bins, then default bins
|
||||
std::vector<AstCoverBin*> defaultBins;
|
||||
|
|
@ -595,47 +616,213 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
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
|
||||
// Default bins match when value doesn't match any other explicit bin
|
||||
void generateDefaultBinMatchCode(AstCoverpoint* coverpointp, AstCoverBin* defBinp,
|
||||
AstNodeExpr* exprp, AstVar* hitVarp) {
|
||||
UINFO(4, " Generating default bin match for: " << defBinp->name());
|
||||
|
||||
// Build OR of all non-default, non-ignore bins
|
||||
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{}};
|
||||
}
|
||||
AstNodeExpr* defaultCondp = buildDefaultCondition(coverpointp, exprp, defBinp->fileline());
|
||||
|
||||
// Apply iff condition if present
|
||||
if (AstNodeExpr* iffp = coverpointp->iffp()) {
|
||||
|
|
@ -903,34 +1090,10 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
int atLeastValue) {
|
||||
UINFO(4, " Generating array bins for: " << arrayBinp->name());
|
||||
|
||||
// Extract all values from the range list
|
||||
std::vector<AstNodeExpr*> values;
|
||||
for (AstNode* rangep = arrayBinp->rangesp(); rangep; rangep = rangep->nextp()) {
|
||||
if (AstInsideRange* const insideRangep = VN_CAST(rangep, InsideRange)) {
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
// Extract all values from the range list (resolves '$', caps/ignores huge ranges)
|
||||
bool unsupported = false;
|
||||
std::vector<AstNodeExpr*> values = extractArrayValues(arrayBinp, exprp, unsupported);
|
||||
if (unsupported) return; // bin ignored (COVERIGN emitted)
|
||||
|
||||
// Create a separate bin for each value
|
||||
int index = 0;
|
||||
|
|
@ -1132,6 +1295,18 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
while (itemp) {
|
||||
AstNode* const nextp = itemp->nextp();
|
||||
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)
|
||||
const auto it = m_coverpointMap.find(refp->name());
|
||||
AstCoverpoint* const foundCpp = (it != m_coverpointMap.end()) ? it->second : nullptr;
|
||||
|
|
@ -1320,15 +1495,50 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
|
||||
void generateCoverageMethodBody(AstFunc* funcp) {
|
||||
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;
|
||||
for (const BinInfo& bi : m_binInfos) {
|
||||
UINFO(6, " Bin: " << bi.binp->name() << " type=" << bi.binp->binsType().ascii());
|
||||
if (bi.binp->binsType() != VCoverBinsType::BINS_IGNORE
|
||||
&& bi.binp->binsType() != VCoverBinsType::BINS_ILLEGAL) {
|
||||
totalBins++;
|
||||
}
|
||||
if (bi.binp->binsType().binIsNormal()) totalBins++;
|
||||
}
|
||||
|
||||
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%.
|
||||
// Any parser-generated initialization of returnVar is overridden by our assignment.
|
||||
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},
|
||||
new AstConst{fl, AstConst::RealDouble{}, 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 (const BinInfo& bi : m_binInfos) {
|
||||
// Skip ignore_bins and illegal_bins in coverage calculation
|
||||
if (bi.binp->binsType() == VCoverBinsType::BINS_IGNORE
|
||||
|| bi.binp->binsType() == VCoverBinsType::BINS_ILLEGAL) {
|
||||
continue;
|
||||
}
|
||||
// Skip ignore/illegal/default bins in coverage calculation
|
||||
if (!bi.binp->binsType().binIsNormal()) continue;
|
||||
|
||||
// if (bin_count >= at_least) covered_count++;
|
||||
AstIf* ifp = new AstIf{
|
||||
|
|
@ -1375,9 +1581,6 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
|||
funcp->addStmtsp(ifp);
|
||||
}
|
||||
|
||||
// Find the return variable
|
||||
AstVar* const returnVarp = VN_AS(funcp->fvarp(), Var);
|
||||
|
||||
// Calculate coverage: (covered_count / 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
|
||||
nodep->typeTablep()->repairCache();
|
||||
VIsCached::clearCacheTree(); // Removing assignments may affect isPure
|
||||
nodep->constPoolp()->reCache();
|
||||
}
|
||||
~DeadVisitor() override {
|
||||
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_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
|
||||
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
|
||||
|
||||
// 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_nSchemeValueQueuesPartial; // Number of variables using Scheme::ValueQueuePartial
|
||||
VDouble0 m_nSharedSetFlags; // "Set" flags actually shared by Scheme::FlagShared variables
|
||||
VDouble0 m_nInitialNBA; // Number of procedural blocks with initial NBA
|
||||
|
||||
// METHODS
|
||||
|
||||
|
|
@ -999,6 +1001,12 @@ class DelayedVisitor final : public VNVisitor {
|
|||
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
|
||||
void visit(AstNetlist* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -1112,21 +1120,36 @@ class DelayedVisitor final : public VNVisitor {
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_needsInitialTrigger);
|
||||
const size_t firstNBAAddedIndex = m_nbas.size();
|
||||
{
|
||||
VL_RESTORER(m_inSuspendableOrFork);
|
||||
VL_RESTORER(m_procp);
|
||||
VL_RESTORER(m_ignoreBlkAndNBlk);
|
||||
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;
|
||||
if (m_inSuspendableOrFork) {
|
||||
if (nodep->isSuspendable()) {
|
||||
m_ignoreBlkAndNBlk = false;
|
||||
m_inNonCombLogic = true;
|
||||
}
|
||||
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
|
||||
// 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
|
||||
AstSenItem* senItemp = nullptr;
|
||||
if (addInitialTrigger) {
|
||||
senItemp = new AstSenItem{nodep->fileline(), AstSenItem::InitialNBA{}};
|
||||
++m_nInitialNBA;
|
||||
}
|
||||
|
||||
for (const AstSenTree* const domainp : m_timingDomains) {
|
||||
if (domainp->sensesp())
|
||||
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,
|
||||
"<= assignment in non-clocked block, should have been converted in V3Active");
|
||||
|
||||
m_needsInitialTrigger |= m_timingDomains.empty();
|
||||
|
||||
// Record scope of this NBA
|
||||
nodep->user2p(m_scopep);
|
||||
|
||||
|
|
@ -1327,6 +1357,7 @@ public:
|
|||
V3Stats::addStat("NBA, variables using ValueQueuePartial scheme",
|
||||
m_nSchemeValueQueuesPartial);
|
||||
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