Compare commits

..
61 Commits
Author SHA1 Message Date
Wilson Snyder 983a06a16f Merge from master for release. 2026-09-05 08:44:29 -04:00
Wilson Snyder 3d2421f3bf Merge from master for release. 2026-07-01 18:30:39 -04:00
Wilson Snyder 28c006ee8c Merge from master for release. 2026-07-01 18:26:56 -04:00
Wilson Snyder 193b1d2c9f Version bump 2026-07-01 18:17:03 -04:00
Wilson Snyder 147fe4ee9f Commentary: Changes update 2026-07-01 18:15:56 -04:00
Wilson Snyder c233a39052 Merge from master for release. 2026-04-26 01:57:12 -04:00
Wilson Snyder 1264184fbb Revert "Fix recursive constant function in $unit scope (V3Scope.cpp:359) (#7174)" (#7178)
This reverts commit 5d1992ef05.
2026-03-02 15:07:20 -05:00
jalcim 5d1992ef05 Fix recursive constant function in $unit scope (V3Scope.cpp:359) (#7174) 2026-03-02 15:04:28 -05:00
Wilson Snyder f4369ed694 Merge from master for release. 2026-02-28 09:31:29 -05:00
Wilson Snyder 7e7b6d7fb8 Merge from master for release. 2026-01-01 07:32:37 -05:00
Wilson Snyder d915745073 Merge from master for release. 2025-11-02 11:17:12 -05:00
Wilson Snyder 0a9d9db5ae Merge from master for release. 2025-08-30 18:14:20 -04:00
Wilson Snyder 318b1c1a34 Merge from master for release. 2025-07-08 23:17:56 -04:00
Wilson Snyder 0dc93c1d5c Merge from master for release. 2025-04-27 13:04:49 -04:00
Wilson Snyder ac3f30ed6c Merge from master for release. 2025-02-24 18:32:52 -05:00
Wilson Snyder 447ab12cec Merge from master for release. 2025-01-01 09:07:59 -05:00
Wilson Snyder 2cb1a8de73 Merge from master for release. 2024-10-27 10:06:28 -04:00
Wilson Snyder 1d79f5c59b Merge from master for release. 2024-08-21 08:54:42 -04:00
Wilson Snyder 065f36ab56 Merge from master for release. 2024-06-15 08:48:03 -04:00
Wilson Snyder c561fe8baf Merge from master for release. 2024-04-05 06:05:57 -04:00
Wilson Snyder 2b4852048d Commentary - update version date 2024-02-25 07:20:30 -05:00
Wilson Snyder 0144e3ebec Merge from master for release. 2024-02-24 07:48:43 -05:00
Wilson Snyder d410e551f8 Merge from master for release. 2024-01-01 03:45:16 -05:00
Wilson Snyder 344f87abe3 Merge from master for release. 2023-10-31 19:51:59 -04:00
Wilson Snyder 57c816f906 Merge from master for release. (update) 2023-09-16 21:44:39 -04:00
Wilson Snyder 82e1dbe162 Merge from master for release. 2023-09-16 20:43:53 -04:00
Wilson Snyder e6b0bdd4df Merge from master for release. 2023-08-06 14:17:13 -04:00
Wilson Snyder 4f13c4d1bb Merge from master for release. 2023-06-13 19:37:22 -04:00
Wilson Snyder c37ec19751 Merge from master for release. 2023-04-30 07:09:22 -04:00
Wilson Snyder aaaf8e75af Merge from master for release. 2023-03-04 11:29:11 -05:00
Wilson Snyder 8cc31c6cf1 Merge from master for release. 2023-01-22 11:52:52 -05:00
Wilson Snyder 8468af1a20 Merge from master for release. 2022-12-14 22:07:37 -05:00
Wilson Snyder db39d70c75 Merge from master for release. 2022-10-29 17:52:23 -04:00
Wilson Snyder 04fff7ebd8 Merge from master for release. 2022-10-01 08:33:50 -04:00
Wilson Snyder b154af87a1 Merge from master for release. 2022-08-31 19:47:07 -04:00
Wilson Snyder 8b7480806a Merge from master for release. 2022-06-19 15:11:19 -04:00
Wilson Snyder e29f5ad559 Merge from master for release. 2022-06-19 15:08:17 -04:00
Wilson Snyder 4618fd5a43 Merge from master for release. 2022-05-02 22:22:20 -04:00
Wilson Snyder ed979c6aa1 Merge from master for release. 2022-03-12 13:14:48 -05:00
Wilson Snyder 141c5da3f9 Merge from master for release. 2021-12-05 11:15:32 -05:00
Wilson Snyder 0185ee5df3 Merge from master for release. 2021-10-17 14:46:03 -04:00
Wilson Snyder 27c08a65e1 Merge from master for release. 2021-09-01 21:02:51 -04:00
Wilson Snyder de408a5eb7 Merge from master for release. 2021-07-07 20:48:51 -04:00
Wilson Snyder fa3c072dd1 Merge from master for release. 2021-06-12 13:01:37 -04:00
Wilson Snyder aac4b7090a Merge from master for release. 2021-04-24 10:40:13 -04:00
Wilson Snyder f0d664530d Merge from master for release. 2021-03-12 15:48:45 -05:00
Wilson Snyder 702140bf93 Merge from master for release. 2021-02-25 21:49:42 -05:00
Wilson Snyder 2075db3db0 Merge from master for release. 2021-01-10 13:35:45 -05:00
Wilson Snyder b7fec513cf Merge from master for release. 2020-12-02 20:13:31 -05:00
Wilson Snyder 78e7758022 Merge from master for release. 2020-11-14 09:58:28 -05:00
Wilson Snyder 818318ddd5 Merge from master for release. 2020-10-15 08:25:44 -04:00
Wilson Snyder 39eea781bc Merge from master for release. 2020-09-07 09:38:50 -04:00
Wilson Snyder c4f7bd6a04 'Merge from master for release.' 2020-08-15 09:18:29 -04:00
Wilson Snyder dd03d0f3c9 'Merge from master for release.' 2020-07-11 09:16:56 -04:00
Wilson Snyder 369ce6af2c 'Merge from master for release.' 2020-06-06 07:49:57 -04:00
Wilson Snyder f83817510d 'Merge from master for release.' 2020-05-03 11:15:21 -04:00
Wilson Snyder bfcd779cfb 'Merge from master for release.' 2020-04-04 08:50:02 -04:00
Wilson Snyder e69c380205 'Merge from master for release.' 2020-03-08 08:39:29 -04:00
Wilson Snyder 61e14aa827 'Merge from master for release.' 2020-02-08 09:15:27 -05:00
Wilson Snyder 0c6c83e278 Merge from master for release. 2020-01-11 07:12:09 -05:00
Wilson Snyder 6f52208b68 Fix clang warning. 2019-12-09 18:28:56 -05:00
1300 changed files with 23003 additions and 69341 deletions
@@ -1,62 +0,0 @@
---
# DESCRIPTION: Github actions composite action
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: Update PR label
description: >-
Add or remove a label on a pull request. Does nothing unless the pull
request is still open and still at the commit the result is for, so that a
result superseded by a push does not label/unlabel the work that replaced it.
inputs:
token:
description: "Token to label with"
required: true
pr-number:
description: "Number of the pull request"
required: true
head-sha:
description: "Commit the result is for"
required: true
label:
description: "Label to set"
required: true
set:
description: "'true' to add the label, 'false' to remove it"
required: true
runs:
using: composite
steps:
- name: Set the label
shell: bash
env:
GH_TOKEN: ${{ inputs.token }}
REPO: ${{ github.repository }}
PR: ${{ inputs.pr-number }}
SHA: ${{ inputs.head-sha }}
LABEL: ${{ inputs.label }}
SET: ${{ inputs.set }}
run: |-
# The state and head of the pull request as they are now, and whether
# it carries the label already
read -r STATE HEAD LABELLED < <(gh pr view "${PR}" \
--repo "${REPO}" \
--json state,headRefOid,labels \
--jq '"\(.state) \(.headRefOid) \(any(.labels[]; .name == env.LABEL))"')
# Only act on a change, so a run does not reapply what is already set
if [ "${STATE}" != OPEN ]; then
# Only label open PRs
echo "PR #${PR} is ${STATE}"
elif [ "${HEAD}" != "${SHA}" ]; then
# Pushed to since, so this result is stale and a new run will decide
echo "PR #${PR} has moved on from ${SHA}"
elif [ "${SET}" = true ] && [ "${LABELLED}" = false ]; then
echo "Adding '${LABEL}' to PR #${PR}"
gh pr edit "${PR}" --repo "${REPO}" --add-label "${LABEL}"
elif [ "${SET}" = false ] && [ "${LABELLED}" = true ]; then
echo "Removing '${LABEL}' from PR #${PR}"
gh pr edit "${PR}" --repo "${REPO}" --remove-label "${LABEL}"
else
echo "No change to PR #${PR}"
fi
@@ -2,14 +2,13 @@
# DESCRIPTION: Github actions config # DESCRIPTION: Github actions config
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: Regression name: Regressions
on: on:
push: push:
branches-ignore: branches-ignore:
- 'dependabot/**' # Avoid duplicates: only run the PR, not the push - 'dependabot/**' # Avoid duplicates: only run the PR, not the push
pull_request: pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
workflow_dispatch: workflow_dispatch:
schedule: schedule:
- cron: '0 0 * * 0' # weekly - cron: '0 0 * * 0' # weekly
@@ -23,77 +22,14 @@ defaults:
working-directory: repo working-directory: repo
concurrency: concurrency:
# At most 1 job per branch. Auto cancel on pull requests and on all forks. # At most 1 job per branch. Auto cancel on pull requests and on all forks
# Of the pull request events, only one that starts a run, or that removes the group: ${{ github.workflow }}-${{ github.ref }}
# 'pr: regression' label to cancel one, shares the group. The rest are
# skipped (a change of another label, or a payload that does not carry the
# label, like an 'opened' delivered after it was added), and a skipped run
# must not cancel the one in progress, so they are given a unique group.
# This way adding the label starts a run, removing it cancels the in-progress
# run, and anything else has no effect. Note such an event still creates a
# run of this workflow, which is skipped, and would then hide the one in
# progress on the pull request. 'pr-run-cleanup.yml' deletes those.
# Key pull request runs on the number: 'github.ref' is the base branch once
# the pull request is closed, so unlabel events would cancel base runs.
# NOTE: The last part of the group expression must be kept in sync with the
# 'start' job condition below: it maps to 'shared' exactly those events that
# satisfy that condition, plus the 'unlabeled' event of the gating label.
group: |-
${{
github.workflow
}}-${{
(github.event_name == 'pull_request')
&& format('pr-{0}', github.event.number) || github.ref
}}-${{
(github.event_name == 'pull_request'
&& (((github.event.action == 'labeled' || github.event.action == 'unlabeled')
&& github.event.label.name != 'pr: regression')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& !contains(github.event.pull_request.labels.*.name, 'pr: regression'))))
&& github.run_id || 'shared'
}}
cancel-in-progress: ${{ github.event_name == 'pull_request' || github.repository != 'verilator/verilator' }} cancel-in-progress: ${{ github.event_name == 'pull_request' || github.repository != 'verilator/verilator' }}
jobs: jobs:
start:
name: Start
# Only run pull request jobs if labelled as needing a regression run.
# Always run push, workflow dispatch and scheduled jobs
if: |
(github.event_name == 'pull_request'
&& ((github.event.action == 'labeled'
&& github.event.label.name == 'pr: regression')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& contains(github.event.pull_request.labels.*.name, 'pr: regression')))) ||
(github.event_name == 'push') ||
(github.event_name == 'workflow_dispatch') ||
(github.event_name == 'schedule')
runs-on: ubuntu-slim
defaults:
run:
working-directory: . # The workflow default is not checked out here
steps:
- name: Enabled
run: echo "Regression is enabled for this run"
- name: Save the pull request number
if: ${{ github.event_name == 'pull_request' }}
run: echo "${{ github.event.number }}" > pr-number.txt
- name: Upload the pull request number
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: pr-number
path: pr-number.txt
overwrite: true
build-2604-gcc: build-2604-gcc:
name: Build | 26.04 | gcc name: Build | 26.04 | gcc
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: gcc cc: gcc
@@ -102,7 +38,6 @@ jobs:
build-2604-clang: build-2604-clang:
name: Build | 26.04 | clang name: Build | 26.04 | clang
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: clang cc: clang
@@ -112,7 +47,6 @@ jobs:
build-2404-gcc: build-2404-gcc:
name: Build | 24.04 | gcc name: Build | 24.04 | gcc
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: gcc cc: gcc
@@ -121,7 +55,6 @@ jobs:
build-2404-clang: build-2404-clang:
name: Build | 24.04 | clang name: Build | 24.04 | clang
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: clang cc: clang
@@ -130,25 +63,22 @@ jobs:
build-2204-gcc: build-2204-gcc:
name: Build | 22.04 | gcc name: Build | 22.04 | gcc
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: gcc cc: gcc
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
sha: ${{ github.sha }} sha: ${{ github.sha }}
build-macos-26-clang: build-macos-15-clang:
name: Build | macos-26 | clang name: Build | macos-15 | clang
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: clang cc: clang
runs-on: macos-26 runs-on: macos-15
sha: ${{ github.sha }} sha: ${{ github.sha }}
build-windows: build-windows:
name: Build | windows-2025-vs2026 | msvc name: Build | windows-2025-vs2026 | msvc
needs: start
runs-on: windows-2025-vs2026 runs-on: windows-2025-vs2026
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
@@ -237,29 +167,4 @@ jobs:
lint-py: lint-py:
name: Lint Python name: Lint Python
needs: start
uses: ./.github/workflows/reusable-lint-py.yml uses: ./.github/workflows/reusable-lint-py.yml
passed:
name: Regression tests passed # This job is used as a required status check on PRs
if: ${{ !cancelled() && needs.start.result != 'skipped' }}
needs:
- start
- build-2604-gcc
- build-2604-clang
- build-2404-gcc
- build-2404-clang
- build-2204-gcc
- build-macos-26-clang
- build-windows
- test-2604-gcc
- test-2604-clang
- test-2404-gcc
- test-2404-clang
- test-2204-gcc
- lint-py
runs-on: ubuntu-slim
steps:
- name: Check all needed jobs succeeded
working-directory: .
run: jq -e 'all(.result == "success")' <<< '${{ toJSON(needs) }}'
+12 -71
View File
@@ -20,77 +20,28 @@ defaults:
shell: bash shell: bash
concurrency: concurrency:
# At most 1 job per branch. Auto cancel all but scheduled jobs. # At most 1 job per branch. Auto cancel all but scheduled jobs
# Of the pull request events, only one that starts a run, or that removes the group: ${{ github.workflow }}-${{ github.ref }}
# 'pr: dev-coverage' label to cancel one, shares the group. The rest are
# skipped (a change of another label, or a payload that does not carry the
# label, like an 'opened' delivered after it was added), and a skipped run
# must not cancel the one in progress, so they are given a unique group.
# This way adding the label starts a run, removing it cancels the in-progress
# run, and anything else has no effect. Note such an event still creates a
# run of this workflow, which is skipped, and would then hide the one in
# progress on the pull request. 'pr-run-cleanup.yml' deletes those.
# Key pull request runs on the number: 'github.ref' is the base branch once
# the pull request is closed, so unlabel events would cancel base runs.
# NOTE: The last part of the group expression must be kept in sync with the
# 'start' job condition below: it maps to 'shared' exactly those events that
# satisfy that condition, plus the 'unlabeled' event of the gating label.
group: |-
${{
github.workflow
}}-${{
(github.event_name == 'pull_request')
&& format('pr-{0}', github.event.number) || github.ref
}}-${{
(github.event_name == 'pull_request'
&& (((github.event.action == 'labeled' || github.event.action == 'unlabeled')
&& github.event.label.name != 'pr: dev-coverage')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& !contains(github.event.pull_request.labels.*.name, 'pr: dev-coverage'))))
&& github.run_id || 'shared'
}}
cancel-in-progress: ${{ github.event_name != 'schedule' }} cancel-in-progress: ${{ github.event_name != 'schedule' }}
jobs: jobs:
start: build:
name: Start name: Build
# Only run scheduled jobs if explicitly enabled for that repo (e.g.: not on forks) # Only run scheduled jobs if explicitly enabled for that repo (e.g.: not on forks)
# Only run pull request jobs if labelled as needing a coverage run # Only run pull request jobs if labelled as needing an coverage run
# Always run workflow dispatch jobs # Always run workflow dispatch jobs
if: | if: |
(github.event_name == 'schedule' (github.event_name == 'schedule'
&& vars.ENABLE_SCHEDULED_JOBS == 'true') || && vars.ENABLE_SCHEDULED_JOBS == 'true') ||
(github.event_name == 'pull_request' (github.event_name == 'pull_request'
&& ((github.event.action == 'labeled' && contains(github.event.pull_request.labels.*.name, 'pr: dev-coverage')) ||
&& github.event.label.name == 'pr: dev-coverage')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& contains(github.event.pull_request.labels.*.name, 'pr: dev-coverage')))) ||
(github.event_name == 'workflow_dispatch') (github.event_name == 'workflow_dispatch')
runs-on: ubuntu-slim
steps:
- name: Save the pull request number
if: ${{ github.event_name == 'pull_request' }}
run: echo "${{ github.event.number }}" > pr-number.txt
- name: Upload the pull request number
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: pr-number
path: pr-number.txt
overwrite: true
build:
name: Build
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: gcc cc: gcc
dev-gcov: true dev-gcov: true
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
# For pull requests, build the head of the pull request branch, not the # For pull requests, build the head of the pull request branch, not the
# merge commit, otherwise patch coverage would include the changes # merge commit, otherwise patch coverage would include the changes
# between the root of the pull request and the target branch # between the root of the pull request and the target branch
@@ -104,7 +55,7 @@ jobs:
archive: ${{ needs.build.outputs.archive }} archive: ${{ needs.build.outputs.archive }}
cc: gcc cc: gcc
dev-gcov: true dev-gcov: true
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
suite: ${{ matrix.test }}${{ matrix.num }} suite: ${{ matrix.test }}${{ matrix.num }}
strategy: strategy:
fail-fast: false fail-fast: false
@@ -118,7 +69,7 @@ jobs:
name: Publish results to codecov.io name: Publish results to codecov.io
needs: test needs: test
if: ${{ contains(needs.*.result, 'success') && !cancelled() }} if: ${{ contains(needs.*.result, 'success') && !cancelled() }}
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
@@ -137,7 +88,7 @@ jobs:
find obj_coverage -type f | paste -sd, | sed "s/^/files=/" >> "$GITHUB_OUTPUT" find obj_coverage -type f | paste -sd, | sed "s/^/files=/" >> "$GITHUB_OUTPUT"
- name: Upload to codecov.io - name: Upload to codecov.io
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7 uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7
with: with:
disable_file_fixes: true disable_file_fixes: true
disable_search: true disable_search: true
@@ -151,7 +102,7 @@ jobs:
name: Prepare HTML report name: Prepare HTML report
needs: [build, test] needs: [build, test]
if: ${{ contains(needs.*.result, 'success') && !cancelled() }} if: ${{ contains(needs.*.result, 'success') && !cancelled() }}
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
steps: steps:
- name: Download repository archive - name: Download repository archive
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
@@ -199,16 +150,6 @@ jobs:
path: report-artifact path: report-artifact
name: coverage-report name: coverage-report
- name: Upload coverage status
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
path: repo/coverage-status.txt
name: coverage-status
overwrite: true
# Not written if the coverage could not be read, see the script above
if-no-files-found: ignore
- name: Upload PR notification - name: Upload PR notification
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
uses: ./repo/.github/actions/upload-pr-notification uses: ./repo/.github/actions/upload-pr-notification
@@ -222,7 +163,7 @@ jobs:
name: Create issue on failure name: Create issue on failure
needs: [publish-codecov, prepare-report] needs: [publish-codecov, prepare-report]
if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && failure() && !cancelled() }} if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && failure() && !cancelled() }}
runs-on: ubuntu-slim runs-on: ubuntu-24.04
steps: steps:
# Creating issues requires elevated privilege # Creating issues requires elevated privilege
- name: Generate access token - name: Generate access token
+3 -3
View File
@@ -64,10 +64,10 @@ jobs:
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }} type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4 uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4 uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4
with: with:
buildkitd-flags: --debug buildkitd-flags: --debug
@@ -78,7 +78,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and Push to Docker - name: Build and Push to Docker
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7 uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
with: with:
context: ${{ env.build_context }} context: ${{ env.build_context }}
+4 -33
View File
@@ -15,36 +15,16 @@ defaults:
run: run:
working-directory: repo working-directory: repo
concurrency:
# At most 1 run per branch: the job pushes what 'make format' changed, so
# concurrent runs on the same branch race on that push. Cancel the run in
# progress, as the newer one formats a tree that already holds its commits.
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
format: format:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
name: Ubuntu 24.04 | format name: Ubuntu 24.04 | format
steps: steps:
# On the main repository, push as the Verilator CI app.
# Forks do not have the app, so fall back to GITHUB_TOKEN there.
- name: Generate access token
id: generate-token
if: ${{ github.repository == 'verilator/verilator' }}
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
owner: verilator
repositories: verilator
permission-contents: write
- name: Checkout - name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with: with:
path: repo path: repo
token: ${{ steps.generate-token.outputs.token || secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- name: Install packages for build - name: Install packages for build
uses: ./repo/.github/actions/install-deps uses: ./repo/.github/actions/install-deps
@@ -52,18 +32,9 @@ jobs:
stage: format stage: format
- name: Configure git identity - name: Configure git identity
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APP_SLUG: ${{ steps.generate-token.outputs.app-slug }}
run: | run: |
if [ -n "$APP_SLUG" ]; then git config --global user.email "[email protected]"
user_id=$(gh api "/users/${APP_SLUG}[bot]" --jq .id) git config --global user.name "github action"
git config --global user.email "${user_id}+${APP_SLUG}[bot]@users.noreply.github.com"
git config --global user.name "${APP_SLUG}[bot]"
else
git config --global user.email "[email protected]"
git config --global user.name "github action"
fi
- name: Configure - name: Configure
run: | run: |
@@ -84,6 +55,6 @@ jobs:
- name: Push - name: Push
run: |- run: |-
if [ -n "$(git status --porcelain)" ]; then if [ -n "$(git status --porcelain)" ]; then
git commit . -m "Apply 'make format'" && git commit . -m "Apply 'make format' [ci skip]" &&
git push origin git push origin
fi fi
+2 -2
View File
@@ -69,11 +69,11 @@ jobs:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deploy-1 id: deploy-1
continue-on-error: true continue-on-error: true
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5 uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
- name: Deploy to GitHub Pages (retry) - name: Deploy to GitHub Pages (retry)
id: deploy-2 id: deploy-2
if: steps.deploy-1.outcome == 'failure' if: steps.deploy-1.outcome == 'failure'
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5 uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
notify: notify:
name: Notify name: Notify
-281
View File
@@ -1,281 +0,0 @@
---
# DESCRIPTION: Github actions config
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# Drives the automation of a pull request.
name: Maintenance - PR automation
on:
pull_request_target: # 'pull_request' can have no write permissions
types: [opened, synchronize, reopened, closed]
workflow_run: # To react to the result of a workflow on a pull request
workflows: ["Code coverage", "RTLMeter", "Regression"]
types: [completed]
push: # A pull request can start conflicting when its base branch moves
branches: [master]
permissions:
contents: read # To check out the local action, everything else uses the CI app token
defaults:
run:
shell: bash
# One job per event source, to avoid startup churn. Use separate gated steps for actions
jobs:
pr-event:
name: PR event
if: |
github.repository == 'verilator/verilator'
&& github.event_name == 'pull_request_target'
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
# latter do not create workflow runs, so the labels would drive nothing
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-pull-requests: write
- name: "Add 'pr: regression' label on open"
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
if: ${{ github.event.action == 'opened' || github.event.action == 'reopened' }}
run: |-
gh pr edit "${{ github.event.number }}" \
--repo "${{ github.repository }}" \
--add-label 'pr: regression'
- name: "Optionally add 'pr: dev-coverage' label on open"
if: ${{ github.event.action == 'opened' || github.event.action == 'reopened' }}
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# Grab changed files
FILES=$(gh api --paginate \
"repos/${{ github.repository }}/pulls/${{ github.event.number }}/files" \
--jq '.[].filename')
echo "Files changed:"
echo "${FILES}"
# Add label if src or include chagned
if grep -q -E '^(src|include)/' <<< "${FILES}"; then
gh pr edit "${{ github.event.number }}" \
--repo "${{ github.repository }}" \
--add-label 'pr: dev-coverage'
fi
- name: "Remove all 'pr*' labels on close"
if: ${{ github.event.action == 'closed' }}
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# Filter in 'jq', so an empty result is not an error, as it is with 'grep'
LABELS=$(gh api \
"repos/${{ github.repository }}/issues/${{ github.event.number }}/labels?per_page=100" \
--jq '[.[].name | select(startswith("pr"))] | join(",")')
if [ -n "${LABELS}" ]; then
echo "Removing labels: ${LABELS}"
gh pr edit "${{ github.event.number }}" \
--repo "${{ github.repository }}" \
--remove-label "${LABELS}"
fi
conflicting:
name: Conflict check
# A pull request can start conflicting when its own head moves, or when the
# base branch moves under it, so sweep the open pull requests on both.
if: |
github.repository == 'verilator/verilator'
&& (github.event_name == 'push'
|| (github.event_name == 'pull_request_target'
&& github.event.action == 'synchronize'))
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
# latter do not create workflow runs, so the labels would drive nothing
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-pull-requests: write
- name: "Update the 'pr-blocked: needs-rebase' label"
uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # 3.1.0
with:
dirtyLabel: "pr-blocked: needs-rebase"
repoToken: ${{ steps.generate-token.outputs.token }}
regression-complete:
name: Regression complete
if: |
github.repository == 'verilator/verilator'
&& github.event_name == 'workflow_run'
&& github.event.workflow_run.name == 'Regression'
&& github.event.workflow_run.event == 'pull_request'
&& (github.event.workflow_run.conclusion == 'success'
|| github.event.workflow_run.conclusion == 'failure')
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
# latter do not create workflow runs, so the labels would drive nothing
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-actions: read
permission-pull-requests: write
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
sparse-checkout: .github/actions/update-pr-label
- name: Get the pull request number
id: pr
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# The run tells us which pull request it was for
if ! gh run download "${{ github.event.workflow_run.id }}" --name pr-number; then
echo "Run has no 'pr-number' artifact"
exit 0
fi
echo "number=$(cat pr-number.txt)" >> "$GITHUB_OUTPUT"
- name: "Update the 'pr-blocked: fix-regression' label"
if: ${{ steps.pr.outputs.number }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: fix-regression"
set: ${{ github.event.workflow_run.conclusion == 'failure' }}
rtlmeter-complete:
name: RTLMeter complete
if: |
github.repository == 'verilator/verilator'
&& github.event_name == 'workflow_run'
&& github.event.workflow_run.name == 'RTLMeter'
&& github.event.workflow_run.event == 'pull_request'
&& (github.event.workflow_run.conclusion == 'success'
|| github.event.workflow_run.conclusion == 'failure')
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
# latter do not create workflow runs, so the labels would drive nothing
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-actions: read
permission-pull-requests: write
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
sparse-checkout: .github/actions/update-pr-label
- name: Get the pull request number
id: pr
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# The run tells us which pull request it was for
if ! gh run download "${{ github.event.workflow_run.id }}" --name pr-number; then
echo "Run has no 'pr-number' artifact"
exit 0
fi
echo "number=$(cat pr-number.txt)" >> "$GITHUB_OUTPUT"
- name: "Update the 'pr-blocked: fix-rtlmeter' label"
if: ${{ steps.pr.outputs.number }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: fix-rtlmeter"
set: ${{ github.event.workflow_run.conclusion == 'failure' }}
coverage-complete:
name: Coverage complete
if: |
github.repository == 'verilator/verilator'
&& github.event_name == 'workflow_run'
&& github.event.workflow_run.name == 'Code coverage'
&& github.event.workflow_run.event == 'pull_request'
&& (github.event.workflow_run.conclusion == 'success'
|| github.event.workflow_run.conclusion == 'failure')
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
# latter do not create workflow runs, so the labels would drive nothing
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-actions: read
permission-pull-requests: write
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
sparse-checkout: .github/actions/update-pr-label
- name: Get the pull request number
id: pr
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# The run tells us which pull request it was for
if ! gh run download "${{ github.event.workflow_run.id }}" --name pr-number; then
echo "Run has no 'pr-number' artifact"
exit 0
fi
echo "number=$(cat pr-number.txt)" >> "$GITHUB_OUTPUT"
- name: "Update the 'pr-blocked: fix-dev-coverage' label"
if: ${{ steps.pr.outputs.number }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: fix-dev-coverage"
set: ${{ github.event.workflow_run.conclusion == 'failure' }}
- name: Get the line coverage
id: coverage
if: ${{ github.event.workflow_run.conclusion == 'success' }}
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# Whether every line the patch touches is covered
if ! gh run download "${{ github.event.workflow_run.id }}" --name coverage-status; then
echo "Run has no 'coverage-status' artifact"
exit 0
fi
echo "covered=$(cat coverage-status.txt)" >> "$GITHUB_OUTPUT"
- name: "Update the 'pr-blocked: improve-coverage' label"
if: ${{ steps.pr.outputs.number && steps.coverage.outputs.covered }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: improve-coverage"
set: ${{ steps.coverage.outputs.covered != 'true' }}
-52
View File
@@ -1,52 +0,0 @@
---
# DESCRIPTION: Github actions config
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# Fails while a pull request carries a 'pr-blocked:*' label, so that it can be
# used as a required status check to hold up the merge. The labels themselves
# are set by hand, or by 'pr-automation.yml'.
#
# Note this runs from the pull request's own merge commit, so a pull request
# can change the check itself, but any such change shows up in its review.
#
# It also runs on a push, and not only on a label change, as a check run is
# reported against the head commit, and so every head needs one of its own.
name: Maintenance - PR blocked
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
permissions:
pull-requests: read
defaults:
run:
shell: bash
concurrency:
group: ${{ github.workflow }}-${{ github.event.number }}
cancel-in-progress: true
jobs:
not-blocked:
name: Not blocked
runs-on: ubuntu-slim
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: "Check for 'pr-blocked:*' labels"
run: |-
# Note these are the labels the pull request has now, and not the ones
# in 'github.event', which is a snapshot that can be stale when
# several are changed in one go
BLOCKED=$(gh pr view "${{ github.event.number }}" \
--repo "${{ github.repository }}" \
--json labels \
--jq '[.labels[].name | select(startswith("pr-blocked:"))] | join(", ")')
if [ -n "${BLOCKED}" ]; then
echo "::error::${BLOCKED}"
exit 1
fi
echo "Not blocked"
+28
View File
@@ -0,0 +1,28 @@
---
# Build and push verilator docker image when tags are pushed to the repository.
# The following variable(s) must be configured in the github repository:
# DOCKER_HUB_NAMESPACE: docker hub namespace.
# The following secrets must be configured in the github repository:
# DOCKER_HUB_USER: user name for logging into docker hub
# DOCKER_HUB_ACCESS_TOKEN: docker hub access token.
name: Maintainance - PR conflict check
on:
workflow_dispatch:
push:
pull_request_target:
types: [synchronize]
permissions: {}
jobs:
main:
runs-on: ubuntu-26.04
permissions:
contents: read
pull-requests: write
steps:
- name: check if prs are dirty
uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # 3.1.0
with:
dirtyLabel: "pr-blocked: needs-rebase"
repoToken: "${{ secrets.GITHUB_TOKEN }}"
-42
View File
@@ -1,42 +0,0 @@
---
# DESCRIPTION: Github actions config
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# Deletes the skipped runs of the label gated workflows on pull requests.
#
# GitHub shows only the latest run of a workflow on a pull request, so a run
# that was skipped hides an earlier run that is actually still in progress, and
# the pull request then reads as though that workflow had finished and
# everything is green. Every label change creates a run of every workflow that
# triggers on label events, and label events cannot be filtered by label name
# under 'on', so these runs cannot be prevented. Deleting them restores the
# in-progress run in the UI.
name: Maintenance - PR run cleanup
on:
workflow_run:
workflows: ["Code coverage", "Regression", "RTLMeter"]
types: [completed]
permissions:
actions: write
defaults:
run:
shell: bash
jobs:
delete-skipped-run:
name: Delete skipped run
if: |
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'skipped'
runs-on: ubuntu-slim
steps:
- name: Delete run
env:
GH_TOKEN: ${{ github.token }}
run: |-
echo "Deleting skipped '${{ github.event.workflow_run.name }}' run #${{ github.event.workflow_run.run_number }}"
gh api --method DELETE "repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}"
+1 -2
View File
@@ -36,8 +36,7 @@ jobs:
- name: Set up Python venv - name: Set up Python venv
uses: ./repo/.github/actions/setup-venv uses: ./repo/.github/actions/setup-venv
with: with:
# Check the event too: 'github.ref' is the base branch on a closed pull request save: ${{ github.ref == 'refs/heads/master' }}
save: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/master' }}
- name: Lint - name: Lint
run: |- run: |-
+2 -3
View File
@@ -92,8 +92,7 @@ jobs:
- name: Set up Python venv - name: Set up Python venv
uses: ./repo/.github/actions/setup-venv uses: ./repo/.github/actions/setup-venv
with: with:
# Check the event too: 'github.ref' is the base branch on a closed pull request save: ${{ github.ref == 'refs/heads/master' }}
save: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/master' }}
- name: Test - name: Test
id: run-test id: run-test
@@ -126,7 +125,7 @@ jobs:
path: ${{ env.CCACHE_DIR }} path: ${{ env.CCACHE_DIR }}
key: ${{ steps.ccache.outputs.key }} key: ${{ steps.ccache.outputs.key }}
# Keep master's cache around longer; PR/branch caches churn faster # Keep master's cache around longer; PR/branch caches churn faster
retention-days: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/master' && 7 || 3 }} retention-days: ${{ github.ref == 'refs/heads/master' && 7 || 3 }}
- name: Fail job if a test failed - name: Fail job if a test failed
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }} if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
+15 -59
View File
@@ -22,36 +22,8 @@ defaults:
shell: bash shell: bash
concurrency: concurrency:
# At most 1 job per branch. Auto cancel all but scheduled jobs. # At most 1 job per branch. Auto cancel all but scheduled jobs
# Of the pull request events, only one that starts a run, or that removes the group: ${{ github.workflow }}-${{ github.ref }}
# 'pr: rtlmeter' label to cancel one, shares the group. The rest are
# skipped (a change of another label, or a payload that does not carry the
# label, like an 'opened' delivered after it was added), and a skipped run
# must not cancel the one in progress, so they are given a unique group.
# This way adding the label starts a run, removing it cancels the in-progress
# run, and anything else has no effect. Note such an event still creates a
# run of this workflow, which is skipped, and would then hide the one in
# progress on the pull request. 'pr-run-cleanup.yml' deletes those.
# Key pull request runs on the number: 'github.ref' is the base branch once
# the pull request is closed, so unlabel events would cancel base runs.
# NOTE: The last part of the group expression must be kept in sync with the
# 'start' job condition below: it maps to 'shared' exactly those events that
# satisfy that condition, plus the 'unlabeled' event of the gating label.
group: |-
${{
github.workflow
}}-${{
(github.event_name == 'pull_request')
&& format('pr-{0}', github.event.number) || github.ref
}}-${{
(github.event_name == 'pull_request'
&& (((github.event.action == 'labeled' || github.event.action == 'unlabeled')
&& github.event.label.name != 'pr: rtlmeter')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& !contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter'))))
&& github.run_id || 'shared'
}}
cancel-in-progress: ${{ github.event_name != 'schedule' }} cancel-in-progress: ${{ github.event_name != 'schedule' }}
jobs: jobs:
@@ -64,30 +36,14 @@ jobs:
(github.event_name == 'schedule' (github.event_name == 'schedule'
&& vars.ENABLE_SCHEDULED_JOBS == 'true') || && vars.ENABLE_SCHEDULED_JOBS == 'true') ||
(github.event_name == 'pull_request' (github.event_name == 'pull_request'
&& ((github.event.action == 'labeled' && contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter')) ||
&& github.event.label.name == 'pr: rtlmeter')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter')))) ||
(github.event_name == 'workflow_dispatch') || (github.event_name == 'workflow_dispatch') ||
(github.event_name == 'push') (github.event_name == 'push')
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
outputs: outputs:
old-sha: ${{ steps.start.outputs.old-sha }} old-sha: ${{ steps.start.outputs.old-sha }}
cases: ${{ steps.cases.outputs.cases }} cases: ${{ steps.cases.outputs.cases }}
steps: steps:
- name: Save the pull request number
if: ${{ github.event_name == 'pull_request' }}
run: echo "${{ github.event.number }}" > pr-number.txt
- name: Upload the pull request number
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: pr-number
path: pr-number.txt
overwrite: true
- name: Checkout - name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
@@ -114,7 +70,7 @@ jobs:
cc: gcc cc: gcc
ccwarn: false ccwarn: false
install: true install: true
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
sha: ${{ github.sha }} sha: ${{ github.sha }}
build-clang-new: build-clang-new:
@@ -125,7 +81,7 @@ jobs:
cc: clang cc: clang
ccwarn: false ccwarn: false
install: true install: true
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
sha: ${{ github.sha }} sha: ${{ github.sha }}
build-gcc-old: build-gcc-old:
@@ -137,7 +93,7 @@ jobs:
cc: gcc cc: gcc
ccwarn: false ccwarn: false
install: true install: true
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
sha: ${{ needs.start.outputs.old-sha }} sha: ${{ needs.start.outputs.old-sha }}
build-clang-old: build-clang-old:
@@ -149,7 +105,7 @@ jobs:
cc: clang cc: clang
ccwarn: false ccwarn: false
install: true install: true
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
sha: ${{ needs.start.outputs.old-sha }} sha: ${{ needs.start.outputs.old-sha }}
run-gcc: run-gcc:
@@ -165,7 +121,7 @@ jobs:
uses: ./.github/workflows/reusable-rtlmeter-run.yml uses: ./.github/workflows/reusable-rtlmeter-run.yml
with: with:
tag: gcc tag: gcc
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
cc: gcc cc: gcc
verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }} verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }}
verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }} verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }}
@@ -192,7 +148,7 @@ jobs:
uses: ./.github/workflows/reusable-rtlmeter-run.yml uses: ./.github/workflows/reusable-rtlmeter-run.yml
with: with:
tag: clang tag: clang
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
cc: clang cc: clang
verilator-archive-new: ${{ needs.build-clang-new.outputs.archive }} verilator-archive-new: ${{ needs.build-clang-new.outputs.archive }}
verilator-archive-old: ${{ needs.build-clang-old.outputs.archive }} verilator-archive-old: ${{ needs.build-clang-old.outputs.archive }}
@@ -219,7 +175,7 @@ jobs:
uses: ./.github/workflows/reusable-rtlmeter-run.yml uses: ./.github/workflows/reusable-rtlmeter-run.yml
with: with:
tag: gcc-hier tag: gcc-hier
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
cc: gcc cc: gcc
verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }} verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }}
verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }} verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }}
@@ -244,7 +200,7 @@ jobs:
(github.event_name == 'pull_request' && (contains(needs.*.result, 'success') && !contains(needs.*.result, 'failure'))) (github.event_name == 'pull_request' && (contains(needs.*.result, 'success') && !contains(needs.*.result, 'failure')))
|| (github.event_name != 'pull_request' && (contains(needs.*.result, 'success') || contains(needs.*.result, 'failure'))) || (github.event_name != 'pull_request' && (contains(needs.*.result, 'success') || contains(needs.*.result, 'failure')))
)}} )}}
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
outputs: outputs:
# The run tags, derived from the 'run-*' dependency job names # The run tags, derived from the 'run-*' dependency job names
run-tags: ${{ steps.tags.outputs.tags }} run-tags: ${{ steps.tags.outputs.tags }}
@@ -327,7 +283,7 @@ jobs:
# dashboard UI currently assumes there is only one data point per # dashboard UI currently assumes there is only one data point per
# calendar day. Results from reruns can be imported manually if needed. # calendar day. Results from reruns can be imported manually if needed.
if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && contains(needs.*.result, 'success') && !cancelled() }} if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && contains(needs.*.result, 'success') && !cancelled() }}
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
steps: steps:
- name: Download combined results - name: Download combined results
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
@@ -378,7 +334,7 @@ jobs:
name: Prepare Pull Request results name: Prepare Pull Request results
needs: combine-results needs: combine-results
if: ${{ github.event_name == 'pull_request' && github.repository == 'verilator/verilator' && needs.combine-results.result == 'success' }} if: ${{ github.event_name == 'pull_request' && github.repository == 'verilator/verilator' && needs.combine-results.result == 'success' }}
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
permissions: permissions:
actions: read actions: read
steps: steps:
@@ -435,7 +391,7 @@ jobs:
name: Create issue on failure name: Create issue on failure
needs: publish-scheduled-results needs: publish-scheduled-results
if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && failure() && !cancelled() }} if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && failure() && !cancelled() }}
runs-on: ubuntu-26.04 runs-on: ubuntu-24.04
steps: steps:
# Creating issues requires elevated privilege # Creating issues requires elevated privilege
- name: Generate access token - name: Generate access token
+6 -7
View File
@@ -72,7 +72,7 @@ top-of-file comment.
- Build in the source tree: `autoconf && ./configure && make -j8`. Configure with - Build in the source tree: `autoconf && ./configure && make -j8`. Configure with
`--enable-ccwarn` so a new compiler warning stops the build. `--enable-ccwarn` so a new compiler warning stops the build.
- Run one test from the repository root: `test_regress/t/t_<name>.py`. - Run one test from the repository root: `test_regress/t/t_<name>.py`.
- Run the full regression tests with `make test`. The complete suite requires - Run the full regression with `make test`. The complete suite requires
configuring with `--enable-longtests` (works on every OS, including macOS). configuring with `--enable-longtests` (works on every OS, including macOS).
______________________________________________________________________ ______________________________________________________________________
@@ -93,8 +93,8 @@ ______________________________________________________________________
- [ ] Ran `make format` (clang-format), `make cppcheck`, and `make lint-py`; - [ ] Ran `make format` (clang-format), `make cppcheck`, and `make lint-py`;
self-reviewed the diff for leftover debug code, stale comments, and self-reviewed the diff for leftover debug code, stale comments, and
copy-paste errors. copy-paste errors.
- [ ] Ran the full regression tests on at least one OS before submitting. Partial - [ ] Ran the full regression on at least one OS before submitting. Partial runs
runs are fine during development, but the submitted PR is expected to pass every are fine during development, but the submitted PR is expected to pass every
test. test.
- [ ] Validated the code changes against recommendations in the AGENTS.md files. - [ ] Validated the code changes against recommendations in the AGENTS.md files.
- [ ] Did not edit `docs/CONTRIBUTORS` (humans only) or `Changes` (maintainer - [ ] Did not edit `docs/CONTRIBUTORS` (humans only) or `Changes` (maintainer
@@ -143,7 +143,6 @@ The API you choose determines which test must accompany the change.
## Commits ## Commits
- Subject line is short and imperative and conventionally ends with the - Subject line is short and imperative and conventionally ends with the PR number:
issue number being fixed. No need for the PR number this will be added `Support property case (#7721)`. A body is optional and common for non-trivial
during git squash. e.g `Support property case (#7721)`. The commit body message is changes.
optional and common for non-trivial changes.
+10 -38
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 cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project( project(
Verilator Verilator
VERSION 5.053 VERSION 5.052
HOMEPAGE_URL https://verilator.org HOMEPAGE_URL https://verilator.org
LANGUAGES CXX LANGUAGES CXX
) )
@@ -49,19 +49,20 @@ if(WIN32)
if(DEFINED ENV{WIN_FLEX_BISON}) if(DEFINED ENV{WIN_FLEX_BISON})
set(WIN_FLEX_BISON "$ENV{WIN_FLEX_BISON}") set(WIN_FLEX_BISON "$ENV{WIN_FLEX_BISON}")
endif() endif()
if(NOT WIN_FLEX_BISON) if(IS_DIRECTORY ${WIN_FLEX_BISON})
message( list(APPEND CMAKE_PREFIX_PATH ${WIN_FLEX_BISON})
FATAL_ERROR
"Please install https://github.com/lexxmark/winflexbison and set WIN_FLEX_BISON environment variable. Please use install cmake target after a successful build."
)
elseif(IS_DIRECTORY "${WIN_FLEX_BISON}")
list(APPEND CMAKE_PREFIX_PATH "${WIN_FLEX_BISON}")
else() else()
message( message(
FATAL_ERROR FATAL_ERROR
"The path specified by WIN_FLEX_BISON environment variable is not an existing folder: ${WIN_FLEX_BISON}" "The path specified by WIN_FLEX_BISON environment variable is not an existing folder: ${WIN_FLEX_BISON}"
) )
endif() endif()
if(NOT WIN_FLEX_BISON)
message(
FATAL_ERROR
"Please install https://github.com/lexxmark/winflexbison and set WIN_FLEX_BISON environment variable. Please use install cmake target after a successful build."
)
endif()
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
endif() endif()
@@ -92,36 +93,7 @@ if(OBJCACHE_ENABLED)
endif() endif()
find_package(BISON) find_package(BISON)
# Prefer the header installed with the selected generator, including through find_package(FLEX)
# symlinks such as Homebrew's bin/flex. Explicit cache entries take precedence.
find_program(
FLEX_EXECUTABLE
NAMES flex win-flex win_flex
DOC "Path to the flex executable"
)
if(NOT FLEX_EXECUTABLE)
message(
FATAL_ERROR
"Cannot find Flex; set FLEX_EXECUTABLE to its executable path"
)
endif()
get_filename_component(flex_real_executable "${FLEX_EXECUTABLE}" REALPATH)
get_filename_component(flex_bin_dir "${flex_real_executable}" DIRECTORY)
find_path(
FLEX_INCLUDE_DIR
FlexLexer.h
PATHS "${flex_bin_dir}/../include" "${flex_bin_dir}"
NO_DEFAULT_PATH
DOC "Path to the flex headers"
)
find_path(FLEX_INCLUDE_DIR FlexLexer.h)
if(NOT FLEX_INCLUDE_DIR)
message(
FATAL_ERROR
"Cannot find FlexLexer.h; set FLEX_INCLUDE_DIR to its directory"
)
endif()
mark_as_advanced(FLEX_EXECUTABLE FLEX_INCLUDE_DIR)
# Build # Build
#set_property(GLOBAL PROPERTY JOB_POOLS one_job=1) #set_property(GLOBAL PROPERTY JOB_POOLS one_job=1)
+3 -151
View File
@@ -10,154 +10,6 @@ The changes in each Verilator version are described below. The
contributors that suggested or implemented a given issue are shown in []. Thanks! contributors that suggested or implemented a given issue are shown in []. Thanks!
Verilator 5.053 devel
==========================
**Other:**
* Add covergroup runtime registry (#8134). [Matthew Ballance]
* Add initial support for solo `binsof` (#8298). [Marco Bartoli]
* Add `-fno-dead-members` and optimize dead class member functions (#8830).
* Add `--coverage-max-bins` and `--coverage-max-real-bins` (#8499). [Marco Bartoli]
* Remove deprecated `--structs-packed`.
* Change internal module inlining to after V3Scope (#1539) (#8453). [Geza Lore, Testorrent USA, Inc.]
* Change JSON dumps to suppress empty strings, etc.
* Change `--protect` to use SHA512 per IEEE 1735.
* Support multi-dimensional module instance arrays (#2675) (#8476). [Geza Lore, Testorrent USA, Inc.]
* Support VPI interface references (#8081). [Todd Strader]
* Support nested array and associative array element member access in constraint 1/4 - pre-cleanup (#8237). [Kamil Danecki, Antmicro Ltd.]
* Support unique constraint inside std::randomize (#8241). [Kornel Uriasz, Antmicro Ltd.]
* Support nested array and associative array element member access in constraint (#8244). [Kamil Danecki, Antmicro Ltd.]
* Support rand dynamic arrays used inside `with` clause of randomize (#8246). [Kamil Danecki, Antmicro Ltd.]
* Support multiplicity in SVA shapes that use NFA rings (#8267). [Artur Bieniek, Antmicro Ltd.]
* Support non-overlapped property implications correctly (#8268). [Artur Bieniek, Antmicro Ltd.]
* Support logic and set operations on binsof (#8306). [Marco Bartoli]
* Support `illegal_bins` and `ignore_bins` for cross bins (#8372). [Marco Bartoli]
* Support `iff` in covergroup bins (#8406) (#8407). [Marco Bartoli]
* Support enclosing cross identifiers in bin selections (#8416). [Marco Bartoli]
* Support dynamic cross layouts and fix covergroup bin exclusions priority (#8424). [Marco Bartoli]
* Support automatic bins for enum coverpoints (#8462) (#8464). [David Harris]
* Support empty queue literals as function and task arguments (#8501) (#8502). [Francesco Urbani]
* Support covergroup fixed-size bin arrays (#8514). [Marco Bartoli]
* Support wildcard bins (#8516). [Marco Bartoli]
* Support coverpoint bin `with` filters (#8518). [Marco Bartoli]
* Support waiting on clocking block event through modport (#8403) (#8530). [Saqib Khan]
* Optimize NBA shadow copies for partial writes (#8266). [Artur Bieniek, Antmicro Ltd.]
* Optimize recursion depth in DFG circular driver tracing (#8272). [Drew Risinger]
* Optimize DFG synthesis symbol table ordering (#8282). [Geza Lore, Testorrent USA, Inc.]
* Optimize redundant vertex cache lookups in DFG peephole (#8283). [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG vertex iteration callbacks (#8284). [Geza Lore, Testorrent USA, Inc.]
* Optimize redundant memoization in DFG CSE (#8285). [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG synthesis of simple continuous assignments (#8286). [Geza Lore, Testorrent USA, Inc.]
* Optimize more selects in DFG (#8292). [Geza Lore, Testorrent USA, Inc.]
* Optimize netlist teardown when about to exit (#8294). [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG algorithms with an open addressing hash table (#8307). [Geza Lore, Testorrent USA, Inc.]
* Optimize data-type type chain recursion (#8310). [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG vertex input edge storage (#8313). [Geza Lore, Testorrent USA, Inc.]
* Optimize successive whole-element array assignments (#8316). [Patrick Creighton]
* Optimize unnecessary per-vertex allocations in NFA lowering (#8319). [Artur Bieniek, Antmicro Ltd.]
* Optimize duplicate lookup in NFA property recursion guard (#8321). [Artur Bieniek, Antmicro Ltd.]
* Optimize NBA assertions by keeping all writers before readers (#8326). [Artur Bieniek, Antmicro Ltd.]
* Optimize wide four-state constant operations in V3Number (#8333). [CatalpaEel]
* Optimize split pass (#8385). [Geza Lore, Testorrent USA, Inc.]
* Optimize covergroup bin metadata lookup (#8404). [Marco Bartoli]
* Optimize dead ports (#8426). [Geza Lore, Testorrent USA, Inc.]
* Optimize instance port connections by aliasing rather than by assignment (#8427). [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG temporary sharing across module instances (#8456). [Michael Bedford Taylor]
* Optimize modport resolution with many interface instances (#8496) (#8497). [Deanyone Su]
* Optimize covergroup bin generation (#8499). [Marco Bartoli]
* Optimize through-package variables in DFG. [Geza Lore, Testorrent USA, Inc.]
* Optimize assertion control queries (#8547). [Artur Bieniek, Antmicro Ltd.]
* Optimize sched-initial/static/final split to once per sub-function (#8549). [Krzysztof Bieganski, Antmicro Ltd.]
* Optimize exhaustive case if-chains with an unconditional else (#8554). [Nick Brereton]
* Move module inlining after V3Scope (#8453) (#1539). [Geza Lore, Testorrent USA, Inc.]
* Fix array slice crash when used as a bare value (#5132) (#8262). [Marco Frank]
* Fix function reference into generate case block (#5312) (#8532). [Hongseok Choi]
* Fix pattern type matching against nested aggregates (#7304).
* Fix covergroup-internal references multi-threaded ordering (#7779) (#8255). [Matthew Ballance]
* Fix typedef through a parameterized sub-interface (#7943 repair) (#8124) (#8128). [em2machine]
* Fix detecting non-inlined awaits in V3Delayed.cpp (#7982). [Igor Zaworski, Antmicro Ltd.]
* Fix phased `solve...before` diversity (#8189) (#8194). [Aditya Shevade]
* Fix array-size constraint on arrays inside base class (#8219). [Kornel Uriasz, Antmicro Ltd.]
* Fix multidimensional packed array type handling (#8235). [Artur Bieniek, Antmicro Ltd.]
* Fix library method argument references (#8252). [Geza Lore, Testorrent USA, Inc.]
* Fix CMake to call Verilator Perl wrapper (#8269) (#8270) (#8311). [Drew Risinger]
* Fix real values and unenforced rand/randc type-eligibility rules in constraints (#8273) (#8275) (#8276). [Aditya Shevade]
* Fix assignment pattern default fill of an array of unpacked structs (#8289). [Nick Brereton]
* Fix escape strings in pattern formatting (#8297). [Marco Bartoli]
* Fix DECLFILENAME warning on nested declarations (#8302). [Martijn Wobbes]
* Fix cache of boundary vertices in DFG cycle-breaking (#8308). [Shriyans Sahoo]
* Fix property and sequence line coverage (#8314). [Andrii, Antmicro Ltd.]
* Fix wide typed pattern formatting primitives (#8325). [Marco Bartoli]
* Fix circular class inheritance hang (#8324) (#8545). [Saqib Khan]
* Fix V3InstrCount with recursive functions (#8327) (#8361). [Geza Lore, Testorrent USA, Inc.]
* Fix parameterized class nested in a parameterized interface (#8328) (#8329). [Kristof Marien]
* Fix printing signed enums and wide enum names (#8338). [Marco Bartoli]
* Fix ignoring cross_auto_bin_max instead of hard error (#8340). [Marco Bartoli]
* Fix wide enum names (#8344) (#8362) (#8365) (#8366). [Marco Bartoli]
* Fix class-qualified self reference to a typedef in a parameterized class (#8348) (#8349). [Dragon-Git]
* Fix crash when elaborating a covergroup option through a forward class (#8351) (#8352). [Kristof Marien]
* Fix missing implicit net for bufif/notif/nmos/pmos data input (#8353) (#8354). [Mike Murunov]
* Fix %p format on class members (#8360). [Marco Bartoli]
* Fix Docker compilation with --trace-fst (#8370). [Giuseppe Manzoni]
* Fix NFA rings for bounded SVA delays (#8371). [Artur Bieniek, Antmicro Ltd.]
* Fix DFG driver tracing of partial assignments (#8377) (#8397). [Geza Lore, Testorrent USA, Inc.]
* Fix unique constraint with multiple arrays (#8378). [Kornel Uriasz, Antmicro Ltd.]
* Fix access types of refs and inouts (#8380). [Igor Zaworski, Antmicro Ltd.]
* Fix copy constructor of classes with constraints (#8381). [Ryszard Rozak, Antmicro Ltd.]
* Fix C++ clang 18 compiler with -fno-slp-vectorize (#8383). [Geza Lore, Testorrent USA, Inc.]
* Fix reevaluation of method calls in wait conditions (#8391). [Jesús Alberto López Soto]
* Fix weighted dist constraints in presence of other constraints (#8392). [Kornel Uriasz, Antmicro Ltd.]
* Fix error on non-existent interface ports (#8393). [Andrii Andrieiev, Antmicro Ltd.]
* Fix compile-time blowup forcing large unpacked arrays (#8395). [Jonathan Drolet]
* Fix false MULTIDRIVEN for writes in tasks never executed (#8401). [Artur Bieniek, Antmicro Ltd.]
* Fix $c() inside a DPI export (#8402). [Nazar Zibilyuk]
* Fix covergroup constructor initialization (#8410). [Marco Bartoli]
* Fix issues with configuring flex during build on MacOS (#8412). [Kaleb Barrett]
* Fix interface modport resolution against non-scope symbol tables (#8420). [Geza Lore, Testorrent USA, Inc.]
* Fix FSM coverage hierarchy being emitted as an absolute path (#8421). [Geza Lore, Testorrent USA, Inc.]
* Fix MSYS header include search to be after toolchain headers (#8422). [Kaleb Barrett]
* Fix losing coverage declarations if first in a module body (#8425). [Geza Lore, Testorrent USA, Inc.]
* Fix force read update sensitivity performance (#8431) (#8433). [Jonathan Drolet]
* Fix streaming dynamic arrays with different element types failing to compile (#8434) (#8435). [Kristof Marien]
* Fix internal error when task without C function is left in the tree (#8437). [Artur Bieniek, Antmicro Ltd.]
* Fix nested random dynamic arrays internal error (#8438) (#8439). [Kristof Marien]
* Fix elaboration of classes reached through deferred references (#8440) (#8510). [Ethan Sifferman]
* Fix VPI discoverability of forceable unpacked struct members (#8445). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix compound types in anonymous structs (#8449). [Artur Bieniek, Antmicro Ltd.]
* Fix internal error on covergroup in a specialized parameterized class (#8450) (#8452). [David Harris]
* Fix data race in type handling (#8451). [Tynan McAuley]
* Fix $sscanf/$fscanf returning -1 after a partial match (#8457) (#8459). [David Harris]
* Fix covergroup '0/'1 bin values not filling to coverpoint width (#8458) (#8460). [David Harris]
* Fix embedded covergroup calling an enclosing class method (#8461) (#8463). [David Harris]
* Fix unsized condition not reduced to one bit (#8465) (#8466). [David Harris]
* Fix comma emission for nested JSON scopes (#8469) (#8474). [Noam Gallmann]
* Fix model failing when host has fewer cores than --threads (#8475). [Artur Bieniek, Antmicro Ltd.]
* Fix `##0` synchronous drive overriding later drives (#8482). [Marco Bartoli]
* Fix disable fork and wait fork affecting pending NBA updates (#8483). [Marco Bartoli]
* Fix nonblocking event trigger never firing in suspendable processes (#8485). [Marco Bartoli]
* Fix setting status of finished process immediately (#8503). [Ryszard Rozak, Antmicro Ltd.]
* Fix constant oversized shifts in V3Premit. [Geza Lore, Testorrent USA, Inc.]
* Fix semaphore waiting on killed process (#8505). [Ryszard Rozak, Antmicro Ltd.]
* Fix mis-setting of process state from FINISHED to KILLED (#8506). [Ryszard Rozak, Antmicro Ltd.]
* Fix rand_mode(0) not preserving dynamic array of struct values used in a dist constraint (#8508) (#8509). [Kristof Marien]
* Fix duplicate struct declaration when inlining (#8515). [Ethan Sifferman]
* Fix bear version detection if uninstalled. [Geza Lore, Testorrent USA, Inc.]
* Fix enums inside randomize() with (#8517). [Kornel Uriasz, Antmicro Ltd.]
* Fix emitting public parameters (#8520). [Geza Lore, Testorrent USA, Inc.]
* Fix verilator_coverage report to align with get_coverage() (#8522) (#8524) (#8531). [Marco Bartoli]
* Fix verilator_coverage reports rounding incomplete coverage to 100.0% (#8523). [Jordan Carlin]
* Fix extern class method rejects matching virtual interface argument type (#8525) (#8526). [Kristof Marien]
* Fix of queue pop return value access (#8529) (#8511). [Igor Zaworski, Antmicro Ltd.]
* Fix derived class construction resets inherited per-instance rand_mode state (#8527) (#8528). [Kristof Marien]
* Fix crash on class handle passed to string port (#8536) (#8538). [Noam Gallmann]
* Fix stack depth calculations over calls (#8544). [Nick Brereton]
* Fix cascading errors after unsupported class dot reference in parameter value (#8551). [Martijn Wobbes, Antmicro Ltd.]
* Fix vpi_put_value crash on NULL value pointer (#8553). [Nick Brereton]
* Fix --hierarchical parameter specialization name collisions (#8555) (#8565). [Marco Bartoli]
* Fix dehash of a suffix after a hashed name (#8556). [Ethan Sifferman]
Verilator 5.052 2026-09-05 Verilator 5.052 2026-09-05
========================== ==========================
@@ -2003,7 +1855,7 @@ Verilator 5.016 2023-09-16
* Optimize Verilator executable size by refactoring error reporting routines (#4446). [Anthony Donlon] * Optimize Verilator executable size by refactoring error reporting routines (#4446). [Anthony Donlon]
* Optimize Verilation runtime pointers and graphs (#4396) (#4397) (#4398). [Krzysztof Bieganski, Antmicro Ltd] * Optimize Verilation runtime pointers and graphs (#4396) (#4397) (#4398). [Krzysztof Bieganski, Antmicro Ltd]
* Optimize preparations towards multithreaded Verilation (#4291) (#4463) (#4476) (#4477) (#4479). [Kamil Rakoczy, Antmicro Ltd] * Optimize preparations towards multithreaded Verilation (#4291) (#4463) (#4476) (#4477) (#4479). [Kamil Rakoczy, Antmicro Ltd]
* Fix Windows filename format, etc (#3873) (#4421). [Anthony Donlon] * Fix Windows filename format, etc (#3873) (#4421). [Anthony Donlon].
* Fix t_dist_cppstyle Perl performance issue (#4085). [Srinivasan Venkataramanan] * Fix t_dist_cppstyle Perl performance issue (#4085). [Srinivasan Venkataramanan]
* Fix using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon] * Fix using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon]
* Fix false INFINITELOOP on forever..mailbox.get() (#4323). [Srinivasan Venkataramanan] * Fix false INFINITELOOP on forever..mailbox.get() (#4323). [Srinivasan Venkataramanan]
@@ -3788,7 +3640,7 @@ Verilator 3.880 2015-12-19
* Fix model restore crash. (#1013) [Jason McMullan] * Fix model restore crash. (#1013) [Jason McMullan]
* Fix arrayed instances to unpacked of same size. (#1015) [Varun Koyyalagunta] * Fix arrayed instances to unpacked of same size. (#1015) [Varun Koyyalagunta]
* Fix slices of unpacked arrays with non-zero LSBs. * Fix slices of unpacked arrays with non-zero LSBs.
* Fix ternary operation with unpacked array. (#1017) [Varun Koyyalagunta] * Fix ternary operation with unpacked array. (#1017) [Varun Koyyalagunta].
Verilator 3.878 2015-11-01 Verilator 3.878 2015-11-01
@@ -3878,7 +3730,7 @@ Verilator 3.870 2015-02-12
* Suppress COMBDLY when inside always_latch. (#864) [Iztok Jeras] * Suppress COMBDLY when inside always_latch. (#864) [Iztok Jeras]
* Support cast operator with expression size. (#865) [Iztok Jeras] * Support cast operator with expression size. (#865) [Iztok Jeras]
* Add warning on slice selection out of bounds. (#875) [Cong Van Nguyen] * Add warning on slice selection out of bounds. (#875) [Cong Van Nguyen].
* Fix member select error broke in 3.868. (#867) [Iztok Jeras] * Fix member select error broke in 3.868. (#867) [Iztok Jeras]
* Fix $sccanf from string. (#866) [David Pierce] * Fix $sccanf from string. (#866) [David Pierce]
* Fix VM_PARALLEL_BUILDS broke in 3.868. (#870) [Hiroki Honda] * Fix VM_PARALLEL_BUILDS broke in 3.868. (#870) [Hiroki Honda]
+4 -8
View File
@@ -710,10 +710,8 @@ override undefine COVERAGE_BASE # Use the above variabels instead
# 'fastcov' setup # 'fastcov' setup
FASTCOV := nodist/fastcov.py FASTCOV := nodist/fastcov.py
FASTCOV_JOBS ?= $(shell nproc) FASTCOV_OPT := -j $(shell nproc)
FASTCOV_OPT := -j $(FASTCOV_JOBS) FASTCOV_OPT += --gcov $(GCOV)
# gcov does not free memory between files, so run it in batches via a wrapper
FASTCOV_OPT += --gcov nodist/gcov_batch
FASTCOV_OPT += --lcov FASTCOV_OPT += --lcov
FASTCOV_OPT += --process-gcno FASTCOV_OPT += --process-gcno
FASTCOV_OPT += --branch-coverage FASTCOV_OPT += --branch-coverage
@@ -749,7 +747,6 @@ FASTCOV_OPT += BROKEN_RTN
FASTCOV_OPT += NUM_ASSERT FASTCOV_OPT += NUM_ASSERT
FASTCOV_OPT += SELF_CHECK FASTCOV_OPT += SELF_CHECK
FASTCOV_OPT += UASSERT FASTCOV_OPT += UASSERT
FASTCOV_OPT += UDEBUGONLY
FASTCOV_OPT += UINFO FASTCOV_OPT += UINFO
FASTCOV_OPT += assert FASTCOV_OPT += assert
FASTCOV_OPT += 'if (VL_UNCOVERABLE' FASTCOV_OPT += 'if (VL_UNCOVERABLE'
@@ -765,7 +762,6 @@ GENHTML_OPT += --rc branch_coverage=1
GENHTML_OPT += --rc genhtml_hi_limit=100 GENHTML_OPT += --rc genhtml_hi_limit=100
GENHTML_OPT += --ignore-errors negative GENHTML_OPT += --ignore-errors negative
GENHTML_OPT += --ignore-errors inconsistent GENHTML_OPT += --ignore-errors inconsistent
GENHTML_OPT += --ignore-errors empty
ifeq ($(COVERAGE_REF_BASE),) ifeq ($(COVERAGE_REF_BASE),)
GENHTML_OPT += --header-title "Code coverage for Verilator $(shell git describe --dirty)" GENHTML_OPT += --header-title "Code coverage for Verilator $(shell git describe --dirty)"
else else
@@ -787,7 +783,7 @@ $(COVERAGE_DIR)/verilator.info: $(GCNO_FILES) $(GCDA_FILES)
@echo "# fastcov: combining all .gcda files into lcov .info" @echo "# fastcov: combining all .gcda files into lcov .info"
@echo "####################################################################" @echo "####################################################################"
@mkdir -p $(COVERAGE_DIR) @mkdir -p $(COVERAGE_DIR)
GCOV=$(GCOV) /usr/bin/time -f "That took %E" \ /usr/bin/time -f "That took %E" \
$(FASTCOV) $(FASTCOV_OPT) --output $@ $(FASTCOV) $(FASTCOV_OPT) --output $@
# Filter combined .info file for patch coverage # Filter combined .info file for patch coverage
@@ -797,8 +793,8 @@ $(COVERAGE_DIR)/verilator-patch.info: $(COVERAGE_DIR)/verilator.info
@echo "####################################################################" @echo "####################################################################"
rm -f $(COVERAGE_DIR)/empty-patch rm -f $(COVERAGE_DIR)/empty-patch
git diff $(COVERAGE_REF_BASE) -- include src > $(COVERAGE_DIR)/filter.patch git diff $(COVERAGE_REF_BASE) -- include src > $(COVERAGE_DIR)/filter.patch
[ -s $(COVERAGE_DIR)/filter.patch ] || touch $(COVERAGE_DIR)/empty-patch
$(FASTCOV) -C $^ --lcov -o $@ --diff-filter $(COVERAGE_DIR)/filter.patch $(FASTCOV) -C $^ --lcov -o $@ --diff-filter $(COVERAGE_DIR)/filter.patch
grep -q '^DA:' $@ || touch $(COVERAGE_DIR)/empty-patch
# Build coverage report # Build coverage report
$(COVERAGE_DIR)/report/index.html: $(COVERAGE_DIR)/verilator$(if $(COVERAGE_REF_BASE),-patch).info $(COVERAGE_DIR)/report/index.html: $(COVERAGE_DIR)/verilator$(if $(COVERAGE_REF_BASE),-patch).info
+11 -11
View File
@@ -23,8 +23,8 @@
.. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator .. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator
:target: https://hub.docker.com/r/verilator/verilator :target: https://hub.docker.com/r/verilator/verilator
.. |badge7| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/regression.yml?branch=master&label=regression .. |badge7| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/build-test.yml?branch=master&label=regressions
:target: https://github.com/verilator/verilator/actions/workflows/regression.yml :target: https://github.com/verilator/verilator/actions/workflows/build-test.yml
.. |badge8| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/rtlmeter.yml?branch=master&event=schedule&label=benchmarks .. |badge8| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/rtlmeter.yml?branch=master&event=schedule&label=benchmarks
:target: https://verilator.github.io/verilator-rtlmeter-results :target: https://verilator.github.io/verilator-rtlmeter-results
@@ -35,7 +35,7 @@ Welcome to Verilator
.. list-table:: .. list-table::
- - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.** - - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.**
- Accepts Verilog or SystemVerilog, including UVM - Accepts Verilog or SystemVerilog
- Performs lint code-quality checks - Performs lint code-quality checks
- Compiles into multithreaded C++, or SystemC - Compiles into multithreaded C++, or SystemC
- Creates JSON to front-end your own tools - Creates JSON to front-end your own tools
@@ -75,14 +75,14 @@ instantiate the model. The resulting Verilated executable performs the
design simulation. Verilator also supports linking Verilator-generated design simulation. Verilator also supports linking Verilator-generated
libraries, optionally encrypted, into other simulators. libraries, optionally encrypted, into other simulators.
Verilator supports all design constructs, most verification constructs Verilator supports all design constructs, most verification constructs,
including UVM, intra-assignment delays (e.g, `#10`), and events. intra-assignment delays (e.g, `#10`), and events. Tristate-bus (`z`) and
Tristate-bus (`z`) and unknowns (`x`) are handled in limited contexts, in a unknowns (`x`) are handled in limited contexts, in a special manor for
special manor for performance. It currently may not be the best choice if performance. It currently may not be the best choice if you are expecting a
you are expecting a full-featured replacement for a closed-source Verilog full-featured replacement for a closed-source Verilog simulator, performing
simulator, performing SDF annotation, or mixed-signal simulation. However, SDF annotation, or mixed-signal simulation. However, if you are looking for
if you are looking for a path to migrate SystemVerilog to C++/SystemC, or a path to migrate SystemVerilog to C++/SystemC, or want high-speed
want high-speed simulation, Verilator is the tool for you. simulation, Verilator is the tool for you.
Performance Performance
=========== ===========
-2
View File
@@ -419,8 +419,6 @@ detailed descriptions of these arguments.
--coverage-expr-max <value> Maximum permutations allowed for an expression --coverage-expr-max <value> Maximum permutations allowed for an expression
--coverage-fsm Enable FSM state/arc coverage --coverage-fsm Enable FSM state/arc coverage
--coverage-line Enable line coverage --coverage-line Enable line coverage
--coverage-max-bins <value> Maximum bins of an integral coverpoint bins declaration
--coverage-max-real-bins <value> Maximum bins of a real coverpoint bins declaration
--coverage-max-width <width> Maximum array depth for coverage --coverage-max-width <width> Maximum array depth for coverage
--coverage-per-instance Enable per-instance coverage counters --coverage-per-instance Enable per-instance coverage counters
--coverage-toggle Enable toggle coverage --coverage-toggle Enable toggle coverage
-10
View File
@@ -79,13 +79,6 @@ fi
if [ "$OPT_LIGHT_DEBUG" = 1 ]; then if [ "$OPT_LIGHT_DEBUG" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-light-debug" CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-light-debug"
fi fi
if [ "$(uname -s)" = Darwin ]; then
# Force the use of the Homebrew flex over the one included as part of the
# Apple SDK that's preinstalled on the MacOS Github Action runners.
FLEX_PREFIX=$(brew --prefix flex)
export LEX="$FLEX_PREFIX/bin/flex"
export CPPFLAGS="${CPPFLAGS:+$CPPFLAGS }-I$FLEX_PREFIX/include"
fi
autoconf autoconf
./configure $CONFIGURE_ARGS CXX="$CXX" ./configure $CONFIGURE_ARGS CXX="$CXX"
@@ -95,9 +88,6 @@ autoconf
ccache -z ccache -z
BUILD_START=$SECONDS BUILD_START=$SECONDS
"$MAKE" -j "$NPROC" -k "$MAKE" -j "$NPROC" -k
# Second make to get failures at bottom, to avoid needing to
# scroll up GitHub action logs (to see first errors half-way up)
"$MAKE" -j "$NPROC" -k
ccache -svv ccache -svv
ccache --evict-older-than "$((SECONDS - BUILD_START + 60))s" ccache --evict-older-than "$((SECONDS - BUILD_START + 60))s"
ccache -svv ccache -svv
-33
View File
@@ -98,36 +98,3 @@ SUMMARY_TEMPLATE
echo "Workflow [#${RUN_NUM}](${RUN_URL}) report: [${RUN_ID}](${REPORT_URL})" > ${NOTIFICATION_DIR}/hist.txt echo "Workflow [#${RUN_NUM}](${RUN_URL}) report: [${RUN_ID}](${REPORT_URL})" > ${NOTIFICATION_DIR}/hist.txt
fi fi
###############################################################################
# Create the coverage status
###############################################################################
# 'pr-automation.yml' labels the pull request from this. Note it is only
# written when the coverage could actually be determined, so that a report we
# could not read leaves the labels of the pull request alone.
if [ -f ${COVERAGE_DIR}/empty-patch ]; then
# Nothing to cover, so count it as covered
COVERED=true
else
# Take the counts, and not the percentage, which is rounded. The line reads
# for example " lines......: 100.00% (60 of 60 lines)"
COUNTS=$(sed -nE 's/^ *lines\.*: *[0-9.]+% \(([0-9]+) of ([0-9]+) lines\).*/\1 \2/p' \
${MAKE_LOG} | tail -n 1)
HIT=${COUNTS%% *}
TOTAL=${COUNTS##* }
if [ -z "${TOTAL}" ]; then
echo "Could not read the line coverage from ${MAKE_LOG}" >&2
COVERED=
elif [ "${HIT}" = "${TOTAL}" ]; then
COVERED=true
else
COVERED=false
fi
fi
if [ -n "${COVERED}" ]; then
echo "Line coverage complete: ${COVERED}"
echo "${COVERED}" > coverage-status.txt
fi
+1 -1
View File
@@ -158,7 +158,7 @@ NEXTHIST_TEMPLATE
# 'pr-notification' for a single run, as the artifacts endpoint lists # '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 # artifacts across all run attempts, and a re-run uploads a new one while
# keeping the previous attempt's artifact. # keeping the previous attempt's artifact.
ARTIFACT_IDS=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts?name=pr-notification" --jq '.artifacts[].id') ARTIFACT_IDS=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
# Delete them all, so we only notify once # Delete them all, so we only notify once
for ARTIFACT_ID in ${ARTIFACT_IDS}; do for ARTIFACT_ID in ${ARTIFACT_IDS}; do
-12
View File
@@ -25,8 +25,6 @@ CASES = {
("BlackParrot:2x2:*", 14), ("BlackParrot:2x2:*", 14),
("BlackParrot:4x4:*", 25), ("BlackParrot:4x4:*", 25),
("Caliptra:default:*", 19), ("Caliptra:default:*", 19),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("HummingbirdV2-E203:default:*", 3), ("HummingbirdV2-E203:default:*", 3),
("HummingbirdV2-E203:nodelay:*", 3), ("HummingbirdV2-E203:nodelay:*", 3),
("NVDLA:*", 29), ("NVDLA:*", 29),
@@ -61,8 +59,6 @@ CASES = {
("BlackParrot:2x2:*", 9), ("BlackParrot:2x2:*", 9),
("BlackParrot:4x4:*", 12), ("BlackParrot:4x4:*", 12),
("Caliptra:default:*", 16), ("Caliptra:default:*", 16),
("HammerBlade:16x8:*", 4),
("HammerBlade:2x1:*", 2),
("HummingbirdV2-E203:default:*", 10), ("HummingbirdV2-E203:default:*", 10),
("HummingbirdV2-E203:nodelay:*", 10), ("HummingbirdV2-E203:nodelay:*", 10),
("NVDLA:*", 28), ("NVDLA:*", 28),
@@ -96,8 +92,6 @@ CASES = {
("BlackParrot:1x1:*", 4), ("BlackParrot:1x1:*", 4),
("BlackParrot:2x2:*", 16), ("BlackParrot:2x2:*", 16),
("BlackParrot:4x4:*", 25), ("BlackParrot:4x4:*", 25),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("NVDLA:*", 25), ("NVDLA:*", 25),
("OpenPiton:16x16:dhry", 16), ("OpenPiton:16x16:dhry", 16),
("OpenPiton:1x1:*", 4), ("OpenPiton:1x1:*", 4),
@@ -116,8 +110,6 @@ CASES_PR = {
("BlackParrot:1x1:*", 5), ("BlackParrot:1x1:*", 5),
("BlackParrot:4x4:*", 25), ("BlackParrot:4x4:*", 25),
("Caliptra:default:*", 19), ("Caliptra:default:*", 19),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("NVDLA:*", 29), ("NVDLA:*", 29),
("HummingbirdV2-E203:default:*", 3), ("HummingbirdV2-E203:default:*", 3),
("HummingbirdV2-E203:nodelay:*", 3), ("HummingbirdV2-E203:nodelay:*", 3),
@@ -150,8 +142,6 @@ CASES_PR = {
("BlackParrot:1x1:*", 5), ("BlackParrot:1x1:*", 5),
("BlackParrot:4x4:*", 12), ("BlackParrot:4x4:*", 12),
("Caliptra:default:*", 16), ("Caliptra:default:*", 16),
("HammerBlade:16x8:*", 4),
("HammerBlade:2x1:*", 2),
("NVDLA:*", 28), ("NVDLA:*", 28),
("HummingbirdV2-E203:default:*", 10), ("HummingbirdV2-E203:default:*", 10),
("HummingbirdV2-E203:nodelay:*", 10), ("HummingbirdV2-E203:nodelay:*", 10),
@@ -183,8 +173,6 @@ CASES_PR = {
"gcc-hier": [ "gcc-hier": [
("BlackParrot:1x1:*", 4), ("BlackParrot:1x1:*", 4),
("BlackParrot:4x4:*", 25), ("BlackParrot:4x4:*", 25),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("NVDLA:*", 25), ("NVDLA:*", 25),
("OpenPiton:16x16:dhry", 16), ("OpenPiton:16x16:dhry", 16),
("OpenPiton:1x1:*", 4), ("OpenPiton:1x1:*", 4),
-1
View File
@@ -29,7 +29,6 @@ RUN apt-get update \
python3 \ python3 \
zlib1g \ zlib1g \
zlib1g-dev \ zlib1g-dev \
liblz4-dev \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
+1 -18
View File
@@ -12,7 +12,7 @@
# Then 'make maintainer-dist' # Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel]) #AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.053 devel], AC_INIT([Verilator],[5.052 2026-09-05],
[https://verilator.org], [https://verilator.org],
[verilator],[https://verilator.org]) [verilator],[https://verilator.org])
@@ -264,7 +264,6 @@ fi
python3_version=$($PYTHON3 --version | head -1) python3_version=$($PYTHON3 --version | head -1)
AC_MSG_RESULT([$PYTHON3 --version = $python3_version]) AC_MSG_RESULT([$PYTHON3 --version = $python3_version])
AC_ARG_VAR([LEX], [Flex executable])
AC_CHECK_PROG(LEX,flex,flex) AC_CHECK_PROG(LEX,flex,flex)
if test "x$LEX" = "x" ; then if test "x$LEX" = "x" ; then
AC_MSG_ERROR([Cannot find "flex" in your PATH, please install it]) AC_MSG_ERROR([Cannot find "flex" in your PATH, please install it])
@@ -272,22 +271,6 @@ fi
flex_version=$($LEX --version | head -1) flex_version=$($LEX --version | head -1)
AC_MSG_RESULT([$LEX --version = $flex_version]) AC_MSG_RESULT([$LEX --version = $flex_version])
# Use the header installed with the selected generator.
flex_include=$($PYTHON3 -c '
import pathlib
import shutil
import sys
print(pathlib.Path(shutil.which(sys.argv@<:@1@:>@) or sys.argv@<:@1@:>@).resolve().parent.parent / "include")
' "$LEX")
if test -f "$flex_include/FlexLexer.h"; then
if test -n "$MSYSTEM"; then
# Search MSYS Flex headers after the active toolchain's system headers.
CPPFLAGS="$CPPFLAGS -idirafter $flex_include"
else
CPPFLAGS="$CPPFLAGS -I$flex_include"
fi
fi
AC_CHECK_PROG(YACC,bison,bison) AC_CHECK_PROG(YACC,bison,bison)
if test "x$YACC" = "x" ; then if test "x$YACC" = "x" ; then
AC_MSG_ERROR([Cannot find "bison" in your PATH, please install it]) AC_MSG_ERROR([Cannot find "bison" in your PATH, please install it])
+1 -25
View File
@@ -7,7 +7,6 @@ contribution terms including the AI policy in ``docs/CONTRIBUTING.rst``.
Please see the Verilator manual for 200+ additional contributors. Thanks to all. Please see the Verilator manual for 200+ additional contributors. Thanks to all.
00>> Please insert into sorted position, and see header as to agreement
24bit-xjkp 24bit-xjkp
404allen404 404allen404
Adam Bagley Adam Bagley
@@ -47,7 +46,6 @@ BRDR LIFE
Brian Li Brian Li
Cameron Kirk Cameron Kirk
Cameron Waite Cameron Waite
CatalpaEel
Chih-Mao Chen Chih-Mao Chen
Chris Bachhuber Chris Bachhuber
Chris Randall Chris Randall
@@ -58,19 +56,16 @@ Chykon
Congcong Cai Congcong Cai
Conor McCullough Conor McCullough
Cookie Cookie
cynicalmindset
Dan Ruelas-Petrisko Dan Ruelas-Petrisko
Daniel Bates Daniel Bates
Danny Oler Danny Oler
Dave Sargeant Dave Sargeant
David Garau David Garau
David Harris
David Horton David Horton
David Ledger David Ledger
David Metz David Metz
David Stanford David Stanford
David Turner David Turner
Deanyone Su
dependabot[bot] dependabot[bot]
Dercury Dercury
Demin Han Demin Han
@@ -79,11 +74,9 @@ Dominick Grochowina
Don Williamson Don Williamson
Dragon-Git Dragon-Git
Drew Ranck Drew Ranck
Drew Risinger
Drew Taussig Drew Taussig
Driss Hafdi Driss Hafdi
Edgar E. Iglesias Edgar E. Iglesias
Edmund Lam
Eddie Rydell Eddie Rydell
em2machine em2machine
emmettifelts emmettifelts
@@ -99,7 +92,6 @@ Fan Shupei
february cozzocrea february cozzocrea
Felix Neumärker Felix Neumärker
Felix Yan Felix Yan
Francesco Urbani
Frans Skarman Frans Skarman
Fuad Ismail Fuad Ismail
Furqan Nadir Furqan Nadir
@@ -121,7 +113,6 @@ Han Qi
Harald Heckmann Harald Heckmann
Hennadii Chernyshchyk Hennadii Chernyshchyk
hjsanjana hjsanjana
Hongseok Choi
Howard Su Howard Su
Huang Rui Huang Rui
Huanghuang Zhou Huanghuang Zhou
@@ -154,7 +145,6 @@ jeffrey.song
Jens Yuechao Liu Jens Yuechao Liu
Jeremy Bennett Jeremy Bennett
Jesse Taube Jesse Taube
Jesús Alberto López Soto
Jevin Sweval Jevin Sweval
Jiacheng Qian Jiacheng Qian
Jiamin Zhu Jiamin Zhu
@@ -192,7 +182,6 @@ Keith Colbert
Kevin Kiningham Kevin Kiningham
Kevin Nygaard Kevin Nygaard
Kornel Uriasz Kornel Uriasz
Kristof Marien
Kritik Bhimani Kritik Bhimani
Krzysztof Bieganski Krzysztof Bieganski
Krzysztof Boronski Krzysztof Boronski
@@ -216,16 +205,13 @@ Maciej Sobkowski
Marcel Chang Marcel Chang
Marco Bartoli Marco Bartoli
Marco Brambilla Marco Brambilla
Marco Frank
Marco Widmer Marco Widmer
Mariusz Glebocki Mariusz Glebocki
Markus Krause Markus Krause
Marlon James Marlon James
Marshal Qiao Marshal Qiao
Martijn Wobbes
Martin Schmidt Martin Schmidt
Martin Stadler Martin Stadler
Martin Velay
Mateusz Gancarz Mateusz Gancarz
Matt Stroud Matt Stroud
Matthew Ballance Matthew Ballance
@@ -238,7 +224,6 @@ Michael Rogenmoser
Michal Czyz Michal Czyz
Michaël Lefebvre Michaël Lefebvre
Miguel Perez Andrade Miguel Perez Andrade
Mike Murunov
Mike Popoloski Mike Popoloski
Miodrag Milanović Miodrag Milanović
Mladen Slijepcevic Mladen Slijepcevic
@@ -251,11 +236,9 @@ Natan Kreimer
Nathan Graybeal Nathan Graybeal
Nathan Kohagen Nathan Kohagen
Nathan Myers Nathan Myers
Nazar Zibilyuk
Nick Brereton Nick Brereton
Nikolai Kumar Nikolai Kumar
Nikolay Puzanov Nikolay Puzanov
Noam Gallmann
Nolan Poe Nolan Poe
Oleh Maksymenko Oleh Maksymenko
Patrick Creighton Patrick Creighton
@@ -298,7 +281,6 @@ Ryan Ziegler
Ryszard Rozak Ryszard Rozak
Saksham Gupta Saksham Gupta
Samuel Riedel Samuel Riedel
Saqib Khan
Sean Cross Sean Cross
Sebastien Van Cauwenberghe Sebastien Van Cauwenberghe
Secturion Secturion
@@ -309,7 +291,6 @@ Seth Pellegrino
Shashvat Prabhu Shashvat Prabhu
Shogo Yamazaki Shogo Yamazaki
Shou-Li Hsu Shou-Li Hsu
Shriyans Sahoo
spomatasmd spomatasmd
Srinivasan Venkataramanan Srinivasan Venkataramanan
Stefan Wallentowitz Stefan Wallentowitz
@@ -318,10 +299,8 @@ Steven Hugg
Stuart Morris Stuart Morris
sumpster sumpster
Szymon Gizler Szymon Gizler
Szymon Jędras
Sören Tempel Sören Tempel
Sumanth Kadiyala Sumanth Kadiyala
Sunimali Rathnayake
Teng Huang Teng Huang
Thomas Aldrian Thomas Aldrian
Thomas Brown Thomas Brown
@@ -342,11 +321,9 @@ Tracy Narine
Trung Nguyen Trung Nguyen
Tudor Timi Tudor Timi
Tymoteusz Blazejczyk Tymoteusz Blazejczyk
Tynan McAuley
Tyrone Marhguy Tyrone Marhguy
Udaya Raj Subedi Udaya Raj Subedi
Udi Finkelstein Udi Finkelstein
UnsignedByte
Unai Martinez-Corral Unai Martinez-Corral
Valentin Atepalikhin Valentin Atepalikhin
Varun Koyyalagunta Varun Koyyalagunta
@@ -362,7 +339,6 @@ Wolfgang Mayerwieser
Xi Zhang Xi Zhang
Yan Xu Yan Xu
Yangyu Chen Yangyu Chen
yash
Yilin Li Yilin Li
Yilou Wang Yilou Wang
Yinan Xu Yinan Xu
@@ -381,4 +357,4 @@ Zixi Li
Zubin Jain Zubin Jain
Àlex Torregrosa Àlex Torregrosa
Ícaro Lima Ícaro Lima
ÍÍ>> Please insert into sorted position, and see header as to agreement Sunimali Rathnayake
+1 -1
View File
@@ -1,5 +1,5 @@
.. comment: generated by t_lint_didnotconverge_bad .. comment: generated by t_lint_didnotconverge_bad
.. code-block:: .. code-block::
-V{t#,#} 'stl' region trigger index 0 is active: @([hybrid] b) -V{t#,#} 'stl' region trigger index 0 is active: @([hybrid] a)
%Error-DIDNOTCONVERGE: Settle region did not converge after '--converge-limit' of 10000 tries %Error-DIDNOTCONVERGE: Settle region did not converge after '--converge-limit' of 10000 tries
-27
View File
@@ -40,30 +40,6 @@ model:
internals, including ``/* verilator public_flat */`` items. internals, including ``/* verilator public_flat */`` items.
.. _model header include macros:
Model Header Include Macros
---------------------------
Code compiled with the generated :file:`{prefix}.mk`, or with the CMake
``verilate()`` command, gets preprocessor macros that expand to the model
header includes, so user C++ need not hard-code the :vlopt:`--prefix`:
.. code-block:: C++
#include VM_PREFIX_INCLUDE // e.g. <Vtop.h>
#include VM_PREFIX_INCLUDE_DPI // e.g. <Vtop__Dpi.h>
``VM_PREFIX_INCLUDE_DPI`` is defined only when Verilator generates the
:file:`{prefix}__Dpi.h` header, which is the case for designs with DPI and
also for public-signal access such as :vlopt:`--public-flat-rw`. Using it
otherwise is a compile-time error.
A target built from several models defines each macro once, naming the
first model, as one target has one set of preprocessor definitions. Code
that must reach a second model's header names it directly.
.. _porting from pre 4.210: .. _porting from pre 4.210:
Model interface changes in version 4.210 Model interface changes in version 4.210
@@ -231,9 +207,6 @@ command line, or the link), you'd then:
#include "Vour__Dpi.h" #include "Vour__Dpi.h"
int add(int a, int b) { return a+b; } int add(int a, int b) { return a+b; }
Rather than hard-coding the prefix, ``#include VM_PREFIX_INCLUDE_DPI`` may
be used instead; see :ref:`Model Header Include Macros`.
DPI System Task/Functions DPI System Task/Functions
------------------------- -------------------------
+8
View File
@@ -18,6 +18,14 @@ C++14 compiler support
the Ubuntu LTS versions of GCC and clang use C++20 by default, estimated the Ubuntu LTS versions of GCC and clang use C++20 by default, estimated
May 2028.) May 2028.)
`--structs-packed` option
The :vlopt:`--structs-packed` option was introduced when Verilator was
first implementing unpacked structs. That feature has been stable now
for multiple years, so :vlopt:`--structs-packed` should no longer be
used. Thus :vlopt:`--structs-packed` will change to a no-operation flag
and the related :option:`UNPACKED` warning will never be issued no
sooner than September 2026.
tcmalloc support tcmalloc support
Verilator currently supports the default malloc, tcmalloc, or jemalloc. Verilator currently supports the default malloc, tcmalloc, or jemalloc.
As jemalloc has better performance, support for tcmalloc may be removed As jemalloc has better performance, support for tcmalloc may be removed
+7 -44
View File
@@ -316,33 +316,6 @@ Summary:
Enables basic block line coverage analysis. See :ref:`Line Coverage`. Enables basic block line coverage analysis. See :ref:`Line Coverage`.
.. option:: --coverage-max-bins <value>
Rarely needed. Specifies the maximum number of bins one covergroup bins
declaration of an integral coverpoint may create: an array of bins such
as ``bins b[] = {[0:$]}``, automatic bins ``bins auto[N]``, or the
automatic bins of a coverpoint from ``option.auto_bin_max``. Defaults to
1024, and may be at most 4294967295 (``2**32 - 1``).
A larger array of bins is ignored with a :option:`COVERIGN` warning, a
larger ``bins auto[N]`` is an error, and a larger
``option.auto_bin_max`` is reduced to this limit with a
:option:`COVERIGN` warning. Increasing the limit increases the memory
the model uses for the bins. See also
:vlopt:`--coverage-max-real-bins`.
.. option:: --coverage-max-real-bins <value>
Rarely needed. Specifies the maximum number of values of one array of
bins of a coverpoint of a ``real`` expression, such as
``bins b[] = {[1:10]}``, each of which is a bin compared separately.
Defaults to 1024, and may be at most 4294967295 (``2**32 - 1``).
A larger array of bins is ignored with a :option:`COVERIGN` warning.
Increasing the limit increases the size of the generated code, which
grows with the number of these bins. See also
:vlopt:`--coverage-max-bins`.
.. option:: --coverage-max-width <width> .. option:: --coverage-max-width <width>
Rarely needed. Specify the maximum bit width of a signal subject to Rarely needed. Specify the maximum bit width of a signal subject to
@@ -733,8 +706,6 @@ Summary:
.. option:: -fno-dead-cells .. option:: -fno-dead-cells
.. option:: -fno-dead-methods
.. option:: -fno-dedup .. option:: -fno-dedup
.. option:: -fno-dfg .. option:: -fno-dfg
@@ -840,10 +811,6 @@ Summary:
.. option:: -fno-lift-expr .. option:: -fno-lift-expr
Deprecated and has no effect (ignored).
In versions before 5.054: Disable lifting of expressions out of statements.
.. option:: -fno-localize .. option:: -fno-localize
.. option:: -fno-merge-cond .. option:: -fno-merge-cond
@@ -852,11 +819,6 @@ Summary:
.. option:: -fno-merge-const-pool .. option:: -fno-merge-const-pool
Deprecated and has no effect (ignored).
In versions before 5.054: Disable merging constant pool entries that have
the same value but different data types.
.. option:: -fno-reloop .. option:: -fno-reloop
.. option:: -fno-reorder .. option:: -fno-reorder
@@ -1828,11 +1790,12 @@ Summary:
.. option:: --structs-packed .. option:: --structs-packed
Removed in 5.054. Deprecated; discontinue use of this option.
Converted all unpacked structures to packed structures, Specifying this Converts all unpacked structures to packed structures, and issues an
option allowed for backward compatibility with versions before Verilator :option:`UNPACKED` warning. Specifying this option allows for backward
5.006, when Verilator would always pack unpacked structures. compatibility with versions before Verilator 5.006, when Verilator would
always pack unpacked structures.
.. option:: -sv .. option:: -sv
@@ -2304,7 +2267,7 @@ Summary:
If using `--x-assign unique`, use the If using `--x-assign unique`, use the
:vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>` :vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>`
runtime option, and seed the runtime random number generator such that runtime option, and seed the runtime random number generator such that
each regression test run gets a different randomization sequence with each regression run gets a different randomization sequence with
:vlopt:`+verilator+seed+\<value\>`. You'll probably also want to print :vlopt:`+verilator+seed+\<value\>`. You'll probably also want to print
any seeds selected, and code to enable rerunning with that same seed, any seeds selected, and code to enable rerunning with that same seed,
so you can reproduce bugs. so you can reproduce bugs.
@@ -2333,7 +2296,7 @@ Summary:
If using `--x-initial unique`, use the If using `--x-initial unique`, use the
:vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>` :vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>`
runtime option, and seed the runtime random number generator such that runtime option, and seed the runtime random number generator such that
each regression test run gets a different randomization sequence with each regression run gets a different randomization sequence with
:vlopt:`+verilator+seed+\<value\>`. You'll probably also want to print :vlopt:`+verilator+seed+\<value\>`. You'll probably also want to print
any seeds selected, and code to enable rerunning with that same seed, any seeds selected, and code to enable rerunning with that same seed,
so you can reproduce bugs. so you can reproduce bugs.
+1 -1
View File
@@ -28,7 +28,7 @@ Quick Install
and install. and install.
#. For build on Windows using MSVC set environment variable WIN_FLEX_BISON #. For build on Windows using MSVC set environment variable WIN_FLEX_BISON
to install directory. For build on Windows/Linux/OS-X using ninja set to install directory. For build on Windows/Linux/OS-X using ninja set
the CMake variable FLEX_INCLUDE_DIR to the directory containing the environment variable FLEX_INCLUDE to the directory containing
FlexLexer.h and ensure that flex/bison is available within the PATH. FlexLexer.h and ensure that flex/bison is available within the PATH.
To obtain Verilator sources download To obtain Verilator sources download
+1 -1
View File
@@ -174,7 +174,7 @@ once, after ``configure``:
# Install dependencies # Install dependencies
sudo apt install python3-pip sudo apt install python3-pip
# Create Python virtual environment in .venv: # Create Python virutal environment in .venv:
make venv make venv
# Or alternatively, to put it somewhere else: # Or alternatively, to put it somewhere else:
+1 -1
View File
@@ -153,7 +153,7 @@ experience depends on the use case, and some experimentation can pay
dividends. For a speedy debug cycle during development, especially on large dividends. For a speedy debug cycle during development, especially on large
designs where C++ compilation speed can dominate, consider using lower designs where C++ compilation speed can dominate, consider using lower
optimization to get to an executable faster. For throughput-oriented use optimization to get to an executable faster. For throughput-oriented use
cases, for example, regression testing, it is usually worth spending extra cases, for example, regressions, it is usually worth spending extra
compilation time to reduce total CPU time. compilation time to reduce total CPU time.
If you will be running many simulations on a single model, you can If you will be running many simulations on a single model, you can
-12
View File
@@ -162,18 +162,6 @@ Hierarchy blocks have some limitations, including:
- Delays are not allowed in hierarchy blocks. - Delays are not allowed in hierarchy blocks.
- Setting type parameters of a hierarchy block is unsupported.
- A ``defparam`` setting a parameter inside a hierarchy block is
unsupported.
- String parameter values of a hierarchy block containing a newline, a
double quote, ``/*``, or whitespace followed by ``//`` are unsupported.
- Values that are equal but differ in width or signedness (``5`` and
``8'd5``), including relative to the default value, are unsupported for a
hierarchy block parameter declared without a data type.
But, the following usage is supported: But, the following usage is supported:
- Nested hierarchy blocks. A hierarchy block may instantiate other - Nested hierarchy blocks. A hierarchy block may instantiate other
+7 -7
View File
@@ -592,10 +592,6 @@ List Of Warnings
``covergroup``, ``coverpoint``, and coverage options, and the ``covergroup``, ``coverpoint``, and coverage options, and the
construct was ignored. construct was ignored.
This includes crosses whose normal-bin Cartesian product exceeds
``2**32 - 1`` tuples. The limit is checked during Verilation for both
automatic and explicit cross bins.
Disabling the :option:`UNSUPPORTED` error also disables this warning. Disabling the :option:`UNSUPPORTED` error also disables this warning.
Ignoring this warning may make Verilator ignore lint checking on the Ignoring this warning may make Verilator ignore lint checking on the
@@ -2503,10 +2499,14 @@ List Of Warnings
.. option:: UNPACKED .. option:: UNPACKED
Historical, never issued since version 5.004. Warns that unpacked structs and unions are not supported because
:vlopt:`--structs-packed` was used, or by up through version 5.004.
Warned that unpacked structs and unions were not supported, or disabled Ignoring this warning will make Verilator treat the structure as packed,
by the since-removed `--structs-packed` option. which may make Verilator simulations differ from other simulators. This
downgrading may also result in what would typically be a legal unpacked
struct/array inside an unpacked struct/array becoming an illegal
unpacked struct/array inside a packed struct/array.
.. option:: UNSATCONSTR .. option:: UNSATCONSTR
+11 -2
View File
@@ -1031,6 +1031,15 @@ Profile-guided optimization make this a bit better, by adjusting mtask
scheduling, but this does not yet guide the packing into mtasks. scheduling, but this does not yet guide the packing into mtasks.
Performance Regression
++++++++++++++++++++++
It would be nice if we had a regression of large designs, with some
diversity of design styles, to test on both single- and multithreaded
modes. This would help to avoid performance regressions, and also to
evaluate the optimizations while minimizing the impact of parasitic noise.
Per-Instance Classes Per-Instance Classes
++++++++++++++++++++ ++++++++++++++++++++
@@ -1636,7 +1645,7 @@ There are some traps to avoid when running regression tests
Hat/Fedora/Centos offer *perl-Pod-Perldoc*', while Debian/Ubuntu/Linux Hat/Fedora/Centos offer *perl-Pod-Perldoc*', while Debian/Ubuntu/Linux
Mint offer \`perl-doc'. Mint offer \`perl-doc'.
- Running regression tests may exhaust resources on some Linux systems, - Running regression may exhaust resources on some Linux systems,
particularly file handles and user processes. Increase these to particularly file handles and user processes. Increase these to
respectively 16,384 and 4,096. The method of doing this is respectively 16,384 and 4,096. The method of doing this is
system-dependent, but on Fedora Linux it would require editing the system-dependent, but on Fedora Linux it would require editing the
@@ -2413,7 +2422,7 @@ driver.py Non-Scenario Arguments
.. option:: --quiet .. option:: --quiet
Suppress all output except for failures and progress messages every 15 Suppress all output except for failures and progress messages every 15
seconds. Intended for use only in automated regression testing. See also seconds. Intended for use only in automated regressions. See also
``--rerun``, and ``--verbose`` which is not the opposite of ``--quiet``. ``--rerun``, and ``--verbose`` which is not the opposite of ``--quiet``.
.. option:: --rerun .. option:: --rerun
-10
View File
@@ -305,7 +305,6 @@ Mahmoudy
Makefile Makefile
Makefiles Makefiles
Maksymenko Maksymenko
Manzoni
Marche Marche
Markley Markley
Marquet Marquet
@@ -347,7 +346,6 @@ Narcis
Nassim Nassim
Nauticus Nauticus
Nayak Nayak
Nazar
Nenakhov Nenakhov
Newgard Newgard
Nigam Nigam
@@ -590,7 +588,6 @@ Yurii
Zaruba Zaruba
Zhang Zhang
Zhi Zhi
Zibilyuk
abirkmanis abirkmanis
accessor accessor
accessors accessors
@@ -630,7 +627,6 @@ benchmarking
biguint biguint
binToOneHot binToOneHot
bindir bindir
binsof
biops biops
bisonpre bisonpre
bitOpTree bitOpTree
@@ -722,7 +718,6 @@ deduplication
defenv defenv
defname defname
defparam defparam
dehash
demangling demangling
dep dep
deparametrized deparametrized
@@ -742,7 +737,6 @@ devel
difftree difftree
dir dir
disambiguates disambiguates
discoverability
displayb displayb
distcc distcc
doxygen doxygen
@@ -953,7 +947,6 @@ makefile
makefiles makefiles
malloc malloc
manpages manpages
memoization
metacomment metacomment
metacomments metacomments
miree miree
@@ -1139,7 +1132,6 @@ shortint
shortreal shortreal
signame signame
signedness signedness
slp
sp sp
specparam specparam
splitme splitme
@@ -1190,7 +1182,6 @@ systemc
taskify taskify
tcmalloc tcmalloc
tcmalloc tcmalloc
teardown
tenghtt tenghtt
testbench testbench
testbenches testbenches
@@ -1264,7 +1255,6 @@ vcddiff
vcoverage vcoverage
vdhotre vdhotre
vec vec
vectorize
ventana ventana
venv venv
ver ver
+97 -152
View File
@@ -1178,29 +1178,19 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
} else if (formatAttr == VL_VFORMATATTR_STRING) { } else if (formatAttr == VL_VFORMATATTR_STRING) {
thingp = va_arg(ap, std::string*); thingp = va_arg(ap, std::string*);
if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override
} else if (formatAttr == VL_VFORMATATTR_ENUM } else if (formatAttr == VL_VFORMATATTR_ENUM) {
|| formatAttr == VL_VFORMATATTR_ENUM_SIGNED) { // Always <= VL_QUADSIZE; emit uses non-ENUM format for wider enums
const int numericAttr = formatAttr == VL_VFORMATATTR_ENUM_SIGNED
? VL_VFORMATATTR_SIGNED
: VL_VFORMATATTR_UNSIGNED;
lbits = va_arg(ap, int); lbits = va_arg(ap, int);
if (lbits <= VL_QUADSIZE) { ld = VL_VA_ARG_Q_(ap, lbits);
ld = VL_VA_ARG_Q_(ap, lbits); strwide.resize(2);
strwide.resize(2); WDataOutP strwidep = WDataOutP::external(strwide.data());
WDataOutP strwidep = WDataOutP::external(strwide.data()); VL_SET_WQ(strwidep, ld);
VL_SET_WQ(strwidep, ld); lwp = strwidep;
lwp = strwidep;
} else {
lwp = WDataInP::external(va_arg(ap, const EData*));
ld = VL_SET_QW(lwp);
}
lsb = lbits - 1; lsb = lbits - 1;
++argn; // Enum value is followed by the generated name string argument ++argn; // Enum value is followed by the generated name string argument
static_cast<void>(va_arg(ap, int)); // VL_VFORMATATTR_STRING static_cast<void>(va_arg(ap, int)); // VL_VFORMATATTR_STRING
enump = va_arg(ap, std::string*); enump = va_arg(ap, std::string*);
if (fmt != 'p' && fmt != 's') { if (enump && !enump->empty()) {
formatAttr = numericAttr;
} else if (enump && !enump->empty()) {
formatAttr = (fmt == 'p') ? VL_VFORMATATTR_COMPLEX : VL_VFORMATATTR_STRING; formatAttr = (fmt == 'p') ? VL_VFORMATATTR_COMPLEX : VL_VFORMATATTR_STRING;
thingp = const_cast<std::string*>(enump); thingp = const_cast<std::string*>(enump);
} else if (fmt == 'p' && widthSet && width == 0) { } else if (fmt == 'p' && widthSet && width == 0) {
@@ -1211,7 +1201,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
if (fmt == 'p') width = 0; if (fmt == 'p') width = 0;
widthSet = true; widthSet = true;
fmt = 'd'; fmt = 'd';
formatAttr = numericAttr; formatAttr = VL_VFORMATATTR_UNSIGNED;
} }
if (widthSet && width == 0) { if (widthSet && width == 0) {
while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb; while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb;
@@ -1292,7 +1282,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
output += t_tmp; output += t_tmp;
} else if (formatAttr == VL_VFORMATATTR_STRING) { } else if (formatAttr == VL_VFORMATATTR_STRING) {
const std::string* const strp = static_cast<const std::string*>(thingp); const std::string* const strp = static_cast<const std::string*>(thingp);
output += VL_TO_STRING(*strp); output += '"' + *strp + '"';
} else if (formatAttr == VL_VFORMATATTR_COMPLEX) { } else if (formatAttr == VL_VFORMATATTR_COMPLEX) {
const std::string* const strp = static_cast<const std::string*>(thingp); const std::string* const strp = static_cast<const std::string*>(thingp);
output += *strp; output += *strp;
@@ -1695,8 +1685,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
_vl_vsss_read_str(fp, floc, fromp, fstr, _vl_vsss_read_str(fp, floc, fromp, fstr,
std::back_insert_iterator<std::string>{t_tmp}, std::back_insert_iterator<std::string>{t_tmp},
"0123456789+-xXzZ?_"); "0123456789+-xXzZ?_");
t_tmp.erase(std::remove(t_tmp.begin(), t_tmp.end(), '_'), t_tmp.end()); if (!t_tmp[0]) goto done;
if (t_tmp.empty()) goto done;
if (formatAttr == VL_VFORMATATTR_SIGNED) { if (formatAttr == VL_VFORMATATTR_SIGNED) {
QData ld = 0; QData ld = 0;
std::sscanf(t_tmp.c_str(), "%30" PRIu64, &ld); std::sscanf(t_tmp.c_str(), "%30" PRIu64, &ld);
@@ -1833,8 +1822,8 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
return got; return got;
done: done:
// Scan stopped early; EOF only if input ended before the first conversion // Scan stopped early, return parsed or EOF
if (got == 0 && _vl_vsss_eof(fp, floc)) return -1; if (_vl_vsss_eof(fp, floc)) return -1;
return got; return got;
} }
@@ -2402,10 +2391,8 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
VL_ZERO_W(rbits, rwp); VL_ZERO_W(rbits, rwp);
switch (std::tolower(fmt)) { switch (std::tolower(fmt)) {
case 'd': { case 'd': {
std::string digits{dp};
digits.erase(std::remove(digits.begin(), digits.end(), '_'), digits.end());
int64_t lld = 0; int64_t lld = 0;
std::sscanf(digits.c_str(), "%30" PRId64, &lld); std::sscanf(dp, "%30" PRId64, &lld);
VL_SET_WQ(rwp, lld); VL_SET_WQ(rwp, lld);
break; break;
} }
@@ -2501,30 +2488,6 @@ std::string VL_TO_STRING(QData lhs) {
std::string VL_TO_STRING(double lhs) { std::string VL_TO_STRING(double lhs) {
return VL_SFORMATF_N_NX("%g", 1, VL_VFORMATATTR_DOUBLE, lhs); return VL_SFORMATF_N_NX("%g", 1, VL_VFORMATATTR_DOUBLE, lhs);
} }
std::string VL_TO_STRING(const std::string& obj) VL_PURE {
std::string out{"\""};
out.reserve(obj.size() + 2);
for (const unsigned char ch : obj) {
switch (ch) {
case '\n': out += "\\n"; break;
case '\r': out += "\\r"; break;
case '\t': out += "\\t"; break;
case '"': out += "\\\""; break;
case '\\': out += "\\\\"; break;
default:
if (std::isprint(ch)) {
out += static_cast<char>(ch);
} else {
out += '\\';
out += static_cast<char>('0' + ((ch >> 6) & 3));
out += static_cast<char>('0' + ((ch >> 3) & 7));
out += static_cast<char>('0' + (ch & 7));
}
break;
}
}
return out + '"';
}
std::string VL_TO_STRING_W(int words, const WDataInP obj) { std::string VL_TO_STRING_W(int words, const WDataInP obj) {
return VL_SFORMATF_N_NX("'h%0x", 1, VL_VFORMATATTR_UNSIGNED, words * VL_EDATASIZE, obj); return VL_SFORMATF_N_NX("'h%0x", 1, VL_VFORMATATTR_UNSIGNED, words * VL_EDATASIZE, obj);
} }
@@ -3131,16 +3094,41 @@ VerilatedContext::Serialized::Serialized() {
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; } bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE { void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
if (m_ns.m_assertCtlsLocked) return; if (assertCtlsLocked()) return;
// Set all assert and directive types when true, clear otherwise. // Set all assert and directive types when true, clear otherwise.
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag; m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
} }
bool VerilatedContext::assertOnGet(VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
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 {
if (assertCtlsLocked()) return;
m_s.m_assertOn |= assertOnMask(types, directives);
}
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
if (assertCtlsLocked()) return;
m_s.m_assertOn &= ~assertOnMask(types, directives);
}
bool VerilatedContext::assertCtlsLocked() const VL_MT_SAFE { return m_ns.m_assertCtlsLocked; }
void VerilatedContext::assertCtlsLocked(bool flag) VL_MT_SAFE { m_ns.m_assertCtlsLocked = flag; }
void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types, void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE { VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the // 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. // selected bits until Unlock; On/Off/Kill leave locked bits unchanged.
// +verilator+assert+lock freezes everything, including Lock/Unlock itself. // +verilator+assert+lock freezes everything, including Lock/Unlock itself.
if (m_ns.m_assertCtlsLocked) return; if (assertCtlsLocked()) return;
const uint32_t mask = assertOnMask(types, directives); const uint32_t mask = assertOnMask(types, directives);
const uint32_t lockedMask = mask & ~m_s.m_assertLock; const uint32_t lockedMask = mask & ~m_s.m_assertLock;
switch (controlType) { switch (controlType) {
@@ -3190,6 +3178,26 @@ void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t typ
.c_str()); .c_str());
} }
} }
uint32_t
VerilatedContext::assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
const uint32_t mask = assertOnMask(type, directive);
if (!mask) return 0;
switch (query) { // LCOV_EXCL_BR_LINE
case VerilatedAssertCtlQuery::ASSERT_CTL_ON: return (m_s.m_assertOn & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_KILL:
assert(mask && (mask & (mask - 1)) == 0);
return m_s.m_assertKill[VL_CLOG2_I(mask)];
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
return (m_s.m_assertPassOnVacuous & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_NONVACUOUS:
return (m_s.m_assertPassOnNonvacuous & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_FAIL_ON: return (m_s.m_assertFailOn & mask) != 0;
default: // LCOV_EXCL_START
VL_FATAL_MT("", 0, "", "Internal: Bad assertCtlGet query");
VL_UNREACHABLE;
} // LCOV_EXCL_STOP
}
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE { void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex}; const VerilatedLockGuard lock{m_mutex};
m_s.m_calcUnusedSigs = flag; m_s.m_calcUnusedSigs = flag;
@@ -3373,15 +3381,6 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
return vl_time_str(timeprecision()); return vl_time_str(timeprecision());
} }
static void warnThreadsOversubscribed(unsigned threads) VL_MT_SAFE {
const unsigned threadsAvailableToProcess = VlOs::getProcessDefaultParallelism();
if (threads > threadsAvailableToProcess) {
VL_PRINTF_MT("%%Warning: Process has %u hardware threads available, but simulation thread "
"count set to %u. This will likely cause significant slowdown.\n",
threadsAvailableToProcess, threads);
}
}
void VerilatedContext::threads(unsigned n) { void VerilatedContext::threads(unsigned n) {
if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "Simulation threads must be >= 1"); if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "Simulation threads must be >= 1");
@@ -3392,10 +3391,14 @@ void VerilatedContext::threads(unsigned n) {
} }
m_useNumaAssign = true; m_useNumaAssign = true;
m_threadsSet = true;
if (m_threads == n) return; // To avoid unnecessary warnings if (m_threads == n) return; // To avoid unnecessary warnings
m_threads = n; m_threads = n;
warnThreadsOversubscribed(m_threads); const unsigned threadsAvailableToProcess = VlOs::getProcessDefaultParallelism();
if (m_threads > threadsAvailableToProcess) {
VL_PRINTF_MT("%%Warning: Process has %u hardware threads available, but simulation thread "
"count set to %u. This will likely cause significant slowdown.\n",
threadsAvailableToProcess, m_threads);
}
} }
void VerilatedContext::useNumaAssign(bool flag) { m_useNumaAssign = flag; } void VerilatedContext::useNumaAssign(bool flag) { m_useNumaAssign = flag; }
@@ -3461,15 +3464,7 @@ void VerilatedContext::addModel(const VerilatedModel* modelp) {
} }
} }
VerilatedVirtualBase* VerilatedContext::threadPoolp(unsigned modelThreads) { VerilatedVirtualBase* VerilatedContext::threadPoolp() {
// The thread count defaults to the number of threads available to the process, which may be
// fewer than a model uses, e.g. a model Verilated with --threads 4 on a single core machine.
// Oversubscribing is slow but works, so grow to fit the model, unless the user picked the
// thread count themselves, in which case addModel() reports the mismatch instead.
if (VL_UNLIKELY(modelThreads > m_threads) && !m_threadsSet && !m_threadPool) {
m_threads = modelThreads;
warnThreadsOversubscribed(m_threads);
}
if (m_threads == 1) return nullptr; if (m_threads == 1) return nullptr;
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1}); if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
return m_threadPool.get(); return m_threadPool.get();
@@ -3585,7 +3580,7 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
std::string str; std::string str;
uint64_t u64; uint64_t u64;
if (arg == "+verilator+assert+lock") { if (arg == "+verilator+assert+lock") {
m_ns.m_assertCtlsLocked = true; assertCtlsLocked(true);
} else if (commandArgVlString(arg, "+verilator+coverage+file+", str)) { } else if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
coverageFilename(str); coverageFilename(str);
} else if (arg == "+verilator+debug") { } else if (arg == "+verilator+debug") {
@@ -3750,17 +3745,11 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
// VerilatedContext:: Methods - scopes // VerilatedContext:: Methods - scopes
void VerilatedContext::scopesDump() const VL_MT_SAFE { void VerilatedContext::scopesDump() const VL_MT_SAFE {
{ const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
const VerilatedLockGuard lock{m_impdatap->m_nameMutex}; VL_PRINTF_MT(" scopesDump:\n");
VL_PRINTF_MT(" scopesDump:\n"); for (const auto& i : m_impdatap->m_nameMap) {
for (const auto& i : m_impdatap->m_nameMap) { const VerilatedScope* const scopep = i.second;
const VerilatedScope* const scopep = i.second; scopep->scopeDump();
scopep->scopeDump();
}
}
{
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
for (const auto& i : m_impdatap->m_ifaceRefMap) i.second.ifaceRefDump();
} }
VL_PRINTF_MT("\n"); VL_PRINTF_MT("\n");
} }
@@ -3790,31 +3779,6 @@ const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
return &(impp()->m_impdatap->m_nameMap); return &(impp()->m_impdatap->m_nameMap);
} }
void VerilatedContextImp::ifaceRefInsert(const VerilatedIfaceRef& ifaceRef) VL_MT_SAFE {
// Slow ok - called once/interface-reference at construction
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
m_impdatap->m_ifaceRefMap.emplace(ifaceRef.fullname(), ifaceRef);
}
void VerilatedContextImp::ifaceRefErase(const std::string& fullname,
const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/interface-reference at destruction
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
const auto it = m_impdatap->m_ifaceRefMap.find(fullname);
// Models sharing an instance name collide on the key; only erase our own,
// so tearing one down leaves another's live reference registered
if (it != m_impdatap->m_ifaceRefMap.end() && it->second.scopep() == scopep) {
m_impdatap->m_ifaceRefMap.erase(it);
}
}
const VerilatedIfaceRef*
VerilatedContext::ifaceRefFind(const char* namep) const VL_MT_SAFE_POSTINIT {
// Thread safe only assuming this is called only after model construction completed
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
const auto& it = m_impdatap->m_ifaceRefMap.find(namep);
if (VL_UNLIKELY(it == m_impdatap->m_ifaceRefMap.end())) return nullptr;
return &it->second;
}
//====================================================================== //======================================================================
// VerilatedContext:: Methods - trace // VerilatedContext:: Methods - trace
@@ -4185,6 +4149,28 @@ std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { retu
//====================================================================== //======================================================================
// VerilatedVar:: Methods // VerilatedVar:: Methods
// 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;
case VLVT_UINT8: size = sizeof(CData); break;
case VLVT_UINT16: size = sizeof(SData); break;
case VLVT_UINT32: size = sizeof(IData); break;
case VLVT_UINT64: size = sizeof(QData); break;
case VLVT_WDATA: size = VL_WORDS_I(entBits()) * sizeof(IData); break;
default: size = 0; break; // LCOV_EXCL_LINE
}
return size;
}
size_t VerilatedVarProps::totalSize() const {
size_t size = entSize();
for (int udim = 0; udim < udims(); ++udim) size *= m_unpacked[udim].elements();
return size;
}
void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE { void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE {
if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr; if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr;
if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr; if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr;
@@ -4326,41 +4312,6 @@ void VerilatedScope::scopesConstructFromTable(const VlScopeTableEntry* entp, siz
} }
} }
// Prefix with the model instance name, as VerilatedScope's constructor does
static std::string vl_ifaceRefFullname(const VerilatedSyms* symsp, const char* suffixp) {
const char* const prefixp = symsp->name();
std::string out{prefixp};
if (*prefixp && *suffixp) out += '.';
out += suffixp;
return out;
}
void VerilatedScope::ifaceRefsInsertFromTable(const VlIfaceRefTableEntry* entp, size_t n,
VerilatedSyms* symsp) VL_MT_UNSAFE {
// Use the model's own context; at destruction threadContextp() may be another's
VerilatedContextImp* const impp = symsp->_vm_contextp__->impp();
uint8_t* const base = reinterpret_cast<uint8_t*>(symsp);
for (size_t i = 0; i < n; ++i) {
const VlIfaceRefTableEntry& e = entp[i];
const VerilatedScope* const scopep
= *reinterpret_cast<VerilatedScope**>(base + e.ptrOffset);
impp->ifaceRefInsert(
VerilatedIfaceRef{scopep, e.namep, vl_ifaceRefFullname(symsp, e.suffixp), e.modportp});
}
}
void VerilatedScope::ifaceRefsEraseFromTable(const VlIfaceRefTableEntry* entp, size_t n,
const VerilatedSyms* symsp) VL_MT_UNSAFE {
VerilatedContextImp* const impp = symsp->_vm_contextp__->impp();
uint8_t* const base = reinterpret_cast<uint8_t*>(const_cast<VerilatedSyms*>(symsp));
for (size_t i = 0; i < n; ++i) {
const VlIfaceRefTableEntry& e = entp[i];
const VerilatedScope* const scopep
= *reinterpret_cast<VerilatedScope**>(base + e.ptrOffset);
impp->ifaceRefErase(vl_ifaceRefFullname(symsp, e.suffixp), scopep);
}
}
VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam, VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int udims, VerilatedVarType vltype, int vlflags, int udims,
uint32_t entSize...) VL_MT_UNSAFE { uint32_t entSize...) VL_MT_UNSAFE {
@@ -4478,7 +4429,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
// Slowpath - Called only when find has failed // Slowpath - Called only when find has failed
const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum) const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum)
+ "' but scope wasn't set, perhaps due to dpi import call without " + "' but scope wasn't set, perhaps due to dpi import call without "
+ "'context', or missing svSetScope (IEEE 1800-2023 35.5.3)"); + "'context', or missing svSetScope. See IEEE 1800-2023 35.5.3.");
VL_FATAL_MT("unknown", 0, "", msg.c_str()); VL_FATAL_MT("unknown", 0, "", msg.c_str());
return nullptr; return nullptr;
} }
@@ -4506,12 +4457,6 @@ void VerilatedScope::scopeDump() const {
} }
} }
void VerilatedIfaceRef::ifaceRefDump() const VL_MT_SAFE_POSTINIT {
VL_PRINTF_MT(" IFACEREF %p: %s -> %s", this, fullname(), scopep()->name());
if (hasModport()) VL_PRINTF_MT(".%s", modport());
VL_PRINTF_MT("\n");
}
void VerilatedHierarchy::add(const VerilatedScope* fromp, const VerilatedScope* top) { void VerilatedHierarchy::add(const VerilatedScope* fromp, const VerilatedScope* top) {
VerilatedImp::hierarchyAdd(fromp, top); VerilatedImp::hierarchyAdd(fromp, top);
} }
+42 -141
View File
@@ -100,9 +100,6 @@ class VerilatedFstC;
class VerilatedFstSc; class VerilatedFstSc;
class VerilatedScope; class VerilatedScope;
class VerilatedScopeNameMap; class VerilatedScopeNameMap;
class VerilatedIfaceRef;
class VerilatedIfaceRefMap;
struct VlIfaceRefTableEntry;
template <typename, typename> template <typename, typename>
class VerilatedTrace; class VerilatedTrace;
class VerilatedTraceBaseC; class VerilatedTraceBaseC;
@@ -112,7 +109,6 @@ class VerilatedVarNameMap;
class VerilatedVcd; class VerilatedVcd;
class VerilatedVcdC; class VerilatedVcdC;
class VerilatedVcdSc; class VerilatedVcdSc;
class VlCovRegistry;
//========================================================================= //=========================================================================
// Basic types // Basic types
@@ -261,7 +257,7 @@ class VL_SCOPED_CAPABILITY VerilatedLockGuard final {
VL_UNCOPYABLE(VerilatedLockGuard); VL_UNCOPYABLE(VerilatedLockGuard);
private: private:
VerilatedMutex& m_mutexr; // Mutex protecting the guard VerilatedMutex& m_mutexr;
public: public:
/// Construct and hold given mutex lock until destruction or unlock() /// Construct and hold given mutex lock until destruction or unlock()
@@ -305,34 +301,6 @@ public:
#endif #endif
}; };
// Internals: VlFileLineDebug stores a SystemVerilog source code location. Used in
// VlCoroutineHandle for debugging purposes, and to locate covergroup weight errors.
class VlFileLineDebug final {
// MEMBERS
#ifdef VL_DEBUG
const char* m_filename = nullptr; // Filename from sources, nullptr for unlnown
int m_lineno = 0; // Line number from sources
#endif
public:
// CONSTRUCTORS
VlFileLineDebug() = default;
VlFileLineDebug(const char* filename, int lineno)
#ifdef VL_DEBUG
: m_filename{filename}
, m_lineno{lineno}
#endif
{
}
// METHODS
#ifdef VL_DEBUG
const char* filename() const { return m_filename; }
int lineno() const { return m_lineno; }
#endif
};
class VlExecutionProfilerBase; class VlExecutionProfilerBase;
//========================================================================= //=========================================================================
@@ -408,7 +376,8 @@ class VerilatedEvalLoop final {
const uint32_t m_convergeLimit; // --converge-limit from compiler command line const uint32_t m_convergeLimit; // --converge-limit from compiler command line
// Where to record --prof-exec sections, or null if not profiling // Where to record --prof-exec sections, or null if not profiling
VlExecutionProfilerBase* m_profilerp = nullptr; VlExecutionProfilerBase* m_profilerp = nullptr;
bool m_profTopLevel = false; // Top level model during profiling // Whether this is the top level model during profiling
bool m_profTopLevel = false;
public: public:
// CONSTRUCTORS // CONSTRUCTORS
@@ -510,8 +479,8 @@ private:
= ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits = ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits
+ 1; + 1;
// Build the assertion-control bit mask for the given assertion x directive types. // Build the assertion-control bit mask for the given assertion x directive types.
static constexpr uint32_t assertOnMask(VerilatedAssertType_t types, static uint32_t assertOnMask(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_PURE; VerilatedAssertDirectiveType_t directives) VL_PURE;
static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1; static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1;
// No termination request has stamped m_finishPendingTime yet // No termination request has stamped m_finishPendingTime yet
static constexpr uint64_t TIME_UNSET = ~0ULL; static constexpr uint64_t TIME_UNSET = ~0ULL;
@@ -529,18 +498,20 @@ protected:
// No std::strings or pointers or will serialize badly! // No std::strings or pointers or will serialize badly!
// Fast path // Fast path
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
// Enabled assertions, for each VerilatedAssertType we store 3-bits, one for each directive std::atomic<uint32_t> m_assertOn{
// type. Last bit guards internal directive types. std::numeric_limits<uint32_t>::max()}; // Enabled assertions,
std::atomic<uint32_t> m_assertOn{std::numeric_limits<uint32_t>::max()}; // 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 std::atomic<uint32_t> m_assertLock{0}; // Locked assertion bits (IEEE 1800-2023 20.11
// Lock/Unlock); same layout as m_assertOn. While // Lock/Unlock); same layout as m_assertOn. While
// a bit is locked, On/Off/Kill leave it unchanged. // a bit is locked, On/Off/Kill leave it unchanged.
// Enabled vacuous pass actions std::atomic<uint32_t> m_assertPassOnVacuous{
std::atomic<uint32_t> m_assertPassOnVacuous{std::numeric_limits<uint32_t>::max()}; std::numeric_limits<uint32_t>::max()}; // Enabled vacuous pass actions
// Enabled nonvacuous pass actions std::atomic<uint32_t> m_assertPassOnNonvacuous{
std::atomic<uint32_t> m_assertPassOnNonvacuous{std::numeric_limits<uint32_t>::max()}; std::numeric_limits<uint32_t>::max()}; // Enabled nonvacuous pass actions
// Enabled fail actions std::atomic<uint32_t> m_assertFailOn{
std::atomic<uint32_t> m_assertFailOn{std::numeric_limits<uint32_t>::max()}; std::numeric_limits<uint32_t>::max()}; // Enabled fail actions
std::array<std::atomic<uint32_t>, ASSERT_CONTROL_SLOT_COUNT> m_assertKill{}; std::array<std::atomic<uint32_t>, ASSERT_CONTROL_SLOT_COUNT> m_assertKill{};
bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated
bool m_fatalOnError = true; // Fatal on $stop/non-fatal error bool m_fatalOnError = true; // Fatal on $stop/non-fatal error
@@ -574,7 +545,8 @@ protected:
// A worker queues $finish before the main thread callback can set m_gotFinish. // A worker queues $finish before the main thread callback can set m_gotFinish.
std::atomic<uint32_t> m_finishPending{0}; // Number of queued $finish callbacks std::atomic<uint32_t> m_finishPending{0}; // Number of queued $finish callbacks
std::atomic<uint64_t> m_finishPendingTime{TIME_UNSET}; // Time of the first callback std::atomic<uint64_t> m_finishPendingTime{TIME_UNSET}; // Time of the first callback
std::atomic<bool> m_assertCtlsLocked{false}; // All assertion-control updates are ignored std::atomic<bool> m_assertCtlsLocked{
false}; // When true, all assertion-control updates are ignored
int m_stopReserved = 0; // Posted $stop requests not yet executed int m_stopReserved = 0; // Posted $stop requests not yet executed
bool m_executingFinal = false; // Running generated final() code bool m_executingFinal = false; // Running generated final() code
uint64_t m_profExecStart = 1; // +prof+exec+start time uint64_t m_profExecStart = 1; // +prof+exec+start time
@@ -608,8 +580,6 @@ protected:
const std::unique_ptr<VerilatedContextImpData> m_impdatap; const std::unique_ptr<VerilatedContextImpData> m_impdatap;
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread) // Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
unsigned m_threads = VlOs::getProcessDefaultParallelism(); unsigned m_threads = VlOs::getProcessDefaultParallelism();
// True if m_threads was set by the user, rather than being the default
bool m_threadsSet = false;
// Use numa automatic CPU-to-thread assignment // Use numa automatic CPU-to-thread assignment
bool m_useNumaAssign = false; bool m_useNumaAssign = false;
// Number of threads in added models // Number of threads in added models
@@ -620,9 +590,6 @@ protected:
std::unique_ptr<VerilatedVirtualBase> m_executionProfiler; std::unique_ptr<VerilatedVirtualBase> m_executionProfiler;
// Coverage access // Coverage access
std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep() std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep()
// Covergroup type/instance nodes. Covergroup data is always collected,
// independent of whether coverage data is recorded (--coverage).
std::unique_ptr<VerilatedVirtualBase> m_covergroupsp; // Pointer for covergroupRegistryp()
// File I/O // File I/O
// Not serialized // Not serialized
@@ -633,8 +600,9 @@ protected:
// List of free descriptors in the MCT region [4, 32) // List of free descriptors in the MCT region [4, 32)
std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex); std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex);
// Magic to check for bad construction
static constexpr uint64_t MAGIC = 0xC35F9A6E5298EE6EULL; // SHA256 "VerilatedContext" static constexpr uint64_t MAGIC = 0xC35F9A6E5298EE6EULL; // SHA256 "VerilatedContext"
uint64_t m_magic = MAGIC; // Magic to check for bad construction uint64_t m_magic = MAGIC;
private: private:
// CONSTRUCTORS // CONSTRUCTORS
@@ -651,9 +619,28 @@ public:
bool assertOn() const VL_MT_SAFE; bool assertOn() const VL_MT_SAFE;
/// Enable all assertion types /// Enable all assertion types
void assertOn(bool flag) VL_MT_SAFE; void assertOn(bool flag) VL_MT_SAFE;
/// Get enabled status for given assertion types
bool assertOnGet(VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
/// Set enabled status for given assertion types
void assertOnSet(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
/// Clear enabled status for given assertion types
void assertOnClear(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
/// Return if assertion-control updates are locked. When locked, RTL assert
// control statements ($asserton/$assertoff/$assertcontrol) are ignored, as
// are updates from the C++ API.
bool assertCtlsLocked() const VL_MT_SAFE;
/// Lock/unlock assertion-control updates.
void assertCtlsLocked(bool flag) VL_MT_SAFE;
/// Apply assertion control for given control, assertion, and directive types /// Apply assertion control for given control, assertion, and directive types
void assertCtl(uint32_t controlType, VerilatedAssertType_t types, void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE; VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
/// Get assertion-control runtime state. Boolean queries return 0/1, Kill returns
/// the generation count.
uint32_t assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
/// Return if calculating of unused signals (for traces) /// Return if calculating of unused signals (for traces)
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; } bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
/// Enable calculation of unused signals (for traces) /// Enable calculation of unused signals (for traces)
@@ -672,8 +659,6 @@ public:
/// Return VerilatedCovContext, allocate if needed /// Return VerilatedCovContext, allocate if needed
/// Note if get unresolved reference then likely forgot to link verilated_cov.cpp /// Note if get unresolved reference then likely forgot to link verilated_cov.cpp
VerilatedCovContext* coveragep() VL_MT_SAFE; VerilatedCovContext* coveragep() VL_MT_SAFE;
/// Returns VlCovRegistry. Allocated on-demand
VlCovRegistry* covergroupRegistryp() VL_MT_SAFE;
/// Return debug level /// Return debug level
static inline int debug() VL_MT_SAFE; /// Set debug level static inline int debug() VL_MT_SAFE; /// Set debug level
/// Debug is currently global, but for forward compatibility have a per-context method /// Debug is currently global, but for forward compatibility have a per-context method
@@ -829,10 +814,6 @@ public:
bool stopRequestReserve(bool maybe) VL_MT_SAFE; bool stopRequestReserve(bool maybe) VL_MT_SAFE;
void stopRequestRelease() VL_MT_SAFE; void stopRequestRelease() VL_MT_SAFE;
// Internal: assertCtlGet() for generated code, with constant type and directive
template <VerilatedAssertType_t T_Type, VerilatedAssertDirectiveType_t T_Directive>
VL_ATTR_ALWINLINE uint32_t assertCtlGet(VerilatedAssertCtlQuery query) const VL_MT_SAFE;
// Internal: access to implementation class // Internal: access to implementation class
VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); } VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); }
const VerilatedContextImp* impp() const VL_MT_SAFE { const VerilatedContextImp* impp() const VL_MT_SAFE {
@@ -841,9 +822,7 @@ public:
// Internal: Model and thread setup // Internal: Model and thread setup
void addModel(const VerilatedModel* modelp); void addModel(const VerilatedModel* modelp);
// Get the thread pool, creating it if needed. 'modelThreads' is the parallelism of the model VerilatedVirtualBase* threadPoolp();
// being constructed, so that the context can grow to the number of threads it requires.
VerilatedVirtualBase* threadPoolp(unsigned modelThreads = 1);
void prepareClone(); void prepareClone();
VerilatedVirtualBase* threadPoolpOnClone(); VerilatedVirtualBase* threadPoolpOnClone();
VerilatedVirtualBase* VerilatedVirtualBase*
@@ -889,9 +868,6 @@ public:
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE; const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE; const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
// Internal: Find interface reference by fully qualified path
const VerilatedIfaceRef* ifaceRefFind(const char* namep) const VL_MT_SAFE_POSTINIT;
// Internal: Serialization setup // Internal: Serialization setup
static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); } static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; } void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
@@ -921,32 +897,6 @@ public: // But for internal use only
virtual const char* name() const = 0; virtual const char* name() const = 0;
}; };
// An interface reference port, and the concrete interface it is connected to.
// Used for VPI; references are not scopes, so are not in VerilatedScopeNameMap.
class VerilatedIfaceRef final {
const VerilatedScope* m_scopep = nullptr; // Concrete interface referred to
const char* m_namep = ""; // Name of the reference port
// Fully qualified path; owned, as the instance name prefix is set at construction
std::string m_fullname;
const char* m_modportp = ""; // Modport name, or "" if none
public:
VerilatedIfaceRef() = default;
VerilatedIfaceRef(const VerilatedScope* scopep, const char* namep, const std::string& fullname,
const char* modportp)
: m_scopep{scopep}
, m_namep{namep}
, m_fullname{fullname}
, m_modportp{modportp} {}
~VerilatedIfaceRef() = default;
// ACCESSORS
const VerilatedScope* scopep() const VL_MT_SAFE_POSTINIT { return m_scopep; }
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
const char* fullname() const VL_MT_SAFE_POSTINIT { return m_fullname.c_str(); }
const char* modport() const VL_MT_SAFE_POSTINIT { return m_modportp; }
bool hasModport() const VL_MT_SAFE_POSTINIT { return m_modportp[0] != '\0'; }
void ifaceRefDump() const VL_MT_SAFE_POSTINIT;
};
//=========================================================================== //===========================================================================
// Verilator scope information class // Verilator scope information class
// Used for internal VPI implementation, and introspection into scopes // Used for internal VPI implementation, and introspection into scopes
@@ -958,9 +908,8 @@ public:
enum Type : uint8_t { enum Type : uint8_t {
SCOPE_MODULE, SCOPE_MODULE,
SCOPE_OTHER, SCOPE_OTHER,
SCOPE_PACKAGE, SCOPE_PACKAGE
SCOPE_INTERFACE }; // Type of a scope, currently only module and package are interesting
}; // Type of a scope, currently only module, package and interface are interesting
private: private:
// Fastpath: // Fastpath:
VerilatedSyms* const m_symsp; // Symbol table VerilatedSyms* const m_symsp; // Symbol table
@@ -993,10 +942,6 @@ public: // But internals only - called from verilated modules, VerilatedSyms
void varsInsertFromTable(const VlVarTableEntry* entp, size_t n, void* basep) VL_MT_UNSAFE; void varsInsertFromTable(const VlVarTableEntry* entp, size_t n, void* basep) VL_MT_UNSAFE;
static void scopesConstructFromTable(const VlScopeTableEntry* entp, size_t n, static void scopesConstructFromTable(const VlScopeTableEntry* entp, size_t n,
VerilatedSyms* symsp) VL_MT_UNSAFE; VerilatedSyms* symsp) VL_MT_UNSAFE;
static void ifaceRefsInsertFromTable(const VlIfaceRefTableEntry* entp, size_t n,
VerilatedSyms* symsp) VL_MT_UNSAFE;
static void ifaceRefsEraseFromTable(const VlIfaceRefTableEntry* entp, size_t n,
const VerilatedSyms* symsp) VL_MT_UNSAFE;
// ACCESSORS // ACCESSORS
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; } const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; } const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }
@@ -1013,15 +958,6 @@ public: // But internals only - called from verilated modules, VerilatedSyms
VerilatedContext* contextp() const { return m_symsp->_vm_contextp__; } VerilatedContext* contextp() const { return m_symsp->_vm_contextp__; }
}; };
// One interface reference, consumed by VerilatedScope::ifaceRefsInsertFromTable()
struct VlIfaceRefTableEntry final {
uint32_t ptrOffset; // offsetof of the referred-to __Vscopep_* member within the Syms object
const char* namep; // Name of the reference port
// Path within the model; as VlScopeTableEntry::namep, instance name prepended at construction
const char* suffixp;
const char* modportp; // Modport name, or "" if none
};
// One scope, consumed by VerilatedScope::scopesConstructFromTable(); replaces // One scope, consumed by VerilatedScope::scopesConstructFromTable(); replaces
// per-scope 'new VerilatedScope{...}' statements, which compiles faster at scale. // per-scope 'new VerilatedScope{...}' statements, which compiles faster at scale.
struct VlScopeTableEntry final { struct VlScopeTableEntry final {
@@ -1340,40 +1276,5 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
#endif #endif
} }
// Defined here, not in-class: VL_CLOG2_I / VL_FATAL_MT (verilated_funcs.h) are not yet in scope
constexpr 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;
}
template <VerilatedAssertType_t T_Type, VerilatedAssertDirectiveType_t T_Directive>
uint32_t VerilatedContext::assertCtlGet(VerilatedAssertCtlQuery query) const VL_MT_SAFE {
// A constexpr local forces compile-time evaluation of the mask, which a plain
// assertOnMask() call does not get from GCC at -Os
constexpr uint32_t mask = assertOnMask(T_Type, T_Directive);
if (!mask) return 0;
// Explicit load(): G++ -Os inlines it but not the implicit conversion.
switch (query) { // LCOV_EXCL_BR_LINE
case VerilatedAssertCtlQuery::ASSERT_CTL_ON: return (m_s.m_assertOn.load() & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_KILL:
assert(mask && (mask & (mask - 1)) == 0);
return m_s.m_assertKill[VL_CLOG2_I(mask)].load();
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
return (m_s.m_assertPassOnVacuous.load() & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_NONVACUOUS:
return (m_s.m_assertPassOnNonvacuous.load() & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_FAIL_ON:
return (m_s.m_assertFailOn.load() & mask) != 0;
default: // LCOV_EXCL_START
VL_FATAL_MT("", 0, "", "Internal: Bad assertCtlGet query");
VL_UNREACHABLE;
} // LCOV_EXCL_STOP
}
#undef VERILATOR_VERILATED_H_INTERNAL_ #undef VERILATOR_VERILATED_H_INTERNAL_
#endif // Guard #endif // Guard
-10
View File
@@ -99,16 +99,6 @@ VK_CPPFLAGS_ALWAYS += \
-DVM_VPI=$(VM_VPI) \ -DVM_VPI=$(VM_VPI) \
$(CFG_CXXFLAGS_NO_UNUSED) \ $(CFG_CXXFLAGS_NO_UNUSED) \
# Left undefined when there is no generated model, so misuse fails at compile time
ifneq ($(VM_PREFIX_INCLUDE),)
VK_CPPFLAGS_ALWAYS += -DVM_PREFIX_INCLUDE="$(VM_PREFIX_INCLUDE)"
endif
# Left undefined when the design has no DPI, so misuse fails at compile time
ifneq ($(VM_PREFIX_INCLUDE_DPI),)
VK_CPPFLAGS_ALWAYS += -DVM_PREFIX_INCLUDE_DPI="$(VM_PREFIX_INCLUDE_DPI)"
endif
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
VK_CPPFLAGS_WALL += -Wall $(CFG_CXXFLAGS_WEXTRA) -Werror VK_CPPFLAGS_WALL += -Wall $(CFG_CXXFLAGS_WEXTRA) -Werror
endif endif
+1 -13
View File
@@ -39,34 +39,26 @@ VLCOVGEN_ITEM("'name':'per_instance','short':'P', 'group':1, 'default':0, 'd
VLCOVGEN_ITEM("'name':'thresh', 'short':'s', 'group':1, 'default':None, 'descr':'Number of hits to consider covered (aka at_least)'") VLCOVGEN_ITEM("'name':'thresh', 'short':'s', 'group':1, 'default':None, 'descr':'Number of hits to consider covered (aka at_least)'")
VLCOVGEN_ITEM("'name':'type', 'short':'t', 'group':1, 'default':'', 'descr':'Type of coverage (block, line, fsm, etc)'") VLCOVGEN_ITEM("'name':'type', 'short':'t', 'group':1, 'default':'', 'descr':'Type of coverage (block, line, fsm, etc)'")
// Bin attributes // Bin attributes
VLCOVGEN_ITEM("'name':'bin', 'short':'B', 'group':0, 'default':'', 'descr':'Bin name for covergroup coverage points'")
VLCOVGEN_ITEM("'name':'bin_type', 'short':'Bt', 'group':0, 'default':'', 'descr':'Kind of a covergroup bin that is not coverable: ignore, illegal, or default'")
VLCOVGEN_ITEM("'name':'comment', 'short':'o', 'group':0, 'default':'', 'descr':'Textual description for the item'")
VLCOVGEN_ITEM("'name':'cross', 'short':'C', 'group':0, 'default':0, 'descr':'True for cross coverage points'")
VLCOVGEN_ITEM("'name':'cross_bins', 'short':'Cb', 'group':0, 'default':'', 'descr':'Comma-separated per-dimension bin names for cross coverage points'") VLCOVGEN_ITEM("'name':'cross_bins', 'short':'Cb', 'group':0, 'default':'', 'descr':'Comma-separated per-dimension bin names for cross coverage points'")
VLCOVGEN_ITEM("'name':'comment', 'short':'o', 'group':0, 'default':'', 'descr':'Textual description for the item'")
VLCOVGEN_ITEM("'name':'fsm_from', 'short':'Ff', 'group':0, 'default':'', 'descr':'FSM source state name for structured FSM coverage points'") VLCOVGEN_ITEM("'name':'fsm_from', 'short':'Ff', 'group':0, 'default':'', 'descr':'FSM source state name for structured FSM coverage points'")
VLCOVGEN_ITEM("'name':'fsm_tag', 'short':'Fg', 'group':0, 'default':'', 'descr':'FSM point tag such as reset, reset_include, or default'") VLCOVGEN_ITEM("'name':'fsm_tag', 'short':'Fg', 'group':0, 'default':'', 'descr':'FSM point tag such as reset, reset_include, or default'")
VLCOVGEN_ITEM("'name':'fsm_to', 'short':'Ft', 'group':0, 'default':'', 'descr':'FSM destination state name for structured FSM coverage points'") VLCOVGEN_ITEM("'name':'fsm_to', 'short':'Ft', 'group':0, 'default':'', 'descr':'FSM destination state name for structured FSM coverage points'")
VLCOVGEN_ITEM("'name':'fsm_var', 'short':'Fv', 'group':0, 'default':'', 'descr':'FSM state variable name for structured FSM coverage points'") VLCOVGEN_ITEM("'name':'fsm_var', 'short':'Fv', 'group':0, 'default':'', 'descr':'FSM state variable name for structured FSM coverage points'")
VLCOVGEN_ITEM("'name':'group_weight','short':'Gw', 'group':0, 'default':None, 'descr':'For totaling covergroups, type_option.weight of the covergroup of this item'")
VLCOVGEN_ITEM("'name':'hier', 'short':'h', 'group':0, 'default':'', 'descr':'Hierarchy path name for the item'") VLCOVGEN_ITEM("'name':'hier', 'short':'h', 'group':0, 'default':'', 'descr':'Hierarchy path name for the item'")
VLCOVGEN_ITEM("'name':'lineno', 'short':'l', 'group':0, 'default':0, 'descr':'Line number for the item'") VLCOVGEN_ITEM("'name':'lineno', 'short':'l', 'group':0, 'default':0, 'descr':'Line number for the item'")
VLCOVGEN_ITEM("'name':'weight', 'short':'w', 'group':0, 'default':None, 'descr':'For totaling items, weight of this item'") VLCOVGEN_ITEM("'name':'weight', 'short':'w', 'group':0, 'default':None, 'descr':'For totaling items, weight of this item'")
// clang-format on // clang-format on
// VLCOVGEN_CIK_AUTO_EDIT_BEGIN // VLCOVGEN_CIK_AUTO_EDIT_BEGIN
#define VL_CIK_BIN "B"
#define VL_CIK_BIN_TYPE "Bt"
#define VL_CIK_COLUMN "n" #define VL_CIK_COLUMN "n"
#define VL_CIK_COMMENT "o" #define VL_CIK_COMMENT "o"
#define VL_CIK_CROSS "C"
#define VL_CIK_CROSS_BINS "Cb" #define VL_CIK_CROSS_BINS "Cb"
#define VL_CIK_FILENAME "f" #define VL_CIK_FILENAME "f"
#define VL_CIK_FSM_FROM "Ff" #define VL_CIK_FSM_FROM "Ff"
#define VL_CIK_FSM_TAG "Fg" #define VL_CIK_FSM_TAG "Fg"
#define VL_CIK_FSM_TO "Ft" #define VL_CIK_FSM_TO "Ft"
#define VL_CIK_FSM_VAR "Fv" #define VL_CIK_FSM_VAR "Fv"
#define VL_CIK_GROUP_WEIGHT "Gw"
#define VL_CIK_HIER "h" #define VL_CIK_HIER "h"
#define VL_CIK_LINENO "l" #define VL_CIK_LINENO "l"
#define VL_CIK_LINESCOV "S" #define VL_CIK_LINESCOV "S"
@@ -85,18 +77,14 @@ public:
// Return the short key code for a given a long coverage key // Return the short key code for a given a long coverage key
static std::string shortKey(const std::string& key) VL_PURE { static std::string shortKey(const std::string& key) VL_PURE {
// VLCOVGEN_SHORT_AUTO_EDIT_BEGIN // VLCOVGEN_SHORT_AUTO_EDIT_BEGIN
if (key == "bin") return VL_CIK_BIN;
if (key == "bin_type") return VL_CIK_BIN_TYPE;
if (key == "column") return VL_CIK_COLUMN; if (key == "column") return VL_CIK_COLUMN;
if (key == "comment") return VL_CIK_COMMENT; if (key == "comment") return VL_CIK_COMMENT;
if (key == "cross") return VL_CIK_CROSS;
if (key == "cross_bins") return VL_CIK_CROSS_BINS; if (key == "cross_bins") return VL_CIK_CROSS_BINS;
if (key == "filename") return VL_CIK_FILENAME; if (key == "filename") return VL_CIK_FILENAME;
if (key == "fsm_from") return VL_CIK_FSM_FROM; if (key == "fsm_from") return VL_CIK_FSM_FROM;
if (key == "fsm_tag") return VL_CIK_FSM_TAG; if (key == "fsm_tag") return VL_CIK_FSM_TAG;
if (key == "fsm_to") return VL_CIK_FSM_TO; if (key == "fsm_to") return VL_CIK_FSM_TO;
if (key == "fsm_var") return VL_CIK_FSM_VAR; if (key == "fsm_var") return VL_CIK_FSM_VAR;
if (key == "group_weight") return VL_CIK_GROUP_WEIGHT;
if (key == "hier") return VL_CIK_HIER; if (key == "hier") return VL_CIK_HIER;
if (key == "lineno") return VL_CIK_LINENO; if (key == "lineno") return VL_CIK_LINENO;
if (key == "linescov") return VL_CIK_LINESCOV; if (key == "linescov") return VL_CIK_LINESCOV;
-11
View File
@@ -28,8 +28,6 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
class VlFileLineDebug;
// Per-bin classification. A bin's kind is which set it lives in (structural), // 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. // not a per-bin field. Only Normal feeds coverage(); the rest are recorded.
// Enumerators are 'KIND_'-prefixed because the bare LRM terms collide with // Enumerators are 'KIND_'-prefixed because the bare LRM terms collide with
@@ -52,9 +50,6 @@ enum class VlCovBinKind : uint8_t {
/// Bounded bin count, so random access by index is the primary usage. /// Bounded bin count, so random access by index is the primary usage.
class VlCoverpointIf VL_NOT_FINAL { class VlCoverpointIf VL_NOT_FINAL {
// MEMBERS
int32_t m_weight = 1; // option.weight; never negative
public: public:
// CONSTRUCTORS // CONSTRUCTORS
virtual ~VlCoverpointIf() = default; virtual ~VlCoverpointIf() = default;
@@ -67,12 +62,6 @@ public:
virtual std::string binName(uint32_t i) const = 0; virtual std::string binName(uint32_t i) const = 0;
// Bins covered / effective total (Normal set only) for the coverage calc // Bins covered / effective total (Normal set only) for the coverage calc
virtual void coverageParts(double& covered, double& total) const = 0; virtual void coverageParts(double& covered, double& total) const = 0;
/// Weight of this item in its covergroup instance's coverage (option.weight,
/// IEEE 1800-2023 19.11)
int32_t weight() const { return m_weight; }
/// Load option.weight, as evaluated by the covergroup constructor. A negative
/// weight is reported as an error, and counts as zero.
void weight(uint32_t value, VlFileLineDebug fileline);
}; };
#endif // Guard #endif // Guard
File diff suppressed because it is too large Load Diff
+33 -579
View File
@@ -32,16 +32,10 @@
#include "verilatedos.h" #include "verilatedos.h"
#include "verilated.h"
#include "verilated_cov_model.h" #include "verilated_cov_model.h"
#include <array>
#include <cstdint> #include <cstdint>
#include <initializer_list>
#include <memory>
#include <string> #include <string>
#include <unordered_map>
#include <utility>
#include <vector> #include <vector>
class VerilatedCovContext; class VerilatedCovContext;
@@ -50,15 +44,6 @@ class VerilatedCovContext;
enum class VlCovBinNaming : uint8_t { enum class VlCovBinNaming : uint8_t {
Single, // "<name>" one bin Single, // "<name>" one bin
Array, // "<name>[i]" bins b[N] value array Array, // "<name>[i]" bins b[N] value array
Numbered, // "<name>_<i>" automatic bins of a coverpoint without bins
Values, // "<name>[v]" bins b[] with a 'with' filter, a bin per value v
};
// How the bins of a 'with' filter (IEEE 1800-2023 19.5.1.1) hold the values it keeps
enum class VlCovBinGrouping : uint8_t {
Single, // bins b = ...: one bin
Values, // bins b[] = ...: a bin for each value, in value order
Fixed, // bins b[N] = ...: distributed over N bins, as a sized array's
}; };
// Specifies the naming scheme for a range of bins, allowing the // Specifies the naming scheme for a range of bins, allowing the
@@ -109,10 +94,6 @@ public:
// VlCoverpointT<MaxHits> adds the inline hit-list array and the incrementBin write // VlCoverpointT<MaxHits> adds the inline hit-list array and the incrementBin write
// path; the cross holds VlCoverpoint* and reads via hitCount()/hitList(). // path; the cross holds VlCoverpoint* and reads via hitCount()/hitList().
class VlCoverpoint VL_NOT_FINAL : public VlCoverpointIf { class VlCoverpoint VL_NOT_FINAL : public VlCoverpointIf {
struct ValueData;
std::unique_ptr<ValueData> m_valuesp; // Optional value metadata and exclusion state
friend class VlCoverCrossDyn;
protected: protected:
// MEMBERS (protected so VlCoverpointT::incrementBin can update them) // MEMBERS (protected so VlCoverpointT::incrementBin can update them)
std::string m_hier; // "covergroup.coverpoint" std::string m_hier; // "covergroup.coverpoint"
@@ -129,27 +110,15 @@ protected:
std::vector<uint32_t> m_crossToBin; std::vector<uint32_t> m_crossToBin;
uint32_t m_hitCount = 0; // entries valid in the hit list this sample uint32_t m_hitCount = 0; // entries valid in the hit list this sample
// PROTECTED METHODS
// Normal bin: VlCoverpointT::incrementBin(), for the bins sizedSample() finds
virtual void incrementNormalBin(uint32_t i) = 0;
private: private:
// PRIVATE METHODS // PRIVATE METHODS
const VlCovNamer& namerFor(uint32_t i) const; // obtain the bin-specific name producer const VlCovNamer& namerFor(uint32_t i) const; // obtain the bin-specific name producer
void addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming, const char* name, void addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming, const char* name,
const char* file, int line, int col); const char* file, int line, int col);
// Declared bin index of the i-th bin reported through VlCoverpointIf
uint32_t reportedBin(uint32_t i) const;
std::string declaredBinName(uint32_t bin) const; // Name of a declared bin index
bool liveBin(uint32_t bin) const; // Normal bin keeps a value outside the exclusions
// Count a sample, if enabled, in a bin of a sized array holding the value, unless it is
// 'last', the bin found before; set 'last'
void sizedHit(VlCovBinKind kind, uint32_t bin, bool enabled, uint32_t& last);
public: public:
// CONSTRUCTORS // CONSTRUCTORS
VlCoverpoint(); VlCoverpoint() = default;
~VlCoverpoint() override;
// METHODS // METHODS
// ---- configuration (from generated constructor) ---- // ---- configuration (from generated constructor) ----
@@ -161,78 +130,13 @@ public:
int line, int col) { int line, int col) {
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col); addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
} }
void addNumberedNamer(VlCovBinKind set, uint32_t count, const char* name, const char* file, void registerBins(VerilatedCovContext* covcontextp, const char* page);
int line, int col) {
addNamer(set, count, VlCovBinNaming::Numbered, name, file, line, col);
}
/// Register the bins in the coverage database, with what verilator_coverage needs to
/// compute coverage (IEEE 1800-2023 19.11): option.at_least, and the weights of the
/// coverpoint, itemWeight, and of its covergroup, groupWeight. The weights are those of
/// every instance, as the database merges the instances.
void registerBins(VerilatedCovContext* covcontextp, const char* page, uint32_t itemWeight,
uint32_t groupWeight);
/// Configure construction-time value metadata for exclusions and cross selections.
void valueType(uint32_t bits, bool isSigned);
/// Describe bin values as {bin, low words, high words} entries, without enumerating them.
void valueRanges(std::initializer_list<EData> entries);
/// Describe runs of bins as {first bin, count, low words, span words, high words} entries:
/// bin k of a run holds [low + k * (span + 1), low + k * (span + 1) + span], and its last
/// bin extends to high.
void valueRuns(std::initializer_list<EData> entries);
/// Describe wildcard patterns as {bin, value words, mask words, low words, high words}.
void valuePatterns(std::initializer_list<EData> entries);
/// State exclusions do not remove values from these transition bins.
void valueTransitions(std::initializer_list<uint32_t> bins);
/// Apply exclusions and freeze the live Normal-bin index space used by crosses.
void valueFinalize();
/// Drop the per-bin values once every cross has been built; sampling needs only exclusions.
void valueRelease();
/// Test state exclusions independently of sampling-time iff guards.
bool valueExcluded(QData value) const;
bool valueExcludedW(WDataInP valuep) const;
/// Add the coverpoint values lo..hi of the next range list element of a sized array of
/// bins, in declaration order.
void sizedRange(QData lo, QData hi);
void sizedRangeW(WDataInP lop, WDataInP hip);
/// Distribute the values sizedRange() added over the bins of the sized array 'name[count]'
/// (IEEE 1800-2023 19.5.1). 'positive' is false for a count below one, which is invalid.
/// At most 'limit' bins may hold values. Needs valueType(); bins append after those of
/// init().
void sizedFinish(VlCovBinKind kind, QData count, bool positive, uint32_t limit,
const char* name, const char* file, int line, int col);
/// Declared bins [sizedFirst(), sizedEnd()) of the sized array 'sized', counted in
/// sizedFinish() order, for cross selections.
uint32_t sizedFirst(uint32_t sized) const;
uint32_t sizedEnd(uint32_t sized) const;
/// Begin the bins of a 'with' filter (IEEE 1800-2023 19.5.1.1), whose candidates are the
/// values sizedRange() added, and whose bins then count as a sized array's. At most
/// 'limit' bins, or runs of values kept.
void withBegin(VlCovBinGrouping grouping, uint32_t limit);
/// Advance to the next run of candidates, withLo() to withHi(); false after the last, or
/// once too many values are kept
bool withNext();
QData withLo() const;
void withLoW(WDataOutP valuep) const;
QData withHi() const;
void withHiW(WDataOutP valuep) const;
/// Keep the values lo..hi, in the order the filter kept them; false once too many are
bool withRun(QData lo, QData hi);
bool withRunW(WDataInP lop, WDataInP hip);
/// Make the bins of the values kept, a sized array 'name[count]' for Fixed grouping (see
/// sizedFinish())
void withFinish(VlCovBinKind kind, QData count, bool positive, const char* name,
const char* file, int line, int col);
// ---- hot path (from generated sample()) ---- // ---- hot path (from generated sample()) ----
// Clear the hit list at the start of each sample() for a cross-fed coverpoint. // Clear the hit list at the start of each sample() for a cross-fed coverpoint.
void clearHitList() { m_hitCount = 0; } void clearHitList() { m_hitCount = 0; }
// Ignore/Illegal/Default: count only; never propagates to cross coverage. // Ignore/Illegal/Default: count only; never propagates to cross coverage.
void recordHit(uint32_t i) { ++m_counts[i]; } void recordHit(uint32_t i) { ++m_counts[i]; }
/// Count a sample in the bins of the sized array 'sized' holding the value, once each,
/// if 'enabled'. True if a bin holds the value, enabled or not.
bool sizedSample(uint32_t sized, QData value, bool enabled);
bool sizedSampleW(uint32_t sized, WDataInP valuep, bool enabled);
// incrementBin (Normal bin: count + hit-list append) lives in VlCoverpointT<MaxHits>, // incrementBin (Normal bin: count + hit-list append) lives in VlCoverpointT<MaxHits>,
// where MaxHits is the gen-time max per-sample bin overlap. // where MaxHits is the gen-time max per-sample bin overlap.
@@ -243,17 +147,21 @@ public:
std::string normalBinName(uint32_t crossIdx) const; // name of the crossIdx-th Normal bin std::string normalBinName(uint32_t crossIdx) const; // name of the crossIdx-th Normal bin
// ---- VlCoverpointIf ---- // ---- VlCoverpointIf ----
/// Bins removed for having no value (IEEE 1800-2023 19.11.1) are not reported. uint32_t binCount() const override { return m_total; }
uint32_t binCount() const override;
std::string binName(uint32_t i) const override; std::string binName(uint32_t i) const override;
// Deliberately not on VlCoverpointIf: only coverage-database registration needs it. // Deliberately not on VlCoverpointIf: only registerBins() needs it, via the
VlCovBinKind binKind(uint32_t i) const { return namerFor(reportedBin(i)).set(); } // concrete coverpoint. A cross has all-Normal bins and exposes no kind, so the
// interface omits it; add it back only if a writer needs it polymorphically.
VlCovBinKind binKind(uint32_t i) const { return namerFor(i).set(); }
void coverageParts(double& covered, double& total) const override { void coverageParts(double& covered, double& total) const override {
// Count Normal bins that reached option.at_least on demand, so the hot // Count Normal bins that reached option.at_least on demand, so the hot
// path (incrementBin) stays a plain counter bump. // path (incrementBin) stays a plain counter bump.
uint32_t numCovered = 0; uint32_t numCovered = 0;
for (const uint32_t bin : m_crossToBin) { for (const VlCovNamer& nm : m_namers) {
if (m_counts[bin] >= m_atLeast) ++numCovered; if (nm.set() != VlCovBinKind::KIND_NORMAL) continue;
for (uint32_t i = nm.base(); i < nm.base() + nm.count(); ++i) {
if (m_counts[i] >= m_atLeast) ++numCovered;
}
} }
covered = numCovered; covered = numCovered;
total = m_normal; total = m_normal;
@@ -290,75 +198,16 @@ public:
if (cx >= 0 && m_hitCount < MaxHits) m_hits[m_hitCount++] = static_cast<uint32_t>(cx); if (cx >= 0 && m_hitCount < MaxHits) m_hits[m_hitCount++] = static_cast<uint32_t>(cx);
} }
const uint32_t* hitList() const override { return m_hits; } const uint32_t* hitList() const override { return m_hits; }
protected:
void incrementNormalBin(uint32_t i) override { incrementBin(i); }
}; };
//============================================================================= //=============================================================================
// VlCoverCross // VlCoverCross
/// Per-instance cross runtime. Holds flat uint32_t[] storage over the /// Per-instance auto cross runtime. Holds flat uint32_t[] storage over the
/// Cartesian product of the feeding coverpoints' Normal bins. Each sample() /// Cartesian product of the feeding coverpoints' Normal bins. Each sample()
/// walks only hit tuples, not the entire product. Bin names are /// walks the coverpoint hit lists (O(hits), not O(product)). Bin names are
/// built on demand for automatic bins; explicit bins select sets of tuples /// built on demand from the coverpoints, so no per-bin name is stored.
/// and replace the corresponding automatic cross bins. Explicit selections
/// are intersected with hit-tuple words once per sample.
/// VlCoverCrossT and VlCoverCrossDyn own their storage. This shared core does not allocate bin
/// storage, and its borrowed storage pointers remain valid for the instance.
class VlCoverCross VL_NOT_FINAL : public VlCoverpointIf { class VlCoverCross final : public VlCoverpointIf {
protected:
struct Dimension final {
VlCoverpoint* cpp; // Feeding coverpoint
const uint32_t* hitsp; // Hit list cached for Cartesian traversal
uint32_t bins; // Normal bin count
uint32_t stride; // Flat-index stride
};
struct Bin final {
const uint64_t* selectionp; // Slice of the cross's selection storage
const char* namep; // Explicit bin name
const char* filep; // Bin declaration file
const uint32_t* wordIndicesp = nullptr; // Slice of the packed selection-word indices
int line; // Bin declaration line
int col; // Bin declaration column
VlCovBinKind kind = VlCovBinKind::KIND_NORMAL; // Normal, ignore, or illegal bin
uint32_t count = 0; // Samples matching the selection and guard
uint32_t numWords = 0; // Number of nonzero selection-word indices
uint32_t iffIndex = 0; // Original guard index, including bins removed during finalization
};
struct Word final {
uint64_t autoExcluded = 0; // Tuples replaced by explicit bins
uint64_t hitBits = 0; // Selected hit tuples, cleared after each sample
uint32_t touchedWord = 0; // Flat word ID, stored by touched-list position
};
template <typename T>
class View final {
T* m_beginp; // First element of the viewed slice
T* m_endp; // One past the last element of the viewed slice
public:
View(T* datap, uint64_t size)
: m_beginp{datap}
, m_endp{datap ? datap + size : nullptr} {}
T& operator[](uint64_t i) const { return m_beginp[i]; }
uint64_t size() const { return m_beginp == m_endp ? 0 : m_endp - m_beginp; }
bool empty() const { return m_beginp == m_endp; }
T* begin() const { return m_beginp; }
T* end() const { return m_endp; }
};
struct Explicit final {
View<Bin> bins; // Explicit bins in declaration order
Word* wordsp; // Masks use flat word indices; touchedWord uses a dense prefix
View<uint32_t> autoBins; // Retained flat indices
View<uint32_t> binWords; // Nonzero selection words, grouped by bin
uint64_t* selectionp; // [bins.size() * ceil(m_numAutoBins / 64)]
uint32_t numBins = 0; // Bins configured by addBin()
uint32_t normalBins = 0; // Explicit bins contributing to coverage
uint32_t minBinWords = 0; // Minimum nonzero-word count across explicit bins
uint32_t numTouchedWords = 0; // Active prefix of wordsp[].touchedWord
};
private:
// MEMBERS // MEMBERS
std::string m_hier; // "covergroup.cross" std::string m_hier; // "covergroup.cross"
const char* m_file = nullptr; // Cross declaration file (registration metadata) const char* m_file = nullptr; // Cross declaration file (registration metadata)
@@ -368,436 +217,41 @@ private:
// Cross bin indexes are unsigned, like the coverpoint bin indexes they are // Cross bin indexes are unsigned, like the coverpoint bin indexes they are
// built from. init() fatals if the product would exceed UINT32_MAX, so every // built from. init() fatals if the product would exceed UINT32_MAX, so every
// index computed here provably fits. That bound is far beyond anything // index computed here provably fits. That bound is far beyond anything
// storable anyway: m_flatCountsp alone would need 16GB. // storable anyway: m_flatCounts alone would need 16GB.
uint32_t m_numAutoBins = 0; // Product of per-dim Normal bin counts uint32_t m_numAutoBins = 0; // Product of per-dim Normal bin counts
uint32_t m_numCovered = 0; // Distinct bins hit >= 1 (maintained incrementally) uint32_t m_numCovered = 0; // Distinct bins hit >= 1 (maintained incrementally)
Dimension* m_dimensionsp = nullptr; // [m_dims], owned by the concrete cross runtime std::vector<uint32_t> m_cpBinCounts; // [m_dims] Normal bin count per dimension
uint32_t* m_flatCountsp = nullptr; // [m_numAutoBins] Per-bin hit counts std::vector<uint32_t> m_stride; // [m_dims] Flat-index stride per dimension
Explicit* m_explicitp = nullptr; // Absent for automatic-only crosses std::vector<uint32_t> m_flatCounts; // [m_numAutoBins] Per-bin hit counts
std::vector<VlCoverpoint*> m_cps; // Feeding coverpoints (the only name source)
// PRIVATE METHODS // PRIVATE METHODS
bool hasExplicitBins() const { return m_explicitp != nullptr; } void iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx);
template <bool T_Explicit, bool T_RecordHits = true>
void iterateProduct(uint32_t dim, uint32_t baseIdx);
void incrementAuto(uint32_t idx) {
if (m_flatCountsp[idx]++ == 0) ++m_numCovered;
}
void incrementBin(Bin& bin);
template <bool T_RecordHits>
void incrementTuple(uint32_t idx) { void incrementTuple(uint32_t idx) {
Explicit& data = *m_explicitp; if (m_flatCounts[idx]++ == 0) ++m_numCovered;
const uint32_t wordIdx = idx / 64;
Word& word = data.wordsp[wordIdx];
if ((word.autoExcluded >> (idx % 64)) & 1U) {
if (T_RecordHits) {
if (!word.hitBits) { data.wordsp[data.numTouchedWords++].touchedWord = wordIdx; }
word.hitBits |= uint64_t{1} << (idx % 64);
}
// Explicit selections consume automatic tuples independently of iff.
return;
}
incrementAuto(idx);
} }
template <bool T_ApplyIffs>
void sampleSingleTuple(uint32_t idx, const bool* binIffs);
template <bool T_ApplyIffs, uint32_t T_Touched, bool T_Dense>
void sampleBins(const bool* binIffs);
template <bool T_ApplyIffs, bool T_Dense>
void sampleHitWords(const bool* binIffs);
uint32_t autoIndex(uint32_t i) const {
return hasExplicitBins() ? m_explicitp->autoBins[i] : i;
}
std::string autoBinName(uint32_t flat) const;
protected:
// CONSTRUCTORS
VlCoverCross(uint32_t dims, uint32_t tuples)
: m_dims{dims}
, m_numAutoBins{tuples} {}
void bindStorage(Dimension* dimensionsp, uint32_t* countsp, Explicit* explicitp = nullptr) {
m_dimensionsp = dimensionsp;
m_flatCountsp = countsp;
m_explicitp = explicitp;
}
void shape(uint32_t dims, uint32_t tuples) {
m_dims = dims;
m_numAutoBins = tuples;
}
void addBinImpl(VlCovBinKind kind, const uint64_t* selectionp, uint32_t words,
const char* namep, const char* filep, int line, int col, uint32_t iffIndex);
public: public:
VL_UNCOPYABLE(VlCoverCross); // CONSTRUCTORS
VlCoverCross() = default;
// METHODS // METHODS
// ---- configuration (from generated constructor, after coverpoints init'd) ---- // ---- configuration (from generated constructor, after coverpoints init'd) ----
virtual void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file, void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
int line, int col); int line, int col);
/// Add a cross bin using a verilation-time bitmap of selected Normal-bin tuples. void registerBins(VerilatedCovContext* covcontextp, const char* page);
void addBin(VlCovBinKind kind, std::initializer_list<uint64_t> selection, const char* namep,
const char* filep, int line, int col);
/// Retain only automatic cross bins not selected by any explicit bin.
virtual void finalizeBins();
/// Register the bins in the coverage database; see VlCoverpoint::registerBins().
void registerBins(VerilatedCovContext* covcontextp, const char* page, uint32_t itemWeight,
uint32_t groupWeight);
// ---- hot path (from generated sample(), after all coverpoints sampled) ---- // ---- hot path (from generated sample(), after all coverpoints sampled) ----
/// Sample automatic and explicit bins, optionally applying per-bin iff guards. void sample(VlCoverpoint* const* cps);
/// Reads the feeding coverpoints saved by init(), so the caller passes no coverpoints.
void sample(const bool* binIffs = nullptr);
// ---- VlCoverpointIf ---- // ---- VlCoverpointIf ----
// Explicit bins (including ignore/illegal) precede retained automatic bins. // A cross is a coverpoint whose bins are the auto cross bins (all Normal).
uint32_t binCount() const override { uint32_t binCount() const override { return m_numAutoBins; }
return hasExplicitBins() std::string binName(uint32_t flat) const override;
? static_cast<uint32_t>(m_explicitp->bins.size() + m_explicitp->autoBins.size())
: m_numAutoBins;
}
std::string binName(uint32_t i) const override;
void coverageParts(double& covered, double& total) const override { void coverageParts(double& covered, double& total) const override {
covered = m_numCovered; covered = m_numCovered;
total = hasExplicitBins() ? m_explicitp->normalBins + m_explicitp->autoBins.size() total = m_numAutoBins;
: m_numAutoBins;
} }
}; };
//=============================================================================
// VlCoverCrossT
/// Cross storage with verilation-time dimensions and bin capacities. All bin
/// data stays at the registry-owned object's address; no per-buffer allocations
/// or per-shape copies of the sampling algorithm are needed.
template <uint32_t Dims, uint32_t Tuples, uint32_t Bins, uint32_t AutoBins, uint64_t BinWords>
class VlCoverCrossT final : public VlCoverCross {
static constexpr uint32_t WORDS = Tuples / 64 + (Tuples % 64 != 0);
static_assert(Bins > 0, "Explicit cross storage requires bins");
std::array<Dimension, Dims> m_dimensions;
std::array<uint32_t, Tuples> m_counts{};
std::array<Bin, Bins> m_bins;
std::array<Word, WORDS> m_words{};
std::array<uint32_t, AutoBins> m_autoBins;
std::array<uint32_t, BinWords> m_binWords;
std::array<uint64_t, static_cast<uint64_t>(Bins) * WORDS> m_selections;
Explicit m_explicit;
public:
VlCoverCrossT()
: VlCoverCross{Dims, Tuples}
, m_explicit{{m_bins.data(), Bins},
m_words.data(),
{m_autoBins.data(), AutoBins},
{m_binWords.data(), BinWords},
m_selections.data()} {
bindStorage(m_dimensions.data(), m_counts.data(), &m_explicit);
}
};
/// Automatic-only crosses omit every explicit-bin array and its bookkeeping.
template <uint32_t Dims, uint32_t Tuples>
class VlCoverCrossT<Dims, Tuples, 0, 0, 0> final : public VlCoverCross {
std::array<Dimension, Dims> m_dimensions;
std::array<uint32_t, Tuples> m_counts{};
public:
VlCoverCrossT()
: VlCoverCross{Dims, Tuples} {
bindStorage(m_dimensions.data(), m_counts.data());
}
};
// Construction-time cross layout over finalized coverpoints, sharing the sampling core.
class VlCoverCrossDyn final : public VlCoverCross {
class Layout;
std::unique_ptr<Layout> m_layoutp; // Owned cross storage and construction-time selections
public:
// CONSTRUCTORS
VlCoverCrossDyn();
~VlCoverCrossDyn() override;
// METHODS
/// Initialize after all feeding coverpoints have finalized their live bins.
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
int line, int col) override;
/// Build cross-bin selections in postfix order.
void selectAll();
/// Start a binsof term over the live bins declared in [first, end) of dimension 'dim'.
void selectDim(uint32_t dim, uint32_t first, uint32_t end, bool negated, bool intersect);
void selectRange(QData lo, QData hi);
void selectRangeW(WDataInP lop, WDataInP hip);
void selectDimEnd();
void selectAnd();
void selectOr();
// Save a selection without renumbering guards when empty bins are removed.
void selectBin(VlCovBinKind kind, const char* namep, const char* filep, int line, int col,
uint32_t iffIndex);
/// Apply cross exclusions and bind finalized storage to the sampling core.
void finalizeBins() override;
};
class VlCovergroupType;
//=============================================================================
// VlCovergroupInst
/// One covergroup instance: owns the coverpoint/cross runtimes created by one
/// SV 'new'. The generated class holds borrowed pointers to them, so the bins
/// outlive the SV object -- the coverage database registers raw count pointers
/// and reads them at write() time, long after the object may have been freed.
///
/// Attach-counted: every VlCovInstHandle bound here holds one count, and the
/// node is retired (see VlCovergroupType::retire) when the last one drops.
class VlCovergroupInst final {
// MEMBERS
// Coverpoint and cross runtimes of this instance; creation == declaration order
std::vector<std::unique_ptr<VlCoverpointIf>> m_items;
VlCovergroupType* const m_typep; // Owning type; outlives this node
const uint32_t m_instId; // Stable identity across churn; NOT the slot
#if !VM_COVERAGE
// Only retire()'s free path uses this; under VM_COVERAGE the node is never
// unlinked, so the slot would be dead. VlCovergroupType sets it.
uint32_t m_slot = 0; // Index into m_typep->m_insts; unlink-by-swap rewrites
#endif
uint32_t m_attachCount = 1; // SV handles bound here; 1 from construction
// option.weight of the SV object that created this node, while that object
// lives; borrowed through VlCovInstHandle::lendWeight().
const IData* m_weightp = nullptr;
IData m_loadedWeight = 1; // Last option.weight loaded through m_weightp
int32_t m_weight = 1; // Weight in use, never negative; kept once the object is gone
VlFileLineDebug m_fileline; // Covergroup declaration, where a negative weight is reported
bool m_retained = false; // VM_COVERAGE: dead, but kept for registered count pointers
// Reads m_items to fold the residue; owns m_slot and m_retained.
friend class VlCovergroupType;
public:
// CONSTRUCTORS
VlCovergroupInst(VlCovergroupType* typep, uint32_t instId)
: m_typep{typep}
, m_instId{instId} {}
VL_UNCOPYABLE(VlCovergroupInst);
// METHODS
// ---- construction (from the generated covergroup constructor) ----
template <uint32_t MaxHits>
VlCoverpointT<MaxHits>* addCoverpoint() {
VlCoverpointT<MaxHits>* const cpp = new VlCoverpointT<MaxHits>{};
m_items.emplace_back(cpp);
return cpp; // borrowed by the generated class
}
template <uint32_t Dims, uint32_t Tuples, uint32_t Bins, uint32_t AutoBins, uint64_t BinWords>
VlCoverCrossT<Dims, Tuples, Bins, AutoBins, BinWords>* addCross() {
auto* const cxp = new VlCoverCrossT<Dims, Tuples, Bins, AutoBins, BinWords>{};
m_items.emplace_back(cxp);
return cxp; // borrowed by the generated class
}
VlCoverCrossDyn* addCrossDyn();
// ---- attach counting (from VlCovInstHandle) ----
void attachInc() { ++m_attachCount; }
// Drops one handle; true if it was the last and the caller must retire the
// node. Retiring is the caller's job because VlCovergroupType is incomplete
// here, and because it frees 'this'.
bool attachDec() { return --m_attachCount == 0; }
// ---- instance weight (from VlCovInstHandle) ----
void lendWeight(const IData* weightp, VlFileLineDebug fileline) {
m_weightp = weightp;
m_fileline = fileline;
loadWeight();
}
// The lending object is being destroyed. Its members may already be gone, so
// the weight is not read again; the last loaded value stays in effect.
void unlendWeight(const IData* weightp) {
if (m_weightp == weightp) m_weightp = nullptr;
}
/// Load option.weight from the lending object. SV writes the member directly
/// (assignments, ref and output arguments, $value$plusargs, ...), so this is
/// where a new value is seen, and checked once: a negative weight is reported as
/// an error, and counts as zero.
void loadWeight();
/// Weight of this instance in its type's coverage (option.weight, IEEE
/// 1800-2023 19.11.3), as last loaded; never negative.
int32_t weight() const { return m_weight; }
// ---- introspection ----
VlCovergroupType* typep() const { return m_typep; }
uint32_t instId() const { return m_instId; }
// True once retired but kept alive because the coverage database holds raw
// pointers into this node's bin counts (VM_COVERAGE); see retire().
bool retained() const { return m_retained; }
/// IEEE 1800-2023 19.11 sums over the items whose coverage has a nonzero
/// denominator: {the sum of each item's option.weight times its coverage
/// (0..100), the sum of those weights}.
std::pair<double, double> coverageSums() const;
/// Instance coverage, as returned by get_inst_coverage(), in 0..100.
double coverage();
};
//=============================================================================
// VlCovRetiredAvg
/// Per-type residue: what survives an instance's death. Fixed size, so it does
/// not grow with churn. Each instance contributes with its option.weight.
struct VlCovRetiredAvg final {
uint64_t count = 0; // Retired instances that contributed (nonzero denominator)
double sumCoverage = 0.0; // Sigma of per-instance weight * coverage (0..100)
double sumWeight = 0.0; // Sigma of per-instance weight
};
//=============================================================================
// VlCovergroupType
/// One covergroup type: owns its live instances, in creation order, plus the
/// residue of the ones that have died.
class VlCovergroupType final {
// MEMBERS
// Live nodes, and -- under VM_COVERAGE -- retired-but-retained ones. Slot
// order is creation order only until the first unlink-by-swap.
std::vector<std::unique_ptr<VlCovergroupInst>> m_insts;
uint32_t m_createdInsts = 0; // Instances ever created; never decremented
uint32_t m_nextInstId = 0; // Monotonic; slots are reused, ids never are
VlCovRetiredAvg m_retired; // Contribution of every instance that has died
IData m_loadedTypeWeight = 1; // Last type_option.weight loaded by coverage()
int32_t m_typeWeight = 1; // type_option.weight in use, never negative
// PRIVATE METHODS
// Harvest instp's contribution into m_retired. Must run before instp is
// unlinked: it reads the instance's items.
void foldResidue(VlCovergroupInst* instp);
public:
// CONSTRUCTORS
VlCovergroupType() = default;
VL_UNCOPYABLE(VlCovergroupType);
// METHODS
VlCovergroupInst* newInstance();
// Called when the last handle to instp drops. Folds the residue, then
// unlinks and frees the node -- except under VM_COVERAGE, where the coverage
// database still holds raw pointers into it and it is only marked retained.
void retire(VlCovergroupInst* instp);
// True if any node here still has an SV handle bound to it, and so can be
// retired again after the registry is destroyed. See ~VlCovRegistry.
bool anyAttached() const;
/// Type coverage, as returned by get_coverage(), in 0..100: the average of
/// every instance's coverage, weighted by its option.weight (IEEE 1800-2023
/// 19.11.3, type_option.merge_instances false). typeWeight is
/// type_option.weight, which decides the result when no instance contributes;
/// like option.weight, it is checked as it is loaded.
double coverage(IData typeWeight, VlFileLineDebug fileline);
// ---- introspection ----
// Test and debug only; generated code never calls these, and SV reaches them
// only via explicit $c. They let a regression test pin node accumulation
// (otherwise visible only as memory growth) and the residue fold.
//
// Instance nodes still reachable from SV. Under VM_COVERAGE this is smaller
// than m_insts.size(), which also holds retained (dead) nodes.
uint32_t liveInstanceCount() const;
// Instances ever created, live or not. Wraps after 4G instances, which no
// introspection use cares about.
uint32_t createdInstanceCount() const { return m_createdInsts; }
// Instances that have died and contributed to the residue.
uint32_t retiredInstanceCount() const { return static_cast<uint32_t>(m_retired.count); }
// Weighted mean coverage over the retired instances only, in 0..100; -1.0 if
// none contributed or their weights sum to zero.
double retiredCoverage() const;
};
//=============================================================================
// VlCovRegistry
/// Every covergroup type and instance in one VerilatedContext. Owned by the
/// VerilatedContext (not by the coverage database, which is only linked under
/// --coverage and is a *consumer* of this data), reached through
/// VerilatedContext::covergroupRegistryp().
class VlCovRegistry final : public VerilatedVirtualBase {
// MEMBERS
std::vector<std::unique_ptr<VlCovergroupType>> m_types; // Creation order
std::unordered_map<std::string, VlCovergroupType*> m_byName; // Lookup, borrowed
// PRIVATE METHODS
VlCovergroupType* findType(const char* typeName) const; // nullptr if unknown
VlCovergroupType* findOrCreateType(const char* typeName);
public:
// CONSTRUCTORS
VlCovRegistry() = default;
~VlCovRegistry() override;
VL_UNCOPYABLE(VlCovRegistry);
// METHODS
// Find-or-create the type node, then add an instance to it. typeName is the
// generated covergroup class name, already --protect-ids obfuscated, and is
// the same string that keys the coverage database's hier/page.
VlCovergroupInst* newCovergroupInst(const char* typeName);
/// Type coverage of a covergroup type (get_coverage()); see
/// VlCovergroupType::coverage(). typeWeight is its type_option.weight.
double typeCoverage(const char* typeName, IData typeWeight, VlFileLineDebug fileline);
// ---- introspection (see VlCovergroupType) ----
// typeName is the obfuscated generated name, so a test using these under
// --protect-ids must pass the obfuscated string; the no-argument form does not.
uint32_t liveInstanceCount() const; // Summed over every type
uint32_t createdInstanceCount() const; // Summed over every type
uint32_t liveInstanceCount(const char* typeName) const; // 0 if type unknown
uint32_t createdInstanceCount(const char* typeName) const; // 0 if type unknown
uint32_t retiredInstanceCount(const char* typeName) const; // 0 if type unknown
double retiredCoverage(const char* typeName) const; // -1.0 if type unknown or none
};
//=============================================================================
// VlCovInstHandle
/// The generated covergroup class's link to its instance node. Attach-counting:
/// the registry owns the node, but the handles are what keep it reachable, and
/// the last one to go retires it.
///
/// Must stay copyable: every generated clone() copy-constructs. A copy shares
/// the node, and so the bin counts -- pre-existing covergroup-copy aliasing.
/// Attach counting makes that lifetime-safe, not correct.
class VlCovInstHandle final {
// MEMBERS
VlCovergroupInst* m_p = nullptr; // Attach-counted; the registry owns the node
const IData* m_weightp = nullptr; // Owning object's option.weight, if lent to m_p
// PRIVATE METHODS
// Drop one attach count, retiring the node if that was the last handle.
// Nothing may touch instp afterwards: retire() may have freed it.
static void release(VlCovergroupInst* instp, const IData* weightp) {
if (VL_UNCOVERABLE(!instp)) return; // Never attach()ed; codegen always does
instp->unlendWeight(weightp);
if (instp->attachDec()) instp->typep()->retire(instp);
}
public:
// CONSTRUCTORS
VlCovInstHandle() = default;
// The copy's owning object lends no weight; the node keeps reading the lender's.
VlCovInstHandle(const VlCovInstHandle& o)
: m_p{o.m_p} {
if (VL_UNCOVERABLE(!m_p)) return; // Unbound source; see release above
m_p->attachInc();
}
// Deleted, not implemented: nothing generates an assignment, and the
// implicit one would copy m_p raw -- no attachInc, no release.
VlCovInstHandle& operator=(const VlCovInstHandle&) = delete;
~VlCovInstHandle() { release(m_p, m_weightp); }
// METHODS
// Bind to a freshly created node, taking over the attach count of 1 it was
// created with. Called once, from the generated covergroup constructor.
void attach(VlCovergroupInst* p) { m_p = p; }
// Let the node read the owning object's option.weight until this handle is
// destroyed. Called once, from the generated constructor, after attach().
void lendWeight(const IData* weightp, VlFileLineDebug fileline) {
m_weightp = weightp;
m_p->lendWeight(weightp, fileline);
}
VlCovergroupInst* p() const { return m_p; }
};
#endif // Guard #endif // Guard
+4 -4
View File
@@ -105,9 +105,9 @@ private:
int m_msb; // Inclusive upper 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 int m_rhsLsb; // Destination index that maps to RHS index 0
const void* m_rhsDatap; // Pointer to RHS storage const void* m_rhsDatap; // Pointer to RHS storage
int m_bitLsb = 0; // Bit's LSB int m_bitLsb = 0;
int m_bitMsb = 0; // Bit's MSB int m_bitMsb = 0;
int m_elemWidth = 0; // Element width int m_elemWidth = 0;
}; };
std::vector<Entry> m_entries; // Sorted by msb, non-overlapping std::vector<Entry> m_entries; // Sorted by msb, non-overlapping
@@ -303,7 +303,7 @@ public:
} }
template <typename T> template <typename T>
T readIndex(const T origVal, int index) const { T readIndex(T origVal, int index) const {
if (m_entries.empty()) return origVal; if (m_entries.empty()) return origVal;
T result = origVal; T result = origVal;
+10 -26
View File
@@ -1438,8 +1438,8 @@ inline void _vl_insert_WI(WDataOutP iowp, IData ld, int hbit, int lbit, int rbit
const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword
iowp[lword] = (iowp[lword] & ~linsmask) | ((lde << loffset) & linsmask); iowp[lword] = (iowp[lword] & ~linsmask) | ((lde << loffset) & linsmask);
// Prevent unsafe write where lword was final writable location and hword is // Prevent unsafe write where lword was final writable location and hword is
// out-of-bounds. rbits==0 means the caller guarantees bounds. // out-of-bounds.
if (VL_LIKELY(!(rbits && hword >= VL_WORDS_I(rbits)))) { if (VL_LIKELY(!(hword == rword && roffset == 0))) {
iowp[hword] iowp[hword]
= (iowp[hword] & ~hinsmask) | ((lde >> nbitsonright) & (hinsmask & cleanmask)); = (iowp[hword] & ~hinsmask) | ((lde >> nbitsonright) & (hinsmask & cleanmask));
} }
@@ -2948,8 +2948,8 @@ inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide<N_Words>& elem) {
for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; } for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; }
} }
// These overloads work for both VlQueue<CData> (and similar) as well // This specialization works for both VlQueue<CData> (and similar) as well
// as VlQueue<VlWide<N>>, including queues with different element types. // as VlQueue<VlWide<N>>.
template <typename T> template <typename T>
inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/, int srcElementBits, inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/, int srcElementBits,
int dstElementBits) { int dstElementBits) {
@@ -2971,32 +2971,16 @@ inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/, int
} }
} }
// Keep the same-type overload separate for its direct-copy optimization and alias handling; // This specialization works for both VlQueue<CData> (and similar) as well
// differently typed queues cannot alias. // as VlQueue<VlWide<N>>.
template <typename T_Dst, typename T_Src> template <typename T>
inline typename std::enable_if<!std::is_same<T_Dst, T_Src>::value>::type inline void VL_REVCOPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int lbits, int srcElementBits,
VL_COPY_Q(VlQueue<T_Dst>& q, const VlQueue<T_Src>& from, int /*lbits*/, int srcElementBits, int dstElementBits) {
int dstElementBits) {
const size_t srcTotalBits = from.size() * srcElementBits;
const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits;
q.renew(dstSize);
for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); }
for (size_t bitIndex = 0; bitIndex < srcTotalBits; ++bitIndex) {
VL_SET_QUEUE_BIT(q, dstElementBits, bitIndex,
VL_GET_QUEUE_BIT(from, srcElementBits, bitIndex));
}
}
// This works for both VlQueue<CData> (and similar) as well as VlQueue<VlWide<N>>,
// including queues with different element types.
template <typename T_Dst, typename T_Src>
inline void VL_REVCOPY_Q(VlQueue<T_Dst>& q, const VlQueue<T_Src>& from, int lbits,
int srcElementBits, int dstElementBits) {
const size_t srcTotalBits = from.size() * srcElementBits; const size_t srcTotalBits = from.size() * srcElementBits;
const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits;
// Always make a copy to handle the case where q and from are the same queue // Always make a copy to handle the case where q and from are the same queue
VlQueue<T_Src> srcCopy = from; VlQueue<T> srcCopy = from;
// Initialize all elements to zero using appropriate method // Initialize all elements to zero using appropriate method
q.renew(dstSize); q.renew(dstSize);
-9
View File
@@ -207,11 +207,6 @@ protected:
// Used by scopeInsert, scopeFind, scopeErase, scopeNameMap // Used by scopeInsert, scopeFind, scopeErase, scopeNameMap
mutable VerilatedMutex m_nameMutex; // Protect m_nameMap mutable VerilatedMutex m_nameMutex; // Protect m_nameMap
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex);
// Map of <interface_reference_name, interface reference>
// Used by ifaceRefInsert, ifaceRefFind, ifaceRefErase, ifaceRefMap
mutable VerilatedMutex m_ifaceRefMutex; // Protect m_ifaceRefMap
VerilatedIfaceRefMap m_ifaceRefMap VL_GUARDED_BY(m_ifaceRefMutex);
}; };
//====================================================================== //======================================================================
@@ -275,10 +270,6 @@ public: // But only for verilated*.cpp
void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE; void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE; void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE;
// METHODS - interface references - INTERNAL only for verilated*.cpp
void ifaceRefInsert(const VerilatedIfaceRef& ifaceRef) VL_MT_SAFE;
void ifaceRefErase(const std::string& fullname, const VerilatedScope* scopep) VL_MT_SAFE;
// METHODS - file IO - INTERNAL only for verilated*.cpp // METHODS - file IO - INTERNAL only for verilated*.cpp
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) { IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
+1 -1
View File
@@ -169,7 +169,7 @@ class VlExecutionProfiler final : public VlExecutionProfilerBase {
// STATE // STATE
VerilatedContext& m_context; // The context this profiler is under VerilatedContext& m_context; // The context this profiler is under
static thread_local ExecutionTrace t_trace; // thread-local trace buffers static thread_local ExecutionTrace t_trace; // thread-local trace buffers
mutable VerilatedMutex m_mutex; // Mutex protecting traces mutable VerilatedMutex m_mutex;
// Map from thread id to &t_trace of given thread // Map from thread id to &t_trace of given thread
std::map<uint32_t, ExecutionTrace*> m_traceps VL_GUARDED_BY(m_mutex); std::map<uint32_t, ExecutionTrace*> m_traceps VL_GUARDED_BY(m_mutex);
+23 -59
View File
@@ -674,38 +674,10 @@ void VlRandomVar::set(const std::string& idx, const std::string& val) const {
} }
} }
void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits, void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits) {
const std::vector<std::string>* layerVarsp) {
// layerVarsp scopes sampling to the current phase's own layer, so a
// phased solve's diversity constraint can't be built entirely out of a
// later phase's (still-unsolved) variable instead of this one's.
std::vector<const VlRandomVar*>& vars = m_randomConstraintVars;
vars.clear();
int varBits = 0;
if (layerVarsp) {
for (const auto& name : *layerVarsp) {
const auto it = m_vars.find(name);
// buildSolveLayers() only ever adds a name after confirming
// it's already in m_vars.
assert(it != m_vars.end());
vars.push_back(it->second.get());
varBits += it->second->totalWidth();
}
} else {
for (const auto& var : m_vars) {
vars.push_back(var.second.get());
varBits += var.second->totalWidth();
}
}
if (varBits == 0) {
// Nothing to sample (e.g. a still-unsized queue/dynamic array, in a
// layer or the whole class) -- tautology instead of the degenerate
// empty-operand expression the loop below would otherwise build.
os << "(= #b1 #b1)";
return;
}
const IData hash = VL_RANDOM_RNG_I(rngr) & ((1 << bits) - 1); const IData hash = VL_RANDOM_RNG_I(rngr) & ((1 << bits) - 1);
int varBits = 0;
for (const auto& var : m_vars) varBits += var.second->totalWidth();
os << "(= #b"; os << "(= #b";
for (int i = bits - 1; i >= 0; i--) os << (VL_BITISSET_I(hash, i) ? '1' : '0'); for (int i = bits - 1; i >= 0; i--) os << (VL_BITISSET_I(hash, i) ? '1' : '0');
if (bits > 1) os << " (concat"; if (bits > 1) os << " (concat";
@@ -713,12 +685,11 @@ void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits,
IData varBitsLeft = varBits; IData varBitsLeft = varBits;
IData varBitsWant = (varBits + 1) / 2; IData varBitsWant = (varBits + 1) / 2;
if (varBits > 2) os << " (bvxor"; if (varBits > 2) os << " (bvxor";
for (const auto& varp : vars) { // LCOV_EXCL_BR_LINE - reservoir-sampling below for (const auto& var : m_vars) {
// always forces its last pick before exhausting vars for (int j = 0; j < var.second->totalWidth(); j++, varBitsLeft--) {
for (int j = 0; j < varp->totalWidth(); j++, varBitsLeft--) {
const bool doEmit = (VL_RANDOM_RNG_I(rngr) % varBitsLeft) < varBitsWant; const bool doEmit = (VL_RANDOM_RNG_I(rngr) % varBitsLeft) < varBitsWant;
if (doEmit) { if (doEmit) {
varp->emitExtract(os, j); var.second->emitExtract(os, j);
if (--varBitsWant == 0) break; if (--varBitsWant == 0) break;
} }
} }
@@ -808,7 +779,7 @@ bool VlRandomizer::next_check_only(VlRNGReseeds& rngr) { return nextRandomize(rn
bool VlRandomizer::next(VlRNGReseeds& rngr) { return nextRandomize(rngr, false); } bool VlRandomizer::next(VlRNGReseeds& rngr) { return nextRandomize(rngr, false); }
bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) { bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) {
if (!checkOnly && m_vars.empty() && m_uniqueArrays.empty()) return true; if (!checkOnly && m_vars.empty() && m_unique_arrays.empty()) return true;
if (checkOnly && m_vars.empty()) return true; // No rand members: trivially SAT if (checkOnly && m_vars.empty()) return true; // No rand members: trivially SAT
VlSolverSession& sess = s_solverSession; VlSolverSession& sess = s_solverSession;
const VerilatedLockGuard lock{sess.m_mutex}; const VerilatedLockGuard lock{sess.m_mutex};
@@ -843,27 +814,21 @@ bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) {
std::vector<std::string> VlRandomizer::buildUniqueExprs() const { std::vector<std::string> VlRandomizer::buildUniqueExprs() const {
std::vector<std::string> exprs; std::vector<std::string> exprs;
if (m_uniqueArrays.empty()) return exprs; if (m_unique_arrays.empty()) return exprs;
const auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars); const auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
for (const auto& [key, arrNameVec] : m_uniqueArrays) { for (const std::string& baseName : m_unique_arrays) {
uint32_t matchingElementsCount = 0; const auto it = m_vars.find(baseName);
std::ostringstream varsString; if (it == m_vars.end()) continue;
const VlRandomVar& var = *it->second;
for (const auto& baseName : arrNameVec) { // Select the elements the array actually holds now, by their own index
const auto it = m_vars.find(baseName); // or key, rather than by ordinal position
if (it == m_vars.end()) continue; var.setArrayInfo(arrVarsp);
const VlRandomVar& var = *it->second; // 'distinct' needs at least two operands; fewer elements are trivially unique
// Select the elements the array actually holds now, by their own index if (var.countMatchingElements(*arrVarsp, baseName) < 2) continue;
// or key, rather than by ordinal position
var.setArrayInfo(arrVarsp);
// 'distinct' needs at least two operands; fewer elements are trivially unique
matchingElementsCount += var.countMatchingElements(*arrVarsp, baseName);
var.emitGetValue(varsString);
}
if (matchingElementsCount < 2) continue;
std::ostringstream os; std::ostringstream os;
os << "(__Vbv (distinct " << varsString.str() << "))"; os << "(__Vbv (distinct ";
var.emitGetValue(os);
os << "))";
exprs.push_back(os.str()); exprs.push_back(os.str());
} }
return exprs; return exprs;
@@ -1612,7 +1577,7 @@ void VlRandomizer::clearConstraints() {
m_constraints_line.clear(); m_constraints_line.clear();
m_solveBefore.clear(); m_solveBefore.clear();
m_softConstraints.clear(); m_softConstraints.clear();
m_uniqueArrays.clear(); // Re-registered by constraint setup m_unique_arrays.clear(); // Re-registered by constraint setup
// Keep m_vars for class member randomization // Keep m_vars for class member randomization
} }
@@ -1625,7 +1590,7 @@ void VlRandomizer::clearAll() {
m_randcConstraintHash = 0; m_randcConstraintHash = 0;
} }
void VlRandomizer::markRandc(const std::string& name) { m_randcVarNames.insert(name); } void VlRandomizer::markRandc(const char* name) { m_randcVarNames.insert(name); }
void VlRandomizer::solveBefore(const std::string& beforeName, const std::string& afterName) { void VlRandomizer::solveBefore(const std::string& beforeName, const std::string& afterName) {
m_solveBefore.emplace_back(beforeName, afterName); m_solveBefore.emplace_back(beforeName, afterName);
@@ -1783,9 +1748,8 @@ bool VlRandomizer::solvePhaseValues(VlSolverSession& sess, VlRNG& rngr,
// Try diversity: add random constraint, re-check. If sat, get // Try diversity: add random constraint, re-check. If sat, get
// updated (more diverse) values. If unsat, keep baseline values. // updated (more diverse) values. If unsat, keep baseline values.
// Scoped to this layer's own vars -- see randomConstraint's comment.
os << "(assert "; os << "(assert ";
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN, &layerVars); randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
os << ")\n"; os << ")\n";
os << "(check-sat)\n"; os << "(check-sat)\n";
if (sess.readStatus() == VlSolverStatus::SAT) { if (sess.readStatus() == VlSolverStatus::SAT) {
+21 -208
View File
@@ -259,13 +259,10 @@ class VlRandomizer VL_NOT_FINAL {
m_constraints_line; // fileline content of the constraint for unsat constraints m_constraints_line; // fileline content of the constraint for unsat constraints
std::vector<std::string> m_softConstraints; // Soft constraints std::vector<std::string> m_softConstraints; // Soft constraints
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
// Scratch buffer for randomConstraint(), reused across calls
std::vector<const VlRandomVar*> m_randomConstraintVars;
std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back) std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back)
// variables // variables
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
// Arrays whose elements must be distinct grouped by unique constraint std::vector<std::string> m_unique_arrays; // Arrays whose elements must be distinct
std::map<uint32_t, std::unordered_set<std::string>> m_uniqueArrays;
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state; const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
const VlQueue<CData>* m_static_randmodep = nullptr; // Static rand_mode state (shared) const VlQueue<CData>* m_static_randmodep = nullptr; // Static rand_mode state (shared)
std::unordered_set<std::string> m_staticVars; // Names of static rand vars std::unordered_set<std::string> m_staticVars; // Names of static rand vars
@@ -282,8 +279,7 @@ class VlRandomizer VL_NOT_FINAL {
bool hasFrozenVar() const; // true if any var is currently rand_mode(0)-frozen bool hasFrozenVar() const; // true if any var is currently rand_mode(0)-frozen
// PRIVATE METHODS // PRIVATE METHODS
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits, void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
const std::vector<std::string>* layerVarsp = nullptr);
// Fetch the model and write it into the registered variables. // Fetch the model and write it into the registered variables.
bool applyModel(VlSolverSession& sess); bool applyModel(VlSolverSession& sess);
bool parseModel(std::istream& is, size_t requested); bool parseModel(std::istream& is, size_t requested);
@@ -470,16 +466,16 @@ public:
// Mark a variable as rand_mode-disabled: solver keeps it in m_vars // Mark a variable as rand_mode-disabled: solver keeps it in m_vars
// (so constraints still reference it) but skips write-back after solving. // (so constraints still reference it) but skips write-back after solving.
void set_var_disabled(const std::string& name) { m_disabledVars.insert(name); } void set_var_disabled(const char* name) { m_disabledVars.insert(name); }
// Clear disabled state for a variable // Clear disabled state for a variable
void clear_var_disabled(const std::string& name) { m_disabledVars.erase(name); } void clear_var_disabled(const char* name) { m_disabledVars.erase(name); }
// --- write_var to register variables --- // --- write_var to register variables ---
// Register scalar variable (non-struct, basic type) // Register scalar variable (non-struct, basic type)
template <typename T> template <typename T>
typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value, typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value,
void>::type void>::type
write_var(T& var, int width, const std::string& name, int dimension, write_var(T& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) { std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) != m_vars.end()) return; if (m_vars.find(name) != m_vars.end()) return;
// TODO: make_unique once VlRandomizer is per-instance not per-ref // TODO: make_unique once VlRandomizer is per-instance not per-ref
@@ -492,7 +488,7 @@ public:
typename std::enable_if<VlIsCustomStruct<T>::value, void>::type typename std::enable_if<VlIsCustomStruct<T>::value, void>::type
write_var(T& var, int width, const char* name, int dimension, write_var(T& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) { std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
modifyMembers(var, var.memberIndices(), name, randmodeIdx); modifyMembers(var, var.memberIndices(), name);
} }
// Register queue of non-struct types // Register queue of non-struct types
@@ -517,7 +513,7 @@ public:
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
write_var(VlQueue<T, N_MaxSize>& var, int width, const char* name, int dimension, write_var(VlQueue<T, N_MaxSize>& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) { std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
record_struct_arr(var, name, dimension, {}, {}, randmodeIdx); if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
} }
// Register unpacked array of non-struct types // Register unpacked array of non-struct types
template <typename T, std::size_t N_Depth> template <typename T, std::size_t N_Depth>
@@ -543,7 +539,7 @@ public:
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
write_var(VlUnpacked<T, N_Depth>& var, int /*width*/, const char* name, int dimension, write_var(VlUnpacked<T, N_Depth>& var, int /*width*/, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) { std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
record_struct_arr(var, name, dimension, {}, {}, randmodeIdx); if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
} }
// Register associative array of non-struct types // Register associative array of non-struct types
@@ -571,7 +567,7 @@ public:
typename std::enable_if<VlContainsCustomStruct<T_Value>::value, void>::type typename std::enable_if<VlContainsCustomStruct<T_Value>::value, void>::type
write_var(VlAssocArray<T_Key, T_Value>& var, int /*width*/, const char* name, int dimension, write_var(VlAssocArray<T_Key, T_Value>& var, int /*width*/, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) { std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
record_struct_arr(var, name, dimension, {}, {}, randmodeIdx); if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
} }
// --- Record Arrays: flat and struct --- // --- Record Arrays: flat and struct ---
@@ -586,23 +582,10 @@ public:
++m_index; ++m_index;
} }
template <typename T>
typename std::enable_if<!std::is_class<T>::value || VlIsVlWide<T>::value, void>::type
update_arr_table(T& var, const std::string& name, int /*dimension*/) {
const std::string key = generateKey(name, m_index++);
const auto it = m_arr_vars.find(key);
assert(it != m_arr_vars.end());
const ArrayInfo& arrayInfo = *it->second;
it->second = std::make_shared<const ArrayInfo>(arrayInfo.m_name, &var, arrayInfo.m_index,
arrayInfo.m_indices, arrayInfo.m_idxWidths);
}
// This is the "Sender" API for the generated code. // This is the "Sender" API for the generated code.
// The elements to make distinct are taken from the array element table at // The elements to make distinct are taken from the array element table at
// solve time, so a container resized by the solver is handled correctly. // solve time, so a container resized by the solver is handled correctly.
void rand_unique(const std::string& name, const uint32_t key) { void rand_unique(const std::string& name) { m_unique_arrays.push_back(name); }
m_uniqueArrays[key].insert(name);
}
// Recursively record all elements in an unpacked array // Recursively record all elements in an unpacked array
template <typename T, std::size_t N_Depth> template <typename T, std::size_t N_Depth>
@@ -620,15 +603,6 @@ public:
} }
} }
// Recursively update pointers to all elements in an unpacked array
template <typename T, std::size_t N_Depth>
void update_arr_table(VlUnpacked<T, N_Depth>& var, const std::string& name, int dimension) {
assert(dimension > 0);
for (size_t i = 0; i < N_Depth; ++i) {
update_arr_table(var[i], name + "[" + std::to_string(i) + "]", dimension - 1);
}
}
// Recursively record all elements in a queue // Recursively record all elements in a queue
template <typename T, size_t N_MaxSize> template <typename T, size_t N_MaxSize>
void record_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension, void record_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
@@ -644,15 +618,6 @@ public:
} }
} }
// Recursively update pointers to all elements in a queue
template <typename T, size_t N_MaxSize>
void update_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension) {
assert(dimension > 0);
for (size_t i = 0; i < var.size(); ++i) {
update_arr_table(var.atWrite(i), name + "[" + std::to_string(i) + "]", dimension - 1);
}
}
// Recursively record all elements in an associative array // Recursively record all elements in an associative array
template <typename T_Key, typename T_Value> template <typename T_Key, typename T_Value>
void record_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string& name, void record_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
@@ -683,27 +648,11 @@ public:
} }
} }
// Recursively update pointers to all elements in an associative array
template <typename T_Key, typename T_Value>
void update_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
int dimension) {
assert(dimension > 0);
for (auto it = var.begin(); it != var.end(); ++it) {
const T_Key& key = it->first;
std::string indexed_name;
std::vector<size_t> integral_index;
size_t idx_width = 0;
process_key(key, indexed_name, integral_index, name, idx_width);
update_arr_table(var.atWrite(key), indexed_name, dimension - 1);
}
}
// Register a single structArray element via write_var // Register a single structArray element via write_var
template <typename T> template <typename T>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
record_struct_arr(T& var, const std::string& name, int /*dimension*/, record_struct_arr(T& var, const std::string& name, int /*dimension*/,
std::vector<IData> indices, std::vector<size_t> idxWidths, std::vector<IData> indices, std::vector<size_t> idxWidths) {
std::uint32_t randmodeIdx) {
std::ostringstream oss; std::ostringstream oss;
for (size_t i = 0; i < indices.size(); ++i) { for (size_t i = 0; i < indices.size(); ++i) {
oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0') oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0')
@@ -711,75 +660,43 @@ public:
if (i < indices.size() - 1) oss << "."; if (i < indices.size() - 1) oss << ".";
} }
write_var(var, 1ULL, write_var(var, 1ULL,
oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL, oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL);
randmodeIdx);
}
template <typename T>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
update_struct_arr(T& var, const std::string& name) {
update_var(var, name.c_str());
} }
// Recursively process VlUnpacked of structs // Recursively process VlUnpacked of structs
template <typename T, std::size_t N_Depth> template <typename T, std::size_t N_Depth>
void record_struct_arr(VlUnpacked<T, N_Depth>& var, const std::string& name, int dimension, void record_struct_arr(VlUnpacked<T, N_Depth>& var, const std::string& name, int dimension,
std::vector<IData> indices, std::vector<size_t> idxWidths, std::vector<IData> indices, std::vector<size_t> idxWidths) {
std::uint32_t randmodeIdx) {
if (dimension > 0 && N_Depth != 0) { if (dimension > 0 && N_Depth != 0) {
constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1); constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1);
idxWidths.push_back(idx_width); idxWidths.push_back(idx_width);
for (size_t i = 0; i < N_Depth; ++i) { for (size_t i = 0; i < N_Depth; ++i) {
indices.push_back(i); indices.push_back(i);
record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths, record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths);
randmodeIdx);
indices.pop_back(); indices.pop_back();
} }
} }
} }
template <typename T, std::size_t N_Depth>
void update_struct_arr(VlUnpacked<T, N_Depth>& var, const std::string& name) {
constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1);
for (size_t i = 0; i < N_Depth; ++i) {
std::ostringstream oss;
oss << name << "." << std::hex << std::setw(int(idx_width / 4)) << std::setfill('0')
<< static_cast<int>(i);
update_struct_arr(var[i], oss.str());
}
}
// Recursively process VlQueue of structs // Recursively process VlQueue of structs
template <typename T, size_t N_MaxSize> template <typename T, size_t N_MaxSize>
void record_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension, void record_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
std::vector<IData> indices, std::vector<size_t> idxWidths, std::vector<IData> indices, std::vector<size_t> idxWidths) {
std::uint32_t randmodeIdx) {
if ((dimension > 0) && (var.size() != 0)) { if ((dimension > 0) && (var.size() != 0)) {
idxWidths.push_back(32); idxWidths.push_back(32);
for (size_t i = 0; i < var.size(); ++i) { for (size_t i = 0; i < var.size(); ++i) {
indices.push_back(i); indices.push_back(i);
record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths, record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths);
randmodeIdx);
indices.pop_back(); indices.pop_back();
} }
} }
} }
template <typename T, size_t N_MaxSize>
void update_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name) {
for (size_t i = 0; i < var.size(); ++i) {
std::ostringstream oss;
oss << name << "." << std::hex << std::setw(8) << std::setfill('0')
<< static_cast<int>(i);
update_struct_arr(var.atWrite(i), oss.str());
}
}
// Recursively process associative arrays of structs // Recursively process associative arrays of structs
template <typename T_Key, typename T_Value> template <typename T_Key, typename T_Value>
void record_struct_arr(VlAssocArray<T_Key, T_Value>& var, const std::string& name, void record_struct_arr(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
int dimension, const std::vector<IData>& indices, int dimension, const std::vector<IData>& indices,
const std::vector<size_t>& idxWidths, std::uint32_t randmodeIdx) { const std::vector<size_t>& idxWidths) {
if ((dimension > 0) && (!var.empty())) { if ((dimension > 0) && (!var.empty())) {
for (auto it = var.begin(); it != var.end(); ++it) { for (auto it = var.begin(); it != var.end(); ++it) {
const T_Key& key = it->first; const T_Key& key = it->first;
@@ -797,127 +714,23 @@ public:
std::string result = oss.str(); std::string result = oss.str();
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0'); result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
record_struct_arr(var.atWrite(key), name + "." + result, dimension - 1, indices, record_struct_arr(var.atWrite(key), name + "." + result, dimension - 1, indices,
idxWidths, randmodeIdx); idxWidths);
} }
} }
} }
template <typename T_Key, typename T_Value>
void update_struct_arr(VlAssocArray<T_Key, T_Value>& var, const std::string& name) {
for (auto it = var.begin(); it != var.end(); ++it) {
const T_Key& key = it->first;
std::string indexed_name;
std::vector<size_t> integral_index;
size_t idx_width = 0;
process_key(key, indexed_name, integral_index, name, idx_width);
std::ostringstream oss;
for (size_t i = 0; i < integral_index.size(); ++i) {
oss << std::hex << static_cast<int>(integral_index[i]);
}
std::string result = oss.str();
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
update_struct_arr(var.atWrite(key), name + "." + result);
}
}
// --- Helper functions --- // --- Helper functions ---
// Helper: Register all members of a user-defined struct // Helper: Register all members of a user-defined struct
template <typename T, std::size_t... I> template <typename T, std::size_t... I>
void modifyMembers(T& obj, std::index_sequence<I...>, const std::string& baseName, void modifyMembers(T& obj, std::index_sequence<I...>, const std::string& baseName) {
std::uint32_t randmodeIdx) {
// Use the indices to access each member via std::get // Use the indices to access each member via std::get
(void)std::initializer_list<int>{ (void)std::initializer_list<int>{
(write_var(std::get<I>(obj.getMembers(obj)), obj.memberWidth()[I], (write_var(std::get<I>(obj.getMembers(obj)), obj.memberWidth()[I],
(baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I], (baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I]),
randmodeIdx),
0)...}; 0)...};
} }
template <typename T, std::size_t... I>
void updateMembers(T& obj, std::index_sequence<I...>, const std::string& baseName) {
(void)std::initializer_list<int>{
(update_var(std::get<I>(obj.getMembers(obj)),
(baseName + "." + obj.memberNames()[I]).c_str()),
0)...};
}
template <typename T>
typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value,
void>::type
update_var(T& var, const char* name) {
auto it = m_vars.find(name);
assert(it != m_vars.end());
it->second = std::make_shared<const VlRandomVar>(
name, it->second->width(), &var, it->second->dimension(), it->second->randModeIdx());
}
template <typename T>
typename std::enable_if<VlIsCustomStruct<T>::value, void>::type update_var(T& var,
const char* name) {
updateMembers(var, var.memberIndices(), name);
}
template <typename T, size_t N_MaxSize>
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
update_var(VlQueue<T, N_MaxSize>& var, const char* name) {
auto it = m_vars.find(name);
assert(it != m_vars.end());
const int dimension = it->second->dimension();
it->second = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T, N_MaxSize>>>(
name, it->second->width(), &var, dimension, it->second->randModeIdx());
m_index = 0;
update_arr_table(var, name, dimension);
}
template <typename T, size_t N_MaxSize>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
update_var(VlQueue<T, N_MaxSize>& var, const char* name) {
update_struct_arr(var, name);
}
template <typename T, std::size_t N_Depth>
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
update_var(VlUnpacked<T, N_Depth>& var, const char* name) {
auto it = m_vars.find(name);
assert(it != m_vars.end());
const int dimension = it->second->dimension();
it->second = std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
name, it->second->width(), &var, dimension, it->second->randModeIdx());
m_index = 0;
update_arr_table(var, name, dimension);
}
template <typename T, std::size_t N_Depth>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
update_var(VlUnpacked<T, N_Depth>& var, const char* name) {
update_struct_arr(var, name);
}
template <typename T_Key, typename T_Value>
typename std::enable_if<!VlContainsCustomStruct<T_Value>::value, void>::type
update_var(VlAssocArray<T_Key, T_Value>& var, const char* name) {
auto it = m_vars.find(name);
assert(it != m_vars.end());
const int dimension = it->second->dimension();
std::vector<size_t> keyWidths;
VlRandomAssocKeyWidths<VlAssocArray<T_Key, T_Value>>::push(keyWidths);
it->second
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
name, it->second->width(), &var, dimension, it->second->randModeIdx(), keyWidths);
m_index = 0;
update_arr_table(var, name, dimension);
}
template <typename T_Key, typename T_Value>
typename std::enable_if<VlContainsCustomStruct<T_Value>::value, void>::type
update_var(VlAssocArray<T_Key, T_Value>& var, const char* name) {
update_struct_arr(var, name);
}
// Helper: Generate unique variable key from name and index // Helper: Generate unique variable key from name and index
static std::string generateKey(const std::string& name, int idx) { static std::string generateKey(const std::string& name, int idx) {
if (!name.empty() && name[0] == '\\') { if (!name.empty() && name[0] == '\\') {
@@ -952,7 +765,7 @@ public:
void disable_soft(const std::string& varName); void disable_soft(const std::string& varName);
void clearConstraints(); void clearConstraints();
void clearAll(); // Clear both constraints and variables void clearAll(); // Clear both constraints and variables
void markRandc(const std::string& name); // Mark variable as randc for cyclic tracking void markRandc(const char* name); // Mark variable as randc for cyclic tracking
void solveBefore(const std::string& beforeName, void solveBefore(const std::string& beforeName,
const std::string& afterName); // Register solve-before ordering const std::string& afterName); // Register solve-before ordering
void set_randmode(const VlQueue<CData>& randmode) { m_randmodep = &randmode; } void set_randmode(const VlQueue<CData>& randmode) { m_randmodep = &randmode; }
+46 -56
View File
@@ -99,6 +99,46 @@ package std;
endfunction endfunction
endclass endclass
// IEEE 1800-specified standard "semaphore"
class semaphore;
protected int m_keyCount;
protected int m_nextKeyCount = '1;
protected longint unsigned m_ticket = 0;
protected longint unsigned m_nextTicket = 0;
function new(int keyCount = 0);
m_keyCount = keyCount;
endfunction
function void put(int keyCount = 1);
m_keyCount += keyCount;
endfunction
task get(int keyCount = 1);
`ifdef VERILATOR_TIMING
longint unsigned ticket;
// Fast path: take if keys fit AND either no one is queued, or
// the head still doesn't fit (so we're not stealing its keys).
if (m_keyCount >= keyCount && m_nextKeyCount > m_keyCount) begin
m_keyCount -= keyCount;
return;
end
ticket = m_nextTicket++;
wait (m_ticket == ticket);
m_nextKeyCount = keyCount;
wait (m_keyCount >= keyCount);
m_keyCount -= keyCount;
m_ticket++;
`endif
endtask
function int try_get(int keyCount = 1);
if (m_keyCount < keyCount) return 0;
m_keyCount -= keyCount;
return 1;
endfunction
endclass
// IEEE 1800-specified standard "process" // IEEE 1800-specified standard "process"
class process; class process;
typedef enum { typedef enum {
@@ -203,56 +243,6 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
endfunction endfunction
endclass endclass
// IEEE 1800-specified standard "semaphore"
class semaphore;
typedef struct {
process proc;
int reqKeys;
} procKeys;
procKeys m_queue[$];
protected int m_keyCount;
local task dropKilled;
// Drop killed processes from the queue
`ifdef VERILATOR_TIMING
while (m_queue.size() > 0 && m_queue[0].proc.status() == process::KILLED) m_queue.pop_front();
`endif
endtask
function new(int keyCount = 0);
m_keyCount = keyCount;
endfunction
function void put(int keyCount = 1);
m_keyCount += keyCount;
endfunction
task get(int keyCount = 1);
`ifdef VERILATOR_TIMING
dropKilled();
// Fast path: take if keys fit AND either no one is queued, or
// the head still doesn't fit (so we're not stealing its keys).
if (m_keyCount >= keyCount && (m_queue.size() == 0 || m_queue[0].reqKeys > keyCount)) begin
m_keyCount -= keyCount;
return;
end
m_queue.push_back('{process::self(), keyCount});
while (m_queue[0].proc != process::self()) begin
wait (m_queue[0].proc.status() == process::KILLED || m_queue[0].proc == process::self());
dropKilled();
end
wait (m_keyCount >= keyCount);
m_keyCount -= keyCount;
m_queue.pop_front();
`endif
endtask
function int try_get(int keyCount = 1);
if (m_keyCount < keyCount) return 0;
m_keyCount -= keyCount;
return 1;
endfunction
endclass
// IEEE 1800-specified standard "std::randomize" // IEEE 1800-specified standard "std::randomize"
function int randomize(); function int randomize();
randomize = 0; randomize = 0;
@@ -263,7 +253,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
// them as such currently, so named with a unique prefix // them as such currently, so named with a unique prefix
typedef struct { typedef struct {
string name; string name;
int weight = 1; int weight;
int goal; int goal;
string comment; string comment;
int at_least; int at_least;
@@ -276,7 +266,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_covergroup_options_t; } vl_covergroup_options_t;
typedef struct { typedef struct {
int weight = 1; int weight;
int goal; int goal;
string comment; string comment;
int at_least; int at_least;
@@ -285,7 +275,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_coverpoint_options_t; } vl_coverpoint_options_t;
typedef struct { typedef struct {
int weight = 1; int weight;
int goal; int goal;
string comment; string comment;
int at_least; int at_least;
@@ -294,7 +284,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_cross_options_t; } vl_cross_options_t;
typedef struct { typedef struct {
int weight = 1; int weight;
int goal; int goal;
string comment; string comment;
bit strobe; bit strobe;
@@ -304,14 +294,14 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_covergroup_type_options_t; } vl_covergroup_type_options_t;
typedef struct { typedef struct {
int weight = 1; int weight;
int goal; int goal;
string comment; string comment;
real real_interval; real real_interval;
} vl_coverpoint_type_options_t; } vl_coverpoint_type_options_t;
typedef struct { typedef struct {
int weight = 1; int weight;
int goal; int goal;
string comment; string comment;
} vl_cross_type_options_t; } vl_cross_type_options_t;
+5 -5
View File
@@ -45,11 +45,11 @@ lint_off -rule WIDTHTRUNC `VLT_UVM_FILES
// Context-sensitive waivers // Context-sensitive waivers
lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case ({is_R, is_W})*" lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case ({is_R, is_W})*"
lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case(orig_severity)*" lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case(orig_severity)*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_callback*" lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_callback*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_component*" lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_component*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_event*" lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_event*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_report_object*" lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_report_object*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_sequence_item*" lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_sequence_item*"
lint_off -rule IGNOREDRETURN `VLT_UVM_FILES -match "* uvm_core_state_callback::add*" lint_off -rule IGNOREDRETURN `VLT_UVM_FILES -match "* uvm_core_state_callback::add*"
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (abstractions[i])*" lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (abstractions[i])*"
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (lock_list[i])*" lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (lock_list[i])*"
+5 -20
View File
@@ -38,8 +38,8 @@
// See also V3Ast::VNumRange // See also V3Ast::VNumRange
class VerilatedRange final { class VerilatedRange final {
int m_left = 0; // Left side of range (pre-':') int m_left = 0;
int m_right = 0; // Right side of range (post-':') int m_right = 0;
protected: protected:
friend class VerilatedVarProps; friend class VerilatedVarProps;
@@ -158,18 +158,7 @@ public:
VerilatedVarFlags vldir() const { VerilatedVarFlags vldir() const {
return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR); return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR);
} }
uint32_t entSize() const VL_MT_SAFE { uint32_t entSize() const VL_MT_SAFE;
if (m_entSize) return m_entSize;
switch (vltype()) {
case VLVT_PTR: return sizeof(void*);
case VLVT_UINT8: return sizeof(CData);
case VLVT_UINT16: return sizeof(SData);
case VLVT_UINT32: return sizeof(IData);
case VLVT_UINT64: return sizeof(QData);
case VLVT_WDATA: return VL_WORDS_I(entBits()) * sizeof(IData);
default: return 0; // LCOV_EXCL_LINE
}
}
uint32_t entBits() const VL_MT_SAFE { uint32_t entBits() const VL_MT_SAFE {
uint32_t bits = 1; uint32_t bits = 1;
for (auto it : m_packed) bits *= it.elements(); for (auto it : m_packed) bits *= it.elements();
@@ -225,11 +214,7 @@ public:
: 0; : 0;
} }
// Total size in bytes (note DPI limited to 4GB) // Total size in bytes (note DPI limited to 4GB)
size_t totalSize() const { size_t totalSize() const;
size_t size = entSize();
for (int udim = 0; udim < udims(); ++udim) size *= m_unpacked[udim].elements();
return size;
}
// Adjust a data pointer to access a given array element, NULL if something goes bad // Adjust a data pointer to access a given array element, NULL if something goes bad
void* datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE; void* datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE;
}; };
@@ -283,7 +268,7 @@ class VerilatedVar final : public VerilatedVarProps {
m_forceControlSignals; // Force control signals m_forceControlSignals; // Force control signals
protected: protected:
const bool m_isParam; // From a parameter const bool m_isParam;
friend class VerilatedScope; friend class VerilatedScope;
// CONSTRUCTORS // CONSTRUCTORS
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype, VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
-9
View File
@@ -72,13 +72,4 @@ public:
~VerilatedHierarchyMap() = default; ~VerilatedHierarchyMap() = default;
}; };
// Map of sorted interface reference names to the concrete interface they refer to
// Keyed by value, as the full name is built at construction
// This is a class instead of typedef/using to allow forward declaration in verilated.h
class VerilatedIfaceRefMap final : public std::map<std::string, VerilatedIfaceRef, std::less<>> {
public:
VerilatedIfaceRefMap() = default;
~VerilatedIfaceRefMap() = default;
};
#endif // Guard #endif // Guard
+1 -1
View File
@@ -140,7 +140,7 @@ class VlWorkerThread final {
}; };
// MEMBERS // MEMBERS
mutable VerilatedMutex m_mutex; // Protection mutex mutable VerilatedMutex m_mutex;
std::condition_variable_any m_cv; std::condition_variable_any m_cv;
// Only notify the condition_variable if the worker is waiting // Only notify the condition_variable if the worker is waiting
bool m_waiting VL_GUARDED_BY(m_mutex) = false; bool m_waiting VL_GUARDED_BY(m_mutex) = false;
+2 -5
View File
@@ -296,11 +296,8 @@ void VlProcess::forkSyncOnKillClear(VlForkSyncState* forkSyncp) {
} }
void VlProcess::state(int s) { void VlProcess::state(int s) {
if (completed()) { if (s == KILLED && m_state != KILLED && m_state != FINISHED && m_forkSyncOnKillp
// We can't change the sttaus of completed process && !m_forkSyncOnKillDone) {
return;
}
if (s == KILLED && m_forkSyncOnKillp && !m_forkSyncOnKillDone) {
m_forkSyncOnKillDone = true; m_forkSyncOnKillDone = true;
m_state = s; m_state = s;
m_forkSyncOnKillp->done(); m_forkSyncOnKillp->done();
+31 -1
View File
@@ -64,6 +64,36 @@
// Placeholder for compiling with --protect-ids // Placeholder for compiling with --protect-ids
#define VL_UNKNOWN "<unknown>" #define VL_UNKNOWN "<unknown>"
//=============================================================================
// VlFileLineDebug stores a SystemVerilog source code location. Used in VlCoroutineHandle for
// debugging purposes.
class VlFileLineDebug final {
// MEMBERS
#ifdef VL_DEBUG
const char* m_filename = nullptr;
int m_lineno = 0;
#endif
public:
// CONSTRUCTORS
// Construct
VlFileLineDebug() = default;
VlFileLineDebug(const char* filename, int lineno)
#ifdef VL_DEBUG
: m_filename{filename}
, m_lineno{lineno}
#endif
{
}
// METHODS
#ifdef VL_DEBUG
const char* filename() const { return m_filename; }
int lineno() const { return m_lineno; }
#endif
};
//============================================================================= //=============================================================================
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is // VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got // cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
@@ -401,7 +431,7 @@ class VlForkSyncState final {
public: public:
size_t m_counter = 0; // When reaches 0, resume suspended coroutine size_t m_counter = 0; // When reaches 0, resume suspended coroutine
VlCoroutineHandle m_susp; // Coroutine to resume VlCoroutineHandle m_susp; // Coroutine to resume
bool m_inited = false; // Initialization complete bool m_inited = false;
size_t m_pendingDones = 0; // done() calls seen before init() (e.g. early killed branch) size_t m_pendingDones = 0; // done() calls seen before init() (e.g. early killed branch)
bool m_inDone = false; // Guard against re-entrant resume recursion from nested kills bool m_inDone = false; // Guard against re-entrant resume recursion from nested kills
bool m_resumePending = false; // Join reached zero again while inside done() bool m_resumePending = false; // Join reached zero again while inside done()
+3 -3
View File
@@ -147,9 +147,9 @@ private:
struct CallbackRecord final { struct CallbackRecord final {
union { // The callback union { // The callback
const initCb_t m_initCb; // Init-callback constructor const initCb_t m_initCb;
const dumpCb_t m_dumpCb; // Dump-callback constructor const dumpCb_t m_dumpCb;
const cleanupCb_t m_cleanupCb; // Cleanup-callback constructor const cleanupCb_t m_cleanupCb;
}; };
const uint32_t m_fidx; // The index of the tracing function const uint32_t m_fidx; // The index of the tracing function
void* const m_userp; // The user pointer to pass to the callback (the symbol table) void* const m_userp; // The user pointer to pass to the callback (the symbol table)
+1 -19
View File
@@ -32,7 +32,6 @@
#include <array> #include <array>
#include <atomic> #include <atomic>
#include <deque> #include <deque>
#include <initializer_list>
#include <map> #include <map>
#include <memory> #include <memory>
#include <set> #include <set>
@@ -232,7 +231,7 @@ extern std::string VL_TO_STRING(SData lhs);
extern std::string VL_TO_STRING(IData lhs); extern std::string VL_TO_STRING(IData lhs);
extern std::string VL_TO_STRING(QData lhs); extern std::string VL_TO_STRING(QData lhs);
extern std::string VL_TO_STRING(double lhs); extern std::string VL_TO_STRING(double lhs);
extern std::string VL_TO_STRING(const std::string& obj) VL_PURE; inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
template <std::size_t N_Words> template <std::size_t N_Words>
inline std::string VL_TO_STRING(const VlWide<N_Words>& obj) { inline std::string VL_TO_STRING(const VlWide<N_Words>& obj) {
return VL_TO_STRING_W(N_Words, obj); return VL_TO_STRING_W(N_Words, obj);
@@ -1085,15 +1084,6 @@ public:
// CONSTRUCTORS // CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it. // m_defaultValue isn't defaulted. Caller's constructor must do it.
VlAssocArray() = default; VlAssocArray() = default;
// Construct with the given entries
explicit VlAssocArray(std::initializer_list<std::pair<const T_Key, T_Value>> init)
: m_map{init}
, m_defaultValue{} {}
// Construct with the given default value and entries
explicit VlAssocArray(const T_Value& defaultValue,
std::initializer_list<std::pair<const T_Key, T_Value>> init)
: m_map{init}
, m_defaultValue{defaultValue} {}
~VlAssocArray() = default; ~VlAssocArray() = default;
VlAssocArray(const VlAssocArray&) = default; VlAssocArray(const VlAssocArray&) = default;
VlAssocArray(VlAssocArray&&) = default; VlAssocArray(VlAssocArray&&) = default;
@@ -1478,14 +1468,6 @@ public:
constexpr std::size_t size() const { return N_Depth; } constexpr std::size_t size() const { return N_Depth; }
// Runtime slice v[loIdx +: N_Out], loIdx being an index into m_storage
template <std::size_t N_Out>
VlUnpacked<T_Value, N_Out> slice(int32_t loIdx) const {
VlUnpacked<T_Value, N_Out> out;
for (std::size_t i = 0; i < N_Out; ++i) out.m_storage[i] = m_storage[loIdx + i];
return out;
}
void fill(const T_Value& value) { void fill(const T_Value& value) {
std::fill(std::begin(m_storage), std::end(m_storage), value); std::fill(std::begin(m_storage), std::end(m_storage), value);
} }
+137 -307
View File
@@ -253,34 +253,34 @@ public:
if (m_fullname.empty()) m_fullname = std::string{m_scopep->name()} + '.' + m_varp->name(); if (m_fullname.empty()) m_fullname = std::string{m_scopep->name()} + '.' + m_varp->name();
return m_fullname.c_str(); return m_fullname.c_str();
} }
virtual void* readDatap() const { return m_varp->datap(); } virtual void* varDatap() const { return m_varp->datap(); }
CData* readCDatap() const { CData* varCDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8););
return static_cast<CData*>(readDatap()); return reinterpret_cast<CData*>(varDatap());
} }
SData* readSDatap() const { SData* varSDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16););
return static_cast<SData*>(readDatap()); return reinterpret_cast<SData*>(varDatap());
} }
IData* readIDatap() const { IData* varIDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32););
return static_cast<IData*>(readDatap()); return reinterpret_cast<IData*>(varDatap());
} }
QData* readQDatap() const { QData* varQDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64););
return static_cast<QData*>(readDatap()); return reinterpret_cast<QData*>(varDatap());
} }
EData* readEDatap() const { EData* varEDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA););
return static_cast<EData*>(readDatap()); return reinterpret_cast<EData*>(varDatap());
} }
double* readRealDatap() const { double* varRealDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL););
return static_cast<double*>(readDatap()); return reinterpret_cast<double*>(varDatap());
} }
std::string* readStringDatap() const { std::string* varStringDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING););
return static_cast<std::string*>(readDatap()); return reinterpret_cast<std::string*>(varDatap());
} }
virtual uint32_t bitOffset() const { return 0; } virtual uint32_t bitOffset() const { return 0; }
}; };
@@ -363,13 +363,11 @@ protected:
public: public:
explicit VerilatedVpioScope(const VerilatedScope* scopep) explicit VerilatedVpioScope(const VerilatedScope* scopep)
: m_scopep{scopep} { : m_scopep{scopep} {
m_fullname = vpiFullnamep(m_scopep->name()); m_fullname = m_scopep->name();
if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
m_name = m_scopep->identifier(); m_name = m_scopep->identifier();
m_defname = m_scopep->defname(); m_defname = m_scopep->defname();
} }
static const char* vpiFullnamep(const char* namep) VL_PURE {
return (std::strncmp(namep, "TOP.", 4) == 0) ? namep + 4 : namep;
}
~VerilatedVpioScope() override = default; ~VerilatedVpioScope() override = default;
// cppcheck-suppress duplInheritedMember // cppcheck-suppress duplInheritedMember
static VerilatedVpioScope* castp(vpiHandle h) { static VerilatedVpioScope* castp(vpiHandle h) {
@@ -412,9 +410,6 @@ public:
m_name = name; m_name = name;
m_fullNameOverride = fullname; m_fullNameOverride = fullname;
} }
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep,
const std::string& name, const std::string& fullname)
: VerilatedVpioVar{varp, scopep, varp->datap(), name, fullname} {}
explicit VerilatedVpioVar(const VerilatedVpioVar* vop) explicit VerilatedVpioVar(const VerilatedVpioVar* vop)
: VerilatedVpioVarBase{vop} { : VerilatedVpioVarBase{vop} {
if (vop) { if (vop) {
@@ -520,7 +515,7 @@ public:
const std::string localName = _vl_vpi_member_local_name(memberVarp->name()); const std::string localName = _vl_vpi_member_local_name(memberVarp->name());
return new VerilatedVpioVar{memberVarp, scopep(), return new VerilatedVpioVar{memberVarp, scopep(),
static_cast<uint8_t*>(readDatap()) + offset, localName, static_cast<uint8_t*>(varDatap()) + offset, localName,
std::string{fullname()} + memberName.substr(parentLen)}; std::string{fullname()} + memberName.substr(parentLen)};
} }
uint32_t type() const override { uint32_t type() const override {
@@ -545,7 +540,7 @@ public:
return m_fullname.c_str(); return m_fullname.c_str();
} }
uint8_t* prevDatap() const { return m_prevDatap; } uint8_t* prevDatap() const { return m_prevDatap; }
void* readDatap() const override { return m_varDatap; } void* varDatap() const override { return m_varDatap; }
void createPrevDatap() { void createPrevDatap() {
if (VL_UNLIKELY(!m_prevDatap)) { if (VL_UNLIKELY(!m_prevDatap)) {
m_prevDatap = new uint8_t[entSize()]; m_prevDatap = new uint8_t[entSize()];
@@ -726,17 +721,6 @@ public:
uint32_t type() const override { return vpiModule; } uint32_t type() const override { return vpiModule; }
}; };
class VerilatedVpioInterface final : public VerilatedVpioScope {
public:
explicit VerilatedVpioInterface(const VerilatedScope* scopep)
: VerilatedVpioScope{scopep} {}
// cppcheck-suppress duplInheritedMember
static VerilatedVpioInterface* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioInterface*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiInterface; }
};
class VerilatedVpioModuleIter final : public VerilatedVpio { class VerilatedVpioModuleIter final : public VerilatedVpio {
const std::vector<const VerilatedScope*>* m_vec; const std::vector<const VerilatedScope*>* m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it; std::vector<const VerilatedScope*>::const_iterator m_it;
@@ -794,8 +778,6 @@ public:
return (new VerilatedVpioScope{modp})->castVpiHandle(); return (new VerilatedVpioScope{modp})->castVpiHandle();
} else if (itype == VerilatedScope::SCOPE_MODULE) { } else if (itype == VerilatedScope::SCOPE_MODULE) {
return (new VerilatedVpioModule{modp})->castVpiHandle(); return (new VerilatedVpioModule{modp})->castVpiHandle();
} else if (itype == VerilatedScope::SCOPE_INTERFACE) {
return (new VerilatedVpioInterface{modp})->castVpiHandle();
} }
} }
} }
@@ -821,90 +803,6 @@ public:
uint32_t type() const override { return vpiPackage; } uint32_t type() const override { return vpiPackage; }
}; };
class VerilatedVpioModport final : public VerilatedVpio {
const VerilatedScope* const m_scopep; // Interface the modport is within
const char* const m_name; // Modport name
const std::string m_fullname; // Interface full name + "." + modport name
public:
VerilatedVpioModport(const VerilatedScope* scopep, const char* namep)
: m_scopep{scopep}
, m_name{namep}
, m_fullname{std::string{VerilatedVpioScope::vpiFullnamep(scopep->name())} + "." + namep} {
}
~VerilatedVpioModport() override = default;
// cppcheck-suppress duplInheritedMember
static VerilatedVpioModport* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModport*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiModport; }
const VerilatedScope* scopep() const { return m_scopep; }
const char* name() const override { return m_name; }
const char* fullname() const override { return m_fullname.c_str(); }
// IEEE 1800-2023 37.15
const char* defname() const override { return m_name; }
};
class VerilatedVpioIfaceRef final : public VerilatedVpio {
// Held by value, as a handle may outlive the model that registered it
const VerilatedIfaceRef m_ifaceRef;
public:
explicit VerilatedVpioIfaceRef(const VerilatedIfaceRef& ifaceRef)
: m_ifaceRef{ifaceRef} {}
~VerilatedVpioIfaceRef() override = default;
// cppcheck-suppress duplInheritedMember
static VerilatedVpioIfaceRef* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioIfaceRef*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiRefObj; }
const VerilatedIfaceRef* ifaceRefp() const { return &m_ifaceRef; }
const char* name() const override { return m_ifaceRef.name(); }
const char* fullname() const override {
return VerilatedVpioScope::vpiFullnamep(m_ifaceRef.fullname());
}
// IEEE 1800-2023 37.15: modport name, else the interface definition name
const char* defname() const override {
return m_ifaceRef.hasModport() ? m_ifaceRef.modport() : m_ifaceRef.scopep()->defname();
}
vpiHandle actual() const {
if (m_ifaceRef.hasModport()) {
return (new VerilatedVpioModport{m_ifaceRef.scopep(), m_ifaceRef.modport()})
->castVpiHandle();
}
return (new VerilatedVpioInterface{m_ifaceRef.scopep()})->castVpiHandle();
}
};
class VerilatedVpioInterfaceIter final : public VerilatedVpio {
const std::vector<const VerilatedScope*>* m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it;
public:
explicit VerilatedVpioInterfaceIter(const std::vector<const VerilatedScope*>& vec)
: m_vec{&vec} {
m_it = m_vec->begin();
}
~VerilatedVpioInterfaceIter() override = default;
// cppcheck-suppress duplInheritedMember
static VerilatedVpioInterfaceIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioInterfaceIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
while (true) {
if (m_it == m_vec->end()) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
const VerilatedScope* const scopep = *m_it++;
if (scopep->type() == VerilatedScope::SCOPE_INTERFACE) {
return (new VerilatedVpioInterface{scopep})->castVpiHandle();
}
}
}
};
class VerilatedVpioInstanceIter final : public VerilatedVpio { class VerilatedVpioInstanceIter final : public VerilatedVpio {
const std::vector<const VerilatedScope*>* m_vec; const std::vector<const VerilatedScope*>* m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it; std::vector<const VerilatedScope*>::const_iterator m_it;
@@ -1097,13 +995,22 @@ public:
case vpiOctStrVal: // FALLTHRU case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU case vpiHexStrVal: // FALLTHRU
case vpiStringVal: // FALLTHRU case vpiStringVal: {
if (VL_UNLIKELY(!valuep->value.str)) return false;
break;
}
case vpiScalarVal: // FALLTHRU case vpiScalarVal: // FALLTHRU
case vpiIntVal: // FALLTHRU case vpiIntVal: // FALLTHRU
case vpiRealVal: // FALLTHRU case vpiRealVal: break;
case vpiVectorVal: return true; case vpiVectorVal: {
default: return false; if (VL_UNLIKELY(!valuep->value.vector)) return false;
break;
} }
default: {
return false;
}
}
return true;
} }
}; };
@@ -1285,8 +1192,8 @@ public:
template <typename T> template <typename T>
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) { static bool valueDiffersFromPrev(VerilatedVpioVar* varop) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p size=%d\n", VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p size=%d\n",
varop->fullname(), *(static_cast<CData*>(varop->readDatap())), varop->fullname(), *(static_cast<CData*>(varop->varDatap())),
*(varop->prevDatap()), varop->readDatap(), varop->prevDatap(), *(varop->prevDatap()), varop->varDatap(), varop->prevDatap(),
varop->entSize());); varop->entSize()););
if (varop->bitSize() == 1) { if (varop->bitSize() == 1) {
T* const prevDatap = reinterpret_cast<T*>( T* const prevDatap = reinterpret_cast<T*>(
@@ -1297,7 +1204,7 @@ public:
prevInfo.m_datap = prevDatap; prevInfo.m_datap = prevDatap;
return vl_vpi_get_word_gen(currInfo) != vl_vpi_get_word_gen(prevInfo); return vl_vpi_get_word_gen(currInfo) != vl_vpi_get_word_gen(prevInfo);
} }
return std::memcmp(varop->prevDatap(), varop->readDatap(), varop->entSize()) != 0; return std::memcmp(varop->prevDatap(), varop->varDatap(), varop->entSize()) != 0;
} }
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) { static bool valueDiffersFromPrev(VerilatedVpioVar* varop) {
switch (varop->varp()->vltype()) { switch (varop->varp()->vltype()) {
@@ -1326,9 +1233,9 @@ public:
prevInfo.m_datap = prevDatap; prevInfo.m_datap = prevDatap;
const T currWord = vl_vpi_get_word_gen(currInfo); const T currWord = vl_vpi_get_word_gen(currInfo);
vl_vpi_put_word_gen(prevInfo, currWord); vl_vpi_put_word_gen(prevInfo, currWord);
assert(std::memcmp(varop->prevDatap(), varop->readDatap(), varop->entSize()) == 0); assert(std::memcmp(varop->prevDatap(), varop->varDatap(), varop->entSize()) == 0);
} else { } else {
std::memcpy(varop->prevDatap(), varop->readDatap(), varop->entSize()); std::memcpy(varop->prevDatap(), varop->varDatap(), varop->entSize());
} }
} }
static void updatePrev(const VerilatedVpioVar* const varop) { static void updatePrev(const VerilatedVpioVar* const varop) {
@@ -1369,7 +1276,7 @@ public:
if (valueDiffersFromPrev(varop)) { if (valueDiffersFromPrev(varop)) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n", VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
ho.id(), varop->fullname(), ho.id(), varop->fullname(),
*(static_cast<CData*>(varop->readDatap())));); *(static_cast<CData*>(varop->varDatap()))););
update.insert(varop); update.insert(varop);
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value); vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap()); (ho.cb_rtnp())(ho.cb_datap());
@@ -1720,9 +1627,9 @@ VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseSignal
// assert(forceEnableSignalVop->entSize() == baseSignalVop->entSize()); // assert(forceEnableSignalVop->entSize() == baseSignalVop->entSize());
assert(forceValueSignalVop->entSize() == baseSignalVop->entSize()); assert(forceValueSignalVop->entSize() == baseSignalVop->entSize());
assert(forceReadSignalVop->entSize() == baseSignalVop->entSize()); assert(forceReadSignalVop->entSize() == baseSignalVop->entSize());
assert(forceEnableSignalVop->readDatap() == forceEnableSignalVarp->datap()); assert(forceEnableSignalVop->varDatap() == forceEnableSignalVarp->datap());
assert(forceValueSignalVop->readDatap() == forceValueSignalVarp->datap()); assert(forceValueSignalVop->varDatap() == forceValueSignalVarp->datap());
assert(forceReadSignalVop->readDatap() == forceReadSignalVarp->datap()); assert(forceReadSignalVop->varDatap() == forceReadSignalVarp->datap());
#endif // VL_DEBUG #endif // VL_DEBUG
return VerilatedVpiImp::ForceControlSignalVops{ return VerilatedVpiImp::ForceControlSignalVops{
@@ -1752,9 +1659,9 @@ double VerilatedVpiImp::getReadDataWord(const VerilatedVpioVar* baseSignalVop,
const VerilatedVpioVar* forceEnableSignalVop, const VerilatedVpioVar* forceEnableSignalVop,
const VerilatedVpioVar* forceValueSignalVop, const VerilatedVpioVar* forceValueSignalVop,
size_t /*bitCount*/, size_t /*bitOffset*/) { size_t /*bitCount*/, size_t /*bitOffset*/) {
const double baseSignalData = *baseSignalVop->readRealDatap(); const double baseSignalData = *baseSignalVop->varRealDatap();
const bool forceEnableData = *forceEnableSignalVop->readCDatap(); const bool forceEnableData = *forceEnableSignalVop->varCDatap();
const double forceValueData = *forceValueSignalVop->readRealDatap(); const double forceValueData = *forceValueSignalVop->varRealDatap();
const double readData = forceEnableData ? forceValueData : baseSignalData; const double readData = forceEnableData ? forceValueData : baseSignalData;
return readData; return readData;
} }
@@ -2092,9 +1999,6 @@ const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE {
"vpiStmt" "vpiStmt"
}; };
// clang-format on // clang-format on
// SystemVerilog relations are numbered far above the Verilog ones
if (vpiVal == vpiActual) return "vpiActual";
if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl) return strFromVpiObjType(vpiVal);
if (vpiVal > vpiStmt || vpiVal < vpiCondition) return "*undefined*"; if (vpiVal > vpiStmt || vpiVal < vpiCondition) return "*undefined*";
return names[vpiVal - vpiCondition]; return names[vpiVal - vpiCondition];
} }
@@ -2498,8 +2402,6 @@ void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiCondition); SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiCondition);
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiStmt); SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiStmt);
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiActual);
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiInterface);
SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbValueChange); SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbValueChange);
SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbAtEndOfSimTime); SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbAtEndOfSimTime);
@@ -2799,7 +2701,7 @@ _vl_vpi_handle_indexed_member_from_scope(const VerilatedScope* const scopep,
VerilatedVpioVar* baseVop VerilatedVpioVar* baseVop
= fullnameOverride.empty() = fullnameOverride.empty()
? new VerilatedVpioVar{baseVarp, varScopep} ? new VerilatedVpioVar{baseVarp, varScopep}
: new VerilatedVpioVar{baseVarp, varScopep, : new VerilatedVpioVar{baseVarp, varScopep, baseVarp->datap(),
_vl_vpi_member_local_name(baseVarp->name()), _vl_vpi_member_local_name(baseVarp->name()),
fullnameOverride}; fullnameOverride};
VerilatedVpioVar* vop = _vl_vpi_handle_apply_indices(baseVop, indices); VerilatedVpioVar* vop = _vl_vpi_handle_apply_indices(baseVop, indices);
@@ -2869,10 +2771,6 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope); const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
const VerilatedVpioVar* const voVarp = VerilatedVpioVar::castp(scope); const VerilatedVpioVar* const voVarp = VerilatedVpioVar::castp(scope);
// Not scopes, so no name resolves relative to them; must not fall through to
// the unprefixed lookup below, which would resolve from the top level
if (VerilatedVpioIfaceRef::castp(scope) || VerilatedVpioModport::castp(scope)) return nullptr;
if (0 == std::strncmp(scopeAndName.c_str(), "$root.", std::strlen("$root."))) { if (0 == std::strncmp(scopeAndName.c_str(), "$root.", std::strlen("$root."))) {
scopeAndName.erase(0, std::strlen("$root.")); scopeAndName.erase(0, std::strlen("$root."));
} else if (voScopep) { } else if (voScopep) {
@@ -2900,15 +2798,8 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
if (scopep->type() == VerilatedScope::SCOPE_PACKAGE) { if (scopep->type() == VerilatedScope::SCOPE_PACKAGE) {
return (new VerilatedVpioPackage{scopep})->castVpiHandle(); return (new VerilatedVpioPackage{scopep})->castVpiHandle();
} }
if (scopep->type() == VerilatedScope::SCOPE_INTERFACE) {
return (new VerilatedVpioInterface{scopep})->castVpiHandle();
}
return (new VerilatedVpioScope{scopep})->castVpiHandle(); return (new VerilatedVpioScope{scopep})->castVpiHandle();
} }
if (const VerilatedIfaceRef* const ifaceRefp
= Verilated::threadContextp()->ifaceRefFind(scopeAndName.c_str())) {
return (new VerilatedVpioIfaceRef{*ifaceRefp})->castVpiHandle();
}
std::string basename = scopeAndName; std::string basename = scopeAndName;
std::string scopename; std::string scopename;
std::string::size_type prevpos = std::string::npos; std::string::size_type prevpos = std::string::npos;
@@ -2971,9 +2862,10 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
if (varp->isParam()) { if (varp->isParam()) {
resultHandle = (new VerilatedVpioParam{varp, scopep})->castVpiHandle(); resultHandle = (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
} else if (!fullnameOverride.empty()) { } else if (!fullnameOverride.empty()) {
resultHandle = (new VerilatedVpioVar{varp, scopep, _vl_vpi_member_local_name(varp->name()), resultHandle
fullnameOverride}) = (new VerilatedVpioVar{varp, scopep, varp->datap(),
->castVpiHandle(); _vl_vpi_member_local_name(varp->name()), fullnameOverride})
->castVpiHandle();
} else { } else {
resultHandle = (new VerilatedVpioVar{varp, scopep})->castVpiHandle(); resultHandle = (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
} }
@@ -3067,24 +2959,6 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
const int32_t val = vop->index().back(); const int32_t val = vop->index().back();
return (new VerilatedVpioConst{val})->castVpiHandle(); return (new VerilatedVpioConst{val})->castVpiHandle();
} }
case vpiActual: {
if (const VerilatedVpioIfaceRef* const vop = VerilatedVpioIfaceRef::castp(object)) {
return vop->actual();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
return nullptr;
}
case vpiInterface: {
if (const VerilatedVpioModport* const vop = VerilatedVpioModport::castp(object)) {
return (new VerilatedVpioInterface{vop->scopep()})->castVpiHandle();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
return nullptr;
}
case vpiScope: { case vpiScope: {
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object); const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr; if (VL_UNLIKELY(!vop)) return nullptr;
@@ -3151,15 +3025,6 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
if (it == map->end()) return nullptr; if (it == map->end()) return nullptr;
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle()); return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
} }
case vpiInterface: {
// IEEE 1800-2023 37.5: interfaces are a one-to-many of a module
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
if (it == map->end()) return nullptr;
return ((new VerilatedVpioInterfaceIter{it->second})->castVpiHandle());
}
case vpiInternalScope: { case vpiInternalScope: {
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object); const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap(); const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
@@ -3370,18 +3235,6 @@ bool vl_check_format(const VerilatedVpioVarBase* vop, const p_vpi_value valuep,
return false; return false;
} }
static const char* vl_put_value_null_member(const p_vpi_value valuep) {
switch (valuep->format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: return valuep->value.str ? nullptr : "str";
case vpiVectorVal: return valuep->value.vector ? nullptr : "vector";
default: return nullptr;
}
}
// Get a VPI format that can be used to fully represent a signal of the given type // Get a VPI format that can be used to fully represent a signal of the given type
PLI_INT32 vl_get_vltype_format(VerilatedVarType vlType) { PLI_INT32 vl_get_vltype_format(VerilatedVarType vlType) {
switch (vlType) { switch (vlType) {
@@ -3441,7 +3294,7 @@ VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t
bitCount, varBits - addOffset}); bitCount, varBits - addOffset});
VarAccessInfo<T> info; VarAccessInfo<T> info;
info.m_datap = static_cast<T*>(vop->readDatap()); info.m_datap = reinterpret_cast<T*>(vop->varDatap());
if (vop->varp()->vltype() == VLVT_WDATA) { if (vop->varp()->vltype() == VLVT_WDATA) {
assert(sizeof(T) == sizeof(EData)); assert(sizeof(T) == sizeof(EData));
assert(bitCount <= wordBits); assert(bitCount <= wordBits);
@@ -3489,7 +3342,8 @@ T vl_vpi_get_word_gen(VarAccessInfo<T> info) {
template <typename T> template <typename T>
T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
return vl_vpi_get_word_gen(vl_vpi_var_access_info<T>(vop, bitCount, addOffset)); const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
return vl_vpi_get_word_gen(info);
} }
template <typename T> template <typename T>
@@ -3507,7 +3361,8 @@ void vl_vpi_put_word_gen(VarAccessInfo<T> info, T word) {
template <typename T> template <typename T>
void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) { void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) {
vl_vpi_put_word_gen(vl_vpi_var_access_info<T>(vop, bitCount, addOffset), word); const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
vl_vpi_put_word_gen(info, word);
} }
// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds // bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds
@@ -3544,7 +3399,7 @@ void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, s
void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
const VerilatedVar* const varp = vop->varp(); const VerilatedVar* const varp = vop->varp();
void* const varDatap = vop->readDatap(); void* const varDatap = vop->varDatap();
if (!vl_check_format(vop, valuep, true)) return; if (!vl_check_format(vop, valuep, true)) return;
// string data type is dynamic and may vary in size during simulation // string data type is dynamic and may vary in size during simulation
@@ -3585,7 +3440,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
return; return;
} else if (valuep->format == vpiBinStrVal) { } else if (valuep->format == vpiBinStrVal) {
t_outDynamicStr.resize(varBits); t_outDynamicStr.resize(varBits);
const CData* datap = static_cast<CData*>(varDatap); const CData* datap = reinterpret_cast<CData*>(varDatap);
for (size_t i = 0; i < varBits; ++i) { for (size_t i = 0; i < varBits; ++i) {
const size_t pos = i + vop->bitOffset(); const size_t pos = i + vop->bitOffset();
const char val = (datap[pos >> 3] >> (pos & 7)) & 1; const char val = (datap[pos >> 3] >> (pos & 7)) & 1;
@@ -3630,10 +3485,10 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
} else if (valuep->format == vpiStringVal) { } else if (valuep->format == vpiStringVal) {
if (varp->vltype() == VLVT_STRING) { if (varp->vltype() == VLVT_STRING) {
if (varp->isParam()) { if (varp->isParam()) {
valuep->value.str = static_cast<char*>(varDatap); valuep->value.str = reinterpret_cast<char*>(varDatap);
return; return;
} }
t_outDynamicStr = *vop->readStringDatap(); t_outDynamicStr = *vop->varStringDatap();
valuep->value.str = const_cast<char*>(t_outDynamicStr.c_str()); valuep->value.str = const_cast<char*>(t_outDynamicStr.c_str());
return; return;
} else { } else {
@@ -3651,7 +3506,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
valuep->value.integer = vl_vpi_get_word(vop, 32, 0); valuep->value.integer = vl_vpi_get_word(vop, 32, 0);
return; return;
} else if (valuep->format == vpiRealVal) { } else if (valuep->format == vpiRealVal) {
valuep->value.real = *(vop->readRealDatap()); valuep->value.real = *(vop->varRealDatap());
return; return;
} else if (valuep->format == vpiScalarVal) { } else if (valuep->format == vpiScalarVal) {
valuep->value.scalar = vl_vpi_get_word(vop, 32, 0) ? vpi1 : vpi0; valuep->value.scalar = vl_vpi_get_word(vop, 32, 0) ? vpi1 : vpi0;
@@ -3742,7 +3597,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
baseSignalVop->fullname(), valuep->format, baseSignalVop->fullname(), valuep->format,
valuep->value.integer); valuep->value.integer);
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n",
baseSignalVop->varp()->datap(), baseSignalVop->readDatap());); baseSignalVop->varp()->datap(), baseSignalVop->varDatap()););
if (VL_UNLIKELY(!baseSignalVop->varp()->isPublicRW())) { if (VL_UNLIKELY(!baseSignalVop->varp()->isPublicRW())) {
VL_VPI_ERROR_(__FILE__, __LINE__, VL_VPI_ERROR_(__FILE__, __LINE__,
@@ -3761,16 +3616,6 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
return nullptr; return nullptr;
} }
if (!vl_check_format(baseSignalVop, valuep, false)) return nullptr; if (!vl_check_format(baseSignalVop, valuep, false)) return nullptr;
// On release valuep is only written
if (forceFlag != vpiReleaseFlag) {
if (const char* const memberp = vl_put_value_null_member(valuep)) {
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Ignoring nullptr value.%s with format %s for '%s'", __func__,
memberp, VerilatedVpiError::strFromVpiVal(valuep->format),
baseSignalVop->fullname());
return nullptr;
}
}
if (delay_mode == vpiInertialDelay) { if (delay_mode == vpiInertialDelay) {
if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) { if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
VL_VPI_WARNING_( VL_VPI_WARNING_(
@@ -3830,7 +3675,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
if (baseSignalVop->varp()->vltype() == VLVT_REAL) { if (baseSignalVop->varp()->vltype() == VLVT_REAL) {
const double readData = VerilatedVpiImp::getReadDataWord<double>( const double readData = VerilatedVpiImp::getReadDataWord<double>(
baseSignalVop, forceEnableSignalVop, forceValueSignalVop, 64, 0); baseSignalVop, forceEnableSignalVop, forceValueSignalVop, 64, 0);
*forceReadSignalVop->readRealDatap() = readData; *forceReadSignalVop->varRealDatap() = readData;
return; return;
} }
@@ -3935,6 +3780,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
} }
if (valuep->format == vpiVectorVal) { if (valuep->format == vpiVectorVal) {
if (VL_UNLIKELY(!valuep->value.vector)) return nullptr;
if (valueVop->varp()->vltype() == VLVT_WDATA) { if (valueVop->varp()->vltype() == VLVT_WDATA) {
const int words = VL_WORDS_I(varBits); const int words = VL_WORDS_I(varBits);
for (int i = 0; i < words; ++i) for (int i = 0; i < words; ++i)
@@ -3951,7 +3797,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
} }
} else if (valuep->format == vpiBinStrVal) { } else if (valuep->format == vpiBinStrVal) {
const int len = std::strlen(valuep->value.str); const int len = std::strlen(valuep->value.str);
CData* const datap = static_cast<CData*>(valueVop->readDatap()); CData* const datap = reinterpret_cast<CData*>(valueVop->varDatap());
for (int i = 0; i < varBits; ++i) { for (int i = 0; i < varBits; ++i) {
const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1'); const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1');
const size_t pos = valueVop->bitOffset() + i; const size_t pos = valueVop->bitOffset() + i;
@@ -4033,7 +3879,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
} else if (valuep->format == vpiStringVal) { } else if (valuep->format == vpiStringVal) {
if (valueVop->varp()->vltype() == VLVT_STRING) { if (valueVop->varp()->vltype() == VLVT_STRING) {
// Does not use valueVop, because strings are not forceable anyway // Does not use valueVop, because strings are not forceable anyway
*(baseSignalVop->readStringDatap()) = valuep->value.str; *(baseSignalVop->varStringDatap()) = valuep->value.str;
return object; return object;
} }
const int chars = VL_BYTES_I(varBits); const int chars = VL_BYTES_I(varBits);
@@ -4050,7 +3896,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
return object; return object;
} else if (valuep->format == vpiRealVal) { } else if (valuep->format == vpiRealVal) {
if (valueVop->varp()->vltype() == VLVT_REAL) { if (valueVop->varp()->vltype() == VLVT_REAL) {
*(valueVop->readRealDatap()) = valuep->value.real; *(valueVop->varRealDatap()) = valuep->value.real;
if (baseSignalVop->varp()->isForceable()) updateVforceRd(); if (baseSignalVop->varp()->isForceable()) updateVforceRd();
return object; return object;
} }
@@ -4095,7 +3941,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
case VLVT_UINT8: case VLVT_UINT8:
case VLVT_UINT16: case VLVT_UINT16:
case VLVT_UINT32: return true; case VLVT_UINT32: return true;
default:; default:; // LCOV_EXCL_LINE
} }
break; break;
case vpiRawTwoStateVal: case vpiRawTwoStateVal:
@@ -4113,7 +3959,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
switch (varp->vltype()) { switch (varp->vltype()) {
case VLVT_UINT8: case VLVT_UINT8:
case VLVT_UINT16: return true; case VLVT_UINT16: return true;
default:; default:; // LCOV_EXCL_LINE
} }
break; break;
case vpiLongIntVal: case vpiLongIntVal:
@@ -4122,7 +3968,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
case VLVT_UINT16: case VLVT_UINT16:
case VLVT_UINT32: case VLVT_UINT32:
case VLVT_UINT64: return true; case VLVT_UINT64: return true;
default:; default:; // LCOV_EXCL_LINE
} }
break; break;
default:; default:;
@@ -4321,10 +4167,10 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readCDatap(), shortintsp); vop->varCDatap(), shortintsp);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readSDatap(), shortintsp); vop->varSDatap(), shortintsp);
} }
return; return;
@@ -4336,13 +4182,13 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readCDatap(), integersp); vop->varCDatap(), integersp);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readSDatap(), integersp); vop->varSDatap(), integersp);
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readIDatap(), integersp); vop->varIDatap(), integersp);
} }
return; return;
@@ -4354,16 +4200,16 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readCDatap(), longintsp); vop->varCDatap(), longintsp);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readSDatap(), longintsp); vop->varSDatap(), longintsp);
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readIDatap(), longintsp); vop->varIDatap(), longintsp);
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readQDatap(), longintsp); vop->varQDatap(), longintsp);
} }
return; return;
@@ -4375,19 +4221,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->readCDatap(), vectorsp); vop->varCDatap(), vectorsp);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->readSDatap(), vectorsp); vop->varSDatap(), vectorsp);
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->readIDatap(), vectorsp); vop->varIDatap(), vectorsp);
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->readQDatap(), vectorsp); vop->varQDatap(), vectorsp);
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->readEDatap(), vectorsp); vop->varEDatap(), vectorsp);
} }
return; return;
@@ -4399,19 +4245,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->readCDatap(), valuep); vop->varCDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->readSDatap(), valuep); vop->varSDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->readIDatap(), valuep); vop->varIDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->readQDatap(), valuep); vop->varQDatap(), valuep);
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->readEDatap(), valuep); vop->varEDatap(), valuep);
} }
return; return;
@@ -4423,19 +4269,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->readCDatap(), valuep); vop->varCDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->readSDatap(), valuep); vop->varSDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->readIDatap(), valuep); vop->varIDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->readQDatap(), valuep); vop->varQDatap(), valuep);
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->readEDatap(), valuep); vop->varEDatap(), valuep);
} }
return; return;
@@ -4498,8 +4344,17 @@ void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const PLI_INT32* index_p, void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const PLI_INT32* index_p,
PLI_UINT32 num) { PLI_UINT32 num) {
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
const VerilatedVar* const varp = vop->varp(); const VerilatedVar* const varp = vop->varp();
const int size = vop->size(); const int size = vop->size();
if (VL_UNCOVERABLE(num > size)) {
VL_VPI_ERROR_(__FILE__, __LINE__,
"%s: Requested elements to set (%u) exceed array size (%u)", __func__, num,
size);
return;
}
const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low(); const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low();
const int index const int index
@@ -4511,129 +4366,114 @@ void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
vop->readCDatap()); vop->varCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
vop->readSDatap()); vop->varSDatap());
return;
} }
return;
} else if (arrayvalue_p->format == vpiIntVal) { } else if (arrayvalue_p->format == vpiIntVal) {
const PLI_UINT32* integersp = reinterpret_cast<PLI_UINT32*>(arrayvalue_p->value.integers); const PLI_UINT32* integersp = reinterpret_cast<PLI_UINT32*>(arrayvalue_p->value.integers);
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
vop->readCDatap()); vop->varCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
vop->readSDatap()); vop->varSDatap());
return;
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
vop->readIDatap()); vop->varIDatap());
return;
} }
return;
} else if (arrayvalue_p->format == vpiLongIntVal) { } else if (arrayvalue_p->format == vpiLongIntVal) {
const PLI_UINT64* longintsp = reinterpret_cast<PLI_UINT64*>(arrayvalue_p->value.longints); const PLI_UINT64* longintsp = reinterpret_cast<PLI_UINT64*>(arrayvalue_p->value.longints);
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->readCDatap()); vop->varCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->readSDatap()); vop->varSDatap());
return;
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->readIDatap()); vop->varIDatap());
return;
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->readQDatap()); vop->varQDatap());
return;
} }
return;
} else if (arrayvalue_p->format == vpiVectorVal) { } else if (arrayvalue_p->format == vpiVectorVal) {
const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors; const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors;
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->readCDatap()); vectorsp, vop->varCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->readSDatap()); vectorsp, vop->varSDatap());
return;
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->readIDatap()); vectorsp, vop->varIDatap());
return;
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->readQDatap()); vectorsp, vop->varQDatap());
return;
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->readEDatap()); vectorsp, vop->varEDatap());
return;
} }
return;
} else if (arrayvalue_p->format == vpiRawFourStateVal) { } else if (arrayvalue_p->format == vpiRawFourStateVal) {
const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals); const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals);
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->readCDatap()); vop->varCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->readSDatap()); vop->varSDatap());
return;
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->readIDatap()); vop->varIDatap());
return;
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->readQDatap()); vop->varQDatap());
return;
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->readEDatap()); vop->varEDatap());
return;
} }
return;
} else if (arrayvalue_p->format == vpiRawTwoStateVal) { } else if (arrayvalue_p->format == vpiRawTwoStateVal) {
const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals); const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals);
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->readCDatap()); vop->varCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->readSDatap()); vop->varSDatap());
return;
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->readIDatap()); vop->varIDatap());
return;
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->readQDatap()); vop->varQDatap());
return;
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->readEDatap()); vop->varEDatap());
return;
} }
return;
} }
// Reached only if vl_check_array_format and this dispatch drift apart
// LCOV_EXCL_START
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for '%s'", VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for '%s'",
__func__, VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), __func__, VerilatedVpiError::strFromVpiVal(arrayvalue_p->format),
vop->fullname()); vop->fullname());
// LCOV_EXCL_STOP
} }
void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p,
@@ -4688,16 +4528,6 @@ void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
return; return;
} }
if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
const unsigned size = vop->size();
if (VL_UNLIKELY(num > size)) {
VL_VPI_ERROR_(__FILE__, __LINE__,
"%s: Requested elements to set (%u) exceed array size (%u)", __func__, num,
size);
return;
}
if (num == 0) return;
vl_put_value_array(object, arrayvalue_p, index_p, num); vl_put_value_array(object, arrayvalue_p, index_p, num);
} }
+2 -16
View File
@@ -318,17 +318,6 @@
# define VL_CONSTEXPR_CXX17 # define VL_CONSTEXPR_CXX17
#endif #endif
//=========================================================================
// C++-2020
#if __cplusplus >= 202002L
# define VL_CONSTINIT_CXX20 constinit
# define VL_NO_UNIQUE_ADDRESS_CXX20 [[no_unique_address]]
#else
# define VL_CONSTINIT_CXX20
# define VL_NO_UNIQUE_ADDRESS_CXX20
#endif
//========================================================================= //=========================================================================
// Optimization // Optimization
@@ -463,8 +452,7 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_VFORMATATTR_SIGNED '~' // (int widthMin, IData/VlWide/etc) Signed number; for %d showing sign #define VL_VFORMATATTR_SIGNED '~' // (int widthMin, IData/VlWide/etc) Signed number; for %d showing sign
#define VL_VFORMATATTR_COMPLEX '!' // (std::string*); for non-POD; e.g. struct, requires %p typically #define VL_VFORMATATTR_COMPLEX '!' // (std::string*); for non-POD; e.g. struct, requires %p typically
#define VL_VFORMATATTR_DOUBLE 'D' // (double); promote %p to %f #define VL_VFORMATATTR_DOUBLE 'D' // (double); promote %p to %f
#define VL_VFORMATATTR_ENUM 'E' // (width, IData/QData/const EData*, STRING, std::string* name) #define VL_VFORMATATTR_ENUM 'E' // (width, IData/QData, std::string* name); <= 64 bit enum with runtime %p/%s
#define VL_VFORMATATTR_ENUM_SIGNED 'F' // Same arguments as ENUM, with a signed numeric value
#define VL_VFORMATATTR_SCOPE 'M' // (char* name, char* scope); for scopes #define VL_VFORMATATTR_SCOPE 'M' // (char* name, char* scope); for scopes
#define VL_VFORMATATTR_STRING 'S' // (char* name, char* scope); for scopes // (std::string*); for %p/%s #define VL_VFORMATATTR_STRING 'S' // (char* name, char* scope); for scopes // (std::string*); for %p/%s
#define VL_VFORMATATTR_TIMEUNIT 'T' // (int timeunit); timeunits passed from V3Emit to runtime #define VL_VFORMATATTR_TIMEUNIT 'T' // (int timeunit); timeunits passed from V3Emit to runtime
@@ -543,12 +531,10 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_EUL(n) VL_UL(n) // Make constant number EData sized #define VL_EUL(n) VL_UL(n) // Make constant number EData sized
#define VL_BITWORD_I(bit) ((bit) / VL_IDATASIZE) ///< Word number for sv DPI vectors #define VL_BITWORD_I(bit) ((bit) / VL_IDATASIZE) ///< Word number for sv DPI vectors
#define VL_BITWORD_Q(bit) ((bit) / VL_QUADSIZE) ///< Quadword number for a bit
#define VL_BITWORD_E(bit) ((bit) >> VL_EDATASIZE_LOG2) ///< Word number for a wide quantity #define VL_BITWORD_E(bit) ((bit) >> VL_EDATASIZE_LOG2) ///< Word number for a wide quantity
#define VL_BITBIT_I(bit) ((bit) & VL_SIZEBITS_I) ///< Bit number for a bit in a long #define VL_BITBIT_I(bit) ((bit) & VL_SIZEBITS_I) ///< Bit number for a bit in a long
#define VL_BITBIT_Q(bit) ((bit) & VL_SIZEBITS_Q) ///< Bit number for a bit in a quad #define VL_BITBIT_Q(bit) ((bit) & VL_SIZEBITS_Q) ///< Bit number for a bit in a quad
#define VL_BITBIT_E(bit) ((bit) & VL_SIZEBITS_E) ///< Bit number for a bit in an EData #define VL_BITBIT_E(bit) ((bit) & VL_SIZEBITS_E) ///< Bit number for a bit in an EData
#define VL_BIT_Q(bit) (1ULL << VL_BITBIT_Q(bit)) ///< Quad with only the given bit set
// Return true if data[bit] set; not 0/1 return, but 0/non-zero return. // Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit))) #define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit)))
@@ -750,7 +736,7 @@ namespace vlstd {
template <typename T> template <typename T>
struct reverse_wrapper final { struct reverse_wrapper final {
const T& m_v; // Pre-wrapped iterator const T& m_v;
explicit reverse_wrapper(const T& a_v) explicit reverse_wrapper(const T& a_v)
: m_v(a_v) {} // Need () constructor : m_v(a_v) {} // Need () constructor
-36
View File
@@ -1,36 +0,0 @@
#!/bin/bash
# DESCRIPTION: Verilator: gcov wrapper for fastcov, bounding gcov memory usage
#
# Usage: GCOV=<gcov> nodist/gcov_batch [gcov options] <files>
#
# fastcov passes all coverage files of a worker to a single gcov process, but
# gcov does not free memory between input files, which can exhaust memory with
# many files. This wrapper runs the gcov in $GCOV on batches of at most 16
# files at a time instead. The output is the same as if all files were passed
# to a single gcov.
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
gcov="${GCOV:-gcov}"
batch=16
opts=()
files=()
for arg in "$@"; do
case "$arg" in
-*) opts+=("$arg") ;;
*) files+=("$arg") ;;
esac
done
# No files, e.g.: 'gcov -v'
if [ ${#files[@]} -eq 0 ]; then
exec "$gcov" "${opts[@]}"
fi
status=0
for ((i = 0; i < ${#files[@]}; i += batch)); do
"$gcov" "${opts[@]}" "${files[@]:i:batch}" || status=$?
done
exit $status
+1 -3
View File
@@ -25,10 +25,8 @@ def message_section(msg: str) -> int:
return 50 return 50
if re.match(r'^Support', msg, flags=re.IGNORECASE): if re.match(r'^Support', msg, flags=re.IGNORECASE):
return 60 return 60
if re.match(r'^Optimize', msg, flags=re.IGNORECASE):
return 70
if re.match(r'^Fix', msg, flags=re.IGNORECASE): if re.match(r'^Fix', msg, flags=re.IGNORECASE):
return 80 return 70
if re.match(r'^(Internals|CI|Tests)', msg, flags=re.IGNORECASE): if re.match(r'^(Internals|CI|Tests)', msg, flags=re.IGNORECASE):
return -1 return -1
if re.match(r'^Bump.* from .* to .*', msg, flags=re.IGNORECASE): # dependabot if re.match(r'^Bump.* from .* to .*', msg, flags=re.IGNORECASE): # dependabot
+2 -5
View File
@@ -12,23 +12,20 @@
# SPDX-FileCopyrightText: 2026 Wilson Snyder # SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
# These flags tested against verible-v0.0-4192-g682a1d50 # These flags tested against verible-v0.0-4080-ga0a8d8eb
verible-verilog-format \ verible-verilog-format \
--inplace \ --inplace \
\ --wrap_end_else_clauses \
--assignment_statement_alignment=flush-left \ --assignment_statement_alignment=flush-left \
--case_items_alignment=flush-left \ --case_items_alignment=flush-left \
--class_member_variable_alignment=flush-left \ --class_member_variable_alignment=flush-left \
--distribution_items_alignment=flush-left \ --distribution_items_alignment=flush-left \
--enum_assignment_statement_alignment=flush-left \ --enum_assignment_statement_alignment=flush-left \
--expand_coverpoints \
--formal_parameters_alignment=flush-left \ --formal_parameters_alignment=flush-left \
--module_net_variable_alignment=flush-left \ --module_net_variable_alignment=flush-left \
--named_parameter_alignment=flush-left \ --named_parameter_alignment=flush-left \
--named_port_alignment=flush-left \ --named_port_alignment=flush-left \
--parameter_declaration_alignment=flush-left \
--port_declarations_alignment=flush-left \ --port_declarations_alignment=flush-left \
--struct_union_members_alignment=flush-left \ --struct_union_members_alignment=flush-left \
--wrap_end_else_clauses \
$* $*
+1 -15
View File
@@ -68,7 +68,6 @@ set(HEADERS
V3Combine.h V3Combine.h
V3Common.h V3Common.h
V3Const.h V3Const.h
V3ConstPool.h
V3Container.h V3Container.h
V3Control.h V3Control.h
V3Coverage.h V3Coverage.h
@@ -115,7 +114,6 @@ set(HEADERS
V3GraphPathChecker.h V3GraphPathChecker.h
V3GraphStream.h V3GraphStream.h
V3Hash.h V3Hash.h
V3HashTable.h
V3Hasher.h V3Hasher.h
V3HierBlock.h V3HierBlock.h
V3Inline.h V3Inline.h
@@ -238,7 +236,6 @@ set(COMMON_SOURCES
V3Clock.cpp V3Clock.cpp
V3Combine.cpp V3Combine.cpp
V3Common.cpp V3Common.cpp
V3ConstPool.cpp
V3Control.cpp V3Control.cpp
V3Const__gen.cpp V3Const__gen.cpp
V3Coverage.cpp V3Coverage.cpp
@@ -295,7 +292,6 @@ set(COMMON_SOURCES
V3GraphPathChecker.cpp V3GraphPathChecker.cpp
V3GraphTest.cpp V3GraphTest.cpp
V3Hash.cpp V3Hash.cpp
V3HashTable.cpp
V3Hasher.cpp V3Hasher.cpp
V3HierBlock.cpp V3HierBlock.cpp
V3Inline.cpp V3Inline.cpp
@@ -349,7 +345,6 @@ set(COMMON_SOURCES
V3Sampled.cpp V3Sampled.cpp
V3Sched.cpp V3Sched.cpp
V3SchedAcyclic.cpp V3SchedAcyclic.cpp
V3SchedCovergroup.cpp
V3SchedPartition.cpp V3SchedPartition.cpp
V3SchedReplicate.cpp V3SchedReplicate.cpp
V3SchedTiming.cpp V3SchedTiming.cpp
@@ -567,18 +562,9 @@ set_target_properties(
MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL> MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL>
#JOB_POOL_LINK one_job # Linking takes lots of resources #JOB_POOL_LINK one_job # Linking takes lots of resources
INTERPROCEDURAL_OPTIMIZATION_RELEASE $<IF:MINGW,FALSE,TRUE> INTERPROCEDURAL_OPTIMIZATION_RELEASE $<IF:MINGW,FALSE,TRUE>
INCLUDE_DIRECTORIES ${FLEX_INCLUDE_DIR}
) )
if(MINGW)
# Search MSYS Flex headers after the active toolchain's system headers.
target_compile_options(
${verilator}
PRIVATE -idirafter "${FLEX_INCLUDE_DIR}"
)
else()
target_include_directories(${verilator} PRIVATE "${FLEX_INCLUDE_DIR}")
endif()
add_dependencies( add_dependencies(
${verilator} ${verilator}
V3Lexer_yy_cpp${CMAKE_BUILD_TYPE} V3Lexer_yy_cpp${CMAKE_BUILD_TYPE}
+19 -4
View File
@@ -35,10 +35,25 @@ endif
UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD) UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
# Use bear if installed, and new enough to support the '--output FILE --' syntax ifeq (,$(wildcard obj_dbg/bear.o))
BEAR := $(shell bear=$$(which bear 2>/dev/null) && tmp=$$(mktemp -d 2>/dev/null) \ ifneq (, $(shell which bear 2>/dev/null))
&& { "$$bear" --output "$$tmp/probe.json" -- true >/dev/null 2>&1 && echo "$$bear"; rm -rf "$$tmp"; }) BEAR := $(shell which bear)
BEAR_OBJ_OPT := $(if $(BEAR),$(BEAR) --append --output obj_dbg/compile_commands.json --) ifeq (, $(shell $(BEAR) --output obj_dbg/comptest.json -- true))
$(shell which bear 2>/dev/null >obj_dbg/bear.o)
else
# unsupported version
BEAR :=
endif
endif
else
BEAR := $(shell cat obj_dbg/bear.o)
endif
ifneq ($(BEAR),)
BEAR_OBJ_OPT := $(BEAR) --append --output obj_dbg/compile_commands.json --
else
BEAR_OBJ_OPT :=
endif
#********************************************************************* #*********************************************************************
+5 -8
View File
@@ -187,6 +187,7 @@ maintainer-copy::
#### Top executable #### Top executable
RAW_OBJS = \ RAW_OBJS = \
V3Const__gen.o \
V3Error.o \ V3Error.o \
V3FileLine.o \ V3FileLine.o \
V3Graph.o \ V3Graph.o \
@@ -195,7 +196,6 @@ RAW_OBJS = \
V3GraphPathChecker.o \ V3GraphPathChecker.o \
V3GraphTest.o \ V3GraphTest.o \
V3Hash.o \ V3Hash.o \
V3HashTable.o \
V3OptionParser.o \ V3OptionParser.o \
V3Os.o \ V3Os.o \
V3ParseGrammar.o \ V3ParseGrammar.o \
@@ -212,7 +212,6 @@ RAW_OBJS_PCH_ASTMT = \
V3Ast.o \ V3Ast.o \
V3AstNodes.o \ V3AstNodes.o \
V3Broken.o \ V3Broken.o \
V3ConstPool.o \
V3Control.o \ V3Control.o \
V3EmitCBase.o \ V3EmitCBase.o \
V3EmitCConstPool.o \ V3EmitCConstPool.o \
@@ -253,7 +252,6 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Clock.o \ V3Clock.o \
V3Combine.o \ V3Combine.o \
V3Common.o \ V3Common.o \
V3Const__gen.o \
V3Coverage.o \ V3Coverage.o \
V3Covergroup.o \ V3Covergroup.o \
V3CoverageJoin.o \ V3CoverageJoin.o \
@@ -332,7 +330,6 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Sampled.o \ V3Sampled.o \
V3Sched.o \ V3Sched.o \
V3SchedAcyclic.o \ V3SchedAcyclic.o \
V3SchedCovergroup.o \
V3SchedPartition.o \ V3SchedPartition.o \
V3SchedReplicate.o \ V3SchedReplicate.o \
V3SchedTiming.o \ V3SchedTiming.o \
@@ -411,6 +408,8 @@ $(TGT): $(PREDEP_H) $(OBJS)
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS) %__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
$(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) $*.cpp $(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) $*.cpp
.SECONDARY:
%.gch: % %.gch: %
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH} -c $< -o $@ $(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH} -c $< -o $@
%.o: %.cpp %.o: %.cpp
@@ -430,7 +429,7 @@ V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
define CXX_ASTMT_template define CXX_ASTMT_template
$(1): $(basename $(1)).cpp V3PchAstMT.h.gch $(1): $(basename $(1)).cpp V3PchAstMT.h.gch
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstMT.h${CFG_GCH_IF_CLANG} -c $$< -o $(1) $(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstMT.h${CFG_GCH_IF_CLANG} -c $(srcdir)/$(basename $(1)).cpp -o $(1)
endef endef
@@ -438,7 +437,7 @@ $(foreach obj,$(RAW_OBJS_PCH_ASTMT),$(eval $(call CXX_ASTMT_template,$(obj))))
define CXX_ASTNOMT_template define CXX_ASTNOMT_template
$(1): $(basename $(1)).cpp V3PchAstNoMT.h.gch $(1): $(basename $(1)).cpp V3PchAstNoMT.h.gch
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstNoMT.h${CFG_GCH_IF_CLANG} -c $$< -o $(1) $(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstNoMT.h${CFG_GCH_IF_CLANG} -c $(srcdir)/$(basename $(1)).cpp -o $(1)
endef endef
@@ -488,8 +487,6 @@ header_cc: $(addsuffix __header_cc.o, $(basename $(HEADER_CC_H)))
%__header_cc.cpp: %.h %__header_cc.cpp: %.h
$(PYTHON3) $(srcdir)/../bin/verilator_includer -DVL_MT_DISABLED_CODE_UNIT=1 $^ > $@ $(PYTHON3) $(srcdir)/../bin/verilator_includer -DVL_MT_DISABLED_CODE_UNIT=1 $^ > $@
.SECONDARY: V3Const__gen.cpp $(addsuffix __header_cc.cpp, $(basename $(HEADER_CC_H)))
.SUFFIXES: .SUFFIXES:
###################################################################### ######################################################################
+2 -2
View File
@@ -689,8 +689,8 @@ class CovergroupEventBindVisitor final : public VNVisitor {
public: public:
CovergroupEventBindVisitor(AstSenTree* eventp, AstClass* classp, AstVarScope* instancep) CovergroupEventBindVisitor(AstSenTree* eventp, AstClass* classp, AstVarScope* instancep)
: m_instancep{instancep} { : m_instancep{instancep} {
classp->foreachMember([&](AstClass* const, AstVar* const varp) { // classp->foreachMember([&](AstClass* const, AstVar* const varp) {
m_memberps.emplace(varp); if (!varp->isStatic()) m_memberps.emplace(varp);
}); });
iterate(eventp); iterate(eventp);
} }
+22 -28
View File
@@ -154,13 +154,6 @@ void V3AssertCommon::lowerSequenceEvents(AstNetlist* nodep) {
V3Global::dumpCheckGlobalTree("assertseqevent", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("assertseqevent", 0, dumpTreeEitherLevel() >= 3);
} }
string V3AssertCommon::assertCtlGetCall(const char* query, VAssertType type,
VAssertDirectiveType directiveType) {
// Template arguments let the runtime build the type/directive mask at C++ compile time
return "vlSymsp->_vm_contextp__->assertCtlGet<"s + std::to_string(type) + ", "s
+ std::to_string(directiveType) + ">(VerilatedAssertCtlQuery::"s + query + ")"s;
}
//###################################################################### //######################################################################
// AssertDeFutureVisitor // AssertDeFutureVisitor
// If any AstFuture, then move all non-future varrefs to be one cycle behind, // If any AstFuture, then move all non-future varrefs to be one cycle behind,
@@ -331,7 +324,6 @@ class AssertVisitor final : public VNVisitor {
VDouble0 m_statLiftedCaseExprs; // Count of purified case expressions VDouble0 m_statLiftedCaseExprs; // Count of purified case expressions
AstNodeFTask* m_ftaskp = nullptr; // Current function/task AstNodeFTask* m_ftaskp = nullptr; // Current function/task
V3UniqueNames m_caseTempNames{"__VCase"}; V3UniqueNames m_caseTempNames{"__VCase"};
V3UniqueNames m_matchCountNames{"__VnfaRemainingMatchCount"}; // Match replay counter names
// Maps from (expression, senTree) to the AstAlways that computes its delayed values. // Maps from (expression, senTree) to the AstAlways that computes its delayed values.
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>> std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
m_modExpr2Sen2DelayedAlwaysp; m_modExpr2Sen2DelayedAlwaysp;
@@ -342,6 +334,11 @@ class AssertVisitor final : public VNVisitor {
std::unordered_map<const AstAlways*, AstVar*> m_delayedAlways2TickTimep; std::unordered_map<const AstAlways*, AstVar*> m_delayedAlways2TickTimep;
// METHODS // METHODS
static string assertCtlGetCall(const char* query, VAssertType type,
VAssertDirectiveType directiveType) {
return "vlSymsp->_vm_contextp__->assertCtlGet(VerilatedAssertCtlQuery::"s + query + ", "s
+ std::to_string(type) + ", "s + std::to_string(directiveType) + ")"s;
}
static const char* assertPassOnQuery(bool vacuous) { static const char* assertPassOnQuery(bool vacuous) {
static constexpr const char* queries[2] static constexpr const char* queries[2]
= {"ASSERT_CTL_PASS_ON_NONVACUOUS", "ASSERT_CTL_PASS_ON_VACUOUS"}; = {"ASSERT_CTL_PASS_ON_NONVACUOUS", "ASSERT_CTL_PASS_ON_VACUOUS"};
@@ -365,9 +362,8 @@ class AssertVisitor final : public VNVisitor {
case VAssertDirectiveType::COVER: case VAssertDirectiveType::COVER:
case VAssertDirectiveType::ASSUME: { case VAssertDirectiveType::ASSUME: {
if (v3Global.opt.assertOn()) { if (v3Global.opt.assertOn()) {
return new AstCExpr{ return new AstCExpr{fl, AstCExpr::Pure{},
fl, AstCExpr::Pure{}, assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
} }
return new AstConst{fl, AstConst::BitFalse{}}; return new AstConst{fl, AstConst::BitFalse{}};
} }
@@ -394,17 +390,15 @@ class AssertVisitor final : public VNVisitor {
VAssertDirectiveType directiveType, bool vacuous) { VAssertDirectiveType directiveType, bool vacuous) {
if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}}; if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}};
if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}}; if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}};
return new AstCExpr{ return new AstCExpr{fl, AstCExpr::Pure{},
fl, AstCExpr::Pure{}, assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1};
V3AssertCommon::assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1};
} }
static AstNodeExpr* assertFailOnCond(FileLine* fl, VAssertType type, static AstNodeExpr* assertFailOnCond(FileLine* fl, VAssertType type,
VAssertDirectiveType directiveType) { VAssertDirectiveType directiveType) {
if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}}; if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}};
if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}}; if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}};
return new AstCExpr{ return new AstCExpr{fl, AstCExpr::Pure{},
fl, AstCExpr::Pure{}, assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 1};
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 1};
} }
string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message, string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message,
VDisplayType severity) { VDisplayType severity) {
@@ -439,20 +433,20 @@ class AssertVisitor final : public VNVisitor {
if (!m_monitorNumVarp) { if (!m_monitorNumVarp) {
m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum", m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
nodep->findUInt64DType()}; nodep->findUInt64DType()};
v3Global.rootp()->dollarUnitPkgp()->addStmtsp(m_monitorNumVarp); v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp);
} }
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access}; AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access};
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgp()); varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
return varrefp; return varrefp;
} }
AstVarRef* newMonitorOffVarRefp(const AstNode* nodep, VAccess access) { AstVarRef* newMonitorOffVarRefp(const AstNode* nodep, VAccess access) {
if (!m_monitorOffVarp) { if (!m_monitorOffVarp) {
m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff", m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
nodep->findBitDType()}; nodep->findBitDType()};
v3Global.rootp()->dollarUnitPkgp()->addStmtsp(m_monitorOffVarp); v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp);
} }
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access}; AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access};
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgp()); varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
return varrefp; return varrefp;
} }
static AstIf* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType, static AstIf* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
@@ -755,11 +749,11 @@ class AssertVisitor final : public VNVisitor {
// reaches zero. // reaches zero.
matchCountp->unlinkFrBack(); matchCountp->unlinkFrBack();
AstVar* const remainingp = new AstVar{ AstVar* const remainingp = new AstVar{
flp, VVarType::MODULETEMP, m_matchCountNames.get(""), matchCountp->dtypep()}; flp, VVarType::BLOCKTEMP, "__VnfaRemainingMatchCount", matchCountp->dtypep()};
remainingp->lifetime(VLifetime::STATIC_EXPLICIT); remainingp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
m_modp->addStmtsp(remainingp); AstBegin* const replayp = new AstBegin{flp, "", remainingp, true};
AstNode* const replaysp replayp->addStmtsp(
= new AstAssign{flp, new AstVarRef{flp, remainingp, VAccess::WRITE}, matchCountp}; new AstAssign{flp, new AstVarRef{flp, remainingp, VAccess::WRITE}, matchCountp});
AstLoop* const loopp = new AstLoop{flp}; AstLoop* const loopp = new AstLoop{flp};
loopp->addStmtsp( loopp->addStmtsp(
new AstLoopTest{flp, loopp, new AstVarRef{flp, remainingp, VAccess::READ}}); new AstLoopTest{flp, loopp, new AstVarRef{flp, remainingp, VAccess::READ}});
@@ -769,8 +763,8 @@ class AssertVisitor final : public VNVisitor {
new AstSub{flp, new AstVarRef{flp, remainingp, VAccess::READ}, new AstSub{flp, new AstVarRef{flp, remainingp, VAccess::READ},
new AstConst{flp, AstConst::WidthedValue{}, new AstConst{flp, AstConst::WidthedValue{},
remainingp->dtypep()->width(), 1}}}); remainingp->dtypep()->width(), 1}}});
replaysp->addNext(loopp); replayp->addStmtsp(loopp);
passsp = replaysp; passsp = replayp;
} }
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp); AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp}; if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
-2
View File
@@ -28,8 +28,6 @@ class V3AssertCommon final {
public: public:
static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED; static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED;
static void lowerSequenceEvents(AstNetlist* nodep) VL_MT_DISABLED; static void lowerSequenceEvents(AstNetlist* nodep) VL_MT_DISABLED;
static string assertCtlGetCall(const char* query, VAssertType type,
VAssertDirectiveType directiveType) VL_MT_DISABLED;
}; };
class V3Assert final { class V3Assert final {
+228 -438
View File
File diff suppressed because it is too large Load Diff
+60 -262
View File
@@ -32,36 +32,6 @@
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Clocking blocks of clocking items
class AssertPreClockingVisitor final : public VNVisitorConst {
// Captures the clocking block of each clocking item before AssertPreVisitor, which needs it
// at synchronous drives, often visited before the clocking block, e.g. in a parent module
// NODE STATE
// AstClockingItem::user3p() // AstClocking*. Clocking block of the item, user3 claimed by
// // AssertPreVisitor
// VISITORS
void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); }
void visit(AstNodeModule* nodep) override { iterateAndNextConstNull(nodep->stmtsp()); }
void visit(AstGenBlock* nodep) override { iterateAndNextConstNull(nodep->itemsp()); }
void visit(AstClocking* nodep) override {
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
if (AstClockingItem* const citemp = VN_CAST(itemp, ClockingItem)) {
citemp->user3p(nodep);
}
}
}
// Clocking blocks are only in modules and their generate blocks
void visit(AstNode*) override {}
public:
// CONSTRUCTORS
explicit AssertPreClockingVisitor(AstNetlist* nodep) { iterateConst(nodep); }
~AssertPreClockingVisitor() override = default;
};
//###################################################################### //######################################################################
// Assert class functions // Assert class functions
@@ -70,21 +40,10 @@ class AssertPreVisitor final : public VNVisitor {
// Eventually inlines calls to sequences, properties, etc. // Eventually inlines calls to sequences, properties, etc.
// We're not parsing the tree, or anything more complicated. // We're not parsing the tree, or anything more complicated.
private: private:
// TYPES
struct SynchDrive final {
AstClockingItem* itemp; // Clocking item of the driven clockvar
AstNodeExpr* refp; // Reference to the driven clockvar
};
// NODE STATE // NODE STATE
// AstClockingItem::user1p() // AstVar*. varp() of ClockingItem after unlink // AstClockingItem::user1p() // AstVar*. varp() of ClockingItem after unlink
// AstClockingItem::user2p() // AstVar*. Flag set by drives of output clockvar
// AstClockingItem::user3p() // AstClocking*. Clocking block of the item, captured
// // by AssertPreClockingVisitor
// AstPExpr::user1() // bool. Created from AstUntil // AstPExpr::user1() // bool. Created from AstUntil
const VNUser1InUse m_inuser1; const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
const VNUser3InUse m_inuser3;
// STATE // STATE
// Current context: // Current context:
AstNetlist* const m_netlistp = nullptr; // Current netlist AstNetlist* const m_netlistp = nullptr; // Current netlist
@@ -109,123 +68,17 @@ private:
V3UniqueNames m_gotoRepNames{"__VgotoRep"}; // Goto repetition counter name generator V3UniqueNames m_gotoRepNames{"__VgotoRep"}; // Goto repetition counter name generator
V3UniqueNames m_nonConsRepNames{"__VnonConsRep"}; // Nonconsecutive rep name generator V3UniqueNames m_nonConsRepNames{"__VnonConsRep"}; // Nonconsecutive rep name generator
V3UniqueNames m_disableCntNames{"__VdisableCnt"}; // Disable condition counter name generator V3UniqueNames m_disableCntNames{"__VdisableCnt"}; // Disable condition counter name generator
V3UniqueNames m_blockNames{"__VassertBlock"}; // Names of blocks with temporaries
V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable name generator V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable name generator
V3UniqueNames m_activeNames{"__VassertsActive"}; // Active asserts map name generator V3UniqueNames m_activeNames{"__VassertsActive"}; // Active asserts map name generator
V3UniqueNames m_drivenNames{"__VclockingDriven"}; // Clockvar drive flag name generator
bool m_inAssign = false; // True if in an AssignNode bool m_inAssign = false; // True if in an AssignNode
bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS
bool m_inSynchDrive = false; // True if in synchronous drive bool m_inSynchDrive = false; // True if in synchronous drive
bool m_inConcatAssign = false; // True if in assignment to a concatenation
std::vector<SynchDrive> m_drives; // Clockvars written by the synchronous drive
bool m_hasCycleDelay = false; // True if node has cycle delay beneath bool m_hasCycleDelay = false; // True if node has cycle delay beneath
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
std::vector<AstSequence*> m_seqsToCleanup; // Sequences to clean up after traversal std::vector<AstSequence*> m_seqsToCleanup; // Sequences to clean up after traversal
// METHODS // METHODS
// Flag set by each drive of an output clockvar, added to the module with its clocking item
AstVar* getCreateDrivenVarp(AstClockingItem* itemp) {
if (!itemp->user2p()) {
AstVar* const varp = new AstVar{itemp->fileline(), VVarType::MODULETEMP,
m_drivenNames.get(itemp), itemp->findBitDType()};
varp->lifetime(VLifetime::STATIC_EXPLICIT);
itemp->user2p(varp);
}
return VN_AS(itemp->user2p(), Var);
}
// Reference to a variable in the scope of a driven clockvar, made like the clockvar's
static AstNodeExpr* newDriveRefp(FileLine* flp, const SynchDrive& drive, AstVar* varp,
const VAccess& access) {
if (const AstVarXRef* const xrefp = VN_CAST(drive.refp, VarXRef)) {
return new AstVarXRef{flp, varp, xrefp->dotted(), access};
} else if (const AstMemberSel* const selp = VN_CAST(drive.refp, MemberSel)) {
// The interface expression is evaluated again, so it must not have side effects;
// cloneTreePure warns if it has any
AstMemberSel* const newSelp
= new AstMemberSel{flp, selp->fromp()->cloneTreePure(false), varp};
newSelp->access(access);
return newSelp;
}
return new AstVarRef{flp, varp, access};
}
// Assignment setting the drive flag of a driven clockvar
AstAssign* newDrivenSetp(FileLine* flp, const SynchDrive& drive) {
AstVar* const varp = getCreateDrivenVarp(drive.itemp);
AstAssign* const setp = new AstAssign{flp, newDriveRefp(flp, drive, varp, VAccess::WRITE),
new AstConst{flp, AstConst::BitTrue{}}};
setp->user1(true);
return setp;
}
// Clocking block of a clocking item
static AstClocking* clockingOf(const AstClockingItem* itemp) {
UASSERT_OBJ(itemp->user3p(), itemp, "Clocking item not captured in a clocking block");
return VN_AS(itemp->user3p(), Clocking);
}
// Clocking event of a driven clockvar, referenced from the drive like the clockvar, or
// nullptr if unsupported, which is reported on the drive's cycle delay
AstSenItem* newDriveSensesp(AstDelay* delayp, const SynchDrive& drive) {
AstSenItem* const origp = clockingOf(drive.itemp)->sensesp();
if (VN_IS(drive.refp, VarRef)) return origp->cloneTree(false);
if (origp->exists([](const AstVarXRef*) { return true; })) {
// The reference is relative to the clocking block
delayp->v3warn(E_UNSUPPORTED, "Unsupported: cycle delay in synchronous drive to"
" clockvar in another scope, whose clocking event"
" has a hierarchical reference");
return nullptr;
}
const AstMemberSel* const selp = VN_CAST(drive.refp, MemberSel);
// Processes outside classes evaluate their events also when not waiting on them,
// possibly before the interface reference is set, so the event is guarded against a
// null reference, which needs an integral value; scheduling does not guard it
const bool guardNull = selp && !VN_IS(m_modp, Class);
if (guardNull && !origp->sensp()->dtypep()->skipRefp()->isIntegralOrPacked()) {
delayp->v3warn(E_UNSUPPORTED, "Unsupported: cycle delay in synchronous drive through"
" an interface reference outside a class, to clocking"
" block with non-integral clocking event");
return nullptr;
}
// Interface declaring the clocking block, if driven through an interface reference
AstIface* const ifacep
= selp ? VN_AS(selp->fromp()->dtypep()->skipRefp(), IfaceRefDType)->ifaceViaCellp()
: nullptr;
AstSenItem* const sensesp = origp->cloneTree(false);
std::vector<AstVarRef*> refps;
sensesp->foreach([&](AstVarRef* refp) {
if (!refp->classOrPackagep()) refps.push_back(refp);
});
for (AstVarRef* const refp : refps) {
AstVar* const varp = refp->varp();
if (ifacep) {
// Mark the variable as read through an interface reference, as V3Width does for
// a member select. The mark is the interface declaring the variable, whose
// instances all share it, so drives through references to any instances agree.
UASSERT_OBJ(!varp->sensIfacep() || varp->sensIfacep() == ifacep, refp,
"Variable read through references to different interfaces");
varp->sensIfacep(ifacep);
}
refp->replaceWith(newDriveRefp(refp->fileline(), drive, varp, refp->access()));
VL_DO_DANGLING(pushDeletep(refp), refp);
}
if (guardNull) guardNullIface(sensesp, selp->fromp());
return sensesp;
}
// Make an integral clocking event through an interface reference false while the reference
// is null
static void guardNullIface(AstSenItem* sensesp, AstNodeExpr* fromp) {
FileLine* const flp = sensesp->fileline();
AstNodeExpr* const sensp = sensesp->sensp()->unlinkFrBack();
sensesp->sensp(new AstCond{flp, newNotNullp(flp, fromp), sensp,
new AstConst{flp, AstConst::DTyped{}, sensp->dtypep()}});
if (AstNodeExpr* const condp = sensesp->condp()) {
condp->unlinkFrBack();
sensesp->condp(new AstLogAnd{flp, newNotNullp(flp, fromp), condp});
}
}
static AstNodeExpr* newNotNullp(FileLine* flp, AstNodeExpr* fromp) {
return new AstNeq{flp, fromp->cloneTreePure(false), new AstConst{flp, AstConst::Null{}}};
}
static void checkSamplingFuncDType(AstNodeExpr* nodep, const AstNode* exprp) { static void checkSamplingFuncDType(AstNodeExpr* nodep, const AstNode* exprp) {
const AstNodeDType* const dtypep = exprp->dtypep()->skipRefp(); const AstNodeDType* const dtypep = exprp->dtypep()->skipRefp();
if (!dtypep->isIntegralOrPacked()) { if (!dtypep->isIntegralOrPacked()) {
@@ -419,21 +272,13 @@ private:
// It has to be converted to a list of ModportClockingVarRefs, // It has to be converted to a list of ModportClockingVarRefs,
// because clocking blocks are removed in this pass // because clocking blocks are removed in this pass
for (AstNode* itemp = nodep->clockingp()->itemsp(); itemp; itemp = itemp->nextp()) { for (AstNode* itemp = nodep->clockingp()->itemsp(); itemp; itemp = itemp->nextp()) {
if (AstClockingItem* const citemp = VN_CAST(itemp, ClockingItem)) { if (const AstClockingItem* citemp = VN_CAST(itemp, ClockingItem)) {
if (AstVar* const varp if (AstVar* const varp
= citemp->varp() ? citemp->varp() : VN_AS(citemp->user1p(), Var)) { = citemp->varp() ? citemp->varp() : VN_AS(citemp->user1p(), Var)) {
AstModportVarRef* const modVarp = new AstModportVarRef{ AstModportVarRef* const modVarp = new AstModportVarRef{
nodep->fileline(), varp->name(), citemp->direction()}; nodep->fileline(), varp->name(), citemp->direction()};
modVarp->varp(varp); modVarp->varp(varp);
nodep->addNextHere(modVarp); nodep->addNextHere(modVarp);
if (citemp->direction().isOutput()) {
// Drives through the modport set the drive flag
AstVar* const drivenVarp = getCreateDrivenVarp(citemp);
AstModportVarRef* const drivenRefp = new AstModportVarRef{
nodep->fileline(), drivenVarp->name(), VDirection::OUTPUT};
drivenRefp->varp(drivenVarp);
nodep->addNextHere(drivenRefp);
}
} }
} }
} }
@@ -446,8 +291,6 @@ private:
// Unused item // Unused item
return; return;
} }
// Flag set by drives of an output clockvar, possibly visited before this item
if (nodep->direction().isOutput()) m_modp->addStmtsp(getCreateDrivenVarp(nodep));
FileLine* const flp = nodep->fileline(); FileLine* const flp = nodep->fileline();
V3Const::constifyEdit(nodep->skewp()); V3Const::constifyEdit(nodep->skewp());
if (!VN_IS(nodep->skewp(), Const)) { if (!VN_IS(nodep->skewp(), Const)) {
@@ -474,26 +317,32 @@ private:
AstInitialStatic* const initClockvarp = new AstInitialStatic{ AstInitialStatic* const initClockvarp = new AstInitialStatic{
flp, new AstAssign{flp, skewedWriteRefp, exprp->cloneTreePure(false)}}; flp, new AstAssign{flp, skewedWriteRefp, exprp->cloneTreePure(false)}};
m_modp->addStmtsp(initClockvarp); m_modp->addStmtsp(initClockvarp);
// Each drive sets a flag, so the signal is also assigned when driven with the same // A var to keep the previous value of the clockvar
// value again, e.g. after another assignment to the signal (IEEE 1800-2023 14.16.2) AstVar* const prevVarp = new AstVar{
AstVar* const drivenVarp = getCreateDrivenVarp(nodep); flp, VVarType::MODULETEMP, "__Vclocking_prev__" + varp->name(), exprp->dtypep()};
m_modp->addStmtsp(new AstInitialStatic{ prevVarp->lifetime(VLifetime::STATIC_EXPLICIT);
flp, new AstAssign{flp, new AstVarRef{flp, drivenVarp, VAccess::WRITE}, AstInitialStatic* const initPrevClockvarp = new AstInitialStatic{
new AstConst{flp, AstConst::BitFalse{}}}}); flp, new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
// Assign the clockvar to the actual var if it was driven skewedReadRefp->cloneTreePure(false)}};
m_modp->addStmtsp(prevVarp);
m_modp->addStmtsp(initPrevClockvarp);
// Assign the clockvar to the actual var; only do it if the clockvar's value has
// changed
AstAssign* const assignp AstAssign* const assignp
= new AstAssign{flp, exprp->cloneTreePure(false), skewedReadRefp}; = new AstAssign{flp, exprp->cloneTreePure(false), skewedReadRefp};
AstIf* const ifp AstIf* const ifp
= new AstIf{flp, new AstVarRef{flp, drivenVarp, VAccess::READ}, = new AstIf{flp,
new AstAssign{flp, new AstVarRef{flp, drivenVarp, VAccess::WRITE}, new AstNeq{flp, new AstVarRef{flp, prevVarp, VAccess::READ},
new AstConst{flp, AstConst::BitFalse{}}}}; skewedReadRefp->cloneTreePure(false)},
ifp->addThensp(assignp); assignp};
ifp->addThensp(new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
skewedReadRefp->cloneTree(false)});
if (skewp->isZero()) { if (skewp->isZero()) {
// Drive the var in Re-NBA (IEEE 1800-2023 14.16) // Drive the var in Re-NBA (IEEE 1800-2023 14.16)
AstSenTree* senTreep AstSenTree* senTreep
= new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}; = new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)};
senTreep->addSensesp(new AstSenItem{ senTreep->addSensesp(
flp, VEdgeType::ET_CHANGED, new AstVarRef{flp, drivenVarp, VAccess::READ}}); new AstSenItem{flp, VEdgeType::ET_CHANGED, skewedReadRefp->cloneTree(false)});
AstCMethodHard* const trigp = new AstCMethodHard{ AstCMethodHard* const trigp = new AstCMethodHard{
nodep->fileline(), nodep->fileline(),
new AstVarRef{flp, m_clockingp->ensureEventp(), VAccess::READ}, new AstVarRef{flp, m_clockingp->ensureEventp(), VAccess::READ},
@@ -565,7 +414,6 @@ private:
flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, VCMethod::DYN_POP, flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, VCMethod::DYN_POP,
new AstTime{nodep->fileline(), m_modp->timeunit()}}; new AstTime{nodep->fileline(), m_modp->timeunit()}};
popp->addPinsp(skewp->unlinkFrBack()); popp->addPinsp(skewp->unlinkFrBack());
refp->access(VAccess::READWRITE); // Only conditionally assigned
popp->addPinsp(refp); popp->addPinsp(refp);
popp->dtypeSetVoid(); popp->dtypeSetVoid();
m_clockingp->addNextHere( m_clockingp->addNextHere(
@@ -598,12 +446,12 @@ private:
FileLine* const flp = nodep->fileline(); FileLine* const flp = nodep->fileline();
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack()); AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
const AstConst* const constp = VN_CAST(valuep, Const); const AstConst* const constp = VN_CAST(valuep, Const);
if (!constp && !m_inSynchDrive) { if (!constp) {
// V3AssertNfa handles non-const delays before this pass and // V3AssertNfa handles non-const delays before this pass and
// replaces the property; this branch should never be reached. // replaces the property; this branch should never be reached.
nodep->v3fatalSrc("Non-constant cycle delay in assertion: " nodep->v3fatalSrc("Non-constant cycle delay in assertion: "
"should have been caught by V3AssertNfa"); "should have been caught by V3AssertNfa");
} else if (constp && constp->isZero()) { } else if (constp->isZero()) {
VL_DO_DANGLING(pushDeletep(valuep), valuep); VL_DO_DANGLING(pushDeletep(valuep), valuep);
if (m_inSynchDrive) { if (m_inSynchDrive) {
// ##0 has no effect in synchronous drives (IEEE 1800-2023 14.11) // ##0 has no effect in synchronous drives (IEEE 1800-2023 14.11)
@@ -640,36 +488,20 @@ private:
return; return;
} }
AstSenItem* sensesp = nullptr; AstSenItem* sensesp = nullptr;
if (!m_drives.empty()) { if (!m_defaultClockingp) {
if (m_inConcatAssign) {
// Also the clockvars may be of different clocking blocks
nodep->v3error("Cycle delays cannot be used in synchronous drives to a"
" concatenation (IEEE 1800-2023 14.16)");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
VL_DO_DANGLING(pushDeletep(valuep), valuep);
return;
}
// Count the cycles of the driven clockvar's clocking block (IEEE 1800-2023 14.16)
sensesp = newDriveSensesp(nodep, m_drives.front());
if (!sensesp) {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
VL_DO_DANGLING(pushDeletep(valuep), valuep);
return;
}
} else if (!m_defaultClockingp) {
if (!m_pexprp) { if (!m_pexprp) {
nodep->v3error("Usage of cycle delays requires default clocking" nodep->v3error("Usage of cycle delays requires default clocking"
" (IEEE 1800-2023 14.11)"); " (IEEE 1800-2023 14.11)");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
VL_DO_DANGLING(pushDeletep(valuep), valuep); VL_DO_DANGLING(valuep->deleteTree(), valuep);
return; return;
} }
sensesp = m_senip->cloneTree(false); sensesp = m_senip;
} else { } else {
sensesp = m_defaultClockingp->sensesp()->cloneTree(false); sensesp = m_defaultClockingp->sensesp();
} }
AstEventControl* const controlp AstEventControl* const controlp = new AstEventControl{
= new AstEventControl{nodep->fileline(), new AstSenTree{flp, sensesp}, nullptr}; nodep->fileline(), new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr};
const std::string delayName = m_cycleDlyNames.get(nodep); const std::string delayName = m_cycleDlyNames.get(nodep);
AstNodeExpr* throughoutp AstNodeExpr* throughoutp
= nodep->throughoutp() ? nodep->throughoutp()->unlinkFrBack() : nullptr; = nodep->throughoutp() ? nodep->throughoutp()->unlinkFrBack() : nullptr;
@@ -678,15 +510,6 @@ private:
nodep->findBasicDType(VBasicDTypeKwd::UINT32)}; nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true}; AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true};
// A non-constant delay of a synchronous drive is counted at runtime, waiting for none if
// zero, like a constant '##0' in a synchronous drive. The count is integral of 64 bits,
// as V3Width makes delays, or real, which is rounded (IEEE 1800-2023 6.12.1)
if (valuep->isDouble()) {
valuep = new AstRToIRoundS{flp, valuep};
valuep->dtypeFrom(cntVarp);
} else {
valuep = new AstSel{flp, valuep, 0, cntVarp->width()};
}
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep}); beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
// Throughout: create flag tracking whether condition held every tick // Throughout: create flag tracking whether condition held every tick
@@ -738,7 +561,8 @@ private:
} }
if (m_disableSeqIfp && remainp) { if (m_disableSeqIfp && remainp) {
AstIf* const disableSeqIfp = m_disableSeqIfp->cloneTree(false); AstIf* const disableSeqIfp = m_disableSeqIfp->cloneTree(false);
disableSeqIfp->addThensp(remainp); // Keep continuation statements in a proper statement-list container.
disableSeqIfp->addThensp(new AstBegin{flp, "", remainp, true});
remainp = disableSeqIfp; remainp = disableSeqIfp;
} }
if (remainp) { if (remainp) {
@@ -795,10 +619,7 @@ private:
nodep->v3error("Only non-blocking assignments can write " nodep->v3error("Only non-blocking assignments can write "
"to clockvars (IEEE 1800-2023 14.16)"); "to clockvars (IEEE 1800-2023 14.16)");
} }
if (m_inAssign) { if (m_inAssign) m_inSynchDrive = true;
m_inSynchDrive = true;
m_drives.push_back({itemp, nodep});
}
} else if (itemp->direction() == VDirection::INPUT) { } else if (itemp->direction() == VDirection::INPUT) {
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)"); nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
} }
@@ -819,10 +640,7 @@ private:
nodep->v3error("Only non-blocking assignments can write " nodep->v3error("Only non-blocking assignments can write "
"to clockvars (IEEE 1800-2023 14.16)"); "to clockvars (IEEE 1800-2023 14.16)");
} }
if (m_inAssign) { if (m_inAssign) m_inSynchDrive = true;
m_inSynchDrive = true;
m_drives.push_back({itemp, nodep});
}
} else if (itemp->direction() == VDirection::INPUT) { } else if (itemp->direction() == VDirection::INPUT) {
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)"); nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
} }
@@ -833,44 +651,22 @@ private:
if (nodep->user1()) return; if (nodep->user1()) return;
VL_RESTORER(m_inAssign); VL_RESTORER(m_inAssign);
VL_RESTORER(m_inSynchDrive); VL_RESTORER(m_inSynchDrive);
VL_RESTORER(m_inConcatAssign);
VL_RESTORER_CLEAR(m_drives);
m_inAssign = true; m_inAssign = true;
m_inSynchDrive = false; m_inSynchDrive = false;
{
// The braces of a concatenation are a replication, by one
const AstNodeExpr* lhsp = nodep->lhsp();
if (const AstReplicate* const repp = VN_CAST(lhsp, Replicate)) lhsp = repp->srcp();
m_inConcatAssign = VN_IS(lhsp, Concat);
}
{ {
VL_RESTORER(m_inAssignDlyLhs); VL_RESTORER(m_inAssignDlyLhs);
m_inAssignDlyLhs = VN_IS(nodep, AssignDly); m_inAssignDlyLhs = VN_IS(nodep, AssignDly);
iterate(nodep->lhsp()); iterate(nodep->lhsp());
} }
iterate(nodep->rhsp()); iterate(nodep->rhsp());
if (nodep->timingControlp()) iterate(nodep->timingControlp()); if (nodep->timingControlp()) {
// A drive without a cycle delay, also after removing '##0', updates the clockvar now iterate(nodep->timingControlp());
if (m_inSynchDrive && !nodep->timingControlp()) { } else if (m_inSynchDrive) {
AstAssign* const assignp = new AstAssign{ AstAssign* const assignp = new AstAssign{
nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()}; nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()};
assignp->user1(true); assignp->user1(true);
nodep->replaceWith(assignp); nodep->replaceWith(assignp);
VL_DO_DANGLING(nodep->deleteTree(), nodep); VL_DO_DANGLING(nodep->deleteTree(), nodep);
nodep = assignp;
}
// Note the drives when they take effect, after the cycle delay if any. Each clockvar
// of the LHS is driven, e.g. in a concatenation which other simulators accept, although
// IEEE 1800-2023 14.16 does not allow it
AstNode* setsp = nullptr;
for (const SynchDrive& drive : m_drives) {
setsp = AstNode::addNext(setsp, newDrivenSetp(nodep->fileline(), drive));
}
if (!setsp) return;
if (AstBegin* const beginp = VN_CAST(nodep->timingControlp(), Begin)) {
beginp->addStmtsp(setsp);
} else {
nodep->addNextHere(setsp);
} }
} }
void visit(AstAlways* nodep) override { void visit(AstAlways* nodep) override {
@@ -1081,7 +877,7 @@ private:
= getProcessAssocArrayDelete(new AstVarRef{flp, activep, VAccess::WRITE}); = getProcessAssocArrayDelete(new AstVarRef{flp, activep, VAccess::WRITE});
// Main assertion block // Main assertion block
AstBegin* const bodyp = new AstBegin{flp, m_blockNames.get(""), nullptr, true}; AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true};
bodyp->addStmtsp(incrementp); bodyp->addStmtsp(incrementp);
bodyp->addStmtsp(loopp); bodyp->addStmtsp(loopp);
bodyp->addStmtsp(clausep); bodyp->addStmtsp(clausep);
@@ -1110,7 +906,7 @@ private:
new AstConst{flp, 1}}}); new AstConst{flp, 1}}});
// Final assertion block // Final assertion block
AstBegin* const finalp = new AstBegin{flp, m_blockNames.get(""), nullptr, true}; AstBegin* const finalp = new AstBegin{flp, "", nullptr, true};
finalp->addStmtsp(activeCountp); finalp->addStmtsp(activeCountp);
finalp->addStmtsp(initActiveCountp); finalp->addStmtsp(initActiveCountp);
finalp->addStmtsp(finalLoopp); finalp->addStmtsp(finalLoopp);
@@ -1276,13 +1072,15 @@ private:
windowp->addStmtsp(new AstLoopTest{ windowp->addStmtsp(new AstLoopTest{
flp, windowp, new AstNot{flp, new AstVarRef{flp, doneVarp, VAccess::READ}}}); flp, windowp, new AstNot{flp, new AstVarRef{flp, doneVarp, VAccess::READ}}});
// if (expr) { fail; done = 1; } -- window closed, expr true again // if (expr) { fail; done = 1; } -- window closed, expr true again
AstNode* const failsp = new AstPExprClause{flp, false}; AstBegin* const failBlockp = new AstBegin{flp, "", nullptr, true};
failsp->addNext(setDone()); failBlockp->addStmtsp(new AstPExprClause{flp, false});
windowp->addStmtsp(new AstIf{flp, exprp->cloneTreePure(false), failsp}); failBlockp->addStmtsp(setDone());
windowp->addStmtsp(new AstIf{flp, exprp->cloneTreePure(false), failBlockp});
// if (rhs) { pass; done = 1; } -- consequent true at this !expr endpoint // if (rhs) { pass; done = 1; } -- consequent true at this !expr endpoint
AstNode* const passsp = new AstPExprClause{flp, true}; AstBegin* const passBlockp = new AstBegin{flp, "", nullptr, true};
passsp->addNext(setDone()); passBlockp->addStmtsp(new AstPExprClause{flp, true});
windowp->addStmtsp(new AstIf{flp, rhsp, passsp}); passBlockp->addStmtsp(setDone());
windowp->addStmtsp(new AstIf{flp, rhsp, passBlockp});
// @(clk) -- advance to next cycle in window // @(clk) -- advance to next cycle in window
windowp->addStmtsp( windowp->addStmtsp(
new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr}); new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
@@ -1496,7 +1294,7 @@ private:
// */ } // */ }
AstBegin* const bodyp = pexprp->bodyp(); AstBegin* const bodyp = pexprp->bodyp();
AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext(); AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext();
AstIf* const guardp = new AstIf{flp, condp, origStmtsp}; AstIf* const guardp = new AstIf{flp, condp, new AstBegin{flp, "", origStmtsp, true}};
bodyp->addStmtsp(guardp); bodyp->addStmtsp(guardp);
nodep->replaceWith(pexprp); nodep->replaceWith(pexprp);
// Don't iterate pexprp here -- it was already iterated when created // Don't iterate pexprp here -- it was already iterated when created
@@ -1570,9 +1368,10 @@ private:
loopp->addStmtsp(new AstLoopTest{ loopp->addStmtsp(new AstLoopTest{
flp, loopp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}}); flp, loopp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}});
{ {
AstNode* const passp = new AstPExprClause{flp}; AstBegin* const passp = new AstBegin{flp, "", nullptr, true};
passp->addNext(decrementVar); passp->addStmtsp(new AstPExprClause{flp});
passp->addNext(setDone()); passp->addStmtsp(decrementVar);
passp->addStmtsp(setDone());
AstNodeExpr* passCondp = rhsp; AstNodeExpr* passCondp = rhsp;
if (nodep->isOverlapping()) { if (nodep->isOverlapping()) {
passCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), passCondp}; passCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), passCondp};
@@ -1580,9 +1379,10 @@ private:
loopp->addStmtsp(new AstIf{flp, passCondp, passp}); loopp->addStmtsp(new AstIf{flp, passCondp, passp});
} }
{ {
AstNode* const failp = new AstPExprClause{flp, false}; AstBegin* const failp = new AstBegin{flp, "", nullptr, true};
failp->addNext(decrementVar->cloneTree(false)); failp->addStmtsp(new AstPExprClause{flp, false});
failp->addNext(setDone()); failp->addStmtsp(decrementVar->cloneTree(false));
failp->addStmtsp(setDone());
loopp->addStmtsp(new AstIf{ loopp->addStmtsp(new AstIf{
flp, flp,
new AstLogAnd{flp, new AstLogAnd{flp,
@@ -1596,7 +1396,7 @@ private:
flp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}, delayp}); flp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}, delayp});
// Main assertion block // Main assertion block
AstBegin* const bodyp = new AstBegin{flp, m_blockNames.get(""), nullptr, true}; AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true};
bodyp->addStmtsp(donep); bodyp->addStmtsp(donep);
bodyp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, donep, VAccess::WRITE}, bodyp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, donep, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}}); new AstConst{flp, AstConst::BitFalse{}}});
@@ -1628,7 +1428,7 @@ private:
new AstConst{flp, 1}}}); new AstConst{flp, 1}}});
// Final assertion block // Final assertion block
AstBegin* const finalp = new AstBegin{flp, m_blockNames.get(""), nullptr, true}; AstBegin* const finalp = new AstBegin{flp, "", nullptr, true};
finalp->addStmtsp(activeCountp); finalp->addStmtsp(activeCountp);
finalp->addStmtsp(initActiveCountp); finalp->addStmtsp(initActiveCountp);
finalp->addStmtsp(finalLoopp); finalp->addStmtsp(finalLoopp);
@@ -1659,7 +1459,7 @@ private:
AstNodeExpr* const passCondp AstNodeExpr* const passCondp
= nodep->isOverlapping() ? new AstLogAnd{flp, lhsp->cloneTreePure(false), rhsCopyp} = nodep->isOverlapping() ? new AstLogAnd{flp, lhsp->cloneTreePure(false), rhsCopyp}
: rhsCopyp; : rhsCopyp;
AstBegin* const beginp = new AstBegin{flp, m_blockNames.get(""), loopp, true}; AstBegin* const beginp = new AstBegin{flp, "", loopp, true};
beginp->addStmtsp( beginp->addStmtsp(
new AstIf{flp, passCondp, new AstPExprClause{flp}, new AstPExprClause{flp, false}}); new AstIf{flp, passCondp, new AstPExprClause{flp}, new AstPExprClause{flp, false}});
@@ -1748,7 +1548,7 @@ private:
"Unsupported: $sampled inside disabled condition of a sequence"); "Unsupported: $sampled inside disabled condition of a sequence");
m_disablep = new AstConst{m_disablep->fileline(), AstConst::BitFalse{}}; m_disablep = new AstConst{m_disablep->fileline(), AstConst::BitFalse{}};
// always a copy is used, so remove it now // always a copy is used, so remove it now
pushDeletep(m_disablep); // m_disablep used later pushDeletep(m_disablep);
} }
FileLine* const flp = nodep->fileline(); FileLine* const flp = nodep->fileline();
// Add counter which counts times the condition turned true // Add counter which counts times the condition turned true
@@ -1777,7 +1577,6 @@ private:
= new AstAssign{flp, new AstVarRef{flp, initialCntp, VAccess::WRITE}, = new AstAssign{flp, new AstVarRef{flp, initialCntp, VAccess::WRITE},
readCntRefp->cloneTree(false)}; readCntRefp->cloneTree(false)};
// Prepend to the sequence body to keep statement list structure valid. // Prepend to the sequence body to keep statement list structure valid.
UASSERT_OBJ(!bodyp->name().empty(), bodyp, "Sequence body should be named");
AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext(); AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext();
bodyp->addStmtsp(initialCntp); bodyp->addStmtsp(initialCntp);
initialCntp->addNextHere(assignp); initialCntp->addNextHere(assignp);
@@ -1787,7 +1586,7 @@ private:
= new AstIf{flp, new AstEq{flp, new AstVarRef{flp, initialCntp, VAccess::READ}, = new AstIf{flp, new AstEq{flp, new AstVarRef{flp, initialCntp, VAccess::READ},
readCntRefp->cloneTree(false)}}; readCntRefp->cloneTree(false)}};
// Delete it, because it is always copied before insetion to the AST // Delete it, because it is always copied before insetion to the AST
pushDeletep(m_disableSeqIfp); // m_disableSeqIfp used later pushDeletep(m_disableSeqIfp);
} }
iterateChildren(nodep); iterateChildren(nodep);
} }
@@ -1823,7 +1622,6 @@ public:
// CONSTRUCTORS // CONSTRUCTORS
explicit AssertPreVisitor(AstNetlist* nodep) explicit AssertPreVisitor(AstNetlist* nodep)
: m_netlistp{nodep} { : m_netlistp{nodep} {
{ AssertPreClockingVisitor{nodep}; }
// Process // Process
iterate(nodep); iterate(nodep);
// Fix up varref names // Fix up varref names
+2 -6
View File
@@ -66,7 +66,7 @@ VCMethod VCMethod::arrayMethod(const string& name) {
std::string VNUser::dumpStr(std::string (*fmtAddrp)(const void*)) const { std::string VNUser::dumpStr(std::string (*fmtAddrp)(const void*)) const {
#ifdef VL_USER_TYPE_CHECKS #ifdef VL_USER_TYPE_CHECKS
if (const uint64_t* const uip = std::get_if<uint64_t>(&m_u)) return "#"s + cvtToStr(*uip); if (const int* const uip = std::get_if<int>(&m_u)) return "#"s + cvtToStr(*uip);
if (void* const* const upp = std::get_if<void*>(&m_u)) return fmtAddrp(*upp); if (void* const* const upp = std::get_if<void*>(&m_u)) return fmtAddrp(*upp);
return ""; return "";
#else #else
@@ -208,10 +208,6 @@ string AstNode::dedotName(const string& namein) {
return pretty; return pretty;
} }
string AstNode::nameNoArray(const string& namein) {
return namein.substr(0, namein.find("__BRA__"));
}
string AstNode::vcdName(const string& namein) { string AstNode::vcdName(const string& namein) {
// VCD tracing expects space to separate hierarchy // VCD tracing expects space to separate hierarchy
// Dots are reserved for dots the user put in the name // Dots are reserved for dots the user put in the name
@@ -365,7 +361,7 @@ string AstNode::vpiName(const string& namein) {
string AstNode::prettyTypeName() const { string AstNode::prettyTypeName() const {
if (name() == "") return typeName(); if (name() == "") return typeName();
return std::string{typeName()} + " '" + prettyNameMsg() + "'"; return std::string{typeName()} + " '" + prettyName() + "'";
} }
//###################################################################### //######################################################################
+41 -45
View File
@@ -56,7 +56,6 @@
// Forward declarations // Forward declarations
class V3Graph; class V3Graph;
class ExecMTask; class ExecMTask;
class VIfaceCaptureTag;
//###################################################################### //######################################################################
@@ -152,11 +151,11 @@ class VNUser final {
#ifdef VL_USER_TYPE_CHECKS #ifdef VL_USER_TYPE_CHECKS
// monostate is an unwritten / cleared slot. It can be read as either form // monostate is an unwritten / cleared slot. It can be read as either form
// and yields nullptr/0. // and yields nullptr/0.
std::variant<std::monostate, uint64_t, void*> m_u; std::variant<std::monostate, int, void*> m_u;
#else #else
union { union {
void* up; void* up;
uint64_t uq; int ui;
} m_u; } m_u;
#endif #endif
@@ -165,7 +164,7 @@ public:
VNUser() = default; VNUser() = default;
// non-explicit: // non-explicit:
// cppcheck-suppress noExplicitConstructor // cppcheck-suppress noExplicitConstructor
VNUser(uint64_t i) { VNUser(int i) {
// VNUser{0} represents the monostate // VNUser{0} represents the monostate
if (i) m_u = i; if (i) m_u = i;
} }
@@ -179,22 +178,22 @@ public:
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE { typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE {
if (std::holds_alternative<std::monostate>(m_u)) return nullptr; if (std::holds_alternative<std::monostate>(m_u)) return nullptr;
void* const* const upp = std::get_if<void*>(&m_u); void* const* const upp = std::get_if<void*>(&m_u);
UASSERT_STATIC(upp, "AstNode user() slot written as uint64_t, read as pointer"); UASSERT_STATIC(upp, "AstNode user() slot written as int, read as pointer");
return reinterpret_cast<T>(*upp); return reinterpret_cast<T>(*upp);
} }
uint64_t toUQuad() const { int toInt() const {
if (std::holds_alternative<std::monostate>(m_u)) return 0; if (std::holds_alternative<std::monostate>(m_u)) return 0;
const uint64_t* const uip = std::get_if<uint64_t>(&m_u); const int* const uip = std::get_if<int>(&m_u);
UASSERT_STATIC(uip, "AstNode user() slot written as pointer, read as uint64_t"); UASSERT_STATIC(uip, "AstNode user() slot written as pointer, read as int");
return *uip; return *uip;
} }
#else #else
VNUser() = default; VNUser() = default;
// non-explicit: // non-explicit:
// cppcheck-suppress noExplicitConstructor // cppcheck-suppress noExplicitConstructor
VNUser(uint64_t i) { VNUser(int i) {
m_u.up = nullptr; m_u.up = nullptr;
m_u.uq = i; m_u.ui = i;
} }
explicit VNUser(void* p) { m_u.up = p; } explicit VNUser(void* p) { m_u.up = p; }
~VNUser() = default; ~VNUser() = default;
@@ -203,7 +202,7 @@ public:
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE { typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE {
return reinterpret_cast<T>(m_u.up); return reinterpret_cast<T>(m_u.up);
} }
uint64_t toUQuad() const { return m_u.uq; } int toInt() const { return m_u.ui; }
#endif #endif
VSymEnt* toSymEnt() const { return to<VSymEnt*>(); } VSymEnt* toSymEnt() const { return to<VSymEnt*>(); }
AstNode* toNodep() const VL_MT_SAFE { return to<AstNode*>(); } AstNode* toNodep() const VL_MT_SAFE { return to<AstNode*>(); }
@@ -392,7 +391,7 @@ protected:
RELINK_OP4 RELINK_OP4
}; };
AstNode* m_oldp = nullptr; // The old node that was linked to this point in the tree AstNode* m_oldp = nullptr; // The old node that was linked to this point in the tree
AstNode* m_backp = nullptr; // Saved AstNode::m_backp of the unlinked node, to relink AstNode* m_backp = nullptr;
AstNode** m_iterpp = nullptr; AstNode** m_iterpp = nullptr;
RelinkWhatEn m_chg = RELINK_BAD; RelinkWhatEn m_chg = RELINK_BAD;
@@ -493,14 +492,14 @@ class AstNode VL_NOT_FINAL {
// This member ordering both allows 64 bit alignment and puts associated data together // This member ordering both allows 64 bit alignment and puts associated data together
// (under VL_USER_TYPE_CHECKS a VNUser is larger than 64 bits, so this packing no // (under VL_USER_TYPE_CHECKS a VNUser is larger than 64 bits, so this packing no
// longer holds; that build trades node size for catching int/pointer confusion) // longer holds; that build trades node size for catching int/pointer confusion)
VNUser m_user1u{nullptr}; // Contains any information the user iteration routine wants VNUser m_user1u{0}; // Contains any information the user iteration routine wants
uint32_t m_user1Cnt = 0; // Mark of when userp was set uint32_t m_user1Cnt = 0; // Mark of when userp was set
uint32_t m_user2Cnt = 0; // Mark of when userp was set uint32_t m_user2Cnt = 0; // Mark of when userp was set
VNUser m_user2u{nullptr}; // Contains any information the user iteration routine wants VNUser m_user2u{0}; // Contains any information the user iteration routine wants
VNUser m_user3u{nullptr}; // Contains any information the user iteration routine wants VNUser m_user3u{0}; // Contains any information the user iteration routine wants
uint32_t m_user3Cnt = 0; // Mark of when userp was set uint32_t m_user3Cnt = 0; // Mark of when userp was set
uint32_t m_user4Cnt = 0; // Mark of when userp was set uint32_t m_user4Cnt = 0; // Mark of when userp was set
VNUser m_user4u{nullptr}; // Contains any information the user iteration routine wants VNUser m_user4u{0}; // Contains any information the user iteration routine wants
// METHODS // METHODS
void op1p(AstNode* nodep) { void op1p(AstNode* nodep) {
@@ -659,7 +658,6 @@ public:
string origNameProtect() const; // origName with --protect-id applied string origNameProtect() const; // origName with --protect-id applied
string shortName() const; // Name with __PVT__ removed for concatenating scopes string shortName() const; // Name with __PVT__ removed for concatenating scopes
static string dedotName(const string& namein); // Name with dots removed static string dedotName(const string& namein); // Name with dots removed
static string nameNoArray(const string& namein); // Name with any array index removed
static string prettyName(const string& namein) VL_PURE; // Name for printing out to the user static string prettyName(const string& namein) VL_PURE; // Name for printing out to the user
static string vpiName(const string& namein); // Name for vpi access static string vpiName(const string& namein); // Name for vpi access
static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors) static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors)
@@ -670,10 +668,7 @@ public:
static string encodeNumber(int64_t num); // Encode number into internal C representation static string encodeNumber(int64_t num); // Encode number into internal C representation
static string vcdName(const string& namein); // Name for printing out to vcd files static string vcdName(const string& namein); // Name for printing out to vcd files
string prettyName() const { return prettyName(name()); } string prettyName() const { return prettyName(name()); }
// Name for messages to the user, by default prettyName(), but friendlier for some nodes, string prettyNameQ() const { return prettyNameQ(name()); }
// such as a specialized class, whose name() is internal
virtual string prettyNameMsg() const { return prettyName(); }
string prettyNameQ() const { return "'"s + prettyNameMsg() + "'"; } // Quoted, for messages
string verilogName() const { return vpiName(origName()); } // Decoded original Verilog name string verilogName() const { return vpiName(origName()); } // Decoded original Verilog name
// "VARREF" for error messages (NOT dtype's pretty name) // "VARREF" for error messages (NOT dtype's pretty name)
string prettyTypeName() const; string prettyTypeName() const;
@@ -713,61 +708,61 @@ public:
VNUser user1u() const VL_MT_STABLE { VNUser user1u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default // Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "user1p used without AstUserInUse"); //UASSERT_STATIC(VNUser1InUse::s_userBusy, "user1p used without AstUserInUse");
return ((m_user1Cnt == VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{nullptr}); return ((m_user1Cnt == VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
} }
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); } AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; } void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; }
void user1p(void* userp) { user1u(VNUser{userp}); } void user1p(void* userp) { user1u(VNUser{userp}); }
void user1(uint64_t val) { user1u(VNUser{val}); } void user1(int val) { user1u(VNUser{val}); }
uint64_t user1() const { return user1u().toUQuad(); } int user1() const { return user1u().toInt(); }
uint64_t user1Inc(uint64_t val = 1) { const uint64_t v = user1(); user1(v + val); return v; } int user1Inc(int val = 1) { const int v = user1(); user1(v + val); return v; }
uint64_t user1Or(uint64_t val) { const uint64_t v = user1(); user1(v | val); return v; } int user1Or(int val) { const int v = user1(); user1(v | val); return v; }
uint64_t user1SetOnce() { const uint64_t v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc() int user1SetOnce() { const int v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
static void user1ClearTree() { VNUser1InUse::clear(); } // Clear userp()'s across the entire tree static void user1ClearTree() { VNUser1InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user2u() const VL_MT_STABLE { VNUser user2u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default // Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "user2p used without AstUserInUse"); //UASSERT_STATIC(VNUser2InUse::s_userBusy, "user2p used without AstUserInUse");
return ((m_user2Cnt == VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{nullptr}); return ((m_user2Cnt == VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
} }
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); } AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; } void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; }
void user2p(void* userp) { user2u(VNUser{userp}); } void user2p(void* userp) { user2u(VNUser{userp}); }
void user2(uint64_t val) { user2u(VNUser{val}); } void user2(int val) { user2u(VNUser{val}); }
uint64_t user2() const { return user2u().toUQuad(); } int user2() const { return user2u().toInt(); }
uint64_t user2Inc(uint64_t val = 1) { const uint64_t v = user2(); user2(v + val); return v; } int user2Inc(int val = 1) { const int v = user2(); user2(v + val); return v; }
uint64_t user2Or(uint64_t val) { const uint64_t v = user2(); user2(v | val); return v; } int user2Or(int val) { const int v = user2(); user2(v | val); return v; }
uint64_t user2SetOnce() { const uint64_t v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc() int user2SetOnce() { const int v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
static void user2ClearTree() { VNUser2InUse::clear(); } // Clear userp()'s across the entire tree static void user2ClearTree() { VNUser2InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user3u() const VL_MT_STABLE { VNUser user3u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default // Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "user3p used without AstUserInUse"); //UASSERT_STATIC(VNUser3InUse::s_userBusy, "user3p used without AstUserInUse");
return ((m_user3Cnt == VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{nullptr}); return ((m_user3Cnt == VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
} }
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); } AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; } void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; }
void user3p(void* userp) { user3u(VNUser{userp}); } void user3p(void* userp) { user3u(VNUser{userp}); }
void user3(uint64_t val) { user3u(VNUser{val}); } void user3(int val) { user3u(VNUser{val}); }
uint64_t user3() const { return user3u().toUQuad(); } int user3() const { return user3u().toInt(); }
uint64_t user3Inc(uint64_t val = 1) { const uint64_t v = user3(); user3(v + val); return v; } int user3Inc(int val = 1) { const int v = user3(); user3(v + val); return v; }
uint64_t user3Or(uint64_t val) { const uint64_t v = user3(); user3(v | val); return v; } int user3Or(int val) { const int v = user3(); user3(v | val); return v; }
uint64_t user3SetOnce() { const uint64_t v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc() int user3SetOnce() { const int v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
static void user3ClearTree() { VNUser3InUse::clear(); } // Clear userp()'s across the entire tree static void user3ClearTree() { VNUser3InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user4u() const VL_MT_STABLE { VNUser user4u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default // Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "user4p used without AstUserInUse"); //UASSERT_STATIC(VNUser4InUse::s_userBusy, "user4p used without AstUserInUse");
return ((m_user4Cnt == VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{nullptr}); return ((m_user4Cnt == VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
} }
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); } AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; } void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; }
void user4p(void* userp) { user4u(VNUser{userp}); } void user4p(void* userp) { user4u(VNUser{userp}); }
void user4(uint64_t val) { user4u(VNUser{val}); } void user4(int val) { user4u(VNUser{val}); }
uint64_t user4() const { return user4u().toUQuad(); } int user4() const { return user4u().toInt(); }
uint64_t user4Or(uint64_t val) { const uint64_t v = user4(); user4(v | val); return v; } int user4Or(int val) { const int v = user4(); user4(v | val); return v; }
uint64_t user4Inc(uint64_t val = 1) { const uint64_t v = user4(); user4(v + val); return v; } int user4Inc(int val = 1) { const int v = user4(); user4(v + val); return v; }
uint64_t user4SetOnce() { const uint64_t v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc() int user4SetOnce() { const int v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
static void user4ClearTree() { VNUser4InUse::clear(); } // Clear userp()'s across the entire tree static void user4ClearTree() { VNUser4InUse::clear(); } // Clear userp()'s across the entire tree
// clang-format on // clang-format on
@@ -1040,6 +1035,7 @@ protected:
AstNode* iterateSubtreeReturnEdits(VNVisitor& v); AstNode* iterateSubtreeReturnEdits(VNVisitor& v);
static void dumpJsonNum(std::ostream& os, const std::string& name, int64_t val); static void dumpJsonNum(std::ostream& os, const std::string& name, int64_t val);
static void dumpJsonBool(std::ostream& os, const std::string& name, bool val);
static void dumpJsonBoolIf(std::ostream& os, const std::string& name, bool val); static void dumpJsonBoolIf(std::ostream& os, const std::string& name, bool val);
static void dumpJsonStr(std::ostream& os, const std::string& name, const std::string& val); static void dumpJsonStr(std::ostream& os, const std::string& name, const std::string& val);
static void dumpJsonPtr(std::ostream& os, const std::string& name, const AstNode* const valp); static void dumpJsonPtr(std::ostream& os, const std::string& name, const AstNode* const valp);
+265 -437
View File
File diff suppressed because it is too large Load Diff
+2 -5
View File
@@ -149,9 +149,6 @@ bool AstActive::hasCombo() const { return m_sentreep->hasCombo(); }
bool AstActive::hasInitial() const { return m_sentreep->hasInitial(); } bool AstActive::hasInitial() const { return m_sentreep->hasInitial(); }
bool AstActive::hasStatic() const { return m_sentreep->hasStatic(); } bool AstActive::hasStatic() const { return m_sentreep->hasStatic(); }
bool AstNodeModule::isDollarUnit() const { return this == v3Global.rootp()->dollarUnitPkgp(); }
bool AstNodeModule::isConstPool() const { return this == v3Global.rootp()->constPoolPkgp(); }
AstAlways::AstAlways(AstAssignW* assignp) AstAlways::AstAlways(AstAssignW* assignp)
: ASTGEN_SUPER_Always(assignp->fileline(), assignp) : ASTGEN_SUPER_Always(assignp->fileline(), assignp)
, m_keyword{VAlwaysKwd::CONT_ASSIGN} {} , m_keyword{VAlwaysKwd::CONT_ASSIGN} {}
@@ -168,10 +165,10 @@ bool AstVar::sameNode(const AstNode* samep) const {
&& covergroupRefMember() == asamep->covergroupRefMember(); && covergroupRefMember() == asamep->covergroupRefMember();
} }
AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarRef* matchp) AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp)
: ASTGEN_SUPER_MatchMasked(fl) { : ASTGEN_SUPER_MatchMasked(fl) {
this->lhsp(lhsp); this->lhsp(lhsp);
this->matchp(matchp); this->matchp(new AstVarRef{fl, matchp, VAccess::READ});
dtypeSetUInt32(); dtypeSetUInt32();
} }
+23 -125
View File
@@ -34,14 +34,10 @@ class AstNodeDType VL_NOT_FINAL : public AstNode {
// Ideally width() would migrate to BasicDType as that's where it makes sense, // Ideally width() would migrate to BasicDType as that's where it makes sense,
// but it's currently so prevalent in the code we leave it here. // but it's currently so prevalent in the code we leave it here.
// Note the below members are included in AstTypeTable::Key lookups // Note the below members are included in AstTypeTable::Key lookups
// dist-ast-dump-suppress // Part of dumpSmall
int m_width = 0; // (also in AstTypeTable::Key) Bit width of operation int m_width = 0; // (also in AstTypeTable::Key) Bit width of operation
// (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation int m_widthMin
// dist-ast-dump-suppress // Part of dumpSmall = 0; // (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation
int m_widthMin = 0;
// dist-ast-dump-suppress // Part of dumpSmall
VSigning m_numeric; // (also in AstTypeTable::Key) Node is signed VSigning m_numeric; // (also in AstTypeTable::Key) Node is signed
// dist-ast-dump-suppress // Part of dumpSmall
bool m_generic = false; // Simple globally referenced type, don't garbage collect bool m_generic = false; // Simple globally referenced type, don't garbage collect
// Unique number assigned to each dtype during creation for IEEE matching // Unique number assigned to each dtype during creation for IEEE matching
static int s_uniqueNum; static int s_uniqueNum;
@@ -103,9 +99,6 @@ public:
return const_cast<AstNodeDType*>( return const_cast<AstNodeDType*>(
static_cast<const AstNodeDType*>(this)->skipRefIterp(true, false)); static_cast<const AstNodeDType*>(this)->skipRefIterp(true, false));
} }
// (Slow) Recurse over MemberDType|ParamTypeDType|RefDType|ConstDType to EnumDType,
// Returns null if not resolved
const AstNodeDType* skipRefToEnumOrNullp() const { return skipRefIterp(true, false, false); }
// (Slow) Recurse over MemberDType|ParamTypeDType|RefDType to other type // (Slow) Recurse over MemberDType|ParamTypeDType|RefDType to other type
const AstNodeDType* skipRefToNonRefp() const { return skipRefIterp(false, false); } const AstNodeDType* skipRefToNonRefp() const { return skipRefIterp(false, false); }
AstNodeDType* skipRefToNonRefp() { AstNodeDType* skipRefToNonRefp() {
@@ -253,9 +246,8 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType {
// //
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Package emitted with // @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Package emitted with
string m_name; // Name from upper typedef, if any string m_name; // Name from upper typedef, if any
string m_typedefName; // Typedef's dtypeName() once moved off the typedef declaring it const int m_uniqueNum;
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming bool m_packed;
bool m_packed; // Packed struct/union, else unpacked
bool m_isFourstate = false; // V3Width computes; true if any member is 4-state bool m_isFourstate = false; // V3Width computes; true if any member is 4-state
bool m_constrainedRand = false; // True if struct has constraint expression bool m_constrainedRand = false; // True if struct has constraint expression
bool m_emitToString = false; // Generate to_string() for this struct/union if set bool m_emitToString = false; // Generate to_string() for this struct/union if set
@@ -271,7 +263,6 @@ protected:
AstNodeUOrStructDType(const AstNodeUOrStructDType& other) AstNodeUOrStructDType(const AstNodeUOrStructDType& other)
: AstNodeDType{other} : AstNodeDType{other}
, m_name{other.m_name} , m_name{other.m_name}
, m_typedefName{other.m_typedefName}
, m_uniqueNum{uniqueNumInc()} , m_uniqueNum{uniqueNumInc()}
, m_packed{other.m_packed} , m_packed{other.m_packed}
, m_isFourstate{other.m_isFourstate} {} , m_isFourstate{other.m_isFourstate} {}
@@ -299,8 +290,6 @@ public:
bool similarDTypeNode(const AstNodeDType* samep) const override; bool similarDTypeNode(const AstNodeDType* samep) const override;
string name() const override VL_MT_STABLE { return m_name; } string name() const override VL_MT_STABLE { return m_name; }
void name(const string& flag) override { m_name = flag; } void name(const string& flag) override { m_name = flag; }
// Keep the name the typedef declaring it gives, as it moves off it to the type table
void typedefName(const string& name) { m_typedefName = name; }
bool packed() const VL_MT_SAFE { return m_packed; } bool packed() const VL_MT_SAFE { return m_packed; }
void packed(bool flag) { m_packed = flag; } void packed(bool flag) { m_packed = flag; }
// packed() but as don't support unpacked, presently all structs // packed() but as don't support unpacked, presently all structs
@@ -326,7 +315,7 @@ public:
class AstEnumItem final : public AstNode { class AstEnumItem final : public AstNode {
// @astgen op1 := rangep : Optional[AstRange] // Range for name appending // @astgen op1 := rangep : Optional[AstRange] // Range for name appending
// @astgen op2 := valuep : Optional[AstNodeExpr] // @astgen op2 := valuep : Optional[AstNodeExpr]
string m_name; // Name of item string m_name;
public: public:
// Parents: ENUM // Parents: ENUM
@@ -416,7 +405,6 @@ class AstBasicDType final : public AstNodeDType {
// @astgen op1 := rangep : Optional[AstRange] // Range of variable // @astgen op1 := rangep : Optional[AstRange] // Range of variable
struct Members final { struct Members final {
VBasicDTypeKwd m_keyword; // (also in VBasicTypeKey) What keyword created basic type VBasicDTypeKwd m_keyword; // (also in VBasicTypeKey) What keyword created basic type
// dist-ast-dump-suppress // Part of dumpSmall
VNumRange m_nrange; // (also in VBasicTypeKey) Numeric msb/lsb (if non-opaque keyword) VNumRange m_nrange; // (also in VBasicTypeKey) Numeric msb/lsb (if non-opaque keyword)
bool operator==(const Members& rhs) const { bool operator==(const Members& rhs) const {
return rhs.m_keyword == m_keyword && rhs.m_nrange == m_nrange; return rhs.m_keyword == m_keyword && rhs.m_nrange == m_nrange;
@@ -505,9 +493,6 @@ public:
bool isStdRandomGenerator() const VL_MT_SAFE { bool isStdRandomGenerator() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::RANDOM_STDGENERATOR; return keyword() == VBasicDTypeKwd::RANDOM_STDGENERATOR;
} }
bool isCovergroupInstHandle() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::COVERGROUP_INSTHANDLE;
}
bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); } bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
bool isString() const VL_MT_STABLE { return keyword().isString(); } bool isString() const VL_MT_STABLE { return keyword().isString(); }
bool isZeroInit() const { return keyword().isZeroInit(); } bool isZeroInit() const { return keyword().isZeroInit(); }
@@ -618,7 +603,6 @@ public:
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override; string prettyDTypeName(bool full) const override;
string prettyNameMsg() const override;
string name() const override VL_MT_STABLE; string name() const override VL_MT_STABLE;
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
int widthAlignBytes() const override { return 0; } int widthAlignBytes() const override { return 0; }
@@ -698,89 +682,13 @@ public:
int widthTotalBytes() const override { return 1; } int widthTotalBytes() const override { return 1; }
bool isCompound() const override { return false; } bool isCompound() const override { return false; }
}; };
class AstCoverCrossDType final : public AstNodeDType {
// Borrowed pointer to VlCoverCrossT<...> or construction-time VlCoverCrossDyn.
const uint32_t m_dimensions;
const uint32_t m_tuples; // Fixed capacity number of cross tuples (VlCoverCrossT's Tuples)
const uint32_t m_bins; // Fixed capacity number of explicit bins (VlCoverCrossT's Bins)
const uint32_t m_autoBins; // Fixed capacity number of auto bins (VlCoverCrossT's AutoBins)
const uint64_t m_binWords; // Fixed capacity selection words (VlCoverCrossT's BinWords)
const bool m_dynamic; // Bin layout is determined at covergroup construction
public:
AstCoverCrossDType(FileLine* fl, uint32_t dimensions, uint32_t tuples, uint32_t bins,
uint32_t autoBins, uint64_t binWords, bool dynamic = false)
: ASTGEN_SUPER_CoverCrossDType(fl)
, m_dimensions{dimensions}
, m_tuples{tuples}
, m_bins{bins}
, m_autoBins{autoBins}
, m_binWords{binWords}
, m_dynamic{dynamic} {
dtypep(this);
}
ASTGEN_MEMBERS_AstCoverCrossDType;
const char* broken() const override {
BROKEN_RTN(m_dimensions == 0);
return nullptr;
}
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
const AstCoverCrossDType* const sp = VN_DBG_AS(samep, CoverCrossDType);
return dimensions() == sp->dimensions() && tuples() == sp->tuples() && bins() == sp->bins()
&& autoBins() == sp->autoBins() && binWords() == sp->binWords()
&& isDynamic() == sp->isDynamic();
} // LCOV_EXCL_STOP
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
void dumpSmall(std::ostream& str) const override;
uint32_t dimensions() const { return m_dimensions; }
uint32_t tuples() const { return m_tuples; }
uint32_t bins() const { return m_bins; }
uint32_t autoBins() const { return m_autoBins; }
uint64_t binWords() const { return m_binWords; }
bool isDynamic() const { return m_dynamic; }
string cppTemplateArgs() const;
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
int widthAlignBytes() const override { return sizeof(void*); }
int widthTotalBytes() const override { return sizeof(void*); }
bool isCompound() const override { return true; }
};
class AstCoverpointDType final : public AstNodeDType {
// Borrowed pointer to a covergroup coverpoint runtime, 'VlCoverpointT<hitBound>*'.
// Follows pattern of AstQueueDType in capturing the compile-time max bin overlap
// template argument.
uint32_t m_hitBound; // VlCoverpointT<> template argument; hit list size, >= 1
public:
AstCoverpointDType(FileLine* fl, uint32_t hitBound)
: ASTGEN_SUPER_CoverpointDType(fl)
, m_hitBound{hitBound} {
dtypep(this);
}
ASTGEN_MEMBERS_AstCoverpointDType;
const char* broken() const override {
BROKEN_RTN(m_hitBound < 1);
return nullptr;
}
// V3Covergroup interns these one-per-hitBound into the type table, so identity is
// equality; there is never a second node with the same bound to compare against.
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
void dumpSmall(std::ostream& str) const override;
// ACCESSORS
uint32_t hitBound() const { return m_hitBound; }
// METHODS
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
int widthAlignBytes() const override { return sizeof(void*); }
int widthTotalBytes() const override { return sizeof(void*); }
bool isCompound() const override { return true; }
};
class AstDefImplicitDType final : public AstNodeDType { class AstDefImplicitDType final : public AstNodeDType {
// For parsing enum/struct/unions that are declared with a variable rather than typedef // For parsing enum/struct/unions that are declared with a variable rather than typedef
// This allows "var enum {...} a,b" to share the enum definition for both variables // This allows "var enum {...} a,b" to share the enum definition for both variables
// After link, these become typedefs // After link, these become typedefs
// @astgen op1 := childDTypep : Optional[AstNodeDType] // @astgen op1 := childDTypep : Optional[AstNodeDType]
string m_name; // Data type name string m_name;
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming const int m_uniqueNum;
public: public:
AstDefImplicitDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp) AstDefImplicitDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp)
@@ -890,9 +798,13 @@ class AstEnumDType final : public AstNodeDType {
// @astgen op2 := itemsp : List[AstEnumItem] // @astgen op2 := itemsp : List[AstEnumItem]
// //
// @astgen ptr := m_refDTypep : Optional[AstNodeDType] // Elements of this type (post-width) // @astgen ptr := m_refDTypep : Optional[AstNodeDType] // Elements of this type (post-width)
public:
using TableMap = std::map<VAttrType, AstVar*>;
private:
string m_name; // Name from upper typedef, if any string m_name; // Name from upper typedef, if any
string m_typedefName; // Typedef's dtypeName() once moved off the typedef declaring it const int m_uniqueNum;
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming TableMap m_tableMap; // Created table for V3Width only to remove duplicates
public: public:
AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstEnumItem* itemsp) AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstEnumItem* itemsp)
@@ -907,7 +819,6 @@ public:
AstEnumDType(const AstEnumDType& other) AstEnumDType(const AstEnumDType& other)
: AstNodeDType{other} : AstNodeDType{other}
, m_name{other.m_name} , m_name{other.m_name}
, m_typedefName{other.m_typedefName}
, m_uniqueNum{uniqueNumInc()} {} , m_uniqueNum{uniqueNumInc()} {}
ASTGEN_MEMBERS_AstEnumDType; ASTGEN_MEMBERS_AstEnumDType;
@@ -927,8 +838,6 @@ public:
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
string name() const override VL_MT_STABLE { return m_name; } string name() const override VL_MT_STABLE { return m_name; }
void name(const string& flag) override { m_name = flag; } void name(const string& flag) override { m_name = flag; }
// Keep the name the typedef declaring it gives, as it moves off it to the type table
void typedefName(const string& name) { m_typedefName = name; }
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
@@ -943,12 +852,13 @@ public:
return count; return count;
} }
bool isCompound() const override { return false; } bool isCompound() const override { return false; }
TableMap& tableMap() { return m_tableMap; }
const TableMap& tableMap() const { return m_tableMap; }
}; };
class AstIfaceGenericDType final : public AstNodeDType { class AstIfaceGenericDType final : public AstNodeDType {
// Generic interface that will be replaced with AstIfaceRefDType // Generic interface that will be replaced with AstIfaceRefDType
FileLine* m_modportFileline; // Where modport token was FileLine* m_modportFileline; // Where modport token was
// dist-ast-dump-suppress // Part of name()
string m_modportName; // "" = no modport string m_modportName; // "" = no modport
public: public:
explicit AstIfaceGenericDType(FileLine* fl) explicit AstIfaceGenericDType(FileLine* fl)
@@ -962,6 +872,8 @@ public:
dtypep(this); dtypep(this);
} }
ASTGEN_MEMBERS_AstIfaceGenericDType; ASTGEN_MEMBERS_AstIfaceGenericDType;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
bool hasDType() const override VL_MT_SAFE { return true; } bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; } bool maybePointedTo() const override VL_MT_SAFE { return true; }
@@ -991,6 +903,7 @@ class AstIfaceRefDType final : public AstNodeDType {
string m_cellName; // "" = no cell, such as when connects to 'input' iface string m_cellName; // "" = no cell, such as when connects to 'input' iface
string m_ifaceName; // Interface name string m_ifaceName; // Interface name
string m_modportName; // "" = no modport string m_modportName; // "" = no modport
bool m_portDecl = false; // Interface_port_declaration
bool m_virtual = false; // True if virtual interface bool m_virtual = false; // True if virtual interface
public: public:
AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName) AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName)
@@ -1021,18 +934,12 @@ public:
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
bool similarDTypeNode(const AstNodeDType* samep) const override { bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
// Each occurrence of a virtual interface type parses to its own node,
// so pointer identity is insufficient; compare the referenced
// interface instead, same equivalence as AstNode::computeCastable uses.
const AstIfaceRefDType* const asamep = VN_DBG_AS(samep, IfaceRefDType);
return ifaceViaCellp() && ifaceViaCellp() == asamep->ifaceViaCellp()
&& modportName() == asamep->modportName() && isVirtual() == asamep->isVirtual();
}
int widthAlignBytes() const override { return 0; } int widthAlignBytes() const override { return 0; }
int widthTotalBytes() const override { return 0; } int widthTotalBytes() const override { return 0; }
bool isPortDecl() const { return m_portDecl; }
void isPortDecl(bool flag) { m_portDecl = flag; }
bool isVirtual() const { return m_virtual; } bool isVirtual() const { return m_virtual; }
void isVirtual(bool flag) { void isVirtual(bool flag) {
m_virtual = flag; m_virtual = flag;
@@ -1067,7 +974,7 @@ class AstMemberDType final : public AstNodeDType {
string m_name; // Name of variable string m_name; // Name of variable
string m_tag; // Holds the string of the verilator tag -- used in JSON output. string m_tag; // Holds the string of the verilator tag -- used in JSON output.
int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member
bool m_constrainedRand = false; // Member has a constraint expression bool m_constrainedRand = false;
VRandAttr m_rand; // Randomizability of this member (rand, randc, etc) VRandAttr m_rand; // Randomizability of this member (rand, randc, etc)
public: public:
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp, AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
@@ -1091,7 +998,6 @@ public:
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name string name() const override VL_MT_STABLE { return m_name; } // * = Var name
bool hasDType() const override VL_MT_SAFE { return true; } bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; } bool maybePointedTo() const override VL_MT_SAFE { return true; }
@@ -1175,7 +1081,6 @@ public:
ASTGEN_MEMBERS_AstParamTypeDType; ASTGEN_MEMBERS_AstParamTypeDType;
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
string prettyDTypeName(bool full) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override VL_MT_STABLE { AstNodeDType* subDTypep() const override VL_MT_STABLE {
return dtypep() ? dtypep() : childDTypep(); return dtypep() ? dtypep() : childDTypep();
@@ -1290,9 +1195,6 @@ class AstRefDType final : public AstNodeDType {
// @astgen ptr := m_refDTypep : Optional[AstNodeDType] // Data type references // @astgen ptr := m_refDTypep : Optional[AstNodeDType] // Data type references
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in // @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in
string m_name; // Name of an AstTypedef string m_name; // Name of an AstTypedef
// Interface capture tag, owned by V3LinkDotIfaceCapture; cloning copies it
const VIfaceCaptureTag* m_captureTagp = nullptr;
public: public:
AstRefDType(FileLine* fl, const string& name) AstRefDType(FileLine* fl, const string& name)
: ASTGEN_SUPER_RefDType(fl) : ASTGEN_SUPER_RefDType(fl)
@@ -1319,13 +1221,12 @@ public:
bool similarDTypeNode(const AstNodeDType* samep) const override { bool similarDTypeNode(const AstNodeDType* samep) const override {
return subDTypep()->similarDType(samep->subDTypep()); return subDTypep()->similarDType(samep->subDTypep());
} }
const char* broken() const override;
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; } string name() const override VL_MT_STABLE { return m_name; }
string prettyDTypeName(bool full) const override { string prettyDTypeName(bool full) const override {
return subDTypep() ? subDTypep()->prettyDTypeName(full) : prettyName(); return subDTypep() ? prettyName(subDTypep()->prettyDTypeName(full)) : prettyName();
} }
AstBasicDType* basicp() const override VL_MT_STABLE { AstBasicDType* basicp() const override VL_MT_STABLE {
return subDTypep() ? subDTypep()->basicp() : nullptr; return subDTypep() ? subDTypep()->basicp() : nullptr;
@@ -1344,8 +1245,6 @@ public:
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; } void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
const VIfaceCaptureTag* captureTagp() const { return m_captureTagp; }
void captureTagp(const VIfaceCaptureTag* tagp) { m_captureTagp = tagp; }
bool isCompound() const override { bool isCompound() const override {
v3fatalSrc("call isCompound on subdata type, not reference"); v3fatalSrc("call isCompound on subdata type, not reference");
return false; return false;
@@ -1517,7 +1416,6 @@ public:
bool sameNode(const AstNode* samep) const override; bool sameNode(const AstNode* samep) const override;
bool similarDTypeNode(const AstNodeDType* samep) const override; bool similarDTypeNode(const AstNodeDType* samep) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override VL_MT_STABLE { AstNodeDType* subDTypep() const override VL_MT_STABLE {
return m_refDTypep ? m_refDTypep : childDTypep(); return m_refDTypep ? m_refDTypep : childDTypep();
+17 -57
View File
@@ -66,18 +66,7 @@ public:
bool isOpaque() const { return VN_IS(this, CvtPackString); } bool isOpaque() const { return VN_IS(this, CvtPackString); }
// True for SVA multi-cycle sequence nodes (SExpr, SConsRep, etc.) // True for SVA multi-cycle sequence nodes (SExpr, SConsRep, etc.)
virtual bool isMultiCycleSva() const { return false; } virtual bool isMultiCycleSva() const { return false; }
const AstNodeExpr* getVAccessTargetRecurse() const; bool isLValue() const;
AstNodeExpr* getVAccessTargetRecurse() {
return const_cast<AstNodeExpr*>( // casting constness away is safe since this function is
// non-const itself therefore, caller guarantees that
// this object is non-const
static_cast<const AstNodeExpr*>(this)->getVAccessTargetRecurse());
}
VAccess getVAccessRecurse() const;
// TODO: this actually means it's a write or RW, not that it's an LValue
bool isLValue() const { return getVAccessRecurse().isWriteOrRW(); }
// Return base var (or const) nodep dereferences
AstNode* baseFromp(bool overMembers);
// Wrap This expression into an AstStmtExpr to denote it occurs in statement position // Wrap This expression into an AstStmtExpr to denote it occurs in statement position
inline AstStmtExpr* makeStmt(); inline AstStmtExpr* makeStmt();
@@ -200,7 +189,6 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeExpr {
// @astgen op2 := argsp : List[AstNodeExpr] // Note: op1 used by some sub-types only // @astgen op2 := argsp : List[AstNodeExpr] // Note: op1 used by some sub-types only
// //
// @astgen ptr := m_funcp : AstCFunc // Function being called // @astgen ptr := m_funcp : AstCFunc // Function being called
// dist-ast-dump-suppress // Too verbose
string m_argTypes; string m_argTypes;
bool m_superReference = false; // Called with super reference bool m_superReference = false; // Called with super reference
@@ -228,7 +216,7 @@ public:
void funcp(AstCFunc* funcp) { m_funcp = funcp; } void funcp(AstCFunc* funcp) { m_funcp = funcp; }
string argTypes() const { return m_argTypes; } string argTypes() const { return m_argTypes; }
void argTypes(const string& str) { m_argTypes = str; } void argTypes(const string& str) { m_argTypes = str; }
bool isUnlikely() const override { return m_funcp && m_funcp->isUnlikely(); }
string emitVerilog() final override { V3ERROR_NA_RETURN(""); } string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
string emitC() final override { V3ERROR_NA_RETURN(""); } string emitC() final override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const final override { return true; } bool cleanOut() const final override { return true; }
@@ -283,7 +271,6 @@ public:
bool superReference() const { return m_superReference; } bool superReference() const { return m_superReference; }
void superReference(bool flag) { m_superReference = flag; } void superReference(bool flag) { m_superReference = flag; }
bool isPure() override; bool isPure() override;
bool isUnlikely() const override { return m_taskp && m_taskp->isUnlikely(); }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstNodeFTaskRef* const asamep = VN_DBG_AS(samep, NodeFTaskRef); const AstNodeFTaskRef* const asamep = VN_DBG_AS(samep, NodeFTaskRef);
return taskp() == asamep->taskp() // return taskp() == asamep->taskp() //
@@ -532,6 +519,7 @@ public:
} }
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; } void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
static AstNodeVarRef* varRefLValueRecurse(AstNode* nodep);
}; };
// === Concrete node types ===================================================== // === Concrete node types =====================================================
@@ -568,7 +556,6 @@ class AstWith final : public AstNode {
private: private:
// 'with (identifier_list) {...}' restricted form (IEEE 1800-2023 18.7). // 'with (identifier_list) {...}' restricted form (IEEE 1800-2023 18.7).
bool m_restricted = false; bool m_restricted = false;
// dist-ast-dump-suppress // V3LinkDot temporary use only
bool m_validated = false; // identifier_list typo / unused checks already run bool m_validated = false; // identifier_list typo / unused checks already run
std::set<std::string> m_restrictedNames; std::set<std::string> m_restrictedNames;
@@ -752,7 +739,7 @@ class AstCMethodHard final : public AstNodeExpr {
// @astgen op2 := pinsp : List[AstNodeExpr] // Arguments // @astgen op2 := pinsp : List[AstNodeExpr] // Arguments
// @astgen op3 := withp : Optional[AstWith] // With clause // @astgen op3 := withp : Optional[AstWith] // With clause
VCMethod m_method; // Which method to call VCMethod m_method; // Which method to call
VIsCached m_purity; // Pure optimizable bool m_pure = false; // Pure optimizable
bool m_usePtr = false; // Use '->' not '.' bool m_usePtr = false; // Use '->' not '.'
public: public:
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VCMethod method, AstNodeExpr* pinsp = nullptr) AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VCMethod method, AstNodeExpr* pinsp = nullptr)
@@ -760,19 +747,15 @@ public:
, m_method{method} { , m_method{method} {
this->fromp(fromp); this->fromp(fromp);
addPinsp(pinsp); addPinsp(pinsp);
setPurity();
} }
ASTGEN_MEMBERS_AstCMethodHard; ASTGEN_MEMBERS_AstCMethodHard;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return method().ascii(); } string name() const override VL_MT_STABLE { return method().ascii(); }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstCMethodHard* const asamep = VN_DBG_AS(samep, CMethodHard); const AstCMethodHard* const asamep = VN_DBG_AS(samep, CMethodHard);
return (m_method == asamep->m_method); return (m_method == asamep->m_method);
} }
bool isPure() override { bool isPure() override { return m_pure; }
if (!m_purity.isCached()) m_purity.set(getPurity());
return m_purity.get();
}
int instrCount() const override; int instrCount() const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); }
@@ -783,7 +766,7 @@ public:
void method(VCMethod value) { m_method = value; } void method(VCMethod value) { m_method = value; }
private: private:
bool getPurity(); void setPurity();
}; };
class AstCReset final : public AstNodeExpr { class AstCReset final : public AstNodeExpr {
// Reset variable at startup // Reset variable at startup
@@ -1253,10 +1236,6 @@ public:
int instrCount() const override { return widthInstrs(); } int instrCount() const override { return widthInstrs(); }
bool isEqAllOnes() const { return num().isEqAllOnes(width()); } bool isEqAllOnes() const { return num().isEqAllOnes(width()); }
bool isEqAllOnesV() const { return num().isEqAllOnes(widthMinV()); } bool isEqAllOnesV() const { return num().isEqAllOnes(widthMinV()); }
// Whether a parameter declared without a data type takes the same type from this value
// as from 'samep': both real, both string, or both integral with the same width and
// signedness. Unlike AstNodeDType::similarDType, ignores whether two-state or four-state.
bool sameValueType(const AstConst* samep) const;
// Parse string and create appropriate type of AstConst. // Parse string and create appropriate type of AstConst.
// May return nullptr on parse failure. // May return nullptr on parse failure.
static AstConst* parseParamLiteral(FileLine* fl, const string& literal); static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
@@ -1446,8 +1425,6 @@ class AstEnumItemRef final : public AstNodeExpr {
// @astgen ptr := m_itemp : Optional[AstEnumItem] // [AfterLink] Pointer to item // @astgen ptr := m_itemp : Optional[AstEnumItem] // [AfterLink] Pointer to item
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in // @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in
string m_name; // Name of enum (for param relink) string m_name; // Name of enum (for param relink)
string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
bool m_containsGenBlock = false; // Contains gen block reference
public: public:
AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep) AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
: ASTGEN_SUPER_EnumItemRef(fl) : ASTGEN_SUPER_EnumItemRef(fl)
@@ -1464,15 +1441,10 @@ public:
int instrCount() const override { return 0; } int instrCount() const override { return 0; }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstEnumItemRef* const sp = VN_DBG_AS(samep, EnumItemRef); const AstEnumItemRef* const sp = VN_DBG_AS(samep, EnumItemRef);
return itemp() == sp->itemp() && dotted() == sp->dotted() return itemp() == sp->itemp();
&& containsGenBlock() == sp->containsGenBlock();
} }
AstEnumItem* itemp() const VL_MT_STABLE { return m_itemp; } AstEnumItem* itemp() const VL_MT_STABLE { return m_itemp; }
void itemp(AstEnumItem* nodep) { m_itemp = nodep; } void itemp(AstEnumItem* nodep) { m_itemp = nodep; }
string dotted() const { return m_dotted; }
void dotted(const string& dotted) { m_dotted = dotted; }
bool containsGenBlock() const { return m_containsGenBlock; }
void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; } bool cleanOut() const override { return true; }
@@ -1486,7 +1458,7 @@ class AstExprStmt final : public AstNodeExpr {
// @astgen op1 := stmtsp : List[AstNode] // @astgen op1 := stmtsp : List[AstNode]
// @astgen op2 := resultp : AstNodeExpr // @astgen op2 := resultp : AstNodeExpr
private: private:
bool m_hasResult = true; // Returns result via resultp() bool m_hasResult = true;
public: public:
AstExprStmt(FileLine* fl, AstNode* stmtsp, AstNodeExpr* resultp) AstExprStmt(FileLine* fl, AstNode* stmtsp, AstNodeExpr* resultp)
@@ -1497,8 +1469,6 @@ public:
} }
ASTGEN_MEMBERS_AstExprStmt; ASTGEN_MEMBERS_AstExprStmt;
// METHODS // METHODS
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; } bool cleanOut() const override { return true; }
@@ -1776,7 +1746,7 @@ public:
bool sameNode(const AstNode* /*samep*/) const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; }
}; };
class AstImplication final : public AstNodeExpr { class AstImplication final : public AstNodeExpr {
// Implication |-> |=> (IEEE 1800-2023 16.12.7) and followed-by #-# #=# // Implication |-> |=> (IEEE 1800-2023 16.12.6) and followed-by #-# #=#
// (IEEE 1800-2023 16.12.9). Antecedent-miss is vacuous-pass for implication // (IEEE 1800-2023 16.12.9). Antecedent-miss is vacuous-pass for implication
// and non-vacuous-fail for followed-by, hence the separate flag. // and non-vacuous-fail for followed-by, hence the separate flag.
// @astgen op1 := lhsp : AstNodeExpr // @astgen op1 := lhsp : AstNodeExpr
@@ -1804,7 +1774,7 @@ public:
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); } int instrCount() const override { return widthInstrs(); }
bool isMultiCycleSva() const override { return m_isFollowedBy || !m_isOverlapped; } bool isMultiCycleSva() const override { return m_isFollowedBy; }
bool isOverlapped() const { return m_isOverlapped; } bool isOverlapped() const { return m_isOverlapped; }
bool isFollowedBy() const { return m_isFollowedBy; } bool isFollowedBy() const { return m_isFollowedBy; }
}; };
@@ -1823,7 +1793,6 @@ public:
using KeyItemMap = std::map<uint64_t, AstInitItem*>; using KeyItemMap = std::map<uint64_t, AstInitItem*>;
private: private:
// dist-ast-dump-suppress // Dumped using dumpInitList
KeyItemMap m_map; // Node value for each array index KeyItemMap m_map; // Node value for each array index
// METHODS // METHODS
void dumpInitList(std::ostream& str) const; void dumpInitList(std::ostream& str) const;
@@ -1897,8 +1866,6 @@ public:
, m_name{name} , m_name{name}
, m_index{index} {} , m_index{index} {}
ASTGEN_MEMBERS_AstLambdaArgRef; ASTGEN_MEMBERS_AstLambdaArgRef;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool sameNode(const AstNode* /*samep*/) const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; }
string emitVerilog() override { return name(); } string emitVerilog() override { return name(); }
string emitC() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); }
@@ -1918,7 +1885,7 @@ class AstMatchMasked final : public AstNodeExpr {
// @astgen op1 := lhsp : AstNodeExpr // @astgen op1 := lhsp : AstNodeExpr
// @astgen op2 := matchp : AstVarRef // @astgen op2 := matchp : AstVarRef
public: public:
inline AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarRef* matchp); inline AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp);
ASTGEN_MEMBERS_AstMatchMasked; ASTGEN_MEMBERS_AstMatchMasked;
string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { return "VL_MATCHMASKED_%lq(%lw, %li, %ri)"; } string emitC() override { return "VL_MATCHMASKED_%lq(%lw, %li, %ri)"; }
@@ -2046,7 +2013,7 @@ class AstParseRef final : public AstNodeExpr {
// @astgen op1 := lhsp : Optional[AstNodeExpr] // @astgen op1 := lhsp : Optional[AstNodeExpr]
// @astgen op2 := ftaskrefp : Optional[AstNodeFTaskRef] // @astgen op2 := ftaskrefp : Optional[AstNodeFTaskRef]
string m_name; // Name of the variable/function/task string m_name;
public: public:
AstParseRef(FileLine* fl, const string& name, AstNodeExpr* lhsp = nullptr, AstParseRef(FileLine* fl, const string& name, AstNodeExpr* lhsp = nullptr,
@@ -2419,7 +2386,6 @@ class AstSFormatArg final : public AstNodeExpr {
// used to pass to (potentially) runtime decoding of format arguments // used to pass to (potentially) runtime decoding of format arguments
// PARENT: SFormatF (or next list of expressions) // PARENT: SFormatF (or next list of expressions)
// @astgen op1 := exprp : AstNodeExpr // @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := namep : Optional[AstNodeExpr] // Runtime enum name lookup
VFormatAttr m_formatAttr; // How to format expression VFormatAttr m_formatAttr; // How to format expression
public: public:
@@ -2442,7 +2408,6 @@ public:
bool cleanOut() const override { return true; } bool cleanOut() const override { return true; }
const char* broken() const override { const char* broken() const override {
BROKEN_RTN(!VN_IS(backp(), SFormatF) && firstAbovep()); // In list under SFormatF BROKEN_RTN(!VN_IS(backp(), SFormatF) && firstAbovep()); // In list under SFormatF
BROKEN_RTN(formatAttr().isEnum() != static_cast<bool>(namep()));
return nullptr; return nullptr;
} }
VFormatAttr formatAttr() const { return m_formatAttr; } VFormatAttr formatAttr() const { return m_formatAttr; }
@@ -2648,7 +2613,7 @@ class AstScopeName final : public AstNodeExpr {
// For display %m and DPI context imports // For display %m and DPI context imports
// Parents: AstSFormatF, AstNodeFTaskRef, AstNodeFTask // Parents: AstSFormatF, AstNodeFTaskRef, AstNodeFTask
std::string m_scopeAttr; std::string m_scopeAttr;
std::string m_scopeEntr; // Scope path for the DPI import/export context name std::string m_scopeEntr;
bool m_dpiExport = false; // Is for dpiExport bool m_dpiExport = false; // Is for dpiExport
const bool m_forFormat; // Is for a format %m const bool m_forFormat; // Is for a format %m
static std::string scopeNameFormatter(const std::string& text); static std::string scopeNameFormatter(const std::string& text);
@@ -3070,8 +3035,6 @@ public:
addConstraintsp(constraintsp); addConstraintsp(constraintsp);
} }
ASTGEN_MEMBERS_AstWithParse; ASTGEN_MEMBERS_AstWithParse;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool sameNode(const AstNode* /*samep*/) const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool restricted() const { return m_restricted; } bool restricted() const { return m_restricted; }
void restricted(bool flag) { m_restricted = flag; } void restricted(bool flag) { m_restricted = flag; }
@@ -4948,6 +4911,9 @@ public:
bool isPredictOptimizable() const override { return true; } bool isPredictOptimizable() const override { return true; }
bool sameNode(const AstNode* /*samep*/) const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; }
int instrCount() const override { return widthInstrs(); } int instrCount() const override { return widthInstrs(); }
// Special operators
// Return base var (or const) nodep dereferences
static AstNode* baseFromp(AstNode* nodep, bool overMembers);
}; };
class AstAssocSel final : public AstNodeSel { class AstAssocSel final : public AstNodeSel {
void init(const AstNode* fromp) { void init(const AstNode* fromp) {
@@ -5200,8 +5166,6 @@ public:
"not coded to create after dtypes resolved"); "not coded to create after dtypes resolved");
} }
ASTGEN_MEMBERS_AstSelBit; ASTGEN_MEMBERS_AstSelBit;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
VAccess access() const { return m_access; } VAccess access() const { return m_access; }
void access(const VAccess& flag) { m_access = flag; } void access(const VAccess& flag) { m_access = flag; }
}; };
@@ -5466,7 +5430,6 @@ public:
enum FmtType : int { ATOI = 10, ATOHEX = 16, ATOOCT = 8, ATOBIN = 2, ATOREAL = -1 }; enum FmtType : int { ATOI = 10, ATOHEX = 16, ATOOCT = 8, ATOBIN = 2, ATOREAL = -1 };
private: private:
// dist-ast-dump-suppress // Part of name()
const FmtType m_fmtType; // Operation type const FmtType m_fmtType; // Operation type
public: public:
AstAtoN(FileLine* fl, AstNodeExpr* lhsp, FmtType fmtType) AstAtoN(FileLine* fl, AstNodeExpr* lhsp, FmtType fmtType)
@@ -6475,7 +6438,6 @@ class AstVarXRef final : public AstNodeVarRef {
string m_dotted; // Dotted part of scope the name()'ed reference is under or "" string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out string m_inlinedDots; // Dotted hierarchy flattened out
bool m_containsGenBlock = false; // Contains gen block reference bool m_containsGenBlock = false; // Contains gen block reference
bool m_readOnlyModport = false; // Linked via an input-only modport, until V3LinkLValue
public: public:
AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access) AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access)
: ASTGEN_SUPER_VarXRef(fl, nullptr, access) : ASTGEN_SUPER_VarXRef(fl, nullptr, access)
@@ -6493,8 +6455,6 @@ public:
void inlinedDots(const string& flag) { m_inlinedDots = flag; } void inlinedDots(const string& flag) { m_inlinedDots = flag; }
bool containsGenBlock() const { return m_containsGenBlock; } bool containsGenBlock() const { return m_containsGenBlock; }
void containsGenBlock(const bool flag) { m_containsGenBlock = flag; } void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
bool readOnlyModport() const { return m_readOnlyModport; }
void readOnlyModport(const bool flag) { m_readOnlyModport = flag; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; } bool cleanOut() const override { return true; }
+90 -273
View File
@@ -44,7 +44,6 @@ class AstNodeCoverDecl VL_NOT_FINAL : public AstNode {
// Coverage counters are emitted in each module object, so duplicate // Coverage counters are emitted in each module object, so duplicate
// no-inline instances can keep independent counts for forcePerInstance. // no-inline instances can keep independent counts for forcePerInstance.
int m_localBinNum = 0; // Per-module coverage bin offset int m_localBinNum = 0; // Per-module coverage bin offset
bool m_perInstance = false; // Don't clone during inlining, already per instance
public: public:
AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment) AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment)
: AstNode(t, fl) : AstNode(t, fl)
@@ -66,8 +65,6 @@ public:
void binNum(int flag) { m_binNum = flag; } void binNum(int flag) { m_binNum = flag; }
int localBinNum() const { return m_localBinNum; } int localBinNum() const { return m_localBinNum; }
void localBinNum(int flag) { m_localBinNum = flag; } void localBinNum(int flag) { m_localBinNum = flag; }
bool perInstance() const { return m_perInstance; }
void perInstance(bool flag) { m_perInstance = flag; }
virtual int size() const = 0; virtual int size() const = 0;
const string& comment() const { return m_text; } // text to insert in code const string& comment() const { return m_text; } // text to insert in code
const string& page() const { return m_page; } const string& page() const { return m_page; }
@@ -77,8 +74,7 @@ public:
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl); const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl);
return (fileline() == asamep->fileline() && hier() == asamep->hier() return (fileline() == asamep->fileline() && hier() == asamep->hier()
&& comment() == asamep->comment() && page() == asamep->page() && comment() == asamep->comment() && page() == asamep->page());
&& perInstance() == asamep->perInstance());
} }
bool isPredictOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; }
void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; } void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; }
@@ -98,7 +94,6 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
// @astgen op4 := scopeNamep : Optional[AstScopeName] // @astgen op4 := scopeNamep : Optional[AstScopeName]
string m_name; // Name of task string m_name; // Name of task
string m_cname; // Name of task if DPI import string m_cname; // Name of task if DPI import
// dist-ast-dump-suppress // Not dumped due to verbosity
string m_dpiCDecl; // Custom DPI-C function declaration string m_dpiCDecl; // Custom DPI-C function declaration
string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8) string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8)
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
@@ -117,8 +112,6 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
bool m_isHideLocal : 1; // Verilog local bool m_isHideLocal : 1; // Verilog local
bool m_isHideProtected : 1; // Verilog protected bool m_isHideProtected : 1; // Verilog protected
bool m_dpiPure : 1; // DPI import pure (vs. virtual pure) bool m_dpiPure : 1; // DPI import pure (vs. virtual pure)
bool m_keepAlive : 1; // Disable dead function elimination
bool m_needsSyms : 1; // Requires vlSymsp argument
bool m_pureVirtual : 1; // Pure virtual bool m_pureVirtual : 1; // Pure virtual
bool m_recursive : 1; // Recursive or part of recursion bool m_recursive : 1; // Recursive or part of recursion
bool m_static : 1; // Static method in class bool m_static : 1; // Static method in class
@@ -151,8 +144,6 @@ protected:
, m_isHideLocal{false} , m_isHideLocal{false}
, m_isHideProtected{false} , m_isHideProtected{false}
, m_dpiPure{false} , m_dpiPure{false}
, m_keepAlive{false}
, m_needsSyms{true}
, m_pureVirtual{false} , m_pureVirtual{false}
, m_recursive{false} , m_recursive{false}
, m_static{false} , m_static{false}
@@ -220,10 +211,6 @@ public:
void isHideProtected(bool flag) { m_isHideProtected = flag; } void isHideProtected(bool flag) { m_isHideProtected = flag; }
bool dpiPure() const { return m_dpiPure; } bool dpiPure() const { return m_dpiPure; }
void dpiPure(bool flag) { m_dpiPure = flag; } void dpiPure(bool flag) { m_dpiPure = flag; }
bool keepAlive() const { return m_keepAlive; }
void keepAlive(bool flag) { m_keepAlive = flag; }
bool needsSyms() const { return m_needsSyms; }
void needsSyms(bool flag) { m_needsSyms = flag; }
bool pureVirtual() const { return m_pureVirtual; } bool pureVirtual() const { return m_pureVirtual; }
void pureVirtual(bool flag) { m_pureVirtual = flag; } void pureVirtual(bool flag) { m_pureVirtual = flag; }
bool recursive() const { return m_recursive; } bool recursive() const { return m_recursive; }
@@ -306,8 +293,8 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
// @astgen op2 := stmtsp : List[AstNode] // @astgen op2 := stmtsp : List[AstNode]
string m_name; // Name of the module string m_name; // Name of the module
const string m_origName; // Name of the module, ignoring name() changes, for dot lookup const string m_origName; // Name of the module, ignoring name() changes, for dot lookup
// dist-ast-dump-suppress // For some user errors messages only, visible where used
string m_someInstanceName; // Hierarchical name of some arbitrary instance of this module. string m_someInstanceName; // Hierarchical name of some arbitrary instance of this module.
// Used for user messages only.
string m_libname; // Work library string m_libname; // Work library
int m_depth = 0; // 1=top module, 2=cell off top, shared things low, for -depth options int m_depth = 0; // 1=top module, 2=cell off top, shared things low, for -depth options
int m_level = 0; // 1=top module, 2=cell off top, shared things have high number int m_level = 0; // 1=top module, 2=cell off top, shared things have high number
@@ -325,6 +312,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
bool m_hasParameterList : 1; // Has #() for parameter declaration bool m_hasParameterList : 1; // Has #() for parameter declaration
bool m_hierBlock : 1; // Hierarchical Block marked by HIER_BLOCK pragma bool m_hierBlock : 1; // Hierarchical Block marked by HIER_BLOCK pragma
bool m_hierParams : 1; // Block containing params for parameterized hier blocks bool m_hierParams : 1; // Block containing params for parameterized hier blocks
bool m_internal : 1; // Internally created
bool m_recursive : 1; // Recursive module bool m_recursive : 1; // Recursive module
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
bool m_parameterizedTemplate : 1; // True when at least one specialized clone exists; bool m_parameterizedTemplate : 1; // True when at least one specialized clone exists;
@@ -346,6 +334,7 @@ protected:
, m_hasParameterList{false} , m_hasParameterList{false}
, m_hierBlock{false} , m_hierBlock{false}
, m_hierParams{false} , m_hierParams{false}
, m_internal{false}
, m_recursive{false} , m_recursive{false}
, m_recursiveClone{false} , m_recursiveClone{false}
, m_parameterizedTemplate{false} , m_parameterizedTemplate{false}
@@ -358,8 +347,6 @@ public:
bool maybePointedTo() const override VL_MT_SAFE { return true; } bool maybePointedTo() const override VL_MT_SAFE { return true; }
string name() const override VL_MT_STABLE { return m_name; } string name() const override VL_MT_STABLE { return m_name; }
virtual bool timescaleMatters() const = 0; virtual bool timescaleMatters() const = 0;
inline bool isDollarUnit() const; // Is the $unit package
inline bool isConstPool() const; // Is the constant pool package
// ACCESSORS // ACCESSORS
void name(const string& name) override { m_name = name; } void name(const string& name) override { m_name = name; }
string origName() const override { return m_origName; } string origName() const override { return m_origName; }
@@ -391,6 +378,8 @@ public:
void hierBlock(bool flag) { m_hierBlock = flag; } void hierBlock(bool flag) { m_hierBlock = flag; }
bool hierParams() const { return m_hierParams; } bool hierParams() const { return m_hierParams; }
void hierParams(bool flag) { m_hierParams = flag; } void hierParams(bool flag) { m_hierParams = flag; }
bool internal() const { return m_internal; }
void internal(bool flag) { m_internal = flag; }
bool recursive() const { return m_recursive; } bool recursive() const { return m_recursive; }
void recursive(bool flag) { m_recursive = flag; } void recursive(bool flag) { m_recursive = flag; }
void recursiveClone(bool flag) { m_recursiveClone = flag; } void recursiveClone(bool flag) { m_recursiveClone = flag; }
@@ -554,7 +543,6 @@ class AstCFunc final : public AstNode {
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
bool m_needProcess : 1; // Needs access to VlProcess of the caller bool m_needProcess : 1; // Needs access to VlProcess of the caller
bool m_recursive : 1; // Recursive or part of recursion bool m_recursive : 1; // Recursive or part of recursion
bool m_unlikely : 1; // Unlikely to get called (though still optimize unlike slow())
bool m_noLife : 1; // Disable V3Life on this function - has multiple calls, and reads Syms bool m_noLife : 1; // Disable V3Life on this function - has multiple calls, and reads Syms
// state // state
bool m_isCovergroupSample : 1; // Automatic covergroup sample() function bool m_isCovergroupSample : 1; // Automatic covergroup sample() function
@@ -587,7 +575,6 @@ public:
m_dpiImportPrototype = false; m_dpiImportPrototype = false;
m_dpiImportWrapper = false; m_dpiImportWrapper = false;
m_recursive = false; m_recursive = false;
m_unlikely = false;
m_noLife = false; m_noLife = false;
m_isCovergroupSample = false; m_isCovergroupSample = false;
m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost
@@ -666,8 +653,6 @@ public:
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; } bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
void recursive(bool flag) { m_recursive = flag; } void recursive(bool flag) { m_recursive = flag; }
bool recursive() const { return m_recursive; } bool recursive() const { return m_recursive; }
void unlikely(bool flag) { m_unlikely = flag; }
bool isUnlikely() const override { return m_unlikely; } // Note virtual override
void noLife(bool flag) { m_noLife = flag; } void noLife(bool flag) { m_noLife = flag; }
bool noLife() const { return m_noLife; } bool noLife() const { return m_noLife; }
bool isCovergroupSample() const { return m_isCovergroupSample; } bool isCovergroupSample() const { return m_isCovergroupSample; }
@@ -700,18 +685,15 @@ class AstCell final : public AstNode {
// A instantiation cell or interface call (don't know which until link) // A instantiation cell or interface call (don't know which until link)
// @astgen op1 := pinsp : List[AstPin] // List of port assignments // @astgen op1 := pinsp : List[AstPin] // List of port assignments
// @astgen op2 := paramsp : List[AstPin] // List of parameter assignments // @astgen op2 := paramsp : List[AstPin] // List of parameter assignments
// Pre V3Param: Range(s) for arrayed instances. // @astgen op3 := rangep : List[AstRange] // Range(s) for arrayed instances; multi-dim chains
// From V3Param to V3Width: ranges of the arrayed cell this element cell was expanded from // via nextp()
// After V3Width: nullptr // @astgen op4 := intfRefsp : List[AstIntfRef] // List of interface references, for tracing
// @astgen op3 := rangep : List[AstRange]
// @astgen op4 := intfRefsp : List[AstIntfRef] // List of interface references, for tracing/VPI
// //
// @astgen ptr := m_modp : Optional[AstNodeModule] // [AfterLink] Pointer to module instanced // @astgen ptr := m_modp : Optional[AstNodeModule] // [AfterLink] Pointer to module instanced
FileLine* m_modNameFileline; // Where module the cell instances token was FileLine* m_modNameFileline; // Where module the cell instances token was
string m_name; // Cell name string m_name; // Cell name
string m_origName; // Original name before dot addition string m_origName; // Original name before dot addition
string m_modName; // Module the cell instances string m_modName; // Module the cell instances
int m_arrayIdx = -1; // Row-major position in instance array, from the left (V3Param-V3Width)
bool m_hasIfaceVar : 1; // True if a Var has been created for this cell bool m_hasIfaceVar : 1; // True if a Var has been created for this cell
bool m_recursive : 1; // Self-recursive module bool m_recursive : 1; // Self-recursive module
bool m_trace : 1; // Trace this cell bool m_trace : 1; // Trace this cell
@@ -746,8 +728,6 @@ public:
FileLine* modNameFileline() const { return m_modNameFileline; } FileLine* modNameFileline() const { return m_modNameFileline; }
AstNodeModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated AstNodeModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated
void modp(AstNodeModule* nodep) { m_modp = nodep; } void modp(AstNodeModule* nodep) { m_modp = nodep; }
int arrayIdx() const { return m_arrayIdx; }
void arrayIdx(int idx) { m_arrayIdx = idx; }
bool hasIfaceVar() const { return m_hasIfaceVar; } bool hasIfaceVar() const { return m_hasIfaceVar; }
void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; } void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; }
void trace(bool flag) { m_trace = flag; } void trace(bool flag) { m_trace = flag; }
@@ -801,9 +781,6 @@ public:
string name() const override VL_MT_STABLE { return m_cellp->name(); } string name() const override VL_MT_STABLE { return m_cellp->name(); }
bool maybePointedTo() const override VL_MT_SAFE { return true; } bool maybePointedTo() const override VL_MT_SAFE { return true; }
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
void scopep(AstScope* nodep) { m_scopep = nodep; }
AstCellInline* cellp() const VL_MT_STABLE { return m_cellp; } // Pointer to the CellInline
void cellp(AstCellInline* nodep) { m_cellp = nodep; }
string origModName() const { string origModName() const {
return m_cellp->origModName(); return m_cellp->origModName();
} // * = modp()->origName() before inlining } // * = modp()->origName() before inlining
@@ -912,8 +889,6 @@ public:
} }
} }
ASTGEN_MEMBERS_AstClockingItem; ASTGEN_MEMBERS_AstClockingItem;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VDirection direction() const { return m_direction; } VDirection direction() const { return m_direction; }
AstClockingItem* outputp() const { return m_outputp; } AstClockingItem* outputp() const { return m_outputp; }
void outputp(AstClockingItem* outputp) { m_outputp = outputp; } void outputp(AstClockingItem* outputp) { m_outputp = outputp; }
@@ -932,11 +907,11 @@ public:
, m_libname{libname} , m_libname{libname}
, m_configname{cellname} {} , m_configname{cellname} {}
ASTGEN_MEMBERS_AstConfig; ASTGEN_MEMBERS_AstConfig;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
std::string name() const override VL_MT_STABLE { return m_libname + "." + m_configname; } std::string name() const override VL_MT_STABLE { return m_libname + "." + m_configname; }
std::string libname() const VL_MT_STABLE { return m_libname; } std::string libname() const VL_MT_STABLE { return m_libname; }
std::string configname() const VL_MT_STABLE { return m_configname; } std::string configname() const VL_MT_STABLE { return m_configname; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
}; };
class AstConfigCell final : public AstNode { class AstConfigCell final : public AstNode {
// Parents: CONFIGRULE // Parents: CONFIGRULE
@@ -995,6 +970,40 @@ public:
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
}; };
class AstConstPool final : public AstNode {
// Container for const static data
// @astgen op1 := modulep : AstModule // m_modp below TODO: fix this mess
//
// @astgen ptr := m_modp : AstModule // The Module holding the Scope below ...
// @astgen ptr := m_scopep : AstScope // Scope holding the constant variables
std::unordered_multimap<uint32_t, AstVarScope*> m_tables; // Constant tables (unpacked arrays)
std::unordered_multimap<uint32_t, AstVarScope*> m_consts; // Constant tables (scalars)
AstVarScope* createNewEntry(const string& name, AstNodeExpr* initp);
public:
explicit AstConstPool(FileLine* fl);
ASTGEN_MEMBERS_AstConstPool;
bool maybePointedTo() const override VL_MT_SAFE { return true; }
void cloneRelink() override { V3ERROR_NA; } // Not cloneable
AstModule* modp() const { return m_modp; }
AstScope* scopep() const { return m_scopep; }
// Find a table (unpacked array) within the constant pool which is initialized with the
// given value, or create one if one does not already exists. The returned VarScope *might*
// have a different dtype than the given initp->dtypep(), including a different element type,
// but it will always have the same size and element width. In contexts where this matters,
// the caller must handle the dtype difference as appropriate.
AstVarScope* findTable(AstInitArray* initp);
// Find a constant within the constant pool which is initialized with the given value, or
// create one if one does not already exists. If 'mergeDType' is true, then the returned
// VarScope *might* have a different type than the given initp->dtypep(). In contexts where
// 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 { class AstConstraint final : public AstNode {
// Constraint // Constraint
// @astgen op1 := itemsp : List[AstNode] // @astgen op1 := itemsp : List[AstNode]
@@ -1042,13 +1051,12 @@ class AstCoverBin final : public AstNode {
// @astgen op4 := transp : List[AstCoverTransSet] // @astgen op4 := transp : List[AstCoverTransSet]
const string m_name; // Base name of the bin const string m_name; // Base name of the bin
const VCoverBinsType m_binsType; // Bin type (eg AUTO, IGNORE, ILLEGAL) const VCoverBinsType m_binsType; // Bin type (eg AUTO, IGNORE, ILLEGAL)
bool m_isArray = false; // Bin is an array: of a bin per value or transition, or of bool m_isArray = false; // Bin is either an auto-sized array of values or transitions
// arraySizep() bins
bool m_isWildcard = false; // Bin uses wildcard matching (independent of ignore/illegal) bool m_isWildcard = false; // Bin uses wildcard matching (independent of ignore/illegal)
public: public:
AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal, AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal,
bool isWildcard = false, AstNodeExpr* iffp = nullptr) bool isWildcard = false)
: ASTGEN_SUPER_CoverBin(fl) : ASTGEN_SUPER_CoverBin(fl)
, m_name{name} , m_name{name}
, m_binsType{isIllegal ? VCoverBinsType::BINS_ILLEGAL , m_binsType{isIllegal ? VCoverBinsType::BINS_ILLEGAL
@@ -1057,14 +1065,12 @@ public:
: VCoverBinsType::BINS_USER))} : VCoverBinsType::BINS_USER))}
, m_isWildcard{isWildcard} { , m_isWildcard{isWildcard} {
addRangesp(rangesp); addRangesp(rangesp);
this->iffp(iffp);
} }
// Constructor for automatic bins // Constructor for automatic bins
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep, AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
VCoverBinsType type = VCoverBinsType::BINS_AUTO)
: ASTGEN_SUPER_CoverBin(fl) : ASTGEN_SUPER_CoverBin(fl)
, m_name{name} , m_name{name}
, m_binsType{type} , m_binsType{VCoverBinsType::BINS_AUTO}
, m_isArray{true} { , m_isArray{true} {
this->arraySizep(arraySizep); this->arraySizep(arraySizep);
} }
@@ -1092,138 +1098,21 @@ public:
bool isArray() const { return m_isArray; } bool isArray() const { return m_isArray; }
void isArray(bool flag) { m_isArray = flag; } void isArray(bool flag) { m_isArray = flag; }
}; };
class AstCoverBinsof final : public AstNode {
// A binsof selection of a coverpoint or one of its named bins
// @astgen op1 := pointp : AstCoverpointRef
// @astgen op2 := rangesp : List[AstNode] // Optional intersect value ranges
string m_name; // Selected bin name, or empty for all bins of the coverpoint
const bool m_isNegated; // Complement the selection within the cross product
public:
AstCoverBinsof(FileLine* fl, AstCoverpointRef* pointp, bool isNegated = false,
AstNode* rangesp = nullptr)
: ASTGEN_SUPER_CoverBinsof(fl)
, m_isNegated{isNegated} {
this->pointp(pointp);
addRangesp(rangesp);
}
ASTGEN_MEMBERS_AstCoverBinsof;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = name; }
bool isNegated() const { return m_isNegated; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
const AstCoverBinsof* const asamep = VN_DBG_AS(samep, CoverBinsof);
return m_name == asamep->m_name && m_isNegated == asamep->m_isNegated;
} // LCOV_EXCL_STOP
};
class AstCoverCrossBin final : public AstNode {
// A named cross bin and its selection expression
// @astgen op1 := selectp : Optional[AstNode] // Null for unsupported selections
// @astgen op2 := iffp : Optional[AstNodeExpr]
const string m_name; // Declared cross bin name
// dist-ast-dump-suppress // Bin kind is shown by verilogKwd() in emitted Verilog.
const VCoverBinsType m_binsType; // Normal, ignore, or illegal bin
public:
AstCoverCrossBin(FileLine* fl, const string& name, AstNode* selectp, AstNodeExpr* iffp,
VCoverBinsType binsType = VCoverBinsType::BINS_USER)
: ASTGEN_SUPER_CoverCrossBin(fl)
, m_name{name}
, m_binsType{binsType} {
this->selectp(selectp);
this->iffp(iffp);
}
ASTGEN_MEMBERS_AstCoverCrossBin;
string name() const override VL_MT_STABLE { return m_name; }
string verilogKwd() const override;
VCoverBinsType binsType() const { return m_binsType; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
const AstCoverCrossBin* const asamep = VN_DBG_AS(samep, CoverCrossBin);
return m_name == asamep->m_name && m_binsType.m_e == asamep->m_binsType.m_e;
} // LCOV_EXCL_STOP
};
class AstCoverCrossRef final : public AstNode {
// The complete bin-tuple space of the enclosing cross
const string m_name; // Referenced cross name
public:
AstCoverCrossRef(FileLine* fl, const string& name)
: ASTGEN_SUPER_CoverCrossRef(fl)
, m_name{name} {}
ASTGEN_MEMBERS_AstCoverCrossRef;
string name() const override VL_MT_STABLE { return m_name; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
return m_name == VN_DBG_AS(samep, CoverCrossRef)->m_name;
} // LCOV_EXCL_STOP
};
class AstCoverCrossSelect final : public AstNode {
// Intersection or union of two cross-bin selections
// @astgen op1 := lhsp : Optional[AstNode] // Null for an unsupported selection
// @astgen op2 := rhsp : Optional[AstNode] // Null for an unsupported selection
const bool m_isOr; // Union (||), rather than intersection (&&)
public:
AstCoverCrossSelect(FileLine* fl, AstNode* lhsp, AstNode* rhsp, bool isOr)
: ASTGEN_SUPER_CoverCrossSelect(fl)
, m_isOr{isOr} {
this->lhsp(lhsp);
this->rhsp(rhsp);
}
ASTGEN_MEMBERS_AstCoverCrossSelect;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool isOr() const { return m_isOr; }
string verilogKwd() const override { return isOr() ? "||" : "&&"; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
return m_isOr == VN_DBG_AS(samep, CoverCrossSelect)->m_isOr;
} // LCOV_EXCL_STOP
};
class AstCoverOption final : public AstNode { class AstCoverOption final : public AstNode {
// Coverage-option assignment // Coverage-option assignment
// @astgen op1 := valuep : AstNodeExpr // @astgen op1 := valuep : AstNodeExpr
const VCoverOptionType m_optType; // Option being assigned const VCoverOptionType m_optType; // Option being assigned
const bool m_typeOption; // type_option (vs option)
public: public:
AstCoverOption(FileLine* fl, bool typeOption, VCoverOptionType optType, AstNodeExpr* valuep) AstCoverOption(FileLine* fl, VCoverOptionType optType, AstNodeExpr* valuep)
: ASTGEN_SUPER_CoverOption(fl) : ASTGEN_SUPER_CoverOption(fl)
, m_optType{optType} , m_optType{optType} {
, m_typeOption{typeOption} {
this->valuep(valuep); this->valuep(valuep);
} }
ASTGEN_MEMBERS_AstCoverOption; ASTGEN_MEMBERS_AstCoverOption;
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
VCoverOptionType optType() const { return m_optType; } VCoverOptionType optType() const { return m_optType; }
bool typeOption() const { return m_typeOption; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
const AstCoverOption* const asamep = VN_DBG_AS(samep, CoverOption);
return m_optType.m_e == asamep->m_optType.m_e && m_typeOption == asamep->m_typeOption;
} // LCOV_EXCL_STOP
};
class AstCoverToggle final : public AstNode {
// Toggle analysis of given signal
// Parents: MODULE
// @astgen op1 := incp : AstCoverInc
// @astgen op2 := origp : AstNodeExpr
// @astgen op3 := changep : AstNodeExpr
public:
AstCoverToggle(FileLine* fl, AstCoverInc* incp, AstNodeExpr* origp, AstNodeExpr* changep)
: ASTGEN_SUPER_CoverToggle(fl) {
this->incp(incp);
this->origp(origp);
this->changep(changep);
}
ASTGEN_MEMBERS_AstCoverToggle;
int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return true; }
bool isOutputter() override {
return false; // Though the AstCoverInc under this is an outputter
}
}; };
class AstCoverTransItem final : public AstNode { class AstCoverTransItem final : public AstNode {
// Represents a single transition item: value or value[*N] or value[->N] or value[=N] // Represents a single transition item: value or value[*N] or value[->N] or value[=N]
@@ -1254,20 +1143,6 @@ public:
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
}; };
class AstCoverWith final : public AstNode {
// The values of a coverpoint bin for which a 'with' filter is true (IEEE 1800-2023
// 19.5.1.1), the only range list element of its AstCoverBin
// @astgen op1 := subp : List[AstNode] // Range list, or AstCoverpointRef for all values
// @astgen op2 := filterp : AstNodeExpr
// @astgen op3 := itemp : Optional[AstVar] // Candidate value 'item', from V3LinkParse
public:
AstCoverWith(FileLine* fl, AstNode* subp, AstNodeExpr* filterp)
: ASTGEN_SUPER_CoverWith(fl) {
addSubp(subp);
this->filterp(filterp);
}
ASTGEN_MEMBERS_AstCoverWith;
};
class AstCovergroup final : public AstNode { class AstCovergroup final : public AstNode {
// Represents a covergroup declaration. V3LinkParse transforms this // Represents a covergroup declaration. V3LinkParse transforms this
// into an AstClass with isCovergroup==true and attaches the clocking // into an AstClass with isCovergroup==true and attaches the clocking
@@ -1383,8 +1258,6 @@ public:
BROKEN_RTN(!fmtp()); BROKEN_RTN(!fmtp());
return nullptr; return nullptr;
} }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
string verilogKwd() const override { return "$"s + string{displayType().ascii()}; } string verilogKwd() const override { return "$"s + string{displayType().ascii()}; }
bool isGateOptimizable() const override { return false; } bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; }
@@ -1447,21 +1320,13 @@ public:
}; };
class AstIntfRef final : public AstNode { class AstIntfRef final : public AstNode {
// An interface reference // An interface reference
string m_name; // Hierarchical path of the reference string m_name; // Name of the reference
string m_baseName; // Final component of m_name, i.e. the reference port name
string m_modportName; // "" = no modport, else name of the modport referenced
public: public:
AstIntfRef(FileLine* fl, const string& name, const string& baseName, const string& modportName) AstIntfRef(FileLine* fl, const string& name)
: ASTGEN_SUPER_IntfRef(fl) : ASTGEN_SUPER_IntfRef(fl)
, m_name{name} , m_name{name} {}
, m_baseName{baseName}
, m_modportName{modportName} {}
ASTGEN_MEMBERS_AstIntfRef;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
string name() const override VL_MT_STABLE { return m_name; } string name() const override VL_MT_STABLE { return m_name; }
string baseName() const { return m_baseName; } ASTGEN_MEMBERS_AstIntfRef;
string modportName() const { return m_modportName; }
}; };
class AstLibrary final : public AstNode { class AstLibrary final : public AstNode {
// Parents: NETLIST // Parents: NETLIST
@@ -1575,7 +1440,7 @@ class AstNetlist final : public AstNode {
// @astgen op3 := miscsp : List[AstNode] // @astgen op3 := miscsp : List[AstNode]
// //
// @astgen ptr := m_typeTablep : AstTypeTable // Reference to type table, for faster lookup // @astgen ptr := m_typeTablep : AstTypeTable // Reference to type table, for faster lookup
// @astgen ptr := m_constPoolPkgp : AstPackage // Constant pool package // @astgen ptr := m_constPoolp : AstConstPool // Reference to constant pool, for faster lookup
// @astgen ptr := m_dollarUnitPkgp : Optional[AstPackage] // $unit // @astgen ptr := m_dollarUnitPkgp : Optional[AstPackage] // $unit
// @astgen ptr := m_stdPackagep : Optional[AstPackage] // SystemVerilog std package // @astgen ptr := m_stdPackagep : Optional[AstPackage] // SystemVerilog std package
// @astgen ptr := m_stdPackageProcessp : Optional[AstClass] // SystemVerilog std process class // @astgen ptr := m_stdPackageProcessp : Optional[AstClass] // SystemVerilog std process class
@@ -1596,8 +1461,6 @@ class AstNetlist final : public AstNode {
// AstConst itself, as AstConst is a very common node and only a small fraction carry this // AstConst itself, as AstConst is a very common node and only a small fraction carry this
// name. // name.
std::unordered_map<const AstConst*, string> m_constOrigParamNames; std::unordered_map<const AstConst*, string> m_constOrigParamNames;
// Module each node is in, only good while the tree holds still
std::unordered_map<const AstNode*, const AstNodeModule*> m_containingModules;
// The model's evaluation entry point functions // The model's evaluation entry point functions
std::array<AstCFunc*, VEval::_ENUM_END> m_evalFuncps{}; std::array<AstCFunc*, VEval::_ENUM_END> m_evalFuncps{};
// The trigger dump function of each region if exists, otherwise nullptr // The trigger dump function of each region if exists, otherwise nullptr
@@ -1619,16 +1482,12 @@ public:
return modulesp(); // First one in the list, for now return modulesp(); // First one in the list, for now
} }
AstTypeTable* typeTablep() { return m_typeTablep; } AstTypeTable* typeTablep() { return m_typeTablep; }
AstConstPool* constPoolp() { return m_constPoolp; }
string astConstOrigParamName(const AstConst* nodep) const; string astConstOrigParamName(const AstConst* nodep) const;
void astConstOrigParamName(const AstConst* nodep, const string& name); void astConstOrigParamName(const AstConst* nodep, const string& name);
void astConstOrigParamNameErase(const AstConst* nodep); void astConstOrigParamNameErase(const AstConst* nodep);
// Find the module a node is in, remembering what it passed on the way.
const AstNodeModule* containingModule(const AstNode* nodep);
// Forget remembered modules, as the tree has moved.
void clearContainingModules() { m_containingModules.clear(); }
AstPackage* constPoolPkgp() const { return m_constPoolPkgp; }
AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; } AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; }
void dollarUnitPkgp(AstPackage* const packagep) { m_dollarUnitPkgp = packagep; } AstPackage* dollarUnitPkgAddp();
AstCFunc* evalFuncp(VEval eval) const { return m_evalFuncps[eval]; } AstCFunc* evalFuncp(VEval eval) const { return m_evalFuncps[eval]; }
void evalFuncp(VEval eval, AstCFunc* funcp) { m_evalFuncps[eval] = funcp; } void evalFuncp(VEval eval, AstCFunc* funcp) { m_evalFuncps[eval] = funcp; }
AstCFunc* dumpTriggersFuncp(VEval eval) const { return m_dumpTriggersFuncps[eval]; } AstCFunc* dumpTriggersFuncp(VEval eval) const { return m_dumpTriggersFuncps[eval]; }
@@ -1892,7 +1751,6 @@ public:
bool maybePointedTo() const override VL_MT_SAFE { return true; } bool maybePointedTo() const override VL_MT_SAFE { return true; }
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
void name(const string& name) override { m_name = name; } void name(const string& name) override { m_name = name; }
string prettyNameMsg() const override;
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
bool sameNode(const AstNode* samep) const override; bool sameNode(const AstNode* samep) const override;
@@ -1900,9 +1758,7 @@ public:
AstNodeModule* modp() const { return m_modp; } AstNodeModule* modp() const { return m_modp; }
// //
AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; } AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; }
void aboveScopep(AstScope* nodep) { m_aboveScopep = nodep; }
AstCell* aboveCellp() const { return m_aboveCellp; } AstCell* aboveCellp() const { return m_aboveCellp; }
void aboveCellp(AstCell* nodep) { m_aboveCellp = nodep; }
bool isTop() const VL_MT_SAFE { return aboveScopep() == nullptr; } // At top of hierarchy bool isTop() const VL_MT_SAFE { return aboveScopep() == nullptr; } // At top of hierarchy
// Create new MODULETEMP variable under this scope // Create new MODULETEMP variable under this scope
AstVarScope* createTemp(const string& name, unsigned width); AstVarScope* createTemp(const string& name, unsigned width);
@@ -2059,11 +1915,8 @@ public:
class AstTextBlock final : public AstNode { class AstTextBlock final : public AstNode {
// Text block emitted into output, with some arbitrary nodes interspersed // Text block emitted into output, with some arbitrary nodes interspersed
// @astgen op1 := nodesp : List[AstNode] // Nodes to print // @astgen op1 := nodesp : List[AstNode] // Nodes to print
// dist-ast-dump-suppress // Omitting text blocks due to verbosity
const std::string m_prefix; // Prefix to print before first element in 'nodesp' const std::string m_prefix; // Prefix to print before first element in 'nodesp'
// dist-ast-dump-suppress // Omitting text blocks due to verbosity
const std::string m_separator; // Separator to print between each element in 'nodesp' const std::string m_separator; // Separator to print between each element in 'nodesp'
// dist-ast-dump-suppress // Omitting text blocks due to verbosity
const std::string m_suffix; // Suffix to pring after last element in 'nodesp' const std::string m_suffix; // Suffix to pring after last element in 'nodesp'
public: public:
explicit AstTextBlock(FileLine* fl, // explicit AstTextBlock(FileLine* fl, //
@@ -2112,7 +1965,6 @@ class AstTypeTable final : public AstNode {
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{}; AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
// //
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>; using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
// dist-ast-dump-suppress // Link to other nodes
DetailedMap m_detailedMap; DetailedMap m_detailedMap;
public: public:
@@ -2141,10 +1993,10 @@ class AstTypedef final : public AstNode {
// @astgen op1 := childDTypep : Optional[AstNodeDType] // @astgen op1 := childDTypep : Optional[AstNodeDType]
// @astgen op4 := attrsp : List[AstNode] // Attributes during early parse // @astgen op4 := attrsp : List[AstNode] // Attributes during early parse
string m_name; // Name of the typedef string m_name;
string m_tag; // Holds the string of the verilator tag -- used in JSON output. string m_tag; // Holds the string of the verilator tag -- used in JSON output.
uint32_t m_declTokenNum; // Declaration token number uint32_t m_declTokenNum; // Declaration token number
bool m_attrPublic = false; // Marked with public; keep even if unused bool m_attrPublic = false;
bool m_isHideLocal : 1; // Verilog local bool m_isHideLocal : 1; // Verilog local
bool m_isHideProtected : 1; // Verilog protected bool m_isHideProtected : 1; // Verilog protected
bool m_isUnderClass : 1; // Underneath class bool m_isUnderClass : 1; // Underneath class
@@ -2174,9 +2026,6 @@ public:
ASTGEN_MEMBERS_AstTypedef; ASTGEN_MEMBERS_AstTypedef;
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
// Name of the type it declares, with the scope declaring it, so with the values of the
// parameters of the scope, e.g. '$unit::Cls#(8)::s_t'
string dtypeName() const;
AstNodeDType* getChildDTypep() const override { return childDTypep(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); }
virtual AstNodeDType* subDTypep() const VL_MT_STABLE { virtual AstNodeDType* subDTypep() const VL_MT_STABLE {
return dtypep() ? dtypep() : childDTypep(); return dtypep() ? dtypep() : childDTypep();
@@ -2313,11 +2162,11 @@ class AstVar final : public AstNode {
bool m_attrSFormat : 1; // User sformat attribute bool m_attrSFormat : 1; // User sformat attribute
bool m_attrSplitVar : 1; // declared with split_var metacomment bool m_attrSplitVar : 1; // declared with split_var metacomment
bool m_attrFsmState : 1; // declared with fsm_state metacomment bool m_attrFsmState : 1; // declared with fsm_state metacomment
bool m_attrFsmRegisterWrapper : 1; // connected to an fsm_register_wrapper instance
bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment
bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment
bool m_constPoolEntry : 1; // Constant pool variable bool m_constPoolEntry : 1; // Constant pool variable
bool m_covergroupRefMember : 1; // Persistent covergroup ref/const ref argument bool m_covergroupRefMember : 1; // Persistent covergroup ref/const ref argument
bool m_embeddedCovergroup : 1; // Instance variable an embedded covergroup declares
bool m_attrFileDescr : 1; // File descriptor bool m_attrFileDescr : 1; // File descriptor
bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration
bool m_icoMaybeWritten : 1; // Design might write this input signal - for ico change detect bool m_icoMaybeWritten : 1; // Design might write this input signal - for ico change detect
@@ -2325,11 +2174,10 @@ class AstVar final : public AstNode {
bool m_isContinuously : 1; // Ever assigned continuously (for force/release) bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
bool m_hasUserInit : 1; // Has initial assignment by user at parse time bool m_hasUserInit : 1; // Has initial assignment by user at parse time
bool m_isStatic : 1; // Static C variable (for Verilog see instead lifetime())
bool m_isPulldown : 1; // Tri0 bool m_isPulldown : 1; // Tri0
bool m_isPullup : 1; // Tri1 bool m_isPullup : 1; // Tri1
bool m_isIfaceParent : 1; // dtype is reference to interface present in this module bool m_isIfaceParent : 1; // dtype is reference to interface present in this module
bool m_isIfaceArraySplit : 1; // Interface array that was split into element variables by
// V3Param, until V3Width removes it
bool m_isInternal : 1; // Internal state, don't add to method pinter bool m_isInternal : 1; // Internal state, don't add to method pinter
bool m_isIfaceParam : 1; // Parameter belongs to an interface/modport bool m_isIfaceParam : 1; // Parameter belongs to an interface/modport
bool m_isDpiOpenArray : 1; // DPI import open array bool m_isDpiOpenArray : 1; // DPI import open array
@@ -2379,11 +2227,11 @@ class AstVar final : public AstNode {
m_attrSFormat = false; m_attrSFormat = false;
m_attrSplitVar = false; m_attrSplitVar = false;
m_attrFsmState = false; m_attrFsmState = false;
m_attrFsmRegisterWrapper = false;
m_attrFsmResetArc = false; m_attrFsmResetArc = false;
m_attrFsmArcInclCond = false; m_attrFsmArcInclCond = false;
m_constPoolEntry = false; m_constPoolEntry = false;
m_covergroupRefMember = false; m_covergroupRefMember = false;
m_embeddedCovergroup = false;
m_attrFileDescr = false; m_attrFileDescr = false;
m_gotNansiType = false; m_gotNansiType = false;
m_icoMaybeWritten = false; m_icoMaybeWritten = false;
@@ -2391,10 +2239,10 @@ class AstVar final : public AstNode {
m_isContinuously = false; m_isContinuously = false;
m_hasStrengthAssignment = false; m_hasStrengthAssignment = false;
m_hasUserInit = false; m_hasUserInit = false;
m_isStatic = false;
m_isPulldown = false; m_isPulldown = false;
m_isPullup = false; m_isPullup = false;
m_isIfaceParent = false; m_isIfaceParent = false;
m_isIfaceArraySplit = false;
m_isInternal = false; m_isInternal = false;
m_isIfaceParam = false; m_isIfaceParam = false;
m_isDpiOpenArray = false; m_isDpiOpenArray = false;
@@ -2507,10 +2355,10 @@ public:
string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument
string dpiTmpVarType(const string& varName) const; string dpiTmpVarType(const string& varName) const;
// Return Verilator internal type for argument: CData, SData, IData, WData // Return Verilator internal type for argument: CData, SData, IData, WData
string vlArgType(bool named, bool forReturn, bool forFunc, bool asRef = false, string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
bool constRef = false) const; bool asRef = false, bool constRef = false) const;
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
string vlEnumDir(bool forMember = false) const; // Return VerilatorVarDir: VLVD_INOUT, etc string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
void combineType(VVarType type); void combineType(VVarType type);
AstNodeDType* getChildDTypep() const override { return childDTypep(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); }
@@ -2530,16 +2378,13 @@ public:
void attrSFormat(bool flag) { m_attrSFormat = flag; } void attrSFormat(bool flag) { m_attrSFormat = flag; }
void attrSplitVar(bool flag) { m_attrSplitVar = flag; } void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
void attrFsmState(bool flag) { m_attrFsmState = flag; } void attrFsmState(bool flag) { m_attrFsmState = flag; }
void attrFsmRegisterWrapper(bool flag) { m_attrFsmRegisterWrapper = flag; }
void attrFsmResetArc(bool flag) { m_attrFsmResetArc = flag; } void attrFsmResetArc(bool flag) { m_attrFsmResetArc = flag; }
void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; } void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; }
bool constPoolEntry() const { return m_constPoolEntry; } bool constPoolEntry() const { return m_constPoolEntry; }
void setConstPoolEntry() { m_constPoolEntry = true; } void setConstPoolEntry() { m_constPoolEntry = true; }
bool covergroupRefMember() const { return m_covergroupRefMember; } bool covergroupRefMember() const { return m_covergroupRefMember; }
void covergroupRefMember(bool flag) { m_covergroupRefMember = flag; } void covergroupRefMember(bool flag) { m_covergroupRefMember = flag; }
// Instance variable an embedded covergroup declares, of the covergroup's anonymous type
// (IEEE 1800-2023 19.4)
bool embeddedCovergroup() const { return m_embeddedCovergroup; }
void embeddedCovergroup(bool flag) { m_embeddedCovergroup = flag; }
void rand(const VRandAttr flag) { m_rand = flag; } void rand(const VRandAttr flag) { m_rand = flag; }
void usedParam(bool flag) { m_usedParam = flag; } void usedParam(bool flag) { m_usedParam = flag; }
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; } void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
@@ -2558,8 +2403,8 @@ public:
void primaryIO(bool flag) { m_primaryIO = flag; } void primaryIO(bool flag) { m_primaryIO = flag; }
void isConst(bool flag) { m_isConst = flag; } void isConst(bool flag) { m_isConst = flag; }
void isContinuously(bool flag) { m_isContinuously = flag; } void isContinuously(bool flag) { m_isContinuously = flag; }
void isStatic(bool flag) { m_isStatic = flag; }
void isIfaceParent(bool flag) { m_isIfaceParent = flag; } void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
void isIfaceArraySplit(bool flag) { m_isIfaceArraySplit = flag; }
void isInternal(bool flag) { m_isInternal = flag; } void isInternal(bool flag) { m_isInternal = flag; }
void isIfaceParam(bool flag) { m_isIfaceParam = flag; } void isIfaceParam(bool flag) { m_isIfaceParam = flag; }
void funcLocal(bool flag) { void funcLocal(bool flag) {
@@ -2647,7 +2492,6 @@ public:
m_varType = VVarType::IFACEREF; m_varType = VVarType::IFACEREF;
} }
bool isIfaceParent() const { return m_isIfaceParent; } bool isIfaceParent() const { return m_isIfaceParent; }
bool isIfaceArraySplit() const { return m_isIfaceArraySplit; }
bool isInternal() const { return m_isInternal; } bool isInternal() const { return m_isInternal; }
bool isIfaceParam() const { return m_isIfaceParam; } bool isIfaceParam() const { return m_isIfaceParam; }
bool isSignal() const { return varType().isSignal(); } bool isSignal() const { return varType().isSignal(); }
@@ -2690,6 +2534,7 @@ public:
bool isRand() const { return m_rand.isRand(); } bool isRand() const { return m_rand.isRand(); }
bool isRandC() const { return m_rand.isRandC(); } bool isRandC() const { return m_rand.isRandC(); }
bool isConst() const VL_MT_SAFE { return m_isConst; } bool isConst() const VL_MT_SAFE { return m_isConst; }
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
bool isLatched() const { return m_isLatched; } bool isLatched() const { return m_isLatched; }
bool isFuncLocal() const { return m_funcLocal; } bool isFuncLocal() const { return m_funcLocal; }
bool isFuncLocalSticky() const { return m_funcLocalSticky; } bool isFuncLocalSticky() const { return m_funcLocalSticky; }
@@ -2702,6 +2547,7 @@ public:
bool attrSFormat() const { return m_attrSFormat; } bool attrSFormat() const { return m_attrSFormat; }
bool attrSplitVar() const { return m_attrSplitVar; } bool attrSplitVar() const { return m_attrSplitVar; }
bool attrFsmState() const { return m_attrFsmState; } bool attrFsmState() const { return m_attrFsmState; }
bool attrFsmRegisterWrapper() const { return m_attrFsmRegisterWrapper; }
bool attrFsmResetArc() const { return m_attrFsmResetArc; } bool attrFsmResetArc() const { return m_attrFsmResetArc; }
bool attrFsmArcInclCond() const { return m_attrFsmArcInclCond; } bool attrFsmArcInclCond() const { return m_attrFsmArcInclCond; }
AstIface* sensIfacep() const { return m_sensIfacep; } AstIface* sensIfacep() const { return m_sensIfacep; }
@@ -2725,6 +2571,13 @@ public:
lifetime(fromp->lifetime()); lifetime(fromp->lifetime());
} }
void combineType(const AstVar* otherp); void combineType(const AstVar* otherp);
void inlineAttrReset(const string& name) {
if (direction() == VDirection::INOUT && varType() == VVarType::WIRE) {
m_varType = VVarType::TRIWIRE;
}
m_direction = VDirection::NONE;
m_name = name;
}
bool needsCReset() const { bool needsCReset() const {
return !isIfaceParent() && !isIfaceRef() && !noReset() && !isParam() && !isStatementTemp() return !isIfaceParent() && !isIfaceRef() && !noReset() && !isParam() && !isStatementTemp()
&& !noCReset() && !(basicp() && basicp()->isEvent()); && !noCReset() && !(basicp() && basicp()->isEvent());
@@ -2767,7 +2620,6 @@ public:
bool sameNode(const AstNode* samep) const override; bool sameNode(const AstNode* samep) const override;
bool hasDType() const override VL_MT_SAFE { return true; } bool hasDType() const override VL_MT_SAFE { return true; }
AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable
void varp(AstVar* nodep) { m_varp = nodep; }
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
void scopep(AstScope* nodep) { m_scopep = nodep; } void scopep(AstScope* nodep) { m_scopep = nodep; }
bool isTrace() const { return m_trace; } bool isTrace() const { return m_trace; }
@@ -2781,10 +2633,10 @@ class AstCoverOtherDecl final : public AstNodeCoverDecl {
// Coverage analysis point declaration // Coverage analysis point declaration
// Used for other than toggle types of coverage // Used for other than toggle types of coverage
string m_linescov; string m_linescov;
string m_fsmVar; // FSM state variable name string m_fsmVar;
string m_fsmFrom; // FSM source state label string m_fsmFrom;
string m_fsmTo; // FSM destination state label string m_fsmTo;
string m_fsmTag; // FSM arc kind tag (e.g. reset, reset_include, default) string m_fsmTag;
int m_offset; // Offset column numbers to uniq-ify IFs int m_offset; // Offset column numbers to uniq-ify IFs
public: public:
AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment, AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment,
@@ -2810,7 +2662,7 @@ public:
const string& fsmTag() const { return m_fsmTag; } const string& fsmTag() const { return m_fsmTag; }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl); const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl);
return Super::sameNode(samep) && linescov() == asamep->linescov(); return AstNodeCoverDecl::sameNode(samep) && linescov() == asamep->linescov();
} }
}; };
class AstCoverToggleDecl final : public AstNodeCoverDecl { class AstCoverToggleDecl final : public AstNodeCoverDecl {
@@ -2832,7 +2684,7 @@ public:
const VNumRange& range() const { return m_range; } const VNumRange& range() const { return m_range; }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl); const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl);
return Super::sameNode(samep) && range() == asamep->range(); return AstNodeCoverDecl::sameNode(samep) && range() == asamep->range();
} }
}; };
@@ -2950,8 +2802,8 @@ public:
class AstCoverCross final : public AstNodeFuncCovItem { class AstCoverCross final : public AstNodeFuncCovItem {
// @astgen op1 := itemsp : List[AstCoverpointRef] // @astgen op1 := itemsp : List[AstCoverpointRef]
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only // @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
// @astgen op3 := binsp : List[AstNode] // Parse: mixed cross bins/options; // @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items;
// // post-LinkParse: AstCoverCrossBin only // // post-LinkParse: empty
// @astgen op4 := iffp : Optional[AstNodeExpr] // Conditional sampling guard // @astgen op4 := iffp : Optional[AstNodeExpr] // Conditional sampling guard
public: public:
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp, AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp,
@@ -3061,12 +2913,8 @@ class AstClass final : public AstNodeModule {
// MEMBERS // MEMBERS
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with // @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
// @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class // @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class
string m_dtypeNameFull; // dtypeName(true) once frozen, as parameters may then be gone
string m_dtypeNameShort; // dtypeName(false) once frozen, as parameters may then be gone
uint32_t m_declTokenNum; // Declaration token number uint32_t m_declTokenNum; // Declaration token number
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends) VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
bool m_hasRandVarsUpdate = false; // Has updateRandVars method,
// which updates pointers to rand variables in clone()
bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?) bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?)
bool m_extended = false; // Is extension or extended by other classes bool m_extended = false; // Is extension or extended by other classes
bool m_interfaceClass = false; // Interface class bool m_interfaceClass = false; // Interface class
@@ -3074,14 +2922,9 @@ class AstClass final : public AstNodeModule {
bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class
bool m_virtual = false; // Virtual class bool m_virtual = false; // Virtual class
bool m_printedFrom = false; // This class is printed from i.e. is used as format arg. bool m_printedFrom = false; // This class is printed from i.e. is used as format arg.
// Mutable, as only a recursion guard of the const dtypeName(), so also not dumped
mutable bool m_dtypeNameBusy = false; // In dtypeName(), which a parameter may lead back to
// Covergroup options (when m_covergroup is true) // Covergroup options (when m_covergroup is true)
int m_cgAutoBinMax = -1; // option.auto_bin_max value (-1 = not set, use default 64) int m_cgAutoBinMax = -1; // option.auto_bin_max value (-1 = not set, use default 64)
string dtypeNameCalc(bool full) const; // dtypeName() as computed from the tree
string dtypeNameScope() const; // Prefix of dtypeName(true) for the scope declaring the class
public: public:
AstClass(FileLine* fl, const string& name, const string& libname) AstClass(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_Class(fl, name, libname) : ASTGEN_SUPER_Class(fl, name, libname)
@@ -3100,8 +2943,6 @@ public:
void covergroupEnclosingClassp(AstClass* classp) { m_covergroupEnclosingClassp = classp; } void covergroupEnclosingClassp(AstClass* classp) { m_covergroupEnclosingClassp = classp; }
AstNode* membersp() const VL_MT_STABLE { return stmtsp(); } AstNode* membersp() const VL_MT_STABLE { return stmtsp(); }
void addMembersp(AstNode* nodep) { addStmtsp(nodep); } void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
bool hasRandVarsUpdate() const { return m_hasRandVarsUpdate; }
void hasRandVarsUpdate(bool flag) { m_hasRandVarsUpdate = flag; }
bool isCovergroup() const { return m_covergroup; } bool isCovergroup() const { return m_covergroup; }
void isCovergroup(bool flag) { m_covergroup = flag; } void isCovergroup(bool flag) { m_covergroup = flag; }
bool isExtended() const { return m_extended; } bool isExtended() const { return m_extended; }
@@ -3116,16 +2957,6 @@ public:
void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; } void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; }
void markPrintedFrom() { m_printedFrom = true; } void markPrintedFrom() { m_printedFrom = true; }
bool isPrintedFrom() const { return m_printedFrom; } bool isPrintedFrom() const { return m_printedFrom; }
// Name of the class as a data type, with the values of its parameters, e.g. 'Cls#(int,5)'.
// With 'full', as for $typename (IEEE 1800-2023 20.6.1), prefixed with the scope declaring
// the class, e.g. '$unit::Cls#(int,5)', and with the types of parameters in full.
string dtypeName(bool full) const;
// Fix dtypeName(), as V3WidthCommit moves parameter types to the type table
void dtypeNameFreeze();
// Whether dtypeName() is fixed, as for a class elaborated from the design
bool dtypeNameFrozen() const { return !m_dtypeNameFull.empty(); }
// Named by dtypeName(), as name() is internal for a specialization, unless still a template
string prettyNameMsg() const override { return hasGParam() ? prettyName() : dtypeName(false); }
// Covergroup options accessors // Covergroup options accessors
int cgAutoBinMax() const { return m_cgAutoBinMax; } int cgAutoBinMax() const { return m_cgAutoBinMax; }
void cgAutoBinMax(int value) { m_cgAutoBinMax = value; } void cgAutoBinMax(int value) { m_cgAutoBinMax = value; }
@@ -3195,25 +3026,17 @@ public:
}; };
class AstIface final : public AstNodeModule { class AstIface final : public AstNodeModule {
// An interface declaration // An interface declaration
string m_dtypeNameFull; // dtypeName(true) once frozen, as parameter types may then be gone
string m_dtypeNameShort; // dtypeName(false) once frozen, as parameter types may then be gone
bool m_hasVirtualRef = false; // There exists a virtual interface reference for this interface bool m_hasVirtualRef = false; // There exists a virtual interface reference for this interface
public: public:
AstIface(FileLine* fl, const string& name, const string& libname) AstIface(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_Iface(fl, name, libname) {} : ASTGEN_SUPER_Iface(fl, name, libname) {}
ASTGEN_MEMBERS_AstIface; ASTGEN_MEMBERS_AstIface;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string verilogKwd() const override { return "interface"; }
// Interfaces have `timescale applicability but lots of code seems to // Interfaces have `timescale applicability but lots of code seems to
// get false warnings if we enable this // get false warnings if we enable this
string verilogKwd() const override { return "interface"; }
bool timescaleMatters() const override { return false; } bool timescaleMatters() const override { return false; }
bool hasVirtualRef() const { return m_hasVirtualRef; } bool hasVirtualRef() const { return m_hasVirtualRef; }
void setHasVirtualRef() { m_hasVirtualRef = true; } void setHasVirtualRef() { m_hasVirtualRef = true; }
// Name of the interface as a data type, with the values of its parameters, e.g. 'ifc#(8)'
string dtypeName(bool full) const;
// Fix dtypeName(), as V3WidthCommit moves parameter types to the type table
void dtypeNameFreeze();
}; };
class AstModule final : public AstNodeModule { class AstModule final : public AstNodeModule {
// A module declaration // A module declaration
@@ -3264,7 +3087,9 @@ public:
: ASTGEN_SUPER_Package(fl, name, libname) {} : ASTGEN_SUPER_Package(fl, name, libname) {}
ASTGEN_MEMBERS_AstPackage; ASTGEN_MEMBERS_AstPackage;
string verilogKwd() const override { return "package"; } string verilogKwd() const override { return "package"; }
bool timescaleMatters() const override { return !isDollarUnit() && !isConstPool(); } bool timescaleMatters() const override { return !isDollarUnit(); }
static string dollarUnitName() { return AstNode::encodeName("$unit"); }
bool isDollarUnit() const { return name() == dollarUnitName(); }
}; };
class AstPrimitive final : public AstNodeModule { class AstPrimitive final : public AstNodeModule {
// A primitive declaration // A primitive declaration
@@ -3416,14 +3241,6 @@ public:
} }
int elementsConst() const VL_MT_STABLE { return hiConst() - loConst() + 1; } int elementsConst() const VL_MT_STABLE { return hiConst() - loConst() + 1; }
bool ascending() const { return leftConst() < rightConst(); } bool ascending() const { return leftConst() < rightConst(); }
// Iterate indices from left to right inclusive, see VNumRange::seqLeftToRight()
VNumRange::Sequence seqLeftToRight() const {
return VNumRange{leftConst(), rightConst()}.seqLeftToRight();
}
// Iterate indices from right to left inclusive, see VNumRange::seqRightToLeft()
VNumRange::Sequence seqRightToLeft() const {
return VNumRange{leftConst(), rightConst()}.seqRightToLeft();
}
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
virtual string emitC() { V3ERROR_NA_RETURN(""); } virtual string emitC() { V3ERROR_NA_RETURN(""); }
+41 -13
View File
@@ -413,16 +413,16 @@ class AstCase final : public AstNodeStmt {
// @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression // @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression
// @astgen op2 := itemsp : List[AstCaseItem] // @astgen op2 := itemsp : List[AstCaseItem]
// @astgen op3 := notParallelp : List[AstNode] // assertion code for non-full case's // @astgen op3 := notParallelp : List[AstNode] // assertion code for non-full case's
VCaseType m_caseType; // 0=case, 1=casex, 2=casez VCaseType m_casex; // 0=case, 1=casex, 2=casez
bool m_fullPragma = false; // Synthesis full_case bool m_fullPragma = false; // Synthesis full_case
bool m_parallelPragma = false; // Synthesis parallel_case bool m_parallelPragma = false; // Synthesis parallel_case
bool m_uniquePragma = false; // unique case bool m_uniquePragma = false; // unique case
bool m_unique0Pragma = false; // unique0 case bool m_unique0Pragma = false; // unique0 case
bool m_priorityPragma = false; // priority case bool m_priorityPragma = false; // priority case
public: public:
AstCase(FileLine* fl, VCaseType caseType, AstNodeExpr* exprp, AstCaseItem* itemsp) AstCase(FileLine* fl, VCaseType casex, AstNodeExpr* exprp, AstCaseItem* itemsp)
: ASTGEN_SUPER_Case(fl) : ASTGEN_SUPER_Case(fl)
, m_caseType{caseType} { , m_casex{casex} {
this->exprp(exprp); this->exprp(exprp);
addItemsp(itemsp); addItemsp(itemsp);
} }
@@ -432,15 +432,15 @@ public:
int instrCount() const override { return INSTR_COUNT_BRANCH; } int instrCount() const override { return INSTR_COUNT_BRANCH; }
string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; } string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
return m_caseType == VN_DBG_AS(samep, Case)->m_caseType; return m_casex == VN_DBG_AS(samep, Case)->m_casex;
} }
bool casex() const { return m_caseType == VCaseType::CT_CASEX; } bool casex() const { return m_casex == VCaseType::CT_CASEX; }
bool casez() const { return m_caseType == VCaseType::CT_CASEZ; } bool casez() const { return m_casex == VCaseType::CT_CASEZ; }
bool caseInside() const { return m_caseType == VCaseType::CT_CASEINSIDE; } bool caseInside() const { return m_casex == VCaseType::CT_CASEINSIDE; }
bool caseMatches() const { return m_caseType == VCaseType::CT_CASEMATCHES; } bool caseMatches() const { return m_casex == VCaseType::CT_CASEMATCHES; }
bool caseSimple() const { return m_caseType == VCaseType::CT_CASE; } bool caseSimple() const { return m_casex == VCaseType::CT_CASE; }
void caseInsideSet() { m_caseType = VCaseType::CT_CASEINSIDE; } void caseInsideSet() { m_casex = VCaseType::CT_CASEINSIDE; }
void caseMatchesSet() { m_caseType = VCaseType::CT_CASEMATCHES; } void caseMatchesSet() { m_casex = VCaseType::CT_CASEMATCHES; }
bool fullPragma() const { return m_fullPragma; } bool fullPragma() const { return m_fullPragma; }
void fullPragma(bool flag) { m_fullPragma = flag; } void fullPragma(bool flag) { m_fullPragma = flag; }
bool parallelPragma() const { return m_parallelPragma; } bool parallelPragma() const { return m_parallelPragma; }
@@ -549,7 +549,29 @@ public:
bool isOutputter() override { return true; } bool isOutputter() override { return true; }
bool isPure() override { return false; } bool isPure() override { return false; }
AstNodeCoverDecl* declp() const { return m_declp; } // Where defined AstNodeCoverDecl* declp() const { return m_declp; } // Where defined
void declp(AstNodeCoverDecl* nodep) { m_declp = nodep; } };
class AstCoverToggle final : public AstNodeStmt {
// Toggle analysis of given signal
// Parents: MODULE
// @astgen op1 := incp : AstCoverInc
// @astgen op2 := origp : AstNodeExpr
// @astgen op3 := changep : AstNodeExpr
public:
AstCoverToggle(FileLine* fl, AstCoverInc* incp, AstNodeExpr* origp, AstNodeExpr* changep)
: ASTGEN_SUPER_CoverToggle(fl) {
this->incp(incp);
this->origp(origp);
this->changep(changep);
}
ASTGEN_MEMBERS_AstCoverToggle;
int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return true; }
bool isOutputter() override {
return false; // Though the AstCoverInc under this is an outputter
}
// but isPure() true
}; };
class AstDeassign final : public AstNodeStmt { class AstDeassign final : public AstNodeStmt {
// Procedural 'deassign' statement // Procedural 'deassign' statement
@@ -1199,6 +1221,13 @@ public:
ASTGEN_MEMBERS_AstSetuphold; ASTGEN_MEMBERS_AstSetuphold;
bool sameNode(const AstNode* /*samep*/) const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; }
}; };
class AstSplitPlaceholder final : public AstNodeStmt {
public:
// Dummy node used within V3Split; never exists outside of V3Split.
explicit AstSplitPlaceholder(FileLine* fl)
: ASTGEN_SUPER_SplitPlaceholder(fl) {}
ASTGEN_MEMBERS_AstSplitPlaceholder;
};
class AstStackTraceT final : public AstNodeStmt { class AstStackTraceT final : public AstNodeStmt {
// $stacktrace used as task // $stacktrace used as task
public: public:
@@ -1311,7 +1340,6 @@ class AstTraceDecl final : public AstNodeStmt {
const VVarType m_varType; // Type of variable (for localparam vs. param) const VVarType m_varType; // Type of variable (for localparam vs. param)
const VDirection m_declDirection; // Declared direction input/output etc const VDirection m_declDirection; // Declared direction input/output etc
const bool m_inDtypeFunc; // Trace decl inside type init function const bool m_inDtypeFunc; // Trace decl inside type init function
// dist-ast-dump-suppress // Not stable and of low value
int m_codeInc{0}; // Code increment for type int m_codeInc{0}; // Code increment for type
public: public:
AstTraceDecl(FileLine* fl, const string& showname, AstTraceDecl(FileLine* fl, const string& showname,
+3739 -3910
View File
File diff suppressed because it is too large Load Diff
-46
View File
@@ -26,8 +26,6 @@
#include "V3Broken.h" #include "V3Broken.h"
#include "V3ConstPool.h"
#include <unordered_set> #include <unordered_set>
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
@@ -229,45 +227,6 @@ private:
} }
return false; return false;
} }
static void checkArgRefs(AstNodeExpr* nodep, const char* descrp, AstNodeExpr* argsp) {
if (!std::strcmp(descrp, "TODO")) return; // Skip if not yet checked
// Check each argument
const char* dp = descrp;
for (AstNodeExpr* argp = argsp; argp; argp = VN_AS(argp->nextp(), NodeExpr)) {
if (argp->fileline()->erroringOn()) return; // Intentionally skip all checks
const AstNodeExpr* const lvalp = argp->getVAccessTargetRecurse();
const VAccess access
= lvalp ? lvalp->getVAccessRecurse() // Since we call it on a result of above call
// there should be no recursion at all
: VAccess{VAccess::READ};
if (dp[0] == '+') --dp; // Repeats the entry before it
switch (dp[0]) {
case 'r':
UASSERT_OBJ(access.isReadOnly(), argp,
"Input argument of library call is not a read-only expression");
break;
case 'w':
UASSERT_OBJ(lvalp, argp, //
"Output argument of library call is not a valid C++ LValue");
UASSERT_OBJ(access.isWriteOnly(), argp,
"Output argument of library call is not write-only");
break;
case 'm':
UASSERT_OBJ(lvalp, argp, //
"Inout argument of library call is not a valid C++ LValue");
UASSERT_OBJ(access.isRW(), argp,
"Inout argument of library call is not read-write");
break;
default:
UASSERT_OBJ(dp[0], argp, "Unexpected trailing arguments to library call");
break; // LCOV_EXCL_LINE
}
++dp;
}
UASSERT_OBJ(!dp[0] || dp[0] == '+', nodep, "Insufficient arguments to library call");
}
// VISITORS // VISITORS
void visit(AstNodeAssign* nodep) override { void visit(AstNodeAssign* nodep) override {
processEnter(nodep); processEnter(nodep);
@@ -332,7 +291,6 @@ private:
void visit(AstCMethodHard* nodep) override { void visit(AstCMethodHard* nodep) override {
++m_nCalls; ++m_nCalls;
processAndIterate(nodep); processAndIterate(nodep);
checkArgRefs(nodep, nodep->method().args(), nodep->pinsp());
} }
void visit(AstNodeFTaskRef* nodep) override { void visit(AstNodeFTaskRef* nodep) override {
++m_nCalls; ++m_nCalls;
@@ -434,10 +392,6 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
// Check every node in tree // Check every node in tree
const BrokenCheckVisitor cvisitor{nodep}; const BrokenCheckVisitor cvisitor{nodep};
// Check the constant pool lookup cache refers only to nodes in the tree
const char* const whyp = V3ConstPool::broken();
UASSERT_OBJ(!whyp, nodep, "Broken constant pool cache: " << whyp);
s_allocTable.checkForLeaks(); s_allocTable.checkForLeaks();
s_linkableTable.clear(); s_linkableTable.clear();
s_brokenCntGlobal.inc(); s_brokenCntGlobal.inc();
+3 -3
View File
@@ -152,7 +152,6 @@ class CCtorsVisitor final : public VNVisitor {
// VISITORS // VISITORS
void visit(AstNodeModule* nodep) override { void visit(AstNodeModule* nodep) override {
if (nodep->isConstPool()) return; // Special emit rules
if (const AstClass* const classp = VN_CAST(nodep, Class)) { if (const AstClass* const classp = VN_CAST(nodep, Class)) {
// Interface class may only have pure virtuals and params which do not need cctor reset // Interface class may only have pure virtuals and params which do not need cctor reset
if (classp->isInterfaceClass()) return; if (classp->isInterfaceClass()) return;
@@ -168,12 +167,12 @@ class CCtorsVisitor final : public VNVisitor {
if (v3Global.opt.coverage()) { if (v3Global.opt.coverage()) {
V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE}; V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE};
for (AstNode *np = nodep->stmtsp(), *nextp; np; np = nextp) { for (AstNode* np = nodep->stmtsp(); np; np = np->nextp()) {
nextp = np->nextp();
if (AstNodeCoverDecl* const coverp = VN_CAST(np, NodeCoverDecl)) { if (AstNodeCoverDecl* const coverp = VN_CAST(np, NodeCoverDecl)) {
// ... else we don't have a static VlSym to be able to coverage insert // ... else we don't have a static VlSym to be able to coverage insert
UASSERT_OBJ(!VN_IS(nodep, Class), coverp, UASSERT_OBJ(!VN_IS(nodep, Class), coverp,
"NodeCoverDecl should be in class's package, not class itself"); "NodeCoverDecl should be in class's package, not class itself");
np = coverp->backp();
configure_coverage.add(coverp->unlinkFrBack()); configure_coverage.add(coverp->unlinkFrBack());
} }
} }
@@ -220,6 +219,7 @@ class CCtorsVisitor final : public VNVisitor {
} }
} }
void visit(AstConstPool*) override {}
void visit(AstNode* nodep) override { iterateChildren(nodep); } void visit(AstNode* nodep) override { iterateChildren(nodep); }
public: public:
-2
View File
@@ -86,7 +86,6 @@ class CUseVisitor final : public VNVisitorConst {
} }
void visit(AstCell* nodep) override { void visit(AstCell* nodep) override {
if (nodep->user1SetOnce()) return; // Process once if (nodep->user1SetOnce()) return; // Process once
if (nodep->modp()->isConstPool()) return; // Special emit rules
// Currently no IMP_INCLUDE because we include __Syms which has them all // Currently no IMP_INCLUDE because we include __Syms which has them all
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name()); addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name());
iterateChildrenConst(nodep); iterateChildrenConst(nodep);
@@ -116,7 +115,6 @@ void V3CUse::cUseAll() {
// Call visitor separately for each module, so visitor state is cleared // Call visitor separately for each module, so visitor state is cleared
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp;
modp = VN_AS(modp->nextp(), NodeModule)) { modp = VN_AS(modp->nextp(), NodeModule)) {
if (modp->isConstPool()) continue; // Special emit rules
// Insert under this module; someday we should e.g. make Ast // Insert under this module; someday we should e.g. make Ast
// for each output file and put under that // for each output file and put under that
CUseVisitor{modp}; CUseVisitor{modp};
+16 -24
View File
@@ -38,7 +38,6 @@
#include "V3Case.h" #include "V3Case.h"
#include "V3ConstPool.h"
#include "V3Stats.h" #include "V3Stats.h"
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
@@ -739,15 +738,18 @@ class CaseVisitor final : public VNVisitor {
} }
} }
// Create the lookup table reference, in the constant pool unless using an inline table // Create the table in the constant pool, unless using an inline table
AstNodeExpr* const tableRefp = [&]() -> AstNodeExpr* { AstVarScope* const tableVscp = [&]() -> AstVarScope* {
if (isTinyTable) return tablep; if (isTinyTable) return nullptr;
AstVarRef* const refp = V3ConstPool::findConst(tablep); AstVarScope* vscp = v3Global.rootp()->constPoolp()->findConst(tablep, true);
VL_DO_DANGLING(tablep->deleteTree(), tablep); // findConst clones VL_DO_DANGLING(tablep->deleteTree(), tablep); // findConst clones
return refp; return vscp;
}(); }();
// Create the lookup index // Create the lookup table reference and index
AstNodeExpr* const tableRefp
= tableVscp ? static_cast<AstNodeExpr*>(new AstVarRef{flp, tableVscp, VAccess::READ})
: static_cast<AstNodeExpr*>(tablep);
AstNodeExpr* const caseExprp AstNodeExpr* const caseExprp
= new AstExtend{flp, nodep->exprp()->cloneTreePure(false), 32}; = new AstExtend{flp, nodep->exprp()->cloneTreePure(false), 32};
AstNodeExpr* const scalep = new AstConst{flp, entryWidth}; AstNodeExpr* const scalep = new AstConst{flp, entryWidth};
@@ -853,14 +855,15 @@ class CaseVisitor final : public VNVisitor {
} }
// Create the tables // Create the tables
AstVarRef* const matchRefp = V3ConstPool::findConst(matchp); AstVarScope* const matchVscp = v3Global.rootp()->constPoolp()->findConst(matchp, true);
AstVarRef* const tableRefp = V3ConstPool::findTable(tablep); AstVarScope* const tableVscp = v3Global.rootp()->constPoolp()->findTable(tablep);
VL_DO_DANGLING(matchp->deleteTree(), matchp); VL_DO_DANGLING(matchp->deleteTree(), matchp);
VL_DO_DANGLING(tablep->deleteTree(), tablep); VL_DO_DANGLING(tablep->deleteTree(), tablep);
// AstMatchMasked produces the index of the matching entry // AstMatchMasked produces the index of the matching entry
AstNodeExpr* const tableRefp = new AstVarRef{flp, tableVscp, VAccess::READ};
AstNodeExpr* const caseExprp = nodep->exprp()->cloneTreePure(false); AstNodeExpr* const caseExprp = nodep->exprp()->cloneTreePure(false);
AstMatchMasked* const indexp = new AstMatchMasked{flp, caseExprp, matchRefp}; AstMatchMasked* const indexp = new AstMatchMasked{flp, caseExprp, matchVscp};
AstNodeExpr* const entryp = new AstArraySel{flp, tableRefp, indexp}; AstNodeExpr* const entryp = new AstArraySel{flp, tableRefp, indexp};
return connectDecoderOutputs(nodep, entryp, "__VcaseDecoderOut"); return connectDecoderOutputs(nodep, entryp, "__VcaseDecoderOut");
@@ -1001,22 +1004,11 @@ class CaseVisitor final : public VNVisitor {
itemp->addCondsp(newCondp); itemp->addCondsp(newCondp);
} }
// If there was no default, make one up, this greatly simplifies below code // If there was no default, add a empty one, this greatly simplifies below code
// and constant propagation will just eliminate it for us later. // and constant propagation will just eliminate it for us later.
if (!hasDefault) { if (!hasDefault) {
if (m_caseDetailsValid && m_caseDetails.exhaustive nodep->addItemsp(new AstCaseItem{
&& !m_caseDetails.exhaustiveOverEnumOnly) { nodep->fileline(), new AstConst{nodep->fileline(), AstConst::BitTrue{}}, nullptr});
// Case is proven exhaustive, so the last is reached only when its test holds,
// make it unconditional
AstCaseItem* const lastp = VN_AS(nodep->itemsp()->lastp(), CaseItem);
pushDeletep(lastp->condsp()->unlinkFrBackWithNext());
lastp->addCondsp(new AstConst{lastp->fileline(), AstConst::BitTrue{}});
} else {
// Otherwise needs an explicit empty default
nodep->addItemsp(new AstCaseItem{
nodep->fileline(), new AstConst{nodep->fileline(), AstConst::BitTrue{}},
nullptr});
}
} }
// Now build the IF statement tree // Now build the IF statement tree
+12 -21
View File
@@ -18,8 +18,7 @@
// Each module: // Each module:
// For each expression, if it requires a clean operand, // For each expression, if it requires a clean operand,
// and the operand is dirty, insert a CLEAN node. // and the operand is dirty, insert a CLEAN node.
// Resize operands (but not variables, variable selects, or formatting metadata) // Resize operands (but not variables or variable selects) to C++ 32/64/wide types.
// to C++ 32/64/wide types.
// Copy all width() values to widthMin() so RANGE, etc can still see orig widths // Copy all width() values to widthMin() so RANGE, etc can still see orig widths
// //
//************************************************************************* //*************************************************************************
@@ -85,7 +84,6 @@ class CleanVisitor final : public VNVisitor {
|| VN_IS(nodep, ConsPackMember) // || VN_IS(nodep, ConsPackMember) //
|| VN_IS(nodep, NodeDType) // Don't want to change variable widths! || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|| VN_IS(nodep, NodeSel) // Array selects should reflect variable widths || VN_IS(nodep, NodeSel) // Array selects should reflect variable widths
|| VN_IS(nodep, SFormatArg) // Retain the logical argument type, not storage width
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays || VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|| VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
@@ -93,8 +91,6 @@ class CleanVisitor final : public VNVisitor {
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType) || VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|| VN_IS(nodep->dtypep()->skipRefp(), StreamDType) || VN_IS(nodep->dtypep()->skipRefp(), StreamDType)
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), CoverCrossDType)
|| VN_IS(nodep->dtypep()->skipRefp(), CoverpointDType)
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) { || VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
} else { } else {
const AstNodeUOrStructDType* const dtypep const AstNodeUOrStructDType* const dtypep
@@ -142,12 +138,12 @@ class CleanVisitor final : public VNVisitor {
computeCppWidth(nodep); computeCppWidth(nodep);
if (!isClean(nodep)) insertClean(nodep); if (!isClean(nodep)) insertClean(nodep);
} }
void ensureCleanAndNext(AstNode* nodep) { void ensureCleanAndNext(AstNodeExpr* nodep) {
// Editing list, careful looping! // Editing list, careful looping!
for (AstNode* argp = nodep; argp;) { for (AstNodeExpr* exprp = nodep; exprp;) {
AstNode* const nextp = argp->nextp(); AstNodeExpr* const nextp = VN_AS(exprp->nextp(), NodeExpr);
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp); ensureClean(exprp);
argp = nextp; exprp = nextp;
} }
} }
@@ -251,7 +247,9 @@ class CleanVisitor final : public VNVisitor {
setClean(nodep, false); setClean(nodep, false);
// We always clean, as we don't trust those pesky users. // We always clean, as we don't trust those pesky users.
if (!VN_IS(nodep->backp(), And)) insertClean(nodep); if (!VN_IS(nodep->backp(), And)) insertClean(nodep);
ensureCleanAndNext(nodep->nodesp()); for (AstNode* argp = nodep->nodesp(); argp; argp = argp->nextp()) {
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
}
} }
void visit(AstTraceDecl* nodep) override {} // Nothing to do here void visit(AstTraceDecl* nodep) override {} // Nothing to do here
void visit(AstTraceInc* nodep) override { void visit(AstTraceInc* nodep) override {
@@ -282,23 +280,16 @@ class CleanVisitor final : public VNVisitor {
iterateChildren(nodep); iterateChildren(nodep);
ensureClean(nodep->condp()); ensureClean(nodep->condp());
} }
void visit(AstSFormatArg* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->exprp());
setClean(nodep, true);
}
void visit(AstSFormatF* nodep) override { void visit(AstSFormatF* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
ensureCleanAndNext(nodep->exprsp()); ensureCleanAndNext(nodep->exprsp());
setClean(nodep, true); // generates a string, so not relevant setClean(nodep, true); // generates a string, so not relevant
} }
void visit(AstCStmt* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->nodesp());
}
void visit(AstCStmtUser* nodep) override { void visit(AstCStmtUser* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
ensureCleanAndNext(nodep->nodesp()); for (AstNode* argp = nodep->nodesp(); argp; argp = argp->nextp()) {
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
}
} }
void visit(AstNodeCCall* nodep) override { void visit(AstNodeCCall* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
+1 -1
View File
@@ -97,7 +97,7 @@ class ClockVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep); VL_DO_DANGLING(nodep->deleteTree(), nodep);
} }
void visit(AstSenTree* nodep) override { void visit(AstSenTree* nodep) override {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); // No longer needed pushDeletep(nodep->unlinkFrBack()); // No longer needed
} }
//========== Move sampled assignments //========== Move sampled assignments
+2 -2
View File
@@ -44,8 +44,8 @@ class CombineVisitor final : VNVisitor {
// TYPES // TYPES
using funcit_t = std::list<AstCFunc*>::iterator; using funcit_t = std::list<AstCFunc*>::iterator;
struct CFuncs final { struct CFuncs final {
std::list<AstCFunc*> m_fast; // Functions that are not slow std::list<AstCFunc*> m_fast;
std::list<AstCFunc*> m_slow; // Functions marked for slow files std::list<AstCFunc*> m_slow;
}; };
// STATE // STATE
+1
View File
@@ -318,6 +318,7 @@ class ToStringVisitor final : public VNVisitorConst {
} }
} }
} }
void visit(AstConstPool*) override {} // Accelerate
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
public: public:
+23 -27
View File
@@ -110,12 +110,12 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
class LeafInfo final { // Leaf node (either AstConst or AstVarRef) class LeafInfo final { // Leaf node (either AstConst or AstVarRef)
// MEMBERS // MEMBERS
bool m_polarity = true; // Invert result due to NOT bool m_polarity = true;
int m_lsb = 0; // LSB of actually used bit of m_refp->varp() int m_lsb = 0; // LSB of actually used bit of m_refp->varp()
int m_msb = 0; // MSB of actually used bit of m_refp->varp() int m_msb = 0; // MSB of actually used bit of m_refp->varp()
int m_wordIdx = -1; // -1 means AstWordSel is not used. int m_wordIdx = -1; // -1 means AstWordSel is not used.
AstVarRef* m_refp = nullptr; // Leaf's variable reference AstVarRef* m_refp = nullptr;
const AstConst* m_constp = nullptr; // Leaf's constant const AstConst* m_constp = nullptr;
public: public:
// CONSTRUCTORS // CONSTRUCTORS
@@ -187,9 +187,9 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
}; };
struct BitPolarityEntry final { // Found bit polarity during iterate() struct BitPolarityEntry final { // Found bit polarity during iterate()
LeafInfo m_info; // Leaf (variable or constant) bit polarity was found on LeafInfo m_info;
bool m_polarity = false; // Polarity the bit must have to match bool m_polarity = false;
int m_bit = 0; // Bit index within the leaf that was tested int m_bit = 0;
BitPolarityEntry(const LeafInfo& info, bool pol, int bit) BitPolarityEntry(const LeafInfo& info, bool pol, int bit)
: m_info{info} : m_info{info}
, m_polarity{pol} , m_polarity{pol}
@@ -198,8 +198,8 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
}; };
struct FrozenNodeInfo final { // Context when a frozen node is found struct FrozenNodeInfo final { // Context when a frozen node is found
bool m_polarity; // Polarity the frozen node must match bool m_polarity;
int m_lsb; // LSB position of the frozen node int m_lsb;
bool operator<(const FrozenNodeInfo& other) const { bool operator<(const FrozenNodeInfo& other) const {
if (m_lsb != other.m_lsb) return m_lsb < other.m_lsb; if (m_lsb != other.m_lsb) return m_lsb < other.m_lsb;
return m_polarity < other.m_polarity; return m_polarity < other.m_polarity;
@@ -207,12 +207,12 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
}; };
class Restorer final { // Restore the original state unless disableRestore() is called class Restorer final { // Restore the original state unless disableRestore() is called
ConstBitOpTreeVisitor& m_visitor; // Visitor whose state is saved and restored ConstBitOpTreeVisitor& m_visitor;
const size_t m_polaritiesSize; // Saved m_visitor.m_bitPolarities size to truncate back to const size_t m_polaritiesSize;
const size_t m_frozenSize; // Saved m_visitor.m_frozenNodes size to truncate back to const size_t m_frozenSize;
const unsigned m_ops; // Saved m_visitor.m_ops to restore const unsigned m_ops;
const bool m_polarity; // Saved m_visitor.m_polarity to restore const bool m_polarity;
bool m_restore = true; // Whether the destructor still needs to restore state bool m_restore = true;
public: public:
explicit Restorer(ConstBitOpTreeVisitor& visitor) explicit Restorer(ConstBitOpTreeVisitor& visitor)
@@ -1015,10 +1015,14 @@ class ConstVisitor final : public VNVisitor {
const bool packReal) { const bool packReal) {
const AstNodeDType* const dtypep = fromp->dtypep()->skipRefp(); const AstNodeDType* const dtypep = fromp->dtypep()->skipRefp();
if (const AstUnpackArrayDType* const unpackDtypep = VN_CAST(dtypep, UnpackArrayDType)) { if (const AstUnpackArrayDType* const unpackDtypep = VN_CAST(dtypep, UnpackArrayDType)) {
for (const int idx : unpackDtypep->declRange().seqLeftToRight()) { const int left = unpackDtypep->left();
const int right = unpackDtypep->right();
const int step = left <= right ? 1 : -1;
for (int idx = left;; idx += step) {
AstArraySel* const selp AstArraySel* const selp
= new AstArraySel{fromp->fileline(), fromp->cloneTreePure(false), idx}; = new AstArraySel{fromp->fileline(), fromp->cloneTreePure(false), idx};
collectFixedAggregateTerms(selp, termps, packReal); collectFixedAggregateTerms(selp, termps, packReal);
if (idx == right) break;
} }
VL_DO_DANGLING(pushDeletep(fromp), fromp); VL_DO_DANGLING(pushDeletep(fromp), fromp);
} else if (const AstNodeUOrStructDType* const sdtypep } else if (const AstNodeUOrStructDType* const sdtypep
@@ -1459,12 +1463,11 @@ class ConstVisitor final : public VNVisitor {
} }
if (const AstShiftR* const shiftp = VN_CAST(rhsp, ShiftR)) { if (const AstShiftR* const shiftp = VN_CAST(rhsp, ShiftR)) {
// 'a >> S' forces the high S bits to zero. Check against the width of the shifted // 'a >> S' forces the high S bits to zero. Check against the width of the shifted
// operand, V3Expand can create shifts wider than their inputs. Must use width(), // operand, V3Expand can create shifts wider than their inputs
// not widthMin(), as bits above widthMin() are not guaranteed to be zero.
if (AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) { if (AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) {
return scp->num().fitsInUInt() return scp->num().fitsInUInt()
&& (lsb + scp->num().toUInt() && (lsb + scp->num().toUInt()
>= static_cast<uint32_t>(shiftp->lhsp()->width())); >= static_cast<uint32_t>(shiftp->lhsp()->widthMin()));
} }
} }
if (const AstMul* const mulp = VN_CAST(rhsp, Mul)) { if (const AstMul* const mulp = VN_CAST(rhsp, Mul)) {
@@ -3966,12 +3969,7 @@ class ConstVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return true; return true;
} }
void visit(AstSFormatArg* nodep) override { void visit(AstSFormatArg* nodep) override { iterateChildren(nodep); }
// Skip namep(): requiring its runtime lookup to be constant can reject valid
// enum-valued constant-function calls in parameters. displayedEnum() resolves
// the name from the folded exprp() value and enum dtype instead.
iterateAndNextNull(nodep->exprp());
}
void visit(AstSFormatF* nodep) override { void visit(AstSFormatF* nodep) override {
// Substitute constants into displays. The main point of this is to // Substitute constants into displays. The main point of this is to
// simplify assertion methodologies which call functions with display's. // simplify assertion methodologies which call functions with display's.
@@ -4035,9 +4033,7 @@ class ConstVisitor final : public VNVisitor {
: VFormatAttr{}; : VFormatAttr{};
if (VN_IS(subargp, Const)) { // Convert it if (VN_IS(subargp, Const)) { // Convert it
const string out const string out
= formatAttr.isEnum() = constNumV(subargp).displayed(nodep, fmt, formatAttr);
? constNumV(subargp).displayedEnum(fargp, fmt)
: constNumV(subargp).displayed(nodep, fmt, formatAttr);
UINFO(9, " DispConst: " << fmt << " -> " << out << " for " UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
<< subargp); << subargp);
// fmt = out w/ replace % with %% as it must later when // fmt = out w/ replace % with %% as it must later when
-219
View File
@@ -1,219 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Constant pool
//
// 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: 2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "V3PchAstMT.h"
#include "V3ConstPool.h"
//######################################################################
// V3ConstPool
bool V3ConstPool::equalMap(const AstInitArray* ap, const AstInitArray* bp) {
// Associative array initializers must have equivalent types, defaults, and entries.
// As in 'equalTable', compare by value, rather than by tree structure.
const AstAssocArrayDType* const aDTypep = VN_AS(ap->dtypep(), AssocArrayDType);
const AstAssocArrayDType* const bDTypep = VN_AS(bp->dtypep(), AssocArrayDType);
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false;
if (!aDTypep->keyDTypep()->sameTree(bDTypep->keyDTypep())) return false;
// The default is optional, but must be the same if present
const AstNode* const aDefaultp = ap->defaultp();
const AstNode* const bDefaultp = bp->defaultp();
UASSERT_OBJ(!aDefaultp || VN_IS(aDefaultp, Const), ap, "Const pool map default not Const");
UASSERT_OBJ(!bDefaultp || VN_IS(bDefaultp, Const), bp, "Const pool map default not Const");
if (!aDefaultp != !bDefaultp) return false;
if (aDefaultp && !aDefaultp->sameTree(bDefaultp)) return false;
// Compare the entries, which are ordered by key. Note an entry explicitly set to the
// default value makes maps compare different, which is safe, but misses merging them.
const AstInitArray::KeyItemMap& aMap = ap->map();
const AstInitArray::KeyItemMap& bMap = bp->map();
if (aMap.size() != bMap.size()) return false;
auto bIt = bMap.cbegin();
for (const auto& aItem : aMap) {
const AstNode* const aValuep = aItem.second->valuep();
const AstNode* const bValuep = bIt->second->valuep();
UASSERT_OBJ(VN_IS(aValuep, Const), aValuep, "Const pool map item not Const");
UASSERT_OBJ(VN_IS(bValuep, Const), bValuep, "Const pool map item not Const");
if (aItem.first != bIt->first) return false;
if (!aValuep->sameTree(bValuep)) return false;
++bIt;
}
return true;
}
bool V3ConstPool::equalTable(const AstInitArray* ap, const AstInitArray* bp) {
// Unpacked array initializers must have equivalent values
// Note, sadly we can't just call ap->sameTree(pb), because both:
// - the dtypes might be different instances
// - the default/inititem children might be in different order yet still yield the same table
// See note in AstInitArray::same about the same. This function instead compares by initializer
// value, rather than by tree structure.
const AstUnpackArrayDType* const aDTypep = VN_AS(ap->dtypep(), UnpackArrayDType);
const AstUnpackArrayDType* const bDTypep = VN_AS(bp->dtypep(), UnpackArrayDType);
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false;
// Compare the range by value, as the bounds might be differently typed constants
if (!(aDTypep->declRange() == bDTypep->declRange())) return false;
// Compare initializer arrays by value. Note this is only called when they hash the same.
const uint64_t size = aDTypep->elementsConst();
for (uint64_t n = 0; n < size; ++n) {
const AstNode* const valAp = ap->getIndexDefaultedValuep(n);
const AstNode* const valBp = bp->getIndexDefaultedValuep(n);
UASSERT_OBJ(VN_IS(valAp, Const), valAp, "Const pool table item not Const");
UASSERT_OBJ(VN_IS(valBp, Const), valBp, "Const pool table item not Const");
if (!valAp->sameTree(valBp)) return false;
}
return true;
}
size_t V3ConstPool::hashTable(const AstInitArray* initp) {
// Hash what 'equalTable' compares: the element type, the range by value, and the values
const AstUnpackArrayDType* const dtypep = VN_AS(initp->dtypep(), UnpackArrayDType);
V3Hash hash = V3Hasher::uncachedHash(dtypep->subDTypep());
hash += dtypep->left();
hash += dtypep->right();
const uint64_t size = dtypep->elementsConst();
for (uint64_t n = 0; n < size; ++n) {
hash += V3Hasher::uncachedHash(initp->getIndexDefaultedValuep(n));
}
return hash.value();
}
template <typename T_Set, typename T_Init>
AstVarRef* V3ConstPool::findOrCreate(T_Set& set, uint32_t& nextr, const char* prefixp,
T_Init* initp) {
// Rebuild the cache if invalidated
if (VL_UNLIKELY(!m_cacheValid)) {
m_cacheValid = true;
m_scopep = nullptr;
m_consts.clear();
m_maps.clear();
m_tables.clear();
m_varScopes.clear();
AstPackage* const pkgp = v3Global.rootp()->constPoolPkgp();
for (AstNode* nodep = pkgp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (AstScope* const scopep = VN_CAST(nodep, Scope)) {
if (m_stage == Stage::SCOPED) m_scopep = scopep;
continue;
}
// Otherwise must be a variable
AstVar* const varp = VN_AS(nodep, Var);
UASSERT_OBJ(varp->constPoolEntry(), varp, "Unmarked variable in constant pool");
if (VN_IS(varp->valuep(), Const)) {
m_consts.insert(varp);
} else if (const AstInitArray* const arrayp = VN_CAST(varp->valuep(), InitArray)) {
if (VN_IS(arrayp->dtypep(), AssocArrayDType)) {
m_maps.insert(varp);
} else {
m_tables.insert(varp);
}
}
}
if (m_scopep) {
for (AstVarScope* vscp = m_scopep->varsp(); vscp;
vscp = VN_AS(vscp->nextp(), VarScope)) {
m_varScopes.insert({vscp->varp(), vscp});
}
}
}
FileLine* const flp = initp->fileline();
AstNodeDType* const dtypep = initp->dtypep();
// Look up/create the variable with this value
const auto pair = set.insertLazy(initp, [&]() {
const std::string name = prefixp + std::to_string(nextr++);
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtypep};
varp->setConstPoolEntry();
varp->lifetime(VLifetime::STATIC_EXPLICIT);
varp->isConst(true);
varp->valuep(initp->cloneTree(false));
v3Global.rootp()->constPoolPkgp()->addStmtsp(varp);
return varp;
});
AstVar* const varp = *pair.first;
// Create a read reference to the entry, of the right form for the current stage
AstVarRef* refp = nullptr;
switch (m_stage) {
case Stage::UNSCOPED: {
// Before V3Scope, reference via the package, as any other package variable
AstPackage* const pkgp = v3Global.rootp()->constPoolPkgp();
refp = new AstVarRef{flp, pkgp, varp, VAccess::READ};
break;
}
case Stage::SCOPED: {
// While scoped, reference the VarScope, creating it for a new entry, or if removed
const auto vscpPair = m_varScopes.insertLazy(varp, [&]() {
AstVarScope* const vscp = new AstVarScope{varp->fileline(), m_scopep, varp};
m_scopep->addVarsp(vscp);
return std::pair<const AstVar*, AstVarScope*>{varp, vscp};
});
refp = new AstVarRef{flp, vscpPair.first->second, VAccess::READ};
break;
}
case Stage::DESCOPED: {
// After V3Descope, no VarScope is needed
refp = new AstVarRef{flp, varp, VAccess::READ};
break;
}
}
// The entry might have been created with a different, but compatible dtype
refp->dtypep(dtypep);
return refp;
}
AstVarRef* V3ConstPool::findConst(AstConst* initp) {
V3ConstPool& self = instance();
return self.findOrCreate(self.m_consts, self.m_nextConst, "CONST_", initp);
}
AstVarRef* V3ConstPool::findMap(AstInitArray* initp) {
UASSERT_OBJ(VN_IS(initp->dtypep(), AssocArrayDType), initp,
"Const pool map must have associative array dtype");
V3ConstPool& self = instance();
return self.findOrCreate(self.m_maps, self.m_nextMap, "MAP_", initp);
}
AstVarRef* V3ConstPool::findTable(AstInitArray* initp) {
UASSERT_OBJ(VN_IS(initp->dtypep(), UnpackArrayDType), initp,
"Const pool table must have unpacked array dtype");
V3ConstPool& self = instance();
return self.findOrCreate(self.m_tables, self.m_nextTable, "TABLE_", initp);
}
AstVarRef* V3ConstPool::find(AstNodeExpr* initp) {
if (AstConst* const constp = VN_CAST(initp, Const)) return findConst(constp);
if (AstInitArray* const arrayp = VN_CAST(initp, InitArray)) {
if (VN_IS(arrayp->dtypep(), AssocArrayDType)) return findMap(arrayp);
if (VN_IS(arrayp->dtypep(), UnpackArrayDType)) return findTable(arrayp);
}
initp->v3fatalSrc("Unhandled constant pool initializer");
return nullptr; // LCOV_EXCL_LINE
}
const char* V3ConstPool::broken() {
const V3ConstPool& self = instance();
if (!self.m_cacheValid) return nullptr;
BROKEN_RTN(self.m_scopep && !self.m_scopep->brokeExists());
for (const AstVar* const varp : self.m_consts) BROKEN_RTN(!varp->brokeExists());
for (const AstVar* const varp : self.m_maps) BROKEN_RTN(!varp->brokeExists());
for (const AstVar* const varp : self.m_tables) BROKEN_RTN(!varp->brokeExists());
for (const auto& pair : self.m_varScopes) {
BROKEN_RTN(!pair.first->brokeExists());
BROKEN_RTN(!pair.second->brokeExists());
}
return nullptr;
}
-163
View File
@@ -1,163 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Constant pool
//
// 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: 2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// V3ConstPool finds or creates entries in the constant pool package
// (AstNetlist::constPoolPkgp()), which holds read-only static data, i.e.:
// constants, maps and tables, shared across the whole design. Entries are
// ordinary package variables, deduplicated by value.
//
//*************************************************************************
#ifndef VERILATOR_V3CONSTPOOL_H_
#define VERILATOR_V3CONSTPOOL_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3HashTable.h"
#include "V3Hasher.h"
//============================================================================
class V3ConstPool final {
friend class V3Global; // Owns the instance, needs constructor access
// TYPES
// Scoping stage of the design, which determines how entries are referenced
enum class Stage : uint8_t {
UNSCOPED, // Before V3Scope: reference via the package
SCOPED, // Between V3Scope and V3Descope: reference via the VarScope
DESCOPED // After V3Descope: plain reference
};
// Hash and equality of constant entries in 'm_consts'
struct ConstHash final {
size_t operator()(const AstVar* varp) const {
return (*this)(VN_AS(varp->valuep(), Const));
}
size_t operator()(const AstConst* initp) const { return initp->num().toHash().value(); }
};
struct ConstEqual final {
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
return (*this)(ap, VN_AS(bp->valuep(), Const)); // Only on collision while rebuild
} // LCOV_EXCL_STOP
bool operator()(const AstVar* varp, const AstConst* initp) const {
// Compare by value, which also checks the width. The dtype is ignored.
return VN_AS(varp->valuep(), Const)->num().isCaseEq(initp->num());
}
};
// Hash and equality of associative array entries in 'm_maps'
struct MapHash final {
size_t operator()(const AstVar* varp) const {
return (*this)(VN_AS(varp->valuep(), InitArray));
}
size_t operator()(const AstInitArray* initp) const {
return V3Hasher::uncachedHash(initp).value();
}
};
struct MapEqual final {
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
return (*this)(ap, VN_AS(bp->valuep(), InitArray)); // Only on collision while rebuild
} // LCOV_EXCL_STOP
bool operator()(const AstVar* varp, const AstInitArray* initp) const {
return equalMap(VN_AS(varp->valuep(), InitArray), initp);
}
};
// Hash and equality of unpacked array entries in 'm_tables'
struct TableHash final {
size_t operator()(const AstVar* varp) const {
return (*this)(VN_AS(varp->valuep(), InitArray));
}
size_t operator()(const AstInitArray* initp) const { //
return hashTable(initp);
}
};
struct TableEqual final {
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
return (*this)(ap, VN_AS(bp->valuep(), InitArray)); // Only on collision while rebuild
} // LCOV_EXCL_STOP
bool operator()(const AstVar* varp, const AstInitArray* initp) const {
return equalTable(VN_AS(varp->valuep(), InitArray), initp);
}
};
// MEMBERS
Stage m_stage = Stage::UNSCOPED; // Scoping stage of the design, see setScoped()/setDescoped()
bool m_cacheValid = false; // Cache below is up to date
AstScope* m_scopep = nullptr; // Scope of the constant pool, between V3Scope and V3Descope
V3HashSet<AstVar*, ConstHash, ConstEqual> m_consts; // Packed constants
V3HashSet<AstVar*, MapHash, MapEqual> m_maps; // Associative array constants (maps)
V3HashSet<AstVar*, TableHash, TableEqual> m_tables; // Unpacked array constants (tables)
V3HashMap<const AstVar*, AstVarScope*> m_varScopes; // VarScope of each entry iff m_scopep
uint32_t m_nextConst = 0; // Sequence number for naming
uint32_t m_nextMap = 0; // Sequence number for naming
uint32_t m_nextTable = 0; // Sequence number for naming
// METHODS
static V3ConstPool& instance() { return *v3Global.constPoolp(); }
static bool equalMap(const AstInitArray* ap, const AstInitArray* bp);
static bool equalTable(const AstInitArray* ap, const AstInitArray* bp);
static size_t hashTable(const AstInitArray* initp);
// Find an entry equal to the given initializer in the given set, or create one named from
// the given prefix and sequence number, and return a read reference to it
template <typename T_Set, typename T_Init>
AstVarRef* findOrCreate(T_Set& set, uint32_t& nextr, const char* prefixp, T_Init* initp);
// CONSTRUCTORS - only V3Global creates and deletes the instance
V3ConstPool() = default;
~V3ConstPool() = default;
VL_UNCOPYABLE(V3ConstPool);
VL_UNMOVABLE(V3ConstPool);
public:
// STATIC METHODS
// Find a constant packed variable with the given value, or create one if one does not
// already exist, and return a read reference to it.
// The reference has the same dtype as initp.
// The referenced variable *might* have a different, but compatible dtype.
static AstVarRef* findConst(AstConst* initp);
// Find a constant associative array (map) with the given value, or create one if one does
// not already exist, and return a read reference to it.
// The reference has the same dtype as initp.
// The referenced variable *might* have a different, but compatible dtype.
static AstVarRef* findMap(AstInitArray* initp);
// Find a constant unpacked array (table) with the given value, or create one if one does
// not already exist, and return a read reference to it.
// The reference has the same dtype as initp.
// The referenced variable *might* have a different, but compatible dtype.
static AstVarRef* findTable(AstInitArray* initp);
// Dispatch to the above based on the kind of initializer.
static AstVarRef* find(AstNodeExpr* initp);
// Invalidate the lookup cache. This must be called after deleting any Vars from the pool.
static void invalidateCache() { instance().m_cacheValid = false; }
// Notify that the design has been scoped, called by V3Scope
static void setScoped() {
V3ConstPool& self = instance();
UASSERT(self.m_stage == Stage::UNSCOPED, "Constant pool scoped twice");
self.m_stage = Stage::SCOPED;
invalidateCache();
}
// Notify that the design has been descoped, called by V3Descope
static void setDescoped() {
V3ConstPool& self = instance();
UASSERT(self.m_stage == Stage::SCOPED, "Constant pool descoped when not scoped");
self.m_stage = Stage::DESCOPED;
invalidateCache();
}
// Check the lookup cache refers only to existing nodes, for V3Broken
static const char* broken();
};
#endif // Guard
-10
View File
@@ -41,15 +41,6 @@ public:
if (itFoundPair.second) m_keys.push_back(key); if (itFoundPair.second) m_keys.push_back(key);
return itFoundPair.second; return itFoundPair.second;
} }
template <typename T>
void insert(T begin, const T end) {
static_assert(std::is_same<typename std::iterator_traits<T>::value_type, T_Key>::value,
"T must be an iterator with value type T_Key");
while (begin != end) {
if (m_keySet.insert(*begin).second) m_keys.push_back(*begin);
++begin;
}
}
void clear() { void clear() {
m_keys.clear(); m_keys.clear();
m_keySet.clear(); m_keySet.clear();
@@ -57,7 +48,6 @@ public:
// ACCESSORS // ACCESSORS
bool empty() const { return m_keys.empty(); } bool empty() const { return m_keys.empty(); }
size_t size() const { return m_keys.size(); }
bool exists(const T_Key& key) const { return m_keySet.find(key) != m_keySet.end(); } bool exists(const T_Key& key) const { return m_keySet.find(key) != m_keySet.end(); }
// ITERATORS // ITERATORS
+36 -36
View File
@@ -491,41 +491,41 @@ public:
IgnIndices results; IgnIndices results;
int nextChange = 0; int nextChange = 0;
tree.find(0, results, nextChange); tree.find(0, results, nextChange);
UASSERT_SELFTEST(results.size(), 0); UASSERT_SELFTEST(const size_t, results.size(), 0);
UASSERT_SELFTEST(nextChange, 10); UASSERT_SELFTEST(const int, nextChange, 10);
tree.find(10, results, nextChange); tree.find(10, results, nextChange);
UASSERT_SELFTEST(results.size(), 2); UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(results[0], 0); UASSERT_SELFTEST(const int, results[0], 0);
UASSERT_SELFTEST(results[1], 3); UASSERT_SELFTEST(const int, results[1], 3);
UASSERT_SELFTEST(nextChange, 11); UASSERT_SELFTEST(const int, nextChange, 11);
tree.find(11, results, nextChange); tree.find(11, results, nextChange);
UASSERT_SELFTEST(results.size(), 1); UASSERT_SELFTEST(const size_t, results.size(), 1);
UASSERT_SELFTEST(results[0], 3); UASSERT_SELFTEST(const int, results[0], 3);
UASSERT_SELFTEST(nextChange, 15); // Center, or would be 20 UASSERT_SELFTEST(const int, nextChange, 15); // Center, or would be 20
tree.find(20, results, nextChange); tree.find(20, results, nextChange);
UASSERT_SELFTEST(results.size(), 3); UASSERT_SELFTEST(const size_t, results.size(), 3);
UASSERT_SELFTEST(results[0], 1); UASSERT_SELFTEST(const int, results[0], 1);
UASSERT_SELFTEST(results[1], 3); UASSERT_SELFTEST(const int, results[1], 3);
UASSERT_SELFTEST(results[2], 4); UASSERT_SELFTEST(const int, results[2], 4);
UASSERT_SELFTEST(nextChange, 21); UASSERT_SELFTEST(const int, nextChange, 21);
tree.find(21, results, nextChange); tree.find(21, results, nextChange);
UASSERT_SELFTEST(results.size(), 2); UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(results[0], 3); UASSERT_SELFTEST(const int, results[0], 3);
UASSERT_SELFTEST(results[1], 4); UASSERT_SELFTEST(const int, results[1], 4);
UASSERT_SELFTEST(nextChange, 25); // Center, or would be 30 UASSERT_SELFTEST(const int, nextChange, 25); // Center, or would be 30
tree.find(30, results, nextChange); tree.find(30, results, nextChange);
UASSERT_SELFTEST(results.size(), 2); UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(results[0], 3); UASSERT_SELFTEST(const int, results[0], 3);
UASSERT_SELFTEST(results[1], 4); UASSERT_SELFTEST(const int, results[1], 4);
UASSERT_SELFTEST(nextChange, 31); UASSERT_SELFTEST(const int, nextChange, 31);
tree.find(40, results, nextChange); tree.find(40, results, nextChange);
UASSERT_SELFTEST(results.size(), 2); UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(results[0], 2); UASSERT_SELFTEST(const int, results[0], 2);
UASSERT_SELFTEST(results[1], 4); UASSERT_SELFTEST(const int, results[1], 4);
UASSERT_SELFTEST(nextChange, 41); UASSERT_SELFTEST(const int, nextChange, 41);
tree.find(41, results, nextChange); tree.find(41, results, nextChange);
UASSERT_SELFTEST(results.size(), 0); UASSERT_SELFTEST(const size_t, results.size(), 0);
UASSERT_SELFTEST(nextChange, std::numeric_limits<int>::max()); UASSERT_SELFTEST(const int, nextChange, std::numeric_limits<int>::max());
// //
points = {{0, 0}}; points = {{0, 0}};
for (const auto& it : points) { for (const auto& it : points) {
@@ -536,8 +536,8 @@ public:
tree.build(data); tree.build(data);
// //
tree.find(50, results, nextChange); tree.find(50, results, nextChange);
UASSERT_SELFTEST(results.size(), 1); UASSERT_SELFTEST(const size_t, results.size(), 1);
UASSERT_SELFTEST(results[0], 5); UASSERT_SELFTEST(const int, results[0], 5);
} }
}; };
@@ -709,8 +709,8 @@ public:
// Tracks what matches are known to hit against V3ControlScopeTraceEntries // Tracks what matches are known to hit against V3ControlScopeTraceEntries
class V3ControlScopeTraceEntryMatch final { class V3ControlScopeTraceEntryMatch final {
public: public:
const V3ControlScopeTraceEntry* m_entryp; // Entry the match refers to const V3ControlScopeTraceEntry* m_entryp;
const string m_scopepart; // Scope prefix being matched against the entry const string m_scopepart;
V3ControlScopeTraceEntryMatch(const V3ControlScopeTraceEntry* entryp, const string& scopepart) V3ControlScopeTraceEntryMatch(const V3ControlScopeTraceEntry* entryp, const string& scopepart)
: m_entryp{entryp} : m_entryp{entryp}
, m_scopepart{scopepart} {} , m_scopepart{scopepart} {}
@@ -780,8 +780,8 @@ public:
// Resolve modules and files in the design // Resolve modules and files in the design
class V3ControlResolverHierWorkerEntry final { class V3ControlResolverHierWorkerEntry final {
const int m_workers; // Number of workers requested for the hierarchical block const int m_workers;
FileLine* const m_flp; // Location the worker count was specified at FileLine* const m_flp;
public: public:
explicit V3ControlResolverHierWorkerEntry(int workers, FileLine* flp) explicit V3ControlResolverHierWorkerEntry(int workers, FileLine* flp)
@@ -798,9 +798,9 @@ class V3ControlResolver final {
V3ControlScopeTraceResolver m_scopeTraces; // Regexp to trace enables V3ControlScopeTraceResolver m_scopeTraces; // Regexp to trace enables
std::unordered_map<string, std::unordered_map<string, uint64_t>> std::unordered_map<string, std::unordered_map<string, uint64_t>>
m_profileData; // Access to profile_data records m_profileData; // Access to profile_data records
uint8_t m_mode = NONE; // Kind of profile_data records is currently active uint8_t m_mode = NONE;
std::unordered_map<string, V3ControlResolverHierWorkerEntry> m_hierWorkers; std::unordered_map<string, V3ControlResolverHierWorkerEntry> m_hierWorkers;
FileLine* m_profileFileLine = nullptr; // Location profile_data was read from FileLine* m_profileFileLine = nullptr;
V3ControlResolver() = default; V3ControlResolver() = default;
~V3ControlResolver() = default; ~V3ControlResolver() = default;
+4 -15
View File
@@ -199,7 +199,6 @@ class CoverageVisitor final : public VNVisitor {
AstVar* const varp = new AstVar{fl_nowarn, VVarType::MODULETEMP, trace_var_name, AstVar* const varp = new AstVar{fl_nowarn, VVarType::MODULETEMP, trace_var_name,
incp->findUInt32DType()}; incp->findUInt32DType()};
varp->setIgnoreSchedWrite(); // Ignore the increment output, so no UNOPTFLAT varp->setIgnoreSchedWrite(); // Ignore the increment output, so no UNOPTFLAT
varp->lifetime(VLifetime::STATIC_IMPLICIT);
varp->trace(true); varp->trace(true);
m_modp->addStmtsp(varp); m_modp->addStmtsp(varp);
UINFO(5, "New coverage trace: " << varp); UINFO(5, "New coverage trace: " << varp);
@@ -284,7 +283,7 @@ class CoverageVisitor final : public VNVisitor {
VL_RESTORER_COPY(m_funcTemps); VL_RESTORER_COPY(m_funcTemps);
createHandle(nodep); createHandle(nodep);
m_modp = nodep; m_modp = nodep;
m_state.m_inModOff = nodep->isTop(); // Already made top shell, no coverage for it m_state.m_inModOff = false; // Haven't made top shell, so tops are real tops
if (!origModp) { if (!origModp) {
// No blocks cross (non-nested) modules, so save some memory // No blocks cross (non-nested) modules, so save some memory
m_varnames.clear(); m_varnames.clear();
@@ -461,7 +460,7 @@ class CoverageVisitor final : public VNVisitor {
if (const AstBasicDType* const basicp = VN_CAST(dtypep, BasicDType)) { if (const AstBasicDType* const basicp = VN_CAST(dtypep, BasicDType)) {
toggleVarBottom(above, varp, basicp->nrange()); toggleVarBottom(above, varp, basicp->nrange());
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { } else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
for (const int index_docs : adtypep->declRange().seqLoToHi()) { for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
const int index_code = index_docs - adtypep->lo(); const int index_code = index_docs - adtypep->lo();
ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]", ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]",
new AstArraySel{varp->fileline(), new AstArraySel{varp->fileline(),
@@ -472,7 +471,7 @@ class CoverageVisitor final : public VNVisitor {
newent.cleanup(); newent.cleanup();
} }
} else if (const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType)) { } else if (const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType)) {
for (const int index_docs : adtypep->declRange().seqLoToHi()) { for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
const AstNodeDType* const subtypep = adtypep->subDTypep()->skipRefp(); const AstNodeDType* const subtypep = adtypep->subDTypep()->skipRefp();
const int index_code = index_docs - adtypep->lo(); const int index_code = index_docs - adtypep->lo();
ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]", ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]",
@@ -1054,7 +1053,7 @@ class CoverageVisitor final : public VNVisitor {
unrolledp = new T_Oper{fl, selp, unrolledp}; unrolledp = new T_Oper{fl, selp, unrolledp};
} }
iterate(unrolledp); iterate(unrolledp);
VL_DO_DANGLING(pushDeletep(unrolledp), unrolledp); pushDeletep(unrolledp);
} else { } else {
iterateChildren(nodep); iterateChildren(nodep);
lineTrack(nodep); lineTrack(nodep);
@@ -1115,16 +1114,6 @@ class CoverageVisitor final : public VNVisitor {
} }
// VISITORS - BOTH // VISITORS - BOTH
void visit(AstProperty* nodep) override {
VL_RESTORER(m_state);
m_state.m_on = false;
iterateChildren(nodep);
}
void visit(AstSequence* nodep) override {
VL_RESTORER(m_state);
m_state.m_on = false;
iterateChildren(nodep);
}
void visit(AstNode* nodep) override { void visit(AstNode* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
lineTrack(nodep); lineTrack(nodep);
+578 -2911
View File
File diff suppressed because it is too large Load Diff
+72 -328
View File
@@ -37,8 +37,6 @@
#include "V3Dead.h" #include "V3Dead.h"
#include "V3ConstPool.h"
#include "V3Graph.h"
#include "V3Stats.h" #include "V3Stats.h"
#include <queue> #include <queue>
@@ -46,199 +44,6 @@
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Dead tracking graph
class DeadVertex final : public V3GraphVertex {
VL_RTTI_IMPL(DeadVertex, V3GraphVertex)
AstNode* const m_nodep; // Node that created vertex
uint64_t m_workPos = 0; // Position on DeadWorkList, 0 = not on list
bool m_removable = false; // Subject to dead removal
bool m_isVirtual = false; // Virtual node, indirection
public:
DeadVertex(V3Graph* graphp, AstNode* nodep)
: V3GraphVertex{graphp}
, m_nodep{nodep} {}
~DeadVertex() override = default;
string dotShape() const override {
return isVirtual() ? "diamond" : removable() ? "rectangle" : "ellipse";
}
AstNode* nodep() const VL_MT_STABLE { return m_nodep; }
string name() const override VL_MT_STABLE {
return (isVirtual() ? "[VIRT] "
: removable() ? "[R] "s
: "[NR] ")
+ nodep()->typeName() + ' ' + cvtToHex(nodep()) + ' ' + nodep()->name();
}
bool removable() const { return m_removable; }
void removable(bool flag) { m_removable = flag; }
bool isVirtual() const { return m_isVirtual; }
void isVirtual(bool flag) { m_isVirtual = flag; }
uint64_t workPos() const { return m_workPos; }
void workPos(uint64_t value) { m_workPos = value; }
};
// Work list which keeps ordering of elements (so stable), adds always to
// the end of the queue, and allows arbitrary removal
class DeadWorkList final {
// MEMBERS
static uint64_t s_sequence; // Sequence number, inc each push
std::map<uint64_t, DeadVertex*> m_works; // Work list by sequence
public:
// METHODS
bool empty() const { return m_works.empty(); }
void push(DeadVertex* vtxp) {
if (vtxp->workPos()) return; // Already on list
const uint64_t id = ++s_sequence;
vtxp->workPos(id);
m_works.emplace(id, vtxp);
UINFO(9, "Worklist.push " << vtxp);
}
void erase(DeadVertex* vtxp) {
const uint64_t id = vtxp->workPos();
if (!id) return;
UINFO(9, "Worklist.erase " << vtxp);
const auto it = m_works.find(id);
UASSERT_OBJ(it != m_works.end(), vtxp->nodep(),
"vertex thought to be on work list but not");
m_works.erase(it);
vtxp->workPos(0);
}
DeadVertex* getPopFront() {
UDEBUGONLY(UASSERT(!empty(), "Front invalid to call on empty list"););
const auto it = m_works.begin();
DeadVertex* const vtxp = it->second;
m_works.erase(it);
vtxp->workPos(0);
UINFO(9, "Worklist.getPopFront " << vtxp);
return vtxp;
}
};
uint64_t DeadWorkList::s_sequence = 0;
class DeadGraph final : public V3Graph {
// NODE STATE
// AstNodeFTask::user2p() -> DeadVertex* for this node
// See const VNUser2InUse m_inuser2; inside DeadVisitor
// MEMBERS
DeadWorkList m_funcs; // Functions eligble for deletion
// Each virtual vertex by the ftask name()
std::unordered_map<std::string, DeadVertex*> m_virtualVtxsp;
void newEdge(DeadVertex* fromp, DeadVertex* top) {
new V3GraphEdge{this, fromp, top, 1, false};
if (top->removable()) m_funcs.erase(top); // Now has an input edge
}
void pushWorkMaybe(DeadVertex* vtxp, bool allowSize1) {
if (vtxp->removable()) {
if (VN_IS(vtxp->nodep(), NodeFTask)
&& (vtxp->inEmpty() || (allowSize1 && vtxp->inSize1())))
m_funcs.push(vtxp);
}
}
public:
// METHODS
DeadGraph() = default;
~DeadGraph() override = default;
DeadVertex* findNewVertex(AstNode* nodep) {
DeadVertex* vtxp = nodep->user2u().to<DeadVertex*>();
if (!vtxp) {
vtxp = new DeadVertex{this, nodep};
nodep->user2p(vtxp);
pushWorkMaybe(vtxp, false);
}
UASSERT_OBJ(vtxp->nodep() == nodep, nodep, "Vertex points at different node");
return vtxp;
}
DeadVertex* findVirtualVertex(AstNode* nodep) {
const auto it = m_virtualVtxsp.find(nodep->name());
DeadVertex* vtxp;
if (it != m_virtualVtxsp.end()) {
vtxp = it->second;
} else {
vtxp = new DeadVertex{this, nodep};
vtxp->removable(true);
vtxp->isVirtual(true);
pushWorkMaybe(vtxp, false);
m_virtualVtxsp.emplace(nodep->name(), vtxp);
}
return vtxp;
}
void findNewRemovableVertex(AstNode* nodep, bool removable) {
DeadVertex* const vtxp = findNewVertex(nodep);
// Wasn't removable before (due to earlier insert), make removable now
if (removable && !vtxp->removable()) {
vtxp->removable(true);
pushWorkMaybe(vtxp, false);
}
}
void deleteNodeVertex(AstNode* nodep) {
if (DeadVertex* const vtxp = nodep->user2u().to<DeadVertex*>()) deleteVertex(vtxp);
}
void deleteVertex(DeadVertex* vtxp) {
UINFO(9, "Delete vertex " << vtxp->name());
// Mark all about-to-empty downstream vertices onto worklist
for (const V3GraphEdge& oedge : vtxp->outEdges()) {
DeadVertex* const toVtxp = static_cast<DeadVertex*>(oedge.top());
pushWorkMaybe(toVtxp, true); // size1 ok as about to delete below
}
// Node shouldn't be looking at user2p later as node being deleting, but in case
if (!vtxp->isVirtual()) vtxp->nodep()->user2p(nullptr);
if (vtxp->removable()) m_funcs.erase(vtxp);
VL_DO_DANGLING(vtxp->unlinkDelete(this), vtxp);
}
// This only tracks usage dependancy, not "containership",
// When all needs disappear the related node is eligble for deletion
void needs(AstNode* nodep, AstNode* parentp) {
if (parentp == nodep) return; // No need for tracking needs itself (recursion)
UINFO(9, "Edge node " << nodep << " -> " << parentp);
DeadVertex* const parentVtxp = findNewVertex(parentp);
DeadVertex* const nodeVtxp = findNewVertex(nodep);
UINFO(9, "Edge need " << parentVtxp << " -> " << nodeVtxp);
newEdge(parentVtxp, nodeVtxp);
}
void needsVirtual(AstNodeFTask* nodep, AstNode* parentp) {
// Virtual call can be to any function in the call hierarchy.
// For simplicity rather than tracking possible multiple base
// classes (due to 'implements' classes there can be more than
// one), we simply assume all virtual functions of the same name
// can call any other virtual function of the same name
// Track via an intermediate node.
// All calling parents' verticies -> Virtual Vertex -> all ftasks verticies
if (parentp == nodep) return; // No need for tracking needs itself (recursion)
DeadVertex* const parentVtxp = findNewVertex(parentp);
DeadVertex* const virtualVtxp = findVirtualVertex(nodep);
UINFO(9, "Edge needVirtual " << parentVtxp << " -> " << virtualVtxp);
newEdge(parentVtxp, virtualVtxp);
}
void funcVirtual(AstNodeFTask* nodep) {
// Virtual Vertex -> all ftasks verticies
DeadVertex* const virtualVtxp = findVirtualVertex(nodep);
DeadVertex* const nodeVtxp = findNewVertex(nodep);
UINFO(9, "Edge funcvirtual " << virtualVtxp << " -> " << nodeVtxp);
newEdge(virtualVtxp, nodeVtxp);
}
bool funcsEmpty() const { return m_funcs.empty(); }
AstNode* funcsGetPopFront() {
DeadVertex* const vtxp = m_funcs.getPopFront();
UASSERT_OBJ(vtxp->inEmpty(), vtxp->nodep(), "Non-empty node on work list");
UASSERT_OBJ(vtxp->removable(), vtxp->nodep(), "Non-removable node on work list");
if (vtxp->isVirtual()) {
// Emptied (no inbound edge) virtual wrapper; all
// destinations verticies are now unused too (e.g. all virtual
// functions of this name may be deleted)
UINFO(9, "Removing virtual " << vtxp);
deleteVertex(vtxp);
return nullptr; // Caller will search again
}
AstNode* const nodep = vtxp->nodep();
return nodep;
}
};
//###################################################################### //######################################################################
// Dead state, as a visitor of each AstNode // Dead state, as a visitor of each AstNode
@@ -247,11 +52,10 @@ class DeadVisitor final : public VNVisitor {
// Entire Netlist: // Entire Netlist:
// AstNodeModule::user1() -> int. Count of number of cells referencing this module. // AstNodeModule::user1() -> int. Count of number of cells referencing this module.
// AstVar::user1() -> int. Count of number of references // AstVar::user1() -> int. Count of number of references
// AstVar::user2() -> bool. Is a formal function argument
// AstVarScope::user1() -> int. Count of number of references // AstVarScope::user1() -> int. Count of number of references
// AstNodeDType::user1() -> int. Count of number of references // AstNodeDType::user1() -> int. Count of number of references
// AstNodeFTask::user1() -> int. Count of number of references (via AstNodeFTaskRefs)
const VNUser1InUse m_inuser1; const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2; // For usage information see DeadGraph
// TYPES // TYPES
using AssignMap = std::multimap<AstVarScope*, AstNodeAssign*>; using AssignMap = std::multimap<AstVarScope*, AstNodeAssign*>;
@@ -260,9 +64,6 @@ class DeadVisitor final : public VNVisitor {
const bool m_elimUserVars; // Allow removal of user's vars const bool m_elimUserVars; // Allow removal of user's vars
const bool m_elimDTypes; // Allow removal of DTypes const bool m_elimDTypes; // Allow removal of DTypes
const bool m_elimCells; // Allow removal of Cells const bool m_elimCells; // Allow removal of Cells
DeadGraph m_graph; // Tracking graph
// List of all encountered to avoid another loop through tree // List of all encountered to avoid another loop through tree
std::vector<AstVar*> m_varsp; std::vector<AstVar*> m_varsp;
std::vector<AstNode*> m_dtypeElimsp; // Data types might eliminate std::vector<AstNode*> m_dtypeElimsp; // Data types might eliminate
@@ -272,6 +73,7 @@ class DeadVisitor final : public VNVisitor {
std::vector<AstCell*> m_cellsp; std::vector<AstCell*> m_cellsp;
std::vector<AstClass*> m_classesp; std::vector<AstClass*> m_classesp;
std::vector<AstTypedef*> m_typedefsp; std::vector<AstTypedef*> m_typedefsp;
std::queue<AstNodeFTask*> m_tasksp; // All the tasks that could be removed if not called
AssignMap m_assignMap; // List of all simple assignments for each variable AssignMap m_assignMap; // List of all simple assignments for each variable
bool m_sideEffect = false; // Side effects discovered in assign RHS bool m_sideEffect = false; // Side effects discovered in assign RHS
@@ -280,20 +82,14 @@ class DeadVisitor final : public VNVisitor {
AstNodeDType* m_curDTypep = nullptr; // Current NodeDType AstNodeDType* m_curDTypep = nullptr; // Current NodeDType
AstNodeModule* m_modp = nullptr; // Current module AstNodeModule* m_modp = nullptr; // Current module
AstForeachHeader* m_foreachHeaderp = nullptr; // Current foreach header AstForeachHeader* m_foreachHeaderp = nullptr; // Current foreach header
AstNode* m_containingFTaskRefp = nullptr; // Parent of ftaskref (e.g. task/module)
bool m_inFTask = false; // Under an AstNodeFTask
// STATE - Statistic tracking // STATE - Statistic tracking
VDouble0 m_statFTasksDemoted; VDouble0 m_statFTasksDeadified;
VDouble0 m_statFTasksMDeadified;
VDouble0 m_statFTasksNMDeadified;
VDouble0 m_statFTasksVirtDeadified;
// METHODS // METHODS
void deleting(AstNode* nodep) { void deleting(AstNode* nodep) {
UINFO(9, " deleting " << nodep); UINFO(9, " deleting " << nodep);
m_graph.deleteNodeVertex(nodep);
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
} }
@@ -319,31 +115,15 @@ class DeadVisitor final : public VNVisitor {
m_dtypeElimsp.push_back(nodep); m_dtypeElimsp.push_back(nodep);
} }
if (VN_IS(m_modp, Package) || VN_IS(m_modp, Class)) m_dtypePkgsp.emplace(nodep, m_modp); if (VN_IS(m_modp, Package) || VN_IS(m_modp, Class)) m_dtypePkgsp.emplace(nodep, m_modp);
// A struct type moved to the type table refers to its package via classOrPackagep
if (const AstNodeUOrStructDType* const sdtypep = VN_CAST(nodep, NodeUOrStructDType)) {
if (sdtypep->classOrPackagep() && sdtypep->classOrPackagep() != m_modp) {
m_dtypePkgsp.emplace(nodep, sdtypep->classOrPackagep());
}
}
if (AstNode* const subnodep = nodep->virtRefDTypep()) subnodep->user1Inc(); if (AstNode* const subnodep = nodep->virtRefDTypep()) subnodep->user1Inc();
if (AstNode* const subnodep = nodep->virtRefDType2p()) subnodep->user1Inc(); if (AstNode* const subnodep = nodep->virtRefDType2p()) subnodep->user1Inc();
} }
void needsTask(AstNodeFTask* taskp, AstNode* containerp) {
if (!taskp) return; // Unlinked
if (taskp->isVirtual()) {
m_graph.needsVirtual(taskp, containerp);
} else {
m_graph.needs(taskp, containerp);
}
}
// VISITORS // VISITORS
void visit(AstNodeModule* nodep) override { void visit(AstNodeModule* nodep) override {
if (m_modp) m_modp->user1Inc(); // e.g. Class under Package if (m_modp) m_modp->user1Inc(); // e.g. Class under Package
VL_RESTORER(m_modp); VL_RESTORER(m_modp);
m_modp = nodep; m_modp = nodep;
VL_RESTORER(m_containingFTaskRefp);
m_containingFTaskRefp = nodep;
if (nodep->dead()) return; if (nodep->dead()) return;
if (nodep->modPublic()) m_modp->user1Inc(); if (nodep->modPublic()) m_modp->user1Inc();
iterateChildren(nodep); iterateChildren(nodep);
@@ -360,16 +140,6 @@ class DeadVisitor final : public VNVisitor {
iterateChildren(nodep); iterateChildren(nodep);
checkAll(nodep); checkAll(nodep);
if (nodep->scopep()) nodep->scopep()->user1Inc(); if (nodep->scopep()) nodep->scopep()->user1Inc();
// Keep formal arguments
for (AstVar* varp = nodep->argsp(); varp; varp = VN_AS(varp->nextp(), Var)) {
varp->user1Inc();
varp->user2(1);
}
}
void visit(AstPin* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->modVarp()) nodep->modVarp()->user1Inc();
} }
void visit(AstScope* nodep) override { void visit(AstScope* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
@@ -378,8 +148,6 @@ class DeadVisitor final : public VNVisitor {
// Class packages might have no children, but need to remain as // Class packages might have no children, but need to remain as
// long as the class they refer to is needed // long as the class they refer to is needed
if (VN_IS(m_modp, Class) || VN_IS(m_modp, ClassPackage)) nodep->user1Inc(); if (VN_IS(m_modp, Class) || VN_IS(m_modp, ClassPackage)) nodep->user1Inc();
// The constant pool scope must remain for entries created later
if (m_modp->isConstPool()) nodep->user1Inc();
if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp()) { if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp()) {
m_scopesp.push_back(nodep); m_scopesp.push_back(nodep);
} }
@@ -407,7 +175,7 @@ class DeadVisitor final : public VNVisitor {
iterateChildren(nodep); iterateChildren(nodep);
if (!m_sideEffect && !nodep->isPure()) m_sideEffect = true; if (!m_sideEffect && !nodep->isPure()) m_sideEffect = true;
checkAll(nodep); checkAll(nodep);
needsTask(nodep->taskp(), m_containingFTaskRefp); if (nodep->taskp()) nodep->taskp()->user1Inc();
if (nodep->classOrPackagep()) { if (nodep->classOrPackagep()) {
if (m_elimCells) { if (m_elimCells) {
nodep->classOrPackagep(nullptr); nodep->classOrPackagep(nullptr);
@@ -419,12 +187,7 @@ class DeadVisitor final : public VNVisitor {
void visit(AstModportFTaskRef* nodep) override { void visit(AstModportFTaskRef* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
checkAll(nodep); checkAll(nodep);
needsTask(nodep->ftaskp(), m_containingFTaskRefp); if (nodep->ftaskp()) nodep->ftaskp()->user1Inc();
}
void visit(AstModportVarRef* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->varp()) nodep->varp()->user1Inc();
} }
void visit(AstRefDType* nodep) override { void visit(AstRefDType* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
@@ -531,11 +294,6 @@ class DeadVisitor final : public VNVisitor {
iterateChildren(nodep); iterateChildren(nodep);
checkAll(nodep); checkAll(nodep);
if (m_foreachHeaderp) nodep->user1Inc(); if (m_foreachHeaderp) nodep->user1Inc();
// Keep formal arguments
if (m_inFTask && nodep->isIO()) {
nodep->user1Inc();
nodep->user2(1);
}
if (mightElimVar(nodep)) { if (mightElimVar(nodep)) {
m_varsp.push_back(nodep); m_varsp.push_back(nodep);
} else { } else {
@@ -569,21 +327,13 @@ class DeadVisitor final : public VNVisitor {
if (assignInAssign) m_sideEffect = true; // Parent assign shouldn't optimize if (assignInAssign) m_sideEffect = true; // Parent assign shouldn't optimize
} }
void visit(AstNodeFTask* nodep) override { void visit(AstNodeFTask* nodep) override {
const bool removable = !(nodep->taskPublic() || nodep->dpiExport() || nodep->dpiImport()
|| nodep->keepAlive() || nodep->isConstructor()
|| (!v3Global.opt.fDeadMethods() && nodep->classMethod()));
m_graph.findNewRemovableVertex(nodep, removable);
//
VL_RESTORER(m_containingFTaskRefp);
VL_RESTORER(m_inFTask);
m_containingFTaskRefp = nodep;
m_inFTask = true;
iterateChildren(nodep); iterateChildren(nodep);
checkAll(nodep); checkAll(nodep);
if (nodep->isVirtual()) m_graph.funcVirtual(nodep); if (nodep->taskPublic() || nodep->dpiExport() || nodep->dpiImport()) {
if (!removable) {
if (m_modp && !m_modp->dead() && !m_modp->verilatorLib()) if (m_modp && !m_modp->dead() && !m_modp->verilatorLib())
m_modp->user1Inc(); // Keep container m_modp->user1Inc(); // Keep container
} else {
m_tasksp.push(nodep);
} }
if (nodep->classOrPackagep()) { if (nodep->classOrPackagep()) {
if (m_elimCells) { if (m_elimCells) {
@@ -624,41 +374,19 @@ class DeadVisitor final : public VNVisitor {
} }
void deadCheckTasks() { void deadCheckTasks() {
while (!m_graph.funcsEmpty()) { while (!m_tasksp.empty()) {
AstNode* const nodep = m_graph.funcsGetPopFront(); AstNodeFTask* taskp = m_tasksp.front();
if (!nodep) continue; m_tasksp.pop();
UINFO(9, "Dead " << nodep); if (taskp->user1() == 0 && !taskp->classMethod()) {
if (AstNodeFTask* const taskp = VN_CAST(nodep, NodeFTask)) { taskp->foreach([this](AstNodeFTaskRef* ftaskrefp) {
if (taskp->isVirtual()) { AstNodeFTask* task2p = ftaskrefp->taskp();
++m_statFTasksVirtDeadified; if (!task2p) return;
} else if (taskp->classMethod()) { task2p->user1Inc(-1);
++m_statFTasksMDeadified; if (task2p->user1() == 0) m_tasksp.push(task2p);
} else { });
++m_statFTasksNMDeadified; taskp->user1(-1); // we don't want to try deleting twice
} deleting(taskp);
} ++m_statFTasksDeadified;
deleting(nodep);
}
}
void deadCheckDemote() {
for (V3GraphVertex& gvtx : m_graph.vertices()) {
DeadVertex* const vtxp = gvtx.cast<DeadVertex>();
// A isVirtual vertex with single out means there's only one target virtual function
// that can be virtually called, so can make it non-virtual for faster execution
// (UVM benefits from this)
if (!vtxp->outSize1()) continue;
if (!vtxp->isVirtual()) continue;
for (V3GraphEdge& edge : vtxp->outEdges()) { // Always a single one
AstNode* const nodep = edge.top()->as<DeadVertex>()->nodep();
AstNodeFTask* const funcp = VN_AS(nodep, NodeFTask);
UASSERT_OBJ(funcp->isVirtual(), funcp,
"Only virtual ftasks should be under DeadVirtualVertex");
if (v3Global.opt.fDeadMethods()) {
funcp->isVirtual(false);
UINFO(9, "Demote to non-virtual " << funcp);
++m_statFTasksDemoted;
}
} }
} }
} }
@@ -672,11 +400,7 @@ class DeadVisitor final : public VNVisitor {
AstNodeModule* nextmodp; AstNodeModule* nextmodp;
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp = nextmodp) { for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp = nextmodp) {
nextmodp = VN_AS(modp->nextp(), NodeModule); nextmodp = VN_AS(modp->nextp(), NodeModule);
// Keep $unit until m_elimCells stages. Note v3Global.opt.serializeOnly() if (modp->dead() || (!modp->isTop() && modp->user1() == 0 && !modp->internal())) {
// won't reach this stage, and will always have an empty $unit. That's ok.
// The constant pool is always kept, entries might be created later.
const bool keep = (!m_elimCells && modp->isDollarUnit()) || modp->isConstPool();
if (modp->dead() || (!modp->isTop() && modp->user1() == 0 && !keep)) {
// > 2 because L1 is the wrapper, L2 is the top user module // > 2 because L1 is the wrapper, L2 is the top user module
UINFO(4, " Dead module " << modp); UINFO(4, " Dead module " << modp);
// And its children may now be killable too; correct counts // And its children may now be killable too; correct counts
@@ -686,7 +410,6 @@ class DeadVisitor final : public VNVisitor {
cellp->modp()->user1Inc(-1); cellp->modp()->user1Inc(-1);
}); });
} }
if (modp->isDollarUnit()) v3Global.rootp()->dollarUnitPkgp(nullptr);
deleting(modp); deleting(modp);
retry = true; retry = true;
} }
@@ -695,7 +418,7 @@ class DeadVisitor final : public VNVisitor {
} }
bool mightElimVar(const AstVar* nodep) const { bool mightElimVar(const AstVar* nodep) const {
if (nodep->isSigPublic()) return false; // Can't elim publics! if (nodep->isSigPublic()) return false; // Can't elim publics!
if (nodep->isPrimaryIO() || nodep->isClassMember() || nodep->sensIfacep()) return false; if (nodep->isIO() || nodep->isClassMember() || nodep->sensIfacep()) return false;
if (nodep->isTemp() && !nodep->isTrace()) return true; if (nodep->isTemp() && !nodep->isTrace()) return true;
return m_elimUserVars; // Post-Trace can kill most anything return m_elimUserVars; // Post-Trace can kill most anything
} }
@@ -747,8 +470,6 @@ class DeadVisitor final : public VNVisitor {
void deadCheckVar() { void deadCheckVar() {
// Delete any unused varscopes // Delete any unused varscopes
for (AstVarScope* vscp : m_vscsp) { for (AstVarScope* vscp : m_vscsp) {
// Keep formal arguments
if (vscp->varp()->user2()) continue;
if (vscp->user1() == 0) { if (vscp->user1() == 0) {
UINFO(4, " Dead " << vscp); UINFO(4, " Dead " << vscp);
const std::pair<AssignMap::iterator, AssignMap::iterator> eqrange const std::pair<AssignMap::iterator, AssignMap::iterator> eqrange
@@ -801,17 +522,53 @@ class DeadVisitor final : public VNVisitor {
} }
} }
// cppcheck-suppress constParameterPointer
void preserveTopIfaces(AstNetlist* rootp) {
// cppcheck-suppress constVariablePointer
for (AstNodeModule* modp = rootp->modulesp(); modp && modp->isTop();
modp = VN_AS(modp->nextp(), NodeModule)) {
for (AstNode* subnodep = modp->stmtsp(); subnodep; subnodep = subnodep->nextp()) {
if (AstVar* const varp = VN_CAST(subnodep, Var)) {
if (varp->isIfaceRef()) {
const AstNodeDType* const subtypep = varp->subDTypep();
const AstIfaceRefDType* ifacerefp = nullptr;
if (VN_IS(subtypep, IfaceRefDType)) {
ifacerefp = VN_AS(varp->subDTypep(), IfaceRefDType);
} else if (VN_IS(subtypep, BracketArrayDType)) {
const AstBracketArrayDType* const arrp
= VN_AS(subtypep, BracketArrayDType);
const AstNodeDType* const arrsubtypep = arrp->subDTypep();
if (VN_IS(arrsubtypep, IfaceRefDType)) {
ifacerefp = VN_AS(arrsubtypep, IfaceRefDType);
}
} else if (VN_IS(subtypep, UnpackArrayDType)) {
const AstUnpackArrayDType* const arrp
= VN_AS(subtypep, UnpackArrayDType);
const AstNodeDType* const arrsubtypep = arrp->subDTypep();
if (VN_IS(arrsubtypep, IfaceRefDType)) {
ifacerefp = VN_AS(arrsubtypep, IfaceRefDType);
}
}
if (ifacerefp && !ifacerefp->cellp()
&& (ifacerefp->ifacep()->user1() == 0)) {
ifacerefp->ifacep()->user1(1);
}
}
}
}
}
}
public: public:
// CONSTRUCTORS // CONSTRUCTORS
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes, DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes,
bool elimCells, bool elimTasks) bool elimCells, bool elimTopIfaces, bool elimTasks)
: m_elimUserVars{elimUserVars} : m_elimUserVars{elimUserVars}
, m_elimDTypes{elimDTypes} , m_elimDTypes{elimDTypes}
, m_elimCells{elimCells} , m_elimCells{elimCells} {
, m_containingFTaskRefp{nodep} {
// Prepare to remove some datatypes // Prepare to remove some datatypes
nodep->typeTablep()->clearCache(); nodep->typeTablep()->clearCache();
// Operate on whole netlist // Operate on whole netlist
iterate(nodep); iterate(nodep);
@@ -825,11 +582,6 @@ public:
if (itr.first->user1()) itr.second->user1Inc(); if (itr.first->user1()) itr.second->user1Inc();
} }
// Simplify redundant edges (e.g. function calls another function many times)
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
if (dumpGraphLevel() >= 9 || debug() >= 9)
m_graph.dumpDotFilePrefixed("dead_graph", false);
if (elimTasks) deadCheckTasks(); if (elimTasks) deadCheckTasks();
deadCheckTypedefs(); deadCheckTypedefs();
deadCheckVar(); deadCheckVar();
@@ -839,24 +591,16 @@ public:
if (elimCells) deadCheckCells(); if (elimCells) deadCheckCells();
deadCheckClasses(); deadCheckClasses();
// Modules after vars, because might be vars we delete inside a mod we delete // Modules after vars, because might be vars we delete inside a mod we delete
if (!elimTopIfaces) preserveTopIfaces(nodep);
deadCheckMod(); deadCheckMod();
// After deleting as much as can, demote some virtual functions
if (elimTasks) deadCheckDemote();
// We may have removed some datatypes, cleanup // We may have removed some datatypes, cleanup
nodep->typeTablep()->repairCache(); nodep->typeTablep()->repairCache();
VIsCached::clearCacheTree(); // Removing assignments may affect isPure VIsCached::clearCacheTree(); // Removing assignments may affect isPure
V3ConstPool::invalidateCache(); // Might have deleted constant pool entries nodep->constPoolp()->rebuildVarScopesAndCache();
} }
~DeadVisitor() override { ~DeadVisitor() override {
V3Stats::addStatSum("Optimizations, FTasks, virtual-to-nonvirtual demotion", V3Stats::addStatSum("Optimizations, deadified FTasks", m_statFTasksDeadified);
m_statFTasksDemoted);
V3Stats::addStatSum("Optimizations, FTasks, deadified, methods", m_statFTasksMDeadified);
V3Stats::addStatSum("Optimizations, FTasks, deadified, non-methods",
m_statFTasksNMDeadified);
V3Stats::addStatSum("Optimizations, FTasks, deadified, virtual",
m_statFTasksVirtDeadified);
}; };
}; };
@@ -865,32 +609,32 @@ public:
void V3Dead::deadifyModules(AstNetlist* nodep) { void V3Dead::deadifyModules(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ // node, elimUserVars, elimDTypes, elimScopes, elimCells, elimTasks { // node, elimUserVars, elimDTypes, elimScopes, elimCells, elimTopIfaces
DeadVisitor{nodep, false, false, false, false, false}; DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported(), false};
} // Destruct before checking } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeEitherLevel() >= 6); V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeEitherLevel() >= 6);
} }
void V3Dead::deadifyDTypes(AstNetlist* nodep) { void V3Dead::deadifyDTypes(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ DeadVisitor{nodep, false, true, false, false, true}; } // Destruct before checking { DeadVisitor{nodep, false, true, false, false, false, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTreeEitherLevel() >= 3);
} }
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) { void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ DeadVisitor{nodep, false, true, true, false, false}; } // Destruct before checking { DeadVisitor{nodep, false, true, true, false, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTreeEitherLevel() >= 3);
} }
void V3Dead::deadifyAll(AstNetlist* nodep) { void V3Dead::deadifyAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ DeadVisitor{nodep, true, true, false, true, true}; } // Destruct before checking { DeadVisitor{nodep, true, true, false, true, false, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTreeEitherLevel() >= 3);
} }
void V3Dead::deadifyAllScoped(AstNetlist* nodep) { void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ DeadVisitor{nodep, true, true, true, true, true}; } // Destruct before checking { DeadVisitor{nodep, true, true, true, true, false, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTreeEitherLevel() >= 3);
} }
+23 -141
View File
@@ -118,7 +118,6 @@
#include "V3Delayed.h" #include "V3Delayed.h"
#include "V3AstUserAllocator.h" #include "V3AstUserAllocator.h"
#include "V3ClassGraph.h"
#include "V3Const.h" #include "V3Const.h"
#include "V3Stats.h" #include "V3Stats.h"
@@ -251,7 +250,6 @@ class DelayedVisitor final : public VNVisitor {
// AstVar::user1() -> bool. Set true if already issued MULTIDRIVEN warning // AstVar::user1() -> bool. Set true if already issued MULTIDRIVEN warning
// AstVarRef::user1() -> bool. Set true if target of NBA // AstVarRef::user1() -> bool. Set true if target of NBA
// AstAssignDly::user1() -> bool. Set true if already visited // AstAssignDly::user1() -> bool. Set true if already visited
// AstCFunc::user1() -> AstUser1Allocator. See `m_cfuncsCache` below
// AstAssignDly::user2p() -> AstVarScope*: Scope this AstAssignDelay is under // AstAssignDly::user2p() -> AstVarScope*: Scope this AstAssignDelay is under
// AstNodeModule::user1p() -> std::unorded_map<std::string, AstVar*> temp map via m_varMap // AstNodeModule::user1p() -> std::unorded_map<std::string, AstVar*> temp map via m_varMap
// AstScope::user1() -> int: Temporary counter for this scope // AstScope::user1() -> int: Temporary counter for this scope
@@ -261,24 +259,6 @@ class DelayedVisitor final : public VNVisitor {
const VNUser1InUse m_user1InUse; const VNUser1InUse m_user1InUse;
const VNUser2InUse m_user2InUse; const VNUser2InUse m_user2InUse;
const VNUser3InUse m_user3InUse; const VNUser3InUse m_user3InUse;
struct CFuncCache final {
VInsertionSet<AstSenTree*> m_timingDomains; // What shall be added to m_timingDomains
std::set<AstCFunc*>
m_includes; // CFuncs whose CFuncCache shall be included into this - this is used to
// break cycles: A->B->A (instead of visiting A while it is still begin
// visited B just marks that it includes A)
enum State : uint8_t {
UNINITIALIZED = 0, // Not initialized members are empty
VISITING, // Visiting - needed for breaking recursion
INITIALIZED, // Members contains correct values
} m_state // Current state of Cache
= UNINITIALIZED;
};
// Caches what should be added to m_timingDomains because of calls to the AstCFunc (with
// recursive check of other AstCFuncs called from inside)
AstUser1Allocator<AstCFunc, CFuncCache> m_cfuncsCache;
AstUser1Allocator<AstNodeModule, std::unordered_map<std::string, AstVar*>> m_varMap; AstUser1Allocator<AstNodeModule, std::unordered_map<std::string, AstVar*>> m_varMap;
AstUser1Allocator<AstVarScope, VarScopeInfo> m_vscpInfo; AstUser1Allocator<AstVarScope, VarScopeInfo> m_vscpInfo;
AstUser3Allocator<AstVarScope, std::vector<WriteReference>> m_writeRefs; AstUser3Allocator<AstVarScope, std::vector<WriteReference>> m_writeRefs;
@@ -286,10 +266,6 @@ class DelayedVisitor final : public VNVisitor {
// STATE - across all visitors // STATE - across all visitors
VInsertionSet<AstSenTree*> m_timingDomains; // Timing resume domains VInsertionSet<AstSenTree*> m_timingDomains; // Timing resume domains
const std::unique_ptr<V3ClassGraph>
m_classGraphp; // class graph to get possibly called functions from a virtual call
std::vector<const AstCFunc*> m_callStack; // Current callstack of AstCFuncs
// STATE - for current visit position (use VL_RESTORER) // STATE - for current visit position (use VL_RESTORER)
AstActive* m_activep = nullptr; // Current activate AstActive* m_activep = nullptr; // Current activate
const AstCFunc* m_cfuncp = nullptr; // Current public C Function const AstCFunc* m_cfuncp = nullptr; // Current public C Function
@@ -300,7 +276,6 @@ class DelayedVisitor final : public VNVisitor {
bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
bool m_inNonCombLogic = false; // We are in non-combinational logic bool m_inNonCombLogic = false; // We are in non-combinational logic
bool m_needsInitialTrigger = false; // Whether a NodeProcedure needs a initial trigger bool m_needsInitialTrigger = false; // Whether a NodeProcedure needs a initial trigger
std::vector<AstSenTree*> m_nbaEventSenTreeps; // Sensitivities of '->>' in the process
AstVarRef* m_currNbaLhsRefp = nullptr; // Current NBA LHS variable reference AstVarRef* m_currNbaLhsRefp = nullptr; // Current NBA LHS variable reference
// STATE - during NBA conversion (after visit) // STATE - during NBA conversion (after visit)
@@ -318,7 +293,6 @@ class DelayedVisitor final : public VNVisitor {
VDouble0 m_nSchemeValueQueuesPartial; // Number of variables using Scheme::ValueQueuePartial VDouble0 m_nSchemeValueQueuesPartial; // Number of variables using Scheme::ValueQueuePartial
VDouble0 m_nSharedSetFlags; // "Set" flags actually shared by Scheme::FlagShared variables VDouble0 m_nSharedSetFlags; // "Set" flags actually shared by Scheme::FlagShared variables
VDouble0 m_nInitialNBA; // Number of procedural blocks with initial NBA VDouble0 m_nInitialNBA; // Number of procedural blocks with initial NBA
VDouble0 m_nonInlinedCAwaitsWithSenTree; // Count uses of not inlined co_awaits
// METHODS // METHODS
@@ -589,17 +563,6 @@ class DelayedVisitor final : public VNVisitor {
return ss.str(); return ss.str();
} }
void addCFuncCachedValues(const AstCFunc* const cfuncp,
std::unordered_set<const AstCFunc*>& visited) {
if (!visited.insert(cfuncp).second) return;
CFuncCache& value = m_cfuncsCache(cfuncp);
m_timingDomains.insert(value.m_timingDomains.begin(), value.m_timingDomains.end());
m_nonInlinedCAwaitsWithSenTree += value.m_timingDomains.size();
for (const AstCFunc* const includedp : value.m_includes) {
addCFuncCachedValues(includedp, visited);
}
}
// Create a temporary variable in the given 'scopep', with the given 'name', and with 'dtypep' // Create a temporary variable in the given 'scopep', with the given 'name', and with 'dtypep'
// type, with the bits selected by 'sLsbp'/'sWidthp' set to 'valuep', other bits set to zero. // type, with the bits selected by 'sLsbp'/'sWidthp' set to 'valuep', other bits set to zero.
// Insert new statements before 'insertp'. // Insert new statements before 'insertp'.
@@ -821,7 +784,7 @@ class DelayedVisitor final : public VNVisitor {
m_prevVscp = vscp; m_prevVscp = vscp;
// Delete original NBA // Delete original NBA
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); pushDeletep(nodep->unlinkFrBack());
} }
// Scheme::FlagUnique // Scheme::FlagUnique
@@ -871,7 +834,7 @@ class DelayedVisitor final : public VNVisitor {
ifp->addThensp(new AstAssign{flp, capturedLhsp, capturedRhsp}); ifp->addThensp(new AstAssign{flp, capturedLhsp, capturedRhsp});
// Delete original NBA // Delete original NBA
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); pushDeletep(nodep->unlinkFrBack());
} }
// Scheme::ValueQueuePartial/Scheme::ValueQueueWhole // Scheme::ValueQueuePartial/Scheme::ValueQueueWhole
@@ -902,9 +865,8 @@ class DelayedVisitor final : public VNVisitor {
activep->addStmtsp(postp); activep->addStmtsp(postp);
// Add the commit // Add the commit
AstCMethodHard* const callp = new AstCMethodHard{ AstCMethodHard* const callp = new AstCMethodHard{
flp, new AstVarRef{flp, queueVscp, VAccess::READWRITE}, VCMethod::NBA_COMMIT}; flp, new AstVarRef{flp, queueVscp, VAccess::READWRITE}, VCMethod::SCHED_COMMIT};
callp->dtypeSetVoid(); callp->dtypeSetVoid();
// TODO: this is a partial update, so must be READWRITE, but that breaks scheduling
callp->addPinsp(new AstVarRef{flp, vscp, VAccess::WRITE}); callp->addPinsp(new AstVarRef{flp, vscp, VAccess::WRITE});
postp->addStmtsp(callp->makeStmt()); postp->addStmtsp(callp->makeStmt());
} }
@@ -1013,7 +975,7 @@ class DelayedVisitor final : public VNVisitor {
// Enqueue the update at the site of the original NBA // Enqueue the update at the site of the original NBA
AstCMethodHard* const callp = new AstCMethodHard{ AstCMethodHard* const callp = new AstCMethodHard{
flp, new AstVarRef{flp, vscpInfo.valueQueueKit().vscp, VAccess::READWRITE}, flp, new AstVarRef{flp, vscpInfo.valueQueueKit().vscp, VAccess::READWRITE},
VCMethod::NBA_ENQUEUE}; VCMethod::SCHED_ENQUEUE};
callp->dtypeSetVoid(); callp->dtypeSetVoid();
callp->addPinsp(valuep); callp->addPinsp(valuep);
if (partial) callp->addPinsp(maskp); if (partial) callp->addPinsp(maskp);
@@ -1021,7 +983,7 @@ class DelayedVisitor final : public VNVisitor {
nodep->addHereThisAsNext(callp->makeStmt()); nodep->addHereThisAsNext(callp->makeStmt());
// Delete original NBA // Delete original NBA
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); pushDeletep(nodep->unlinkFrBack());
} }
// Record where a variable is assigned // Record where a variable is assigned
@@ -1045,43 +1007,6 @@ class DelayedVisitor final : public VNVisitor {
return procedurep && procedurep->sentreep(); return procedurep && procedurep->sentreep();
} }
// Visit AstCFunc from a AstNodeCCall - this is made into a separate quasi-visitor because
// AstCFunc that is not called from the code (e.g.: DPI exports) does not need to be visited
// this way. Also, not visiting such AstCFuncs allows to avoid caching results for them which
// this function does - which could lead to excessive memory usage
void visitCalledCFunc(AstCFunc* const nodep) {
CFuncCache& value = m_cfuncsCache(nodep);
switch (value.m_state) {
case CFuncCache::UNINITIALIZED: {
// Save current state
VL_RESTORER_CLEAR(m_timingDomains);
// Visit
value.m_state = CFuncCache::VISITING;
m_callStack.push_back(nodep);
{
VL_RESTORER(m_cfuncp);
m_cfuncp = nodep;
iterateChildren(nodep);
}
m_callStack.pop_back();
value.m_state = CFuncCache::INITIALIZED;
// Save a cache
std::swap(m_timingDomains, value.m_timingDomains);
} break;
case CFuncCache::VISITING: {
for (size_t i = m_callStack.size() - 1; m_callStack.at(i) != nodep; --i) {
m_cfuncsCache(m_callStack[i]).m_includes.insert(nodep);
}
return; // Break recursion
}
case CFuncCache::INITIALIZED: break;
}
std::unordered_set<const AstCFunc*> visited;
addCFuncCachedValues(nodep, visited);
}
// VISITORS // VISITORS
void visit(AstNetlist* nodep) override { void visit(AstNetlist* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
@@ -1178,6 +1103,11 @@ class DelayedVisitor final : public VNVisitor {
m_scopep = nodep; m_scopep = nodep;
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
m_cfuncp = nodep;
iterateChildren(nodep);
}
void visit(AstActive* nodep) override { void visit(AstActive* nodep) override {
UASSERT_OBJ(!m_activep, nodep, "Should not nest"); UASSERT_OBJ(!m_activep, nodep, "Should not nest");
VL_RESTORER(m_activep); VL_RESTORER(m_activep);
@@ -1191,7 +1121,6 @@ class DelayedVisitor final : public VNVisitor {
} }
void visit(AstNodeProcedure* nodep) override { void visit(AstNodeProcedure* nodep) override {
VL_RESTORER(m_needsInitialTrigger); VL_RESTORER(m_needsInitialTrigger);
VL_RESTORER_CLEAR(m_nbaEventSenTreeps);
const size_t firstNBAAddedIndex = m_nbas.size(); const size_t firstNBAAddedIndex = m_nbas.size();
{ {
VL_RESTORER(m_inSuspendableOrFork); VL_RESTORER(m_inSuspendableOrFork);
@@ -1243,8 +1172,6 @@ class DelayedVisitor final : public VNVisitor {
for (size_t i = firstNBAAddedIndex; i < m_nbas.size(); ++i) { for (size_t i = firstNBAAddedIndex; i < m_nbas.size(); ++i) {
m_vscpInfo(m_nbas[i].vscp).addSensitivity(senItemp); m_vscpInfo(m_nbas[i].vscp).addSensitivity(senItemp);
} }
for (AstSenTree* const senTreep : m_nbaEventSenTreeps)
senTreep->addSensesp(senItemp->cloneTree(true));
// Done with these // Done with these
VL_DO_DANGLING(senItemp->deleteTree(), senItemp); VL_DO_DANGLING(senItemp->deleteTree(), senItemp);
} }
@@ -1255,7 +1182,6 @@ class DelayedVisitor final : public VNVisitor {
} }
void visit(AstCAwait* nodep) override { void visit(AstCAwait* nodep) override {
if (nodep->sentreep()) m_timingDomains.insert(nodep->sentreep()); if (nodep->sentreep()) m_timingDomains.insert(nodep->sentreep());
iterateChildren(nodep);
} }
void visit(AstFireEvent* nodep) override { void visit(AstFireEvent* nodep) override {
UASSERT_OBJ(v3Global.hasEvents(), nodep, "Inconsistent"); UASSERT_OBJ(v3Global.hasEvents(), nodep, "Inconsistent");
@@ -1270,15 +1196,8 @@ class DelayedVisitor final : public VNVisitor {
cstmtp->add(");"); cstmtp->add(");");
AstNode* newp = cstmtp; AstNode* newp = cstmtp;
const AstVarRef* const vrefp = VN_CAST(eventp, VarRef); if (nodep->isDelayed()) {
if (nodep->isDelayed() && (m_cfuncp || !vrefp)) { const AstVarRef* const vrefp = VN_AS(eventp, VarRef);
// V3Timing converts these with --timing
nodep->v3warn(E_NOTIMING, "Nonblocking event trigger "
<< (m_cfuncp ? "in a non-inlined function/task"
: "of a class or interface member or "
"array element")
<< " requires --timing");
} else if (nodep->isDelayed()) {
const std::string newvarname = "__Vdly__" + vrefp->varp()->shortName(); const std::string newvarname = "__Vdly__" + vrefp->varp()->shortName();
AstVarScope* const dlyvscp AstVarScope* const dlyvscp
= createTemp(flp, vrefp->varScopep()->scopep(), newvarname, 1); = createTemp(flp, vrefp->varScopep()->scopep(), newvarname, 1);
@@ -1287,35 +1206,20 @@ class DelayedVisitor final : public VNVisitor {
return new AstVarRef{flp, dlyvscp, access}; return new AstVarRef{flp, dlyvscp, access};
}; };
AstAlwaysPre* const prep = new AstAlwaysPre{flp};
prep->addStmtsp(new AstAssign{flp, dlyRef(VAccess::WRITE),
new AstConst{flp, AstConst::BitFalse{}}});
AstAlwaysPost* const postp = new AstAlwaysPost{flp}; AstAlwaysPost* const postp = new AstAlwaysPost{flp};
AstIf* const ifp = new AstIf{flp, dlyRef(VAccess::READ)}; {
postp->addStmtsp(ifp); AstIf* const ifp = new AstIf{flp, dlyRef(VAccess::READ)};
postp->addStmtsp(ifp);
ifp->addThensp(newp);
}
UASSERT_OBJ(m_activep, nodep, "No active to handle FireEvent"); UASSERT_OBJ(m_activep, nodep, "No active to handle FireEvent");
AstSenTree* senTreep = m_activep->sentreep(); AstActive* const activep = new AstActive{flp, "nba-event", m_activep->sentreep()};
AstActive* activep = nullptr;
if (m_inSuspendableOrFork && (senTreep->hasInitial() || senTreep->hasClocked())) {
// Suspendable code can trigger whenever it resumes, so fire when its NBAs are
// committed (see visit(AstNodeProcedure*)), clearing the flag like
// Scheme::FlagUnique
senTreep = new AstSenTree{
flp, senTreep->hasClocked() ? senTreep->sensesp()->cloneTree(true) : nullptr};
m_nbaEventSenTreeps.push_back(senTreep);
m_needsInitialTrigger |= m_timingDomains.empty();
dlyvscp->varp()->setIgnorePostWrite();
ifp->addThensp(new AstAssign{flp, dlyRef(VAccess::WRITE),
new AstConst{flp, AstConst::BitFalse{}}});
activep = new AstActive{flp, "nba-event", senTreep};
activep->senTreeStorep(senTreep);
} else {
activep = new AstActive{flp, "nba-event", senTreep};
AstAlwaysPre* const prep = new AstAlwaysPre{flp};
prep->addStmtsp(new AstAssign{flp, dlyRef(VAccess::WRITE),
new AstConst{flp, AstConst::BitFalse{}}});
activep->addStmtsp(prep);
}
ifp->addThensp(newp);
m_activep->addNextHere(activep); m_activep->addNextHere(activep);
activep->addStmtsp(prep);
activep->addStmtsp(postp); activep->addStmtsp(postp);
newp = new AstAssign{flp, dlyRef(VAccess::WRITE), newp = new AstAssign{flp, dlyRef(VAccess::WRITE),
@@ -1433,24 +1337,6 @@ class DelayedVisitor final : public VNVisitor {
m_inLoop = true; m_inLoop = true;
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstNodeCCall* const nodep) override {
iterateChildren(nodep);
// We need to visit bodies of non-inlined functions
const auto& cfuncps = m_classGraphp->getCallPossibleCFuncs(nodep);
if (cfuncps.empty()) {
visitCalledCFunc(nodep->funcp());
} else {
for (AstCFunc* const cfuncp : cfuncps) visitCalledCFunc(cfuncp);
}
}
void visit(AstCFunc* const nodep) override {
const auto& value = m_cfuncsCache(nodep);
// Check whether it was already visited by visitCalledCFunc()
if (value.m_state != CFuncCache::UNINITIALIZED) return;
VL_RESTORER(m_cfuncp);
m_cfuncp = nodep;
iterateChildren(nodep);
}
// Pre/Post logic are created here and their content need no further changes, so ignore. // Pre/Post logic are created here and their content need no further changes, so ignore.
void visit(AstAlwaysPre*) override {} void visit(AstAlwaysPre*) override {}
@@ -1461,10 +1347,7 @@ class DelayedVisitor final : public VNVisitor {
public: public:
// CONSTRUCTORS // CONSTRUCTORS
explicit DelayedVisitor(AstNetlist* nodep) explicit DelayedVisitor(AstNetlist* nodep) { iterate(nodep); }
: m_classGraphp{V3ClassGraph::build(nodep)} {
iterate(nodep);
}
~DelayedVisitor() override { ~DelayedVisitor() override {
V3Stats::addStat("NBA, variables using ShadowVar scheme", m_nSchemeShadowVar); V3Stats::addStat("NBA, variables using ShadowVar scheme", m_nSchemeShadowVar);
V3Stats::addStat("NBA, variables using ShadowVarMasked scheme", m_nSchemeShadowVarMasked); V3Stats::addStat("NBA, variables using ShadowVarMasked scheme", m_nSchemeShadowVarMasked);
@@ -1475,7 +1358,6 @@ public:
m_nSchemeValueQueuesPartial); m_nSchemeValueQueuesPartial);
V3Stats::addStat("Optimizations, NBA flags shared", m_nSharedSetFlags); V3Stats::addStat("Optimizations, NBA flags shared", m_nSharedSetFlags);
V3Stats::addStat("Procedures needing initial NBA trigger", m_nInitialNBA); V3Stats::addStat("Procedures needing initial NBA trigger", m_nInitialNBA);
V3Stats::addStat("Non-inlined co_awaits with SenTree", m_nonInlinedCAwaitsWithSenTree);
} }
}; };
+1 -4
View File
@@ -110,10 +110,7 @@ class DepthVisitor final : public VNVisitor {
// (Here instead of new visitor after V3Descope just to avoid another visitor) // (Here instead of new visitor after V3Descope just to avoid another visitor)
void needNonStaticFunc(AstNode* nodep) { void needNonStaticFunc(AstNode* nodep) {
UASSERT_OBJ(m_cfuncp, nodep, "Non-static accessor not under a function"); UASSERT_OBJ(m_cfuncp, nodep, "Non-static accessor not under a function");
// DPI export implementations are always invoked via the generic DPI dispatcher, if (m_cfuncp->isStatic()) {
// which can only ever supply the symbol table pointer, never a scope-specific
// "this", so they must remain static regardless of what they contain.
if (m_cfuncp->isStatic() && !m_cfuncp->dpiExportImpl()) {
UINFO(5, "Mark non-public due to " << nodep); UINFO(5, "Mark non-public due to " << nodep);
m_cfuncp->isStatic(false); m_cfuncp->isStatic(false);
} }
+3 -4
View File
@@ -26,7 +26,6 @@
#include "V3Descope.h" #include "V3Descope.h"
#include "V3ConstPool.h"
#include "V3EmitCBase.h" #include "V3EmitCBase.h"
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
@@ -160,7 +159,8 @@ class DescopeVisitor final : public VNVisitor {
if (AstVar* const portp = VN_CAST(stmtp, Var)) { if (AstVar* const portp = VN_CAST(stmtp, Var)) {
if (portp->isIO() && !portp->isFuncReturn()) { if (portp->isIO() && !portp->isFuncReturn()) {
AstVarRef* const newp = new AstVarRef{ AstVarRef* const newp = new AstVarRef{
portp->fileline(), portp, portp->direction().pinAccess()}; portp->fileline(), portp,
portp->isWritable() ? VAccess::WRITE : VAccess::READ};
argsp = AstNode::addNext(argsp, newp); argsp = AstNode::addNext(argsp, newp);
} }
} }
@@ -236,7 +236,7 @@ class DescopeVisitor final : public VNVisitor {
if (varp->isFuncLocal()) { if (varp->isFuncLocal()) {
// Reference to function locals need no self pointer // Reference to function locals need no self pointer
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()}); nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
} else if (scopep->modp()->isConstPool()) { } else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) {
// Reference to constant pool value need no self pointer // Reference to constant pool value need no self pointer
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()}); nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
} else { } else {
@@ -295,6 +295,5 @@ public:
void V3Descope::descopeAll(AstNetlist* nodep) { void V3Descope::descopeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ DescopeVisitor{nodep}; } // Destruct before checking { DescopeVisitor{nodep}; } // Destruct before checking
V3ConstPool::setDescoped();
V3Global::dumpCheckGlobalTree("descope", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("descope", 0, dumpTreeEitherLevel() >= 3);
} }

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