merge upstream master and resolve conflicts

This commit is contained in:
Yilou Wang 2026-06-26 10:13:46 +02:00
commit 0019a967b4
475 changed files with 18092 additions and 4443 deletions

View File

@ -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) }}

View File

@ -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

View File

@ -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

View File

@ -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 }}

View File

@ -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

View File

@ -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

View File

@ -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 }}

View File

@ -27,7 +27,7 @@ jobs:
name: Sub-lint | Python
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
path: repo

View File

@ -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 }}

View File

@ -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 }}

View File

@ -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 }}

View File

@ -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

142
AGENTS.md Normal file
View File

@ -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
View File

@ -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

View File

@ -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 =

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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
=====================================

38
docs/AGENTS.md Normal file
View File

@ -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.

View File

@ -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
=========================

View File

@ -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

View File

@ -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
=======================

View File

@ -0,0 +1,7 @@
.. comment: generated by t_finaldly_bad
.. code-block:: sv
:linenos:
:emphasize-lines: 2
bit foo;
final foo <= 1; // <--- Error

View File

@ -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;
| ^~

View File

@ -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

View File

@ -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``.

View File

@ -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

View File

@ -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,

View File

@ -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.

View File

@ -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

View File

@ -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
============

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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,

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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
==================

View File

@ -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
===============

View File

@ -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
===================

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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:

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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
===============

View File

@ -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

60
include/AGENTS.md Normal file
View File

@ -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` |

View File

@ -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)

View File

@ -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 {

View File

@ -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);
}

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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() {}
};

View File

@ -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;
}

View File

@ -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,

View File

@ -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));

View File

@ -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

241
src/AGENTS.md Normal file
View File

@ -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 |

View File

@ -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

View File

@ -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 \

View File

@ -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); }

View File

@ -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 {

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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()) {

View File

@ -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");

View File

@ -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:

View File

@ -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'

View File

@ -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:

View File

@ -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);

View File

@ -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

View File

@ -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;
};

View File

@ -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());

File diff suppressed because it is too large Load Diff

View File

@ -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());

View File

@ -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)");

View File

@ -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(

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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