Merge remote-tracking branch 'upstream/master' into fix/gap33-inside-impl-mixwidth

This commit is contained in:
Yilou Wang 2026-07-06 15:42:30 +02:00
commit 119493ee8a
588 changed files with 21702 additions and 6504 deletions

View File

@ -16,6 +16,7 @@ on:
permissions:
contents: read
actions: read
defaults:
run:
@ -28,259 +29,143 @@ concurrency:
jobs:
build-2404-gcc:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
build-2604-gcc:
name: Build | 26.04 | gcc
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
runs-on: ubuntu-26.04
sha: ${{ github.sha }}
build-2604-clang:
name: Build | 26.04 | clang
uses: ./.github/workflows/reusable-build.yml
with:
cc: clang
dev-asan: true # Build (and run) with address sanitizer
runs-on: ubuntu-26.04
sha: ${{ github.sha }}
build-2404-gcc:
name: Build | 24.04 | gcc
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
runs-on: ubuntu-24.04
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-24.04, cc: gcc, asan: 0}
build-2404-clang:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
name: Build | 24.04 | clang
uses: ./.github/workflows/reusable-build.yml
with:
cc: clang
runs-on: ubuntu-24.04
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-24.04, cc: clang, asan: 1}
build-2204-gcc:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
name: Build | 22.04 | gcc
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
runs-on: ubuntu-22.04
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: gcc, asan: 0}
build-2204-clang:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
build-macos-15-clang:
name: Build | macos-15 | clang
uses: ./.github/workflows/reusable-build.yml
with:
cc: clang
runs-on: macos-15
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
with:
sha: ${{ github.sha }}
os: ${{ matrix.os }}
os-name: osx
cc: ${{ matrix.cc }}
dev-asan: ${{ matrix.asan }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
- {os: macos-15, cc: gcc, asan: 0}
build-osx-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: osx
cc: ${{ matrix.cc }}
dev-asan: ${{ matrix.asan }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
- {os: macos-15, cc: clang, asan: 0}
build-windows:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- {os: windows-2025-vs2026, cc: msvc}
env:
CI_OS_NAME: win
CCACHE_COMPRESS: 1
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_LIMIT_MULTIPLE: 0.95
name: Build | windows-2025-vs2026 | msvc
runs-on: windows-2025-vs2026
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
path: repo
- name: Cache $CCACHE_DIR
uses: actions/cache@v5
- name: Cache win_flex_bison
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: ${{ env.CCACHE_DIR }}
key: msbuild-msvc-cmake
path: ${{ github.workspace }}/win_flex_bison
key: win_flex_bison
- name: compile
env:
WIN_FLEX_BISON: ${{ github.workspace }}/.ccache
run: ./ci/ci-win-compile.ps1
- name: test build
run: ./ci/ci-win-test.ps1
- name: Zip up repository
run: Compress-Archive -LiteralPath install -DestinationPath verilator.zip
- name: Upload zip archive
uses: actions/upload-artifact@v7
with:
path: ${{ github.workspace }}/repo/verilator.zip
name: verilator-win.zip
test-2604-gcc:
name: Test | 26.04 | gcc | ${{ matrix.suite }}
needs: build-2604-gcc
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build-2604-gcc.outputs.archive }}
cc: gcc
runs-on: ubuntu-26.04
suite: ${{ matrix.suite }}
strategy:
fail-fast: false
matrix:
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
test-2604-clang:
name: Test | 26.04 | clang | ${{ matrix.suite }}
needs: build-2604-clang
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build-2604-clang.outputs.archive }}
cc: clang
runs-on: ubuntu-26.04
suite: ${{ matrix.suite }}
strategy:
fail-fast: false
matrix:
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
test-2404-gcc:
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
name: Test | 24.04 | gcc | ${{ matrix.suite }}
needs: build-2404-gcc
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build-2404-gcc.outputs.archive }}
os: ${{ matrix.os }}
cc: ${{ matrix.cc }}
reloc: ${{ matrix.reloc }}
cc: gcc
runs-on: ubuntu-24.04
suite: ${{ matrix.suite }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
# Ubuntu 24.04 gcc
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-0}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-1}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-2}
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
test-2404-clang:
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
name: Test | 24.04 | clang | ${{ matrix.suite }}
needs: build-2404-clang
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build-2404-clang.outputs.archive }}
os: ${{ matrix.os }}
cc: ${{ matrix.cc }}
reloc: ${{ matrix.reloc }}
cc: clang
reloc: true # Test with relocated installation
runs-on: ubuntu-24.04
suite: ${{ matrix.suite }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
# Ubuntu 24.04 clang
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-0}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-1}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-2}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-3}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-0}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-1}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-2}
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
test-2204-gcc:
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
name: Test | 22.04 | gcc | ${{ matrix.suite }}
needs: build-2204-gcc
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build-2204-gcc.outputs.archive }}
os: ${{ matrix.os }}
cc: ${{ matrix.cc }}
reloc: ${{ matrix.reloc }}
cc: gcc
runs-on: ubuntu-22.04
suite: ${{ matrix.suite }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
# Ubuntu 22.04 gcc
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-0}
- {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}
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, 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

@ -13,6 +13,7 @@ on:
permissions:
contents: read
actions: read
defaults:
run:
@ -38,15 +39,13 @@ jobs:
(github.event_name == 'workflow_dispatch')
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
dev-gcov: true
runs-on: ubuntu-24.04
# For pull requests, build the head of the pull request branch, not the
# merge commit, otherwise patch coverage would include the changes
# between the root of the pull request and the target branch
sha: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
os: ubuntu-24.04
os-name: linux
cc: gcc
dev-asan: 0
dev-gcov: 1
test:
name: Test | ${{ matrix.test }}${{ matrix.num }}
@ -54,11 +53,10 @@ jobs:
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build.outputs.archive }}
os: ubuntu-24.04
cc: gcc
reloc: 0
dev-gcov: true
runs-on: ubuntu-24.04
suite: ${{ matrix.test }}${{ matrix.num }}
dev-gcov: 1
strategy:
fail-fast: false
matrix:
@ -74,10 +72,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 +88,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,18 +112,18 @@ 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 }}
- name: Unpack repository archive
run: |
tar -x -z -f ${{ needs.build.outputs.archive }}
tar --zstd -x -f ${{ needs.build.outputs.archive }}
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 +170,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 +193,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

@ -15,18 +15,12 @@ jobs:
format:
runs-on: ubuntu-24.04
name: Ubuntu 24.04 | format
env:
CI_OS_NAME: linux
CI_RUNS_ON: ubuntu-24.04
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
env:
CI_BUILD_STAGE_NAME: build
run: |
sudo apt install clang-format-18 || \
sudo apt install clang-format-18

View File

@ -7,7 +7,7 @@ name: Pages
on:
push:
branches: [master]
paths: ["ci/**", ".github/workflows"]
paths: ["ci/**", ".github/workflows/**"]
workflow_dispatch:
workflow_run:
workflows: ["Code coverage", "RTLMeter"]
@ -22,8 +22,11 @@ permissions:
# Allow only one concurrent deployment, skipping runs queued between the run
# in-progress and latest queued. However, do NOT cancel in-progress runs as we
# want to allow these deployments to complete.
# A skipped-upstream run does no work (see the build job's if:), so put it in its
# own throwaway group: otherwise, as the newest queued run, it would cancel a
# pending real run and then deploy nothing.
concurrency:
group: "pages"
group: ${{ (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'skipped') && format('pages-skip-{0}', github.run_id) || 'pages' }}
cancel-in-progress: false
defaults:
@ -34,11 +37,15 @@ jobs:
build:
name: Build content
runs-on: ubuntu-24.04
# A skipped upstream run (e.g. Code coverage / RTLMeter on a branch push)
# still fires workflow_run; don't rebuild and redeploy for it. deploy and
# notify need this job, so they cascade-skip too.
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion != 'skipped' }}
outputs:
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 +55,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
@ -58,10 +65,17 @@ jobs:
runs-on: ubuntu-24.04
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
url: ${{ steps.deploy-2.outputs.page_url || steps.deploy-1.outputs.page_url }}
steps:
# GitHub's Pages backend intermittently fails a deployment mid-sync, retry
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v5
id: deploy-1
continue-on-error: true
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
- name: Deploy to GitHub Pages (retry)
id: deploy-2
if: steps.deploy-1.outcome == 'failure'
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
notify:
name: Notify
@ -70,13 +84,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

@ -7,37 +7,47 @@ name: reusable-build
on:
workflow_call:
inputs:
cc:
description: "Compiler to use: 'gcc' or 'clang'"
required: true
type: string
ccwarn:
description: "Build Verilator with warnings treated as errors (--enable-ccwarn)"
required: false
default: true
type: boolean
dev-asan:
description: "Build Verilator with the address sanitizer (--enable-dev-asan)"
required: false
default: false
type: boolean
dev-gcov:
description: "Build Verilator with gcov instrumentation (--enable-dev-gcov)"
required: false
default: false
type: boolean
install:
description: "Archive the Verilator installation, not the repo tree"
required: false
default: false
type: boolean
runs-on:
description: "Runner to build on, e.g. ubuntu-24.04"
required: true
type: string
sha:
description: "Commit SHA to build"
required: true
type: string
os: # e.g. ubuntu-24.04
required: true
type: string
cc: # 'clang' or 'gcc'
required: true
type: string
os-name: # 'linux' or 'osx'
required: true
type: string
dev-asan:
required: true
type: number
dev-gcov:
required: true
type: number
outputs:
archive:
description: "Name of the built repository archive artifact"
description: "Name of the built archive artifact"
value: ${{ jobs.build.outputs.archive }}
env:
CI_OS_NAME: ${{ inputs.os-name }}
CCACHE_COMPRESS: 1
CACHE_BASE_KEY: build-${{ inputs.runs-on }}-${{ inputs.cc }}${{ inputs.ccwarn && '-ccwarn' || '' }}${{ inputs.dev-asan && '-asan' || '' }}${{ inputs.dev-gcov && '-gcov' || '' }}
CCACHE_COMPILERCHECK: content
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_LIMIT_MULTIPLE: 0.95
INSTALL_DIR: ${{ github.workspace }}/install
RELOC_DIR: ${{ github.workspace }}/relloc
defaults:
run:
@ -48,52 +58,70 @@ jobs:
build:
name: Build
runs-on: ${{ inputs.os }}
runs-on: ${{ inputs.runs-on }}
outputs:
archive: ${{ steps.create-archive.outputs.archive }}
env:
CI_BUILD_STAGE_NAME: build
CI_DEV_ASAN: ${{ inputs.dev-asan }}
CI_DEV_GCOV: ${{ inputs.dev-gcov }}
CI_RUNS_ON: ${{ inputs.os }}
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
CACHE_BASE_KEY: build-${{ inputs.os }}-${{ inputs.cc }}
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
# Coverage needs full history; install needs it for the 'git describe'
# behind 'verilator --version'
fetch-depth: ${{ (inputs.install || inputs.dev-gcov) && '0' || '1' }}
# ccache is unreliable on the macOS runners; disable it there
- name: Disable ccache on macOS
if: ${{ startsWith(inputs.runs-on, 'macos') }}
run: echo "CCACHE_DISABLE=1" >> "$GITHUB_ENV"
- name: Cache $CCACHE_DIR
uses: actions/cache@v5
if: ${{ env.CCACHE_DISABLE != '1' }}
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
env:
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ env.CACHE_KEY }}-${{ inputs.sha }}
restore-keys: |
${{ env.CACHE_KEY }}-
${{ env.CACHE_KEY }}
- name: Install packages for build
run: ./ci/ci-install.bash
run: ./ci/ci-install.bash build
- name: Build
run: ./ci/ci-script.bash
run: |
./ci/ci-build.bash \
--prefix ${{ github.workspace }}/install \
--compiler ${{ inputs.cc }} \
${{ inputs.ccwarn && '--ccwarn' || '' }} \
${{ inputs.dev-asan && '--asan' || '' }} \
${{ inputs.dev-gcov && '--gcov' || '' }}
- name: Create repository archive
- name: Install
if: ${{ inputs.install }}
run: make install
- name: Create archive
id: create-archive
working-directory: ${{ github.workspace }}
run: |
# Name of the archive must be unique based on the build parameters
ARCHIVE=verilator-${{ inputs.sha }}-${{ inputs.os }}-${{ inputs.cc }}-${{ inputs.dev-asan }}-${{ inputs.dev-gcov }}.tar.gz
tar --posix -c -z -f $ARCHIVE repo
# Archive name, unique per build; flavour tags mark non-default builds
ARCHIVE="verilator-${{ inputs.sha }}-${{ inputs.runs-on }}-${{ inputs.cc }}"
ARCHIVE="$ARCHIVE${{ inputs.ccwarn && '-ccwarn' || '' }}"
ARCHIVE="$ARCHIVE${{ inputs.dev-asan && '-asan' || '' }}"
ARCHIVE="$ARCHIVE${{ inputs.dev-gcov && '-gcov' || '' }}"
ARCHIVE="$ARCHIVE.tar.zst"
# zstd compresses faster than gzip and decompresses far faster in the many downstream jobs
ZSTD_NBTHREADS=0 tar --posix --zstd -c -f "$ARCHIVE" ${{ inputs.install && 'install' || 'repo' }}
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
- name: Upload repository archive
uses: actions/upload-artifact@v7
- name: Upload archive
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
name: ${{ steps.create-archive.outputs.archive }}
overwrite: true
# Archive is already zstd-compressed; skip upload-artifact's zip pass
compression-level: 0

View File

@ -7,14 +7,6 @@ name: reusable-lint-py
on:
workflow_call:
env:
CI_OS_NAME: linux
CI_BUILD_STAGE_NAME: build
CI_RUNS_ON: ubuntu-22.04
CCACHE_COMPRESS: 1
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_LIMIT_MULTIPLE: 0.95
defaults:
run:
shell: bash
@ -27,12 +19,12 @@ jobs:
name: Sub-lint | Python
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
path: repo
- name: Install packages for build
run: ./ci/ci-install.bash
run: ./ci/ci-install.bash build
- name: Configure
run: |

View File

@ -1,96 +0,0 @@
---
# DESCRIPTION: Github actions config
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: reusable-rtlmeter-build
on:
workflow_call:
inputs:
runs-on:
description: "Runner to use, e.g.: ubuntu-24.04"
type: string
required: true
cc:
description: "Compiler to use: 'gcc' or 'clang'"
type: string
required: true
sha:
description: "Git SHA to build"
type: string
required: true
outputs:
archive:
description: "Name of the built installation archive artifact"
value: ${{ jobs.build.outputs.archive }}
defaults:
run:
shell: bash
env:
CCACHE_DIR: ${{ github.workspace }}/ccache
CCACHE_MAXSIZE: 512M
jobs:
build:
name: Build
runs-on: ${{ inputs.runs-on }}
outputs:
archive: ${{ steps.create-archive.outputs.archive }}
steps:
- name: Install dependencies
run: |
echo "path-exclude /usr/share/doc/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
echo "path-exclude /usr/share/man/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
sudo apt update || \
sudo apt update
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev || \
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev
- name: Use saved ccache
uses: actions/cache@v5
with:
path: ccache
key: rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}-${{ github.run_id }}
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}
- name: Checkout
uses: actions/checkout@v6
with:
path: repo
ref: ${{ inputs.sha }}
fetch-depth: 0 # Required for 'git describe' used for 'verilator --version'
- name: Configure
working-directory: repo
run: |
autoconf
./configure --prefix=${{ github.workspace }}/install CXX=${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
- name: Make
working-directory: repo
run: make -j $(nproc)
- name: Install
working-directory: repo
run: make install
- name: Tar up installation
id: create-archive
run: |
SHA=$(git -C repo rev-parse HEAD)
ARCHIVE=verilator-$SHA-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}.tar.gz
tar --posix -c -z -f $ARCHIVE install
echo "archive=$ARCHIVE" >> $GITHUB_OUTPUT
- name: Upload Verilator installation archive
uses: actions/upload-artifact@v7
with:
path: ${{ steps.create-archive.outputs.archive }}
name: ${{ steps.create-archive.outputs.archive }}
overwrite: true

View File

@ -20,11 +20,11 @@ on:
type: string
required: true
verilator-archive-new:
description: "Name of the installation archive artifact from reusable-rtlmeter-build, new version"
description: "Name of the installation archive artifact from reusable-build, new version"
type: string
required: true
verilator-archive-old:
description: "Name of the installation archive artifact from reusable-rtlmeter-build, old version"
description: "Name of the installation archive artifact from reusable-build, old version"
type: string
required: false
default: ""
@ -53,8 +53,8 @@ defaults:
shell: bash
env:
CCACHE_DIR: ${{ github.workspace }}/ccache
CCACHE_MAXSIZE: 512M
# RTLMeter measures compile/execute times, so caching must stay off to keep
# timings representative; ccache is still on PATH but acts as a pass-through
CCACHE_DISABLE: 1
jobs:
@ -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
@ -82,26 +82,18 @@ jobs:
working-directory: rtlmeter
run: make venv
- name: Use saved ccache
if: ${{ env.CCACHE_DISABLE == 0 }}
uses: actions/cache@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 }}
restore-keys: rtlmeter-run-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.cases }}-${{ inputs.compileArgs }}
########################################################################
# Run with new Verilator
########################################################################
- name: Download Verilator installation archive - new
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: ${{ inputs.verilator-archive-new }}
- name: Unpack Verilator installation archive - new
run: |
tar -x -z -f ${{ inputs.verilator-archive-new }}
tar --zstd -x -f ${{ inputs.verilator-archive-new }}
mv install verilator-new
- name: Compile cases - new
@ -135,7 +127,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,14 +149,14 @@ 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 }}
- name: Unpack Verilator installation archive - old
if: ${{ inputs.verilator-archive-old != '' }}
run: |
tar -x -z -f ${{ inputs.verilator-archive-old }}
tar --zstd -x -f ${{ inputs.verilator-archive-old }}
mv install verilator-old
- name: Compile cases - old
@ -198,7 +190,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

@ -11,29 +11,38 @@ on:
description: "Name of the repository archive artifact from reusable-build"
required: true
type: string
os: # e.g. ubuntu-24.04
required: true
type: string
cc: # gcc or clang
required: true
type: string
reloc: # 0 or 1
required: true
type: number
suite: # e.g. dist-vlt-0
cc:
description: "Compiler to use: 'gcc' or 'clang'"
required: true
type: string
dev-gcov:
description: "Collect gcov coverage data from the test run"
required: false
default: false
type: boolean
reloc:
description: "Relocate the installation before testing"
required: false
default: false
type: boolean
runs-on:
description: "Runner to test on, e.g. ubuntu-24.04"
required: true
type: number
type: string
suite:
description: "Test suite to run, e.g. dist-vlt-0"
required: true
type: string
permissions:
contents: read
actions: read
env:
CI_OS_NAME: linux
CCACHE_COMPRESS: 1
CCACHE_COMPILERCHECK: content
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_LIMIT_MULTIPLE: 0.95
INSTALL_DIR: ${{ github.workspace }}/install
RELOC_DIR: ${{ github.workspace }}/relloc
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
GH_TOKEN: ${{ github.token }}
defaults:
run:
@ -43,19 +52,12 @@ defaults:
jobs:
test:
runs-on: ${{ inputs.os }}
runs-on: ${{ inputs.runs-on }}
name: Test
env:
CI_BUILD_STAGE_NAME: test
CI_RUNS_ON: ${{ inputs.os }}
CI_RELOC: ${{inputs.reloc }}
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
CACHE_BASE_KEY: test-${{ inputs.os }}-${{ inputs.cc }}-${{inputs.reloc }}-${{ inputs.suite }}
CCACHE_MAXSIZE: 100M # Per build per suite (* 5 * 5 = 2500M in total)
steps:
- name: Download repository archive
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: ${{ inputs.archive }}
path: ${{ github.workspace }}
@ -63,32 +65,90 @@ jobs:
- name: Unpack repository archive
working-directory: ${{ github.workspace }}
run: |
tar -x -z -f ${{ inputs.archive }}
tar --zstd -x -f ${{ inputs.archive }}
ls -lsha
- name: Cache $CCACHE_DIR
uses: actions/cache@v5
# Test-job ccache is stored as an artifact, not actions/cache
# - pull_request: restore this PR's own previous run; if none, fall back to the PR's target branch.
# - branch push: restore that branch's own cache
# Artifact names are a global, unauthenticated namespace, so a PR could
# upload a "-branch-<x>" artifact impersonating a branch. Branch-scoped
# restores are therefore provenance-checked below (must come from a same-repo
# push to that branch); the pr-<N> scope is not (a PR can only affect its own
# unmerged cache).
- name: Compute ccache artifact name
# Pass github refs via env, not ${{ }} interpolation, so a branch name
# containing shell metacharacters can't break or inject into the script.
env:
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
restore-keys: |
${{ env.CACHE_KEY }}-
BASE_REF: ${{ github.event.pull_request.base.ref }}
REF_NAME: ${{ github.ref_name }}
run: |
base="ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.suite }}"
sanitize() { printf '%s' "$1" | tr '/' '_'; } # artifact names cannot contain '/'
if [ "${{ github.event_name }}" = pull_request ]; then
echo "CCACHE_ARTIFACT=${base}-pr-${{ github.event.pull_request.number }}" >> "$GITHUB_ENV"
echo "CCACHE_ARTIFACT_BRANCH=" >> "$GITHUB_ENV"
echo "CCACHE_FALLBACK=${base}-branch-$(sanitize "$BASE_REF")" >> "$GITHUB_ENV"
echo "CCACHE_FALLBACK_BRANCH=${BASE_REF}" >> "$GITHUB_ENV"
else
echo "CCACHE_ARTIFACT=${base}-branch-$(sanitize "$REF_NAME")" >> "$GITHUB_ENV"
echo "CCACHE_ARTIFACT_BRANCH=${REF_NAME}" >> "$GITHUB_ENV"
fi
- name: Restore ccache from artifact
continue-on-error: true # a cold start is fine; never fail the job here
run: |
set -euo pipefail
mkdir -p "$CCACHE_DIR"
# Return newest non-expired artifact named $1 (empty if none). When
# $2 (branch) is non-empty, require provenance: the artifact must come from
# a same-repo run (head_repository_id == repository_id) whose head branch
# is $2, so a fork PR cannot forge a branch-scoped artifact.
newest_run_id() {
want="$1" br="$2" gh api \
"/repos/$GITHUB_REPOSITORY/actions/artifacts?name=$1&per_page=100" \
--jq '.artifacts[]
| select(.expired == false and .name == env.want)
| select(env.br == ""
or (.workflow_run.head_branch == env.br
and .workflow_run.head_repository_id == .workflow_run.repository_id))
| [.created_at, (.workflow_run.id | tostring)] | @tsv' \
| sort | tail -n1 | cut -f2
}
restore() { # $1=name $2=branch(empty ok); returns 0 on a successful restore
local name="$1" br="$2" rid tarball
[ -n "$name" ] || return 1
rid="$(newest_run_id "$name" "$br")" || return 1
[ -n "$rid" ] || return 1
echo "Restoring ccache from '$name' (run $rid)"
gh run download "$rid" --name "$name" --dir "$RUNNER_TEMP/ccache-dl" || return 1
tarball="$(find "$RUNNER_TEMP/ccache-dl" -name ccache.tar.zst -print -quit)"
[ -n "$tarball" ] || return 1
tar -I zstd -x -f "$tarball" -C "$CCACHE_DIR"
}
if restore "${CCACHE_ARTIFACT:-}" "${CCACHE_ARTIFACT_BRANCH:-}"; then
echo "Restored primary ccache"
exit 0
fi
if restore "${CCACHE_FALLBACK:-}" "${CCACHE_FALLBACK_BRANCH:-}"; then
echo "Restored fallback ccache"
exit 0
fi
echo "No ccache artifact found; starting cold"
- name: Install test dependencies
run: |
./ci/ci-install.bash
./ci/ci-install.bash test
make venv
- name: Test
id: run-test
continue-on-error: true
env:
TESTS: ${{ inputs.suite }}
run: |
source .venv/bin/activate
./ci/ci-script.bash
./ci/ci-test.bash \
--suite ${{ inputs.suite }} \
${{ inputs.reloc && format('--reloc {0}/reloc', github.workspace) || '' }}
- name: Combine code coverage data
if: ${{ inputs.dev-gcov }}
@ -99,11 +159,25 @@ 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 }}
- name: Pack ccache
if: ${{ !cancelled() && env.CCACHE_ARTIFACT != '' }}
run: tar -I 'zstd -T0' -cf "$GITHUB_WORKSPACE/ccache.tar.zst" -C "$CCACHE_DIR" .
- name: Save ccache artifact
if: ${{ !cancelled() && env.CCACHE_ARTIFACT != '' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: ${{ env.CCACHE_ARTIFACT }}
path: ${{ github.workspace }}/ccache.tar.zst
retention-days: 3
overwrite: true
compression-level: 0
- name: Fail job if a test failed
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
run: |-

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
@ -65,39 +65,47 @@ jobs:
build-gcc-new:
name: Build New Verilator - GCC
needs: start
uses: ./.github/workflows/reusable-rtlmeter-build.yml
uses: ./.github/workflows/reusable-build.yml
with:
runs-on: ubuntu-24.04
cc: gcc
ccwarn: false
install: true
runs-on: ubuntu-24.04
sha: ${{ github.sha }}
build-clang-new:
name: Build New Verilator - Clang
needs: start
uses: ./.github/workflows/reusable-rtlmeter-build.yml
uses: ./.github/workflows/reusable-build.yml
with:
runs-on: ubuntu-24.04
cc: clang
ccwarn: false
install: true
runs-on: ubuntu-24.04
sha: ${{ github.sha }}
build-gcc-old:
name: Build Old Verilator - GCC
needs: start
if: ${{ needs.start.outputs.old-sha != '' }}
uses: ./.github/workflows/reusable-rtlmeter-build.yml
uses: ./.github/workflows/reusable-build.yml
with:
runs-on: ubuntu-24.04
cc: gcc
ccwarn: false
install: true
runs-on: ubuntu-24.04
sha: ${{ needs.start.outputs.old-sha }}
build-clang-old:
name: Build Old Verilator - Clang
needs: start
if: ${{ needs.start.outputs.old-sha != '' }}
uses: ./.github/workflows/reusable-rtlmeter-build.yml
uses: ./.github/workflows/reusable-build.yml
with:
runs-on: ubuntu-24.04
cc: clang
ccwarn: false
install: true
runs-on: ubuntu-24.04
sha: ${{ needs.start.outputs.old-sha }}
run-gcc:
@ -208,7 +216,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 +240,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 +267,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 +286,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 +339,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 +349,7 @@ jobs:
run: make venv
- name: Checkout Verilator
uses: actions/checkout@v6
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
path: verilator
@ -367,13 +375,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 +400,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.

View File

@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(
Verilator
VERSION 5.049
VERSION 5.051
HOMEPAGE_URL https://verilator.org
LANGUAGES CXX
)

95
Changes
View File

@ -10,12 +10,26 @@ The changes in each Verilator version are described below. The
contributors that suggested or implemented a given issue are shown in []. Thanks!
Verilator 5.049 devel
Verilator 5.051 devel
==========================
**Other:**
* Add comments as a branch description in coverage .info files (#7843). [Eryk Szpotanski]
* Optimize random initialization. [Geza Lore, Testorrent USA, Inc.]
* Fix DFG misoptimizing bound checks (#7755). [Jakub Michalski]
* Fix unique0 case side effects (#7787). [Pawel Klopotek]
* Fix cleaning purity cache after assertions. [Geza Lore, Testorrent USA, Inc.]
* Fix clang++ ambiguous overload of '==' operator (#7863). [Pawel Kojma, Antmicro Ltd.]
* Fix heap-use-after-free in `VlRNG::VlRNG()` (#7865). [Dragon-Git]
Verilator 5.050 2026-07-01
==========================
**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 +41,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 +67,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 +77,24 @@ 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 hierarchical reference cross members (#7749) (#7820). [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]
* Support global $assertcontrol (#7807). [Artur Bieniek, Antmicro Ltd.]
* Support VPI access to unpacked struct members (#7823). [Nick Brereton]
* Support variable-length intersect in SVA sequences (#7835). [Yilou Wang]
* Support dynamic loading of VPI extensions (#7727). [Matthew Ballance]
* Optimize DFG cycle breaking to do less work (#7210). [Geza Lore, Testorrent USA, Inc.]
* 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 +107,27 @@ 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 statically known oversize shifts (#7806). [Geza Lore, Testorrent USA, Inc.]
* Optimize generated function inlining (#7811). [Geza Lore, Testorrent USA, Inc.]
* Optimize bit-scan loops into most-set-bit or $countones (#7822). [Thomas Santerre]
* Optimize additional expression patterns (#7824). [Geza Lore, Testorrent USA, Inc.]
* Optimize module inlining heuristic (#7837). [Geza Lore, Testorrent USA, Inc.]
* Fix `$bits` on unpacked structs (#4521) (#7796). [Nick Brereton]
* 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).
@ -118,6 +172,7 @@ Verilator 5.049 devel
* Fix reference counting for modport task references (#7628). [Nick Brereton]
* Fix internal error when handling typedefs containing parameterized class type members (#7635) (#7661). [em2machine]
* Fix forceable signal with a procedural continuous assign (#7638) (#7639). [Zubin Jain]
* Fix scheduling of virtual interface method writes (#7641). [Artur Bieniek, Antmicro Ltd.]
* Fix implicit conversions of VlWide (#7642). [Geza Lore, Testorrent USA, Inc.]
* Fix CASEINCOMPLETE to not warn on `unique0 case` (#7647).
* Fix hierarchical coverage counts for duplicate no-inline module instances (#7649). [Yogish Sekhar]
@ -128,13 +183,45 @@ 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 `$` as unsupported coverpoint-bin range bounds (#7750) (#7825). [Matthew Ballance]
* 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 randomize() with skipping derived pre/post_randomize (#7799). [Yilou Wang]
* Fix skewed dist operator for arrays (#7802). [Jakub Wasilewski, Antmicro Ltd.]
* Fix assertion when loop unrolling failed (#7810). [Geza Lore, Testorrent USA, Inc.]
* Fix CASEINCOMPLETE for all uncovered enum items (#7815) (#7817). [Saksham]
* Fix split optimization nested-class crash (#7826). [Igor Zaworski, Antmicro Ltd.]
* Fix class/var named identically to an enclosing-scope type (#7827) (#7828). [Tom Jackson]
* Fix performance on large package-scoped structs (#7830). [Wolfgang Mayerwieser]
* Fix unclocked concurrent assertion misreported as unsupported (#7831). [Yilou Wang]
* Fix insertion of expression coverage statement (#7832). [Ryszard Rozak, Antmicro Ltd.]
* Fix lifetime of expression coverage variable (#7834). [Ryszard Rozak, Antmicro Ltd.]
* Fix disable iff ignored when its condition is held continuously true (#7841). [Yilou Wang]
* Fix constant pool cache after dead scope removal (#7845). [Nick Brereton]
* Fix covergroups without --coverage (#7848) (#7849). [Joshua Leahy]
* Fix class scope '::' reference through an inherited type parameter (#7844). [Sergey Chusov]
Verilator 5.048 2026-04-26
@ -471,7 +558,7 @@ Verilator 5.044 2026-01-01
* Support clocking output delay `1step` (#6681). [Ondrej Ille]
* Support parsing of dotted `bins_expression` (#6683). [Pawel Kojma, Antmicro Ltd.]
* Support constant expression cycle delays in sequences (#6691). [Ryszard Rozak, Antmicro Ltd.]
* Support general global constraints (#6709) (#6711). [Yilou Wang]
* Support general global constraints (#6709) (#6711) (#7833) (#7838). [Yilou Wang]
* Support complex std::randomize patterns (#6736) (#6737). [Yilou Wang]
* Support `rand_mode` in global constraint gathering (#6740) (#6752). [Yilou Wang]
* Support reduction or in constraints (#6840). [Pawel Kojma, Antmicro Ltd.]
@ -5665,7 +5752,7 @@ Verilator 3.104 2003-04-30
**Major:**
* Indicate direction of ports with VL_IN and VL_OUT.
* Allow $c32, etc, to specify width of the $c statement for VCS.
* Allow $c32, etc, to specify width of the $c statement.
* Numerous performance improvements, worth about 25%
**Minor:**

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
@ -652,6 +653,7 @@ description of these arguments.
+verilator+solver+file+<filename> Set random solver log filename
+verilator+V Show verbose version and config
+verilator+version Show version and exit
+verilator+vpi+<library>[:<bootstrap>] Load VPI shared library
+verilator+wno+unsatconstr+<value> Disable constraint warnings

85
ci/ci-build.bash Executable file
View File

@ -0,0 +1,85 @@
#!/usr/bin/env bash
# DESCRIPTION: Verilator: CI build job script
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
# Executed in the 'build' stage.
################################################################################
# Destructive to the checkout (reconfigures and rebuilds); never run locally
if [ "$GITHUB_ACTIONS" != "true" ]; then
echo "ERROR: $(basename "$0") must only be run in GitHub Actions CI" >&2
exit 1
fi
set -e
set -x
source "$(dirname "$0")/ci-common.bash"
################################################################################
# Parse arguments
OPT_ASAN=0
OPT_CCWARN=0
OPT_GCOV=0
OPT_COMPILER=
OPT_PREFIX=
while [ $# -gt 0 ]; do
case "$1" in
--asan) OPT_ASAN=1 ;;
--ccwarn) OPT_CCWARN=1 ;;
--compiler)
[ $# -ge 2 ] || fatal "--compiler requires an argument"
OPT_COMPILER="$2"
shift
;;
--gcov) OPT_GCOV=1 ;;
--prefix)
[ $# -ge 2 ] || fatal "--prefix requires an argument"
OPT_PREFIX="$2"
shift
;;
*) fatal "Unknown option: '$1'" ;;
esac
shift
done
[ -n "$OPT_COMPILER" ] || fatal "--compiler is required"
[ -n "$OPT_PREFIX" ] || fatal "--prefix is required"
# Map the compiler name to the executable name configure CXX expects
case "$OPT_COMPILER" in
clang) CXX=clang++ ;;
gcc) CXX=g++ ;;
*) fatal "Unknown compiler: '$OPT_COMPILER'" ;;
esac
################################################################################
# Configure
CONFIGURE_ARGS="--prefix=$OPT_PREFIX --enable-longtests"
if [ "$OPT_CCWARN" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-ccwarn"
fi
if [ "$OPT_ASAN" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-asan"
CXX="$CXX -DVL_LEAK_CHECKS"
fi
if [ "$OPT_GCOV" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-gcov"
fi
autoconf
./configure $CONFIGURE_ARGS CXX="$CXX"
################################################################################
# Build
ccache -z
BUILD_START=$SECONDS
"$MAKE" -j "$NPROC" -k
ccache -svv
ccache --evict-older-than "$((SECONDS - BUILD_START + 60))s"
ccache -svv

35
ci/ci-common.bash Normal file
View File

@ -0,0 +1,35 @@
# DESCRIPTION: Verilator: CI common definitions, sourced by the stage scripts
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
# Common definitions sourced by the CI stage scripts; not executed directly.
################################################################################
fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
}
case "$(uname -s)" in
Linux)
HOST_OS=linux
MAKE=make
NPROC=$(nproc)
# Distro id/version; subshell keeps os-release out of our namespace
if [ -r /etc/os-release ]; then
DISTRO_ID=$(. /etc/os-release; echo "$ID")
DISTRO_VERSION=$(. /etc/os-release; echo "$VERSION_ID")
fi
;;
Darwin)
HOST_OS=macOS
MAKE=make
NPROC=$(sysctl -n hw.logicalcpu)
;;
*)
fatal "Unknown host OS: '$(uname -s)'"
;;
esac
export MAKE

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
# DESCRIPTION: Verilator: CI dependency install script
#
# SPDX-FileCopyrightText: 2020 Geza Lore
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
@ -10,30 +10,33 @@
# required by the particular build stage.
################################################################################
# Installs system packages with sudo; never run locally
if [ "$GITHUB_ACTIONS" != "true" ]; then
echo "ERROR: $(basename "$0") must only be run in GitHub Actions CI" >&2
exit 1
fi
set -e
set -x
cd $(dirname "$0")/..
source "$(dirname "$0")/ci-common.bash"
################################################################################
# Parse arguments
# Which stage to install dependencies for: 'build' or 'test'
STAGE="$1"
################################################################################
# Install dependencies
# Avoid occasional cpan failures "Issued certificate has expired."
export PERL_LWP_SSL_VERIFY_HOSTNAME=0
echo "check_certificate = off" >> ~/.wgetrc
fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
}
if [ "$CI_OS_NAME" = "linux" ]; then
MAKE=make
elif [ "$CI_OS_NAME" = "osx" ]; then
MAKE=make
elif [ "$CI_OS_NAME" = "freebsd" ]; then
MAKE=gmake
else
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fi
if [ "$CI_OS_NAME" = "linux" ]; then
if [ "$HOST_OS" = "linux" ]; then
# Avoid slow "processing triggers for man db"
echo "path-exclude /usr/share/doc/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
echo "path-exclude /usr/share/man/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
@ -44,100 +47,100 @@ install-wavediff() {
source ci/docker/buildenv/wavetools.conf
local _base_url="https://github.com/hudson-trading/wavetools/releases/download/${WAVETOOLS_VERSION}"
local _platform
if [ "$CI_OS_NAME" = "linux" ]; then
if [ "$HOST_OS" = "linux" ]; then
_platform="linux-x86_64"
elif [ "$CI_OS_NAME" = "osx" ]; then
elif [ "$HOST_OS" = "macOS" ]; then
_platform="macos-arm64"
elif [ "$CI_OS_NAME" = "windows" ]; then
_platform="windows-x86_64"
else
echo "WARNING: No wavetools binary available for CI_OS_NAME=$CI_OS_NAME, skipping"
echo "WARNING: No wavetools binary available for HOST_OS=$HOST_OS, skipping"
return 0
fi
local _tmpdir
_tmpdir=$(mktemp -d)
local _archive="wavetools-${WAVETOOLS_VERSION}-${_platform}"
if [ "$CI_OS_NAME" = "windows" ]; then
wget -q -O "${_tmpdir}/${_archive}.zip" "${_base_url}/${_archive}.zip"
unzip -o "${_tmpdir}/${_archive}.zip" -d "${_tmpdir}"
else
wget -q -O "${_tmpdir}/${_archive}.tar.gz" "${_base_url}/${_archive}.tar.gz"
tar -xzf "${_tmpdir}/${_archive}.tar.gz" -C "${_tmpdir}"
fi
wget -q -O "${_tmpdir}/${_archive}.tar.gz" "${_base_url}/${_archive}.tar.gz"
tar -xzf "${_tmpdir}/${_archive}.tar.gz" -C "${_tmpdir}"
sudo cp "${_tmpdir}/${_archive}/wavediff" /usr/local/bin/wavediff
rm -rf "${_tmpdir}"
}
if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
if [ "$STAGE" = "build" ]; then
##############################################################################
# Dependencies of jobs in the 'build' stage, i.e.: packages required to
# build Verilator
if [ "$CI_OS_NAME" = "linux" ]; then
sudo apt-get update ||
sudo apt-get update
sudo apt-get install --yes ccache help2man libfl-dev ||
sudo apt-get install --yes ccache help2man libfl-dev
if [[ ! "$CI_RUNS_ON" =~ "ubuntu-22.04" ]]; then
# Some conflict of libunwind verison on 22.04, can live without it for now
sudo apt-get install --yes libjemalloc-dev ||
sudo apt-get install --yes libjemalloc-dev
fi
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
sudo apt-get install --yes libsystemc libsystemc-dev ||
sudo apt-get install --yes libsystemc libsystemc-dev
if [ "$HOST_OS" = "linux" ]; then
if [ "$DISTRO_ID" = "ubuntu" ]; then
PACKAGES=(
bear
ccache
help2man
libfl-dev
libsystemc-dev
mold
)
# libunwind conflict on 22.04, can live without libjemalloc there
if [ "$DISTRO_VERSION" != "22.04" ]; then
PACKAGES+=(libjemalloc-dev)
fi
sudo apt-get update ||
sudo apt-get update
sudo apt-get install --yes "${PACKAGES[@]}" ||
sudo apt-get install --yes "${PACKAGES[@]}"
fi
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
sudo apt-get install --yes bear mold ||
sudo apt-get install --yes bear mold
fi
elif [ "$CI_OS_NAME" = "osx" ]; then
elif [ "$HOST_OS" = "macOS" ]; then
PACKAGES=(
autoconf
bison
ccache
flex
gperftools
help2man
perl
)
brew update ||
brew update
brew install ccache perl gperftools autoconf bison flex help2man ||
brew install ccache perl gperftools autoconf bison flex help2man
elif [ "$CI_OS_NAME" = "freebsd" ]; then
sudo pkg install -y autoconf bison ccache gmake perl5
brew install "${PACKAGES[@]}" ||
brew install "${PACKAGES[@]}"
else
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fatal "Unknown HOST_OS: '$HOST_OS'"
fi
if [ -n "$CCACHE_DIR" ]; then
mkdir -p "$CCACHE_DIR"
fi
elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
elif [ "$STAGE" = "test" ]; then
##############################################################################
# Dependencies of jobs in the 'test' stage, i.e.: packages required to
# run the tests
if [ "$CI_OS_NAME" = "linux" ]; then
sudo apt-get update ||
sudo apt-get update
# libfl-dev needed for internal coverage's test runs
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3 ||
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3
# Required for test_regress/t/t_dist_attributes.py
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
sudo apt-get install --yes python3-clang mold ||
sudo apt-get install --yes python3-clang mold
if [ "$HOST_OS" = "linux" ]; then
if [ "$DISTRO_ID" = "ubuntu" ]; then
PACKAGES=(
ccache
gdb
jq
lcov
libfl-dev
libsystemc-dev
mold
python3-clang
z3
)
sudo apt-get update ||
sudo apt-get update
sudo apt-get install --yes "${PACKAGES[@]}" ||
sudo apt-get install --yes "${PACKAGES[@]}"
fi
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
sudo apt-get install --yes libsystemc libsystemc-dev ||
sudo apt-get install --yes libsystemc libsystemc-dev
fi
fi
elif [ "$CI_OS_NAME" = "osx" ]; then
elif [ "$HOST_OS" = "macOS" ]; then
PACKAGES=(
ccache
jq
perl
z3
)
brew update ||
brew update
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
brew install ccache perl jq z3
elif [ "$CI_OS_NAME" = "freebsd" ]; then
# fst2vcd fails with "Could not open '<input file>', exiting."
sudo pkg install -y ccache gmake perl5 python3 jq z3
brew install "${PACKAGES[@]}" ||
brew install "${PACKAGES[@]}"
else
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fatal "Unknown HOST_OS: '$HOST_OS'"
fi
# Common installs
install-wavediff
@ -146,5 +149,13 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
else
##############################################################################
# Unknown build stage
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
fatal "Unknown stage '$STAGE' (expected 'build' or 'test')"
fi
# Report where the tools we may have installed live (ok if some are missing)
set +x
echo "Tools:"
for bin in autoconf bear bison ccache flex gdb help2man jq lcov mold perl wavediff z3; do
echo -n " $bin: "
which "$bin" || echo "Not found"
done

View File

@ -34,9 +34,14 @@ for RUN_ID in ${PR_RUN_IDS//,/ }; do
cat ${ARTIFACTS_DIR}/body.txt
gh pr comment $(cat ${ARTIFACTS_DIR}/pr-number.txt) --body-file ${ARTIFACTS_DIR}/body.txt
# Get the artifact ID
ARTIFACT_ID=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
# Get the artifact IDs. Note there can be more than one artifact named
# 'pr-notification' for a single run, as the artifacts endpoint lists
# artifacts across all run attempts, and a re-run uploads a new one while
# keeping the previous attempt's artifact.
ARTIFACT_IDS=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
# Delete it, so we only notify once
gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}"
# Delete them all, so we only notify once
for ARTIFACT_ID in ${ARTIFACT_IDS}; do
gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}"
done
done

View File

@ -1,208 +0,0 @@
#!/usr/bin/env bash
# DESCRIPTION: Verilator: CI main job script
#
# SPDX-FileCopyrightText: 2020 Geza Lore
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
# This is the main script executed in the 'script' phase by all jobs. We use a
# single script to keep the CI setting simple. We pass job parameters via
# environment variables using 'env' keys.
################################################################################
set -e
set -x
fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
}
if [ "$CI_OS_NAME" = "linux" ]; then
export MAKE=make
NPROC=$(nproc)
elif [ "$CI_OS_NAME" = "osx" ]; then
export MAKE=make
NPROC=$(sysctl -n hw.logicalcpu)
# Disable ccache, doesn't always work in GitHub Actions
export OBJCACHE=
elif [ "$CI_OS_NAME" = "freebsd" ]; then
export MAKE=gmake
NPROC=$(sysctl -n hw.ncpu)
else
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fi
NPROC=$(expr $NPROC '+' 1)
if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
##############################################################################
# Build verilator
autoconf
CONFIGURE_ARGS="--enable-longtests --enable-ccwarn"
if [ "$CI_DEV_ASAN" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-asan"
CXX="$CXX -DVL_LEAK_CHECKS"
fi
if [ "$CI_DEV_GCOV" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-gcov"
fi
./configure $CONFIGURE_ARGS --prefix="$INSTALL_DIR"
ccache -z
"$MAKE" -j "$NPROC" -k
# 22.04: ccache -s -v
ccache -s
if [ "$CI_OS_NAME" = "osx" ]; then
file bin/verilator_bin
file bin/verilator_bin_dbg
md5 bin/verilator_bin
md5 bin/verilator_bin_dbg
stat bin/verilator_bin
stat bin/verilator_bin_dbg
fi
elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
##############################################################################
# Run tests
export VERILATOR_TEST_NO_CONTRIBUTORS=1 # Separate workflow check
export VERILATOR_TEST_NO_LINT_PY=1 # Separate workflow check
if [ "$CI_OS_NAME" = "osx" ]; then
export VERILATOR_TEST_NO_GDB=1 # Pain to get GDB to work on OS X
# TODO below may no longer be required as configure checks for -pg
export VERILATOR_TEST_NO_GPROF=1 # Apple Clang has no -pg
# export PATH="/Applications/gtkwave.app/Contents/Resources/bin:$PATH" # fst2vcd
file bin/verilator_bin
file bin/verilator_bin_dbg
md5 bin/verilator_bin
md5 bin/verilator_bin_dbg
stat bin/verilator_bin
stat bin/verilator_bin_dbg
# For some reason, the dbg exe is corrupted by this point ('file' reports
# it as data rather than a Mach-O). Unclear if this is an OS X issue or
# CI's. Remove the file and re-link...
rm bin/verilator_bin_dbg
"$MAKE" -j "$NPROC" -k
elif [ "$CI_OS_NAME" = "freebsd" ]; then
export VERILATOR_TEST_NO_GDB=1 # Disable for now, ideally should run
# TODO below may no longer be required as configure checks for -pg
export VERILATOR_TEST_NO_GPROF=1 # gprof is a bit different on FreeBSD, disable
fi
TEST_REGRESS=test_regress
if [ "$CI_RELOC" == 1 ]; then
# Testing that the installation is relocatable.
"$MAKE" install
mkdir -p "$RELOC_DIR"
mv "$INSTALL_DIR" "$RELOC_DIR/relocated-install"
export VERILATOR_ROOT="$RELOC_DIR/relocated-install/share/verilator"
TEST_REGRESS="$RELOC_DIR/test_regress"
mv test_regress "$TEST_REGRESS"
NODIST="$RELOC_DIR/nodist"
mv nodist "$NODIST"
# Feeling brave?
find . -delete
ls -la .
fi
# Run the specified test
ccache -z
case $TESTS in
dist-vlt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=0/4
;;
dist-vlt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=1/4
;;
dist-vlt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=2/4
;;
dist-vlt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=3/4
;;
vltmt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=0/3
;;
vltmt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=1/3
;;
vltmt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=2/3
;;
coverage-dist)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist"
;;
coverage-vlt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=0/10
;;
coverage-vlt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=1/10
;;
coverage-vlt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=2/10
;;
coverage-vlt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=3/10
;;
coverage-vlt-4)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=4/10
;;
coverage-vlt-5)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=5/10
;;
coverage-vlt-6)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=6/10
;;
coverage-vlt-7)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=7/10
;;
coverage-vlt-8)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=8/10
;;
coverage-vlt-9)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=9/10
;;
coverage-vltmt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=0/10
;;
coverage-vltmt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=1/10
;;
coverage-vltmt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=2/10
;;
coverage-vltmt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=3/10
;;
coverage-vltmt-4)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=4/10
;;
coverage-vltmt-5)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=5/10
;;
coverage-vltmt-6)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=6/10
;;
coverage-vltmt-7)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=7/10
;;
coverage-vltmt-8)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=8/10
;;
coverage-vltmt-9)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=9/10
;;
*)
fatal "Unknown TESTS: $TESTS"
;;
esac
# To see load average (1 minute, 5 minute, 15 minute)
uptime
# 22.04: ccache -s -v
ccache -s
else
##############################################################################
# Unknown build stage
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
fi

169
ci/ci-test.bash Executable file
View File

@ -0,0 +1,169 @@
#!/usr/bin/env bash
# DESCRIPTION: Verilator: CI test job script
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
# Executed in the 'test' stage.
################################################################################
# Destructive to the checkout (runs 'find . -delete'); never run locally
if [ "$GITHUB_ACTIONS" != "true" ]; then
echo "ERROR: $(basename "$0") must only be run in GitHub Actions CI" >&2
exit 1
fi
set -e
set -x
source "$(dirname "$0")/ci-common.bash"
################################################################################
# Parse arguments
OPT_RELOC=
OPT_SUITE=
while [ $# -gt 0 ]; do
case "$1" in
--reloc)
[ $# -ge 2 ] || fatal "--reloc requires an argument"
OPT_RELOC="$2"
shift
;;
--suite)
[ $# -ge 2 ] || fatal "--suite requires an argument"
OPT_SUITE="$2"
shift
;;
*) fatal "Unknown option: '$1'" ;;
esac
shift
done
[ -n "$OPT_SUITE" ] || fatal "--suite is required"
################################################################################
# Run tests
export VERILATOR_TEST_NO_CONTRIBUTORS=1 # Separate workflow check
export VERILATOR_TEST_NO_LINT_PY=1 # Separate workflow check
if [ "$HOST_OS" = "macOS" ]; then
export VERILATOR_TEST_NO_GDB=1 # No working GDB on macOS
fi
TEST_REGRESS=test_regress
if [ -n "$OPT_RELOC" ]; then
# Testing that the installation is relocatable.
"$MAKE" install
# Install prefix, as configured into the Makefile at build time
INSTALL_DIR=$(sed -n 's/^prefix = //p' Makefile)
mkdir -p "$OPT_RELOC"
mv "$INSTALL_DIR" "$OPT_RELOC/relocated-install"
export VERILATOR_ROOT="$OPT_RELOC/relocated-install/share/verilator"
TEST_REGRESS="$OPT_RELOC/test_regress"
mv test_regress "$TEST_REGRESS"
NODIST="$OPT_RELOC/nodist"
mv nodist "$NODIST"
# Delete everything else
find . -delete
ls -la .
fi
# Run the specified suite
ccache -z
TEST_START=$SECONDS
case $OPT_SUITE in
dist-vlt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=0/4
;;
dist-vlt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=1/4
;;
dist-vlt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=2/4
;;
dist-vlt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=3/4
;;
vltmt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=0/3
;;
vltmt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=1/3
;;
vltmt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=2/3
;;
coverage-dist)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist"
;;
coverage-vlt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=0/10
;;
coverage-vlt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=1/10
;;
coverage-vlt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=2/10
;;
coverage-vlt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=3/10
;;
coverage-vlt-4)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=4/10
;;
coverage-vlt-5)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=5/10
;;
coverage-vlt-6)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=6/10
;;
coverage-vlt-7)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=7/10
;;
coverage-vlt-8)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=8/10
;;
coverage-vlt-9)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=9/10
;;
coverage-vltmt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=0/10
;;
coverage-vltmt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=1/10
;;
coverage-vltmt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=2/10
;;
coverage-vltmt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=3/10
;;
coverage-vltmt-4)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=4/10
;;
coverage-vltmt-5)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=5/10
;;
coverage-vltmt-6)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=6/10
;;
coverage-vltmt-7)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=7/10
;;
coverage-vltmt-8)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=8/10
;;
coverage-vltmt-9)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=9/10
;;
*)
fatal "Unknown suite: $OPT_SUITE"
;;
esac
ccache -svv
ccache --evict-older-than "$((SECONDS - TEST_START + 60))s"
ccache -svv
uptime # To see load average

View File

@ -6,19 +6,33 @@
Set-PSDebug -Trace 1
if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
$NPROC = $env:NUMBER_OF_PROCESSORS
# Absolute path for the win_flex_bison install; CMake reads this from the environment
$env:WIN_FLEX_BISON = "$(Resolve-Path ..)/win_flex_bison"
if (-Not (Test-Path $env:WIN_FLEX_BISON/win_bison.exe)) {
git clone --depth 1 https://github.com/lexxmark/winflexbison
cd winflexbison
mkdir build
cd build
cmake .. --install-prefix $PWD/../../../.ccache
cmake --build . --config Release -j 3
cmake --install . --prefix $PWD/../../../.ccache
cmake .. --install-prefix $env:WIN_FLEX_BISON
cmake --build . --config Release -j $NPROC
cmake --install . --prefix $env:WIN_FLEX_BISON
cd ../..
}
# Enter the MSVC developer shell so cl/link are on PATH for the Ninja generator
$VsPath = & "${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe" -latest -products * -property installationPath
Import-Module "$VsPath/Common7/Tools/Microsoft.VisualStudio.DevShell.dll"
Enter-VsDevShell -VsInstallPath $VsPath -SkipAutomaticLocation -DevCmdArguments "-arch=x64 -host_arch=x64"
mkdir build
cd build
cmake .. --install-prefix $PWD/../install
cmake --build . --config Release -j 3
# Ninja saturates all cores; the MSBuild generator only parallelizes across
# projects and Verilator is a single target, so it compiles serially. /Od skips
# optimization: this job only checks that Verilator builds and can verilate an
# example, so an optimized binary is not needed and the codegen time dominates.
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release --install-prefix $PWD/../install "-DCMAKE_CXX_FLAGS_RELEASE=/Od /DNDEBUG"
cmake --build .
cmake --install . --prefix $PWD/../install

View File

@ -7,13 +7,17 @@
Set-PSDebug -Trace 1
$NPROC = $env:NUMBER_OF_PROCESSORS
cd install
$Env:VERILATOR_ROOT=$PWD
cd examples/cmake_tracing_c
mkdir build
cd build
cmake ..
cmake --build . --config Release -j 3
# /Od skips optimization; this only checks the example verilates and builds, so
# an optimized binary is not needed (see ci-win-compile.ps1).
cmake .. "-DCMAKE_CXX_FLAGS_RELEASE=/Od /DNDEBUG"
cmake --build . --config Release -j $NPROC
# TODO put this back in, see issue# 5163
# Release/example.exe

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

View File

@ -12,7 +12,7 @@
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.049 devel],
AC_INIT([Verilator],[5.051 devel],
[https://verilator.org],
[verilator],[https://verilator.org])
@ -589,6 +589,34 @@ m4_foreach([ldflag], [
AC_SUBST(CFG_LDLIBS_THREADS)
AC_SUBST(CFG_LDFLAGS_THREADS_CMAKE)
# Find link flags for runtime VPI library loading (+verilator+vpi+<lib>).
# The model executable must export its VPI symbols so the dlopen'd library can
# resolve them: -rdynamic (GNU ld) or -Wl,-export_dynamic (Darwin); the first the
# linker accepts wins. -ldl provides dlopen/dlsym where it is a separate library.
_MY_LDLIBS_CHECK_SET(CFG_LDFLAGS_DYNAMIC, -rdynamic)
# -Wl,-export_dynamic contains a comma, so probe it directly rather than through
# the _MY_LDLIBS_CHECK_* macros (which re-expand their flag argument unquoted).
if test "$CFG_LDFLAGS_DYNAMIC" = ""; then
ACO_SAVE_LIBS="$LIBS"
LIBS="$LIBS -Wl,-export_dynamic"
AC_MSG_CHECKING([whether $CXX linker accepts -Wl,-export_dynamic])
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([[]])],
[_my_result=yes
if test -s conftest.err; then
if grep -e "-export_dynamic" conftest.err >/dev/null; then
_my_result=no
fi
fi],
[_my_result=no])
AC_MSG_RESULT($_my_result)
LIBS="$ACO_SAVE_LIBS"
if test "$_my_result" = "yes"; then CFG_LDFLAGS_DYNAMIC="-Wl,-export_dynamic"; fi
fi
AC_SUBST(CFG_LDFLAGS_DYNAMIC)
_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_DYNAMIC, -ldl)
AC_SUBST(CFG_LDLIBS_DYNAMIC)
# If 'mold' is installed, use it to link for faster buildtimes
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -fuse-ld=mold)
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_VERILATED, -fuse-ld=mold)

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)
@ -60,6 +61,7 @@ David Turner
Dercury
Diego Roux
Dominick Grochowina
Dragon-Git
Don Williamson
Drew Ranck
Drew Taussig
@ -68,6 +70,7 @@ Edgar E. Iglesias
Eric Mejdrich
Eric Müller
Eric Rippey
Eryk Szpotański
Eunseo Song
Ethan Sifferman
Eyck Jentzsch
@ -145,6 +148,7 @@ Jose Loyola
Josep Sans
Joseph Nwabueze
Josh Redford
Joshua Leahy
Julian Carrier
Julian Daube
Julie Schwartz
@ -158,6 +162,7 @@ Kefa Chen
Keith Colbert
Kevin Kiningham
Kevin Nygaard
Kornel Uriasz
Kritik Bhimani
Krzysztof Bieganski
Krzysztof Boronski
@ -250,10 +255,12 @@ Rowan Goemans
Rupert Swarbrick
Ryan Ziegler
Ryszard Rozak
Saksham Gupta
Samuel Riedel
Sean Cross
Sebastien Van Cauwenberghe
Secturion
Sergey Chusov
Sergey Fedorov
Sergi Granell
Seth Pellegrino
@ -270,12 +277,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 +306,7 @@ Vito Gamberini
Wei-Lun Chiu
William D. Jones
Wilson Snyder
Wolfgang Mayerwieser
Xi Zhang
Yan Xu
Yangyu Chen
@ -326,3 +336,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.
@ -210,9 +211,9 @@ Verilator extends the DPI format to allow using the same scheme to
efficiently add system functions. Use a dollar-sign prefixed system
function name for the import, but note it must be escaped.
.. code-block:: sv
.. code-block::
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
@ -138,6 +139,20 @@ Options:
Displays program version and exits.
.. option:: +verilator+vpi+<library>[:<bootstrap>]
Load a VPI shared library before simulation starts. Only available when the
model was Verilated with :vlopt:`--vpi` and :vlopt:`--main` (or
:vlopt:`--binary`). ``<library>`` is the path to the shared library. If
``:<bootstrap>`` is given, that named no-argument function is called;
otherwise the library's ``vlog_startup_routines`` array (IEEE 1800 38.37.2) is
invoked. May be repeated to load multiple libraries.
Runtime loading is supported on POSIX platforms only (it relies on the
executable exporting its VPI symbols to the loaded library); on Windows the
argument is rejected and the VPI code must instead be statically linked
into the model.
.. option:: +verilator+wno+unsatconstr+<value>
Disable unsatisfied constraint warnings at simulation runtime. When set to

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

@ -477,6 +477,7 @@ Syms
Synopsys
SystemC
SystemVerilog
Szpotanski
Takatsukasa
Tambe
Tarik
@ -537,6 +538,7 @@ Verilog
Vighnesh
Viktor
Vilp
VlRNG
VlWide
Vlip
Vm
@ -684,6 +686,7 @@ countones
cout
covergroup
covergroups
coverpoint
coverpoints
cpp
cppstyle
@ -861,6 +864,7 @@ hx
hyperthreading
hyperthreads
icecream
ico
idmap
ifdef
ifdefed
@ -916,6 +920,7 @@ linters
linux
liu
livelock
liveness
lldb
ln
loc
@ -924,6 +929,7 @@ localparams
localtime
logicals
longint
lookups
lossy
lsb
lubc
@ -943,6 +949,7 @@ misconnected
misconversion
misdetecting
misoptimized
misoptimizing
missized
mk
mno
@ -1204,6 +1211,7 @@ typename
uint
un
unbased
unclocked
uncomment
undef
undefineall

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

@ -89,6 +89,12 @@
# include <unistd.h>
# define _VL_HAVE_GETRLIMIT
#endif
#if VM_VPI
# include <cstring>
# ifndef _WIN32
# include <dlfcn.h> // dlopen
# endif
#endif
#include "verilated_threads.h"
// clang-format on
@ -260,6 +266,58 @@ void VL_WARN_MT(const char* filename, int linenum, const char* hier, const char*
}});
}
//===========================================================================
// Runtime VPI shared library loading (--vpi)
// Load one VPI shared library named by a +verilator+vpi+<lib>[:<bootstrap>] argument.
// 'arg' is the payload after the prefix: either "<lib>" (invoke the library's
// vlog_startup_routines array) or "<lib>:<bootstrap>" (invoke the named bootstrap).
void Verilated::loadVpiLib(const std::string& arg) VL_MT_UNSAFE {
#if VM_VPI
if (arg.empty()) return;
#ifdef _WIN32
VL_FATAL_MT("", 0, "",
"+verilator+vpi+: runtime VPI library loading is not supported on"
" Windows; link the VPI code into the model instead");
#else
using vlog_startup_t = void (*)();
// Split <lib>:<bootstrap> on the last ':'
const std::string::size_type colon_pos = arg.rfind(':');
const bool has_entry = (colon_pos != std::string::npos);
const std::string libpath = has_entry ? arg.substr(0, colon_pos) : arg;
const std::string entry_name = has_entry ? arg.substr(colon_pos + 1) : std::string{};
void* handle = dlopen(libpath.c_str(), RTLD_LAZY);
if (!handle)
// The library path is stable; the dlerror() text is platform-specific, so put it on
// a separate "- " line (test golden files strip "- " lines, keeping output portable).
VL_FATAL_MT(
"", 0, "",
(std::string{"Cannot load VPI library: "} + libpath + "\n- dlerror: " + dlerror())
.c_str());
if (has_entry) {
vlog_startup_t bsp = reinterpret_cast<vlog_startup_t>(dlsym(handle, entry_name.c_str()));
if (!bsp)
VL_FATAL_MT(
"", 0, "",
(std::string{"Cannot find VPI bootstrap '"} + entry_name + "' in: " + libpath)
.c_str());
bsp();
} else {
vlog_startup_t* routinesp
= reinterpret_cast<vlog_startup_t*>(dlsym(handle, "vlog_startup_routines"));
if (!routinesp)
VL_FATAL_MT(
"", 0, "",
(std::string{"Cannot find 'vlog_startup_routines' in: "} + libpath).c_str());
for (int j = 0; routinesp[j]; ++j) routinesp[j]();
}
#endif
#else
// Never reached: the command-line handler only calls this when compiled with --vpi.
(void)arg;
#endif
}
//===========================================================================
// Debug prints
@ -318,6 +376,12 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
}});
}
void VL_FFLUSH_MT() VL_MT_SAFE {
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 {
@ -499,9 +563,10 @@ IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE {
}
IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
if (Verilated::threadContextp()->randReset() == 0) return 0;
const int randReset = Verilated::threadContextp()->randReset();
if (randReset == 0) return 0;
IData data = ~0;
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
if (randReset != 1) { // if 2, randomize
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
data = rng.rand64();
}
@ -510,9 +575,10 @@ IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT
}
QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
if (Verilated::threadContextp()->randReset() == 0) return 0;
const int randReset = Verilated::threadContextp()->randReset();
if (randReset == 0) return 0;
QData data = ~0ULL;
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
if (randReset != 1) { // if 2, randomize
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
data = rng.rand64();
}
@ -522,10 +588,17 @@ QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT
WDataOutP VL_SCOPED_RAND_RESET_W(int obits, WDataOutP outwp, uint64_t scopeHash,
uint64_t salt) VL_MT_UNSAFE {
if (Verilated::threadContextp()->randReset() != 2) { return VL_RAND_RESET_W(obits, outwp); }
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = rng.rand64();
outwp[VL_WORDS_I(obits) - 1] = rng.rand64() & VL_MASK_E(obits);
const int words = VL_WORDS_I(obits);
const int randReset = Verilated::threadContextp()->randReset();
if (randReset == 0) {
VL_MEMSET_ZERO_W(outwp, words);
} else if (randReset == 1) {
VL_MEMSET_ONES_W(outwp, words);
} else {
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
for (int i = 0; i < words; ++i) outwp[i] = rng.rand64();
}
outwp[words - 1] &= VL_MASK_E(obits);
return outwp;
}
@ -550,30 +623,14 @@ WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint64_t sco
}
IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
if (Verilated::threadContextp()->randReset() == 0) return 0;
const int randReset = Verilated::threadContextp()->randReset();
if (randReset == 0) return 0;
IData data = ~0;
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
data = VL_RANDOM_I();
}
if (randReset != 1) data = VL_RANDOM_I(); // if 2, randomize
data &= VL_MASK_I(obits);
return data;
}
QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
if (Verilated::threadContextp()->randReset() == 0) return 0;
QData data = ~0ULL;
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
data = VL_RANDOM_Q();
}
data &= VL_MASK_Q(obits);
return data;
}
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_I(32);
outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_I(32) & VL_MASK_E(obits);
return outwp;
}
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
// Not inlined to speed up compilation of slowpath code
return VL_ZERO_W(obits, outwp);
@ -3028,40 +3085,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};
@ -3480,6 +3594,17 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
// and the run can be reproduced by passing +verilator+seed+<that_value>.
if (u64 == 0) u64 = pickRandomSeed();
randSeed(static_cast<int>(u64));
} else if (commandArgVlString(arg, "+verilator+vpi+", str)) {
// With --vpi, load the requested shared library now. Without --vpi there is
// no VPI runtime, so warn the argument is ignored.
#if VM_VPI
Verilated::loadVpiLib(str);
#else
VL_WARN_MT(
"COMMAND_LINE", 0, "",
("+verilator+vpi+ ignored: simulation was not compiled with --vpi '" + arg + "'")
.c_str()); // LCOV_EXCL_LINE (gcov zeroes this wrapped continuation line)
#endif
} else if (arg == "+verilator+V") {
VerilatedImp::versionDump(); // Someday more info too
VL_FATAL_MT("COMMAND_LINE", 0, "",
@ -3893,6 +4018,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 +4135,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,
@ -1000,6 +1036,9 @@ public:
static void scTraceBeforeElaborationError() VL_ATTR_NORETURN VL_MT_SAFE;
static void stackCheck(QData needSize) VL_MT_UNSAFE;
// Internal: Load a VPI shared library (+verilator+vpi+<lib>[:<bootstrap>])
static void loadVpiLib(const std::string& arg) VL_MT_UNSAFE;
// Internal: Get and set DPI context
static const VerilatedScope* dpiScope() VL_MT_SAFE { return t_s.t_dpiScopep; }
static void dpiScope(const VerilatedScope* scopep) VL_MT_SAFE { t_s.t_dpiScopep = scopep; }

View File

@ -52,6 +52,9 @@ CFG_GCH_IF_CLANG = @CFG_GCH_IF_CLANG@
CFG_LDFLAGS_VERILATED = @CFG_LDFLAGS_VERILATED@
# Linker libraries for multithreading
CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
# Linker flags/libraries for runtime VPI library loading (+verilator+vpi+<lib>)
CFG_LDFLAGS_DYNAMIC = @CFG_LDFLAGS_DYNAMIC@
CFG_LDLIBS_DYNAMIC = @CFG_LDLIBS_DYNAMIC@
######################################################################
# Programs
@ -93,6 +96,7 @@ VK_CPPFLAGS_ALWAYS += \
-DVM_TRACE_FST=$(VM_TRACE_FST) \
-DVM_TRACE_VCD=$(VM_TRACE_VCD) \
-DVM_TRACE_SAIF=$(VM_TRACE_SAIF) \
-DVM_VPI=$(VM_VPI) \
$(CFG_CXXFLAGS_NO_UNUSED) \
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users

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,83 @@
// -*- 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
///
/// Linked when covergroups are present. The coverage-database registration
/// is compiled only with "verilator --coverage".
///
//=============================================================================
#include "verilatedos.h"
#include "verilated_covergroup.h"
#if VM_COVERAGE
#include "verilated_cov.h"
#endif
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;
}
#if VM_COVERAGE
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);
}
}
}
#endif // VM_COVERAGE

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(); }
@ -123,10 +124,6 @@ extern WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint6
/// Random reset a signal of given width (init time only)
extern IData VL_RAND_RESET_I(int obits) VL_MT_SAFE;
/// Random reset a signal of given width (init time only)
extern QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE;
/// Random reset a signal of given width (init time only)
extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE;
/// Zero reset a signal (slow - else use VL_ZERO_W)
extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE;
@ -268,6 +265,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 +899,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;
}
@ -973,14 +1021,10 @@ static inline IData VL_EQ_R(int words, VlQueue<T> q, WDataInP const rwp) VL_PURE
} else if (sizeof(T) == 4) {
for (int i = 0; (i < wordsInQ + 1); ++i) { nequal |= (q.at(wordsInQ - i) ^ rwp[i]); }
} else if (sizeof(T) == 8) {
QData temp = 0;
int qSize = q.size() - 1;
for (int i = 0; (i < qSize); i += 2) {
temp = q.at(qSize - i);
nequal |= (static_cast<QData>(q.at(qSize - i)) >> 32 ^ rwp[i + 1]);
temp = rwp[i + 1];
nequal |= (static_cast<QData>(q.at(qSize - i)) ^ rwp[i]);
temp = rwp[i];
}
}
return (nequal == 0);
@ -990,7 +1034,6 @@ template <std::size_t N_Words>
static inline IData VL_EQ_R(int words, const VlQueue<VlWide<N_Words>>& q,
WDataInP const rwp) VL_PURE {
EData nequal = 0;
const int wordsInQ = q.size() * N_Words;
if ((q.size() * N_Words) != words) { return false; }
int count = 0;
for (int qIndex = q.size() - 1; qIndex >= 0; qIndex--) {
@ -1901,7 +1944,6 @@ static inline void VL_STREAMR_RRI(int lbits, VlQueue<T_Value>& to_q,
const VlQueue<VlWide<N_Words>>& from_q, IData rd) VL_MT_SAFE {
to_q.clear();
VL_CONSTEXPR_CXX17 size_t otherSize = 4 * N_Words;
VL_CONSTEXPR_CXX17 size_t sizeOfThis = sizeof(T_Value);
T_Value temp = 0;
for (auto val : from_q) {

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

@ -337,6 +337,7 @@ public:
~VlProcess() {
if (m_parentp) m_parentp->detach(this);
if (t_currentp == this) t_currentp = m_parentp.get();
}
void attach(VlProcess* childp) { m_children.insert(childp); }
@ -2069,8 +2070,24 @@ struct VlNull final {
operator T*() const {
return nullptr;
}
template <class T>
bool operator==(T* rhs) const {
return !rhs;
}
template <class T>
bool operator==(const T* rhs) const {
return !rhs;
}
};
inline bool operator==(const void* ptr, VlNull) { return !ptr; }
template <class T>
inline bool operator==(T* lhs, VlNull) {
return !lhs;
}
template <class T>
inline bool operator==(const T* lhs, VlNull) {
return !lhs;
}
//===================================================================
// Verilog class reference container

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

@ -20,9 +20,83 @@
#include "V3AstUserAllocator.h"
#include "V3Stats.h"
#include "V3UniqueNames.h"
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 +227,26 @@ 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
VDouble0 m_statLiftedCaseExprs; // Count of purified case expressions
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
V3UniqueNames m_caseTempNames{"__VCase"};
// 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 +266,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 +282,27 @@ class AssertVisitor final : public VNVisitor {
}
VL_UNREACHABLE;
}
static bool isControlled(VAssertDirectiveType directiveType) {
return (static_cast<int>(directiveType)
& (static_cast<int>(VAssertDirectiveType::ASSERT)
| static_cast<int>(VAssertDirectiveType::COVER)
| static_cast<int>(VAssertDirectiveType::ASSUME)));
}
static AstNodeExpr* assertPassOnCond(FileLine* fl, VAssertType type,
VAssertDirectiveType directiveType, bool vacuous) {
if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}};
if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}};
return new AstCExpr{fl, AstCExpr::Pure{},
assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1};
}
static AstNodeExpr* assertFailOnCond(FileLine* fl, VAssertType type,
VAssertDirectiveType directiveType) {
if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}};
if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}};
return new AstCExpr{fl, AstCExpr::Pure{},
assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 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,30 @@ 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
newp->user2(true); // Mark as an assertOn() check
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
newp->user2(true); // Mark as an assertOn() check
return newp;
}
static AstIf* assertCond(const AstNodeCoverOrAssert* nodep, AstNodeExpr* propp,
AstNode* passsp, AstNode* failsp) {
@ -444,6 +545,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 +557,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 +580,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 +595,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
@ -582,8 +698,36 @@ class AssertVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
}
iterateChildren(nodep);
iterateChildren(nodep);
if (nodep->user2()) {
// Combine consecutive assertOn checks if possible
if (AstIf* const backp = VN_CAST(nodep->backp(), If)) {
if (backp->nextp() == nodep //
&& backp->user2() //
&& backp->condp()->sameTree(nodep->condp())) {
++m_statAssertOnCombined;
backp->addThensp(nodep->thensp()->unlinkFrBackWithNext());
nodep->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
}
// Combine nested assertOn checks if possible
if (nodep->thensp() && !nodep->thensp()->nextp() && isEmptyStmt(nodep->elsesp())) {
AstIf* const checkp = VN_CAST(nodep->thensp(), If);
if (checkp //
&& checkp->user2() //
&& checkp->condp()->sameTree(nodep->condp())) {
++m_statAssertOnCombined;
nodep->addThensp(checkp->thensp()->unlinkFrBackWithNext());
VL_DO_DANGLING(pushDeletep(checkp->unlinkFrBack()), checkp);
return;
}
}
return;
}
// Swap assertOn check with single statement 'if' statement to bubble up for combining
@ -611,24 +755,32 @@ 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
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
// We also need to do it before V3Begin, co that pragmas like `unique0` also work correctly
if (!nodep->exprp()->isPure()) {
++m_statLiftedCaseExprs;
FileLine* const fl = nodep->exprp()->fileline();
AstVar* const varp = new AstVar{fl, VVarType::BLOCKTEMP, m_caseTempNames.get(nodep),
nodep->exprp()->dtypep()};
AstNodeExpr* const origp = nodep->exprp()->unlinkFrBack();
nodep->addHereThisAsNext(
new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, origp});
nodep->exprp(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);
}
VIsCached::clearCacheTree();
}
iterateChildren(nodep);
if (!nodep->user1SetOnce()) {
bool has_default = false;
@ -807,8 +959,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 +1069,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 +1121,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 { //
@ -1006,11 +1153,12 @@ class AssertVisitor final : public VNVisitor {
VL_RESTORER(m_modp);
VL_RESTORER(m_modPastNum);
VL_RESTORER(m_modStrobeNum);
VL_RESTORER(m_modExpr2Sen2DelayedAlwaysp);
VL_RESTORER(m_finalp);
VL_RESTORER_CLEAR(m_modExpr2Sen2DelayedAlwaysp);
m_modp = nodep;
m_modPastNum = 0;
m_modStrobeNum = 0;
m_modExpr2Sen2DelayedAlwaysp.clear();
m_finalp = nullptr;
iterateChildren(nodep);
}
void visit(AstNodeProcedure* nodep) override {
@ -1057,6 +1205,9 @@ public:
V3Stats::addStat("Assertions, $past variables", m_statPastVars);
V3Stats::addStat("Assertions, assertOn checks combined", m_statAssertOnCombined);
V3Stats::addStat("Assertions, assertOn checks hoisted", m_statAssertOnHoisted);
V3Stats::addStat("Assertions, lifted impure case expressions", m_statLiftedCaseExprs);
// Rewrites can change purity, e.g. by compiling out assertion statements with --no-assert
VIsCached::clearCacheTree();
}
};

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 and missing variable scopes after potential removals
void rebuildVarScopesAndCache();
};
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:

Some files were not shown because too many files have changed in this diff Show More