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
1228 changed files with 17673 additions and 59579 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
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: Regression
name: Regressions
on:
push:
branches-ignore:
- 'dependabot/**' # Avoid duplicates: only run the PR, not the push
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
workflow_dispatch:
schedule:
- cron: '0 0 * * 0' # weekly
@@ -23,77 +22,14 @@ defaults:
working-directory: repo
concurrency:
# 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
# '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'
}}
# At most 1 job per branch. Auto cancel on pull requests and on all forks
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || github.repository != 'verilator/verilator' }}
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:
name: Build | 26.04 | gcc
needs: start
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
@@ -102,7 +38,6 @@ jobs:
build-2604-clang:
name: Build | 26.04 | clang
needs: start
uses: ./.github/workflows/reusable-build.yml
with:
cc: clang
@@ -112,7 +47,6 @@ jobs:
build-2404-gcc:
name: Build | 24.04 | gcc
needs: start
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
@@ -121,7 +55,6 @@ jobs:
build-2404-clang:
name: Build | 24.04 | clang
needs: start
uses: ./.github/workflows/reusable-build.yml
with:
cc: clang
@@ -130,25 +63,22 @@ jobs:
build-2204-gcc:
name: Build | 22.04 | gcc
needs: start
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
runs-on: ubuntu-22.04
sha: ${{ github.sha }}
build-macos-26-clang:
name: Build | macos-26 | clang
needs: start
build-macos-15-clang:
name: Build | macos-15 | clang
uses: ./.github/workflows/reusable-build.yml
with:
cc: clang
runs-on: macos-26
runs-on: macos-15
sha: ${{ github.sha }}
build-windows:
name: Build | windows-2025-vs2026 | msvc
needs: start
runs-on: windows-2025-vs2026
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
@@ -237,29 +167,4 @@ jobs:
lint-py:
name: Lint Python
needs: start
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
concurrency:
# 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
# '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'
}}
# At most 1 job per branch. Auto cancel all but scheduled jobs
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name != 'schedule' }}
jobs:
start:
name: Start
build:
name: Build
# 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
if: |
(github.event_name == 'schedule'
&& vars.ENABLE_SCHEDULED_JOBS == 'true') ||
(github.event_name == 'pull_request'
&& ((github.event.action == 'labeled'
&& 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')))) ||
&& contains(github.event.pull_request.labels.*.name, 'pr: dev-coverage')) ||
(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
with:
cc: gcc
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
# merge commit, otherwise patch coverage would include the changes
# between the root of the pull request and the target branch
@@ -104,7 +55,7 @@ jobs:
archive: ${{ needs.build.outputs.archive }}
cc: gcc
dev-gcov: true
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
suite: ${{ matrix.test }}${{ matrix.num }}
strategy:
fail-fast: false
@@ -118,7 +69,7 @@ jobs:
name: Publish results to codecov.io
needs: test
if: ${{ contains(needs.*.result, 'success') && !cancelled() }}
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
@@ -137,7 +88,7 @@ jobs:
find obj_coverage -type f | paste -sd, | sed "s/^/files=/" >> "$GITHUB_OUTPUT"
- name: Upload to codecov.io
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7
with:
disable_file_fixes: true
disable_search: true
@@ -151,7 +102,7 @@ jobs:
name: Prepare HTML report
needs: [build, test]
if: ${{ contains(needs.*.result, 'success') && !cancelled() }}
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
steps:
- name: Download repository archive
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
@@ -199,16 +150,6 @@ jobs:
path: report-artifact
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
if: ${{ github.event_name == 'pull_request' }}
uses: ./repo/.github/actions/upload-pr-notification
@@ -222,7 +163,7 @@ jobs:
name: Create issue on failure
needs: [publish-codecov, prepare-report]
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:
# Creating issues requires elevated privilege
- name: Generate access token
+3 -3
View File
@@ -64,10 +64,10 @@ jobs:
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
- name: Set up QEMU
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4
with:
buildkitd-flags: --debug
@@ -78,7 +78,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- 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'
with:
context: ${{ env.build_context }}
+4 -33
View File
@@ -15,36 +15,16 @@ defaults:
run:
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:
format:
runs-on: ubuntu-24.04
name: Ubuntu 24.04 | format
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
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
path: repo
token: ${{ steps.generate-token.outputs.token || secrets.GITHUB_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install packages for build
uses: ./repo/.github/actions/install-deps
@@ -52,18 +32,9 @@ jobs:
stage: format
- name: Configure git identity
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APP_SLUG: ${{ steps.generate-token.outputs.app-slug }}
run: |
if [ -n "$APP_SLUG" ]; then
user_id=$(gh api "/users/${APP_SLUG}[bot]" --jq .id)
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
git config --global user.email "[email protected]"
git config --global user.name "github action"
- name: Configure
run: |
@@ -84,6 +55,6 @@ jobs:
- name: Push
run: |-
if [ -n "$(git status --porcelain)" ]; then
git commit . -m "Apply 'make format'" &&
git commit . -m "Apply 'make format' [ci skip]" &&
git push origin
fi
+2 -2
View File
@@ -69,11 +69,11 @@ jobs:
- name: Deploy to GitHub Pages
id: deploy-1
continue-on-error: true
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
- name: Deploy to GitHub Pages (retry)
id: deploy-2
if: steps.deploy-1.outcome == 'failure'
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
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
uses: ./repo/.github/actions/setup-venv
with:
# Check the event too: 'github.ref' is the base branch on a closed pull request
save: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/master' }}
save: ${{ github.ref == 'refs/heads/master' }}
- name: Lint
run: |-
+2 -3
View File
@@ -92,8 +92,7 @@ jobs:
- name: Set up Python venv
uses: ./repo/.github/actions/setup-venv
with:
# Check the event too: 'github.ref' is the base branch on a closed pull request
save: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/master' }}
save: ${{ github.ref == 'refs/heads/master' }}
- name: Test
id: run-test
@@ -126,7 +125,7 @@ jobs:
path: ${{ env.CCACHE_DIR }}
key: ${{ steps.ccache.outputs.key }}
# 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
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
+15 -59
View File
@@ -22,36 +22,8 @@ defaults:
shell: bash
concurrency:
# 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
# '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'
}}
# At most 1 job per branch. Auto cancel all but scheduled jobs
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name != 'schedule' }}
jobs:
@@ -64,30 +36,14 @@ jobs:
(github.event_name == 'schedule'
&& vars.ENABLE_SCHEDULED_JOBS == 'true') ||
(github.event_name == 'pull_request'
&& ((github.event.action == 'labeled'
&& github.event.label.name == 'pr: rtlmeter')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter')))) ||
&& contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter')) ||
(github.event_name == 'workflow_dispatch') ||
(github.event_name == 'push')
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
outputs:
old-sha: ${{ steps.start.outputs.old-sha }}
cases: ${{ steps.cases.outputs.cases }}
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
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
@@ -114,7 +70,7 @@ jobs:
cc: gcc
ccwarn: false
install: true
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
sha: ${{ github.sha }}
build-clang-new:
@@ -125,7 +81,7 @@ jobs:
cc: clang
ccwarn: false
install: true
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
sha: ${{ github.sha }}
build-gcc-old:
@@ -137,7 +93,7 @@ jobs:
cc: gcc
ccwarn: false
install: true
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
sha: ${{ needs.start.outputs.old-sha }}
build-clang-old:
@@ -149,7 +105,7 @@ jobs:
cc: clang
ccwarn: false
install: true
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
sha: ${{ needs.start.outputs.old-sha }}
run-gcc:
@@ -165,7 +121,7 @@ jobs:
uses: ./.github/workflows/reusable-rtlmeter-run.yml
with:
tag: gcc
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
cc: gcc
verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }}
verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }}
@@ -192,7 +148,7 @@ jobs:
uses: ./.github/workflows/reusable-rtlmeter-run.yml
with:
tag: clang
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
cc: clang
verilator-archive-new: ${{ needs.build-clang-new.outputs.archive }}
verilator-archive-old: ${{ needs.build-clang-old.outputs.archive }}
@@ -219,7 +175,7 @@ jobs:
uses: ./.github/workflows/reusable-rtlmeter-run.yml
with:
tag: gcc-hier
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
cc: gcc
verilator-archive-new: ${{ needs.build-gcc-new.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')))
)}}
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
outputs:
# The run tags, derived from the 'run-*' dependency job names
run-tags: ${{ steps.tags.outputs.tags }}
@@ -327,7 +283,7 @@ jobs:
# dashboard UI currently assumes there is only one data point per
# 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() }}
runs-on: ubuntu-26.04
runs-on: ubuntu-24.04
steps:
- name: Download combined results
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
@@ -378,7 +334,7 @@ jobs:
name: Prepare Pull Request results
needs: combine-results
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:
actions: read
steps:
@@ -435,7 +391,7 @@ jobs:
name: Create issue on failure
needs: publish-scheduled-results
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:
# Creating issues requires elevated privilege
- 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
`--enable-ccwarn` so a new compiler warning stops the build.
- Run one test from the repository root: `test_regress/t/t_<name>.py`.
- Run the full regression 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).
______________________________________________________________________
@@ -93,8 +93,8 @@ ______________________________________________________________________
- [ ] Ran `make format` (clang-format), `make cppcheck`, and `make lint-py`;
self-reviewed the diff for leftover debug code, stale comments, and
copy-paste errors.
- [ ] Ran the full regression tests on at least one OS before submitting. Partial
runs are fine during development, but the submitted PR is expected to pass every
- [ ] Ran the full regression on at least one OS before submitting. Partial runs
are fine during development, but the submitted PR is expected to pass every
test.
- [ ] Validated the code changes against recommendations in the AGENTS.md files.
- [ ] 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
- Subject line is short and imperative and conventionally ends with the
issue number being fixed. No need for the PR number this will be added
during git squash. e.g `Support property case (#7721)`. The commit body message is
optional and common for non-trivial changes.
- Subject line is short and imperative and conventionally ends with the PR number:
`Support property case (#7721)`. A body is optional and common for non-trivial
changes.
+2 -31
View File
@@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(
Verilator
VERSION 5.053
VERSION 5.052
HOMEPAGE_URL https://verilator.org
LANGUAGES CXX
)
@@ -93,36 +93,7 @@ if(OBJCACHE_ENABLED)
endif()
find_package(BISON)
# Prefer the header installed with the selected generator, including through
# 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)
find_package(FLEX)
# Build
#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!
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
==========================
@@ -2003,7 +1855,7 @@ Verilator 5.016 2023-09-16
* 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 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 using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon]
* 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 arrayed instances to unpacked of same size. (#1015) [Varun Koyyalagunta]
* 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
@@ -3878,7 +3730,7 @@ Verilator 3.870 2015-02-12
* Suppress COMBDLY when inside always_latch. (#864) [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 $sccanf from string. (#866) [David Pierce]
* Fix VM_PARALLEL_BUILDS broke in 3.868. (#870) [Hiroki Honda]
-2
View File
@@ -747,7 +747,6 @@ FASTCOV_OPT += BROKEN_RTN
FASTCOV_OPT += NUM_ASSERT
FASTCOV_OPT += SELF_CHECK
FASTCOV_OPT += UASSERT
FASTCOV_OPT += UDEBUGONLY
FASTCOV_OPT += UINFO
FASTCOV_OPT += assert
FASTCOV_OPT += 'if (VL_UNCOVERABLE'
@@ -763,7 +762,6 @@ GENHTML_OPT += --rc branch_coverage=1
GENHTML_OPT += --rc genhtml_hi_limit=100
GENHTML_OPT += --ignore-errors negative
GENHTML_OPT += --ignore-errors inconsistent
GENHTML_OPT += --ignore-errors empty
ifeq ($(COVERAGE_REF_BASE),)
GENHTML_OPT += --header-title "Code coverage for Verilator $(shell git describe --dirty)"
else
+11 -11
View File
@@ -23,8 +23,8 @@
.. |badge5| image:: https://img.shields.io/docker/pulls/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
:target: https://github.com/verilator/verilator/actions/workflows/regression.yml
.. |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/build-test.yml
.. |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
@@ -35,7 +35,7 @@ Welcome to Verilator
.. list-table::
- - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.**
- Accepts Verilog or SystemVerilog, including UVM
- Accepts Verilog or SystemVerilog
- Performs lint code-quality checks
- Compiles into multithreaded C++, or SystemC
- 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
libraries, optionally encrypted, into other simulators.
Verilator supports all design constructs, most verification constructs
including UVM, intra-assignment delays (e.g, `#10`), and events.
Tristate-bus (`z`) and unknowns (`x`) are handled in limited contexts, in a
special manor for performance. It currently may not be the best choice if
you are expecting a full-featured replacement for a closed-source Verilog
simulator, performing SDF annotation, or mixed-signal simulation. However,
if you are looking for a path to migrate SystemVerilog to C++/SystemC, or
want high-speed simulation, Verilator is the tool for you.
Verilator supports all design constructs, most verification constructs,
intra-assignment delays (e.g, `#10`), and events. Tristate-bus (`z`) and
unknowns (`x`) are handled in limited contexts, in a special manor for
performance. It currently may not be the best choice if you are expecting a
full-featured replacement for a closed-source Verilog simulator, performing
SDF annotation, or mixed-signal simulation. However, if you are looking for
a path to migrate SystemVerilog to C++/SystemC, or want high-speed
simulation, Verilator is the tool for you.
Performance
===========
-2
View File
@@ -419,8 +419,6 @@ detailed descriptions of these arguments.
--coverage-expr-max <value> Maximum permutations allowed for an expression
--coverage-fsm Enable FSM state/arc 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-per-instance Enable per-instance coverage counters
--coverage-toggle Enable toggle coverage
-10
View File
@@ -79,13 +79,6 @@ fi
if [ "$OPT_LIGHT_DEBUG" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-light-debug"
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
./configure $CONFIGURE_ARGS CXX="$CXX"
@@ -95,9 +88,6 @@ autoconf
ccache -z
BUILD_START=$SECONDS
"$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 --evict-older-than "$((SECONDS - BUILD_START + 60))s"
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
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
# artifacts across all run attempts, and a re-run uploads a new one while
# keeping the previous attempt's artifact.
ARTIFACT_IDS=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts?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
for ARTIFACT_ID in ${ARTIFACT_IDS}; do
-8
View File
@@ -25,8 +25,6 @@ CASES = {
("BlackParrot:2x2:*", 14),
("BlackParrot:4x4:*", 25),
("Caliptra:default:*", 19),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("HummingbirdV2-E203:default:*", 3),
("HummingbirdV2-E203:nodelay:*", 3),
("NVDLA:*", 29),
@@ -61,8 +59,6 @@ CASES = {
("BlackParrot:2x2:*", 9),
("BlackParrot:4x4:*", 12),
("Caliptra:default:*", 16),
("HammerBlade:16x8:*", 4),
("HammerBlade:2x1:*", 2),
("HummingbirdV2-E203:default:*", 10),
("HummingbirdV2-E203:nodelay:*", 10),
("NVDLA:*", 28),
@@ -114,8 +110,6 @@ CASES_PR = {
("BlackParrot:1x1:*", 5),
("BlackParrot:4x4:*", 25),
("Caliptra:default:*", 19),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("NVDLA:*", 29),
("HummingbirdV2-E203:default:*", 3),
("HummingbirdV2-E203:nodelay:*", 3),
@@ -148,8 +142,6 @@ CASES_PR = {
("BlackParrot:1x1:*", 5),
("BlackParrot:4x4:*", 12),
("Caliptra:default:*", 16),
("HammerBlade:16x8:*", 4),
("HammerBlade:2x1:*", 2),
("NVDLA:*", 28),
("HummingbirdV2-E203:default:*", 10),
("HummingbirdV2-E203:nodelay:*", 10),
-1
View File
@@ -29,7 +29,6 @@ RUN apt-get update \
python3 \
zlib1g \
zlib1g-dev \
liblz4-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
+1 -18
View File
@@ -12,7 +12,7 @@
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.053 devel],
AC_INIT([Verilator],[5.052 2026-09-05],
[https://verilator.org],
[verilator],[https://verilator.org])
@@ -264,7 +264,6 @@ fi
python3_version=$($PYTHON3 --version | head -1)
AC_MSG_RESULT([$PYTHON3 --version = $python3_version])
AC_ARG_VAR([LEX], [Flex executable])
AC_CHECK_PROG(LEX,flex,flex)
if test "x$LEX" = "x" ; then
AC_MSG_ERROR([Cannot find "flex" in your PATH, please install it])
@@ -272,22 +271,6 @@ fi
flex_version=$($LEX --version | head -1)
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)
if test "x$YACC" = "x" ; then
AC_MSG_ERROR([Cannot find "bison" in your PATH, please install it])
+1 -22
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.
00>> Please insert into sorted position, and see header as to agreement
24bit-xjkp
404allen404
Adam Bagley
@@ -47,7 +46,6 @@ BRDR LIFE
Brian Li
Cameron Kirk
Cameron Waite
CatalpaEel
Chih-Mao Chen
Chris Bachhuber
Chris Randall
@@ -63,13 +61,11 @@ Daniel Bates
Danny Oler
Dave Sargeant
David Garau
David Harris
David Horton
David Ledger
David Metz
David Stanford
David Turner
Deanyone Su
dependabot[bot]
Dercury
Demin Han
@@ -78,11 +74,9 @@ Dominick Grochowina
Don Williamson
Dragon-Git
Drew Ranck
Drew Risinger
Drew Taussig
Driss Hafdi
Edgar E. Iglesias
Edmund Lam
Eddie Rydell
em2machine
emmettifelts
@@ -98,7 +92,6 @@ Fan Shupei
february cozzocrea
Felix Neumärker
Felix Yan
Francesco Urbani
Frans Skarman
Fuad Ismail
Furqan Nadir
@@ -120,7 +113,6 @@ Han Qi
Harald Heckmann
Hennadii Chernyshchyk
hjsanjana
Hongseok Choi
Howard Su
Huang Rui
Huanghuang Zhou
@@ -153,7 +145,6 @@ jeffrey.song
Jens Yuechao Liu
Jeremy Bennett
Jesse Taube
Jesús Alberto López Soto
Jevin Sweval
Jiacheng Qian
Jiamin Zhu
@@ -191,7 +182,6 @@ Keith Colbert
Kevin Kiningham
Kevin Nygaard
Kornel Uriasz
Kristof Marien
Kritik Bhimani
Krzysztof Bieganski
Krzysztof Boronski
@@ -215,13 +205,11 @@ Maciej Sobkowski
Marcel Chang
Marco Bartoli
Marco Brambilla
Marco Frank
Marco Widmer
Mariusz Glebocki
Markus Krause
Marlon James
Marshal Qiao
Martijn Wobbes
Martin Schmidt
Martin Stadler
Mateusz Gancarz
@@ -236,7 +224,6 @@ Michael Rogenmoser
Michal Czyz
Michaël Lefebvre
Miguel Perez Andrade
Mike Murunov
Mike Popoloski
Miodrag Milanović
Mladen Slijepcevic
@@ -249,11 +236,9 @@ Natan Kreimer
Nathan Graybeal
Nathan Kohagen
Nathan Myers
Nazar Zibilyuk
Nick Brereton
Nikolai Kumar
Nikolay Puzanov
Noam Gallmann
Nolan Poe
Oleh Maksymenko
Patrick Creighton
@@ -296,7 +281,6 @@ Ryan Ziegler
Ryszard Rozak
Saksham Gupta
Samuel Riedel
Saqib Khan
Sean Cross
Sebastien Van Cauwenberghe
Secturion
@@ -307,7 +291,6 @@ Seth Pellegrino
Shashvat Prabhu
Shogo Yamazaki
Shou-Li Hsu
Shriyans Sahoo
spomatasmd
Srinivasan Venkataramanan
Stefan Wallentowitz
@@ -316,10 +299,8 @@ Steven Hugg
Stuart Morris
sumpster
Szymon Gizler
Szymon Jędras
Sören Tempel
Sumanth Kadiyala
Sunimali Rathnayake
Teng Huang
Thomas Aldrian
Thomas Brown
@@ -340,11 +321,9 @@ Tracy Narine
Trung Nguyen
Tudor Timi
Tymoteusz Blazejczyk
Tynan McAuley
Tyrone Marhguy
Udaya Raj Subedi
Udi Finkelstein
UnsignedByte
Unai Martinez-Corral
Valentin Atepalikhin
Varun Koyyalagunta
@@ -378,4 +357,4 @@ Zixi Li
Zubin Jain
Àlex Torregrosa
Í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
.. 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
-27
View File
@@ -40,30 +40,6 @@ model:
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:
Model interface changes in version 4.210
@@ -231,9 +207,6 @@ command line, or the link), you'd then:
#include "Vour__Dpi.h"
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
-------------------------
+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
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
Verilator currently supports the default malloc, tcmalloc, or jemalloc.
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`.
.. 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>
Rarely needed. Specify the maximum bit width of a signal subject to
@@ -733,8 +706,6 @@ Summary:
.. option:: -fno-dead-cells
.. option:: -fno-dead-methods
.. option:: -fno-dedup
.. option:: -fno-dfg
@@ -840,10 +811,6 @@ Summary:
.. 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-merge-cond
@@ -852,11 +819,6 @@ Summary:
.. 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-reorder
@@ -1828,11 +1790,12 @@ Summary:
.. option:: --structs-packed
Removed in 5.054.
Deprecated; discontinue use of this option.
Converted all unpacked structures to packed structures, Specifying this
option allowed for backward compatibility with versions before Verilator
5.006, when Verilator would always pack unpacked structures.
Converts all unpacked structures to packed structures, and issues an
:option:`UNPACKED` warning. Specifying this option allows for backward
compatibility with versions before Verilator 5.006, when Verilator would
always pack unpacked structures.
.. option:: -sv
@@ -2304,7 +2267,7 @@ Summary:
If using `--x-assign unique`, use the
:vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>`
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
any seeds selected, and code to enable rerunning with that same seed,
so you can reproduce bugs.
@@ -2333,7 +2296,7 @@ Summary:
If using `--x-initial unique`, use the
:vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>`
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
any seeds selected, and code to enable rerunning with that same seed,
so you can reproduce bugs.
+1 -1
View File
@@ -28,7 +28,7 @@ Quick Install
and install.
#. For build on Windows using MSVC set environment variable WIN_FLEX_BISON
to install directory. For build on Windows/Linux/OS-X using ninja set
the 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.
To obtain Verilator sources download
+1 -1
View File
@@ -174,7 +174,7 @@ once, after ``configure``:
# Install dependencies
sudo apt install python3-pip
# Create Python virtual environment in .venv:
# Create Python virutal environment in .venv:
make venv
# 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
designs where C++ compilation speed can dominate, consider using lower
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.
If you will be running many simulations on a single model, you can
+7 -7
View File
@@ -592,10 +592,6 @@ List Of Warnings
``covergroup``, ``coverpoint``, and coverage options, and the
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.
Ignoring this warning may make Verilator ignore lint checking on the
@@ -2503,10 +2499,14 @@ List Of Warnings
.. 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
by the since-removed `--structs-packed` option.
Ignoring this warning will make Verilator treat the structure as packed,
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
+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.
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
++++++++++++++++++++
@@ -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
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
respectively 16,384 and 4,096. The method of doing this is
system-dependent, but on Fedora Linux it would require editing the
@@ -2413,7 +2422,7 @@ driver.py Non-Scenario Arguments
.. option:: --quiet
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``.
.. option:: --rerun
-10
View File
@@ -305,7 +305,6 @@ Mahmoudy
Makefile
Makefiles
Maksymenko
Manzoni
Marche
Markley
Marquet
@@ -347,7 +346,6 @@ Narcis
Nassim
Nauticus
Nayak
Nazar
Nenakhov
Newgard
Nigam
@@ -590,7 +588,6 @@ Yurii
Zaruba
Zhang
Zhi
Zibilyuk
abirkmanis
accessor
accessors
@@ -630,7 +627,6 @@ benchmarking
biguint
binToOneHot
bindir
binsof
biops
bisonpre
bitOpTree
@@ -722,7 +718,6 @@ deduplication
defenv
defname
defparam
dehash
demangling
dep
deparametrized
@@ -742,7 +737,6 @@ devel
difftree
dir
disambiguates
discoverability
displayb
distcc
doxygen
@@ -953,7 +947,6 @@ makefile
makefiles
malloc
manpages
memoization
metacomment
metacomments
miree
@@ -1139,7 +1132,6 @@ shortint
shortreal
signame
signedness
slp
sp
specparam
splitme
@@ -1190,7 +1182,6 @@ systemc
taskify
tcmalloc
tcmalloc
teardown
tenghtt
testbench
testbenches
@@ -1264,7 +1255,6 @@ vcddiff
vcoverage
vdhotre
vec
vectorize
ventana
venv
ver
+95 -147
View File
@@ -1178,29 +1178,19 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
} else if (formatAttr == VL_VFORMATATTR_STRING) {
thingp = va_arg(ap, std::string*);
if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override
} else if (formatAttr == VL_VFORMATATTR_ENUM
|| formatAttr == VL_VFORMATATTR_ENUM_SIGNED) {
const int numericAttr = formatAttr == VL_VFORMATATTR_ENUM_SIGNED
? VL_VFORMATATTR_SIGNED
: VL_VFORMATATTR_UNSIGNED;
} else if (formatAttr == VL_VFORMATATTR_ENUM) {
// Always <= VL_QUADSIZE; emit uses non-ENUM format for wider enums
lbits = va_arg(ap, int);
if (lbits <= VL_QUADSIZE) {
ld = VL_VA_ARG_Q_(ap, lbits);
strwide.resize(2);
WDataOutP strwidep = WDataOutP::external(strwide.data());
VL_SET_WQ(strwidep, ld);
lwp = strwidep;
} else {
lwp = WDataInP::external(va_arg(ap, const EData*));
ld = VL_SET_QW(lwp);
}
ld = VL_VA_ARG_Q_(ap, lbits);
strwide.resize(2);
WDataOutP strwidep = WDataOutP::external(strwide.data());
VL_SET_WQ(strwidep, ld);
lwp = strwidep;
lsb = lbits - 1;
++argn; // Enum value is followed by the generated name string argument
static_cast<void>(va_arg(ap, int)); // VL_VFORMATATTR_STRING
enump = va_arg(ap, std::string*);
if (fmt != 'p' && fmt != 's') {
formatAttr = numericAttr;
} else if (enump && !enump->empty()) {
if (enump && !enump->empty()) {
formatAttr = (fmt == 'p') ? VL_VFORMATATTR_COMPLEX : VL_VFORMATATTR_STRING;
thingp = const_cast<std::string*>(enump);
} 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;
widthSet = true;
fmt = 'd';
formatAttr = numericAttr;
formatAttr = VL_VFORMATATTR_UNSIGNED;
}
if (widthSet && width == 0) {
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;
} else if (formatAttr == VL_VFORMATATTR_STRING) {
const std::string* const strp = static_cast<const std::string*>(thingp);
output += VL_TO_STRING(*strp);
output += '"' + *strp + '"';
} else if (formatAttr == VL_VFORMATATTR_COMPLEX) {
const std::string* const strp = static_cast<const std::string*>(thingp);
output += *strp;
@@ -1832,8 +1822,8 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
return got;
done:
// Scan stopped early; EOF only if input ended before the first conversion
if (got == 0 && _vl_vsss_eof(fp, floc)) return -1;
// Scan stopped early, return parsed or EOF
if (_vl_vsss_eof(fp, floc)) return -1;
return got;
}
@@ -2498,30 +2488,6 @@ std::string VL_TO_STRING(QData lhs) {
std::string VL_TO_STRING(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) {
return VL_SFORMATF_N_NX("'h%0x", 1, VL_VFORMATATTR_UNSIGNED, words * VL_EDATASIZE, obj);
}
@@ -3128,16 +3094,41 @@ VerilatedContext::Serialized::Serialized() {
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
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.
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,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the
// selected bits until Unlock; On/Off/Kill leave locked bits unchanged.
// +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 lockedMask = mask & ~m_s.m_assertLock;
switch (controlType) {
@@ -3187,6 +3178,26 @@ void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t typ
.c_str());
}
}
uint32_t
VerilatedContext::assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
const uint32_t mask = assertOnMask(type, directive);
if (!mask) return 0;
switch (query) { // LCOV_EXCL_BR_LINE
case VerilatedAssertCtlQuery::ASSERT_CTL_ON: return (m_s.m_assertOn & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_KILL:
assert(mask && (mask & (mask - 1)) == 0);
return m_s.m_assertKill[VL_CLOG2_I(mask)];
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
return (m_s.m_assertPassOnVacuous & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_NONVACUOUS:
return (m_s.m_assertPassOnNonvacuous & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_FAIL_ON: return (m_s.m_assertFailOn & mask) != 0;
default: // LCOV_EXCL_START
VL_FATAL_MT("", 0, "", "Internal: Bad assertCtlGet query");
VL_UNREACHABLE;
} // LCOV_EXCL_STOP
}
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_calcUnusedSigs = flag;
@@ -3370,15 +3381,6 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
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) {
if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "Simulation threads must be >= 1");
@@ -3389,10 +3391,14 @@ void VerilatedContext::threads(unsigned n) {
}
m_useNumaAssign = true;
m_threadsSet = true;
if (m_threads == n) return; // To avoid unnecessary warnings
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; }
@@ -3458,15 +3464,7 @@ void VerilatedContext::addModel(const VerilatedModel* modelp) {
}
}
VerilatedVirtualBase* VerilatedContext::threadPoolp(unsigned modelThreads) {
// 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);
}
VerilatedVirtualBase* VerilatedContext::threadPoolp() {
if (m_threads == 1) return nullptr;
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
return m_threadPool.get();
@@ -3582,7 +3580,7 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
std::string str;
uint64_t u64;
if (arg == "+verilator+assert+lock") {
m_ns.m_assertCtlsLocked = true;
assertCtlsLocked(true);
} else if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
coverageFilename(str);
} else if (arg == "+verilator+debug") {
@@ -3747,17 +3745,11 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
// VerilatedContext:: Methods - scopes
void VerilatedContext::scopesDump() const VL_MT_SAFE {
{
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
VL_PRINTF_MT(" scopesDump:\n");
for (const auto& i : m_impdatap->m_nameMap) {
const VerilatedScope* const scopep = i.second;
scopep->scopeDump();
}
}
{
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
for (const auto& i : m_impdatap->m_ifaceRefMap) i.second.ifaceRefDump();
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
VL_PRINTF_MT(" scopesDump:\n");
for (const auto& i : m_impdatap->m_nameMap) {
const VerilatedScope* const scopep = i.second;
scopep->scopeDump();
}
VL_PRINTF_MT("\n");
}
@@ -3787,31 +3779,6 @@ const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
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
@@ -4182,6 +4149,28 @@ std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { retu
//======================================================================
// 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 {
if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr;
if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr;
@@ -4323,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,
VerilatedVarType vltype, int vlflags, int udims,
uint32_t entSize...) VL_MT_UNSAFE {
@@ -4475,7 +4429,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
// Slowpath - Called only when find has failed
const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum)
+ "' 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());
return nullptr;
}
@@ -4503,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) {
VerilatedImp::hierarchyAdd(fromp, top);
}
+42 -141
View File
@@ -100,9 +100,6 @@ class VerilatedFstC;
class VerilatedFstSc;
class VerilatedScope;
class VerilatedScopeNameMap;
class VerilatedIfaceRef;
class VerilatedIfaceRefMap;
struct VlIfaceRefTableEntry;
template <typename, typename>
class VerilatedTrace;
class VerilatedTraceBaseC;
@@ -112,7 +109,6 @@ class VerilatedVarNameMap;
class VerilatedVcd;
class VerilatedVcdC;
class VerilatedVcdSc;
class VlCovRegistry;
//=========================================================================
// Basic types
@@ -261,7 +257,7 @@ class VL_SCOPED_CAPABILITY VerilatedLockGuard final {
VL_UNCOPYABLE(VerilatedLockGuard);
private:
VerilatedMutex& m_mutexr; // Mutex protecting the guard
VerilatedMutex& m_mutexr;
public:
/// Construct and hold given mutex lock until destruction or unlock()
@@ -305,34 +301,6 @@ public:
#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;
//=========================================================================
@@ -408,7 +376,8 @@ class VerilatedEvalLoop final {
const uint32_t m_convergeLimit; // --converge-limit from compiler command line
// Where to record --prof-exec sections, or null if not profiling
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:
// CONSTRUCTORS
@@ -510,8 +479,8 @@ private:
= ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits
+ 1;
// Build the assertion-control bit mask for the given assertion x directive types.
static constexpr uint32_t assertOnMask(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_PURE;
static uint32_t assertOnMask(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_PURE;
static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1;
// No termination request has stamped m_finishPendingTime yet
static constexpr uint64_t TIME_UNSET = ~0ULL;
@@ -529,18 +498,20 @@ protected:
// No std::strings or pointers or will serialize badly!
// Fast path
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
// type. Last bit guards internal directive types.
std::atomic<uint32_t> m_assertOn{std::numeric_limits<uint32_t>::max()};
std::atomic<uint32_t> m_assertOn{
std::numeric_limits<uint32_t>::max()}; // Enabled assertions,
// for each VerilatedAssertType we store
// 3-bits, one for each directive type. Last
// bit guards internal directive types.
std::atomic<uint32_t> m_assertLock{0}; // Locked assertion bits (IEEE 1800-2023 20.11
// Lock/Unlock); same layout as m_assertOn. While
// a bit is locked, On/Off/Kill leave it unchanged.
// Enabled vacuous pass actions
std::atomic<uint32_t> m_assertPassOnVacuous{std::numeric_limits<uint32_t>::max()};
// Enabled nonvacuous pass actions
std::atomic<uint32_t> m_assertPassOnNonvacuous{std::numeric_limits<uint32_t>::max()};
// Enabled fail actions
std::atomic<uint32_t> m_assertFailOn{std::numeric_limits<uint32_t>::max()};
std::atomic<uint32_t> m_assertPassOnVacuous{
std::numeric_limits<uint32_t>::max()}; // Enabled vacuous pass actions
std::atomic<uint32_t> m_assertPassOnNonvacuous{
std::numeric_limits<uint32_t>::max()}; // Enabled nonvacuous pass actions
std::atomic<uint32_t> m_assertFailOn{
std::numeric_limits<uint32_t>::max()}; // Enabled fail actions
std::array<std::atomic<uint32_t>, ASSERT_CONTROL_SLOT_COUNT> m_assertKill{};
bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated
bool m_fatalOnError = true; // Fatal on $stop/non-fatal error
@@ -574,7 +545,8 @@ protected:
// 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<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
bool m_executingFinal = false; // Running generated final() code
uint64_t m_profExecStart = 1; // +prof+exec+start time
@@ -608,8 +580,6 @@ protected:
const std::unique_ptr<VerilatedContextImpData> m_impdatap;
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
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
bool m_useNumaAssign = false;
// Number of threads in added models
@@ -620,9 +590,6 @@ protected:
std::unique_ptr<VerilatedVirtualBase> m_executionProfiler;
// Coverage access
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
// Not serialized
@@ -633,8 +600,9 @@ protected:
// List of free descriptors in the MCT region [4, 32)
std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex);
// Magic to check for bad construction
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:
// CONSTRUCTORS
@@ -651,9 +619,28 @@ public:
bool assertOn() const VL_MT_SAFE;
/// Enable all assertion types
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
void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
/// Get assertion-control runtime state. Boolean queries return 0/1, Kill returns
/// the generation count.
uint32_t assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
/// Return if calculating of unused signals (for traces)
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
/// Enable calculation of unused signals (for traces)
@@ -672,8 +659,6 @@ public:
/// Return VerilatedCovContext, allocate if needed
/// Note if get unresolved reference then likely forgot to link verilated_cov.cpp
VerilatedCovContext* coveragep() VL_MT_SAFE;
/// Returns VlCovRegistry. Allocated on-demand
VlCovRegistry* covergroupRegistryp() VL_MT_SAFE;
/// Return 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
@@ -829,10 +814,6 @@ public:
bool stopRequestReserve(bool maybe) 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
VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); }
const VerilatedContextImp* impp() const VL_MT_SAFE {
@@ -841,9 +822,7 @@ public:
// Internal: Model and thread setup
void addModel(const VerilatedModel* modelp);
// Get the thread pool, creating it if needed. 'modelThreads' is the parallelism of the model
// being constructed, so that the context can grow to the number of threads it requires.
VerilatedVirtualBase* threadPoolp(unsigned modelThreads = 1);
VerilatedVirtualBase* threadPoolp();
void prepareClone();
VerilatedVirtualBase* threadPoolpOnClone();
VerilatedVirtualBase*
@@ -889,9 +868,6 @@ public:
const VerilatedScope* scopeFind(const char* namep) const 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
static constexpr size_t serialized1Size() VL_PURE { return sizeof(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;
};
// 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
// Used for internal VPI implementation, and introspection into scopes
@@ -958,9 +908,8 @@ public:
enum Type : uint8_t {
SCOPE_MODULE,
SCOPE_OTHER,
SCOPE_PACKAGE,
SCOPE_INTERFACE
}; // Type of a scope, currently only module, package and interface are interesting
SCOPE_PACKAGE
}; // Type of a scope, currently only module and package are interesting
private:
// Fastpath:
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;
static void scopesConstructFromTable(const VlScopeTableEntry* entp, size_t n,
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
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
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__; }
};
// 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
// per-scope 'new VerilatedScope{...}' statements, which compiles faster at scale.
struct VlScopeTableEntry final {
@@ -1340,40 +1276,5 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
#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_
#endif // Guard
-10
View File
@@ -99,16 +99,6 @@ VK_CPPFLAGS_ALWAYS += \
-DVM_VPI=$(VM_VPI) \
$(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
VK_CPPFLAGS_WALL += -Wall $(CFG_CXXFLAGS_WEXTRA) -Werror
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':'type', 'short':'t', 'group':1, 'default':'', 'descr':'Type of coverage (block, line, fsm, etc)'")
// 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':'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_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_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':'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'")
// clang-format on
// VLCOVGEN_CIK_AUTO_EDIT_BEGIN
#define VL_CIK_BIN "B"
#define VL_CIK_BIN_TYPE "Bt"
#define VL_CIK_COLUMN "n"
#define VL_CIK_COMMENT "o"
#define VL_CIK_CROSS "C"
#define VL_CIK_CROSS_BINS "Cb"
#define VL_CIK_FILENAME "f"
#define VL_CIK_FSM_FROM "Ff"
#define VL_CIK_FSM_TAG "Fg"
#define VL_CIK_FSM_TO "Ft"
#define VL_CIK_FSM_VAR "Fv"
#define VL_CIK_GROUP_WEIGHT "Gw"
#define VL_CIK_HIER "h"
#define VL_CIK_LINENO "l"
#define VL_CIK_LINESCOV "S"
@@ -85,18 +77,14 @@ public:
// Return the short key code for a given a long coverage key
static std::string shortKey(const std::string& key) VL_PURE {
// 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 == "comment") return VL_CIK_COMMENT;
if (key == "cross") return VL_CIK_CROSS;
if (key == "cross_bins") return VL_CIK_CROSS_BINS;
if (key == "filename") return VL_CIK_FILENAME;
if (key == "fsm_from") return VL_CIK_FSM_FROM;
if (key == "fsm_tag") return VL_CIK_FSM_TAG;
if (key == "fsm_to") return VL_CIK_FSM_TO;
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 == "lineno") return VL_CIK_LINENO;
if (key == "linescov") return VL_CIK_LINESCOV;
-11
View File
@@ -28,8 +28,6 @@
#include <cstdint>
#include <string>
class VlFileLineDebug;
// Per-bin classification. A bin's kind is which set it lives in (structural),
// not a per-bin field. Only Normal feeds coverage(); the rest are recorded.
// Enumerators are 'KIND_'-prefixed because the bare LRM terms collide with
@@ -52,9 +50,6 @@ enum class VlCovBinKind : uint8_t {
/// Bounded bin count, so random access by index is the primary usage.
class VlCoverpointIf VL_NOT_FINAL {
// MEMBERS
int32_t m_weight = 1; // option.weight; never negative
public:
// CONSTRUCTORS
virtual ~VlCoverpointIf() = default;
@@ -67,12 +62,6 @@ public:
virtual std::string binName(uint32_t i) const = 0;
// Bins covered / effective total (Normal set only) for the coverage calc
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
File diff suppressed because it is too large Load Diff
+33 -579
View File
@@ -32,16 +32,10 @@
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_cov_model.h"
#include <array>
#include <cstdint>
#include <initializer_list>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
class VerilatedCovContext;
@@ -50,15 +44,6 @@ class VerilatedCovContext;
enum class VlCovBinNaming : uint8_t {
Single, // "<name>" one bin
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
@@ -109,10 +94,6 @@ public:
// VlCoverpointT<MaxHits> adds the inline hit-list array and the incrementBin write
// path; the cross holds VlCoverpoint* and reads via hitCount()/hitList().
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:
// MEMBERS (protected so VlCoverpointT::incrementBin can update them)
std::string m_hier; // "covergroup.coverpoint"
@@ -129,27 +110,15 @@ protected:
std::vector<uint32_t> m_crossToBin;
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 METHODS
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,
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:
// CONSTRUCTORS
VlCoverpoint();
~VlCoverpoint() override;
VlCoverpoint() = default;
// METHODS
// ---- configuration (from generated constructor) ----
@@ -161,78 +130,13 @@ public:
int line, int col) {
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
}
void addNumberedNamer(VlCovBinKind set, uint32_t count, const char* name, const char* file,
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);
void registerBins(VerilatedCovContext* covcontextp, const char* page);
// ---- hot path (from generated sample()) ----
// Clear the hit list at the start of each sample() for a cross-fed coverpoint.
void clearHitList() { m_hitCount = 0; }
// Ignore/Illegal/Default: count only; never propagates to cross coverage.
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>,
// 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
// ---- VlCoverpointIf ----
/// Bins removed for having no value (IEEE 1800-2023 19.11.1) are not reported.
uint32_t binCount() const override;
uint32_t binCount() const override { return m_total; }
std::string binName(uint32_t i) const override;
// Deliberately not on VlCoverpointIf: only coverage-database registration needs it.
VlCovBinKind binKind(uint32_t i) const { return namerFor(reportedBin(i)).set(); }
// Deliberately not on VlCoverpointIf: only registerBins() needs it, via the
// 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 {
// Count Normal bins that reached option.at_least on demand, so the hot
// path (incrementBin) stays a plain counter bump.
uint32_t numCovered = 0;
for (const uint32_t bin : m_crossToBin) {
if (m_counts[bin] >= m_atLeast) ++numCovered;
for (const VlCovNamer& nm : m_namers) {
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;
total = m_normal;
@@ -290,75 +198,16 @@ public:
if (cx >= 0 && m_hitCount < MaxHits) m_hits[m_hitCount++] = static_cast<uint32_t>(cx);
}
const uint32_t* hitList() const override { return m_hits; }
protected:
void incrementNormalBin(uint32_t i) override { incrementBin(i); }
};
//=============================================================================
// 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()
/// walks only hit tuples, not the entire product. Bin names are
/// built on demand for automatic bins; explicit bins select sets of tuples
/// 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.
/// walks the coverpoint hit lists (O(hits), not O(product)). Bin names are
/// built on demand from the coverpoints, so no per-bin name is stored.
class VlCoverCross VL_NOT_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:
class VlCoverCross final : public VlCoverpointIf {
// MEMBERS
std::string m_hier; // "covergroup.cross"
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
// built from. init() fatals if the product would exceed UINT32_MAX, so every
// 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_numCovered = 0; // Distinct bins hit >= 1 (maintained incrementally)
Dimension* m_dimensionsp = nullptr; // [m_dims], owned by the concrete cross runtime
uint32_t* m_flatCountsp = nullptr; // [m_numAutoBins] Per-bin hit counts
Explicit* m_explicitp = nullptr; // Absent for automatic-only crosses
std::vector<uint32_t> m_cpBinCounts; // [m_dims] Normal bin count per dimension
std::vector<uint32_t> m_stride; // [m_dims] Flat-index stride per dimension
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
bool hasExplicitBins() const { return m_explicitp != nullptr; }
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 iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx);
void incrementTuple(uint32_t idx) {
Explicit& data = *m_explicitp;
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);
if (m_flatCounts[idx]++ == 0) ++m_numCovered;
}
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:
VL_UNCOPYABLE(VlCoverCross);
// CONSTRUCTORS
VlCoverCross() = default;
// METHODS
// ---- configuration (from generated constructor, after coverpoints init'd) ----
virtual void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
int line, int col);
/// Add a cross bin using a verilation-time bitmap of selected Normal-bin tuples.
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);
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
int line, int col);
void registerBins(VerilatedCovContext* covcontextp, const char* page);
// ---- hot path (from generated sample(), after all coverpoints sampled) ----
/// Sample automatic and explicit bins, optionally applying per-bin iff guards.
/// Reads the feeding coverpoints saved by init(), so the caller passes no coverpoints.
void sample(const bool* binIffs = nullptr);
void sample(VlCoverpoint* const* cps);
// ---- VlCoverpointIf ----
// Explicit bins (including ignore/illegal) precede retained automatic bins.
uint32_t binCount() const override {
return hasExplicitBins()
? static_cast<uint32_t>(m_explicitp->bins.size() + m_explicitp->autoBins.size())
: m_numAutoBins;
}
std::string binName(uint32_t i) const override;
// A cross is a coverpoint whose bins are the auto cross bins (all Normal).
uint32_t binCount() const override { return m_numAutoBins; }
std::string binName(uint32_t flat) const override;
void coverageParts(double& covered, double& total) const override {
covered = m_numCovered;
total = hasExplicitBins() ? m_explicitp->normalBins + m_explicitp->autoBins.size()
: m_numAutoBins;
total = 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
+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_rhsLsb; // Destination index that maps to RHS index 0
const void* m_rhsDatap; // Pointer to RHS storage
int m_bitLsb = 0; // Bit's LSB
int m_bitMsb = 0; // Bit's MSB
int m_elemWidth = 0; // Element width
int m_bitLsb = 0;
int m_bitMsb = 0;
int m_elemWidth = 0;
};
std::vector<Entry> m_entries; // Sorted by msb, non-overlapping
@@ -303,7 +303,7 @@ public:
}
template <typename T>
T readIndex(const T origVal, int index) const {
T readIndex(T origVal, int index) const {
if (m_entries.empty()) return 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
iowp[lword] = (iowp[lword] & ~linsmask) | ((lde << loffset) & linsmask);
// Prevent unsafe write where lword was final writable location and hword is
// out-of-bounds. rbits==0 means the caller guarantees bounds.
if (VL_LIKELY(!(rbits && hword >= VL_WORDS_I(rbits)))) {
// out-of-bounds.
if (VL_LIKELY(!(hword == rword && roffset == 0))) {
iowp[hword]
= (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; }
}
// These overloads work for both VlQueue<CData> (and similar) as well
// as VlQueue<VlWide<N>>, including queues with different element types.
// This specialization works for both VlQueue<CData> (and similar) as well
// as VlQueue<VlWide<N>>.
template <typename T>
inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/, int srcElementBits,
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;
// differently typed queues cannot alias.
template <typename T_Dst, typename T_Src>
inline typename std::enable_if<!std::is_same<T_Dst, T_Src>::value>::type
VL_COPY_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 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) {
// This specialization works for both VlQueue<CData> (and similar) as well
// as VlQueue<VlWide<N>>.
template <typename T>
inline void VL_REVCOPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int lbits, int srcElementBits,
int dstElementBits) {
const size_t srcTotalBits = from.size() * srcElementBits;
const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits;
// 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
q.renew(dstSize);
-9
View File
@@ -207,11 +207,6 @@ protected:
// Used by scopeInsert, scopeFind, scopeErase, scopeNameMap
mutable VerilatedMutex m_nameMutex; // Protect m_nameMap
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 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
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
VerilatedContext& m_context; // The context this profiler is under
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
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,
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;
}
void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits) {
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";
for (int i = bits - 1; i >= 0; i--) os << (VL_BITISSET_I(hash, i) ? '1' : '0');
if (bits > 1) os << " (concat";
@@ -713,12 +685,11 @@ void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits,
IData varBitsLeft = varBits;
IData varBitsWant = (varBits + 1) / 2;
if (varBits > 2) os << " (bvxor";
for (const auto& varp : vars) { // LCOV_EXCL_BR_LINE - reservoir-sampling below
// always forces its last pick before exhausting vars
for (int j = 0; j < varp->totalWidth(); j++, varBitsLeft--) {
for (const auto& var : m_vars) {
for (int j = 0; j < var.second->totalWidth(); j++, varBitsLeft--) {
const bool doEmit = (VL_RANDOM_RNG_I(rngr) % varBitsLeft) < varBitsWant;
if (doEmit) {
varp->emitExtract(os, j);
var.second->emitExtract(os, j);
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::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
VlSolverSession& sess = s_solverSession;
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> 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);
for (const auto& [key, arrNameVec] : m_uniqueArrays) {
uint32_t matchingElementsCount = 0;
std::ostringstream varsString;
for (const auto& baseName : arrNameVec) {
const auto it = m_vars.find(baseName);
if (it == m_vars.end()) continue;
const VlRandomVar& var = *it->second;
// Select the elements the array actually holds now, by their own index
// 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;
for (const std::string& baseName : m_unique_arrays) {
const auto it = m_vars.find(baseName);
if (it == m_vars.end()) continue;
const VlRandomVar& var = *it->second;
// Select the elements the array actually holds now, by their own index
// or key, rather than by ordinal position
var.setArrayInfo(arrVarsp);
// 'distinct' needs at least two operands; fewer elements are trivially unique
if (var.countMatchingElements(*arrVarsp, baseName) < 2) continue;
std::ostringstream os;
os << "(__Vbv (distinct " << varsString.str() << "))";
os << "(__Vbv (distinct ";
var.emitGetValue(os);
os << "))";
exprs.push_back(os.str());
}
return exprs;
@@ -1612,7 +1577,7 @@ void VlRandomizer::clearConstraints() {
m_constraints_line.clear();
m_solveBefore.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
}
@@ -1625,7 +1590,7 @@ void VlRandomizer::clearAll() {
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) {
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
// updated (more diverse) values. If unsat, keep baseline values.
// Scoped to this layer's own vars -- see randomConstraint's comment.
os << "(assert ";
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN, &layerVars);
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
os << ")\n";
os << "(check-sat)\n";
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
std::vector<std::string> m_softConstraints; // Soft constraints
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)
// variables
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
// Arrays whose elements must be distinct grouped by unique constraint
std::map<uint32_t, std::unordered_set<std::string>> m_uniqueArrays;
std::vector<std::string> m_unique_arrays; // Arrays whose elements must be distinct
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
const VlQueue<CData>* m_static_randmodep = nullptr; // Static rand_mode state (shared)
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
// PRIVATE METHODS
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits,
const std::vector<std::string>* layerVarsp = nullptr);
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
// Fetch the model and write it into the registered variables.
bool applyModel(VlSolverSession& sess);
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
// (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
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 ---
// Register scalar variable (non-struct, basic type)
template <typename T>
typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value,
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()) {
if (m_vars.find(name) != m_vars.end()) return;
// 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
write_var(T& var, int width, const char* name, int dimension,
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
@@ -517,7 +513,7 @@ public:
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
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()) {
record_struct_arr(var, name, dimension, {}, {}, randmodeIdx);
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
}
// Register unpacked array of non-struct types
template <typename T, std::size_t N_Depth>
@@ -543,7 +539,7 @@ public:
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
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()) {
record_struct_arr(var, name, dimension, {}, {}, randmodeIdx);
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
}
// Register associative array of non-struct types
@@ -571,7 +567,7 @@ public:
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,
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 ---
@@ -586,23 +582,10 @@ public:
++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.
// 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.
void rand_unique(const std::string& name, const uint32_t key) {
m_uniqueArrays[key].insert(name);
}
void rand_unique(const std::string& name) { m_unique_arrays.push_back(name); }
// Recursively record all elements in an unpacked array
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
template <typename T, size_t N_MaxSize>
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
template <typename T_Key, typename T_Value>
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
template <typename T>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
record_struct_arr(T& var, const std::string& name, int /*dimension*/,
std::vector<IData> indices, std::vector<size_t> idxWidths,
std::uint32_t randmodeIdx) {
std::vector<IData> indices, std::vector<size_t> idxWidths) {
std::ostringstream oss;
for (size_t i = 0; i < indices.size(); ++i) {
oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0')
@@ -711,75 +660,43 @@ public:
if (i < indices.size() - 1) oss << ".";
}
write_var(var, 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());
oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL);
}
// Recursively process VlUnpacked of structs
template <typename T, std::size_t N_Depth>
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::uint32_t randmodeIdx) {
std::vector<IData> indices, std::vector<size_t> idxWidths) {
if (dimension > 0 && N_Depth != 0) {
constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1);
idxWidths.push_back(idx_width);
for (size_t i = 0; i < N_Depth; ++i) {
indices.push_back(i);
record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths,
randmodeIdx);
record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths);
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
template <typename T, size_t N_MaxSize>
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::uint32_t randmodeIdx) {
std::vector<IData> indices, std::vector<size_t> idxWidths) {
if ((dimension > 0) && (var.size() != 0)) {
idxWidths.push_back(32);
for (size_t i = 0; i < var.size(); ++i) {
indices.push_back(i);
record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths,
randmodeIdx);
record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths);
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
template <typename T_Key, typename T_Value>
void record_struct_arr(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
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())) {
for (auto it = var.begin(); it != var.end(); ++it) {
const T_Key& key = it->first;
@@ -797,127 +714,23 @@ public:
std::string result = oss.str();
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
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: Register all members of a user-defined struct
template <typename T, std::size_t... I>
void modifyMembers(T& obj, std::index_sequence<I...>, const std::string& baseName,
std::uint32_t randmodeIdx) {
void modifyMembers(T& obj, std::index_sequence<I...>, const std::string& baseName) {
// Use the indices to access each member via std::get
(void)std::initializer_list<int>{
(write_var(std::get<I>(obj.getMembers(obj)), obj.memberWidth()[I],
(baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I],
randmodeIdx),
(baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I]),
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
static std::string generateKey(const std::string& name, int idx) {
if (!name.empty() && name[0] == '\\') {
@@ -952,7 +765,7 @@ public:
void disable_soft(const std::string& varName);
void clearConstraints();
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,
const std::string& afterName); // Register solve-before ordering
void set_randmode(const VlQueue<CData>& randmode) { m_randmodep = &randmode; }
+46 -56
View File
@@ -99,6 +99,46 @@ package std;
endfunction
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"
class process;
typedef enum {
@@ -203,56 +243,6 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
endfunction
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"
function int randomize();
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
typedef struct {
string name;
int weight = 1;
int weight;
int goal;
string comment;
int at_least;
@@ -276,7 +266,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_covergroup_options_t;
typedef struct {
int weight = 1;
int weight;
int goal;
string comment;
int at_least;
@@ -285,7 +275,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_coverpoint_options_t;
typedef struct {
int weight = 1;
int weight;
int goal;
string comment;
int at_least;
@@ -294,7 +284,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_cross_options_t;
typedef struct {
int weight = 1;
int weight;
int goal;
string comment;
bit strobe;
@@ -304,14 +294,14 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_covergroup_type_options_t;
typedef struct {
int weight = 1;
int weight;
int goal;
string comment;
real real_interval;
} vl_coverpoint_type_options_t;
typedef struct {
int weight = 1;
int weight;
int goal;
string comment;
} vl_cross_type_options_t;
+5 -20
View File
@@ -38,8 +38,8 @@
// See also V3Ast::VNumRange
class VerilatedRange final {
int m_left = 0; // Left side of range (pre-':')
int m_right = 0; // Right side of range (post-':')
int m_left = 0;
int m_right = 0;
protected:
friend class VerilatedVarProps;
@@ -158,18 +158,7 @@ public:
VerilatedVarFlags vldir() const {
return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR);
}
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 entSize() const VL_MT_SAFE;
uint32_t entBits() const VL_MT_SAFE {
uint32_t bits = 1;
for (auto it : m_packed) bits *= it.elements();
@@ -225,11 +214,7 @@ public:
: 0;
}
// Total size in bytes (note DPI limited to 4GB)
size_t totalSize() const {
size_t size = entSize();
for (int udim = 0; udim < udims(); ++udim) size *= m_unpacked[udim].elements();
return size;
}
size_t totalSize() const;
// 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;
};
@@ -283,7 +268,7 @@ class VerilatedVar final : public VerilatedVarProps {
m_forceControlSignals; // Force control signals
protected:
const bool m_isParam; // From a parameter
const bool m_isParam;
friend class VerilatedScope;
// CONSTRUCTORS
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
-9
View File
@@ -72,13 +72,4 @@ public:
~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
+1 -1
View File
@@ -140,7 +140,7 @@ class VlWorkerThread final {
};
// MEMBERS
mutable VerilatedMutex m_mutex; // Protection mutex
mutable VerilatedMutex m_mutex;
std::condition_variable_any m_cv;
// Only notify the condition_variable if the worker is waiting
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) {
if (completed()) {
// We can't change the sttaus of completed process
return;
}
if (s == KILLED && m_forkSyncOnKillp && !m_forkSyncOnKillDone) {
if (s == KILLED && m_state != KILLED && m_state != FINISHED && m_forkSyncOnKillp
&& !m_forkSyncOnKillDone) {
m_forkSyncOnKillDone = true;
m_state = s;
m_forkSyncOnKillp->done();
+31 -1
View File
@@ -64,6 +64,36 @@
// Placeholder for compiling with --protect-ids
#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
// 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:
size_t m_counter = 0; // When reaches 0, resume suspended coroutine
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)
bool m_inDone = false; // Guard against re-entrant resume recursion from nested kills
bool m_resumePending = false; // Join reached zero again while inside done()
+3 -3
View File
@@ -147,9 +147,9 @@ private:
struct CallbackRecord final {
union { // The callback
const initCb_t m_initCb; // Init-callback constructor
const dumpCb_t m_dumpCb; // Dump-callback constructor
const cleanupCb_t m_cleanupCb; // Cleanup-callback constructor
const initCb_t m_initCb;
const dumpCb_t m_dumpCb;
const cleanupCb_t m_cleanupCb;
};
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)
+1 -19
View File
@@ -32,7 +32,6 @@
#include <array>
#include <atomic>
#include <deque>
#include <initializer_list>
#include <map>
#include <memory>
#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(QData 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>
inline std::string VL_TO_STRING(const VlWide<N_Words>& obj) {
return VL_TO_STRING_W(N_Words, obj);
@@ -1085,15 +1084,6 @@ public:
// CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it.
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(const VlAssocArray&) = default;
VlAssocArray(VlAssocArray&&) = default;
@@ -1478,14 +1468,6 @@ public:
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) {
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();
return m_fullname.c_str();
}
virtual void* readDatap() const { return m_varp->datap(); }
CData* readCDatap() const {
virtual void* varDatap() const { return m_varp->datap(); }
CData* varCDatap() const {
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););
return static_cast<SData*>(readDatap());
return reinterpret_cast<SData*>(varDatap());
}
IData* readIDatap() const {
IData* varIDatap() const {
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););
return static_cast<QData*>(readDatap());
return reinterpret_cast<QData*>(varDatap());
}
EData* readEDatap() const {
EData* varEDatap() const {
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););
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););
return static_cast<std::string*>(readDatap());
return reinterpret_cast<std::string*>(varDatap());
}
virtual uint32_t bitOffset() const { return 0; }
};
@@ -363,13 +363,11 @@ protected:
public:
explicit VerilatedVpioScope(const VerilatedScope* 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_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;
// cppcheck-suppress duplInheritedMember
static VerilatedVpioScope* castp(vpiHandle h) {
@@ -412,9 +410,6 @@ public:
m_name = name;
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)
: VerilatedVpioVarBase{vop} {
if (vop) {
@@ -520,7 +515,7 @@ public:
const std::string localName = _vl_vpi_member_local_name(memberVarp->name());
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)};
}
uint32_t type() const override {
@@ -545,7 +540,7 @@ public:
return m_fullname.c_str();
}
uint8_t* prevDatap() const { return m_prevDatap; }
void* readDatap() const override { return m_varDatap; }
void* varDatap() const override { return m_varDatap; }
void createPrevDatap() {
if (VL_UNLIKELY(!m_prevDatap)) {
m_prevDatap = new uint8_t[entSize()];
@@ -726,17 +721,6 @@ public:
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 {
const std::vector<const VerilatedScope*>* m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it;
@@ -794,8 +778,6 @@ public:
return (new VerilatedVpioScope{modp})->castVpiHandle();
} else if (itype == VerilatedScope::SCOPE_MODULE) {
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; }
};
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 {
const std::vector<const VerilatedScope*>* m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it;
@@ -1097,13 +995,22 @@ public:
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: // FALLTHRU
case vpiStringVal: {
if (VL_UNLIKELY(!valuep->value.str)) return false;
break;
}
case vpiScalarVal: // FALLTHRU
case vpiIntVal: // FALLTHRU
case vpiRealVal: // FALLTHRU
case vpiVectorVal: return true;
default: return false;
case vpiRealVal: break;
case vpiVectorVal: {
if (VL_UNLIKELY(!valuep->value.vector)) return false;
break;
}
default: {
return false;
}
}
return true;
}
};
@@ -1285,8 +1192,8 @@ public:
template <typename T>
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",
varop->fullname(), *(static_cast<CData*>(varop->readDatap())),
*(varop->prevDatap()), varop->readDatap(), varop->prevDatap(),
varop->fullname(), *(static_cast<CData*>(varop->varDatap())),
*(varop->prevDatap()), varop->varDatap(), varop->prevDatap(),
varop->entSize()););
if (varop->bitSize() == 1) {
T* const prevDatap = reinterpret_cast<T*>(
@@ -1297,7 +1204,7 @@ public:
prevInfo.m_datap = prevDatap;
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) {
switch (varop->varp()->vltype()) {
@@ -1326,9 +1233,9 @@ public:
prevInfo.m_datap = prevDatap;
const T currWord = vl_vpi_get_word_gen(currInfo);
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 {
std::memcpy(varop->prevDatap(), varop->readDatap(), varop->entSize());
std::memcpy(varop->prevDatap(), varop->varDatap(), varop->entSize());
}
}
static void updatePrev(const VerilatedVpioVar* const varop) {
@@ -1369,7 +1276,7 @@ public:
if (valueDiffersFromPrev(varop)) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
ho.id(), varop->fullname(),
*(static_cast<CData*>(varop->readDatap()))););
*(static_cast<CData*>(varop->varDatap()))););
update.insert(varop);
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap());
@@ -1720,9 +1627,9 @@ VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseSignal
// assert(forceEnableSignalVop->entSize() == baseSignalVop->entSize());
assert(forceValueSignalVop->entSize() == baseSignalVop->entSize());
assert(forceReadSignalVop->entSize() == baseSignalVop->entSize());
assert(forceEnableSignalVop->readDatap() == forceEnableSignalVarp->datap());
assert(forceValueSignalVop->readDatap() == forceValueSignalVarp->datap());
assert(forceReadSignalVop->readDatap() == forceReadSignalVarp->datap());
assert(forceEnableSignalVop->varDatap() == forceEnableSignalVarp->datap());
assert(forceValueSignalVop->varDatap() == forceValueSignalVarp->datap());
assert(forceReadSignalVop->varDatap() == forceReadSignalVarp->datap());
#endif // VL_DEBUG
return VerilatedVpiImp::ForceControlSignalVops{
@@ -1752,9 +1659,9 @@ double VerilatedVpiImp::getReadDataWord(const VerilatedVpioVar* baseSignalVop,
const VerilatedVpioVar* forceEnableSignalVop,
const VerilatedVpioVar* forceValueSignalVop,
size_t /*bitCount*/, size_t /*bitOffset*/) {
const double baseSignalData = *baseSignalVop->readRealDatap();
const bool forceEnableData = *forceEnableSignalVop->readCDatap();
const double forceValueData = *forceValueSignalVop->readRealDatap();
const double baseSignalData = *baseSignalVop->varRealDatap();
const bool forceEnableData = *forceEnableSignalVop->varCDatap();
const double forceValueData = *forceValueSignalVop->varRealDatap();
const double readData = forceEnableData ? forceValueData : baseSignalData;
return readData;
}
@@ -2092,9 +1999,6 @@ const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE {
"vpiStmt"
};
// 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*";
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, vpiStmt);
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiActual);
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiInterface);
SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbValueChange);
SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbAtEndOfSimTime);
@@ -2799,7 +2701,7 @@ _vl_vpi_handle_indexed_member_from_scope(const VerilatedScope* const scopep,
VerilatedVpioVar* baseVop
= fullnameOverride.empty()
? new VerilatedVpioVar{baseVarp, varScopep}
: new VerilatedVpioVar{baseVarp, varScopep,
: new VerilatedVpioVar{baseVarp, varScopep, baseVarp->datap(),
_vl_vpi_member_local_name(baseVarp->name()),
fullnameOverride};
VerilatedVpioVar* vop = _vl_vpi_handle_apply_indices(baseVop, indices);
@@ -2869,10 +2771,6 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::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."))) {
scopeAndName.erase(0, std::strlen("$root."));
} else if (voScopep) {
@@ -2900,15 +2798,8 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
if (scopep->type() == VerilatedScope::SCOPE_PACKAGE) {
return (new VerilatedVpioPackage{scopep})->castVpiHandle();
}
if (scopep->type() == VerilatedScope::SCOPE_INTERFACE) {
return (new VerilatedVpioInterface{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 scopename;
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()) {
resultHandle = (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
} else if (!fullnameOverride.empty()) {
resultHandle = (new VerilatedVpioVar{varp, scopep, _vl_vpi_member_local_name(varp->name()),
fullnameOverride})
->castVpiHandle();
resultHandle
= (new VerilatedVpioVar{varp, scopep, varp->datap(),
_vl_vpi_member_local_name(varp->name()), fullnameOverride})
->castVpiHandle();
} else {
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();
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: {
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
@@ -3151,15 +3025,6 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
if (it == map->end()) return nullptr;
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: {
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
@@ -3370,18 +3235,6 @@ bool vl_check_format(const VerilatedVpioVarBase* vop, const p_vpi_value valuep,
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
PLI_INT32 vl_get_vltype_format(VerilatedVarType vlType) {
switch (vlType) {
@@ -3441,7 +3294,7 @@ VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t
bitCount, varBits - addOffset});
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) {
assert(sizeof(T) == sizeof(EData));
assert(bitCount <= wordBits);
@@ -3489,7 +3342,8 @@ T vl_vpi_get_word_gen(VarAccessInfo<T> info) {
template <typename T>
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>
@@ -3507,7 +3361,8 @@ void vl_vpi_put_word_gen(VarAccessInfo<T> info, T word) {
template <typename T>
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
@@ -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) {
const VerilatedVar* const varp = vop->varp();
void* const varDatap = vop->readDatap();
void* const varDatap = vop->varDatap();
if (!vl_check_format(vop, valuep, true)) return;
// 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;
} else if (valuep->format == vpiBinStrVal) {
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) {
const size_t pos = i + vop->bitOffset();
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) {
if (varp->vltype() == VLVT_STRING) {
if (varp->isParam()) {
valuep->value.str = static_cast<char*>(varDatap);
valuep->value.str = reinterpret_cast<char*>(varDatap);
return;
}
t_outDynamicStr = *vop->readStringDatap();
t_outDynamicStr = *vop->varStringDatap();
valuep->value.str = const_cast<char*>(t_outDynamicStr.c_str());
return;
} 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);
return;
} else if (valuep->format == vpiRealVal) {
valuep->value.real = *(vop->readRealDatap());
valuep->value.real = *(vop->varRealDatap());
return;
} else if (valuep->format == vpiScalarVal) {
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,
valuep->value.integer);
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())) {
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;
}
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 (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
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) {
const double readData = VerilatedVpiImp::getReadDataWord<double>(
baseSignalVop, forceEnableSignalVop, forceValueSignalVop, 64, 0);
*forceReadSignalVop->readRealDatap() = readData;
*forceReadSignalVop->varRealDatap() = readData;
return;
}
@@ -3935,6 +3780,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
}
if (valuep->format == vpiVectorVal) {
if (VL_UNLIKELY(!valuep->value.vector)) return nullptr;
if (valueVop->varp()->vltype() == VLVT_WDATA) {
const int words = VL_WORDS_I(varBits);
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) {
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) {
const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1');
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) {
if (valueVop->varp()->vltype() == VLVT_STRING) {
// Does not use valueVop, because strings are not forceable anyway
*(baseSignalVop->readStringDatap()) = valuep->value.str;
*(baseSignalVop->varStringDatap()) = valuep->value.str;
return object;
}
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;
} else if (valuep->format == vpiRealVal) {
if (valueVop->varp()->vltype() == VLVT_REAL) {
*(valueVop->readRealDatap()) = valuep->value.real;
*(valueVop->varRealDatap()) = valuep->value.real;
if (baseSignalVop->varp()->isForceable()) updateVforceRd();
return object;
}
@@ -4095,7 +3941,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
case VLVT_UINT8:
case VLVT_UINT16:
case VLVT_UINT32: return true;
default:;
default:; // LCOV_EXCL_LINE
}
break;
case vpiRawTwoStateVal:
@@ -4113,7 +3959,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
switch (varp->vltype()) {
case VLVT_UINT8:
case VLVT_UINT16: return true;
default:;
default:; // LCOV_EXCL_LINE
}
break;
case vpiLongIntVal:
@@ -4122,7 +3968,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
case VLVT_UINT16:
case VLVT_UINT32:
case VLVT_UINT64: return true;
default:;
default:; // LCOV_EXCL_LINE
}
break;
default:;
@@ -4321,10 +4167,10 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readCDatap(), shortintsp);
vop->varCDatap(), shortintsp);
} else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readSDatap(), shortintsp);
vop->varSDatap(), shortintsp);
}
return;
@@ -4336,13 +4182,13 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readCDatap(), integersp);
vop->varCDatap(), integersp);
} else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readSDatap(), integersp);
vop->varSDatap(), integersp);
} else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readIDatap(), integersp);
vop->varIDatap(), integersp);
}
return;
@@ -4354,16 +4200,16 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readCDatap(), longintsp);
vop->varCDatap(), longintsp);
} else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readSDatap(), longintsp);
vop->varSDatap(), longintsp);
} else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readIDatap(), longintsp);
vop->varIDatap(), longintsp);
} else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->readQDatap(), longintsp);
vop->varQDatap(), longintsp);
}
return;
@@ -4375,19 +4221,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->readCDatap(), vectorsp);
vop->varCDatap(), vectorsp);
} else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->readSDatap(), vectorsp);
vop->varSDatap(), vectorsp);
} else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->readIDatap(), vectorsp);
vop->varIDatap(), vectorsp);
} else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->readQDatap(), vectorsp);
vop->varQDatap(), vectorsp);
} else if (varp->vltype() == VLVT_WDATA) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->readEDatap(), vectorsp);
vop->varEDatap(), vectorsp);
}
return;
@@ -4399,19 +4245,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->readCDatap(), valuep);
vop->varCDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->readSDatap(), valuep);
vop->varSDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->readIDatap(), valuep);
vop->varIDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->readQDatap(), valuep);
vop->varQDatap(), valuep);
} else if (varp->vltype() == VLVT_WDATA) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->readEDatap(), valuep);
vop->varEDatap(), valuep);
}
return;
@@ -4423,19 +4269,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->readCDatap(), valuep);
vop->varCDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->readSDatap(), valuep);
vop->varSDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->readIDatap(), valuep);
vop->varIDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->readQDatap(), valuep);
vop->varQDatap(), valuep);
} else if (varp->vltype() == VLVT_WDATA) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->readEDatap(), valuep);
vop->varEDatap(), valuep);
}
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,
PLI_UINT32 num) {
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 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 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) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
vop->readCDatap());
return;
vop->varCDatap());
} else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
vop->readSDatap());
return;
vop->varSDatap());
}
return;
} else if (arrayvalue_p->format == vpiIntVal) {
const PLI_UINT32* integersp = reinterpret_cast<PLI_UINT32*>(arrayvalue_p->value.integers);
if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
vop->readCDatap());
return;
vop->varCDatap());
} else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
vop->readSDatap());
return;
vop->varSDatap());
} else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
vop->readIDatap());
return;
vop->varIDatap());
}
return;
} else if (arrayvalue_p->format == vpiLongIntVal) {
const PLI_UINT64* longintsp = reinterpret_cast<PLI_UINT64*>(arrayvalue_p->value.longints);
if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->readCDatap());
return;
vop->varCDatap());
} else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->readSDatap());
return;
vop->varSDatap());
} else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->readIDatap());
return;
vop->varIDatap());
} else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->readQDatap());
return;
vop->varQDatap());
}
return;
} else if (arrayvalue_p->format == vpiVectorVal) {
const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors;
if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->readCDatap());
return;
vectorsp, vop->varCDatap());
} else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->readSDatap());
return;
vectorsp, vop->varSDatap());
} else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->readIDatap());
return;
vectorsp, vop->varIDatap());
} else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->readQDatap());
return;
vectorsp, vop->varQDatap());
} else if (varp->vltype() == VLVT_WDATA) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->readEDatap());
return;
vectorsp, vop->varEDatap());
}
return;
} else if (arrayvalue_p->format == vpiRawFourStateVal) {
const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals);
if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->readCDatap());
return;
vop->varCDatap());
} else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->readSDatap());
return;
vop->varSDatap());
} else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->readIDatap());
return;
vop->varIDatap());
} else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->readQDatap());
return;
vop->varQDatap());
} else if (varp->vltype() == VLVT_WDATA) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->readEDatap());
return;
vop->varEDatap());
}
return;
} else if (arrayvalue_p->format == vpiRawTwoStateVal) {
const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals);
if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->readCDatap());
return;
vop->varCDatap());
} else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->readSDatap());
return;
vop->varSDatap());
} else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->readIDatap());
return;
vop->varIDatap());
} else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->readQDatap());
return;
vop->varQDatap());
} else if (varp->vltype() == VLVT_WDATA) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->readEDatap());
return;
vop->varEDatap());
}
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'",
__func__, VerilatedVpiError::strFromVpiVal(arrayvalue_p->format),
vop->fullname());
// LCOV_EXCL_STOP
}
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;
}
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);
}
+2 -16
View File
@@ -318,17 +318,6 @@
# define VL_CONSTEXPR_CXX17
#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
@@ -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_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_ENUM 'E' // (width, IData/QData/const EData*, STRING, std::string* name)
#define VL_VFORMATATTR_ENUM_SIGNED 'F' // Same arguments as ENUM, with a signed numeric value
#define VL_VFORMATATTR_ENUM 'E' // (width, IData/QData, std::string* name); <= 64 bit enum with runtime %p/%s
#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_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_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_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_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.
#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit)))
@@ -750,7 +736,7 @@ namespace vlstd {
template <typename T>
struct reverse_wrapper final {
const T& m_v; // Pre-wrapped iterator
const T& m_v;
explicit reverse_wrapper(const T& a_v)
: m_v(a_v) {} // Need () constructor
+1 -3
View File
@@ -25,10 +25,8 @@ def message_section(msg: str) -> int:
return 50
if re.match(r'^Support', msg, flags=re.IGNORECASE):
return 60
if re.match(r'^Optimize', msg, flags=re.IGNORECASE):
return 70
if re.match(r'^Fix', msg, flags=re.IGNORECASE):
return 80
return 70
if re.match(r'^(Internals|CI|Tests)', msg, flags=re.IGNORECASE):
return -1
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-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 \
--inplace \
\
--wrap_end_else_clauses \
--assignment_statement_alignment=flush-left \
--case_items_alignment=flush-left \
--class_member_variable_alignment=flush-left \
--distribution_items_alignment=flush-left \
--enum_assignment_statement_alignment=flush-left \
--expand_coverpoints \
--formal_parameters_alignment=flush-left \
--module_net_variable_alignment=flush-left \
--named_parameter_alignment=flush-left \
--named_port_alignment=flush-left \
--parameter_declaration_alignment=flush-left \
--port_declarations_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
V3Common.h
V3Const.h
V3ConstPool.h
V3Container.h
V3Control.h
V3Coverage.h
@@ -115,7 +114,6 @@ set(HEADERS
V3GraphPathChecker.h
V3GraphStream.h
V3Hash.h
V3HashTable.h
V3Hasher.h
V3HierBlock.h
V3Inline.h
@@ -238,7 +236,6 @@ set(COMMON_SOURCES
V3Clock.cpp
V3Combine.cpp
V3Common.cpp
V3ConstPool.cpp
V3Control.cpp
V3Const__gen.cpp
V3Coverage.cpp
@@ -295,7 +292,6 @@ set(COMMON_SOURCES
V3GraphPathChecker.cpp
V3GraphTest.cpp
V3Hash.cpp
V3HashTable.cpp
V3Hasher.cpp
V3HierBlock.cpp
V3Inline.cpp
@@ -349,7 +345,6 @@ set(COMMON_SOURCES
V3Sampled.cpp
V3Sched.cpp
V3SchedAcyclic.cpp
V3SchedCovergroup.cpp
V3SchedPartition.cpp
V3SchedReplicate.cpp
V3SchedTiming.cpp
@@ -567,18 +562,9 @@ set_target_properties(
MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL>
#JOB_POOL_LINK one_job # Linking takes lots of resources
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(
${verilator}
V3Lexer_yy_cpp${CMAKE_BUILD_TYPE}
+19 -4
View File
@@ -35,10 +35,25 @@ endif
UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
# Use bear if installed, and new enough to support the '--output FILE --' syntax
BEAR := $(shell bear=$$(which bear 2>/dev/null) && tmp=$$(mktemp -d 2>/dev/null) \
&& { "$$bear" --output "$$tmp/probe.json" -- true >/dev/null 2>&1 && echo "$$bear"; rm -rf "$$tmp"; })
BEAR_OBJ_OPT := $(if $(BEAR),$(BEAR) --append --output obj_dbg/compile_commands.json --)
ifeq (,$(wildcard obj_dbg/bear.o))
ifneq (, $(shell which bear 2>/dev/null))
BEAR := $(shell which bear)
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
RAW_OBJS = \
V3Const__gen.o \
V3Error.o \
V3FileLine.o \
V3Graph.o \
@@ -195,7 +196,6 @@ RAW_OBJS = \
V3GraphPathChecker.o \
V3GraphTest.o \
V3Hash.o \
V3HashTable.o \
V3OptionParser.o \
V3Os.o \
V3ParseGrammar.o \
@@ -212,7 +212,6 @@ RAW_OBJS_PCH_ASTMT = \
V3Ast.o \
V3AstNodes.o \
V3Broken.o \
V3ConstPool.o \
V3Control.o \
V3EmitCBase.o \
V3EmitCConstPool.o \
@@ -253,7 +252,6 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Clock.o \
V3Combine.o \
V3Common.o \
V3Const__gen.o \
V3Coverage.o \
V3Covergroup.o \
V3CoverageJoin.o \
@@ -332,7 +330,6 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Sampled.o \
V3Sched.o \
V3SchedAcyclic.o \
V3SchedCovergroup.o \
V3SchedPartition.o \
V3SchedReplicate.o \
V3SchedTiming.o \
@@ -411,6 +408,8 @@ $(TGT): $(PREDEP_H) $(OBJS)
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
$(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) $*.cpp
.SECONDARY:
%.gch: %
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH} -c $< -o $@
%.o: %.cpp
@@ -430,7 +429,7 @@ V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
define CXX_ASTMT_template
$(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
@@ -438,7 +437,7 @@ $(foreach obj,$(RAW_OBJS_PCH_ASTMT),$(eval $(call CXX_ASTMT_template,$(obj))))
define CXX_ASTNOMT_template
$(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
@@ -488,8 +487,6 @@ header_cc: $(addsuffix __header_cc.o, $(basename $(HEADER_CC_H)))
%__header_cc.cpp: %.h
$(PYTHON3) $(srcdir)/../bin/verilator_includer -DVL_MT_DISABLED_CODE_UNIT=1 $^ > $@
.SECONDARY: V3Const__gen.cpp $(addsuffix __header_cc.cpp, $(basename $(HEADER_CC_H)))
.SUFFIXES:
######################################################################
+22 -28
View File
@@ -154,13 +154,6 @@ void V3AssertCommon::lowerSequenceEvents(AstNetlist* nodep) {
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
// 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
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
V3UniqueNames m_caseTempNames{"__VCase"};
V3UniqueNames m_matchCountNames{"__VnfaRemainingMatchCount"}; // Match replay counter names
// Maps from (expression, senTree) to the AstAlways that computes its delayed values.
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
m_modExpr2Sen2DelayedAlwaysp;
@@ -342,6 +334,11 @@ class AssertVisitor final : public VNVisitor {
std::unordered_map<const AstAlways*, AstVar*> m_delayedAlways2TickTimep;
// METHODS
static string assertCtlGetCall(const char* query, VAssertType type,
VAssertDirectiveType directiveType) {
return "vlSymsp->_vm_contextp__->assertCtlGet(VerilatedAssertCtlQuery::"s + query + ", "s
+ std::to_string(type) + ", "s + std::to_string(directiveType) + ")"s;
}
static const char* assertPassOnQuery(bool vacuous) {
static constexpr const char* queries[2]
= {"ASSERT_CTL_PASS_ON_NONVACUOUS", "ASSERT_CTL_PASS_ON_VACUOUS"};
@@ -365,9 +362,8 @@ class AssertVisitor final : public VNVisitor {
case VAssertDirectiveType::COVER:
case VAssertDirectiveType::ASSUME: {
if (v3Global.opt.assertOn()) {
return new AstCExpr{
fl, AstCExpr::Pure{},
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
return new AstCExpr{fl, AstCExpr::Pure{},
assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
}
return new AstConst{fl, AstConst::BitFalse{}};
}
@@ -394,17 +390,15 @@ class AssertVisitor final : public VNVisitor {
VAssertDirectiveType directiveType, bool vacuous) {
if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}};
if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}};
return new AstCExpr{
fl, AstCExpr::Pure{},
V3AssertCommon::assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1};
return new AstCExpr{fl, AstCExpr::Pure{},
assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1};
}
static AstNodeExpr* assertFailOnCond(FileLine* fl, VAssertType type,
VAssertDirectiveType directiveType) {
if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}};
if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}};
return new AstCExpr{
fl, AstCExpr::Pure{},
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 1};
return new AstCExpr{fl, AstCExpr::Pure{},
assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 1};
}
string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message,
VDisplayType severity) {
@@ -439,20 +433,20 @@ class AssertVisitor final : public VNVisitor {
if (!m_monitorNumVarp) {
m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
nodep->findUInt64DType()};
v3Global.rootp()->dollarUnitPkgp()->addStmtsp(m_monitorNumVarp);
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp);
}
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access};
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgp());
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
return varrefp;
}
AstVarRef* newMonitorOffVarRefp(const AstNode* nodep, VAccess access) {
if (!m_monitorOffVarp) {
m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
nodep->findBitDType()};
v3Global.rootp()->dollarUnitPkgp()->addStmtsp(m_monitorOffVarp);
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp);
}
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access};
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgp());
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
return varrefp;
}
static AstIf* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
@@ -755,11 +749,11 @@ class AssertVisitor final : public VNVisitor {
// reaches zero.
matchCountp->unlinkFrBack();
AstVar* const remainingp = new AstVar{
flp, VVarType::MODULETEMP, m_matchCountNames.get(""), matchCountp->dtypep()};
remainingp->lifetime(VLifetime::STATIC_EXPLICIT);
m_modp->addStmtsp(remainingp);
AstNode* const replaysp
= new AstAssign{flp, new AstVarRef{flp, remainingp, VAccess::WRITE}, matchCountp};
flp, VVarType::BLOCKTEMP, "__VnfaRemainingMatchCount", matchCountp->dtypep()};
remainingp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
AstBegin* const replayp = new AstBegin{flp, "", remainingp, true};
replayp->addStmtsp(
new AstAssign{flp, new AstVarRef{flp, remainingp, VAccess::WRITE}, matchCountp});
AstLoop* const loopp = new AstLoop{flp};
loopp->addStmtsp(
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 AstConst{flp, AstConst::WidthedValue{},
remainingp->dtypep()->width(), 1}}});
replaysp->addNext(loopp);
passsp = replaysp;
replayp->addStmtsp(loopp);
passsp = replayp;
}
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
-2
View File
@@ -28,8 +28,6 @@ class V3AssertCommon final {
public:
static void collectDefaultDisable(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 {
+228 -438
View File
File diff suppressed because it is too large Load Diff
+29 -27
View File
@@ -68,7 +68,6 @@ private:
V3UniqueNames m_gotoRepNames{"__VgotoRep"}; // Goto repetition counter name generator
V3UniqueNames m_nonConsRepNames{"__VnonConsRep"}; // Nonconsecutive rep 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_activeNames{"__VassertsActive"}; // Active asserts map name generator
bool m_inAssign = false; // True if in an AssignNode
@@ -415,7 +414,6 @@ private:
flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, VCMethod::DYN_POP,
new AstTime{nodep->fileline(), m_modp->timeunit()}};
popp->addPinsp(skewp->unlinkFrBack());
refp->access(VAccess::READWRITE); // Only conditionally assigned
popp->addPinsp(refp);
popp->dtypeSetVoid();
m_clockingp->addNextHere(
@@ -563,7 +561,8 @@ private:
}
if (m_disableSeqIfp && remainp) {
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;
}
if (remainp) {
@@ -660,9 +659,9 @@ private:
iterate(nodep->lhsp());
}
iterate(nodep->rhsp());
if (nodep->timingControlp()) iterate(nodep->timingControlp());
// A drive without a cycle delay, also after removing '##0', updates the clockvar now
if (m_inSynchDrive && !nodep->timingControlp()) {
if (nodep->timingControlp()) {
iterate(nodep->timingControlp());
} else if (m_inSynchDrive) {
AstAssign* const assignp = new AstAssign{
nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()};
assignp->user1(true);
@@ -878,7 +877,7 @@ private:
= getProcessAssocArrayDelete(new AstVarRef{flp, activep, VAccess::WRITE});
// 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(loopp);
bodyp->addStmtsp(clausep);
@@ -907,7 +906,7 @@ private:
new AstConst{flp, 1}}});
// 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(initActiveCountp);
finalp->addStmtsp(finalLoopp);
@@ -1073,13 +1072,15 @@ private:
windowp->addStmtsp(new AstLoopTest{
flp, windowp, new AstNot{flp, new AstVarRef{flp, doneVarp, VAccess::READ}}});
// if (expr) { fail; done = 1; } -- window closed, expr true again
AstNode* const failsp = new AstPExprClause{flp, false};
failsp->addNext(setDone());
windowp->addStmtsp(new AstIf{flp, exprp->cloneTreePure(false), failsp});
AstBegin* const failBlockp = new AstBegin{flp, "", nullptr, true};
failBlockp->addStmtsp(new AstPExprClause{flp, false});
failBlockp->addStmtsp(setDone());
windowp->addStmtsp(new AstIf{flp, exprp->cloneTreePure(false), failBlockp});
// if (rhs) { pass; done = 1; } -- consequent true at this !expr endpoint
AstNode* const passsp = new AstPExprClause{flp, true};
passsp->addNext(setDone());
windowp->addStmtsp(new AstIf{flp, rhsp, passsp});
AstBegin* const passBlockp = new AstBegin{flp, "", nullptr, true};
passBlockp->addStmtsp(new AstPExprClause{flp, true});
passBlockp->addStmtsp(setDone());
windowp->addStmtsp(new AstIf{flp, rhsp, passBlockp});
// @(clk) -- advance to next cycle in window
windowp->addStmtsp(
new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
@@ -1293,7 +1294,7 @@ private:
// */ }
AstBegin* const bodyp = pexprp->bodyp();
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);
nodep->replaceWith(pexprp);
// Don't iterate pexprp here -- it was already iterated when created
@@ -1367,9 +1368,10 @@ private:
loopp->addStmtsp(new AstLoopTest{
flp, loopp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}});
{
AstNode* const passp = new AstPExprClause{flp};
passp->addNext(decrementVar);
passp->addNext(setDone());
AstBegin* const passp = new AstBegin{flp, "", nullptr, true};
passp->addStmtsp(new AstPExprClause{flp});
passp->addStmtsp(decrementVar);
passp->addStmtsp(setDone());
AstNodeExpr* passCondp = rhsp;
if (nodep->isOverlapping()) {
passCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), passCondp};
@@ -1377,9 +1379,10 @@ private:
loopp->addStmtsp(new AstIf{flp, passCondp, passp});
}
{
AstNode* const failp = new AstPExprClause{flp, false};
failp->addNext(decrementVar->cloneTree(false));
failp->addNext(setDone());
AstBegin* const failp = new AstBegin{flp, "", nullptr, true};
failp->addStmtsp(new AstPExprClause{flp, false});
failp->addStmtsp(decrementVar->cloneTree(false));
failp->addStmtsp(setDone());
loopp->addStmtsp(new AstIf{
flp,
new AstLogAnd{flp,
@@ -1393,7 +1396,7 @@ private:
flp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}, delayp});
// 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(new AstAssign{flp, new AstVarRef{flp, donep, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}});
@@ -1425,7 +1428,7 @@ private:
new AstConst{flp, 1}}});
// 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(initActiveCountp);
finalp->addStmtsp(finalLoopp);
@@ -1456,7 +1459,7 @@ private:
AstNodeExpr* const passCondp
= nodep->isOverlapping() ? new AstLogAnd{flp, lhsp->cloneTreePure(false), rhsCopyp}
: rhsCopyp;
AstBegin* const beginp = new AstBegin{flp, m_blockNames.get(""), loopp, true};
AstBegin* const beginp = new AstBegin{flp, "", loopp, true};
beginp->addStmtsp(
new AstIf{flp, passCondp, new AstPExprClause{flp}, new AstPExprClause{flp, false}});
@@ -1545,7 +1548,7 @@ private:
"Unsupported: $sampled inside disabled condition of a sequence");
m_disablep = new AstConst{m_disablep->fileline(), AstConst::BitFalse{}};
// always a copy is used, so remove it now
pushDeletep(m_disablep); // m_disablep used later
pushDeletep(m_disablep);
}
FileLine* const flp = nodep->fileline();
// Add counter which counts times the condition turned true
@@ -1574,7 +1577,6 @@ private:
= new AstAssign{flp, new AstVarRef{flp, initialCntp, VAccess::WRITE},
readCntRefp->cloneTree(false)};
// 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();
bodyp->addStmtsp(initialCntp);
initialCntp->addNextHere(assignp);
@@ -1584,7 +1586,7 @@ private:
= new AstIf{flp, new AstEq{flp, new AstVarRef{flp, initialCntp, VAccess::READ},
readCntRefp->cloneTree(false)}};
// Delete it, because it is always copied before insetion to the AST
pushDeletep(m_disableSeqIfp); // m_disableSeqIfp used later
pushDeletep(m_disableSeqIfp);
}
iterateChildren(nodep);
}
+1 -5
View File
@@ -66,7 +66,7 @@ VCMethod VCMethod::arrayMethod(const string& name) {
std::string VNUser::dumpStr(std::string (*fmtAddrp)(const void*)) const {
#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);
return "";
#else
@@ -208,10 +208,6 @@ string AstNode::dedotName(const string& namein) {
return pretty;
}
string AstNode::nameNoArray(const string& namein) {
return namein.substr(0, namein.find("__BRA__"));
}
string AstNode::vcdName(const string& namein) {
// VCD tracing expects space to separate hierarchy
// Dots are reserved for dots the user put in the name
+40 -41
View File
@@ -56,7 +56,6 @@
// Forward declarations
class V3Graph;
class ExecMTask;
class VIfaceCaptureTag;
//######################################################################
@@ -152,11 +151,11 @@ class VNUser final {
#ifdef VL_USER_TYPE_CHECKS
// monostate is an unwritten / cleared slot. It can be read as either form
// and yields nullptr/0.
std::variant<std::monostate, uint64_t, void*> m_u;
std::variant<std::monostate, int, void*> m_u;
#else
union {
void* up;
uint64_t uq;
int ui;
} m_u;
#endif
@@ -165,7 +164,7 @@ public:
VNUser() = default;
// non-explicit:
// cppcheck-suppress noExplicitConstructor
VNUser(uint64_t i) {
VNUser(int i) {
// VNUser{0} represents the monostate
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 {
if (std::holds_alternative<std::monostate>(m_u)) return nullptr;
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);
}
uint64_t toUQuad() const {
int toInt() const {
if (std::holds_alternative<std::monostate>(m_u)) return 0;
const uint64_t* const uip = std::get_if<uint64_t>(&m_u);
UASSERT_STATIC(uip, "AstNode user() slot written as pointer, read as uint64_t");
const int* const uip = std::get_if<int>(&m_u);
UASSERT_STATIC(uip, "AstNode user() slot written as pointer, read as int");
return *uip;
}
#else
VNUser() = default;
// non-explicit:
// cppcheck-suppress noExplicitConstructor
VNUser(uint64_t i) {
VNUser(int i) {
m_u.up = nullptr;
m_u.uq = i;
m_u.ui = i;
}
explicit VNUser(void* p) { m_u.up = p; }
~VNUser() = default;
@@ -203,7 +202,7 @@ public:
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE {
return reinterpret_cast<T>(m_u.up);
}
uint64_t toUQuad() const { return m_u.uq; }
int toInt() const { return m_u.ui; }
#endif
VSymEnt* toSymEnt() const { return to<VSymEnt*>(); }
AstNode* toNodep() const VL_MT_SAFE { return to<AstNode*>(); }
@@ -392,7 +391,7 @@ protected:
RELINK_OP4
};
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;
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
// (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)
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_user2Cnt = 0; // Mark of when userp was set
VNUser m_user2u{nullptr}; // Contains any information the user iteration routine wants
VNUser m_user3u{nullptr}; // Contains any information the user iteration routine wants
VNUser m_user2u{0}; // 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_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
void op1p(AstNode* nodep) {
@@ -659,7 +658,6 @@ public:
string origNameProtect() const; // origName with --protect-id applied
string shortName() const; // Name with __PVT__ removed for concatenating scopes
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 vpiName(const string& namein); // Name for vpi access
static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors)
@@ -710,61 +708,61 @@ public:
VNUser user1u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//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(); }
void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; }
void user1p(void* userp) { user1u(VNUser{userp}); }
void user1(uint64_t val) { user1u(VNUser{val}); }
uint64_t user1() const { return user1u().toUQuad(); }
uint64_t user1Inc(uint64_t val = 1) { const uint64_t v = user1(); user1(v + val); return v; }
uint64_t user1Or(uint64_t val) { const uint64_t 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()
void user1(int val) { user1u(VNUser{val}); }
int user1() const { return user1u().toInt(); }
int user1Inc(int val = 1) { const int v = user1(); user1(v + val); return v; }
int user1Or(int val) { const int v = user1(); user1(v | val); return v; }
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
VNUser user2u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//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(); }
void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; }
void user2p(void* userp) { user2u(VNUser{userp}); }
void user2(uint64_t val) { user2u(VNUser{val}); }
uint64_t user2() const { return user2u().toUQuad(); }
uint64_t user2Inc(uint64_t val = 1) { const uint64_t v = user2(); user2(v + val); return v; }
uint64_t user2Or(uint64_t val) { const uint64_t 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()
void user2(int val) { user2u(VNUser{val}); }
int user2() const { return user2u().toInt(); }
int user2Inc(int val = 1) { const int v = user2(); user2(v + val); return v; }
int user2Or(int val) { const int v = user2(); user2(v | val); return v; }
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
VNUser user3u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//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(); }
void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; }
void user3p(void* userp) { user3u(VNUser{userp}); }
void user3(uint64_t val) { user3u(VNUser{val}); }
uint64_t user3() const { return user3u().toUQuad(); }
uint64_t user3Inc(uint64_t val = 1) { const uint64_t v = user3(); user3(v + val); return v; }
uint64_t user3Or(uint64_t val) { const uint64_t 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()
void user3(int val) { user3u(VNUser{val}); }
int user3() const { return user3u().toInt(); }
int user3Inc(int val = 1) { const int v = user3(); user3(v + val); return v; }
int user3Or(int val) { const int v = user3(); user3(v | val); return v; }
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
VNUser user4u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//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(); }
void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; }
void user4p(void* userp) { user4u(VNUser{userp}); }
void user4(uint64_t val) { user4u(VNUser{val}); }
uint64_t user4() const { return user4u().toUQuad(); }
uint64_t user4Or(uint64_t val) { const uint64_t v = user4(); user4(v | val); return v; }
uint64_t user4Inc(uint64_t val = 1) { const uint64_t 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()
void user4(int val) { user4u(VNUser{val}); }
int user4() const { return user4u().toInt(); }
int user4Or(int val) { const int v = user4(); user4(v | val); return v; }
int user4Inc(int val = 1) { const int v = user4(); user4(v + val); return v; }
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
// clang-format on
@@ -1037,6 +1035,7 @@ protected:
AstNode* iterateSubtreeReturnEdits(VNVisitor& v);
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 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);
+265 -400
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::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)
: ASTGEN_SUPER_Always(assignp->fileline(), assignp)
, m_keyword{VAlwaysKwd::CONT_ASSIGN} {}
@@ -168,10 +165,10 @@ bool AstVar::sameNode(const AstNode* samep) const {
&& covergroupRefMember() == asamep->covergroupRefMember();
}
AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarRef* matchp)
AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp)
: ASTGEN_SUPER_MatchMasked(fl) {
this->lhsp(lhsp);
this->matchp(matchp);
this->matchp(new AstVarRef{fl, matchp, VAccess::READ});
dtypeSetUInt32();
}
+22 -108
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,
// but it's currently so prevalent in the code we leave it here.
// 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
// (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation
// dist-ast-dump-suppress // Part of dumpSmall
int m_widthMin = 0;
// dist-ast-dump-suppress // Part of dumpSmall
int m_widthMin
= 0; // (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation
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
// Unique number assigned to each dtype during creation for IEEE matching
static int s_uniqueNum;
@@ -250,8 +246,8 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType {
//
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Package emitted with
string m_name; // Name from upper typedef, if any
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
bool m_packed; // Packed struct/union, else unpacked
const int m_uniqueNum;
bool m_packed;
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_emitToString = false; // Generate to_string() for this struct/union if set
@@ -319,7 +315,7 @@ public:
class AstEnumItem final : public AstNode {
// @astgen op1 := rangep : Optional[AstRange] // Range for name appending
// @astgen op2 := valuep : Optional[AstNodeExpr]
string m_name; // Name of item
string m_name;
public:
// Parents: ENUM
@@ -409,7 +405,6 @@ class AstBasicDType final : public AstNodeDType {
// @astgen op1 := rangep : Optional[AstRange] // Range of variable
struct Members final {
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)
bool operator==(const Members& rhs) const {
return rhs.m_keyword == m_keyword && rhs.m_nrange == m_nrange;
@@ -498,9 +493,6 @@ public:
bool isStdRandomGenerator() const VL_MT_SAFE {
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 isString() const VL_MT_STABLE { return keyword().isString(); }
bool isZeroInit() const { return keyword().isZeroInit(); }
@@ -690,89 +682,13 @@ public:
int widthTotalBytes() const override { return 1; }
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 {
// 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
// After link, these become typedefs
// @astgen op1 := childDTypep : Optional[AstNodeDType]
string m_name; // Data type name
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
string m_name;
const int m_uniqueNum;
public:
AstDefImplicitDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp)
@@ -882,8 +798,13 @@ class AstEnumDType final : public AstNodeDType {
// @astgen op2 := itemsp : List[AstEnumItem]
//
// @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
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
const int m_uniqueNum;
TableMap m_tableMap; // Created table for V3Width only to remove duplicates
public:
AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstEnumItem* itemsp)
@@ -931,12 +852,13 @@ public:
return count;
}
bool isCompound() const override { return false; }
TableMap& tableMap() { return m_tableMap; }
const TableMap& tableMap() const { return m_tableMap; }
};
class AstIfaceGenericDType final : public AstNodeDType {
// Generic interface that will be replaced with AstIfaceRefDType
FileLine* m_modportFileline; // Where modport token was
// dist-ast-dump-suppress // Part of name()
string m_modportName; // "" = no modport
public:
explicit AstIfaceGenericDType(FileLine* fl)
@@ -950,6 +872,8 @@ public:
dtypep(this);
}
ASTGEN_MEMBERS_AstIfaceGenericDType;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
void dumpSmall(std::ostream& str) const override;
bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
@@ -979,6 +903,7 @@ class AstIfaceRefDType final : public AstNodeDType {
string m_cellName; // "" = no cell, such as when connects to 'input' iface
string m_ifaceName; // Interface name
string m_modportName; // "" = no modport
bool m_portDecl = false; // Interface_port_declaration
bool m_virtual = false; // True if virtual interface
public:
AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName)
@@ -1010,16 +935,11 @@ public:
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
bool similarDTypeNode(const AstNodeDType* samep) const override {
// 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();
}
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
int widthAlignBytes() 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; }
void isVirtual(bool flag) {
m_virtual = flag;
@@ -1054,7 +974,7 @@ class AstMemberDType final : public AstNodeDType {
string m_name; // Name of variable
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
bool m_constrainedRand = false; // Member has a constraint expression
bool m_constrainedRand = false;
VRandAttr m_rand; // Randomizability of this member (rand, randc, etc)
public:
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
@@ -1275,9 +1195,6 @@ class AstRefDType final : public AstNodeDType {
// @astgen ptr := m_refDTypep : Optional[AstNodeDType] // Data type references
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in
string m_name; // Name of an AstTypedef
// Interface capture tag, owned by V3LinkDotIfaceCapture; cloning copies it
const VIfaceCaptureTag* m_captureTagp = nullptr;
public:
AstRefDType(FileLine* fl, const string& name)
: ASTGEN_SUPER_RefDType(fl)
@@ -1304,7 +1221,6 @@ public:
bool similarDTypeNode(const AstNodeDType* samep) const override {
return subDTypep()->similarDType(samep->subDTypep());
}
const char* broken() const override;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
@@ -1329,8 +1245,6 @@ public:
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
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 {
v3fatalSrc("call isCompound on subdata type, not reference");
return false;
+16 -45
View File
@@ -66,18 +66,7 @@ public:
bool isOpaque() const { return VN_IS(this, CvtPackString); }
// True for SVA multi-cycle sequence nodes (SExpr, SConsRep, etc.)
virtual bool isMultiCycleSva() const { return false; }
const AstNodeExpr* getVAccessTargetRecurse() 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);
bool isLValue() const;
// Wrap This expression into an AstStmtExpr to denote it occurs in statement position
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 ptr := m_funcp : AstCFunc // Function being called
// dist-ast-dump-suppress // Too verbose
string m_argTypes;
bool m_superReference = false; // Called with super reference
@@ -228,7 +216,7 @@ public:
void funcp(AstCFunc* funcp) { m_funcp = funcp; }
string argTypes() const { return m_argTypes; }
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 emitC() final override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const final override { return true; }
@@ -283,7 +271,6 @@ public:
bool superReference() const { return m_superReference; }
void superReference(bool flag) { m_superReference = flag; }
bool isPure() override;
bool isUnlikely() const override { return m_taskp && m_taskp->isUnlikely(); }
bool sameNode(const AstNode* samep) const override {
const AstNodeFTaskRef* const asamep = VN_DBG_AS(samep, NodeFTaskRef);
return taskp() == asamep->taskp() //
@@ -532,6 +519,7 @@ public:
}
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
static AstNodeVarRef* varRefLValueRecurse(AstNode* nodep);
};
// === Concrete node types =====================================================
@@ -568,7 +556,6 @@ class AstWith final : public AstNode {
private:
// 'with (identifier_list) {...}' restricted form (IEEE 1800-2023 18.7).
bool m_restricted = false;
// dist-ast-dump-suppress // V3LinkDot temporary use only
bool m_validated = false; // identifier_list typo / unused checks already run
std::set<std::string> m_restrictedNames;
@@ -752,7 +739,7 @@ class AstCMethodHard final : public AstNodeExpr {
// @astgen op2 := pinsp : List[AstNodeExpr] // Arguments
// @astgen op3 := withp : Optional[AstWith] // With clause
VCMethod m_method; // Which method to call
VIsCached m_purity; // Pure optimizable
bool m_pure = false; // Pure optimizable
bool m_usePtr = false; // Use '->' not '.'
public:
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VCMethod method, AstNodeExpr* pinsp = nullptr)
@@ -760,19 +747,15 @@ public:
, m_method{method} {
this->fromp(fromp);
addPinsp(pinsp);
setPurity();
}
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(); }
bool sameNode(const AstNode* samep) const override {
const AstCMethodHard* const asamep = VN_DBG_AS(samep, CMethodHard);
return (m_method == asamep->m_method);
}
bool isPure() override {
if (!m_purity.isCached()) m_purity.set(getPurity());
return m_purity.get();
}
bool isPure() override { return m_pure; }
int instrCount() const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
@@ -783,7 +766,7 @@ public:
void method(VCMethod value) { m_method = value; }
private:
bool getPurity();
void setPurity();
};
class AstCReset final : public AstNodeExpr {
// Reset variable at startup
@@ -1475,7 +1458,7 @@ class AstExprStmt final : public AstNodeExpr {
// @astgen op1 := stmtsp : List[AstNode]
// @astgen op2 := resultp : AstNodeExpr
private:
bool m_hasResult = true; // Returns result via resultp()
bool m_hasResult = true;
public:
AstExprStmt(FileLine* fl, AstNode* stmtsp, AstNodeExpr* resultp)
@@ -1486,8 +1469,6 @@ public:
}
ASTGEN_MEMBERS_AstExprStmt;
// METHODS
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
@@ -1765,7 +1746,7 @@ public:
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
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
// and non-vacuous-fail for followed-by, hence the separate flag.
// @astgen op1 := lhsp : AstNodeExpr
@@ -1793,7 +1774,7 @@ public:
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
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 isFollowedBy() const { return m_isFollowedBy; }
};
@@ -1812,7 +1793,6 @@ public:
using KeyItemMap = std::map<uint64_t, AstInitItem*>;
private:
// dist-ast-dump-suppress // Dumped using dumpInitList
KeyItemMap m_map; // Node value for each array index
// METHODS
void dumpInitList(std::ostream& str) const;
@@ -1886,8 +1866,6 @@ public:
, m_name{name}
, m_index{index} {}
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; }
string emitVerilog() override { return name(); }
string emitC() override { V3ERROR_NA_RETURN(""); }
@@ -1907,7 +1885,7 @@ class AstMatchMasked final : public AstNodeExpr {
// @astgen op1 := lhsp : AstNodeExpr
// @astgen op2 := matchp : AstVarRef
public:
inline AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarRef* matchp);
inline AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp);
ASTGEN_MEMBERS_AstMatchMasked;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { return "VL_MATCHMASKED_%lq(%lw, %li, %ri)"; }
@@ -2035,7 +2013,7 @@ class AstParseRef final : public AstNodeExpr {
// @astgen op1 := lhsp : Optional[AstNodeExpr]
// @astgen op2 := ftaskrefp : Optional[AstNodeFTaskRef]
string m_name; // Name of the variable/function/task
string m_name;
public:
AstParseRef(FileLine* fl, const string& name, AstNodeExpr* lhsp = nullptr,
@@ -2408,7 +2386,6 @@ class AstSFormatArg final : public AstNodeExpr {
// used to pass to (potentially) runtime decoding of format arguments
// PARENT: SFormatF (or next list of expressions)
// @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := namep : Optional[AstNodeExpr] // Runtime enum name lookup
VFormatAttr m_formatAttr; // How to format expression
public:
@@ -2431,7 +2408,6 @@ public:
bool cleanOut() const override { return true; }
const char* broken() const override {
BROKEN_RTN(!VN_IS(backp(), SFormatF) && firstAbovep()); // In list under SFormatF
BROKEN_RTN(formatAttr().isEnum() != static_cast<bool>(namep()));
return nullptr;
}
VFormatAttr formatAttr() const { return m_formatAttr; }
@@ -2637,7 +2613,7 @@ class AstScopeName final : public AstNodeExpr {
// For display %m and DPI context imports
// Parents: AstSFormatF, AstNodeFTaskRef, AstNodeFTask
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
const bool m_forFormat; // Is for a format %m
static std::string scopeNameFormatter(const std::string& text);
@@ -3059,8 +3035,6 @@ public:
addConstraintsp(constraintsp);
}
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 restricted() const { return m_restricted; }
void restricted(bool flag) { m_restricted = flag; }
@@ -4937,6 +4911,9 @@ public:
bool isPredictOptimizable() const override { return true; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
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 {
void init(const AstNode* fromp) {
@@ -5189,8 +5166,6 @@ public:
"not coded to create after dtypes resolved");
}
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; }
void access(const VAccess& flag) { m_access = flag; }
};
@@ -5455,7 +5430,6 @@ public:
enum FmtType : int { ATOI = 10, ATOHEX = 16, ATOOCT = 8, ATOBIN = 2, ATOREAL = -1 };
private:
// dist-ast-dump-suppress // Part of name()
const FmtType m_fmtType; // Operation type
public:
AstAtoN(FileLine* fl, AstNodeExpr* lhsp, FmtType fmtType)
@@ -6464,7 +6438,6 @@ class AstVarXRef final : public AstNodeVarRef {
string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out
bool m_containsGenBlock = false; // Contains gen block reference
bool m_readOnlyModport = false; // Linked via an input-only modport, until V3LinkLValue
public:
AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access)
: ASTGEN_SUPER_VarXRef(fl, nullptr, access)
@@ -6482,8 +6455,6 @@ public:
void inlinedDots(const string& flag) { m_inlinedDots = flag; }
bool containsGenBlock() const { return m_containsGenBlock; }
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 emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
+84 -230
View File
@@ -44,7 +44,6 @@ class AstNodeCoverDecl VL_NOT_FINAL : public AstNode {
// Coverage counters are emitted in each module object, so duplicate
// no-inline instances can keep independent counts for forcePerInstance.
int m_localBinNum = 0; // Per-module coverage bin offset
bool m_perInstance = false; // Don't clone during inlining, already per instance
public:
AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment)
: AstNode(t, fl)
@@ -66,8 +65,6 @@ public:
void binNum(int flag) { m_binNum = flag; }
int localBinNum() const { return m_localBinNum; }
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;
const string& comment() const { return m_text; } // text to insert in code
const string& page() const { return m_page; }
@@ -77,8 +74,7 @@ public:
bool sameNode(const AstNode* samep) const override {
const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl);
return (fileline() == asamep->fileline() && hier() == asamep->hier()
&& comment() == asamep->comment() && page() == asamep->page()
&& perInstance() == asamep->perInstance());
&& comment() == asamep->comment() && page() == asamep->page());
}
bool isPredictOptimizable() const override { return false; }
void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; }
@@ -98,7 +94,6 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
// @astgen op4 := scopeNamep : Optional[AstScopeName]
string m_name; // Name of task
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_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
@@ -117,8 +112,6 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
bool m_isHideLocal : 1; // Verilog local
bool m_isHideProtected : 1; // Verilog protected
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_recursive : 1; // Recursive or part of recursion
bool m_static : 1; // Static method in class
@@ -151,8 +144,6 @@ protected:
, m_isHideLocal{false}
, m_isHideProtected{false}
, m_dpiPure{false}
, m_keepAlive{false}
, m_needsSyms{true}
, m_pureVirtual{false}
, m_recursive{false}
, m_static{false}
@@ -220,10 +211,6 @@ public:
void isHideProtected(bool flag) { m_isHideProtected = flag; }
bool dpiPure() const { return m_dpiPure; }
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; }
void pureVirtual(bool flag) { m_pureVirtual = flag; }
bool recursive() const { return m_recursive; }
@@ -306,8 +293,8 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
// @astgen op2 := stmtsp : List[AstNode]
string m_name; // Name of the module
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.
// Used for user messages only.
string m_libname; // Work library
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
@@ -325,6 +312,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
bool m_hasParameterList : 1; // Has #() for parameter declaration
bool m_hierBlock : 1; // Hierarchical Block marked by HIER_BLOCK pragma
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_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
bool m_parameterizedTemplate : 1; // True when at least one specialized clone exists;
@@ -346,6 +334,7 @@ protected:
, m_hasParameterList{false}
, m_hierBlock{false}
, m_hierParams{false}
, m_internal{false}
, m_recursive{false}
, m_recursiveClone{false}
, m_parameterizedTemplate{false}
@@ -358,8 +347,6 @@ public:
bool maybePointedTo() const override VL_MT_SAFE { return true; }
string name() const override VL_MT_STABLE { return m_name; }
virtual bool timescaleMatters() const = 0;
inline bool isDollarUnit() const; // Is the $unit package
inline bool isConstPool() const; // Is the constant pool package
// ACCESSORS
void name(const string& name) override { m_name = name; }
string origName() const override { return m_origName; }
@@ -391,6 +378,8 @@ public:
void hierBlock(bool flag) { m_hierBlock = flag; }
bool hierParams() const { return m_hierParams; }
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; }
void recursive(bool flag) { m_recursive = 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_needProcess : 1; // Needs access to VlProcess of the caller
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
// state
bool m_isCovergroupSample : 1; // Automatic covergroup sample() function
@@ -587,7 +575,6 @@ public:
m_dpiImportPrototype = false;
m_dpiImportWrapper = false;
m_recursive = false;
m_unlikely = false;
m_noLife = false;
m_isCovergroupSample = false;
m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost
@@ -666,8 +653,6 @@ public:
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
void recursive(bool flag) { m_recursive = flag; }
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; }
bool noLife() const { return m_noLife; }
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)
// @astgen op1 := pinsp : List[AstPin] // List of port assignments
// @astgen op2 := paramsp : List[AstPin] // List of parameter assignments
// Pre V3Param: Range(s) for arrayed instances.
// From V3Param to V3Width: ranges of the arrayed cell this element cell was expanded from
// After V3Width: nullptr
// @astgen op3 := rangep : List[AstRange]
// @astgen op4 := intfRefsp : List[AstIntfRef] // List of interface references, for tracing/VPI
// @astgen op3 := rangep : List[AstRange] // Range(s) for arrayed instances; multi-dim chains
// via nextp()
// @astgen op4 := intfRefsp : List[AstIntfRef] // List of interface references, for tracing
//
// @astgen ptr := m_modp : Optional[AstNodeModule] // [AfterLink] Pointer to module instanced
FileLine* m_modNameFileline; // Where module the cell instances token was
string m_name; // Cell name
string m_origName; // Original name before dot addition
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_recursive : 1; // Self-recursive module
bool m_trace : 1; // Trace this cell
@@ -746,8 +728,6 @@ public:
FileLine* modNameFileline() const { return m_modNameFileline; }
AstNodeModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated
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; }
void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; }
void trace(bool flag) { m_trace = flag; }
@@ -801,9 +781,6 @@ public:
string name() const override VL_MT_STABLE { return m_cellp->name(); }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
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 {
return m_cellp->origModName();
} // * = modp()->origName() before inlining
@@ -912,8 +889,6 @@ public:
}
}
ASTGEN_MEMBERS_AstClockingItem;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VDirection direction() const { return m_direction; }
AstClockingItem* outputp() const { return m_outputp; }
void outputp(AstClockingItem* outputp) { m_outputp = outputp; }
@@ -932,11 +907,11 @@ public:
, m_libname{libname}
, m_configname{cellname} {}
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 libname() const VL_MT_STABLE { return m_libname; }
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 {
// Parents: CONFIGRULE
@@ -995,6 +970,40 @@ public:
void dump(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 {
// Constraint
// @astgen op1 := itemsp : List[AstNode]
@@ -1042,13 +1051,12 @@ class AstCoverBin final : public AstNode {
// @astgen op4 := transp : List[AstCoverTransSet]
const string m_name; // Base name of the bin
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
// arraySizep() bins
bool m_isArray = false; // Bin is either an auto-sized array of values or transitions
bool m_isWildcard = false; // Bin uses wildcard matching (independent of ignore/illegal)
public:
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)
, m_name{name}
, m_binsType{isIllegal ? VCoverBinsType::BINS_ILLEGAL
@@ -1057,14 +1065,12 @@ public:
: VCoverBinsType::BINS_USER))}
, m_isWildcard{isWildcard} {
addRangesp(rangesp);
this->iffp(iffp);
}
// Constructor for automatic bins
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep,
VCoverBinsType type = VCoverBinsType::BINS_AUTO)
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_binsType{type}
, m_binsType{VCoverBinsType::BINS_AUTO}
, m_isArray{true} {
this->arraySizep(arraySizep);
}
@@ -1092,138 +1098,21 @@ public:
bool isArray() const { return m_isArray; }
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 {
// Coverage-option assignment
// @astgen op1 := valuep : AstNodeExpr
const VCoverOptionType m_optType; // Option being assigned
const bool m_typeOption; // type_option (vs option)
public:
AstCoverOption(FileLine* fl, bool typeOption, VCoverOptionType optType, AstNodeExpr* valuep)
AstCoverOption(FileLine* fl, VCoverOptionType optType, AstNodeExpr* valuep)
: ASTGEN_SUPER_CoverOption(fl)
, m_optType{optType}
, m_typeOption{typeOption} {
, m_optType{optType} {
this->valuep(valuep);
}
ASTGEN_MEMBERS_AstCoverOption;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
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 {
// 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 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 {
// Represents a covergroup declaration. V3LinkParse transforms this
// into an AstClass with isCovergroup==true and attaches the clocking
@@ -1383,8 +1258,6 @@ public:
BROKEN_RTN(!fmtp());
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()}; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
@@ -1447,21 +1320,13 @@ public:
};
class AstIntfRef final : public AstNode {
// An interface reference
string m_name; // Hierarchical path 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
string m_name; // Name of the reference
public:
AstIntfRef(FileLine* fl, const string& name, const string& baseName, const string& modportName)
AstIntfRef(FileLine* fl, const string& name)
: ASTGEN_SUPER_IntfRef(fl)
, 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;
, m_name{name} {}
string name() const override VL_MT_STABLE { return m_name; }
string baseName() const { return m_baseName; }
string modportName() const { return m_modportName; }
ASTGEN_MEMBERS_AstIntfRef;
};
class AstLibrary final : public AstNode {
// Parents: NETLIST
@@ -1575,7 +1440,7 @@ class AstNetlist final : public AstNode {
// @astgen op3 := miscsp : List[AstNode]
//
// @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_stdPackagep : Optional[AstPackage] // SystemVerilog std package
// @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
// name.
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
std::array<AstCFunc*, VEval::_ENUM_END> m_evalFuncps{};
// 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
}
AstTypeTable* typeTablep() { return m_typeTablep; }
AstConstPool* constPoolp() { return m_constPoolp; }
string astConstOrigParamName(const AstConst* nodep) const;
void astConstOrigParamName(const AstConst* nodep, const string& name);
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; }
void dollarUnitPkgp(AstPackage* const packagep) { m_dollarUnitPkgp = packagep; }
AstPackage* dollarUnitPkgAddp();
AstCFunc* evalFuncp(VEval eval) const { return m_evalFuncps[eval]; }
void evalFuncp(VEval eval, AstCFunc* funcp) { m_evalFuncps[eval] = funcp; }
AstCFunc* dumpTriggersFuncp(VEval eval) const { return m_dumpTriggersFuncps[eval]; }
@@ -1899,9 +1758,7 @@ public:
AstNodeModule* modp() const { return m_modp; }
//
AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; }
void aboveScopep(AstScope* nodep) { m_aboveScopep = nodep; }
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
// Create new MODULETEMP variable under this scope
AstVarScope* createTemp(const string& name, unsigned width);
@@ -2058,11 +1915,8 @@ public:
class AstTextBlock final : public AstNode {
// Text block emitted into output, with some arbitrary nodes interspersed
// @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'
// dist-ast-dump-suppress // Omitting text blocks due to verbosity
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'
public:
explicit AstTextBlock(FileLine* fl, //
@@ -2111,7 +1965,6 @@ class AstTypeTable final : public AstNode {
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
//
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
// dist-ast-dump-suppress // Link to other nodes
DetailedMap m_detailedMap;
public:
@@ -2140,10 +1993,10 @@ class AstTypedef final : public AstNode {
// @astgen op1 := childDTypep : Optional[AstNodeDType]
// @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.
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_isHideProtected : 1; // Verilog protected
bool m_isUnderClass : 1; // Underneath class
@@ -2309,6 +2162,7 @@ class AstVar final : public AstNode {
bool m_attrSFormat : 1; // User sformat attribute
bool m_attrSplitVar : 1; // declared with split_var metacomment
bool m_attrFsmState : 1; // declared with fsm_state metacomment
bool m_attrFsmRegisterWrapper : 1; // connected to an fsm_register_wrapper instance
bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment
bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment
bool m_constPoolEntry : 1; // Constant pool variable
@@ -2324,8 +2178,6 @@ class AstVar final : public AstNode {
bool m_isPulldown : 1; // Tri0
bool m_isPullup : 1; // Tri1
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_isIfaceParam : 1; // Parameter belongs to an interface/modport
bool m_isDpiOpenArray : 1; // DPI import open array
@@ -2375,6 +2227,7 @@ class AstVar final : public AstNode {
m_attrSFormat = false;
m_attrSplitVar = false;
m_attrFsmState = false;
m_attrFsmRegisterWrapper = false;
m_attrFsmResetArc = false;
m_attrFsmArcInclCond = false;
m_constPoolEntry = false;
@@ -2390,7 +2243,6 @@ class AstVar final : public AstNode {
m_isPulldown = false;
m_isPullup = false;
m_isIfaceParent = false;
m_isIfaceArraySplit = false;
m_isInternal = false;
m_isIfaceParam = false;
m_isDpiOpenArray = false;
@@ -2506,7 +2358,7 @@ public:
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
bool asRef = false, bool constRef = false) const;
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
void combineType(VVarType type);
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
@@ -2526,6 +2378,7 @@ public:
void attrSFormat(bool flag) { m_attrSFormat = flag; }
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
void attrFsmState(bool flag) { m_attrFsmState = flag; }
void attrFsmRegisterWrapper(bool flag) { m_attrFsmRegisterWrapper = flag; }
void attrFsmResetArc(bool flag) { m_attrFsmResetArc = flag; }
void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; }
bool constPoolEntry() const { return m_constPoolEntry; }
@@ -2552,7 +2405,6 @@ public:
void isContinuously(bool flag) { m_isContinuously = flag; }
void isStatic(bool flag) { m_isStatic = flag; }
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
void isIfaceArraySplit(bool flag) { m_isIfaceArraySplit = flag; }
void isInternal(bool flag) { m_isInternal = flag; }
void isIfaceParam(bool flag) { m_isIfaceParam = flag; }
void funcLocal(bool flag) {
@@ -2640,7 +2492,6 @@ public:
m_varType = VVarType::IFACEREF;
}
bool isIfaceParent() const { return m_isIfaceParent; }
bool isIfaceArraySplit() const { return m_isIfaceArraySplit; }
bool isInternal() const { return m_isInternal; }
bool isIfaceParam() const { return m_isIfaceParam; }
bool isSignal() const { return varType().isSignal(); }
@@ -2696,6 +2547,7 @@ public:
bool attrSFormat() const { return m_attrSFormat; }
bool attrSplitVar() const { return m_attrSplitVar; }
bool attrFsmState() const { return m_attrFsmState; }
bool attrFsmRegisterWrapper() const { return m_attrFsmRegisterWrapper; }
bool attrFsmResetArc() const { return m_attrFsmResetArc; }
bool attrFsmArcInclCond() const { return m_attrFsmArcInclCond; }
AstIface* sensIfacep() const { return m_sensIfacep; }
@@ -2719,6 +2571,13 @@ public:
lifetime(fromp->lifetime());
}
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 {
return !isIfaceParent() && !isIfaceRef() && !noReset() && !isParam() && !isStatementTemp()
&& !noCReset() && !(basicp() && basicp()->isEvent());
@@ -2761,7 +2620,6 @@ public:
bool sameNode(const AstNode* samep) const override;
bool hasDType() const override VL_MT_SAFE { return true; }
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
void scopep(AstScope* nodep) { m_scopep = nodep; }
bool isTrace() const { return m_trace; }
@@ -2775,10 +2633,10 @@ class AstCoverOtherDecl final : public AstNodeCoverDecl {
// Coverage analysis point declaration
// Used for other than toggle types of coverage
string m_linescov;
string m_fsmVar; // FSM state variable name
string m_fsmFrom; // FSM source state label
string m_fsmTo; // FSM destination state label
string m_fsmTag; // FSM arc kind tag (e.g. reset, reset_include, default)
string m_fsmVar;
string m_fsmFrom;
string m_fsmTo;
string m_fsmTag;
int m_offset; // Offset column numbers to uniq-ify IFs
public:
AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment,
@@ -2804,7 +2662,7 @@ public:
const string& fsmTag() const { return m_fsmTag; }
bool sameNode(const AstNode* samep) const override {
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 {
@@ -2826,7 +2684,7 @@ public:
const VNumRange& range() const { return m_range; }
bool sameNode(const AstNode* samep) const override {
const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl);
return Super::sameNode(samep) && range() == asamep->range();
return AstNodeCoverDecl::sameNode(samep) && range() == asamep->range();
}
};
@@ -2944,8 +2802,8 @@ public:
class AstCoverCross final : public AstNodeFuncCovItem {
// @astgen op1 := itemsp : List[AstCoverpointRef]
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
// @astgen op3 := binsp : List[AstNode] // Parse: mixed cross bins/options;
// // post-LinkParse: AstCoverCrossBin only
// @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items;
// // post-LinkParse: empty
// @astgen op4 := iffp : Optional[AstNodeExpr] // Conditional sampling guard
public:
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp,
@@ -3057,8 +2915,6 @@ class AstClass final : public AstNodeModule {
// @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class
uint32_t m_declTokenNum; // Declaration token number
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_extended = false; // Is extension or extended by other classes
bool m_interfaceClass = false; // Interface class
@@ -3087,8 +2943,6 @@ public:
void covergroupEnclosingClassp(AstClass* classp) { m_covergroupEnclosingClassp = classp; }
AstNode* membersp() const VL_MT_STABLE { return stmtsp(); }
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; }
void isCovergroup(bool flag) { m_covergroup = flag; }
bool isExtended() const { return m_extended; }
@@ -3177,11 +3031,9 @@ public:
AstIface(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_Iface(fl, name, libname) {}
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
// get false warnings if we enable this
string verilogKwd() const override { return "interface"; }
bool timescaleMatters() const override { return false; }
bool hasVirtualRef() const { return m_hasVirtualRef; }
void setHasVirtualRef() { m_hasVirtualRef = true; }
@@ -3235,7 +3087,9 @@ public:
: ASTGEN_SUPER_Package(fl, name, libname) {}
ASTGEN_MEMBERS_AstPackage;
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 {
// A primitive declaration
+41 -13
View File
@@ -413,16 +413,16 @@ class AstCase final : public AstNodeStmt {
// @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression
// @astgen op2 := itemsp : List[AstCaseItem]
// @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_parallelPragma = false; // Synthesis parallel_case
bool m_uniquePragma = false; // unique case
bool m_unique0Pragma = false; // unique0 case
bool m_priorityPragma = false; // priority case
public:
AstCase(FileLine* fl, VCaseType caseType, AstNodeExpr* exprp, AstCaseItem* itemsp)
AstCase(FileLine* fl, VCaseType casex, AstNodeExpr* exprp, AstCaseItem* itemsp)
: ASTGEN_SUPER_Case(fl)
, m_caseType{caseType} {
, m_casex{casex} {
this->exprp(exprp);
addItemsp(itemsp);
}
@@ -432,15 +432,15 @@ public:
int instrCount() const override { return INSTR_COUNT_BRANCH; }
string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; }
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 casez() const { return m_caseType == VCaseType::CT_CASEZ; }
bool caseInside() const { return m_caseType == VCaseType::CT_CASEINSIDE; }
bool caseMatches() const { return m_caseType == VCaseType::CT_CASEMATCHES; }
bool caseSimple() const { return m_caseType == VCaseType::CT_CASE; }
void caseInsideSet() { m_caseType = VCaseType::CT_CASEINSIDE; }
void caseMatchesSet() { m_caseType = VCaseType::CT_CASEMATCHES; }
bool casex() const { return m_casex == VCaseType::CT_CASEX; }
bool casez() const { return m_casex == VCaseType::CT_CASEZ; }
bool caseInside() const { return m_casex == VCaseType::CT_CASEINSIDE; }
bool caseMatches() const { return m_casex == VCaseType::CT_CASEMATCHES; }
bool caseSimple() const { return m_casex == VCaseType::CT_CASE; }
void caseInsideSet() { m_casex = VCaseType::CT_CASEINSIDE; }
void caseMatchesSet() { m_casex = VCaseType::CT_CASEMATCHES; }
bool fullPragma() const { return m_fullPragma; }
void fullPragma(bool flag) { m_fullPragma = flag; }
bool parallelPragma() const { return m_parallelPragma; }
@@ -549,7 +549,29 @@ public:
bool isOutputter() override { return true; }
bool isPure() override { return false; }
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 {
// Procedural 'deassign' statement
@@ -1199,6 +1221,13 @@ public:
ASTGEN_MEMBERS_AstSetuphold;
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 {
// $stacktrace used as task
public:
@@ -1311,7 +1340,6 @@ class AstTraceDecl final : public AstNodeStmt {
const VVarType m_varType; // Type of variable (for localparam vs. param)
const VDirection m_declDirection; // Declared direction input/output etc
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
public:
AstTraceDecl(FileLine* fl, const string& showname,
+3727 -3667
View File
File diff suppressed because it is too large Load Diff
-46
View File
@@ -26,8 +26,6 @@
#include "V3Broken.h"
#include "V3ConstPool.h"
#include <unordered_set>
VL_DEFINE_DEBUG_FUNCTIONS;
@@ -229,45 +227,6 @@ private:
}
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
void visit(AstNodeAssign* nodep) override {
processEnter(nodep);
@@ -332,7 +291,6 @@ private:
void visit(AstCMethodHard* nodep) override {
++m_nCalls;
processAndIterate(nodep);
checkArgRefs(nodep, nodep->method().args(), nodep->pinsp());
}
void visit(AstNodeFTaskRef* nodep) override {
++m_nCalls;
@@ -434,10 +392,6 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
// Check every node in tree
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_linkableTable.clear();
s_brokenCntGlobal.inc();
+3 -3
View File
@@ -152,7 +152,6 @@ class CCtorsVisitor final : public VNVisitor {
// VISITORS
void visit(AstNodeModule* nodep) override {
if (nodep->isConstPool()) return; // Special emit rules
if (const AstClass* const classp = VN_CAST(nodep, Class)) {
// Interface class may only have pure virtuals and params which do not need cctor reset
if (classp->isInterfaceClass()) return;
@@ -168,12 +167,12 @@ class CCtorsVisitor final : public VNVisitor {
if (v3Global.opt.coverage()) {
V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE};
for (AstNode *np = nodep->stmtsp(), *nextp; np; np = nextp) {
nextp = np->nextp();
for (AstNode* np = nodep->stmtsp(); np; np = np->nextp()) {
if (AstNodeCoverDecl* const coverp = VN_CAST(np, NodeCoverDecl)) {
// ... else we don't have a static VlSym to be able to coverage insert
UASSERT_OBJ(!VN_IS(nodep, Class), coverp,
"NodeCoverDecl should be in class's package, not class itself");
np = coverp->backp();
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); }
public:
-2
View File
@@ -86,7 +86,6 @@ class CUseVisitor final : public VNVisitorConst {
}
void visit(AstCell* nodep) override {
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
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name());
iterateChildrenConst(nodep);
@@ -116,7 +115,6 @@ void V3CUse::cUseAll() {
// Call visitor separately for each module, so visitor state is cleared
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp;
modp = VN_AS(modp->nextp(), NodeModule)) {
if (modp->isConstPool()) continue; // Special emit rules
// Insert under this module; someday we should e.g. make Ast
// for each output file and put under that
CUseVisitor{modp};
+16 -24
View File
@@ -38,7 +38,6 @@
#include "V3Case.h"
#include "V3ConstPool.h"
#include "V3Stats.h"
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
AstNodeExpr* const tableRefp = [&]() -> AstNodeExpr* {
if (isTinyTable) return tablep;
AstVarRef* const refp = V3ConstPool::findConst(tablep);
// Create the table in the constant pool, unless using an inline table
AstVarScope* const tableVscp = [&]() -> AstVarScope* {
if (isTinyTable) return nullptr;
AstVarScope* vscp = v3Global.rootp()->constPoolp()->findConst(tablep, true);
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
= new AstExtend{flp, nodep->exprp()->cloneTreePure(false), 32};
AstNodeExpr* const scalep = new AstConst{flp, entryWidth};
@@ -853,14 +855,15 @@ class CaseVisitor final : public VNVisitor {
}
// Create the tables
AstVarRef* const matchRefp = V3ConstPool::findConst(matchp);
AstVarRef* const tableRefp = V3ConstPool::findTable(tablep);
AstVarScope* const matchVscp = v3Global.rootp()->constPoolp()->findConst(matchp, true);
AstVarScope* const tableVscp = v3Global.rootp()->constPoolp()->findTable(tablep);
VL_DO_DANGLING(matchp->deleteTree(), matchp);
VL_DO_DANGLING(tablep->deleteTree(), tablep);
// AstMatchMasked produces the index of the matching entry
AstNodeExpr* const tableRefp = new AstVarRef{flp, tableVscp, VAccess::READ};
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};
return connectDecoderOutputs(nodep, entryp, "__VcaseDecoderOut");
@@ -1001,22 +1004,11 @@ class CaseVisitor final : public VNVisitor {
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.
if (!hasDefault) {
if (m_caseDetailsValid && m_caseDetails.exhaustive
&& !m_caseDetails.exhaustiveOverEnumOnly) {
// 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});
}
nodep->addItemsp(new AstCaseItem{
nodep->fileline(), new AstConst{nodep->fileline(), AstConst::BitTrue{}}, nullptr});
}
// Now build the IF statement tree
+12 -21
View File
@@ -18,8 +18,7 @@
// Each module:
// For each expression, if it requires a clean operand,
// and the operand is dirty, insert a CLEAN node.
// Resize operands (but not variables, variable selects, or formatting metadata)
// to C++ 32/64/wide types.
// Resize operands (but not variables or variable selects) to C++ 32/64/wide types.
// 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, NodeDType) // Don't want to change 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(), WildcardArrayDType)
|| 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(), StreamDType)
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), CoverCrossDType)
|| VN_IS(nodep->dtypep()->skipRefp(), CoverpointDType)
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
} else {
const AstNodeUOrStructDType* const dtypep
@@ -142,12 +138,12 @@ class CleanVisitor final : public VNVisitor {
computeCppWidth(nodep);
if (!isClean(nodep)) insertClean(nodep);
}
void ensureCleanAndNext(AstNode* nodep) {
void ensureCleanAndNext(AstNodeExpr* nodep) {
// Editing list, careful looping!
for (AstNode* argp = nodep; argp;) {
AstNode* const nextp = argp->nextp();
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
argp = nextp;
for (AstNodeExpr* exprp = nodep; exprp;) {
AstNodeExpr* const nextp = VN_AS(exprp->nextp(), NodeExpr);
ensureClean(exprp);
exprp = nextp;
}
}
@@ -251,7 +247,9 @@ class CleanVisitor final : public VNVisitor {
setClean(nodep, false);
// We always clean, as we don't trust those pesky users.
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(AstTraceInc* nodep) override {
@@ -282,23 +280,16 @@ class CleanVisitor final : public VNVisitor {
iterateChildren(nodep);
ensureClean(nodep->condp());
}
void visit(AstSFormatArg* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->exprp());
setClean(nodep, true);
}
void visit(AstSFormatF* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->exprsp());
setClean(nodep, true); // generates a string, so not relevant
}
void visit(AstCStmt* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->nodesp());
}
void visit(AstCStmtUser* nodep) override {
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 {
iterateChildren(nodep);
+1 -1
View File
@@ -97,7 +97,7 @@ class ClockVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void visit(AstSenTree* nodep) override {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); // No longer needed
pushDeletep(nodep->unlinkFrBack()); // No longer needed
}
//========== Move sampled assignments
+2 -2
View File
@@ -44,8 +44,8 @@ class CombineVisitor final : VNVisitor {
// TYPES
using funcit_t = std::list<AstCFunc*>::iterator;
struct CFuncs final {
std::list<AstCFunc*> m_fast; // Functions that are not slow
std::list<AstCFunc*> m_slow; // Functions marked for slow files
std::list<AstCFunc*> m_fast;
std::list<AstCFunc*> m_slow;
};
// 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); }
public:
+18 -26
View File
@@ -110,12 +110,12 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
class LeafInfo final { // Leaf node (either AstConst or AstVarRef)
// 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_msb = 0; // MSB of actually used bit of m_refp->varp()
int m_wordIdx = -1; // -1 means AstWordSel is not used.
AstVarRef* m_refp = nullptr; // Leaf's variable reference
const AstConst* m_constp = nullptr; // Leaf's constant
AstVarRef* m_refp = nullptr;
const AstConst* m_constp = nullptr;
public:
// CONSTRUCTORS
@@ -187,9 +187,9 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
};
struct BitPolarityEntry final { // Found bit polarity during iterate()
LeafInfo m_info; // Leaf (variable or constant) bit polarity was found on
bool m_polarity = false; // Polarity the bit must have to match
int m_bit = 0; // Bit index within the leaf that was tested
LeafInfo m_info;
bool m_polarity = false;
int m_bit = 0;
BitPolarityEntry(const LeafInfo& info, bool pol, int bit)
: m_info{info}
, m_polarity{pol}
@@ -198,8 +198,8 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
};
struct FrozenNodeInfo final { // Context when a frozen node is found
bool m_polarity; // Polarity the frozen node must match
int m_lsb; // LSB position of the frozen node
bool m_polarity;
int m_lsb;
bool operator<(const FrozenNodeInfo& other) const {
if (m_lsb != other.m_lsb) return m_lsb < other.m_lsb;
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
ConstBitOpTreeVisitor& m_visitor; // Visitor whose state is saved and restored
const size_t m_polaritiesSize; // Saved m_visitor.m_bitPolarities size to truncate back to
const size_t m_frozenSize; // Saved m_visitor.m_frozenNodes size to truncate back to
const unsigned m_ops; // Saved m_visitor.m_ops to restore
const bool m_polarity; // Saved m_visitor.m_polarity to restore
bool m_restore = true; // Whether the destructor still needs to restore state
ConstBitOpTreeVisitor& m_visitor;
const size_t m_polaritiesSize;
const size_t m_frozenSize;
const unsigned m_ops;
const bool m_polarity;
bool m_restore = true;
public:
explicit Restorer(ConstBitOpTreeVisitor& visitor)
@@ -1463,12 +1463,11 @@ class ConstVisitor final : public VNVisitor {
}
if (const AstShiftR* const shiftp = VN_CAST(rhsp, ShiftR)) {
// 'a >> S' forces the high S bits to zero. Check against the width of the shifted
// operand, V3Expand can create shifts wider than their inputs. Must use width(),
// not widthMin(), as bits above widthMin() are not guaranteed to be zero.
// operand, V3Expand can create shifts wider than their inputs
if (AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) {
return scp->num().fitsInUInt()
&& (lsb + scp->num().toUInt()
>= static_cast<uint32_t>(shiftp->lhsp()->width()));
>= static_cast<uint32_t>(shiftp->lhsp()->widthMin()));
}
}
if (const AstMul* const mulp = VN_CAST(rhsp, Mul)) {
@@ -3970,12 +3969,7 @@ class ConstVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return true;
}
void visit(AstSFormatArg* nodep) override {
// 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(AstSFormatArg* nodep) override { iterateChildren(nodep); }
void visit(AstSFormatF* nodep) override {
// Substitute constants into displays. The main point of this is to
// simplify assertion methodologies which call functions with display's.
@@ -4039,9 +4033,7 @@ class ConstVisitor final : public VNVisitor {
: VFormatAttr{};
if (VN_IS(subargp, Const)) { // Convert it
const string out
= formatAttr.isEnum()
? constNumV(subargp).displayedEnum(fargp, fmt)
: constNumV(subargp).displayed(nodep, fmt, formatAttr);
= constNumV(subargp).displayed(nodep, fmt, formatAttr);
UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
<< subargp);
// fmt = out w/ replace % with %% as it must later when
-220
View File
@@ -1,220 +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->isStatic(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);
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() {
m_keys.clear();
m_keySet.clear();
@@ -57,7 +48,6 @@ public:
// ACCESSORS
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(); }
// ITERATORS
+36 -36
View File
@@ -491,41 +491,41 @@ public:
IgnIndices results;
int nextChange = 0;
tree.find(0, results, nextChange);
UASSERT_SELFTEST(results.size(), 0);
UASSERT_SELFTEST(nextChange, 10);
UASSERT_SELFTEST(const size_t, results.size(), 0);
UASSERT_SELFTEST(const int, nextChange, 10);
tree.find(10, results, nextChange);
UASSERT_SELFTEST(results.size(), 2);
UASSERT_SELFTEST(results[0], 0);
UASSERT_SELFTEST(results[1], 3);
UASSERT_SELFTEST(nextChange, 11);
UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 0);
UASSERT_SELFTEST(const int, results[1], 3);
UASSERT_SELFTEST(const int, nextChange, 11);
tree.find(11, results, nextChange);
UASSERT_SELFTEST(results.size(), 1);
UASSERT_SELFTEST(results[0], 3);
UASSERT_SELFTEST(nextChange, 15); // Center, or would be 20
UASSERT_SELFTEST(const size_t, results.size(), 1);
UASSERT_SELFTEST(const int, results[0], 3);
UASSERT_SELFTEST(const int, nextChange, 15); // Center, or would be 20
tree.find(20, results, nextChange);
UASSERT_SELFTEST(results.size(), 3);
UASSERT_SELFTEST(results[0], 1);
UASSERT_SELFTEST(results[1], 3);
UASSERT_SELFTEST(results[2], 4);
UASSERT_SELFTEST(nextChange, 21);
UASSERT_SELFTEST(const size_t, results.size(), 3);
UASSERT_SELFTEST(const int, results[0], 1);
UASSERT_SELFTEST(const int, results[1], 3);
UASSERT_SELFTEST(const int, results[2], 4);
UASSERT_SELFTEST(const int, nextChange, 21);
tree.find(21, results, nextChange);
UASSERT_SELFTEST(results.size(), 2);
UASSERT_SELFTEST(results[0], 3);
UASSERT_SELFTEST(results[1], 4);
UASSERT_SELFTEST(nextChange, 25); // Center, or would be 30
UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 3);
UASSERT_SELFTEST(const int, results[1], 4);
UASSERT_SELFTEST(const int, nextChange, 25); // Center, or would be 30
tree.find(30, results, nextChange);
UASSERT_SELFTEST(results.size(), 2);
UASSERT_SELFTEST(results[0], 3);
UASSERT_SELFTEST(results[1], 4);
UASSERT_SELFTEST(nextChange, 31);
UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 3);
UASSERT_SELFTEST(const int, results[1], 4);
UASSERT_SELFTEST(const int, nextChange, 31);
tree.find(40, results, nextChange);
UASSERT_SELFTEST(results.size(), 2);
UASSERT_SELFTEST(results[0], 2);
UASSERT_SELFTEST(results[1], 4);
UASSERT_SELFTEST(nextChange, 41);
UASSERT_SELFTEST(const size_t, results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 2);
UASSERT_SELFTEST(const int, results[1], 4);
UASSERT_SELFTEST(const int, nextChange, 41);
tree.find(41, results, nextChange);
UASSERT_SELFTEST(results.size(), 0);
UASSERT_SELFTEST(nextChange, std::numeric_limits<int>::max());
UASSERT_SELFTEST(const size_t, results.size(), 0);
UASSERT_SELFTEST(const int, nextChange, std::numeric_limits<int>::max());
//
points = {{0, 0}};
for (const auto& it : points) {
@@ -536,8 +536,8 @@ public:
tree.build(data);
//
tree.find(50, results, nextChange);
UASSERT_SELFTEST(results.size(), 1);
UASSERT_SELFTEST(results[0], 5);
UASSERT_SELFTEST(const size_t, results.size(), 1);
UASSERT_SELFTEST(const int, results[0], 5);
}
};
@@ -709,8 +709,8 @@ public:
// Tracks what matches are known to hit against V3ControlScopeTraceEntries
class V3ControlScopeTraceEntryMatch final {
public:
const V3ControlScopeTraceEntry* m_entryp; // Entry the match refers to
const string m_scopepart; // Scope prefix being matched against the entry
const V3ControlScopeTraceEntry* m_entryp;
const string m_scopepart;
V3ControlScopeTraceEntryMatch(const V3ControlScopeTraceEntry* entryp, const string& scopepart)
: m_entryp{entryp}
, m_scopepart{scopepart} {}
@@ -780,8 +780,8 @@ public:
// Resolve modules and files in the design
class V3ControlResolverHierWorkerEntry final {
const int m_workers; // Number of workers requested for the hierarchical block
FileLine* const m_flp; // Location the worker count was specified at
const int m_workers;
FileLine* const m_flp;
public:
explicit V3ControlResolverHierWorkerEntry(int workers, FileLine* flp)
@@ -798,9 +798,9 @@ class V3ControlResolver final {
V3ControlScopeTraceResolver m_scopeTraces; // Regexp to trace enables
std::unordered_map<string, std::unordered_map<string, uint64_t>>
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;
FileLine* m_profileFileLine = nullptr; // Location profile_data was read from
FileLine* m_profileFileLine = nullptr;
V3ControlResolver() = default;
~V3ControlResolver() = default;
+2 -13
View File
@@ -199,7 +199,6 @@ class CoverageVisitor final : public VNVisitor {
AstVar* const varp = new AstVar{fl_nowarn, VVarType::MODULETEMP, trace_var_name,
incp->findUInt32DType()};
varp->setIgnoreSchedWrite(); // Ignore the increment output, so no UNOPTFLAT
varp->lifetime(VLifetime::STATIC_IMPLICIT);
varp->trace(true);
m_modp->addStmtsp(varp);
UINFO(5, "New coverage trace: " << varp);
@@ -284,7 +283,7 @@ class CoverageVisitor final : public VNVisitor {
VL_RESTORER_COPY(m_funcTemps);
createHandle(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) {
// No blocks cross (non-nested) modules, so save some memory
m_varnames.clear();
@@ -1054,7 +1053,7 @@ class CoverageVisitor final : public VNVisitor {
unrolledp = new T_Oper{fl, selp, unrolledp};
}
iterate(unrolledp);
VL_DO_DANGLING(pushDeletep(unrolledp), unrolledp);
pushDeletep(unrolledp);
} else {
iterateChildren(nodep);
lineTrack(nodep);
@@ -1115,16 +1114,6 @@ class CoverageVisitor final : public VNVisitor {
}
// 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 {
iterateChildren(nodep);
lineTrack(nodep);
+578 -2911
View File
File diff suppressed because it is too large Load Diff
+72 -322
View File
@@ -37,8 +37,6 @@
#include "V3Dead.h"
#include "V3ConstPool.h"
#include "V3Graph.h"
#include "V3Stats.h"
#include <queue>
@@ -46,199 +44,6 @@
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
@@ -247,11 +52,10 @@ class DeadVisitor final : public VNVisitor {
// Entire Netlist:
// AstNodeModule::user1() -> int. Count of number of cells referencing this module.
// AstVar::user1() -> int. Count of number of references
// AstVar::user2() -> bool. Is a formal function argument
// AstVarScope::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 VNUser2InUse m_inuser2; // For usage information see DeadGraph
// TYPES
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_elimDTypes; // Allow removal of DTypes
const bool m_elimCells; // Allow removal of Cells
DeadGraph m_graph; // Tracking graph
// List of all encountered to avoid another loop through tree
std::vector<AstVar*> m_varsp;
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<AstClass*> m_classesp;
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
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
AstNodeModule* m_modp = nullptr; // Current module
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
VDouble0 m_statFTasksDemoted;
VDouble0 m_statFTasksMDeadified;
VDouble0 m_statFTasksNMDeadified;
VDouble0 m_statFTasksVirtDeadified;
VDouble0 m_statFTasksDeadified;
// METHODS
void deleting(AstNode* nodep) {
UINFO(9, " deleting " << nodep);
m_graph.deleteNodeVertex(nodep);
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
@@ -322,22 +118,12 @@ class DeadVisitor final : public VNVisitor {
if (AstNode* const subnodep = nodep->virtRefDTypep()) 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
void visit(AstNodeModule* nodep) override {
if (m_modp) m_modp->user1Inc(); // e.g. Class under Package
VL_RESTORER(m_modp);
m_modp = nodep;
VL_RESTORER(m_containingFTaskRefp);
m_containingFTaskRefp = nodep;
if (nodep->dead()) return;
if (nodep->modPublic()) m_modp->user1Inc();
iterateChildren(nodep);
@@ -354,16 +140,6 @@ class DeadVisitor final : public VNVisitor {
iterateChildren(nodep);
checkAll(nodep);
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 {
iterateChildren(nodep);
@@ -372,8 +148,6 @@ class DeadVisitor final : public VNVisitor {
// Class packages might have no children, but need to remain as
// long as the class they refer to is needed
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()) {
m_scopesp.push_back(nodep);
}
@@ -401,7 +175,7 @@ class DeadVisitor final : public VNVisitor {
iterateChildren(nodep);
if (!m_sideEffect && !nodep->isPure()) m_sideEffect = true;
checkAll(nodep);
needsTask(nodep->taskp(), m_containingFTaskRefp);
if (nodep->taskp()) nodep->taskp()->user1Inc();
if (nodep->classOrPackagep()) {
if (m_elimCells) {
nodep->classOrPackagep(nullptr);
@@ -413,12 +187,7 @@ class DeadVisitor final : public VNVisitor {
void visit(AstModportFTaskRef* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
needsTask(nodep->ftaskp(), m_containingFTaskRefp);
}
void visit(AstModportVarRef* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->varp()) nodep->varp()->user1Inc();
if (nodep->ftaskp()) nodep->ftaskp()->user1Inc();
}
void visit(AstRefDType* nodep) override {
iterateChildren(nodep);
@@ -525,11 +294,6 @@ class DeadVisitor final : public VNVisitor {
iterateChildren(nodep);
checkAll(nodep);
if (m_foreachHeaderp) nodep->user1Inc();
// Keep formal arguments
if (m_inFTask && nodep->isIO()) {
nodep->user1Inc();
nodep->user2(1);
}
if (mightElimVar(nodep)) {
m_varsp.push_back(nodep);
} else {
@@ -563,21 +327,13 @@ class DeadVisitor final : public VNVisitor {
if (assignInAssign) m_sideEffect = true; // Parent assign shouldn't optimize
}
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);
checkAll(nodep);
if (nodep->isVirtual()) m_graph.funcVirtual(nodep);
if (!removable) {
if (nodep->taskPublic() || nodep->dpiExport() || nodep->dpiImport()) {
if (m_modp && !m_modp->dead() && !m_modp->verilatorLib())
m_modp->user1Inc(); // Keep container
} else {
m_tasksp.push(nodep);
}
if (nodep->classOrPackagep()) {
if (m_elimCells) {
@@ -618,41 +374,19 @@ class DeadVisitor final : public VNVisitor {
}
void deadCheckTasks() {
while (!m_graph.funcsEmpty()) {
AstNode* const nodep = m_graph.funcsGetPopFront();
if (!nodep) continue;
UINFO(9, "Dead " << nodep);
if (AstNodeFTask* const taskp = VN_CAST(nodep, NodeFTask)) {
if (taskp->isVirtual()) {
++m_statFTasksVirtDeadified;
} else if (taskp->classMethod()) {
++m_statFTasksMDeadified;
} else {
++m_statFTasksNMDeadified;
}
}
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;
}
while (!m_tasksp.empty()) {
AstNodeFTask* taskp = m_tasksp.front();
m_tasksp.pop();
if (taskp->user1() == 0 && !taskp->classMethod()) {
taskp->foreach([this](AstNodeFTaskRef* ftaskrefp) {
AstNodeFTask* task2p = ftaskrefp->taskp();
if (!task2p) return;
task2p->user1Inc(-1);
if (task2p->user1() == 0) m_tasksp.push(task2p);
});
taskp->user1(-1); // we don't want to try deleting twice
deleting(taskp);
++m_statFTasksDeadified;
}
}
}
@@ -666,11 +400,7 @@ class DeadVisitor final : public VNVisitor {
AstNodeModule* nextmodp;
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp = nextmodp) {
nextmodp = VN_AS(modp->nextp(), NodeModule);
// Keep $unit until m_elimCells stages. Note v3Global.opt.serializeOnly()
// 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)) {
if (modp->dead() || (!modp->isTop() && modp->user1() == 0 && !modp->internal())) {
// > 2 because L1 is the wrapper, L2 is the top user module
UINFO(4, " Dead module " << modp);
// And its children may now be killable too; correct counts
@@ -680,7 +410,6 @@ class DeadVisitor final : public VNVisitor {
cellp->modp()->user1Inc(-1);
});
}
if (modp->isDollarUnit()) v3Global.rootp()->dollarUnitPkgp(nullptr);
deleting(modp);
retry = true;
}
@@ -689,7 +418,7 @@ class DeadVisitor final : public VNVisitor {
}
bool mightElimVar(const AstVar* nodep) const {
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;
return m_elimUserVars; // Post-Trace can kill most anything
}
@@ -741,8 +470,6 @@ class DeadVisitor final : public VNVisitor {
void deadCheckVar() {
// Delete any unused varscopes
for (AstVarScope* vscp : m_vscsp) {
// Keep formal arguments
if (vscp->varp()->user2()) continue;
if (vscp->user1() == 0) {
UINFO(4, " Dead " << vscp);
const std::pair<AssignMap::iterator, AssignMap::iterator> eqrange
@@ -795,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:
// CONSTRUCTORS
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes,
bool elimCells, bool elimTasks)
bool elimCells, bool elimTopIfaces, bool elimTasks)
: m_elimUserVars{elimUserVars}
, m_elimDTypes{elimDTypes}
, m_elimCells{elimCells}
, m_containingFTaskRefp{nodep} {
, m_elimCells{elimCells} {
// Prepare to remove some datatypes
nodep->typeTablep()->clearCache();
// Operate on whole netlist
iterate(nodep);
@@ -819,11 +582,6 @@ public:
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();
deadCheckTypedefs();
deadCheckVar();
@@ -833,24 +591,16 @@ public:
if (elimCells) deadCheckCells();
deadCheckClasses();
// Modules after vars, because might be vars we delete inside a mod we delete
if (!elimTopIfaces) preserveTopIfaces(nodep);
deadCheckMod();
// After deleting as much as can, demote some virtual functions
if (elimTasks) deadCheckDemote();
// We may have removed some datatypes, cleanup
nodep->typeTablep()->repairCache();
VIsCached::clearCacheTree(); // Removing assignments may affect isPure
V3ConstPool::invalidateCache(); // Might have deleted constant pool entries
nodep->constPoolp()->rebuildVarScopesAndCache();
}
~DeadVisitor() override {
V3Stats::addStatSum("Optimizations, FTasks, virtual-to-nonvirtual demotion",
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);
V3Stats::addStatSum("Optimizations, deadified FTasks", m_statFTasksDeadified);
};
};
@@ -859,32 +609,32 @@ public:
void V3Dead::deadifyModules(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":");
{ // node, elimUserVars, elimDTypes, elimScopes, elimCells, elimTasks
DeadVisitor{nodep, false, false, false, false, false};
{ // node, elimUserVars, elimDTypes, elimScopes, elimCells, elimTopIfaces
DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported(), false};
} // Destruct before checking
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeEitherLevel() >= 6);
}
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
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);
}
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
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);
}
void V3Dead::deadifyAll(AstNetlist* nodep) {
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);
}
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
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);
}
+23 -141
View File
@@ -118,7 +118,6 @@
#include "V3Delayed.h"
#include "V3AstUserAllocator.h"
#include "V3ClassGraph.h"
#include "V3Const.h"
#include "V3Stats.h"
@@ -251,7 +250,6 @@ class DelayedVisitor final : public VNVisitor {
// AstVar::user1() -> bool. Set true if already issued MULTIDRIVEN warning
// AstVarRef::user1() -> bool. Set true if target of NBA
// AstAssignDly::user1() -> bool. Set true if already visited
// AstCFunc::user1() -> AstUser1Allocator. See `m_cfuncsCache` below
// AstAssignDly::user2p() -> AstVarScope*: Scope this AstAssignDelay is under
// AstNodeModule::user1p() -> std::unorded_map<std::string, AstVar*> temp map via m_varMap
// AstScope::user1() -> int: Temporary counter for this scope
@@ -261,24 +259,6 @@ class DelayedVisitor final : public VNVisitor {
const VNUser1InUse m_user1InUse;
const VNUser2InUse m_user2InUse;
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<AstVarScope, VarScopeInfo> m_vscpInfo;
AstUser3Allocator<AstVarScope, std::vector<WriteReference>> m_writeRefs;
@@ -286,10 +266,6 @@ class DelayedVisitor final : public VNVisitor {
// STATE - across all visitors
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)
AstActive* m_activep = nullptr; // Current activate
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_inNonCombLogic = false; // We are in non-combinational logic
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
// 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_nSharedSetFlags; // "Set" flags actually shared by Scheme::FlagShared variables
VDouble0 m_nInitialNBA; // Number of procedural blocks with initial NBA
VDouble0 m_nonInlinedCAwaitsWithSenTree; // Count uses of not inlined co_awaits
// METHODS
@@ -589,17 +563,6 @@ class DelayedVisitor final : public VNVisitor {
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'
// type, with the bits selected by 'sLsbp'/'sWidthp' set to 'valuep', other bits set to zero.
// Insert new statements before 'insertp'.
@@ -821,7 +784,7 @@ class DelayedVisitor final : public VNVisitor {
m_prevVscp = vscp;
// Delete original NBA
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
pushDeletep(nodep->unlinkFrBack());
}
// Scheme::FlagUnique
@@ -871,7 +834,7 @@ class DelayedVisitor final : public VNVisitor {
ifp->addThensp(new AstAssign{flp, capturedLhsp, capturedRhsp});
// Delete original NBA
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
pushDeletep(nodep->unlinkFrBack());
}
// Scheme::ValueQueuePartial/Scheme::ValueQueueWhole
@@ -902,9 +865,8 @@ class DelayedVisitor final : public VNVisitor {
activep->addStmtsp(postp);
// Add the commit
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();
// TODO: this is a partial update, so must be READWRITE, but that breaks scheduling
callp->addPinsp(new AstVarRef{flp, vscp, VAccess::WRITE});
postp->addStmtsp(callp->makeStmt());
}
@@ -1013,7 +975,7 @@ class DelayedVisitor final : public VNVisitor {
// Enqueue the update at the site of the original NBA
AstCMethodHard* const callp = new AstCMethodHard{
flp, new AstVarRef{flp, vscpInfo.valueQueueKit().vscp, VAccess::READWRITE},
VCMethod::NBA_ENQUEUE};
VCMethod::SCHED_ENQUEUE};
callp->dtypeSetVoid();
callp->addPinsp(valuep);
if (partial) callp->addPinsp(maskp);
@@ -1021,7 +983,7 @@ class DelayedVisitor final : public VNVisitor {
nodep->addHereThisAsNext(callp->makeStmt());
// Delete original NBA
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
pushDeletep(nodep->unlinkFrBack());
}
// Record where a variable is assigned
@@ -1045,43 +1007,6 @@ class DelayedVisitor final : public VNVisitor {
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
void visit(AstNetlist* nodep) override {
iterateChildren(nodep);
@@ -1178,6 +1103,11 @@ class DelayedVisitor final : public VNVisitor {
m_scopep = nodep;
iterateChildren(nodep);
}
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
m_cfuncp = nodep;
iterateChildren(nodep);
}
void visit(AstActive* nodep) override {
UASSERT_OBJ(!m_activep, nodep, "Should not nest");
VL_RESTORER(m_activep);
@@ -1191,7 +1121,6 @@ class DelayedVisitor final : public VNVisitor {
}
void visit(AstNodeProcedure* nodep) override {
VL_RESTORER(m_needsInitialTrigger);
VL_RESTORER_CLEAR(m_nbaEventSenTreeps);
const size_t firstNBAAddedIndex = m_nbas.size();
{
VL_RESTORER(m_inSuspendableOrFork);
@@ -1243,8 +1172,6 @@ class DelayedVisitor final : public VNVisitor {
for (size_t i = firstNBAAddedIndex; i < m_nbas.size(); ++i) {
m_vscpInfo(m_nbas[i].vscp).addSensitivity(senItemp);
}
for (AstSenTree* const senTreep : m_nbaEventSenTreeps)
senTreep->addSensesp(senItemp->cloneTree(true));
// Done with these
VL_DO_DANGLING(senItemp->deleteTree(), senItemp);
}
@@ -1255,7 +1182,6 @@ class DelayedVisitor final : public VNVisitor {
}
void visit(AstCAwait* nodep) override {
if (nodep->sentreep()) m_timingDomains.insert(nodep->sentreep());
iterateChildren(nodep);
}
void visit(AstFireEvent* nodep) override {
UASSERT_OBJ(v3Global.hasEvents(), nodep, "Inconsistent");
@@ -1270,15 +1196,8 @@ class DelayedVisitor final : public VNVisitor {
cstmtp->add(");");
AstNode* newp = cstmtp;
const AstVarRef* const vrefp = VN_CAST(eventp, VarRef);
if (nodep->isDelayed() && (m_cfuncp || !vrefp)) {
// 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()) {
if (nodep->isDelayed()) {
const AstVarRef* const vrefp = VN_AS(eventp, VarRef);
const std::string newvarname = "__Vdly__" + vrefp->varp()->shortName();
AstVarScope* const dlyvscp
= createTemp(flp, vrefp->varScopep()->scopep(), newvarname, 1);
@@ -1287,35 +1206,20 @@ class DelayedVisitor final : public VNVisitor {
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};
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");
AstSenTree* senTreep = 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);
AstActive* const activep = new AstActive{flp, "nba-event", m_activep->sentreep()};
m_activep->addNextHere(activep);
activep->addStmtsp(prep);
activep->addStmtsp(postp);
newp = new AstAssign{flp, dlyRef(VAccess::WRITE),
@@ -1433,24 +1337,6 @@ class DelayedVisitor final : public VNVisitor {
m_inLoop = true;
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.
void visit(AstAlwaysPre*) override {}
@@ -1461,10 +1347,7 @@ class DelayedVisitor final : public VNVisitor {
public:
// CONSTRUCTORS
explicit DelayedVisitor(AstNetlist* nodep)
: m_classGraphp{V3ClassGraph::build(nodep)} {
iterate(nodep);
}
explicit DelayedVisitor(AstNetlist* nodep) { iterate(nodep); }
~DelayedVisitor() override {
V3Stats::addStat("NBA, variables using ShadowVar scheme", m_nSchemeShadowVar);
V3Stats::addStat("NBA, variables using ShadowVarMasked scheme", m_nSchemeShadowVarMasked);
@@ -1475,7 +1358,6 @@ public:
m_nSchemeValueQueuesPartial);
V3Stats::addStat("Optimizations, NBA flags shared", m_nSharedSetFlags);
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)
void needNonStaticFunc(AstNode* nodep) {
UASSERT_OBJ(m_cfuncp, nodep, "Non-static accessor not under a function");
// DPI export implementations are always invoked via the generic DPI dispatcher,
// 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()) {
if (m_cfuncp->isStatic()) {
UINFO(5, "Mark non-public due to " << nodep);
m_cfuncp->isStatic(false);
}
+3 -4
View File
@@ -26,7 +26,6 @@
#include "V3Descope.h"
#include "V3ConstPool.h"
#include "V3EmitCBase.h"
VL_DEFINE_DEBUG_FUNCTIONS;
@@ -160,7 +159,8 @@ class DescopeVisitor final : public VNVisitor {
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
if (portp->isIO() && !portp->isFuncReturn()) {
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);
}
}
@@ -236,7 +236,7 @@ class DescopeVisitor final : public VNVisitor {
if (varp->isFuncLocal()) {
// Reference to function locals need no self pointer
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
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
} else {
@@ -295,6 +295,5 @@ public:
void V3Descope::descopeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":");
{ DescopeVisitor{nodep}; } // Destruct before checking
V3ConstPool::setDescoped();
V3Global::dumpCheckGlobalTree("descope", 0, dumpTreeEitherLevel() >= 3);
}
+25 -76
View File
@@ -21,7 +21,6 @@
#include "V3Ast.h"
#include "V3EmitV.h"
#include "V3File.h"
#include "V3Stats.h"
VL_DEFINE_DEBUG_FUNCTIONS;
@@ -32,8 +31,6 @@ DfgGraph::DfgGraph(const string& name)
: m_name{name} {}
DfgGraph::~DfgGraph() {
V3Stats::addStatSum("Optimizations, DFG, temporary declarations reused",
static_cast<double>(m_tempDeclarationsReused));
forEachVertex([&](DfgVertex& vtx) { vtx.unlinkDelete(*this); });
}
@@ -101,31 +98,26 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
}
}
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& prefix,
const DfgDataType& dtype, AstScope* scopep) {
// AstVar declarations outlive all DFG graphs. Splitting or merging graphs
// does not transfer slots: each graph creates globally unique declarations.
TempDeclarations& temps = m_temporaries[scopep->modp()][{prefix, dtype.astDtypep()}];
const size_t slot = temps.m_scopeCounts[scopep]++;
AstVar* varp;
if (slot == temps.m_declps.size()) {
// Construct the name stub on the first new declaration in this graph
if (m_tmpNameStub.empty()) {
// Use the hash of the graph name (avoid long names and non-identifiers)
const std::string hash = V3Hash{m_name}.toString();
// Graph hashes may collide, so track multiplicity to keep names globally unique
static std::unordered_map<std::string, uint32_t> s_multiplicity;
m_tmpNameStub += '_' + hash + '_' + std::to_string(s_multiplicity[hash]++) + '_';
}
const std::string varName
= "__Vdfg" + prefix + m_tmpNameStub + std::to_string(m_tmpNameCount++);
varp = new AstVar{flp, VVarType::MODULETEMP, varName, dtype.astDtypep()};
scopep->modp()->addStmtsp(varp);
temps.m_declps.emplace_back(varp);
} else {
varp = temps.m_declps[slot];
++m_tempDeclarationsReused;
std::string DfgGraph::makeUniqueName(const std::string& prefix, size_t n) {
// Construct the tmpNameStub if we have not done so yet
if (m_tmpNameStub.empty()) {
// Use the hash of the graph name (avoid long names and non-identifiers)
const std::string hash = V3Hash{m_name}.toString();
// We need to keep every variable globally unique, and graph hashed
// names might not be, so keep a static table to track multiplicity
static std::unordered_map<std::string, uint32_t> s_multiplicity;
m_tmpNameStub += '_' + hash + '_' + std::to_string(s_multiplicity[hash]++) + '_';
}
// Assemble the globally unique name
return "__Vdfg" + prefix + m_tmpNameStub + std::to_string(n);
}
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name,
const DfgDataType& dtype, AstScope* scopep) {
// Create AstVar
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtype.astDtypep()};
// Add AstVar to the scope's module
scopep->modp()->addStmtsp(varp);
// Create AstVarScope
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
// Add to scope
@@ -481,11 +473,8 @@ DfgGraph::neighborhood(const std::vector<const DfgVertex*>& vtxps, size_t n) con
//------------------------------------------------------------------------------
// DfgVertex
DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dt,
DfgEdge* inlineInputsp, uint32_t nInputs)
: m_inlineInputsp{inlineInputsp}
, m_nInputs{nInputs}
, m_filelinep{flp}
DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dt)
: m_filelinep{flp}
, m_dtype{dt}
, m_type{type} {
dfg.addVertex(*this);
@@ -802,18 +791,19 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED
}
AstScope* const rootp = v3Global.rootp()->topScopep()->scopep();
AstScope* const constPoolp = v3Global.rootp()->constPoolp()->scopep();
// Note: the recursive invocation can cause a re-hash but that will not invalidate references
AstScope*& resultr = cache[this];
if (!resultr) {
// Mark to prevent infinite recursion on circular graphs - should never be called on such
resultr = reinterpret_cast<AstScope*>(1);
// Find scope based on sources, falling back on the root scope. Never use a package
// scope, including the constant pool, so new variables are not created in packages.
// Find scope based on sources, falling back on the root scope,
// also make sure it's not the constant pool scope, which is special.
AstScope* foundp = nullptr;
foreachSource([&](DfgVertex& src) {
AstScope* const scp = src.scopep(cache, true);
if (scp != rootp && !VN_IS(scp->modp(), Package)) {
if (scp != rootp && scp != constPoolp) {
foundp = scp;
return true;
}
@@ -839,47 +829,6 @@ void DfgVertex::unlinkDelete(DfgGraph& dfg) {
delete this;
}
//------------------------------------------------------------------------------
// DfgVertexVar
std::pair<DfgVertex*, uint32_t> DfgVertexVar::driverOfRange(uint32_t lo, uint32_t size) {
DfgVertex* const srcp = this->srcp();
// Not driven at all
if (!srcp) return {nullptr, 0};
// If volatile, can have other drivers
if (isVolatile()) return {nullptr, 0};
// Don't inline CReset
if (srcp->is<DfgCReset>()) return {nullptr, 0};
// If not driven via a splice, then it is driven whole, at the same offsets
DfgVertexSplice* const splicep = srcp->cast<DfgVertexSplice>();
if (!splicep) return {srcp, lo};
// Find the driver that covers the whole searched range, if there is a single one
const uint32_t hi = lo + size - 1;
DfgVertex* driverp = nullptr;
uint32_t driverLo = 0;
bool useDefault = defaultp();
splicep->foreachDriver([&](DfgVertex& src, const uint32_t dLo) {
const uint32_t dHi = dLo + src.size() - 1;
// Note whether it overlaps the searched range, so the default cannot be used
if (dLo <= hi && lo <= dHi) useDefault = false;
// If it does not cover the whole searched range, move on
if (lo < dLo || dHi < hi) return false;
// Save the driver that covers the whole searched range
driverp = &src;
driverLo = dLo;
return true;
});
// If a single driver covers the searched range, it is the one
if (driverp) return {driverp, lo - driverLo};
// Otherwise the default driver is responsible for it, if nothing else overlaps it
if (useDefault) return {defaultp(), lo};
// Not driven by a single vertex
return {nullptr, 0};
}
//######################################################################
// Renders the canonical pattern S-expression for a single DfgVertex
+31 -87
View File
@@ -46,11 +46,9 @@
#include <algorithm>
#include <array>
#include <functional>
#include <map>
#include <new>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#ifndef VL_NOT_FINAL
@@ -121,28 +119,6 @@ public:
inline void relinkSrcp(DfgVertex* srcp);
};
//------------------------------------------------------------------------------
// Input edge storage of a fixed arity vertices, embedded in the vertex itself.
template <uint32_t N_Edges>
class DfgInlineEdgeStorage final {
static_assert(N_Edges > 0, "'DfgInlineEdgeStorage' must hold at least one edge");
VL_UNCOPYABLE(DfgInlineEdgeStorage);
VL_UNMOVABLE(DfgInlineEdgeStorage);
public:
union { // Union, for manual memory management, so DfgEdge need no implicit constructor
DfgEdge m_edges[N_Edges]; // The input edges of the owning vertex
};
explicit DfgInlineEdgeStorage(DfgVertex* vtxp) {
for (uint32_t i = 0; i < N_Edges; ++i) new (&m_edges[i]) DfgEdge{vtxp};
}
~DfgInlineEdgeStorage() {
for (uint32_t i = 0; i < N_Edges; ++i) m_edges[i].~DfgEdge();
}
DfgInlineEdgeStorage() = delete;
};
//------------------------------------------------------------------------------
// Dataflow graph vertex
class DfgVertex VL_NOT_FINAL {
@@ -151,12 +127,10 @@ class DfgVertex VL_NOT_FINAL {
friend class DfgVisitor;
template <typename, bool>
friend class DfgUserMap;
friend class DfgVertexVariadic;
// STATE
V3ListLinks<DfgVertex> m_links; // V3List links in the DfgGraph
DfgEdge* const m_inlineInputsp; // Input edges stored inline in the vertex (iff fixed arity)
uint32_t m_nInputs; // Number of input edges
std::vector<std::unique_ptr<DfgEdge>> m_inputps; // Input edges, as vector, for fast indexing
DfgEdge::List m_sinks; // List of sink edges of this vertex
FileLine* const m_filelinep; // Source location
@@ -186,22 +160,25 @@ public:
protected:
// CONSTRUCTOR
DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dt,
DfgEdge* inlineInputsp, uint32_t nInputs) VL_MT_DISABLED;
DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dt) VL_MT_DISABLED;
// Use unlinkDelete instead
virtual ~DfgVertex() VL_MT_DISABLED = default;
private:
// Get input edge 'i'
inline DfgEdge* inputEdgep(size_t i) const;
// Create a new input edge and return it
DfgEdge* newInput() {
m_inputps.emplace_back(new DfgEdge{this});
return m_inputps.back().get();
}
public:
// Get input 'i'
DfgVertex* inputp(size_t i) const { return inputEdgep(i)->srcp(); }
DfgVertex* inputp(size_t i) const { return m_inputps[i]->srcp(); }
// Relink input 'i'
void inputp(size_t i, DfgVertex* vtxp) { inputEdgep(i)->relinkSrcp(vtxp); }
void inputp(size_t i, DfgVertex* vtxp) { m_inputps[i]->relinkSrcp(vtxp); }
// The number of inputs this vertex has. Some might be unconnected.
size_t nInputs() const { return m_nInputs; }
size_t nInputs() const { return m_inputps.size(); }
// Unlink all inputs and reset to no inputs - use very carefully
void resetInputs() { m_inputps.clear(); }
// The type of this vertex
VDfgType type() const { return m_type; }
@@ -270,12 +247,9 @@ public:
// Calls given function 'f' for each source vertex of this vertex. If 'f'
// returns true, further sources are not iterated and this method returns
// true itself. Unconnected source edges are not iterated.
template <typename T_Callable>
bool foreachSource(T_Callable&& f) {
static_assert(vlstd::is_invocable_r<bool, T_Callable, DfgVertex&>::value,
"T_Callable 'f' must have a signature compatible with 'bool(DfgVertex&)'");
for (size_t i = 0; i < m_nInputs; ++i) {
if (DfgVertex* const srcp = inputEdgep(i)->srcp()) {
bool foreachSource(std::function<bool(DfgVertex&)> f) {
for (const std::unique_ptr<DfgEdge>& edgep : m_inputps) {
if (DfgVertex* const srcp = edgep->srcp()) {
if (f(*srcp)) return true;
}
}
@@ -285,13 +259,9 @@ public:
// Calls given function 'f' for each source vertex of this vertex. If 'f'
// returns true, further sources are not iterated and this method returns
// true itself. Unconnected source edges are not iterated.
template <typename T_Callable>
bool foreachSource(T_Callable&& f) const {
static_assert(
vlstd::is_invocable_r<bool, T_Callable, const DfgVertex&>::value,
"T_Callable 'f' must have a signature compatible with 'bool(const DfgVertex&)'");
for (size_t i = 0; i < m_nInputs; ++i) {
if (const DfgVertex* const srcp = inputEdgep(i)->srcp()) {
bool foreachSource(std::function<bool(const DfgVertex&)> f) const {
for (const std::unique_ptr<DfgEdge>& edgep : m_inputps) {
if (DfgVertex* const srcp = edgep->srcp()) {
if (f(*srcp)) return true;
}
}
@@ -302,10 +272,7 @@ public:
// returns true, further sinks are not iterated and this method returns
// true itself. Unlinking/deleting the given sink during iteration is safe,
// but not other sinks of this vertex.
template <typename T_Callable>
bool foreachSink(T_Callable&& f) {
static_assert(vlstd::is_invocable_r<bool, T_Callable, DfgVertex&>::value,
"T_Callable 'f' must have a signature compatible with 'bool(DfgVertex&)'");
bool foreachSink(std::function<bool(DfgVertex&)> f) {
for (const DfgEdge* const edgep : m_sinks.unlinkable()) {
if (f(*edgep->dstp())) return true;
}
@@ -315,11 +282,7 @@ public:
// Calls given function 'f' for each sink vertex of this vertex. If 'f'
// returns true, further sinks are not iterated and this method returns
// true itself.
template <typename T_Callable>
bool foreachSink(T_Callable&& f) const {
static_assert(
vlstd::is_invocable_r<bool, T_Callable, const DfgVertex&>::value,
"T_Callable 'f' must have a signature compatible with 'bool(const DfgVertex&)'");
bool foreachSink(std::function<bool(const DfgVertex&)> f) const {
for (const DfgEdge& edge : m_sinks) {
if (f(*edge.dstp())) return true;
}
@@ -430,19 +393,6 @@ class DfgGraph final {
size_t m_size = 0; // Number of vertices in the graph
const std::string m_name; // Name of graph - need not be unique
std::string m_tmpNameStub{""}; // Name stub for temporary variables - computed lazy
size_t m_tmpNameCount = 0; // Sequence number for newly created temporary declarations
// Slots are local to this graph and keyed by module, prefix and type.
// Different prefixes may carry different AstVar attributes, but temporaries
// with the same prefix may share an AstVar, so they must have identical ones.
// Each scope consumes each slot at most once.
struct TempDeclarations final {
std::map<AstScope*, size_t> m_scopeCounts; // Next slot for each instance
std::vector<AstVar*> m_declps; // Declarations indexed by slot
};
std::map<AstNodeModule*, std::map<std::pair<std::string, AstNodeDType*>, TempDeclarations>>
m_temporaries; // Shared slots indexed by module, purpose, and type
uint64_t m_tempDeclarationsReused = 0; // Declarations shared across instance scopes
// The only way to access thes is via DfgUserMap, so mutable is appropriate,
// the map can change while the graph is const.
@@ -541,12 +491,14 @@ public:
// DfgVertexVar instances representing the same Ast variable are unified.
void mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) VL_MT_DISABLED;
// Create a new scoped variable. Instances of a module share temporary
// declarations of the same prefix and type, but have independent storage.
// Each scope uses a declaration at most once; new declarations get unique names.
// As the AstVar may be shared, callers must set identical AstVar attributes
// on all temporaries created with the same prefix.
DfgVertexVar* makeNewVar(FileLine*, const std::string& prefix, const DfgDataType&,
// Genarete a unique name. The provided 'prefix' and 'n' values will be part of the name, and
// must be unique (as a pair) in each invocation for this graph.
std::string makeUniqueName(const std::string& prefix, size_t n) VL_MT_DISABLED;
// Create a new variable with the given name and data type. For a Scoped
// Dfg, the AstScope where the corresponding AstVarScope will be inserted
// must be provided
DfgVertexVar* makeNewVar(FileLine*, const std::string& name, const DfgDataType&,
AstScope*) VL_MT_DISABLED;
// Split this graph into individual components (unique sub-graphs with no edges between them).
@@ -606,14 +558,14 @@ namespace V3Dfg {
// Returns true if variable can be represented in the graph
inline bool isSupported(const AstVarScope* vscp) {
const AstNodeModule* const modp = vscp->scopep()->modp();
if (VN_IS(modp, Module) || VN_IS(modp, Package)) {
// Regular modules and packages supported
if (VN_IS(modp, Module)) {
// Regular module supported
} else if (const AstIface* const ifacep = VN_CAST(modp, Iface)) {
// Interfaces supported if there are no virtual interfaces for
// them, otherwise they cannot be resovled statically.
if (ifacep->hasVirtualRef()) return false;
} else {
return false; // Anything else (class, etc) not supported
return false; // Anything else (package, class, etc) not supported
}
if (DfgVertexVar::hasRWRefs(vscp)) return false; // Referenced via READWRITE references
// Check the AstVar
@@ -859,14 +811,6 @@ void DfgEdge::relinkSrcp(DfgVertex* srcp) {
// DfgVertex {{{
DfgEdge* DfgVertex::inputEdgep(size_t i) const {
UDEBUGONLY(UASSERT_OBJ(i < m_nInputs, this, "Input index out of range"););
if (VL_LIKELY(m_inlineInputsp)) return m_inlineInputsp + i;
// 'm_inlineInputsp' is null exactly for a DfgVertexVariadic
UDEBUGONLY(UASSERT_OBJ(is<DfgVertexVariadic>(), this, "Vertex without input edge storage"););
return static_cast<const DfgVertexVariadic*>(this)->m_edgeps[i].get();
}
bool DfgVertex::isCheaperThanLoad() const {
// Constants
if (is<DfgConst>()) return true;
+186 -242
View File
@@ -152,9 +152,9 @@ class TraceDriver final : public DfgVisitor {
// TYPES
// Key for caching the result of a trace
struct CacheKey final {
DfgVertex* m_vtxp; // Vertex being traced
uint32_t m_lsb; // LSB of the range within m_vtxp being traced
uint32_t m_msb; // MSB of the range within m_vtxp being traced
DfgVertex* m_vtxp;
uint32_t m_lsb;
uint32_t m_msb;
CacheKey() = delete;
CacheKey(DfgVertex* vtxp, uint32_t lsb, uint32_t msb)
@@ -187,20 +187,11 @@ class TraceDriver final : public DfgVisitor {
uint32_t m_lsb = 0; // LSB to extract from the currently visited Vertex
uint32_t m_msb = 0; // MSB to extract from the currently visited Vertex
std::vector<uint32_t> m_idxs; // Indices to extract from the currently visited Vertex
std::vector<DfgVertex**> m_pendingps; // Pending pass-through cache entries across trace calls
// Result of tracing the currently visited Vertex. Use RETURN_RESULT below!
// Result of tracing the currently visited Vertex. Use SET_RESULT below!
DfgVertex* m_resp = nullptr;
// Set instead of 'm_resp' when the result of tracing the currently visited Vertex is
// exactly the result of tracing 'm_tailp' at the updated [m_msb:m_lsb]. 'trace'
// resolves these iteratively. Use RETURN_RESULT_TAIL below!
DfgVertex* m_tailp = nullptr;
// When tracing a splice, this is the corresponding 'defaultp' of the variable it drives
DfgVertex* m_defaultp = nullptr;
DfgVertex* m_splicep = nullptr; // The splice vertex being traced, just for assertions
DfgVertex* m_defaultp = nullptr; // When tracing a variable, this is its 'defaultp', if any
// Result cache for reusing already traced vertices
std::unordered_map<CacheKey, DfgVertex*, CacheKey::Hash, CacheKey::Equal> m_cache;
// Cache for boundary vertices (where m_sccInfo.get(*vtxp) != m_component)
std::unordered_map<CacheKey, DfgVertex*, CacheKey::Hash, CacheKey::Equal> m_boundaryCache;
#ifdef VL_DEBUG
std::ofstream m_lineCoverageFile; // Line coverage file, just for testing
@@ -239,10 +230,12 @@ class TraceDriver final : public DfgVisitor {
// Create temporary capable of holding the result of 'vtxp'
DfgVertexVar* createTmp(const char* prefix, DfgVertex* vtxp) {
AstNode* nodep = v3Global.rootp();
const std::string name = m_dfg.makeUniqueName(prefix, nodep->user2Inc());
FileLine* const flp = vtxp->fileline();
DfgVertex::ScopeCache scopeCache;
AstScope* const scopep = vtxp->scopep(scopeCache);
DfgVertexVar* const varp = m_dfg.makeNewVar(flp, prefix, vtxp->dtype(), scopep);
DfgVertexVar* const varp = m_dfg.makeNewVar(flp, name, vtxp->dtype(), scopep);
varp->vscp()->varp()->isInternal(true);
varp->tmpForp(varp->vscp());
m_sccInfo.add(*varp, 0);
@@ -250,108 +243,60 @@ class TraceDriver final : public DfgVisitor {
}
// Trace drivers of the given packed vertex, at the given bit range.
DfgVertex* trace(DfgVertex* vtxp, uint32_t msb, uint32_t lsb) {
// Many vertices just pass the traced bit range through to one of their sources
// (see RETURN_RESULT_TAIL). Chains of such vertices can be tens of thousands of
// vertices long in large designs (e.g.: a wide concatenation), so resolve them
// in this loop instead of by recursion, which would overflow the C++ stack.
// 'm_pendingps' holds the cache entries of the vertices on the current chain. They
// all yield the same result, which is filled in when the chain is resolved.
// Note: references into 'm_cache' are stable across the insertions below.
DfgVertex* const startVtxp = vtxp;
const uint32_t startMsb = msb;
const uint32_t startLsb = lsb;
const size_t pendingBegin = m_pendingps.size();
DfgVertex* resp = nullptr;
DfgVertex* trace(DfgVertex* const vtxp, const uint32_t msb, const uint32_t lsb) {
UASSERT_OBJ(vtxp->isPacked(), vtxp, "Can only trace packed type vertices");
UASSERT_OBJ(vtxp->size() > msb, vtxp, "Traced Vertex too narrow");
while (true) {
UASSERT_OBJ(vtxp->isPacked(), vtxp, "Can only trace packed type vertices");
UASSERT_OBJ(vtxp->size() > msb, vtxp, "Traced Vertex too narrow");
UASSERT_OBJ(!m_defaultp || vtxp == m_splicep, vtxp, "Tracing wrong vertex");
// Get the cache entry, which is the resulting driver that is not part of
// the same component as vtxp
DfgVertex*& respr = m_cache
.emplace(std::piecewise_construct, //
std::forward_as_tuple(vtxp, msb, lsb), //
std::forward_as_tuple(nullptr))
.first->second;
// Trace the vertex
if (respr) {
// If already traced this vtxp/msb/lsb, just use the result.
// This is important to avoid combinatorial explosion when the
// same sub-expression is needed multiple times.
} else if (m_sccInfo.get(*vtxp) != m_component) {
// If the currently traced vertex is in a different component,
// then we found what we were looking for. But if it's a splice
// with a corresponding default, we need to keep going as the
// splice does not fully define the value we are seeking.
if (m_sccInfo.get(*vtxp) != m_component && !m_defaultp) {
DfgVertex*& respr
= m_boundaryCache
.emplace(std::piecewise_construct, std::forward_as_tuple(vtxp, msb, lsb),
std::forward_as_tuple(nullptr))
.first->second;
if (!respr) {
respr = vtxp;
// If the result is a splice, we need to insert a temporary for it
// as a splice cannot be fed into arbitray logic
if (DfgVertexSplice* const splicep = respr->cast<DfgVertexSplice>()) {
DfgVertexVar* const tmpp = createTmp("TraceDriver", splicep);
// Note: we can't do 'splicep->replaceWith(tmpp)', as other
// variable sinks of the splice might have a defaultp driver.
tmpp->srcp(splicep);
respr = tmpp;
}
// Apply a Sel to extract the relevant bits if only a part is needed
if (msb != respr->width() - 1 || lsb != 0) {
DfgSel* const selp = make<DfgSel>(respr, msb - lsb + 1);
selp->fromp(respr);
selp->lsb(lsb);
respr = selp;
}
}
resp = respr;
break;
// then we found what we were looking for.
respr = vtxp;
// If the result is a splice, we need to insert a temporary for it
// as a splice cannot be fed into arbitray logic
if (DfgVertexSplice* const splicep = respr->cast<DfgVertexSplice>()) {
DfgVertexVar* const tmpp = createTmp("TraceDriver", splicep);
// Note: we can't do 'splicep->replaceWith(tmpp)', as other
// variable sinks of the splice might have a defaultp driver.
tmpp->srcp(splicep);
respr = tmpp;
}
// Normal cache for intra-component traces
DfgVertex*& respr = m_cache
.emplace(std::piecewise_construct, //
std::forward_as_tuple(vtxp, msb, lsb), //
std::forward_as_tuple(nullptr))
.first->second;
// Trace the vertex
if (respr) {
// If already traced this vtxp/msb/lsb, just use the result.
// This is important to avoid combinatorial explosion when the
// same sub-expression is needed multiple times.
} else {
// Otherwise visit the vertex to trace it
VL_RESTORER(m_msb);
VL_RESTORER(m_lsb);
VL_RESTORER_CLEAR(m_idxs);
VL_RESTORER(m_resp);
VL_RESTORER(m_tailp);
m_msb = msb;
m_lsb = lsb;
m_resp = nullptr;
m_tailp = nullptr;
iterate(vtxp);
UASSERT_OBJ((m_resp != nullptr) != (m_tailp != nullptr), vtxp,
"Expected exactly one trace result");
if (m_tailp) {
// This vertex just passes the range through, so continue with its
// source, at the updated range. Its cache entry is filled in when
// the chain is resolved.
m_pendingps.push_back(&respr);
vtxp = m_tailp;
msb = m_msb;
lsb = m_lsb;
continue;
}
respr = m_resp;
// Apply a Sel to extract the relevant bits if only a part is needed
if (msb != respr->width() - 1 || lsb != 0) {
DfgSel* const selp = make<DfgSel>(respr, msb - lsb + 1);
selp->fromp(respr);
selp->lsb(lsb);
respr = selp;
}
resp = respr;
break;
} else {
// Otherwise visit the vertex to trace it
VL_RESTORER(m_msb);
VL_RESTORER(m_lsb);
VL_RESTORER_CLEAR(m_idxs);
VL_RESTORER(m_resp);
m_msb = msb;
m_lsb = lsb;
m_resp = nullptr;
iterate(vtxp);
respr = m_resp;
}
// We only ever trace drivers of bits that are known to be independent
// of the cycles, so we should always be able to find an acyclic driver.
UASSERT_OBJ(resp, startVtxp, "Tracing driver failed for " << startVtxp->typeName());
UASSERT_OBJ(resp->width() == (startMsb - startLsb + 1), startVtxp, "Wrong result width");
// Every vertex on the pass-through chain resolves to the same driver
for (size_t i = pendingBegin; i < m_pendingps.size(); ++i) *m_pendingps[i] = resp;
m_pendingps.resize(pendingBegin);
return resp;
UASSERT_OBJ(respr, vtxp, "Tracing driver failed for " << vtxp->typeName());
UASSERT_OBJ(respr->width() == (msb - lsb + 1), vtxp, "Wrong result width");
return respr;
}
// Trace drivers of the given array vertex, at the current m_idxs, m_msb, m_lsb.
@@ -430,59 +375,27 @@ class TraceDriver final : public DfgVisitor {
return resp;
}
// Use this macro to return the result from 'visit' methods. This also emits
// Use this macro to set the result in 'visit' methods. This also emits
// a line to m_lineCoverageFile for testing.
// TODO: Use C++20 std::source_location instead of a macro
#ifdef VL_DEBUG
#define TRACE_RESULT_COVER() \
#define SET_RESULT(vtxp) \
do { \
m_resp = vtxp; \
if (VL_UNLIKELY(m_lineCoverageFile.is_open())) m_lineCoverageFile << __LINE__ << '\n'; \
} while (false)
#else
#define TRACE_RESULT_COVER() \
do { \
} while (false)
#define SET_RESULT(vtxp) m_resp = vtxp;
#endif
#define RETURN_RESULT(vtxp) \
do { \
m_resp = (vtxp); \
TRACE_RESULT_COVER(); \
return; \
} while (false)
// Use this macro in 'visit' methods when the result is exactly the result of tracing
// 'srcp' at [smsb:slsb]. 'trace' then continues iteratively instead of recursing, so
// arbitrarily long chains of such pass-through vertices do not consume C++ stack.
// Note this overwrites 'm_msb'/'m_lsb', which is fine as the visitor returns
// immediately. Only use this when no state set up by the visitor (e.g.: 'm_defaultp')
// has to stay live while 'srcp' is traced, as that happens after the visitor returned.
#define RETURN_RESULT_TAIL(srcp, smsb, slsb) \
do { \
/* Evaluate all arguments before assigning, as they usually read 'm_msb'/'m_lsb' */ \
DfgVertex* const tailp = (srcp); \
const uint32_t tailMsb = (smsb); \
const uint32_t tailLsb = (slsb); \
m_tailp = tailp; \
m_msb = tailMsb; \
m_lsb = tailLsb; \
TRACE_RESULT_COVER(); \
return; \
} while (false)
// VISITORS
void visit(DfgVertex* vtxp) override { // LCOV_EXCL_START
vtxp->v3fatalSrc("TraceDriver - Unhandled vertex type: " << vtxp->typeName());
} // LCOV_EXCL_STOP
void visit(DfgSplicePacked* vtxp) override {
UASSERT_OBJ(m_splicep == vtxp, vtxp, "Unexpected trace of DfgSplicePacked");
DfgVertex* defaultp = m_defaultp;
m_defaultp = nullptr;
m_splicep = nullptr;
struct Driver final {
DfgVertex* m_vtxp; // Vertex driving this range
DfgVertex* m_vtxp;
uint32_t m_lsb; // LSB of driven range (internal, not Verilog)
uint32_t m_msb; // MSB of driven range (internal, not Verilog)
Driver() = delete;
@@ -494,27 +407,25 @@ class TraceDriver final : public DfgVisitor {
std::vector<Driver> drivers;
// Look at all the drivers, one might cover the whole range, but also gather all drivers
bool tryWholeDefault = defaultp;
DfgVertex* coverp = nullptr; // Driver covering the whole searched range, if any
uint32_t coverLsb = 0; // LSB of the range driven by 'coverp'
vtxp->foreachDriver([&](DfgVertex& src, uint32_t lsb) {
bool tryWholeDefault = m_defaultp;
const bool done = vtxp->foreachDriver([&](DfgVertex& src, uint32_t lsb) {
const uint32_t msb = lsb + src.width() - 1;
drivers.emplace_back(&src, lsb, msb);
// Check if this driver covers any of the bits, then we can't use whole default
if (m_msb >= lsb && msb >= m_lsb) tryWholeDefault = false;
// If it does not cover the whole searched bit range, move on
if (m_lsb < lsb || msb < m_msb) return false;
// Driver covers whole search range, we are done gathering
coverp = &src;
coverLsb = lsb;
// Driver covers whole search range, trace that and we are done
SET_RESULT(trace(&src, m_msb - lsb, m_lsb - lsb));
return true;
});
// Trace the driver covering the whole searched range, if there is one
if (coverp) RETURN_RESULT_TAIL(coverp, m_msb - coverLsb, m_lsb - coverLsb);
if (done) return;
// Trace the default driver if no other drivers cover the searched range
if (tryWholeDefault) RETURN_RESULT_TAIL(defaultp, m_msb, m_lsb);
if (tryWholeDefault) {
SET_RESULT(trace(m_defaultp, m_msb, m_lsb));
return;
}
// Hard case: We need to combine multiple drivers to produce the searched bit range
@@ -533,8 +444,8 @@ class TraceDriver final : public DfgVisitor {
if (driver.m_lsb > m_msb) break;
// Gap below this driver, trace default to fill it
if (driver.m_lsb > lsb) {
UASSERT_OBJ(defaultp, vtxp, "Should have a default driver if needs tracing");
termps.emplace_back(trace(defaultp, driver.m_lsb - 1, lsb));
UASSERT_OBJ(m_defaultp, vtxp, "Should have a default driver if needs tracing");
termps.emplace_back(trace(m_defaultp, driver.m_lsb - 1, lsb));
lsb = driver.m_lsb;
}
// Driver covers searched range, pick the needed/available bits
@@ -543,8 +454,8 @@ class TraceDriver final : public DfgVisitor {
lsb = lim + 1;
}
if (m_msb >= lsb) {
UASSERT_OBJ(defaultp, vtxp, "Should have a default driver if needs tracing");
termps.emplace_back(trace(defaultp, m_msb, lsb));
UASSERT_OBJ(m_defaultp, vtxp, "Should have a default driver if needs tracing");
termps.emplace_back(trace(m_defaultp, m_msb, lsb));
}
// The earlier cheks cover the case when either a whole driver or the default covers
@@ -560,46 +471,33 @@ class TraceDriver final : public DfgVisitor {
catp->lhsp(termp);
resp = catp;
}
RETURN_RESULT(resp);
SET_RESULT(resp);
}
void visit(DfgSpliceArray* vtxp) override {
UASSERT_OBJ(m_splicep == vtxp, vtxp, "Unexpected trace of DfgSpliceArray");
DfgVertex* const defaultp = m_defaultp;
m_defaultp = nullptr;
m_splicep = nullptr;
// Explicit per-element driver (a UnitArray wrapping the element value)
const uint32_t idx = m_idxs.back();
if (DfgVertex* const driverp = vtxp->driverAt(idx)) {
DfgVertex* const srcp = driverp->as<DfgUnitArray>()->srcp();
if (srcp->is<DfgVertexSplice>()) {
// Partial-element propagation is rejected during synthesis.
UASSERT_OBJ(!defaultp, vtxp, "Array default with partial element driver");
m_splicep = srcp;
}
if (DfgVertex* const driverp = vtxp->driverAt(m_idxs.back())) {
// Consume this index, then trace the element value
RETURN_RESULT(tracePopIdx(srcp));
SET_RESULT(tracePopIdx(driverp->as<DfgUnitArray>()->srcp()));
return;
}
// An element not driven explicitly comes from the default array at the same index.
UASSERT_OBJ(defaultp, vtxp, "Independent array element should have a driver or default");
RETURN_RESULT(traceSameIdx(defaultp));
// TODO: this is unreachable, as syntheis can't create it today.
// // Element not driven explicitly, so it comes from the default array. Keep the
// // index pending (the default is the whole array, indexed the same way) and
// // continue tracing it.
// UASSERT_OBJ(m_defaultp, vtxp, "Independent array element should have a driver or
// default"); SET RESULT(traceSameIdx(m_defaultp));
}
void visit(DfgVertexVar* vtxp) override {
UASSERT_OBJ(!vtxp->isVolatile(), vtxp, "Should not trace through volatile variable");
DfgVertex* const srcp = vtxp->srcp();
DfgVertex* const defaultp = vtxp->defaultp();
DfgVertex* const drvp = srcp ? srcp : defaultp;
// If we are about to trace a splice, set the defaultp to the corresponding default
if (srcp && srcp->is<DfgVertexSplice>()) {
m_defaultp = defaultp;
m_splicep = srcp;
}
VL_RESTORER(m_defaultp);
m_defaultp = vtxp->defaultp();
DfgVertex* const drvp = vtxp->srcp() ? vtxp->srcp() : m_defaultp;
UASSERT_OBJ(drvp, vtxp, "Should not have to trace undriven variable");
// Packed variable: trace the driver. Array variable: continue navigating it at
// the pending element (both at the same bit range).
RETURN_RESULT(m_idxs.empty() ? trace(drvp, m_msb, m_lsb) : traceSameIdx(drvp));
SET_RESULT(m_idxs.empty() ? trace(drvp, m_msb, m_lsb) : traceSameIdx(drvp));
}
void visit(DfgArraySel* vtxp) override {
@@ -607,7 +505,8 @@ class TraceDriver final : public DfgVisitor {
// structure of 'fromp'. This handles arbitrarily nested (multi-dimensional)
// arrays, as each nested ArraySel pushes a further index.
if (const DfgConst* const idxp = vtxp->bitp()->cast<DfgConst>()) {
RETURN_RESULT(tracePushIdx(vtxp->fromp(), idxp->toU32()));
SET_RESULT(tracePushIdx(vtxp->fromp(), idxp->toU32()));
return;
}
// If index is not constant, independence was proven only if the 'fromp' is
@@ -620,13 +519,13 @@ class TraceDriver final : public DfgVisitor {
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
selp->fromp(resp);
selp->lsb(m_lsb);
RETURN_RESULT(selp);
SET_RESULT(selp);
}
void visit(DfgUnitArray* vtxp) override {
// Single-element array adapter, the pending index must be 0, unwrap the element
UASSERT_OBJ(m_idxs.back() == 0, vtxp, "UnitArray element index should be 0");
RETURN_RESULT(tracePopIdx(vtxp->srcp()));
SET_RESULT(tracePopIdx(vtxp->srcp()));
}
void visit(DfgConcat* vtxp) override {
@@ -634,14 +533,20 @@ class TraceDriver final : public DfgVisitor {
DfgVertex* const lhsp = vtxp->lhsp();
const uint32_t rWidth = rhsp->width();
// If the traced bits are wholly in the RHS
if (rWidth > m_msb) RETURN_RESULT_TAIL(rhsp, m_msb, m_lsb);
if (rWidth > m_msb) {
SET_RESULT(trace(rhsp, m_msb, m_lsb));
return;
}
// If the traced bits are wholly in the LHS
if (m_lsb >= rWidth) RETURN_RESULT_TAIL(lhsp, m_msb - rWidth, m_lsb - rWidth);
if (m_lsb >= rWidth) {
SET_RESULT(trace(lhsp, m_msb - rWidth, m_lsb - rWidth));
return;
}
// The traced bit spans both sides, trace both
DfgConcat* const resp = make<DfgConcat>(vtxp, m_msb - m_lsb + 1);
resp->rhsp(trace(rhsp, rWidth - 1, m_lsb));
resp->lhsp(trace(lhsp, m_msb - rWidth, 0));
RETURN_RESULT(resp);
SET_RESULT(resp);
}
void visit(DfgRep* vtxp) override {
@@ -654,75 +559,91 @@ class TraceDriver final : public DfgVisitor {
DfgSel* const resp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
resp->fromp(repp);
resp->lsb(m_lsb);
RETURN_RESULT(resp);
SET_RESULT(resp);
return;
}
// If the requested bits are within the same repliacted word
if (m_msb / sWidth == m_lsb / sWidth) {
RETURN_RESULT_TAIL(srcp, m_msb % sWidth, m_lsb % sWidth);
SET_RESULT(trace(srcp, m_msb % sWidth, m_lsb % sWidth));
return;
}
// The requested bits span two replicated words
DfgConcat* const resp = make<DfgConcat>(vtxp, m_msb - m_lsb + 1);
resp->rhsp(trace(srcp, sWidth - 1, m_lsb % sWidth));
resp->lhsp(trace(srcp, m_msb % sWidth, 0));
RETURN_RESULT(resp);
SET_RESULT(resp);
}
void visit(DfgExtend* vtxp) override {
DfgVertex* const srcp = vtxp->srcp();
const uint32_t sWidth = srcp->width();
// If the traced bits are wholly in the input
if (sWidth > m_msb) RETURN_RESULT_TAIL(srcp, m_msb, m_lsb);
if (sWidth > m_msb) {
SET_RESULT(trace(srcp, m_msb, m_lsb));
return;
}
// If the traced bits are wholly in the extension
if (m_lsb >= sWidth) RETURN_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
if (m_lsb >= sWidth) {
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
return;
}
// The traced bits span both sides
DfgExtend* const resp = make<DfgExtend>(vtxp, m_msb - m_lsb + 1);
resp->srcp(trace(srcp, sWidth - 1, m_lsb));
RETURN_RESULT(resp);
SET_RESULT(resp);
}
void visit(DfgExtendS* vtxp) override {
DfgVertex* const srcp = vtxp->srcp();
const uint32_t sWidth = srcp->width();
// If the traced bits are wholly in the input
if (sWidth > m_msb) RETURN_RESULT_TAIL(srcp, m_msb, m_lsb);
if (sWidth > m_msb) {
SET_RESULT(trace(srcp, m_msb, m_lsb));
return;
}
// If the traced bits are wholly in the extension
if (m_lsb >= sWidth) {
if (m_msb == m_lsb) RETURN_RESULT_TAIL(srcp, sWidth - 1, sWidth - 1);
DfgExtendS* const resp = make<DfgExtendS>(vtxp, m_msb - m_lsb + 1);
resp->srcp(trace(srcp, sWidth - 1, sWidth - 1));
RETURN_RESULT(resp);
DfgVertex* const sp = trace(srcp, sWidth - 1, sWidth - 1);
if (m_msb == m_lsb) {
SET_RESULT(sp);
} else {
DfgExtendS* const resp = make<DfgExtendS>(vtxp, m_msb - m_lsb + 1);
resp->srcp(sp);
SET_RESULT(resp);
}
return;
}
// The traced bits span both sides
DfgExtendS* const resp = make<DfgExtendS>(vtxp, m_msb - m_lsb + 1);
resp->srcp(trace(srcp, sWidth - 1, m_lsb));
RETURN_RESULT(resp);
SET_RESULT(resp);
}
void visit(DfgSel* vtxp) override {
const uint32_t lsb = vtxp->lsb();
RETURN_RESULT_TAIL(vtxp->srcp(), m_msb + lsb, m_lsb + lsb);
SET_RESULT(trace(vtxp->srcp(), m_msb + lsb, m_lsb + lsb));
}
void visit(DfgNot* vtxp) override {
DfgNot* const resp = make<DfgNot>(vtxp, m_msb - m_lsb + 1);
resp->srcp(trace(vtxp->srcp(), m_msb, m_lsb));
RETURN_RESULT(resp);
SET_RESULT(resp);
}
void visit(DfgAnd* vtxp) override { RETURN_RESULT(traceBitwiseBinary(vtxp)); }
void visit(DfgOr* vtxp) override { RETURN_RESULT(traceBitwiseBinary(vtxp)); }
void visit(DfgXor* vtxp) override { RETURN_RESULT(traceBitwiseBinary(vtxp)); }
void visit(DfgAdd* vtxp) override { RETURN_RESULT(traceAddSub(vtxp)); }
void visit(DfgSub* vtxp) override { RETURN_RESULT(traceAddSub(vtxp)); }
void visit(DfgRedAnd* vtxp) override { RETURN_RESULT(traceReduction(vtxp)); }
void visit(DfgRedOr* vtxp) override { RETURN_RESULT(traceReduction(vtxp)); }
void visit(DfgRedXor* vtxp) override { RETURN_RESULT(traceReduction(vtxp)); }
void visit(DfgEq* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
void visit(DfgNeq* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
void visit(DfgLt* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
void visit(DfgLte* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
void visit(DfgGt* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
void visit(DfgGte* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
void visit(DfgAnd* vtxp) override { SET_RESULT(traceBitwiseBinary(vtxp)); }
void visit(DfgOr* vtxp) override { SET_RESULT(traceBitwiseBinary(vtxp)); }
void visit(DfgXor* vtxp) override { SET_RESULT(traceBitwiseBinary(vtxp)); }
void visit(DfgAdd* vtxp) override { SET_RESULT(traceAddSub(vtxp)); }
void visit(DfgSub* vtxp) override { SET_RESULT(traceAddSub(vtxp)); }
void visit(DfgRedAnd* vtxp) override { SET_RESULT(traceReduction(vtxp)); }
void visit(DfgRedOr* vtxp) override { SET_RESULT(traceReduction(vtxp)); }
void visit(DfgRedXor* vtxp) override { SET_RESULT(traceReduction(vtxp)); }
void visit(DfgEq* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
void visit(DfgNeq* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
void visit(DfgLt* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
void visit(DfgLte* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
void visit(DfgGt* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
void visit(DfgGte* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
void visit(DfgShiftRS* vtxp) override {
DfgVertex* const lhsp = vtxp->lhsp();
@@ -732,28 +653,34 @@ class TraceDriver final : public DfgVisitor {
const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? 0 : vtxp->width() - shiftAmnt;
// If the traced bits are wholly in the input
if (lowerWidth > m_msb) {
RETURN_RESULT_TAIL(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt);
SET_RESULT(trace(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt));
return;
}
// If the traced bits are wholly in the extension
if (m_lsb >= lowerWidth) {
DfgExtendS* const resp = make<DfgExtendS>(vtxp, m_msb - m_lsb + 1);
resp->srcp(trace(lhsp, lhsp->width() - 1, lhsp->width() - 1));
RETURN_RESULT(resp);
SET_RESULT(resp);
return;
}
// The traced bits span both sides
DfgExtendS* const resp = make<DfgExtendS>(vtxp, m_msb - m_lsb + 1);
resp->srcp(trace(lhsp, lowerWidth - 1 + shiftAmnt, m_lsb + shiftAmnt));
RETURN_RESULT(resp);
SET_RESULT(resp);
return;
}
DfgShiftRS* const shiftrsp = make<DfgShiftRS>(vtxp, vtxp->lhsp()->width() - m_lsb);
shiftrsp->rhsp(trace(vtxp->rhsp(), vtxp->rhsp()->width() - 1, 0));
shiftrsp->lhsp(trace(vtxp->lhsp(), vtxp->lhsp()->width() - 1, m_lsb));
if (m_msb == vtxp->lhsp()->width() - 1) RETURN_RESULT(shiftrsp);
if (m_msb == vtxp->lhsp()->width() - 1) {
SET_RESULT(shiftrsp);
return;
}
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
selp->fromp(shiftrsp);
selp->lsb(0);
RETURN_RESULT(selp);
SET_RESULT(selp);
}
void visit(DfgShiftR* vtxp) override {
@@ -764,24 +691,32 @@ class TraceDriver final : public DfgVisitor {
const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? 0 : vtxp->width() - shiftAmnt;
// If the traced bits are wholly in the input
if (lowerWidth > m_msb) {
RETURN_RESULT_TAIL(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt);
SET_RESULT(trace(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt));
return;
}
// If the traced bits are wholly in the extension
if (m_lsb >= lowerWidth) RETURN_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
if (m_lsb >= lowerWidth) {
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
return;
}
// The traced bits span both sides
DfgExtend* const resp = make<DfgExtend>(vtxp, m_msb - m_lsb + 1);
resp->srcp(trace(lhsp, lowerWidth - 1 + shiftAmnt, m_lsb + shiftAmnt));
RETURN_RESULT(resp);
SET_RESULT(resp);
return;
}
DfgShiftR* const shiftrp = make<DfgShiftR>(vtxp, vtxp->lhsp()->width() - m_lsb);
shiftrp->rhsp(trace(vtxp->rhsp(), vtxp->rhsp()->width() - 1, 0));
shiftrp->lhsp(trace(vtxp->lhsp(), vtxp->lhsp()->width() - 1, m_lsb));
if (m_msb == vtxp->lhsp()->width() - 1) RETURN_RESULT(shiftrp);
if (m_msb == vtxp->lhsp()->width() - 1) {
SET_RESULT(shiftrp);
return;
}
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
selp->fromp(shiftrp);
selp->lsb(0);
RETURN_RESULT(selp);
SET_RESULT(selp);
}
void visit(DfgShiftL* vtxp) override {
@@ -792,25 +727,33 @@ class TraceDriver final : public DfgVisitor {
const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? vtxp->width() : shiftAmnt;
// If the traced bits are wholly in the input
if (m_lsb >= lowerWidth) {
RETURN_RESULT_TAIL(lhsp, m_msb - shiftAmnt, m_lsb - shiftAmnt);
SET_RESULT(trace(lhsp, m_msb - shiftAmnt, m_lsb - shiftAmnt));
return;
}
// If the traced bits are wholly in the extension
if (lowerWidth > m_msb) RETURN_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
if (lowerWidth > m_msb) {
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
return;
}
// The traced bits span both sides
DfgConcat* const resp = make<DfgConcat>(vtxp, m_msb - m_lsb + 1);
resp->rhsp(make<DfgConst>(vtxp, resp->width() - (m_msb - lowerWidth + 1)));
resp->lhsp(trace(lhsp, m_msb - shiftAmnt, lowerWidth - shiftAmnt));
RETURN_RESULT(resp);
SET_RESULT(resp);
return;
}
DfgShiftL* const shiftlp = make<DfgShiftL>(vtxp, m_msb + 1);
shiftlp->rhsp(trace(vtxp->rhsp(), vtxp->rhsp()->width() - 1, 0));
shiftlp->lhsp(trace(vtxp->lhsp(), m_msb, 0));
if (m_lsb == 0) RETURN_RESULT(shiftlp);
if (m_lsb == 0) {
SET_RESULT(shiftlp);
return;
}
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
selp->fromp(shiftlp);
selp->lsb(m_lsb);
RETURN_RESULT(selp);
SET_RESULT(selp);
}
void visit(DfgCond* vtxp) override {
@@ -818,7 +761,7 @@ class TraceDriver final : public DfgVisitor {
resp->condp(trace(vtxp->condp(), vtxp->condp()->width() - 1, 0));
resp->thenp(trace(vtxp->thenp(), m_msb, m_lsb));
resp->elsep(trace(vtxp->elsep(), m_msb, m_lsb));
RETURN_RESULT(resp);
SET_RESULT(resp);
}
void visit(DfgMatchMasked* vtxp) override {
@@ -828,12 +771,10 @@ class TraceDriver final : public DfgVisitor {
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
selp->fromp(resp);
selp->lsb(m_lsb);
RETURN_RESULT(selp);
SET_RESULT(selp);
}
#undef RETURN_RESULT
#undef TRACE_RESULT_COVER
#undef RETURN_RESULT_TAIL
#undef SET_RESULT
public:
// CONSTRUCTOR
@@ -1652,6 +1593,9 @@ void breakCycles(DfgGraph& dfg, V3DfgBreakCyclesContext& ctx) {
if (dumpDfgLevel() >= level) dfg.dumpDotFilePrefixed("breakCycles-" + name);
};
// AstNetlist/AstNodeModule user2 used as sequence numbers for temporaries
const VNUser2InUse user2InUse;
// Show input for debugging
dump(7, dfg, "input");
+182 -139
View File
@@ -25,11 +25,8 @@
#ifndef VERILATOR_V3DFGCACHE_H_
#define VERILATOR_V3DFGCACHE_H_
#include "verilatedos.h"
#include "V3Dfg.h"
#include "V3DfgDataType.h"
#include "V3HashTable.h"
#include <type_traits>
@@ -55,107 +52,137 @@ struct V3DfgCacheType<Vertex, CacheBase, VertexBase, Cache, Pairs...> final {
class V3DfgCache final {
// TYPES
// Hashing and comparison of the cached vertices. Each takes either a vertex, or the
// parts a vertex would be created from, so a lookup needs no vertex and no key object.
class KeySel final {
const DfgDataType& m_dtype;
const DfgVertex* const m_fromp;
const uint32_t m_lsb;
// DfgSel
struct HashSel final {
size_t operator()(const DfgSel* vtxp) const {
return operator()(vtxp->dtype(), vtxp->fromp(), vtxp->lsb());
}
size_t operator()(const DfgDataType& dtype, const DfgVertex* fromp, uint32_t lsb) const {
// cppcheck-suppress unreadVariable // cppcheck bug
V3Hash hash = dtype.hash();
hash += vertexHash(fromp);
hash += lsb;
return hash.value();
}
};
struct EqualSel final {
bool operator()(const DfgSel* ap, const DfgSel* bp) const {
return operator()(ap, bp->dtype(), bp->fromp(), bp->lsb());
}
bool operator()(const DfgSel* vtxp, const DfgDataType& dtype, const DfgVertex* fromp,
uint32_t lsb) const {
return vtxp->lsb() == lsb && vtxp->dtype() == dtype
&& vertexEqual(vtxp->fromp(), fromp);
}
public:
KeySel(const DfgDataType& dtype, DfgVertex* fromp, uint32_t lsb)
: m_dtype{dtype}
, m_fromp{fromp}
, m_lsb{lsb} {}
explicit KeySel(const DfgSel* vtxp)
: m_dtype{vtxp->dtype()}
, m_fromp{vtxp->fromp()}
, m_lsb{vtxp->lsb()} {}
struct Hash final {
size_t operator()(const KeySel& key) const {
// cppcheck-suppress unreadVariable // cppcheck bug
V3Hash hash = key.m_dtype.hash();
hash += vertexHash(key.m_fromp);
hash += key.m_lsb;
return hash.value();
}
};
struct Equal final {
bool operator()(const KeySel& a, const KeySel& b) const {
return a.m_lsb == b.m_lsb && a.m_dtype == b.m_dtype
&& vertexEqual(a.m_fromp, b.m_fromp);
}
};
};
// DfgVertexUnary
struct HashUnary final {
size_t operator()(const DfgVertexUnary* vtxp) const {
return operator()(vtxp->dtype(), vtxp->inputp(0));
}
size_t operator()(const DfgDataType& dtype, const DfgVertex* source0p) const {
V3Hash hash = dtype.hash();
hash += vertexHash(source0p);
return hash.value();
}
};
struct EqualUnary final {
bool operator()(const DfgVertexUnary* ap, const DfgVertexUnary* bp) const {
return operator()(ap, bp->dtype(), bp->inputp(0));
}
bool operator()(const DfgVertexUnary* vtxp, const DfgDataType& dtype,
const DfgVertex* source0p) const {
return vtxp->dtype() == dtype && vertexEqual(vtxp->inputp(0), source0p);
}
class KeyUnary final {
const DfgDataType& m_dtype;
const DfgVertex* const m_source0p;
public:
// cppcheck-suppress noExplicitConstructor
KeyUnary(const DfgDataType& dtype, DfgVertex* source0p)
: m_dtype{dtype}
, m_source0p{source0p} {}
explicit KeyUnary(const DfgVertexUnary* vtxp)
: m_dtype{vtxp->dtype()}
, m_source0p{vtxp->inputp(0)} {}
struct Hash final {
size_t operator()(const KeyUnary& key) const { //
V3Hash hash = key.m_dtype.hash();
hash += vertexHash(key.m_source0p);
return hash.value();
}
};
struct Equal final {
bool operator()(const KeyUnary& a, const KeyUnary& b) const {
return a.m_dtype == b.m_dtype && vertexEqual(a.m_source0p, b.m_source0p);
}
};
};
// DfgVertexBinary
struct HashBinary final {
size_t operator()(const DfgVertexBinary* vtxp) const {
return operator()(vtxp->dtype(), vtxp->inputp(0), vtxp->inputp(1));
}
size_t operator()(const DfgDataType& dtype, const DfgVertex* source0p,
const DfgVertex* source1p) const {
V3Hash hash = dtype.hash();
hash += vertexHash(source0p);
hash += vertexHash(source1p);
return hash.value();
}
};
struct EqualBinary final {
bool operator()(const DfgVertexBinary* ap, const DfgVertexBinary* bp) const {
return operator()(ap, bp->dtype(), bp->inputp(0), bp->inputp(1));
}
bool operator()(const DfgVertexBinary* vtxp, const DfgDataType& dtype,
const DfgVertex* source0p, const DfgVertex* source1p) const {
return vtxp->dtype() == dtype && vertexEqual(vtxp->inputp(0), source0p)
&& vertexEqual(vtxp->inputp(1), source1p);
}
class KeyBinary final {
const DfgDataType& m_dtype;
const DfgVertex* const m_source0p;
const DfgVertex* const m_source1p;
public:
KeyBinary(const DfgDataType& dtype, DfgVertex* source0p, DfgVertex* source1p)
: m_dtype{dtype}
, m_source0p{source0p}
, m_source1p{source1p} {}
explicit KeyBinary(const DfgVertexBinary* vtxp)
: m_dtype{vtxp->dtype()}
, m_source0p{vtxp->inputp(0)}
, m_source1p{vtxp->inputp(1)} {}
struct Hash final {
size_t operator()(const KeyBinary& key) const {
V3Hash hash = key.m_dtype.hash();
hash += vertexHash(key.m_source0p);
hash += vertexHash(key.m_source1p);
return hash.value();
}
};
struct Equal final {
bool operator()(const KeyBinary& a, const KeyBinary& b) const {
return a.m_dtype == b.m_dtype && vertexEqual(a.m_source0p, b.m_source0p)
&& vertexEqual(a.m_source1p, b.m_source1p);
}
};
};
// DfgVertexTernary
struct HashTernary final {
size_t operator()(const DfgVertexTernary* vtxp) const {
return operator()(vtxp->dtype(), vtxp->inputp(0), vtxp->inputp(1), vtxp->inputp(2));
}
size_t operator()(const DfgDataType& dtype, const DfgVertex* source0p,
const DfgVertex* source1p, const DfgVertex* source2p) const {
V3Hash hash = dtype.hash();
hash += vertexHash(source0p);
hash += vertexHash(source1p);
hash += vertexHash(source2p);
return hash.value();
}
class KeyTernary final {
const DfgDataType& m_dtype;
const DfgVertex* const m_source0p;
const DfgVertex* const m_source1p;
const DfgVertex* const m_source2p;
public:
KeyTernary(const DfgDataType& dtype, DfgVertex* source0p, DfgVertex* source1p,
DfgVertex* source2p)
: m_dtype{dtype}
, m_source0p{source0p}
, m_source1p{source1p}
, m_source2p{source2p} {}
explicit KeyTernary(const DfgVertexTernary* vtxp)
: m_dtype{vtxp->dtype()}
, m_source0p{vtxp->inputp(0)}
, m_source1p{vtxp->inputp(1)}
, m_source2p{vtxp->inputp(2)} {}
struct Hash final {
size_t operator()(const KeyTernary& key) const {
V3Hash hash = key.m_dtype.hash();
hash += vertexHash(key.m_source0p);
hash += vertexHash(key.m_source1p);
hash += vertexHash(key.m_source2p);
return hash.value();
}
};
struct Equal final {
bool operator()(const KeyTernary& a, const KeyTernary& b) const {
return a.m_dtype == b.m_dtype && vertexEqual(a.m_source0p, b.m_source0p)
&& vertexEqual(a.m_source1p, b.m_source1p)
&& vertexEqual(a.m_source2p, b.m_source2p);
}
};
};
struct EqualTernary final {
bool operator()(const DfgVertexTernary* ap, const DfgVertexTernary* bp) const {
return operator()(ap, bp->dtype(), bp->inputp(0), bp->inputp(1), bp->inputp(2));
}
bool operator()(const DfgVertexTernary* vtxp, const DfgDataType& dtype,
const DfgVertex* source0p, const DfgVertex* source1p,
const DfgVertex* source2p) const {
return vtxp->dtype() == dtype && vertexEqual(vtxp->inputp(0), source0p)
&& vertexEqual(vtxp->inputp(1), source1p)
&& vertexEqual(vtxp->inputp(2), source2p);
}
};
// Base class of vertex caches
class CacheBase VL_NOT_FINAL {
protected:
// These set the operands of a new vertex
@@ -183,68 +210,86 @@ class V3DfgCache final {
public:
// CacheBase does not cache anything
virtual DfgVertex* cache(DfgVertex*) { return nullptr; }
virtual void invalidate(DfgVertex*) {}
virtual void invalidate(const DfgVertex*) {}
};
template <typename T_Vertex, typename T_Hash, typename T_Equal>
template <typename T_Key, typename T_Vertex>
class Cache final : public CacheBase {
static_assert(std::is_base_of<DfgVertex, T_Vertex>::value, "T_Vertex must be a DfgVertex");
// TYPES
using Hash = typename T_Key::Hash;
using Equal = typename T_Key::Equal;
using Map = std::unordered_map<T_Key, T_Vertex*, Hash, Equal>;
// STATE
V3HashSet<T_Vertex*, T_Hash, T_Equal> m_set;
Map m_map;
// METHODS
// These return a reference to the mapped entry, inserting a nullptr if not yet exists
template <typename... T_Args>
T_Vertex*& entry(T_Args&&... args) {
const T_Key key{std::forward<T_Args>(args)...};
return m_map[key];
}
template <typename... T_Args>
typename Map::iterator find(T_Args&&... args) {
const T_Key key{std::forward<T_Args>(args)...};
return m_map.find(key);
}
public:
// Add an existing vertex to the cache. If an equivalent but different vertex exists,
// it is returned and the cache is not updated. Returns nullptr if the vertex is inserted.
// Add an existing vertex to the cache. If an equivalent exists,
// it is returned and the cache is not updated.
DfgVertex* cache(DfgVertex* vtxp) override {
UDEBUGONLY(UASSERT_OBJ(vtxp->is<T_Vertex>(), vtxp, "Vertex is wrong type"););
T_Vertex* const typedp = static_cast<T_Vertex*>(vtxp);
T_Vertex* const cachedp = *m_set.insert(typedp).first;
return cachedp != vtxp ? cachedp : nullptr;
UASSERT_OBJ(vtxp->is<T_Vertex>(), vtxp, "Vertex is wrong type");
T_Vertex*& entrypr = entry(static_cast<const T_Vertex*>(vtxp));
if (entrypr && entrypr != vtxp) return entrypr;
entrypr = static_cast<T_Vertex*>(vtxp);
return nullptr;
}
// Remove an existing vertex from the cache, if it is the cached vertex, otherwise no-op
void invalidate(DfgVertex* vtxp) override {
UDEBUGONLY(UASSERT_OBJ(vtxp->is<T_Vertex>(), vtxp, "Vertex is wrong type"););
T_Vertex* const typedp = static_cast<T_Vertex*>(vtxp);
const auto it = m_set.find(typedp);
if (it != m_set.end() && *it == typedp) m_set.erase(it);
void invalidate(const DfgVertex* vtxp) override {
UASSERT_OBJ(vtxp->is<T_Vertex>(), vtxp, "Vertex is wrong type");
const auto it = find(static_cast<const T_Vertex*>(vtxp));
if (it != m_map.end() && it->second == vtxp) m_map.erase(it);
}
// Get vertex with given operands, return nullptr if not in cache
template <typename Vertex, typename... Operands>
Vertex* get(const DfgDataType& dtype, Operands... operands) {
const auto it = m_set.find(dtype, operands...);
return it != m_set.end() ? static_cast<Vertex*>(*it) : nullptr;
const auto it = find(dtype, operands...);
return it != m_map.end() ? static_cast<Vertex*>(it->second) : nullptr;
}
// Get vertex with given operands, if does not exist, create it
// Get or create (and insert) vertex with given operands
template <typename Vertex, typename... Operands>
Vertex* getOrCreate(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype,
Operands... operands) {
const auto pair = m_set.insertLazy(dtype, operands..., [&]() -> T_Vertex* {
Vertex* const newp = new Vertex{dfg, flp, dtype};
T_Vertex*& entryr = entry(dtype, operands...);
if (!entryr) {
T_Vertex* const newp = new Vertex{dfg, flp, dtype};
setOperands(newp, operands...);
return newp;
});
T_Vertex* const vtxp = *pair.first;
UDEBUGONLY(UASSERT_OBJ(vtxp->template is<Vertex>(), vtxp, "Vertex is wrong type"););
return static_cast<Vertex*>(vtxp);
entryr = newp;
}
return static_cast<Vertex*>(entryr);
}
};
// Map from Vertex type to cache type
// clang-format off
template <typename Vertex>
using CacheType = typename V3DfgCacheType<Vertex, CacheBase,
DfgSel, /* -> */ Cache<DfgSel, HashSel, EqualSel>,
DfgVertexUnary, /* -> */ Cache<DfgVertexUnary, HashUnary, EqualUnary>,
DfgVertexBinary, /* -> */ Cache<DfgVertexBinary, HashBinary, EqualBinary>,
DfgVertexTernary, /* -> */ Cache<DfgVertexTernary, HashTernary, EqualTernary>
>::Type;
// clang-format on
using CacheType =
typename V3DfgCacheType<Vertex, CacheBase, //
DfgSel, Cache<KeySel, DfgSel>, //
DfgVertexUnary, Cache<KeyUnary, DfgVertexUnary>, //
DfgVertexBinary, Cache<KeyBinary, DfgVertexBinary>, //
DfgVertexTernary, Cache<KeyTernary, DfgVertexTernary> //
>::Type;
// STATE
DfgGraph& m_dfg; // The DfgGraph we are caching the vertices of
// The per type caches
// The per type caches
#define VERTEX_CACHE_DECLARE_CACHE(t) CacheType<t> m_cache##t;
FOREACH_DFG_VERTEX_TYPE(VERTEX_CACHE_DECLARE_CACHE)
#undef VERTEX_CACHE_DECLARE_CACHE
@@ -283,23 +328,21 @@ class V3DfgCache final {
}
public:
// Note: the cache starts out empty. If the caller wants existing vertices
// to be found, it must add them itself by calling 'cache' on each.
explicit V3DfgCache(DfgGraph& dfg)
: m_dfg{dfg} {
// Initialize the type to cache lookup table
// Initialize the type to cache lookup table
#define VERTEX_CACHE_DECLARE_CACHE_PTR(t) m_vtxType2Cachep[t::dfgType()] = &m_cache##t;
FOREACH_DFG_VERTEX_TYPE(VERTEX_CACHE_DECLARE_CACHE_PTR)
FOREACH_DFG_VERTEX_TYPE(VERTEX_CACHE_DECLARE_CACHE_PTR)
#undef VERTEX_CACHE_DECLARE_CACHE_PTR
}
// Add an existing vertex to the cache. If an equivalent (but different) already exists,
// it is returned and the cache is not updated.
DfgVertex
* cache(DfgVertex * vtxp) {
return m_vtxType2Cachep[vtxp->type()]->cache(vtxp);
// Add all operation vertices to the cache
for (DfgVertex& vtx : m_dfg.opVertices()) cache(&vtx);
}
// Add an existing vertex to the cache. If an equivalent (but different) already exists,
// it is returned and the cache is not updated.
DfgVertex* cache(DfgVertex* vtxp) { return m_vtxType2Cachep[vtxp->type()]->cache(vtxp); }
// Remove an exiting vertex, it is the cached vertex.
void invalidate(DfgVertex* vtxp) { m_vtxType2Cachep[vtxp->type()]->invalidate(vtxp); }

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