Compare commits

..
Author SHA1 Message Date
Wilson Snyder 5ed3b46dba No need to revert verilatedos.h 2026-09-15 13:40:10 -04:00
Wilson Snyder 6da2657b56 Revert "Fix wide enum names (#8344)"
This reverts commit c65f312d6e.
2026-09-15 12:22:58 -04:00
1015 changed files with 16575 additions and 50461 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@1f40c72289eff860ee54a304f1438e3cff362e0a # 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
-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.
+9 -37
View File
@@ -49,19 +49,20 @@ if(WIN32)
if(DEFINED ENV{WIN_FLEX_BISON})
set(WIN_FLEX_BISON "$ENV{WIN_FLEX_BISON}")
endif()
if(NOT WIN_FLEX_BISON)
message(
FATAL_ERROR
"Please install https://github.com/lexxmark/winflexbison and set WIN_FLEX_BISON environment variable. Please use install cmake target after a successful build."
)
elseif(IS_DIRECTORY "${WIN_FLEX_BISON}")
list(APPEND CMAKE_PREFIX_PATH "${WIN_FLEX_BISON}")
if(IS_DIRECTORY ${WIN_FLEX_BISON})
list(APPEND CMAKE_PREFIX_PATH ${WIN_FLEX_BISON})
else()
message(
FATAL_ERROR
"The path specified by WIN_FLEX_BISON environment variable is not an existing folder: ${WIN_FLEX_BISON}"
)
endif()
if(NOT WIN_FLEX_BISON)
message(
FATAL_ERROR
"Please install https://github.com/lexxmark/winflexbison and set WIN_FLEX_BISON environment variable. Please use install cmake target after a successful build."
)
endif()
set(CMAKE_CXX_STANDARD 20)
endif()
@@ -92,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)
+1 -102
View File
@@ -18,32 +18,16 @@ Verilator 5.053 devel
* 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.]
@@ -53,109 +37,24 @@ Verilator 5.053 devel
* 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]
* Fix format of signed enums (#8338). [Marco Bartoli]
Verilator 5.052 2026-09-05
+4 -7
View File
@@ -710,10 +710,8 @@ override undefine COVERAGE_BASE # Use the above variabels instead
# 'fastcov' setup
FASTCOV := nodist/fastcov.py
FASTCOV_JOBS ?= $(shell nproc)
FASTCOV_OPT := -j $(FASTCOV_JOBS)
# gcov does not free memory between files, so run it in batches via a wrapper
FASTCOV_OPT += --gcov nodist/gcov_batch
FASTCOV_OPT := -j $(shell nproc)
FASTCOV_OPT += --gcov $(GCOV)
FASTCOV_OPT += --lcov
FASTCOV_OPT += --process-gcno
FASTCOV_OPT += --branch-coverage
@@ -765,7 +763,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
@@ -787,7 +784,7 @@ $(COVERAGE_DIR)/verilator.info: $(GCNO_FILES) $(GCDA_FILES)
@echo "# fastcov: combining all .gcda files into lcov .info"
@echo "####################################################################"
@mkdir -p $(COVERAGE_DIR)
GCOV=$(GCOV) /usr/bin/time -f "That took %E" \
/usr/bin/time -f "That took %E" \
$(FASTCOV) $(FASTCOV_OPT) --output $@
# Filter combined .info file for patch coverage
@@ -797,8 +794,8 @@ $(COVERAGE_DIR)/verilator-patch.info: $(COVERAGE_DIR)/verilator.info
@echo "####################################################################"
rm -f $(COVERAGE_DIR)/empty-patch
git diff $(COVERAGE_REF_BASE) -- include src > $(COVERAGE_DIR)/filter.patch
[ -s $(COVERAGE_DIR)/filter.patch ] || touch $(COVERAGE_DIR)/empty-patch
$(FASTCOV) -C $^ --lcov -o $@ --diff-filter $(COVERAGE_DIR)/filter.patch
grep -q '^DA:' $@ || touch $(COVERAGE_DIR)/empty-patch
# Build coverage report
$(COVERAGE_DIR)/report/index.html: $(COVERAGE_DIR)/verilator$(if $(COVERAGE_REF_BASE),-patch).info
+11 -11
View File
@@ -23,8 +23,8 @@
.. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator
: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
-12
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),
@@ -96,8 +92,6 @@ CASES = {
("BlackParrot:1x1:*", 4),
("BlackParrot:2x2:*", 16),
("BlackParrot:4x4:*", 25),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("NVDLA:*", 25),
("OpenPiton:16x16:dhry", 16),
("OpenPiton:1x1:*", 4),
@@ -116,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),
@@ -150,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),
@@ -183,8 +173,6 @@ CASES_PR = {
"gcc-hier": [
("BlackParrot:1x1:*", 4),
("BlackParrot:4x4:*", 25),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("NVDLA:*", 25),
("OpenPiton:16x16:dhry", 16),
("OpenPiton:1x1:*", 4),
-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/*
-17
View File
@@ -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 -19
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
@@ -58,19 +57,16 @@ Chykon
Congcong Cai
Conor McCullough
Cookie
cynicalmindset
Dan Ruelas-Petrisko
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
@@ -83,7 +79,6 @@ Drew Risinger
Drew Taussig
Driss Hafdi
Edgar E. Iglesias
Edmund Lam
Eddie Rydell
em2machine
emmettifelts
@@ -99,7 +94,6 @@ Fan Shupei
february cozzocrea
Felix Neumärker
Felix Yan
Francesco Urbani
Frans Skarman
Fuad Ismail
Furqan Nadir
@@ -121,7 +115,6 @@ Han Qi
Harald Heckmann
Hennadii Chernyshchyk
hjsanjana
Hongseok Choi
Howard Su
Huang Rui
Huanghuang Zhou
@@ -154,7 +147,6 @@ jeffrey.song
Jens Yuechao Liu
Jeremy Bennett
Jesse Taube
Jesús Alberto López Soto
Jevin Sweval
Jiacheng Qian
Jiamin Zhu
@@ -225,7 +217,6 @@ Marshal Qiao
Martijn Wobbes
Martin Schmidt
Martin Stadler
Martin Velay
Mateusz Gancarz
Matt Stroud
Matthew Ballance
@@ -251,11 +242,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
@@ -298,7 +287,6 @@ Ryan Ziegler
Ryszard Rozak
Saksham Gupta
Samuel Riedel
Saqib Khan
Sean Cross
Sebastien Van Cauwenberghe
Secturion
@@ -309,7 +297,6 @@ Seth Pellegrino
Shashvat Prabhu
Shogo Yamazaki
Shou-Li Hsu
Shriyans Sahoo
spomatasmd
Srinivasan Venkataramanan
Stefan Wallentowitz
@@ -318,10 +305,8 @@ Steven Hugg
Stuart Morris
sumpster
Szymon Gizler
Szymon Jędras
Sören Tempel
Sumanth Kadiyala
Sunimali Rathnayake
Teng Huang
Thomas Aldrian
Thomas Brown
@@ -342,11 +327,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
@@ -362,7 +345,6 @@ Wolfgang Mayerwieser
Xi Zhang
Yan Xu
Yangyu Chen
yash
Yilin Li
Yilou Wang
Yinan Xu
@@ -381,4 +363,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
-------------------------
+2 -38
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
@@ -840,10 +813,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 +821,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
@@ -2304,7 +2268,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 +2297,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
@@ -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
-12
View File
@@ -162,18 +162,6 @@ Hierarchy blocks have some limitations, including:
- Delays are not allowed in hierarchy blocks.
- Setting type parameters of a hierarchy block is unsupported.
- A ``defparam`` setting a parameter inside a hierarchy block is
unsupported.
- String parameter values of a hierarchy block containing a newline, a
double quote, ``/*``, or whitespace followed by ``//`` are unsupported.
- Values that are equal but differ in width or signedness (``5`` and
``8'd5``), including relative to the default value, are unsupported for a
hierarchy block parameter declared without a data type.
But, the following usage is supported:
- Nested hierarchy blocks. A hierarchy block may instantiate other
+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
-7
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
@@ -722,7 +719,6 @@ deduplication
defenv
defname
defparam
dehash
demangling
dep
deparametrized
@@ -742,7 +738,6 @@ devel
difftree
dir
disambiguates
discoverability
displayb
distcc
doxygen
@@ -1139,7 +1134,6 @@ shortint
shortreal
signame
signedness
slp
sp
specparam
splitme
@@ -1264,7 +1258,6 @@ vcddiff
vcoverage
vdhotre
vec
vectorize
ventana
venv
ver
+61 -44
View File
@@ -1180,28 +1180,23 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override
} else if (formatAttr == VL_VFORMATATTR_ENUM
|| formatAttr == VL_VFORMATATTR_ENUM_SIGNED) {
// Always <= VL_QUADSIZE; emit uses non-ENUM format for wider enums
const int numericAttr = formatAttr == VL_VFORMATATTR_ENUM_SIGNED
? VL_VFORMATATTR_SIGNED
: VL_VFORMATATTR_UNSIGNED;
lbits = va_arg(ap, int);
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;
if (fmt == 'd') formatAttr = numericAttr;
thingp = const_cast<std::string*>(enump);
} else if (fmt == 'p' && widthSet && width == 0) {
output += "'h";
@@ -1695,8 +1690,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
_vl_vsss_read_str(fp, floc, fromp, fstr,
std::back_insert_iterator<std::string>{t_tmp},
"0123456789+-xXzZ?_");
t_tmp.erase(std::remove(t_tmp.begin(), t_tmp.end(), '_'), t_tmp.end());
if (t_tmp.empty()) goto done;
if (!t_tmp[0]) goto done;
if (formatAttr == VL_VFORMATATTR_SIGNED) {
QData ld = 0;
std::sscanf(t_tmp.c_str(), "%30" PRIu64, &ld);
@@ -1833,8 +1827,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;
}
@@ -2402,10 +2396,8 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
VL_ZERO_W(rbits, rwp);
switch (std::tolower(fmt)) {
case 'd': {
std::string digits{dp};
digits.erase(std::remove(digits.begin(), digits.end(), '_'), digits.end());
int64_t lld = 0;
std::sscanf(digits.c_str(), "%30" PRId64, &lld);
std::sscanf(dp, "%30" PRId64, &lld);
VL_SET_WQ(rwp, lld);
break;
}
@@ -3131,16 +3123,32 @@ 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);
}
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) {
@@ -3373,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");
@@ -3392,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; }
@@ -3461,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();
@@ -3585,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") {
@@ -4185,6 +4180,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;
@@ -4478,7 +4495,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;
}
+51 -67
View File
@@ -261,7 +261,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 +305,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 +380,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 +483,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 inline 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 +502,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 +549,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 +584,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
@@ -633,8 +607,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 +626,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.
inline 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)
@@ -829,10 +823,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 +831,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*
@@ -1341,9 +1329,8 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
}
// 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 {
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) {
@@ -1351,24 +1338,21 @@ VerilatedContext::assertOnMask(VerilatedAssertType_t types,
}
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);
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;
// 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_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)].load();
return m_s.m_assertKill[VL_CLOG2_I(mask)];
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
return (m_s.m_assertPassOnVacuous.load() & mask) != 0;
return (m_s.m_assertPassOnVacuous & 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;
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;
-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
+45 -221
View File
@@ -41,7 +41,6 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
class VerilatedCovContext;
@@ -50,15 +49,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 +99,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 +115,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 +135,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 +152,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,9 +203,6 @@ 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); }
};
//=============================================================================
@@ -303,7 +213,7 @@ protected:
/// 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
/// VlCoverCrossT owns the fixed arrays. This shared core does not allocate bin
/// storage, and its borrowed storage pointers remain valid for the instance.
class VlCoverCross VL_NOT_FINAL : public VlCoverpointIf {
@@ -315,16 +225,14 @@ protected:
uint32_t stride; // Flat-index stride
};
struct Bin final {
const uint64_t* selectionp; // Slice of the cross's selection storage
const uint64_t* selectionp; // Slice of the fixed 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
const uint32_t* wordIndicesp = nullptr; // Slice of the packed selection-word indices
};
struct Word final {
uint64_t autoExcluded = 0; // Tuples replaced by explicit bins
@@ -333,8 +241,8 @@ protected:
};
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
T* m_beginp;
T* m_endp;
public:
View(T* datap, uint64_t size)
@@ -353,7 +261,6 @@ protected:
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
};
@@ -371,7 +278,7 @@ private:
// storable anyway: m_flatCountsp 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
Dimension* m_dimensionsp = nullptr; // [m_dims], owned by VlCoverCrossT
uint32_t* m_flatCountsp = nullptr; // [m_numAutoBins] Per-bin hit counts
Explicit* m_explicitp = nullptr; // Absent for automatic-only crosses
@@ -382,7 +289,6 @@ private:
void incrementAuto(uint32_t idx) {
if (m_flatCountsp[idx]++ == 0) ++m_numCovered;
}
void incrementBin(Bin& bin);
template <bool T_RecordHits>
void incrementTuple(uint32_t idx) {
Explicit& data = *m_explicitp;
@@ -419,28 +325,20 @@ protected:
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);
// 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);
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);
void addBin(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 finalizeBins();
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.
@@ -448,7 +346,7 @@ public:
void sample(const bool* binIffs = nullptr);
// ---- VlCoverpointIf ----
// Explicit bins (including ignore/illegal) precede retained automatic bins.
// Explicit bins precede retained automatic bins; all are Normal bins.
uint32_t binCount() const override {
return hasExplicitBins()
? static_cast<uint32_t>(m_explicitp->bins.size() + m_explicitp->autoBins.size())
@@ -457,8 +355,7 @@ public:
std::string binName(uint32_t i) 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 = binCount();
}
};
@@ -507,36 +404,6 @@ public:
}
};
// 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;
//=============================================================================
@@ -561,12 +428,6 @@ class VlCovergroupInst final {
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.
@@ -593,7 +454,6 @@ public:
m_items.emplace_back(cxp);
return cxp; // borrowed by the generated class
}
VlCoverCrossDyn* addCrossDyn();
// ---- attach counting (from VlCovInstHandle) ----
void attachInc() { ++m_attachCount; }
@@ -602,49 +462,35 @@ public:
// 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();
// Sum of the instance's items' covered/total bin counts. Matches what the
// generated get_inst_coverage() computes; see foldResidue().
void coverageParts(double& covered, double& total) const {
covered = 0.0;
total = 0.0;
for (const auto& itemp : m_items) {
double c = 0.0;
double t = 0.0;
itemp->coverageParts(c, t);
covered += c;
total += t;
}
}
};
//=============================================================================
// 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.
/// not grow with churn. Weight is 1 everywhere until option.weight is plumbed.
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
double sumCoverage = 0.0; // Sigma of per-instance coverage, each in 0..100
};
//=============================================================================
@@ -660,13 +506,11 @@ class VlCovergroupType final {
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);
void foldResidue(const VlCovergroupInst* instp);
public:
// CONSTRUCTORS
@@ -682,12 +526,6 @@ public:
// 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
@@ -702,8 +540,7 @@ public:
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.
// Mean coverage over the retired instances only, in 0..100; -1.0 if none.
double retiredCoverage() const;
};
@@ -721,7 +558,6 @@ class VlCovRegistry final : public VerilatedVirtualBase {
// PRIVATE METHODS
VlCovergroupType* findType(const char* typeName) const; // nullptr if unknown
VlCovergroupType* findOrCreateType(const char* typeName);
public:
// CONSTRUCTORS
@@ -734,9 +570,6 @@ public:
// 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
@@ -762,21 +595,18 @@ public:
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) {
static void release(VlCovergroupInst* instp) {
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
@@ -785,18 +615,12 @@ public:
// 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); }
~VlCovInstHandle() { release(m_p); }
// 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; }
};
+3 -3
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
+8 -24
View File
@@ -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);
+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);
+15 -21
View File
@@ -808,7 +808,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 +843,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 +1606,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
}
+16 -200
View File
@@ -264,8 +264,7 @@ class VlRandomizer VL_NOT_FINAL {
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
@@ -492,7 +491,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 +516,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 +542,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 +570,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 +585,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 +606,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 +621,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 +651,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 +663,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 +717,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] == '\\') {
+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 -5
View File
@@ -45,11 +45,11 @@ lint_off -rule WIDTHTRUNC `VLT_UVM_FILES
// Context-sensitive waivers
lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case ({is_R, is_W})*"
lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case(orig_severity)*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_callback*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_component*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_event*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_report_object*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_sequence_item*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_callback*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_component*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_event*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_report_object*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_sequence_item*"
lint_off -rule IGNOREDRETURN `VLT_UVM_FILES -match "* uvm_core_state_callback::add*"
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (abstractions[i])*"
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (lock_list[i])*"
+5 -20
View File
@@ -38,8 +38,8 @@
// See also V3Ast::VNumRange
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,
+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)
-10
View File
@@ -32,7 +32,6 @@
#include <array>
#include <atomic>
#include <deque>
#include <initializer_list>
#include <map>
#include <memory>
#include <set>
@@ -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;
+135 -163
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; }
};
@@ -412,9 +412,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 +517,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 +542,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()];
@@ -1097,13 +1094,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 +1291,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 +1303,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 +1332,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 +1375,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 +1726,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 +1758,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;
}
@@ -2799,7 +2805,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);
@@ -2971,9 +2977,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();
}
@@ -3370,18 +3377,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 +3436,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 +3484,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 +3503,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 +3541,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 +3582,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 +3627,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 +3648,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 +3739,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 +3758,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 +3817,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 +3922,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 +3939,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 +4021,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 +4038,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 +4083,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 +4101,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 +4110,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 +4309,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 +4324,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 +4342,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 +4363,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 +4387,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 +4411,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 +4486,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 +4508,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 +4670,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);
}
+1 -5
View File
@@ -322,10 +322,8 @@
// 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
@@ -543,12 +541,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 +746,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
-36
View File
@@ -1,36 +0,0 @@
#!/bin/bash
# DESCRIPTION: Verilator: gcov wrapper for fastcov, bounding gcov memory usage
#
# Usage: GCOV=<gcov> nodist/gcov_batch [gcov options] <files>
#
# fastcov passes all coverage files of a worker to a single gcov process, but
# gcov does not free memory between input files, which can exhaust memory with
# many files. This wrapper runs the gcov in $GCOV on batches of at most 16
# files at a time instead. The output is the same as if all files were passed
# to a single gcov.
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
gcov="${GCOV:-gcov}"
batch=16
opts=()
files=()
for arg in "$@"; do
case "$arg" in
-*) opts+=("$arg") ;;
*) files+=("$arg") ;;
esac
done
# No files, e.g.: 'gcov -v'
if [ ${#files[@]} -eq 0 ]; then
exec "$gcov" "${opts[@]}"
fi
status=0
for ((i = 0; i < ${#files[@]}; i += batch)); do
"$gcov" "${opts[@]}" "${files[@]:i:batch}" || status=$?
done
exit $status
+1 -12
View File
@@ -68,7 +68,6 @@ set(HEADERS
V3Combine.h
V3Common.h
V3Const.h
V3ConstPool.h
V3Container.h
V3Control.h
V3Coverage.h
@@ -238,7 +237,6 @@ set(COMMON_SOURCES
V3Clock.cpp
V3Combine.cpp
V3Common.cpp
V3ConstPool.cpp
V3Control.cpp
V3Const__gen.cpp
V3Coverage.cpp
@@ -567,18 +565,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 -6
View File
@@ -187,6 +187,7 @@ maintainer-copy::
#### Top executable
RAW_OBJS = \
V3Const__gen.o \
V3Error.o \
V3FileLine.o \
V3Graph.o \
@@ -212,7 +213,6 @@ RAW_OBJS_PCH_ASTMT = \
V3Ast.o \
V3AstNodes.o \
V3Broken.o \
V3ConstPool.o \
V3Control.o \
V3EmitCBase.o \
V3EmitCConstPool.o \
@@ -253,7 +253,6 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Clock.o \
V3Combine.o \
V3Common.o \
V3Const__gen.o \
V3Coverage.o \
V3Covergroup.o \
V3CoverageJoin.o \
@@ -411,6 +410,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 +431,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 +439,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 +489,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:
######################################################################
+2 -2
View File
@@ -689,8 +689,8 @@ class CovergroupEventBindVisitor final : public VNVisitor {
public:
CovergroupEventBindVisitor(AstSenTree* eventp, AstClass* classp, AstVarScope* instancep)
: m_instancep{instancep} {
classp->foreachMember([&](AstClass* const, AstVar* const varp) { //
m_memberps.emplace(varp);
classp->foreachMember([&](AstClass* const, AstVar* const varp) {
if (!varp->isStatic()) m_memberps.emplace(varp);
});
iterate(eventp);
}
+18 -24
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) {
@@ -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 {
+98 -74
View File
@@ -243,6 +243,12 @@ static AstNodeExpr* sampled(AstNodeExpr* exprp) {
return new AstSampled{exprp->fileline(), exprp, exprp->dtypep(), true};
}
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"};
@@ -253,14 +259,13 @@ static AstNodeExpr* assertOnCond(FileLine* flp, VAssertType type,
VAssertDirectiveType directiveType) {
if (!v3Global.opt.assertOn()) { return new AstConst{flp, AstConst::BitFalse{}}; }
return new AstCExpr{flp, AstCExpr::Pure{},
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
}
static AstNodeExpr* assertKillGet(FileLine* flp, VAssertType type,
VAssertDirectiveType directiveType) {
return new AstCExpr{flp, AstCExpr::Pure{},
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_KILL", type, directiveType),
32};
assertCtlGetCall("ASSERT_CTL_KILL", type, directiveType), 32};
}
static string assertActionControlPrefix(VAssertDirectiveType directiveType) {
@@ -275,21 +280,19 @@ static string assertActionControlPrefix(VAssertDirectiveType directiveType) {
static AstNodeExpr* assertPassOnCond(FileLine* flp, VAssertType type,
VAssertDirectiveType directiveType, bool vacuous) {
return new AstCExpr{
flp, AstCExpr::Pure{},
assertActionControlPrefix(directiveType)
+ V3AssertCommon::assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType)
+ "))"s,
1};
return new AstCExpr{flp, AstCExpr::Pure{},
assertActionControlPrefix(directiveType)
+ assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType)
+ "))"s,
1};
}
static AstNodeExpr* assertFailOnCond(FileLine* flp, VAssertType type,
VAssertDirectiveType directiveType) {
return new AstCExpr{
flp, AstCExpr::Pure{},
assertActionControlPrefix(directiveType)
+ V3AssertCommon::assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType) + "))"s,
1};
return new AstCExpr{flp, AstCExpr::Pure{},
assertActionControlPrefix(directiveType)
+ assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType) + "))"s,
1};
}
static AstIf* newPassOnIf(FileLine* flp, AstNodeExpr* firep, AstNode* bodyp, VAssertType type,
@@ -332,7 +335,6 @@ class SvaNfaBuilder final {
bool m_isCoverSeq = false;
// Unsupported endpoint topology must reject, not ignore, or the wait hangs
bool m_isSeqEvent = false;
bool m_inSequencePrefix = false; // Prefix composition does not preserve every endpoint
struct RangeDelayRejectInfo final {
SvaStateVertex* startp = nullptr;
@@ -583,13 +585,10 @@ class SvaNfaBuilder final {
// Build NFA for an SExpr. finalCond = RHS (not yet added as a vertex).
// isTopLevelStep: marks outermost required boolean check as rejectOnFail.
// Apply a range delay `##[M:N]` to currentp. Returns true on success. On
// failure, emits a diagnostic and returns false.
bool applyRangeDelay(AstSExpr* sexprp, SvaStateVertex*& currentp,
std::vector<SvaStateVertex*>& midSources,
RangeDelayRejectInfo* rangeRejectInfop = nullptr) {
FileLine* const flp = sexprp->fileline();
AstDelay* const delayp = VN_AS(sexprp->delayp(), Delay);
AstNodeExpr* const rhsExprp = sexprp->exprp();
// failure, sets outErrorEmitted per semantic-error policy and returns false.
bool applyRangeDelay(AstDelay* delayp, AstNodeExpr* rhsExprp, SvaStateVertex*& currentp,
std::vector<SvaStateVertex*>& midSources, FileLine* flp,
bool& outErrorEmitted, RangeDelayRejectInfo* rangeRejectInfop = nullptr) {
const unsigned minDelay = getConstUInt(delayp->lhsp());
if (delayp->isUnbounded()) {
// `##[M:$]`: wait M cycles, then retain every matured attempt in a
@@ -614,42 +613,62 @@ class SvaNfaBuilder final {
}
const unsigned range = maxDelay - minDelay;
currentp = addDelayChain(currentp, minDelay, flp);
const bool multiCycleRhs = rhsExprp->isMultiCycleSva();
const AstNodeExpr* const preExprp = sexprp->preExprp();
// Prefix and nested-sequence composition can lose endpoints or their multiplicity
// required by cover sequence (IEEE 1800-2023 16.14.3), regardless of the range width.
if (m_isCoverSeq
&& (multiCycleRhs || m_inSequencePrefix
|| (preExprp && preExprp->isMultiCycleSva()))) {
// kChainLimit bounds per-attempt unrolled vertices. Above this, a
// ring buffer (constant-size state) is used instead, so the vertex
// count is O(1) in range regardless of user input; no adversarial N
// blowup is possible.
constexpr unsigned kChainLimit = 256;
// IEEE 1800-2023 16.14.3: only a small bounded range before a plain
// boolean enumerates every end-of-match below. The large-range ring uses
// first-match clearing and the nested-sequence merge collapses ends, so
// reject those for a cover sequence rather than under-count.
if (m_isCoverSeq && (range > kChainLimit || VN_IS(rhsExprp, SExpr))) {
warnEndpointUnsupported(flp, "this ranged cycle delay");
outErrorEmitted = true;
return false;
}
if (m_inSequencePrefix) {
flp->v3warn(E_UNSUPPORTED,
"Unsupported: Bounded ranged cycle delay in a sequence prefix");
return false;
}
if (multiCycleRhs) {
// Launch a candidate at every eligible tick. Candidate-local misses are not
// assertion rejects while a later position can still match.
if (range > kChainLimit) {
currentp = addDelayChain(currentp, range + 1U, flp, false,
rhsExprp->isMultiCycleSva() ? nullptr : rhsExprp);
} else if (VN_IS(rhsExprp, SExpr)) {
// Nested-SExpr RHS: merge all [M,N] positions. Candidate-local misses
// are not assertion rejects while a later position can still match.
if (rangeRejectInfop) {
const int rhsLen = fixedLength(rhsExprp);
if (rhsLen >= 0) *rangeRejectInfop = {currentp, range, rhsLen};
}
SvaStateVertex* const tailp = addDelayChain(currentp, range + 1U, flp, false);
SvaStateVertex* const mergeVtxp = scopedCreateVertex();
mergeVtxp->m_isUnbounded = true;
guardedLink(currentp, mergeVtxp, flp);
guardedLink(tailp, mergeVtxp, flp);
for (unsigned i = 0; i < range; ++i) {
SvaStateVertex* const nextVtxp = scopedCreateVertex();
guardedEdge(currentp, nextVtxp, flp);
guardedLink(nextVtxp, mergeVtxp, flp);
currentp = nextVtxp;
}
currentp = mergeVtxp;
m_inUnboundedScope = true;
} else {
// The first eligible tick is explicit. The ring retains attempts for the
// remaining ticks and exposes the final tick for rejection. Only properties
// clear on success, since cover sequence counts every endpoint.
// Pure boolean RHS: register chain. Each mid-position links to
// match (match-only); last position is the reject source.
// For cover_sequence (IEEE 1800-2023 16.14.3) the advance edge is
// unconditional so every (start, end) pair fires independently --
// dropping NOT(b) turns "first-match-wins" into "every end fires".
AstVar* const hoistVarp
= m_isCoverSeq ? nullptr : tryHoistSampled(rhsExprp, flp, range);
midSources.push_back(currentp);
currentp = addDelayChain(currentp, range + 1U, flp, false,
m_isCoverSeq ? nullptr : rhsExprp);
for (unsigned i = 0; i < range; ++i) {
SvaStateVertex* const nextVtxp = scopedCreateVertex();
if (m_isCoverSeq) {
guardedEdge(currentp, nextVtxp, flp);
} else {
AstNodeExpr* const notExprp
= new AstLogNot{flp, sampledRefOrClone(hoistVarp, rhsExprp, flp)};
guardedEdge(currentp, nextVtxp, notExprp, flp);
}
if (i < range - 1) midSources.push_back(nextVtxp);
currentp = nextVtxp;
}
}
return true;
}
@@ -668,11 +687,14 @@ class SvaNfaBuilder final {
= result.finalCondp ? sampled(result.finalCondp->cloneTreePure(false)) : nullptr;
SvaStateVertex* const successNowp = scopedCreateVertex();
guardedLink(srcp, successNowp, condp, flp);
// Retain a success through the latest deadline it can satisfy.
SvaStateVertex* const historyp
= addDelayChain(successNowp, info.range + 1U, flp, false);
guardedLink(successNowp, expiryMatchp, flp);
guardedLink(historyp, expiryMatchp, flp);
SvaStateVertex* stagep = successNowp;
guardedLink(stagep, expiryMatchp, flp);
for (unsigned i = 0; i < info.range; ++i) {
SvaStateVertex* const nextp = scopedCreateVertex();
guardedEdge(stagep, nextp, flp);
stagep = nextp;
guardedLink(stagep, expiryMatchp, flp);
}
}
SvaStateVertex* const sinkVtxp = m_graph.createStateVertex();
@@ -693,8 +715,6 @@ class SvaNfaBuilder final {
// Handle LHS (preExpr)
SvaStateVertex* currentp = entryVtxp;
if (AstNodeExpr* const preExprp = sexprp->preExprp()) {
VL_RESTORER(m_inSequencePrefix);
m_inSequencePrefix = true;
const BuildResult pre = buildExpr(preExprp, currentp, isTopLevelStep);
if (!pre.valid()) return BuildResult::fail(pre.errorEmitted); // LCOV_EXCL_LINE
if (pre.finalCondp) {
@@ -717,9 +737,10 @@ class SvaNfaBuilder final {
RangeDelayRejectInfo rangeRejectInfo;
const bool addRangeReject = isTopLevelStep && !m_inUnboundedScope;
if (delayp->isRangeDelay()) {
if (!applyRangeDelay(sexprp, currentp, rangeMidSources,
addRangeReject ? &rangeRejectInfo : nullptr)) {
return BuildResult::failWithError();
bool errorEmitted = false;
if (!applyRangeDelay(delayp, sexprp->exprp(), currentp, rangeMidSources, flp,
errorEmitted, addRangeReject ? &rangeRejectInfo : nullptr)) {
return BuildResult::fail(errorEmitted);
}
} else {
const unsigned delayCycles = getConstUInt(delayp->lhsp());
@@ -1836,7 +1857,7 @@ class SvaNfaLowering final {
};
UASSERT_OBJ(size > 0, idxExprp, "Ring size must be positive");
if (size == 1) {
VL_DO_DANGLING(idxExprp->deleteTree(), idxExprp);
idxExprp->deleteTree();
return u32Const(0);
}
// idx == size - 1 ? 0 : idx + 1
@@ -2891,7 +2912,6 @@ class AssertNfaVisitor final : public VNVisitor {
V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable names
V3UniqueNames m_disableCntNames{"__VnfaDis"}; // Disable-iff counter names
V3UniqueNames m_propTempNames{"__VnfaSampled"}; // Hoisted $sampled(propp) temps
V3UniqueNames m_failCountNames{"__VnfaRemainingFailCount"}; // Fail replay counter names
std::set<const AstProperty*> m_inliningProps; // Recursion guard for inlineNamedProperty
template <typename T_Node>
@@ -3274,12 +3294,13 @@ class AssertNfaVisitor final : public VNVisitor {
// IEEE 1800-2023 16.12 requires one action-block evaluation per failed
// thread. AstAssert handles the first, so replay the rest here.
AstVar* const remainingFailCountVarp
= new AstVar{flp, VVarType::MODULETEMP, m_failCountNames.get(""),
= new AstVar{flp, VVarType::BLOCKTEMP, "__VnfaRemainingFailCount",
m_modp->findBasicDType(VBasicDTypeKwd::UINT32)};
remainingFailCountVarp->lifetime(VLifetime::STATIC_EXPLICIT);
m_modp->addStmtsp(remainingFailCountVarp);
AstNode* const replayStmtsp = new AstAssign{
flp, new AstVarRef{flp, remainingFailCountVarp, VAccess::WRITE}, threadFailCountp};
remainingFailCountVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
AstBegin* const replayBlockp = new AstBegin{flp, "", remainingFailCountVarp, true};
replayBlockp->addStmtsp(
new AstAssign{flp, new AstVarRef{flp, remainingFailCountVarp, VAccess::WRITE},
threadFailCountp});
AstLoop* const replayLoopp = new AstLoop{flp};
replayLoopp->addStmtsp(new AstLoopTest{
flp, replayLoopp,
@@ -3293,9 +3314,9 @@ class AssertNfaVisitor final : public VNVisitor {
replayLoopp->addStmtsp(
new AstAssign{flp, new AstVarRef{flp, remainingFailCountVarp, VAccess::WRITE},
decrementedFailCountp});
replayStmtsp->addNext(replayLoopp);
replayBlockp->addStmtsp(replayLoopp);
m_modp->addStmtsp(
new AstAlways{flp, VAlwaysKwd::ALWAYS, threadFailReplaySenTreep, replayStmtsp});
new AstAlways{flp, VAlwaysKwd::ALWAYS, threadFailReplaySenTreep, replayBlockp});
}
}
@@ -3322,10 +3343,10 @@ class AssertNfaVisitor final : public VNVisitor {
// Recursively walk a consequent. Returns cycle length consumed and
// substitutes each VarRef to a captured local var with $past(rhs, K)
// (or rhs inline when K == 0). Reports E_UNSUPPORTED on non-constant
// delays or composite sequence operators and returns -1 on failure.
int
walkSubstituteMatchItems(AstNodeExpr* nodep, unsigned K,
const std::unordered_map<const AstVar*, AstNodeExpr*>& matchItems) {
// delays or composite sequence operators.
int walkSubstituteMatchItems(AstNodeExpr* nodep, unsigned K,
const std::unordered_map<const AstVar*, AstNodeExpr*>& matchItems,
bool& errorEmitted) {
if (AstSExpr* const sexprp = VN_CAST(nodep, SExpr)) {
// IEEE 1800-2023 16.9.2: cycle_delay's lhsp is a constant_expression
// and the delay form in a sequence is always `##N`, folded by
@@ -3338,23 +3359,25 @@ class AssertNfaVisitor final : public VNVisitor {
sexprp->v3warn(E_UNSUPPORTED, "Unsupported: property local variable used across "
"non-constant cycle delay in consequent"
" (IEEE 1800-2023 16.10)");
errorEmitted = true;
return -1;
}
const unsigned delayCycles = VN_AS(delayp->lhsp(), Const)->toUInt();
int preLen = 0;
if (AstNodeExpr* const prep = sexprp->preExprp()) {
preLen = walkSubstituteMatchItems(prep, K, matchItems);
if (preLen < 0) return -1;
preLen = walkSubstituteMatchItems(prep, K, matchItems, errorEmitted);
if (errorEmitted) return -1;
}
const int bodyLen
= walkSubstituteMatchItems(sexprp->exprp(), K + preLen + delayCycles, matchItems);
if (bodyLen < 0) return -1;
const int bodyLen = walkSubstituteMatchItems(sexprp->exprp(), K + preLen + delayCycles,
matchItems, errorEmitted);
if (errorEmitted) return -1;
return preLen + delayCycles + bodyLen;
}
if (nodep->isMultiCycleSva()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: property local variable used across "
"composite sequence operator in consequent"
" (IEEE 1800-2023 16.10)");
errorEmitted = true;
return -1;
}
std::vector<AstVarRef*> refs;
@@ -3386,11 +3409,12 @@ class AssertNfaVisitor final : public VNVisitor {
matchItems[lhsRefp->varp()] = assignp->rhsp();
}
const unsigned startK = parts.isOverlapped ? 0 : 1;
const int length = walkSubstituteMatchItems(seqBodyp, startK, matchItems);
bool errorEmitted = false;
walkSubstituteMatchItems(seqBodyp, startK, matchItems, errorEmitted);
// Match-item substitution / strip mutates ancestor purity. Release
// builds don't auto-clear caches on edits, so refresh here.
VIsCached::clearCacheTree();
if (length < 0) return true;
if (errorEmitted) return true;
AstNodeExpr* const antBoolp = exprStmtp->resultp()->unlinkFrBack();
exprStmtp->replaceWith(antBoolp);
VL_DO_DANGLING(pushDeletep(exprStmtp), exprStmtp);
+60 -261
View File
@@ -32,36 +32,6 @@
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Clocking blocks of clocking items
class AssertPreClockingVisitor final : public VNVisitorConst {
// Captures the clocking block of each clocking item before AssertPreVisitor, which needs it
// at synchronous drives, often visited before the clocking block, e.g. in a parent module
// NODE STATE
// AstClockingItem::user3p() // AstClocking*. Clocking block of the item, user3 claimed by
// // AssertPreVisitor
// VISITORS
void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); }
void visit(AstNodeModule* nodep) override { iterateAndNextConstNull(nodep->stmtsp()); }
void visit(AstGenBlock* nodep) override { iterateAndNextConstNull(nodep->itemsp()); }
void visit(AstClocking* nodep) override {
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
if (AstClockingItem* const citemp = VN_CAST(itemp, ClockingItem)) {
citemp->user3p(nodep);
}
}
}
// Clocking blocks are only in modules and their generate blocks
void visit(AstNode*) override {}
public:
// CONSTRUCTORS
explicit AssertPreClockingVisitor(AstNetlist* nodep) { iterateConst(nodep); }
~AssertPreClockingVisitor() override = default;
};
//######################################################################
// Assert class functions
@@ -70,21 +40,10 @@ class AssertPreVisitor final : public VNVisitor {
// Eventually inlines calls to sequences, properties, etc.
// We're not parsing the tree, or anything more complicated.
private:
// TYPES
struct SynchDrive final {
AstClockingItem* itemp; // Clocking item of the driven clockvar
AstNodeExpr* refp; // Reference to the driven clockvar
};
// NODE STATE
// AstClockingItem::user1p() // AstVar*. varp() of ClockingItem after unlink
// AstClockingItem::user2p() // AstVar*. Flag set by drives of output clockvar
// AstClockingItem::user3p() // AstClocking*. Clocking block of the item, captured
// // by AssertPreClockingVisitor
// AstPExpr::user1() // bool. Created from AstUntil
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
const VNUser3InUse m_inuser3;
// STATE
// Current context:
AstNetlist* const m_netlistp = nullptr; // Current netlist
@@ -109,123 +68,17 @@ 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
V3UniqueNames m_drivenNames{"__VclockingDriven"}; // Clockvar drive flag name generator
bool m_inAssign = false; // True if in an AssignNode
bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS
bool m_inSynchDrive = false; // True if in synchronous drive
bool m_inConcatAssign = false; // True if in assignment to a concatenation
std::vector<SynchDrive> m_drives; // Clockvars written by the synchronous drive
bool m_hasCycleDelay = false; // True if node has cycle delay beneath
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
std::vector<AstSequence*> m_seqsToCleanup; // Sequences to clean up after traversal
// METHODS
// Flag set by each drive of an output clockvar, added to the module with its clocking item
AstVar* getCreateDrivenVarp(AstClockingItem* itemp) {
if (!itemp->user2p()) {
AstVar* const varp = new AstVar{itemp->fileline(), VVarType::MODULETEMP,
m_drivenNames.get(itemp), itemp->findBitDType()};
varp->lifetime(VLifetime::STATIC_EXPLICIT);
itemp->user2p(varp);
}
return VN_AS(itemp->user2p(), Var);
}
// Reference to a variable in the scope of a driven clockvar, made like the clockvar's
static AstNodeExpr* newDriveRefp(FileLine* flp, const SynchDrive& drive, AstVar* varp,
const VAccess& access) {
if (const AstVarXRef* const xrefp = VN_CAST(drive.refp, VarXRef)) {
return new AstVarXRef{flp, varp, xrefp->dotted(), access};
} else if (const AstMemberSel* const selp = VN_CAST(drive.refp, MemberSel)) {
// The interface expression is evaluated again, so it must not have side effects;
// cloneTreePure warns if it has any
AstMemberSel* const newSelp
= new AstMemberSel{flp, selp->fromp()->cloneTreePure(false), varp};
newSelp->access(access);
return newSelp;
}
return new AstVarRef{flp, varp, access};
}
// Assignment setting the drive flag of a driven clockvar
AstAssign* newDrivenSetp(FileLine* flp, const SynchDrive& drive) {
AstVar* const varp = getCreateDrivenVarp(drive.itemp);
AstAssign* const setp = new AstAssign{flp, newDriveRefp(flp, drive, varp, VAccess::WRITE),
new AstConst{flp, AstConst::BitTrue{}}};
setp->user1(true);
return setp;
}
// Clocking block of a clocking item
static AstClocking* clockingOf(const AstClockingItem* itemp) {
UASSERT_OBJ(itemp->user3p(), itemp, "Clocking item not captured in a clocking block");
return VN_AS(itemp->user3p(), Clocking);
}
// Clocking event of a driven clockvar, referenced from the drive like the clockvar, or
// nullptr if unsupported, which is reported on the drive's cycle delay
AstSenItem* newDriveSensesp(AstDelay* delayp, const SynchDrive& drive) {
AstSenItem* const origp = clockingOf(drive.itemp)->sensesp();
if (VN_IS(drive.refp, VarRef)) return origp->cloneTree(false);
if (origp->exists([](const AstVarXRef*) { return true; })) {
// The reference is relative to the clocking block
delayp->v3warn(E_UNSUPPORTED, "Unsupported: cycle delay in synchronous drive to"
" clockvar in another scope, whose clocking event"
" has a hierarchical reference");
return nullptr;
}
const AstMemberSel* const selp = VN_CAST(drive.refp, MemberSel);
// Processes outside classes evaluate their events also when not waiting on them,
// possibly before the interface reference is set, so the event is guarded against a
// null reference, which needs an integral value; scheduling does not guard it
const bool guardNull = selp && !VN_IS(m_modp, Class);
if (guardNull && !origp->sensp()->dtypep()->skipRefp()->isIntegralOrPacked()) {
delayp->v3warn(E_UNSUPPORTED, "Unsupported: cycle delay in synchronous drive through"
" an interface reference outside a class, to clocking"
" block with non-integral clocking event");
return nullptr;
}
// Interface declaring the clocking block, if driven through an interface reference
AstIface* const ifacep
= selp ? VN_AS(selp->fromp()->dtypep()->skipRefp(), IfaceRefDType)->ifaceViaCellp()
: nullptr;
AstSenItem* const sensesp = origp->cloneTree(false);
std::vector<AstVarRef*> refps;
sensesp->foreach([&](AstVarRef* refp) {
if (!refp->classOrPackagep()) refps.push_back(refp);
});
for (AstVarRef* const refp : refps) {
AstVar* const varp = refp->varp();
if (ifacep) {
// Mark the variable as read through an interface reference, as V3Width does for
// a member select. The mark is the interface declaring the variable, whose
// instances all share it, so drives through references to any instances agree.
UASSERT_OBJ(!varp->sensIfacep() || varp->sensIfacep() == ifacep, refp,
"Variable read through references to different interfaces");
varp->sensIfacep(ifacep);
}
refp->replaceWith(newDriveRefp(refp->fileline(), drive, varp, refp->access()));
VL_DO_DANGLING(pushDeletep(refp), refp);
}
if (guardNull) guardNullIface(sensesp, selp->fromp());
return sensesp;
}
// Make an integral clocking event through an interface reference false while the reference
// is null
static void guardNullIface(AstSenItem* sensesp, AstNodeExpr* fromp) {
FileLine* const flp = sensesp->fileline();
AstNodeExpr* const sensp = sensesp->sensp()->unlinkFrBack();
sensesp->sensp(new AstCond{flp, newNotNullp(flp, fromp), sensp,
new AstConst{flp, AstConst::DTyped{}, sensp->dtypep()}});
if (AstNodeExpr* const condp = sensesp->condp()) {
condp->unlinkFrBack();
sensesp->condp(new AstLogAnd{flp, newNotNullp(flp, fromp), condp});
}
}
static AstNodeExpr* newNotNullp(FileLine* flp, AstNodeExpr* fromp) {
return new AstNeq{flp, fromp->cloneTreePure(false), new AstConst{flp, AstConst::Null{}}};
}
static void checkSamplingFuncDType(AstNodeExpr* nodep, const AstNode* exprp) {
const AstNodeDType* const dtypep = exprp->dtypep()->skipRefp();
if (!dtypep->isIntegralOrPacked()) {
@@ -419,21 +272,13 @@ private:
// It has to be converted to a list of ModportClockingVarRefs,
// because clocking blocks are removed in this pass
for (AstNode* itemp = nodep->clockingp()->itemsp(); itemp; itemp = itemp->nextp()) {
if (AstClockingItem* const citemp = VN_CAST(itemp, ClockingItem)) {
if (const AstClockingItem* citemp = VN_CAST(itemp, ClockingItem)) {
if (AstVar* const varp
= citemp->varp() ? citemp->varp() : VN_AS(citemp->user1p(), Var)) {
AstModportVarRef* const modVarp = new AstModportVarRef{
nodep->fileline(), varp->name(), citemp->direction()};
modVarp->varp(varp);
nodep->addNextHere(modVarp);
if (citemp->direction().isOutput()) {
// Drives through the modport set the drive flag
AstVar* const drivenVarp = getCreateDrivenVarp(citemp);
AstModportVarRef* const drivenRefp = new AstModportVarRef{
nodep->fileline(), drivenVarp->name(), VDirection::OUTPUT};
drivenRefp->varp(drivenVarp);
nodep->addNextHere(drivenRefp);
}
}
}
}
@@ -446,8 +291,6 @@ private:
// Unused item
return;
}
// Flag set by drives of an output clockvar, possibly visited before this item
if (nodep->direction().isOutput()) m_modp->addStmtsp(getCreateDrivenVarp(nodep));
FileLine* const flp = nodep->fileline();
V3Const::constifyEdit(nodep->skewp());
if (!VN_IS(nodep->skewp(), Const)) {
@@ -474,26 +317,32 @@ private:
AstInitialStatic* const initClockvarp = new AstInitialStatic{
flp, new AstAssign{flp, skewedWriteRefp, exprp->cloneTreePure(false)}};
m_modp->addStmtsp(initClockvarp);
// Each drive sets a flag, so the signal is also assigned when driven with the same
// value again, e.g. after another assignment to the signal (IEEE 1800-2023 14.16.2)
AstVar* const drivenVarp = getCreateDrivenVarp(nodep);
m_modp->addStmtsp(new AstInitialStatic{
flp, new AstAssign{flp, new AstVarRef{flp, drivenVarp, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}}});
// Assign the clockvar to the actual var if it was driven
// A var to keep the previous value of the clockvar
AstVar* const prevVarp = new AstVar{
flp, VVarType::MODULETEMP, "__Vclocking_prev__" + varp->name(), exprp->dtypep()};
prevVarp->lifetime(VLifetime::STATIC_EXPLICIT);
AstInitialStatic* const initPrevClockvarp = new AstInitialStatic{
flp, new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
skewedReadRefp->cloneTreePure(false)}};
m_modp->addStmtsp(prevVarp);
m_modp->addStmtsp(initPrevClockvarp);
// Assign the clockvar to the actual var; only do it if the clockvar's value has
// changed
AstAssign* const assignp
= new AstAssign{flp, exprp->cloneTreePure(false), skewedReadRefp};
AstIf* const ifp
= new AstIf{flp, new AstVarRef{flp, drivenVarp, VAccess::READ},
new AstAssign{flp, new AstVarRef{flp, drivenVarp, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}}};
ifp->addThensp(assignp);
= new AstIf{flp,
new AstNeq{flp, new AstVarRef{flp, prevVarp, VAccess::READ},
skewedReadRefp->cloneTreePure(false)},
assignp};
ifp->addThensp(new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
skewedReadRefp->cloneTree(false)});
if (skewp->isZero()) {
// Drive the var in Re-NBA (IEEE 1800-2023 14.16)
AstSenTree* senTreep
= new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)};
senTreep->addSensesp(new AstSenItem{
flp, VEdgeType::ET_CHANGED, new AstVarRef{flp, drivenVarp, VAccess::READ}});
senTreep->addSensesp(
new AstSenItem{flp, VEdgeType::ET_CHANGED, skewedReadRefp->cloneTree(false)});
AstCMethodHard* const trigp = new AstCMethodHard{
nodep->fileline(),
new AstVarRef{flp, m_clockingp->ensureEventp(), VAccess::READ},
@@ -598,12 +447,12 @@ private:
FileLine* const flp = nodep->fileline();
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
const AstConst* const constp = VN_CAST(valuep, Const);
if (!constp && !m_inSynchDrive) {
if (!constp) {
// V3AssertNfa handles non-const delays before this pass and
// replaces the property; this branch should never be reached.
nodep->v3fatalSrc("Non-constant cycle delay in assertion: "
"should have been caught by V3AssertNfa");
} else if (constp && constp->isZero()) {
} else if (constp->isZero()) {
VL_DO_DANGLING(pushDeletep(valuep), valuep);
if (m_inSynchDrive) {
// ##0 has no effect in synchronous drives (IEEE 1800-2023 14.11)
@@ -640,36 +489,20 @@ private:
return;
}
AstSenItem* sensesp = nullptr;
if (!m_drives.empty()) {
if (m_inConcatAssign) {
// Also the clockvars may be of different clocking blocks
nodep->v3error("Cycle delays cannot be used in synchronous drives to a"
" concatenation (IEEE 1800-2023 14.16)");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
VL_DO_DANGLING(pushDeletep(valuep), valuep);
return;
}
// Count the cycles of the driven clockvar's clocking block (IEEE 1800-2023 14.16)
sensesp = newDriveSensesp(nodep, m_drives.front());
if (!sensesp) {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
VL_DO_DANGLING(pushDeletep(valuep), valuep);
return;
}
} else if (!m_defaultClockingp) {
if (!m_defaultClockingp) {
if (!m_pexprp) {
nodep->v3error("Usage of cycle delays requires default clocking"
" (IEEE 1800-2023 14.11)");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
VL_DO_DANGLING(pushDeletep(valuep), valuep);
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
VL_DO_DANGLING(valuep->deleteTree(), valuep);
return;
}
sensesp = m_senip->cloneTree(false);
sensesp = m_senip;
} else {
sensesp = m_defaultClockingp->sensesp()->cloneTree(false);
sensesp = m_defaultClockingp->sensesp();
}
AstEventControl* const controlp
= new AstEventControl{nodep->fileline(), new AstSenTree{flp, sensesp}, nullptr};
AstEventControl* const controlp = new AstEventControl{
nodep->fileline(), new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr};
const std::string delayName = m_cycleDlyNames.get(nodep);
AstNodeExpr* throughoutp
= nodep->throughoutp() ? nodep->throughoutp()->unlinkFrBack() : nullptr;
@@ -678,15 +511,6 @@ private:
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true};
// A non-constant delay of a synchronous drive is counted at runtime, waiting for none if
// zero, like a constant '##0' in a synchronous drive. The count is integral of 64 bits,
// as V3Width makes delays, or real, which is rounded (IEEE 1800-2023 6.12.1)
if (valuep->isDouble()) {
valuep = new AstRToIRoundS{flp, valuep};
valuep->dtypeFrom(cntVarp);
} else {
valuep = new AstSel{flp, valuep, 0, cntVarp->width()};
}
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
// Throughout: create flag tracking whether condition held every tick
@@ -738,7 +562,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) {
@@ -795,10 +620,7 @@ private:
nodep->v3error("Only non-blocking assignments can write "
"to clockvars (IEEE 1800-2023 14.16)");
}
if (m_inAssign) {
m_inSynchDrive = true;
m_drives.push_back({itemp, nodep});
}
if (m_inAssign) m_inSynchDrive = true;
} else if (itemp->direction() == VDirection::INPUT) {
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
}
@@ -819,10 +641,7 @@ private:
nodep->v3error("Only non-blocking assignments can write "
"to clockvars (IEEE 1800-2023 14.16)");
}
if (m_inAssign) {
m_inSynchDrive = true;
m_drives.push_back({itemp, nodep});
}
if (m_inAssign) m_inSynchDrive = true;
} else if (itemp->direction() == VDirection::INPUT) {
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
}
@@ -833,44 +652,22 @@ private:
if (nodep->user1()) return;
VL_RESTORER(m_inAssign);
VL_RESTORER(m_inSynchDrive);
VL_RESTORER(m_inConcatAssign);
VL_RESTORER_CLEAR(m_drives);
m_inAssign = true;
m_inSynchDrive = false;
{
// The braces of a concatenation are a replication, by one
const AstNodeExpr* lhsp = nodep->lhsp();
if (const AstReplicate* const repp = VN_CAST(lhsp, Replicate)) lhsp = repp->srcp();
m_inConcatAssign = VN_IS(lhsp, Concat);
}
{
VL_RESTORER(m_inAssignDlyLhs);
m_inAssignDlyLhs = VN_IS(nodep, AssignDly);
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);
nodep->replaceWith(assignp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
nodep = assignp;
}
// Note the drives when they take effect, after the cycle delay if any. Each clockvar
// of the LHS is driven, e.g. in a concatenation which other simulators accept, although
// IEEE 1800-2023 14.16 does not allow it
AstNode* setsp = nullptr;
for (const SynchDrive& drive : m_drives) {
setsp = AstNode::addNext(setsp, newDrivenSetp(nodep->fileline(), drive));
}
if (!setsp) return;
if (AstBegin* const beginp = VN_CAST(nodep->timingControlp(), Begin)) {
beginp->addStmtsp(setsp);
} else {
nodep->addNextHere(setsp);
}
}
void visit(AstAlways* nodep) override {
@@ -1081,7 +878,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);
@@ -1110,7 +907,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);
@@ -1276,13 +1073,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});
@@ -1496,7 +1295,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
@@ -1570,9 +1369,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};
@@ -1580,9 +1380,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,
@@ -1596,7 +1397,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{}}});
@@ -1628,7 +1429,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);
@@ -1659,7 +1460,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}});
@@ -1748,7 +1549,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
@@ -1777,7 +1578,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);
@@ -1787,7 +1587,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);
}
@@ -1823,7 +1623,6 @@ public:
// CONSTRUCTORS
explicit AssertPreVisitor(AstNetlist* nodep)
: m_netlistp{nodep} {
{ AssertPreClockingVisitor{nodep}; }
// Process
iterate(nodep);
// Fix up varref names
+1 -5
View File
@@ -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
@@ -365,7 +361,7 @@ string AstNode::vpiName(const string& namein) {
string AstNode::prettyTypeName() const {
if (name() == "") return typeName();
return std::string{typeName()} + " '" + prettyNameMsg() + "'";
return std::string{typeName()} + " '" + prettyName() + "'";
}
//######################################################################
+2 -7
View File
@@ -56,7 +56,6 @@
// Forward declarations
class V3Graph;
class ExecMTask;
class VIfaceCaptureTag;
//######################################################################
@@ -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;
@@ -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)
@@ -670,10 +668,7 @@ public:
static string encodeNumber(int64_t num); // Encode number into internal C representation
static string vcdName(const string& namein); // Name for printing out to vcd files
string prettyName() const { return prettyName(name()); }
// Name for messages to the user, by default prettyName(), but friendlier for some nodes,
// such as a specialized class, whose name() is internal
virtual string prettyNameMsg() const { return prettyName(); }
string prettyNameQ() const { return "'"s + prettyNameMsg() + "'"; } // Quoted, for messages
string prettyNameQ() const { return prettyNameQ(name()); }
string verilogName() const { return vpiName(origName()); } // Decoded original Verilog name
// "VARREF" for error messages (NOT dtype's pretty name)
string prettyTypeName() const;
+148 -248
View File
@@ -75,11 +75,11 @@ public:
};
enum en m_e;
const char* ascii() const {
static constexpr const char* const names[] = {"RD", "WR", "RW", "--"};
static const char* const names[] = {"RD", "WR", "RW", "--"};
return names[m_e];
}
const char* arrow() const {
static constexpr const char* const names[] = {"[RV] <-", "[LV] =>", "[LRV] <=>", "--"};
static const char* const names[] = {"[RV] <-", "[LV] =>", "[LRV] <=>", "--"};
return names[m_e];
}
VAccess()
@@ -119,7 +119,7 @@ public:
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
static constexpr const char* const names[]
static const char* const names[]
= {"always", "always_ff", "always_latch", "always_comb", "cont_assign"};
return names[m_e];
}
@@ -160,18 +160,18 @@ public:
constexpr operator en() const { return m_e; }
const char* ascii() const {
// IEEE 1800-2023 20.11
static constexpr const char* const names[] = {"",
"",
"",
"$asserton",
"$assertoff",
"$assertkill",
"$assertpasson",
"$assertpassoff",
"$assertfailon",
"$assertfailoff",
"$assertnonvacuouson",
"$assertvacuousoff"};
static const char* const names[] = {"",
"",
"",
"$asserton",
"$assertoff",
"$assertkill",
"$assertpasson",
"$assertpassoff",
"$assertfailon",
"$assertfailoff",
"$assertnonvacuouson",
"$assertvacuousoff"};
return names[m_e];
}
};
@@ -311,7 +311,7 @@ public:
constexpr VPropStrength(en _e)
: m_e{_e} {}
const char* ascii() const {
static constexpr const char* const names[] = {"default", "weak", "strong"};
static const char* const names[] = {"default", "weak", "strong"};
return names[m_e];
}
};
@@ -379,7 +379,7 @@ public:
enum en m_e;
const char* ascii() const {
// clang-format off
static constexpr const char* const names[] = {
static const char* const names[] = {
"%E-AT",
"DIM_BITS", "DIM_BITS_OR_NUMBER", "DIM_DIMENSIONS",
"DIM_HIGH", "DIM_INCREMENT", "DIM_LEFT",
@@ -520,69 +520,69 @@ public:
};
enum en m_e;
const char* ascii() const VL_MT_SAFE {
static constexpr const char* const names[] = {"%E-unk",
"bit",
"byte",
"chandle",
"void",
"event",
"int",
"integer",
"logic",
"longint",
"real",
"shortint",
"time",
"string",
"untyped",
"VerilatedScope*",
"char*",
"VlMTaskState",
"VlDelayScheduler",
"VlTriggerScheduler",
"VlDynamicTriggerScheduler",
"VlFork",
"VlProcessRef",
"VlRandomizer",
"VlStdRandomizer",
"VlCovInstHandle",
"IData",
"QData",
"LOGIC_IMPLICIT",
" MAX"};
static const char* const names[] = {"%E-unk",
"bit",
"byte",
"chandle",
"void",
"event",
"int",
"integer",
"logic",
"longint",
"real",
"shortint",
"time",
"string",
"untyped",
"VerilatedScope*",
"char*",
"VlMTaskState",
"VlDelayScheduler",
"VlTriggerScheduler",
"VlDynamicTriggerScheduler",
"VlFork",
"VlProcessRef",
"VlRandomizer",
"VlStdRandomizer",
"VlCovInstHandle",
"IData",
"QData",
"LOGIC_IMPLICIT",
" MAX"};
return names[m_e];
}
const char* dpiType() const {
static constexpr const char* const names[] = {"%E-unk",
"svBit",
"char",
"void*",
"void",
"char",
"int",
"%E-integer",
"svLogic",
"long long",
"double",
"short",
"%E-time",
"const char*",
"%E-untyped",
"dpiScope",
"const char*",
"%E-mtaskstate",
"%E-dly-sched",
"%E-trig-sched",
"%E-dyn-sched",
"%E-fork",
"%E-proc-ref",
"%E-rand-gen",
"%E-stdrand-gen",
"%E-cg-insthandle",
"IData",
"QData",
"%E-logic-implct",
" MAX"};
static const char* const names[] = {"%E-unk",
"svBit",
"char",
"void*",
"void",
"char",
"int",
"%E-integer",
"svLogic",
"long long",
"double",
"short",
"%E-time",
"const char*",
"%E-untyped",
"dpiScope",
"const char*",
"%E-mtaskstate",
"%E-dly-sched",
"%E-trig-sched",
"%E-dyn-sched",
"%E-fork",
"%E-proc-ref",
"%E-rand-gen",
"%E-stdrand-gen",
"%E-cg-insthandle",
"IData",
"QData",
"%E-logic-implct",
" MAX"};
return names[m_e];
}
static void selfTest() {
@@ -694,7 +694,7 @@ public:
const char* traceSigType() const {
// VerilatedTraceSigType to used in trace signal declaration
static constexpr const char* const lut[] = {
static const char* const lut[] = {
/* UNKNOWN: */ "", // Should not be traced
/* BIT: */ "BIT",
/* BYTE: */ "BYTE",
@@ -754,7 +754,7 @@ public:
explicit VBoolOrUnknown(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
const char* ascii() const {
static constexpr const char* const names[] = {"FALSE", "TRUE", "UNK"};
static const char* const names[] = {"FALSE", "TRUE", "UNK"};
return names[m_e];
}
bool isKnown() const { return m_e != BU_UNKNOWN; }
@@ -798,11 +798,11 @@ public:
return m_e;
}
const char* ascii() const {
static constexpr const char* const names[] = {"", "VL_LIKELY", "VL_UNLIKELY"};
static const char* const names[] = {"", "VL_LIKELY", "VL_UNLIKELY"};
return names[m_e];
}
const char* asciiShort() const {
static constexpr const char* const names[] = {"", "L", "!L"};
static const char* const names[] = {"", "L", "!L"};
return names[m_e];
}
};
@@ -874,56 +874,18 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
macro(COVERGROUP_ADD_BIN, "addBin", false, "r+") \
macro(COVERGROUP_ADD_COVERPOINT, "addCoverpoint", false, "") \
macro(COVERGROUP_ADD_CROSS, "addCross", false, "") \
macro(COVERGROUP_ADD_CROSS_DYN, "addCrossDyn", false, "") \
macro(COVERGROUP_ADD_NUMBERED_NAMER, "addNumberedNamer", false, "r+") \
macro(COVERGROUP_ADD_SINGLE_NAMER, "addSingleNamer", false, "r+") \
macro(COVERGROUP_ATTACH, "attach", false, "r") \
macro(COVERGROUP_CLEAR_HIT_LIST, "clearHitList", false, "") \
macro(COVERGROUP_COVERAGE, "coverage", false, "") \
macro(COVERGROUP_COVERAGE_PARTS, "coverageParts", false, "TODO") \
macro(COVERGROUP_FINALIZE_BINS, "finalizeBins", false, "") \
macro(COVERGROUP_INCREMENT_BIN, "incrementBin", false, "r") \
macro(COVERGROUP_INIT, "init", false, "r+") \
macro(COVERGROUP_INST_P, "p", PURE, "") \
macro(COVERGROUP_LEND_WEIGHT, "lendWeight", false, "rr") \
macro(COVERGROUP_RECORD_HIT, "recordHit", false, "r") \
macro(COVERGROUP_REGISTER_BINS, "registerBins", false, "rrrr") \
macro(COVERGROUP_REGISTER_BINS, "registerBins", false, "rr") \
macro(COVERGROUP_SAMPLE, "sample", false, "") \
macro(COVERGROUP_SAMPLE_IFFS, "sample", false, "r") \
macro(COVERGROUP_SELECT_ALL, "selectAll", false, "") \
macro(COVERGROUP_SELECT_AND, "selectAnd", false, "") \
macro(COVERGROUP_SELECT_BIN, "selectBin", false, "r+") \
macro(COVERGROUP_SELECT_DIM, "selectDim", false, "r+") \
macro(COVERGROUP_SELECT_DIM_END, "selectDimEnd", false, "") \
macro(COVERGROUP_SELECT_OR, "selectOr", false, "") \
macro(COVERGROUP_SELECT_RANGE, "selectRange", false, "rr") \
macro(COVERGROUP_SELECT_RANGE_W, "selectRangeW", false, "rr") \
macro(COVERGROUP_SIZED_END, "sizedEnd", PURE, "r") \
macro(COVERGROUP_SIZED_FINISH, "sizedFinish", false, "r+") \
macro(COVERGROUP_SIZED_FIRST, "sizedFirst", PURE, "r") \
macro(COVERGROUP_SIZED_RANGE, "sizedRange", false, "rr") \
macro(COVERGROUP_SIZED_RANGE_W, "sizedRangeW", false, "rr") \
macro(COVERGROUP_SIZED_SAMPLE, "sizedSample", false, "rrr") \
macro(COVERGROUP_SIZED_SAMPLE_W, "sizedSampleW", false, "rrr") \
macro(COVERGROUP_TYPE_COVERAGE, "typeCoverage", false, "rrr") \
macro(COVERGROUP_VALUE_EXCLUDED, "valueExcluded", PURE, "r") \
macro(COVERGROUP_VALUE_EXCLUDED_W, "valueExcludedW", PURE, "r") \
macro(COVERGROUP_VALUE_FINALIZE, "valueFinalize", false, "") \
macro(COVERGROUP_VALUE_PATTERNS, "valuePatterns", false, "r") \
macro(COVERGROUP_VALUE_RANGES, "valueRanges", false, "r") \
macro(COVERGROUP_VALUE_RELEASE, "valueRelease", false, "") \
macro(COVERGROUP_VALUE_RUNS, "valueRuns", false, "r") \
macro(COVERGROUP_VALUE_TRANSITIONS, "valueTransitions", false, "r") \
macro(COVERGROUP_VALUE_TYPE, "valueType", false, "rr") \
macro(COVERGROUP_WEIGHT, "weight", false, "rr") \
macro(COVERGROUP_WITH_BEGIN, "withBegin", false, "rr") \
macro(COVERGROUP_WITH_FINISH, "withFinish", false, "r+") \
macro(COVERGROUP_WITH_HI, "withHi", false, "") \
macro(COVERGROUP_WITH_HI_W, "withHiW", false, "w") \
macro(COVERGROUP_WITH_LO, "withLo", false, "") \
macro(COVERGROUP_WITH_LO_W, "withLoW", false, "w") \
macro(COVERGROUP_WITH_NEXT, "withNext", false, "") \
macro(COVERGROUP_WITH_RUN, "withRun", false, "rr") \
macro(COVERGROUP_WITH_RUN_W, "withRunW", false, "rr") \
macro(DYN_AT_WRITE_APPEND, "atWriteAppend", false, "r") \
macro(DYN_AT_WRITE_APPEND_BACK, "atWriteAppendBack", false, "r") \
macro(DYN_CLEAR, "clear", false, "") \
@@ -966,11 +928,10 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
macro(RANDOMIZER_DISABLE_SOFT, "disable_soft", false, "r") \
macro(RANDOMIZER_HARD, "hard", false, "r+") \
macro(RANDOMIZER_SOFT, "soft", false, "rrrr") \
macro(RANDOMIZER_UNIQUE, "rand_unique", false, "rr") \
macro(RANDOMIZER_UNIQUE, "rand_unique", false, "r") \
macro(RANDOMIZER_MARK_RANDC, "markRandc", false, "r") \
macro(RANDOMIZER_SOLVE_BEFORE, "solveBefore", false, "rr") \
macro(RANDOMIZER_PIN_VAR, "pin_var", false, "rrr") \
macro(RANDOMIZER_UPDATE_VAR, "update_var", false, "TODO") \
macro(RANDOMIZER_WRITE_VAR, "write_var", false, "TODO") \
macro(RANDOMIZER_SET_VAR_DISABLED, "set_var_disabled", false, "r") \
macro(RANDOMIZER_CLEAR_VAR_DISABLED, "clear_var_disabled", false, "r") \
@@ -1018,7 +979,7 @@ public:
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const VL_PURE {
static constexpr const char* const values[] = {
static const char* const values[] = {
#define VL_CMETHOD_NAME(id, method, pure, args) method,
FOR_EACH_CMETHOD(VL_CMETHOD_NAME)
#undef VL_CMETHOD_NAME
@@ -1027,7 +988,7 @@ public:
return values[m_e];
}
bool isPure() const VL_PURE {
static constexpr bool values[] = {
static const bool values[] = {
#define VL_CMETHOD_PURE(id, method, pure, args) pure,
FOR_EACH_CMETHOD(VL_CMETHOD_PURE)
#undef VL_CMETHOD_PURE
@@ -1036,7 +997,7 @@ public:
return values[m_e];
}
const char* args() const VL_PURE {
static constexpr const char* const values[] = {
static const char* const values[] = {
#define VL_CMETHOD_ARGS(id, method, pure, args) args,
FOR_EACH_CMETHOD(VL_CMETHOD_ARGS)
#undef VL_CMETHOD_ARGS
@@ -1106,7 +1067,7 @@ public:
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const VL_PURE {
static constexpr const char* const names[] = {"none", "ctor_var_reset_call"};
static const char* const names[] = {"none", "ctor_var_reset_call"};
return names[m_e];
}
bool isNone() const { return m_e == NONE; }
@@ -1142,7 +1103,7 @@ public:
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const VL_PURE {
static constexpr const char* const names[]
static const char* const names[]
= {"CASE", "CASEX", "CASEZ", "CASEINSIDE", "CASEMATCHES", "RANDSEQUENCE"};
return names[m_e];
}
@@ -1169,7 +1130,7 @@ public:
};
enum en m_e;
const char* ascii() const {
static constexpr const char* const names[]
static const char* const names[]
= {"UNSUPPORTED", "SAMEISH", "COMPATIBLE", "ENUM_EXPLICIT",
"ENUM_IMPLICIT", "DYNAMIC_CLASS", "INCOMPATIBLE"};
return names[m_e];
@@ -1199,8 +1160,7 @@ class VCoverBinsType final {
public:
enum en : uint8_t {
BINS_ARRAY, // Array of bins with user-speciifed size
BINS_AUTO, // Automatic bins of a 'bins auto[N]' declaration
BINS_AUTO_IMPLICIT, // Automatic bins of a coverpoint without bins (IEEE 1800-2023 19.5.3)
BINS_AUTO, // Auto-sized array of bins (eg auto_bin_max)
BINS_DEFAULT, // Default bin
BINS_IGNORE, // Ignore bin
BINS_ILLEGAL, // Illegal bin
@@ -1216,15 +1176,8 @@ public:
: m_e{_e} {}
constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE
const char* ascii() const {
static constexpr const char* const names[]
= {"array", "auto", "auto_implicit", "default", "ignore_bins",
"illegal_bins", "transition", "bins", "wildcard"};
return names[m_e];
}
// Keyword declaring the bins, without any 'wildcard' prefix
const char* verilogKwd() const {
static constexpr const char* const names[] = {
"bins", "bins", "bins", "bins", "ignore_bins", "illegal_bins", "bins", "bins", "bins"};
static const char* const names[]
= {"user", "array", "auto", "ignore", "illegal", "default", "wildcard", "transition"};
return names[m_e];
}
// VlCovBinKind enumerator naming the bin's set
@@ -1278,23 +1231,23 @@ public:
constexpr VCoverOptionType(en _e)
: m_e{_e} {}
const char* ascii() const {
static constexpr const char* const names[] = {"weight",
"goal",
"at_least",
"auto_bin_max",
"per_instance",
"comment",
"name",
"cross_num_print_missing",
"cross_retain_auto_bins",
"detect_overlap",
"get_inst_coverage",
"strobe",
"merge_instances",
"distribute_first",
"real_interval",
"cross_auto_bin_max",
"unknown"};
static const char* const names[] = {"weight",
"goal",
"at_least",
"auto_bin_max",
"per_instance",
"comment",
"name",
"cross_num_print_missing",
"cross_retain_auto_bins",
"detect_overlap",
"get_inst_coverage",
"strobe",
"merge_instances",
"distribute_first",
"real_interval",
"cross_auto_bin_max",
"unknown"};
return names[m_e];
}
};
@@ -1322,12 +1275,11 @@ public:
: m_e(static_cast<en>(_e)) {} // LCOV_EXCL_STOP // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE
const char* ascii() const {
static constexpr const char* const names[] = {"", "[*]", "[->]", "[=]"};
static const char* const names[] = {"", "[*]", "[->]", "[=]"};
return names[m_e];
}
const char* asciiJson() const {
static constexpr const char* const names[]
= {"\"none\"", "\"consec\"", "\"goto\"", "\"noncons\""};
static const char* const names[] = {"\"none\"", "\"consec\"", "\"goto\"", "\"noncons\""};
return names[m_e];
}
};
@@ -1347,13 +1299,11 @@ public:
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const VL_MT_SAFE { return m_e; }
const char* ascii() const {
static constexpr const char* const names[]
= {"NONE", "INPUT", "OUTPUT", "INOUT", "REF", "CONSTREF"};
static const char* const names[] = {"NONE", "INPUT", "OUTPUT", "INOUT", "REF", "CONSTREF"};
return names[m_e];
}
string verilogKwd() const {
static constexpr const char* const names[]
= {"", "input", "output", "inout", "ref", "const ref"};
static const char* const names[] = {"", "input", "output", "inout", "ref", "const ref"};
return names[m_e];
}
string prettyName() const { return verilogKwd(); }
@@ -1364,7 +1314,6 @@ public:
bool isNonOutput() const {
return m_e == INPUT || m_e == INOUT || m_e == REF || m_e == CONSTREF;
}
bool isOutput() const { return m_e == OUTPUT; }
bool isReadOnly() const VL_MT_SAFE { return m_e == INPUT || m_e == CONSTREF; }
bool isWritable() const VL_MT_SAFE { return m_e == OUTPUT || m_e == INOUT || m_e == REF; }
bool isRef() const VL_MT_SAFE { return m_e == REF; }
@@ -1379,17 +1328,6 @@ public:
}
return "VerilatedTraceSigDirection::NONE";
}
VAccess pinAccess() const {
switch (m_e) {
case NONE: return VAccess::NOCHANGE;
case INPUT:
case CONSTREF: return VAccess::READ;
case OUTPUT: return VAccess::WRITE;
case INOUT:
case REF: return VAccess::READWRITE;
}
v3fatalSrc("Unhandled VDirection");
}
};
constexpr bool operator==(const VDirection& lhs, const VDirection& rhs) VL_MT_SAFE {
return lhs.m_e == rhs.m_e;
@@ -1430,7 +1368,7 @@ public:
bool addNewline() const { return m_e != DT_WRITE; }
bool needScopeTracking() const { return m_e != DT_DISPLAY && m_e != DT_WRITE; }
const char* ascii() const {
static constexpr const char* const names[]
static const char* const names[]
= {"display", "write", "monitor", "strobe", "info", "error", "warning", "fatal"};
return names[m_e];
}
@@ -1456,9 +1394,8 @@ public:
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
static constexpr const char* const names[]
= {"$dumpfile", "$dumpvars", "$dumpall", "$dumpflush",
"$dumplimit", "$dumpoff", "$dumpon"};
static const char* const names[] = {"$dumpfile", "$dumpvars", "$dumpall", "$dumpflush",
"$dumplimit", "$dumpoff", "$dumpon"};
return names[m_e];
}
};
@@ -1496,7 +1433,7 @@ public:
};
enum en m_e;
bool clockedStmt() const {
static constexpr bool clocked[] = {
static const bool clocked[] = {
true, // ET_CHANGED
true, // ET_BOTHEDGE
true, // ET_POSEDGE
@@ -1526,13 +1463,13 @@ public:
}
}
const char* ascii() const {
static constexpr const char* const names[]
static const char* const names[]
= {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE", "ET_INITIAL_NBA",
"COMBO", "COMBO_STAR", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
return names[m_e];
}
const char* verilogKwd() const {
static constexpr const char* const names[] = {
static const char* const names[] = {
"[changed]", "edge", "posedge", "negedge", "[event]", "[true]", "[initial_nba]",
"*", "*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
return names[m_e];
@@ -1595,7 +1532,7 @@ public:
enum en m_e;
const char* ascii() const {
static constexpr const char* const values[] = {
static const char* const values[] = {
#define VL_EVAL_NAME(id, iterated, triggers, first, slow) #id,
FOR_EACH_EVAL(VL_EVAL_NAME)
#undef VL_EVAL_NAME
@@ -1604,7 +1541,7 @@ public:
return values[m_e];
}
bool isIterated() const {
static constexpr bool values[] = {
static const bool values[] = {
#define VL_EVAL_IS_ITERATED(id, iterated, triggers, first, slow) iterated,
FOR_EACH_EVAL(VL_EVAL_IS_ITERATED)
#undef VL_EVAL_IS_ITERATED
@@ -1613,7 +1550,7 @@ public:
return values[m_e];
}
bool hasTriggers() const {
static constexpr bool values[] = {
static const bool values[] = {
#define VL_EVAL_HAS_TRIGGERS(id, iterated, triggers, first, slow) triggers,
FOR_EACH_EVAL(VL_EVAL_HAS_TRIGGERS)
#undef VL_EVAL_HAS_TRIGGERS
@@ -1622,7 +1559,7 @@ public:
return values[m_e];
}
bool firstIteration() const {
static constexpr bool values[] = {
static const bool values[] = {
#define VL_EVAL_FIRST(id, iterated, triggers, first, slow) first,
FOR_EACH_EVAL(VL_EVAL_FIRST)
#undef VL_EVAL_FIRST
@@ -1631,7 +1568,7 @@ public:
return values[m_e];
}
bool slow() const {
static constexpr bool values[] = {
static const bool values[] = {
#define VL_EVAL_SLOW(id, iterated, triggers, first, slow) slow,
FOR_EACH_EVAL(VL_EVAL_SLOW)
#undef VL_EVAL_SLOW
@@ -1674,7 +1611,7 @@ public:
enum en : uint8_t { NONE, ENUM, STRUCT, UNION, CLASS, INTERFACE_CLASS, GENERIC_INTERFACE };
enum en m_e;
const char* ascii() const {
static constexpr const char* const names[]
static const char* const names[]
= {"none", "enum", "struct", "union", "class", "interface class", "generic interface"};
return names[m_e];
}
@@ -1744,11 +1681,11 @@ public:
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
static constexpr const char* const names[] = {"JOIN", "JOIN_ANY", "JOIN_NONE"};
static const char* const names[] = {"JOIN", "JOIN_ANY", "JOIN_NONE"};
return names[m_e];
}
const char* verilogKwd() const {
static constexpr const char* const names[] = {"join", "join_any", "join_none"};
static const char* const names[] = {"join", "join_any", "join_none"};
return names[m_e];
}
bool join() const { return m_e == JOIN; }
@@ -1777,8 +1714,7 @@ public:
}; // Static propagated from above
enum en m_e;
const char* ascii() const {
static constexpr const char* const names[]
= {"NONE", "VAUTOM", "VAUTOMI", "VSTATIC", "VSTATICI"};
static const char* const names[] = {"NONE", "VAUTOM", "VAUTOMI", "VSTATIC", "VSTATICI"};
return names[m_e];
}
VLifetime()
@@ -1816,36 +1752,8 @@ inline std::ostream& operator<<(std::ostream& os, const VLifetime& rhs) {
class VNumRange final {
public:
// Iterable sequence of integers
class Sequence final {
class Iterator final {
int m_index; // Current index
const int m_inc; // Step, +1 or -1
public:
Iterator(int index, int inc)
: m_index{index}
, m_inc{inc} {}
int operator*() const { return m_index; }
Iterator& operator++() {
m_index += m_inc;
return *this;
}
bool operator!=(const Iterator& that) const { return m_index != that.m_index; }
};
const int m_begin; // First index
const int m_end; // One past the last index
const int m_inc; // Step, +1 or -1
public:
Sequence(int first, int last, int inc)
: m_begin{first}
, m_end{last + inc}
, m_inc{inc} {}
Iterator begin() const { return Iterator{m_begin, m_inc}; }
Iterator end() const { return Iterator{m_end, m_inc}; }
};
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;
bool m_ranged = false; // Has a range
bool operator==(const VNumRange& rhs) const {
return m_left == rhs.m_left && m_right == rhs.m_right && m_ranged == rhs.m_ranged;
@@ -1883,14 +1791,6 @@ public:
return m_left > m_right ? m_right : m_left;
} // How to show a declaration
int leftToRightInc() const { return ascending() ? 1 : -1; }
// Iterate indices from left() to right() inclusive: 'for (int i : range.seqLeftToRight())'
Sequence seqLeftToRight() const { return {m_left, m_right, leftToRightInc()}; }
// Iterate indices from right() to left() inclusive: 'for (int i : range.seqRightToLeft())'
Sequence seqRightToLeft() const { return {m_right, m_left, -leftToRightInc()}; }
// Iterate indices from lo() to hi() inclusive: 'for (int i : range.seqLoToHi())'
Sequence seqLoToHi() const { return {lo(), hi(), 1}; }
// Iterate indices from hi() to lo() inclusive: 'for (int i : range.seqHiToLo())'
Sequence seqHiToLo() const { return {hi(), lo(), -1}; }
int elements() const VL_MT_SAFE { return hi() - lo() + 1; }
bool ranged() const { return m_ranged; }
bool ascending() const { return m_left < m_right; }
@@ -1934,7 +1834,7 @@ public:
};
enum en m_e;
const char* ascii() const {
static constexpr const char* const names[] = {
static const char* const names[] = {
"COVERAGE_BLOCK_OFF", //
"HIER_BLOCK", //
"HIER_PARAMS", //
@@ -1982,7 +1882,7 @@ public:
};
enum en m_e;
const char* ascii() const {
static constexpr const char* const names[] = {"NONE", "RAND", "RANDC", "RAND_INLINE"};
static const char* const names[] = {"NONE", "RAND", "RANDC", "RAND_INLINE"};
return names[m_e];
}
VRandAttr()
@@ -2049,7 +1949,7 @@ public:
};
enum en m_e;
const char* ascii() const {
static constexpr const char* const names[] = {"UNSIGNED", "SIGNED", "NOSIGN"};
static const char* const names[] = {"UNSIGNED", "SIGNED", "NOSIGN"};
return names[m_e];
}
VSigning()
@@ -2088,7 +1988,7 @@ public:
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
static constexpr const char* const names[]
static const char* const names[]
= {"highz", "small", "medium", "weak", "large", "pull", "strong", "supply"};
return names[m_e];
}
@@ -2118,13 +2018,13 @@ public:
};
enum en m_e;
const char* ascii() const {
static constexpr const char* const names[] = {"`systemc_ctor", //
"`systemc_dtor", //
"`systemc_header", //
"`systemc_header_post", //
"`systemc_implementation", //
"`systemc_imp_header", //
"`systemc_interface"};
static const char* const names[] = {"`systemc_ctor", //
"`systemc_dtor", //
"`systemc_header", //
"`systemc_header_post", //
"`systemc_implementation", //
"`systemc_imp_header", //
"`systemc_interface"};
return names[m_e];
}
// cppcheck-suppress noExplicitConstructor
@@ -2153,7 +2053,7 @@ public:
: m_e{_e} {}
constexpr operator en() const { return m_e; }
const char* ascii() const {
static constexpr const char* const names[]
static const char* const names[]
= {"ARRAY_PACKED", "ARRAY_UNPACKED", "SCOPE_MODULE", "SCOPE_INTERFACE",
"STRUCT_PACKED", "STRUCT_UNPACKED", "UNION_PACKED"};
return names[m_e];
@@ -2191,11 +2091,11 @@ public:
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
static constexpr const char* const names[] = {"CONSTANT", "FULL", "CHANGE"};
static const char* const names[] = {"CONSTANT", "FULL", "CHANGE"};
return names[m_e];
}
const char* func_prefix() const {
static constexpr const char* const names[] = {"trace_const", "trace_full", "trace_chg"};
static const char* const names[] = {"trace_const", "trace_full", "trace_chg"};
return names[m_e];
}
};
@@ -2228,7 +2128,7 @@ public:
constexpr operator en() const { return m_e; }
bool containsAny(VUseType other) const { return m_e & other.m_e; }
const char* ascii() const {
static constexpr const char* const names[] = {"INT_FWD", "INT_INC", "INT_FWD_INC"};
static const char* const names[] = {"INT_FWD", "INT_INC", "INT_FWD_INC"};
return names[m_e - 1];
}
};
@@ -2281,7 +2181,7 @@ public:
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
static constexpr const char* const names[]
static const char* const names[]
= {"?", "GPARAM", "LPARAM", "SPECPARAM", "GENVAR", "VAR",
"SUPPLY0", "SUPPLY1", "WIRE", "WREAL", "TRIAND", "TRIOR",
"TRIWIRE", "TRI0", "TRI1", "PORT", "BLOCKTEMP", "MODULETEMP",
@@ -2320,7 +2220,7 @@ public:
const char* traceSigKind() const {
// VerilatedTraceSigKind to used in trace signal declaration
static constexpr const char* const lut[] = {
static const char* const lut[] = {
/* UNKNOWN: */ "", // Should not be traced
/* GPARAM: */ "PARAMETER",
/* LPARAM: */ "PARAMETER",
+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();
}
+30 -52
View File
@@ -103,9 +103,6 @@ public:
return const_cast<AstNodeDType*>(
static_cast<const AstNodeDType*>(this)->skipRefIterp(true, false));
}
// (Slow) Recurse over MemberDType|ParamTypeDType|RefDType|ConstDType to EnumDType,
// Returns null if not resolved
const AstNodeDType* skipRefToEnumOrNullp() const { return skipRefIterp(true, false, false); }
// (Slow) Recurse over MemberDType|ParamTypeDType|RefDType to other type
const AstNodeDType* skipRefToNonRefp() const { return skipRefIterp(false, false); }
AstNodeDType* skipRefToNonRefp() {
@@ -253,9 +250,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
string m_typedefName; // Typedef's dtypeName() once moved off the typedef declaring it
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
@@ -271,7 +267,6 @@ protected:
AstNodeUOrStructDType(const AstNodeUOrStructDType& other)
: AstNodeDType{other}
, m_name{other.m_name}
, m_typedefName{other.m_typedefName}
, m_uniqueNum{uniqueNumInc()}
, m_packed{other.m_packed}
, m_isFourstate{other.m_isFourstate} {}
@@ -299,8 +294,6 @@ public:
bool similarDTypeNode(const AstNodeDType* samep) const override;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& flag) override { m_name = flag; }
// Keep the name the typedef declaring it gives, as it moves off it to the type table
void typedefName(const string& name) { m_typedefName = name; }
bool packed() const VL_MT_SAFE { return m_packed; }
void packed(bool flag) { m_packed = flag; }
// packed() but as don't support unpacked, presently all structs
@@ -326,7 +319,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
@@ -618,7 +611,6 @@ public:
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
string prettyNameMsg() const override;
string name() const override VL_MT_STABLE;
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
int widthAlignBytes() const override { return 0; }
@@ -699,24 +691,22 @@ public:
bool isCompound() const override { return false; }
};
class AstCoverCrossDType final : public AstNodeDType {
// Borrowed pointer to VlCoverCrossT<...> or construction-time VlCoverCrossDyn.
// Borrowed pointer to VlCoverCrossT<dimensions, tuples, bins, autoBins, binWords>.
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
const uint32_t m_tuples;
const uint32_t m_bins;
const uint32_t m_autoBins;
const uint64_t m_binWords;
public:
AstCoverCrossDType(FileLine* fl, uint32_t dimensions, uint32_t tuples, uint32_t bins,
uint32_t autoBins, uint64_t binWords, bool dynamic = false)
uint32_t autoBins, uint64_t binWords)
: ASTGEN_SUPER_CoverCrossDType(fl)
, m_dimensions{dimensions}
, m_tuples{tuples}
, m_bins{bins}
, m_autoBins{autoBins}
, m_binWords{binWords}
, m_dynamic{dynamic} {
, m_binWords{binWords} {
dtypep(this);
}
ASTGEN_MEMBERS_AstCoverCrossDType;
@@ -724,12 +714,11 @@ public:
BROKEN_RTN(m_dimensions == 0);
return nullptr;
}
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
bool sameNode(const AstNode* samep) const override {
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
&& autoBins() == sp->autoBins() && binWords() == sp->binWords();
}
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
@@ -739,7 +728,6 @@ public:
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*); }
@@ -779,8 +767,8 @@ class AstDefImplicitDType final : public AstNodeDType {
// 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)
@@ -890,9 +878,14 @@ 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
string m_typedefName; // Typedef's dtypeName() once moved off the typedef declaring it
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
const int m_uniqueNum;
// dist-ast-dump-suppress // Skip dumping cache
TableMap m_tableMap; // Created table for V3Width only to remove duplicates
public:
AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstEnumItem* itemsp)
@@ -907,7 +900,6 @@ public:
AstEnumDType(const AstEnumDType& other)
: AstNodeDType{other}
, m_name{other.m_name}
, m_typedefName{other.m_typedefName}
, m_uniqueNum{uniqueNumInc()} {}
ASTGEN_MEMBERS_AstEnumDType;
@@ -927,8 +919,6 @@ public:
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& flag) override { m_name = flag; }
// Keep the name the typedef declaring it gives, as it moves off it to the type table
void typedefName(const string& name) { m_typedefName = name; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
@@ -943,6 +933,8 @@ 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 {
@@ -991,6 +983,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)
@@ -1021,18 +1014,12 @@ public:
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;
string prettyDTypeName(bool full) 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;
@@ -1067,7 +1054,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,
@@ -1091,7 +1078,6 @@ public:
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;
string prettyDTypeName(bool full) const override;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
@@ -1175,7 +1161,6 @@ public:
ASTGEN_MEMBERS_AstParamTypeDType;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
string prettyDTypeName(bool full) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override VL_MT_STABLE {
return dtypep() ? dtypep() : childDTypep();
@@ -1290,9 +1275,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)
@@ -1319,13 +1301,12 @@ 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;
string name() const override VL_MT_STABLE { return m_name; }
string prettyDTypeName(bool full) const override {
return subDTypep() ? subDTypep()->prettyDTypeName(full) : prettyName();
return subDTypep() ? prettyName(subDTypep()->prettyDTypeName(full)) : prettyName();
}
AstBasicDType* basicp() const override VL_MT_STABLE {
return subDTypep() ? subDTypep()->basicp() : nullptr;
@@ -1344,8 +1325,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;
@@ -1517,7 +1496,6 @@ public:
bool sameNode(const AstNode* samep) const override;
bool similarDTypeNode(const AstNodeDType* samep) const override;
void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override VL_MT_STABLE {
return m_refDTypep ? m_refDTypep : childDTypep();
+16 -36
View File
@@ -66,16 +66,13 @@ 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: consolidate cLValueTargetp, isLValue, baseFromp
// If the expression is a valid C++ LValue, return the target reference, else nullptr
// This always returns either AstVarRef, AstMemberSel, or nullptr
AstNodeExpr* cLValueTargetp();
// TODO: this actually means it's a write or RW, not that it's an LValue
bool isLValue() const { return getVAccessRecurse().isWriteOrRW(); }
bool isLValue() const;
// Return base var (or const) nodep dereferences
AstNode* baseFromp(bool overMembers);
@@ -532,6 +529,7 @@ public:
}
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
static AstNodeVarRef* varRefLValueRecurse(AstNode* nodep);
};
// === Concrete node types =====================================================
@@ -752,7 +750,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,6 +758,7 @@ public:
, m_method{method} {
this->fromp(fromp);
addPinsp(pinsp);
setPurity();
}
ASTGEN_MEMBERS_AstCMethodHard;
void dump(std::ostream& str) const override;
@@ -769,10 +768,7 @@ public:
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 +779,7 @@ public:
void method(VCMethod value) { m_method = value; }
private:
bool getPurity();
void setPurity();
};
class AstCReset final : public AstNodeExpr {
// Reset variable at startup
@@ -1253,10 +1249,6 @@ public:
int instrCount() const override { return widthInstrs(); }
bool isEqAllOnes() const { return num().isEqAllOnes(width()); }
bool isEqAllOnesV() const { return num().isEqAllOnes(widthMinV()); }
// Whether a parameter declared without a data type takes the same type from this value
// as from 'samep': both real, both string, or both integral with the same width and
// signedness. Unlike AstNodeDType::similarDType, ignores whether two-state or four-state.
bool sameValueType(const AstConst* samep) const;
// Parse string and create appropriate type of AstConst.
// May return nullptr on parse failure.
static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
@@ -1446,8 +1438,6 @@ class AstEnumItemRef final : public AstNodeExpr {
// @astgen ptr := m_itemp : Optional[AstEnumItem] // [AfterLink] Pointer to item
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in
string m_name; // Name of enum (for param relink)
string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
bool m_containsGenBlock = false; // Contains gen block reference
public:
AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
: ASTGEN_SUPER_EnumItemRef(fl)
@@ -1464,15 +1454,10 @@ public:
int instrCount() const override { return 0; }
bool sameNode(const AstNode* samep) const override {
const AstEnumItemRef* const sp = VN_DBG_AS(samep, EnumItemRef);
return itemp() == sp->itemp() && dotted() == sp->dotted()
&& containsGenBlock() == sp->containsGenBlock();
return itemp() == sp->itemp();
}
AstEnumItem* itemp() const VL_MT_STABLE { return m_itemp; }
void itemp(AstEnumItem* nodep) { m_itemp = nodep; }
string dotted() const { return m_dotted; }
void dotted(const string& dotted) { m_dotted = dotted; }
bool containsGenBlock() const { return m_containsGenBlock; }
void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
@@ -1486,7 +1471,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)
@@ -1918,7 +1903,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)"; }
@@ -2046,7 +2031,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,
@@ -2419,7 +2404,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:
@@ -2442,7 +2426,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; }
@@ -2648,7 +2631,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);
@@ -6475,7 +6458,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)
@@ -6493,8 +6475,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; }
+74 -170
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; }
@@ -118,7 +114,6 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
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
@@ -152,7 +147,6 @@ protected:
, m_isHideProtected{false}
, m_dpiPure{false}
, m_keepAlive{false}
, m_needsSyms{true}
, m_pureVirtual{false}
, m_recursive{false}
, m_static{false}
@@ -222,8 +216,6 @@ public:
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; }
@@ -358,8 +350,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; }
@@ -700,10 +690,8 @@ 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 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/VPI
//
// @astgen ptr := m_modp : Optional[AstNodeModule] // [AfterLink] Pointer to module instanced
@@ -711,7 +699,6 @@ class AstCell final : public AstNode {
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 +733,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 +786,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
@@ -995,6 +977,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 +1058,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 +1072,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);
}
@@ -1123,40 +1136,16 @@ class AstCoverCrossBin final : public AstNode {
// @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)
AstCoverCrossBin(FileLine* fl, const string& name, AstNode* selectp, AstNodeExpr* iffp)
: ASTGEN_SUPER_CoverCrossBin(fl)
, m_name{name}
, m_binsType{binsType} {
, m_name{name} {
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
@@ -1184,46 +1173,17 @@ 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 +1214,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
@@ -1575,7 +1521,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 +1542,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,14 +1563,10 @@ 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; }
AstCFunc* evalFuncp(VEval eval) const { return m_evalFuncps[eval]; }
@@ -1892,7 +1832,6 @@ public:
bool maybePointedTo() const override VL_MT_SAFE { return true; }
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
void name(const string& name) override { m_name = name; }
string prettyNameMsg() const override;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool sameNode(const AstNode* samep) const override;
@@ -1900,9 +1839,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);
@@ -2141,10 +2078,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
@@ -2174,9 +2111,6 @@ public:
ASTGEN_MEMBERS_AstTypedef;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
// Name of the type it declares, with the scope declaring it, so with the values of the
// parameters of the scope, e.g. '$unit::Cls#(8)::s_t'
string dtypeName() const;
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
virtual AstNodeDType* subDTypep() const VL_MT_STABLE {
return dtypep() ? dtypep() : childDTypep();
@@ -2313,11 +2247,11 @@ 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
bool m_covergroupRefMember : 1; // Persistent covergroup ref/const ref argument
bool m_embeddedCovergroup : 1; // Instance variable an embedded covergroup declares
bool m_attrFileDescr : 1; // File descriptor
bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration
bool m_icoMaybeWritten : 1; // Design might write this input signal - for ico change detect
@@ -2325,11 +2259,10 @@ class AstVar final : public AstNode {
bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
bool m_hasUserInit : 1; // Has initial assignment by user at parse time
bool m_isStatic : 1; // Static C variable (for Verilog see instead lifetime())
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
@@ -2379,11 +2312,11 @@ 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;
m_covergroupRefMember = false;
m_embeddedCovergroup = false;
m_attrFileDescr = false;
m_gotNansiType = false;
m_icoMaybeWritten = false;
@@ -2391,10 +2324,10 @@ class AstVar final : public AstNode {
m_isContinuously = false;
m_hasStrengthAssignment = false;
m_hasUserInit = false;
m_isStatic = false;
m_isPulldown = false;
m_isPullup = false;
m_isIfaceParent = false;
m_isIfaceArraySplit = false;
m_isInternal = false;
m_isIfaceParam = false;
m_isDpiOpenArray = false;
@@ -2507,10 +2440,10 @@ public:
string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument
string dpiTmpVarType(const string& varName) const;
// Return Verilator internal type for argument: CData, SData, IData, WData
string vlArgType(bool named, bool forReturn, bool forFunc, bool asRef = false,
bool constRef = false) const;
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(); }
@@ -2530,16 +2463,13 @@ 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; }
void setConstPoolEntry() { m_constPoolEntry = true; }
bool covergroupRefMember() const { return m_covergroupRefMember; }
void covergroupRefMember(bool flag) { m_covergroupRefMember = flag; }
// Instance variable an embedded covergroup declares, of the covergroup's anonymous type
// (IEEE 1800-2023 19.4)
bool embeddedCovergroup() const { return m_embeddedCovergroup; }
void embeddedCovergroup(bool flag) { m_embeddedCovergroup = flag; }
void rand(const VRandAttr flag) { m_rand = flag; }
void usedParam(bool flag) { m_usedParam = flag; }
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
@@ -2558,8 +2488,8 @@ public:
void primaryIO(bool flag) { m_primaryIO = flag; }
void isConst(bool flag) { m_isConst = flag; }
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) {
@@ -2647,7 +2577,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(); }
@@ -2690,6 +2619,7 @@ public:
bool isRand() const { return m_rand.isRand(); }
bool isRandC() const { return m_rand.isRandC(); }
bool isConst() const VL_MT_SAFE { return m_isConst; }
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
bool isLatched() const { return m_isLatched; }
bool isFuncLocal() const { return m_funcLocal; }
bool isFuncLocalSticky() const { return m_funcLocalSticky; }
@@ -2702,6 +2632,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; }
@@ -2725,6 +2656,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());
@@ -2767,7 +2705,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; }
@@ -2781,10 +2718,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,
@@ -3061,12 +2998,8 @@ class AstClass final : public AstNodeModule {
// MEMBERS
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
// @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class
string m_dtypeNameFull; // dtypeName(true) once frozen, as parameters may then be gone
string m_dtypeNameShort; // dtypeName(false) once frozen, as parameters may then be gone
uint32_t m_declTokenNum; // Declaration token number
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
@@ -3074,14 +3007,9 @@ class AstClass final : public AstNodeModule {
bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class
bool m_virtual = false; // Virtual class
bool m_printedFrom = false; // This class is printed from i.e. is used as format arg.
// Mutable, as only a recursion guard of the const dtypeName(), so also not dumped
mutable bool m_dtypeNameBusy = false; // In dtypeName(), which a parameter may lead back to
// Covergroup options (when m_covergroup is true)
int m_cgAutoBinMax = -1; // option.auto_bin_max value (-1 = not set, use default 64)
string dtypeNameCalc(bool full) const; // dtypeName() as computed from the tree
string dtypeNameScope() const; // Prefix of dtypeName(true) for the scope declaring the class
public:
AstClass(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_Class(fl, name, libname)
@@ -3100,8 +3028,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; }
@@ -3116,16 +3042,6 @@ public:
void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; }
void markPrintedFrom() { m_printedFrom = true; }
bool isPrintedFrom() const { return m_printedFrom; }
// Name of the class as a data type, with the values of its parameters, e.g. 'Cls#(int,5)'.
// With 'full', as for $typename (IEEE 1800-2023 20.6.1), prefixed with the scope declaring
// the class, e.g. '$unit::Cls#(int,5)', and with the types of parameters in full.
string dtypeName(bool full) const;
// Fix dtypeName(), as V3WidthCommit moves parameter types to the type table
void dtypeNameFreeze();
// Whether dtypeName() is fixed, as for a class elaborated from the design
bool dtypeNameFrozen() const { return !m_dtypeNameFull.empty(); }
// Named by dtypeName(), as name() is internal for a specialization, unless still a template
string prettyNameMsg() const override { return hasGParam() ? prettyName() : dtypeName(false); }
// Covergroup options accessors
int cgAutoBinMax() const { return m_cgAutoBinMax; }
void cgAutoBinMax(int value) { m_cgAutoBinMax = value; }
@@ -3195,8 +3111,6 @@ public:
};
class AstIface final : public AstNodeModule {
// An interface declaration
string m_dtypeNameFull; // dtypeName(true) once frozen, as parameter types may then be gone
string m_dtypeNameShort; // dtypeName(false) once frozen, as parameter types may then be gone
bool m_hasVirtualRef = false; // There exists a virtual interface reference for this interface
public:
AstIface(FileLine* fl, const string& name, const string& libname)
@@ -3210,10 +3124,6 @@ public:
bool timescaleMatters() const override { return false; }
bool hasVirtualRef() const { return m_hasVirtualRef; }
void setHasVirtualRef() { m_hasVirtualRef = true; }
// Name of the interface as a data type, with the values of its parameters, e.g. 'ifc#(8)'
string dtypeName(bool full) const;
// Fix dtypeName(), as V3WidthCommit moves parameter types to the type table
void dtypeNameFreeze();
};
class AstModule final : public AstNodeModule {
// A module declaration
@@ -3264,7 +3174,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
@@ -3416,14 +3328,6 @@ public:
}
int elementsConst() const VL_MT_STABLE { return hiConst() - loConst() + 1; }
bool ascending() const { return leftConst() < rightConst(); }
// Iterate indices from left to right inclusive, see VNumRange::seqLeftToRight()
VNumRange::Sequence seqLeftToRight() const {
return VNumRange{leftConst(), rightConst()}.seqLeftToRight();
}
// Iterate indices from right to left inclusive, see VNumRange::seqRightToLeft()
VNumRange::Sequence seqRightToLeft() const {
return VNumRange{leftConst(), rightConst()}.seqRightToLeft();
}
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
virtual string emitC() { V3ERROR_NA_RETURN(""); }
+30 -1
View File
@@ -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:
+277 -372
View File
File diff suppressed because it is too large Load Diff
+12 -11
View File
@@ -26,8 +26,6 @@
#include "V3Broken.h"
#include "V3ConstPool.h"
#include <unordered_set>
VL_DEFINE_DEBUG_FUNCTIONS;
@@ -236,11 +234,18 @@ private:
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};
const AstNodeExpr* const lvalp = argp->cLValueTargetp();
const VAccess access = [&]() -> VAccess {
if (const AstVarRef* const varrefp = VN_CAST(lvalp, VarRef)) {
return varrefp->access();
}
if (const AstMemberSel* const memberselp = VN_CAST(lvalp, MemberSel)) {
return memberselp->access();
}
UASSERT_OBJ(!lvalp, argp, "Unknown LValue expression");
// Not an LValue, so it's read-only
return VAccess::READ;
}();
if (dp[0] == '+') --dp; // Repeats the entry before it
switch (dp[0]) {
case 'r':
@@ -434,10 +439,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
-5
View File
@@ -282,11 +282,6 @@ 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());
+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:
+23 -27
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)
@@ -1015,10 +1015,14 @@ class ConstVisitor final : public VNVisitor {
const bool packReal) {
const AstNodeDType* const dtypep = fromp->dtypep()->skipRefp();
if (const AstUnpackArrayDType* const unpackDtypep = VN_CAST(dtypep, UnpackArrayDType)) {
for (const int idx : unpackDtypep->declRange().seqLeftToRight()) {
const int left = unpackDtypep->left();
const int right = unpackDtypep->right();
const int step = left <= right ? 1 : -1;
for (int idx = left;; idx += step) {
AstArraySel* const selp
= new AstArraySel{fromp->fileline(), fromp->cloneTreePure(false), idx};
collectFixedAggregateTerms(selp, termps, packReal);
if (idx == right) break;
}
VL_DO_DANGLING(pushDeletep(fromp), fromp);
} else if (const AstNodeUOrStructDType* const sdtypep
@@ -1459,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)) {
@@ -3966,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.
@@ -4035,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
-219
View File
@@ -1,219 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Constant pool
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of either the GNU Lesser General Public License Version 3
// or the Perl Artistic License Version 2.0.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "V3PchAstMT.h"
#include "V3ConstPool.h"
//######################################################################
// V3ConstPool
bool V3ConstPool::equalMap(const AstInitArray* ap, const AstInitArray* bp) {
// Associative array initializers must have equivalent types, defaults, and entries.
// As in 'equalTable', compare by value, rather than by tree structure.
const AstAssocArrayDType* const aDTypep = VN_AS(ap->dtypep(), AssocArrayDType);
const AstAssocArrayDType* const bDTypep = VN_AS(bp->dtypep(), AssocArrayDType);
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false;
if (!aDTypep->keyDTypep()->sameTree(bDTypep->keyDTypep())) return false;
// The default is optional, but must be the same if present
const AstNode* const aDefaultp = ap->defaultp();
const AstNode* const bDefaultp = bp->defaultp();
UASSERT_OBJ(!aDefaultp || VN_IS(aDefaultp, Const), ap, "Const pool map default not Const");
UASSERT_OBJ(!bDefaultp || VN_IS(bDefaultp, Const), bp, "Const pool map default not Const");
if (!aDefaultp != !bDefaultp) return false;
if (aDefaultp && !aDefaultp->sameTree(bDefaultp)) return false;
// Compare the entries, which are ordered by key. Note an entry explicitly set to the
// default value makes maps compare different, which is safe, but misses merging them.
const AstInitArray::KeyItemMap& aMap = ap->map();
const AstInitArray::KeyItemMap& bMap = bp->map();
if (aMap.size() != bMap.size()) return false;
auto bIt = bMap.cbegin();
for (const auto& aItem : aMap) {
const AstNode* const aValuep = aItem.second->valuep();
const AstNode* const bValuep = bIt->second->valuep();
UASSERT_OBJ(VN_IS(aValuep, Const), aValuep, "Const pool map item not Const");
UASSERT_OBJ(VN_IS(bValuep, Const), bValuep, "Const pool map item not Const");
if (aItem.first != bIt->first) return false;
if (!aValuep->sameTree(bValuep)) return false;
++bIt;
}
return true;
}
bool V3ConstPool::equalTable(const AstInitArray* ap, const AstInitArray* bp) {
// Unpacked array initializers must have equivalent values
// Note, sadly we can't just call ap->sameTree(pb), because both:
// - the dtypes might be different instances
// - the default/inititem children might be in different order yet still yield the same table
// See note in AstInitArray::same about the same. This function instead compares by initializer
// value, rather than by tree structure.
const AstUnpackArrayDType* const aDTypep = VN_AS(ap->dtypep(), UnpackArrayDType);
const AstUnpackArrayDType* const bDTypep = VN_AS(bp->dtypep(), UnpackArrayDType);
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false;
// Compare the range by value, as the bounds might be differently typed constants
if (!(aDTypep->declRange() == bDTypep->declRange())) return false;
// Compare initializer arrays by value. Note this is only called when they hash the same.
const uint64_t size = aDTypep->elementsConst();
for (uint64_t n = 0; n < size; ++n) {
const AstNode* const valAp = ap->getIndexDefaultedValuep(n);
const AstNode* const valBp = bp->getIndexDefaultedValuep(n);
UASSERT_OBJ(VN_IS(valAp, Const), valAp, "Const pool table item not Const");
UASSERT_OBJ(VN_IS(valBp, Const), valBp, "Const pool table item not Const");
if (!valAp->sameTree(valBp)) return false;
}
return true;
}
size_t V3ConstPool::hashTable(const AstInitArray* initp) {
// Hash what 'equalTable' compares: the element type, the range by value, and the values
const AstUnpackArrayDType* const dtypep = VN_AS(initp->dtypep(), UnpackArrayDType);
V3Hash hash = V3Hasher::uncachedHash(dtypep->subDTypep());
hash += dtypep->left();
hash += dtypep->right();
const uint64_t size = dtypep->elementsConst();
for (uint64_t n = 0; n < size; ++n) {
hash += V3Hasher::uncachedHash(initp->getIndexDefaultedValuep(n));
}
return hash.value();
}
template <typename T_Set, typename T_Init>
AstVarRef* V3ConstPool::findOrCreate(T_Set& set, uint32_t& nextr, const char* prefixp,
T_Init* initp) {
// Rebuild the cache if invalidated
if (VL_UNLIKELY(!m_cacheValid)) {
m_cacheValid = true;
m_scopep = nullptr;
m_consts.clear();
m_maps.clear();
m_tables.clear();
m_varScopes.clear();
AstPackage* const pkgp = v3Global.rootp()->constPoolPkgp();
for (AstNode* nodep = pkgp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (AstScope* const scopep = VN_CAST(nodep, Scope)) {
if (m_stage == Stage::SCOPED) m_scopep = scopep;
continue;
}
// Otherwise must be a variable
AstVar* const varp = VN_AS(nodep, Var);
UASSERT_OBJ(varp->constPoolEntry(), varp, "Unmarked variable in constant pool");
if (VN_IS(varp->valuep(), Const)) {
m_consts.insert(varp);
} else if (const AstInitArray* const arrayp = VN_CAST(varp->valuep(), InitArray)) {
if (VN_IS(arrayp->dtypep(), AssocArrayDType)) {
m_maps.insert(varp);
} else {
m_tables.insert(varp);
}
}
}
if (m_scopep) {
for (AstVarScope* vscp = m_scopep->varsp(); vscp;
vscp = VN_AS(vscp->nextp(), VarScope)) {
m_varScopes.insert({vscp->varp(), vscp});
}
}
}
FileLine* const flp = initp->fileline();
AstNodeDType* const dtypep = initp->dtypep();
// Look up/create the variable with this value
const auto pair = set.insertLazy(initp, [&]() {
const std::string name = prefixp + std::to_string(nextr++);
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtypep};
varp->setConstPoolEntry();
varp->lifetime(VLifetime::STATIC_EXPLICIT);
varp->isConst(true);
varp->valuep(initp->cloneTree(false));
v3Global.rootp()->constPoolPkgp()->addStmtsp(varp);
return varp;
});
AstVar* const varp = *pair.first;
// Create a read reference to the entry, of the right form for the current stage
AstVarRef* refp = nullptr;
switch (m_stage) {
case Stage::UNSCOPED: {
// Before V3Scope, reference via the package, as any other package variable
AstPackage* const pkgp = v3Global.rootp()->constPoolPkgp();
refp = new AstVarRef{flp, pkgp, varp, VAccess::READ};
break;
}
case Stage::SCOPED: {
// While scoped, reference the VarScope, creating it for a new entry, or if removed
const auto vscpPair = m_varScopes.insertLazy(varp, [&]() {
AstVarScope* const vscp = new AstVarScope{varp->fileline(), m_scopep, varp};
m_scopep->addVarsp(vscp);
return std::pair<const AstVar*, AstVarScope*>{varp, vscp};
});
refp = new AstVarRef{flp, vscpPair.first->second, VAccess::READ};
break;
}
case Stage::DESCOPED: {
// After V3Descope, no VarScope is needed
refp = new AstVarRef{flp, varp, VAccess::READ};
break;
}
}
// The entry might have been created with a different, but compatible dtype
refp->dtypep(dtypep);
return refp;
}
AstVarRef* V3ConstPool::findConst(AstConst* initp) {
V3ConstPool& self = instance();
return self.findOrCreate(self.m_consts, self.m_nextConst, "CONST_", initp);
}
AstVarRef* V3ConstPool::findMap(AstInitArray* initp) {
UASSERT_OBJ(VN_IS(initp->dtypep(), AssocArrayDType), initp,
"Const pool map must have associative array dtype");
V3ConstPool& self = instance();
return self.findOrCreate(self.m_maps, self.m_nextMap, "MAP_", initp);
}
AstVarRef* V3ConstPool::findTable(AstInitArray* initp) {
UASSERT_OBJ(VN_IS(initp->dtypep(), UnpackArrayDType), initp,
"Const pool table must have unpacked array dtype");
V3ConstPool& self = instance();
return self.findOrCreate(self.m_tables, self.m_nextTable, "TABLE_", initp);
}
AstVarRef* V3ConstPool::find(AstNodeExpr* initp) {
if (AstConst* const constp = VN_CAST(initp, Const)) return findConst(constp);
if (AstInitArray* const arrayp = VN_CAST(initp, InitArray)) {
if (VN_IS(arrayp->dtypep(), AssocArrayDType)) return findMap(arrayp);
if (VN_IS(arrayp->dtypep(), UnpackArrayDType)) return findTable(arrayp);
}
initp->v3fatalSrc("Unhandled constant pool initializer");
return nullptr; // LCOV_EXCL_LINE
}
const char* V3ConstPool::broken() {
const V3ConstPool& self = instance();
if (!self.m_cacheValid) return nullptr;
BROKEN_RTN(self.m_scopep && !self.m_scopep->brokeExists());
for (const AstVar* const varp : self.m_consts) BROKEN_RTN(!varp->brokeExists());
for (const AstVar* const varp : self.m_maps) BROKEN_RTN(!varp->brokeExists());
for (const AstVar* const varp : self.m_tables) BROKEN_RTN(!varp->brokeExists());
for (const auto& pair : self.m_varScopes) {
BROKEN_RTN(!pair.first->brokeExists());
BROKEN_RTN(!pair.second->brokeExists());
}
return nullptr;
}
-163
View File
@@ -1,163 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Constant pool
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of either the GNU Lesser General Public License Version 3
// or the Perl Artistic License Version 2.0.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// V3ConstPool finds or creates entries in the constant pool package
// (AstNetlist::constPoolPkgp()), which holds read-only static data, i.e.:
// constants, maps and tables, shared across the whole design. Entries are
// ordinary package variables, deduplicated by value.
//
//*************************************************************************
#ifndef VERILATOR_V3CONSTPOOL_H_
#define VERILATOR_V3CONSTPOOL_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3HashTable.h"
#include "V3Hasher.h"
//============================================================================
class V3ConstPool final {
friend class V3Global; // Owns the instance, needs constructor access
// TYPES
// Scoping stage of the design, which determines how entries are referenced
enum class Stage : uint8_t {
UNSCOPED, // Before V3Scope: reference via the package
SCOPED, // Between V3Scope and V3Descope: reference via the VarScope
DESCOPED // After V3Descope: plain reference
};
// Hash and equality of constant entries in 'm_consts'
struct ConstHash final {
size_t operator()(const AstVar* varp) const {
return (*this)(VN_AS(varp->valuep(), Const));
}
size_t operator()(const AstConst* initp) const { return initp->num().toHash().value(); }
};
struct ConstEqual final {
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
return (*this)(ap, VN_AS(bp->valuep(), Const)); // Only on collision while rebuild
} // LCOV_EXCL_STOP
bool operator()(const AstVar* varp, const AstConst* initp) const {
// Compare by value, which also checks the width. The dtype is ignored.
return VN_AS(varp->valuep(), Const)->num().isCaseEq(initp->num());
}
};
// Hash and equality of associative array entries in 'm_maps'
struct MapHash final {
size_t operator()(const AstVar* varp) const {
return (*this)(VN_AS(varp->valuep(), InitArray));
}
size_t operator()(const AstInitArray* initp) const {
return V3Hasher::uncachedHash(initp).value();
}
};
struct MapEqual final {
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
return (*this)(ap, VN_AS(bp->valuep(), InitArray)); // Only on collision while rebuild
} // LCOV_EXCL_STOP
bool operator()(const AstVar* varp, const AstInitArray* initp) const {
return equalMap(VN_AS(varp->valuep(), InitArray), initp);
}
};
// Hash and equality of unpacked array entries in 'm_tables'
struct TableHash final {
size_t operator()(const AstVar* varp) const {
return (*this)(VN_AS(varp->valuep(), InitArray));
}
size_t operator()(const AstInitArray* initp) const { //
return hashTable(initp);
}
};
struct TableEqual final {
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
return (*this)(ap, VN_AS(bp->valuep(), InitArray)); // Only on collision while rebuild
} // LCOV_EXCL_STOP
bool operator()(const AstVar* varp, const AstInitArray* initp) const {
return equalTable(VN_AS(varp->valuep(), InitArray), initp);
}
};
// MEMBERS
Stage m_stage = Stage::UNSCOPED; // Scoping stage of the design, see setScoped()/setDescoped()
bool m_cacheValid = false; // Cache below is up to date
AstScope* m_scopep = nullptr; // Scope of the constant pool, between V3Scope and V3Descope
V3HashSet<AstVar*, ConstHash, ConstEqual> m_consts; // Packed constants
V3HashSet<AstVar*, MapHash, MapEqual> m_maps; // Associative array constants (maps)
V3HashSet<AstVar*, TableHash, TableEqual> m_tables; // Unpacked array constants (tables)
V3HashMap<const AstVar*, AstVarScope*> m_varScopes; // VarScope of each entry iff m_scopep
uint32_t m_nextConst = 0; // Sequence number for naming
uint32_t m_nextMap = 0; // Sequence number for naming
uint32_t m_nextTable = 0; // Sequence number for naming
// METHODS
static V3ConstPool& instance() { return *v3Global.constPoolp(); }
static bool equalMap(const AstInitArray* ap, const AstInitArray* bp);
static bool equalTable(const AstInitArray* ap, const AstInitArray* bp);
static size_t hashTable(const AstInitArray* initp);
// Find an entry equal to the given initializer in the given set, or create one named from
// the given prefix and sequence number, and return a read reference to it
template <typename T_Set, typename T_Init>
AstVarRef* findOrCreate(T_Set& set, uint32_t& nextr, const char* prefixp, T_Init* initp);
// CONSTRUCTORS - only V3Global creates and deletes the instance
V3ConstPool() = default;
~V3ConstPool() = default;
VL_UNCOPYABLE(V3ConstPool);
VL_UNMOVABLE(V3ConstPool);
public:
// STATIC METHODS
// Find a constant packed variable with the given value, or create one if one does not
// already exist, and return a read reference to it.
// The reference has the same dtype as initp.
// The referenced variable *might* have a different, but compatible dtype.
static AstVarRef* findConst(AstConst* initp);
// Find a constant associative array (map) with the given value, or create one if one does
// not already exist, and return a read reference to it.
// The reference has the same dtype as initp.
// The referenced variable *might* have a different, but compatible dtype.
static AstVarRef* findMap(AstInitArray* initp);
// Find a constant unpacked array (table) with the given value, or create one if one does
// not already exist, and return a read reference to it.
// The reference has the same dtype as initp.
// The referenced variable *might* have a different, but compatible dtype.
static AstVarRef* findTable(AstInitArray* initp);
// Dispatch to the above based on the kind of initializer.
static AstVarRef* find(AstNodeExpr* initp);
// Invalidate the lookup cache. This must be called after deleting any Vars from the pool.
static void invalidateCache() { instance().m_cacheValid = false; }
// Notify that the design has been scoped, called by V3Scope
static void setScoped() {
V3ConstPool& self = instance();
UASSERT(self.m_stage == Stage::UNSCOPED, "Constant pool scoped twice");
self.m_stage = Stage::SCOPED;
invalidateCache();
}
// Notify that the design has been descoped, called by V3Descope
static void setDescoped() {
V3ConstPool& self = instance();
UASSERT(self.m_stage == Stage::SCOPED, "Constant pool descoped when not scoped");
self.m_stage = Stage::DESCOPED;
invalidateCache();
}
// Check the lookup cache refers only to existing nodes, for V3Broken
static const char* broken();
};
#endif // Guard
+6 -6
View File
@@ -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;
+3 -4
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);
@@ -461,7 +460,7 @@ class CoverageVisitor final : public VNVisitor {
if (const AstBasicDType* const basicp = VN_CAST(dtypep, BasicDType)) {
toggleVarBottom(above, varp, basicp->nrange());
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
for (const int index_docs : adtypep->declRange().seqLoToHi()) {
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
const int index_code = index_docs - adtypep->lo();
ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]",
new AstArraySel{varp->fileline(),
@@ -472,7 +471,7 @@ class CoverageVisitor final : public VNVisitor {
newent.cleanup();
}
} else if (const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType)) {
for (const int index_docs : adtypep->declRange().seqLoToHi()) {
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
const AstNodeDType* const subtypep = adtypep->subDTypep()->skipRefp();
const int index_code = index_docs - adtypep->lo();
ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]",
@@ -1054,7 +1053,7 @@ class CoverageVisitor final : public VNVisitor {
unrolledp = new T_Oper{fl, selp, unrolledp};
}
iterate(unrolledp);
VL_DO_DANGLING(pushDeletep(unrolledp), unrolledp);
pushDeletep(unrolledp);
} else {
iterateChildren(nodep);
lineTrack(nodep);
+778 -2354
View File
File diff suppressed because it is too large Load Diff
+52 -47
View File
@@ -37,7 +37,6 @@
#include "V3Dead.h"
#include "V3ConstPool.h"
#include "V3Graph.h"
#include "V3Stats.h"
@@ -247,7 +246,6 @@ 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
const VNUser1InUse m_inuser1;
@@ -281,7 +279,6 @@ class DeadVisitor final : public VNVisitor {
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;
@@ -319,12 +316,6 @@ class DeadVisitor final : public VNVisitor {
m_dtypeElimsp.push_back(nodep);
}
if (VN_IS(m_modp, Package) || VN_IS(m_modp, Class)) m_dtypePkgsp.emplace(nodep, m_modp);
// A struct type moved to the type table refers to its package via classOrPackagep
if (const AstNodeUOrStructDType* const sdtypep = VN_CAST(nodep, NodeUOrStructDType)) {
if (sdtypep->classOrPackagep() && sdtypep->classOrPackagep() != m_modp) {
m_dtypePkgsp.emplace(nodep, sdtypep->classOrPackagep());
}
}
if (AstNode* const subnodep = nodep->virtRefDTypep()) subnodep->user1Inc();
if (AstNode* const subnodep = nodep->virtRefDType2p()) subnodep->user1Inc();
}
@@ -360,16 +351,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);
@@ -378,8 +359,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);
}
@@ -421,11 +400,6 @@ class DeadVisitor final : public VNVisitor {
checkAll(nodep);
needsTask(nodep->ftaskp(), m_containingFTaskRefp);
}
void visit(AstModportVarRef* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->varp()) nodep->varp()->user1Inc();
}
void visit(AstRefDType* nodep) override {
iterateChildren(nodep);
checkDType(nodep);
@@ -531,11 +505,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 {
@@ -575,9 +544,7 @@ class DeadVisitor final : public VNVisitor {
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);
@@ -674,8 +641,7 @@ class DeadVisitor final : public VNVisitor {
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();
const bool keep = !m_elimCells && modp == v3Global.rootp()->dollarUnitPkgp();
if (modp->dead() || (!modp->isTop() && modp->user1() == 0 && !keep)) {
// > 2 because L1 is the wrapper, L2 is the top user module
UINFO(4, " Dead module " << modp);
@@ -686,7 +652,9 @@ class DeadVisitor final : public VNVisitor {
cellp->modp()->user1Inc(-1);
});
}
if (modp->isDollarUnit()) v3Global.rootp()->dollarUnitPkgp(nullptr);
if (modp == v3Global.rootp()->dollarUnitPkgp()) {
v3Global.rootp()->dollarUnitPkgp(nullptr);
}
deleting(modp);
retry = true;
}
@@ -695,7 +663,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
}
@@ -747,8 +715,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
@@ -801,10 +767,48 @@ 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}
@@ -839,6 +843,7 @@ 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
@@ -847,7 +852,7 @@ public:
// 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",
@@ -865,32 +870,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);
}
+15 -41
View File
@@ -300,7 +300,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)
@@ -821,7 +820,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 +870,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
@@ -1021,7 +1020,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
@@ -1191,7 +1190,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 +1241,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);
}
@@ -1270,15 +1266,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 +1276,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),
+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 -35
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;
}
+27 -68
View File
@@ -46,7 +46,6 @@
#include <algorithm>
#include <array>
#include <functional>
#include <map>
#include <new>
#include <type_traits>
#include <unordered_map>
@@ -121,28 +120,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 +128,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 +161,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; }
@@ -274,8 +252,8 @@ public:
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()) {
for (const std::unique_ptr<DfgEdge>& edgep : m_inputps) {
if (DfgVertex* const srcp = edgep->srcp()) {
if (f(*srcp)) return true;
}
}
@@ -290,8 +268,8 @@ public:
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()) {
for (const std::unique_ptr<DfgEdge>& edgep : m_inputps) {
if (const DfgVertex* const srcp = edgep->srcp()) {
if (f(*srcp)) return true;
}
}
@@ -430,19 +408,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 +506,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 +573,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 +826,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");
+11 -12
View File
@@ -409,18 +409,17 @@ public:
// Sub contexts - keep sorted by type
V3DfgAstToDfgContext m_ast2DfgContext;
V3DfgBinToOneHotContext m_binToOneHotContext; // Statistics context for V3DfgBinToOneHot
V3DfgBreakCyclesContext m_breakCyclesContext; // Statistics context for V3DfgBreakCycles
V3DfgCseContext m_cseContext0{"1st"}; // Statistics context for CSE stage 1
V3DfgCseContext m_cseContext1{"2nd"}; // Statistics context for CSE stage 2
V3DfgDfgToAstContext m_dfg2AstContext; // Statistics context for V3DfgDfgToAst
V3DfgPeepholeContext m_peepholeContext; // Statistics context for V3DfgPeephole
V3DfgPushDownSelsContext m_pushDownSelsContext; // Statistics context for V3DfgPushDownSels
V3DfgRegularizeContext m_regularizeContext; // Statistics context for V3DfgRegularize
V3DfgRemoveSelectsContext m_removeSelectsContext; // Stats context for V3DfgRemoveSelects
V3DfgRemoveUnobservableContext
m_removeUnobservableContext; // Stats context for V3DfgRemoveUnobservable
V3DfgSynthesisContext m_synthContext; // Statistics context for V3DfgSynthesize
V3DfgBinToOneHotContext m_binToOneHotContext;
V3DfgBreakCyclesContext m_breakCyclesContext;
V3DfgCseContext m_cseContext0{"1st"};
V3DfgCseContext m_cseContext1{"2nd"};
V3DfgDfgToAstContext m_dfg2AstContext;
V3DfgPeepholeContext m_peepholeContext;
V3DfgPushDownSelsContext m_pushDownSelsContext;
V3DfgRegularizeContext m_regularizeContext;
V3DfgRemoveSelectsContext m_removeSelectsContext;
V3DfgRemoveUnobservableContext m_removeUnobservableContext;
V3DfgSynthesisContext m_synthContext;
// CONSTRUCTOR
V3DfgContext() = default;
+1 -1
View File
@@ -242,7 +242,7 @@ class DfgToAstVisitor final : DfgVisitor {
FileLine* const flp = vtxp->fileline();
AstNodeExpr* const lhsp = convertDfgVertexToAstNodeExpr(vtxp->lhsp());
AstVarScope* const matchp = vtxp->matchp()->as<DfgVertexVar>()->vscp();
m_resultp = new AstMatchMasked{flp, lhsp, new AstVarRef{flp, matchp, VAccess::READ}};
m_resultp = new AstMatchMasked{flp, lhsp, matchp};
}
void visit(DfgRep* vtxp) override {
+8 -1
View File
@@ -82,7 +82,7 @@ class DataflowOptimize final {
const bool hasExtRd = //
varp->isPrimaryIO() // Top level port - readable
|| varp->isSigUserRdPublic() // Readable by user
|| varp->constPoolEntry() // Held in V3ConstPool lookup cache, keep
|| varp->constPoolEntry() // Stored in AstConstPool hashmap, but read only
;
const bool hasExtWr = //
(varp->isPrimaryIO() && varp->isNonOutput()) // Top level port - writable
@@ -183,6 +183,13 @@ class DataflowOptimize final {
}
DataflowOptimize(AstNetlist* netlistp) {
// Mark interfaces that might be referenced by a virtual interface
if (v3Global.hasVirtIfaces()) {
netlistp->typeTablep()->foreach([](const AstIfaceRefDType* nodep) {
if (!nodep->isVirtual()) return;
nodep->ifaceViaCellp()->setHasVirtualRef();
});
}
// Mark variables with external references
markExternallyReferencedVariables(netlistp);
// Dump stage stats
+10 -3
View File
@@ -344,6 +344,9 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
// Fast path exit if we surely don't need to convet anything
if (nTerms < TERM_LIMIT) return;
// Sequence numbers for name generation
size_t nTables = 0;
DfgVertex::ScopeCache scopeCache;
// Create decoders for each srcp
@@ -416,7 +419,8 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
// If there is an existing result variable, use that
if (DfgVertexVar* const vp = srcp->getResultVar()) return vp->as<DfgVarPacked>();
// Otherwise create a new variable
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, "BinToOneHot_Idx", idxDType, scopep);
const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables);
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, name, idxDType, scopep);
vtxp->vscp()->varp()->isInternal(true);
vtxp->srcp(srcp);
return vtxp->as<DfgVarPacked>();
@@ -424,7 +428,8 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
AstVarScope* const idxVscp = idxVtxp->vscp();
// The previous index variable - we don't need a vertex for this
AstVarScope* const preVscp = [&]() {
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, "BinToOneHot_Pre", idxDType, scopep);
const std::string name = dfg.makeUniqueName("BinToOneHot_Pre", nTables);
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, name, idxDType, scopep);
AstVarScope* const vscp = vtxp->vscp();
VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
vscp->varp()->isInternal(true);
@@ -434,13 +439,15 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
}();
// The table variable
DfgVarArray* const tabVtxp = [&]() {
DfgVertexVar* const varp = dfg.makeNewVar(flp, "BinToOneHot_Tab", tabDType, scopep);
const std::string name = dfg.makeUniqueName("BinToOneHot_Tab", nTables);
DfgVertexVar* const varp = dfg.makeNewVar(flp, name, tabDType, scopep);
varp->vscp()->varp()->isInternal(true);
varp->vscp()->varp()->noReset(true);
varp->setHasModWrRefs();
return varp->as<DfgVarArray>();
}();
++nTables;
++ctx.m_decodersCreated;
// Initialize 'tab' and 'pre' variables statically
+3 -1
View File
@@ -209,6 +209,7 @@ class V3DfgPeephole final : public DfgVisitor {
DfgVertex* m_vtxp = nullptr; // Currently considered vertex
size_t m_currentGeneration = 0; // Current generation number
size_t m_lastId = 0; // Last unique vertex ID assigned
size_t m_nTemps = 0; // Number of temporary variables created
// Scope for transient temporariy variables cerated in this pass. They should all be
// eliminated wihtin this pass, so anything should be ok, pick the top scope as easy to find.
AstScope* const m_tmpScopep = v3Global.rootp()->topScopep()->scopep();
@@ -2180,8 +2181,9 @@ class V3DfgPeephole final : public DfgVisitor {
DfgSplicePacked* const sp = new DfgSplicePacked{m_dfg, flp, vtxp->dtype()};
m_vInfo[sp].m_id = ++m_lastId;
sp->addDriver(catp, lsb, flp);
const std::string name = m_dfg.makeUniqueName("PeepholeNarrow", m_nTemps++);
DfgVertexVar* const varp
= m_dfg.makeNewVar(flp, "PeepholeNarrow", vtxp->dtype(), m_tmpScopep);
= m_dfg.makeNewVar(flp, name, vtxp->dtype(), m_tmpScopep);
varp->tmpForp(varp->vscp());
m_vInfo[varp].m_id = ++m_lastId;
varp->vscp()->varp()->isInternal(true);
+4 -2
View File
@@ -30,6 +30,7 @@ class DfgRegularize final {
// STATE
DfgGraph& m_dfg; // The graph being processed
V3DfgRegularizeContext& m_ctx; // The optimization context for stats
size_t m_nTmps = 0; // Number of temporaries added to this graph - for variable names only
VNDeleter m_deleter; // Deletes replacement nodes at the end
// METHODS
@@ -207,10 +208,11 @@ class DfgRegularize final {
if (!needsTemporary(*vtxp, *vtxp)) continue;
// Need to create an intermediate variable
++m_ctx.m_temporariesIntroduced;
const std::string name = m_dfg.makeUniqueName("Regularize", m_nTmps);
FileLine* const flp = vtxp->fileline();
AstScope* const scopep = vtxp->scopep(scopeCache);
DfgVertexVar* const newp
= m_dfg.makeNewVar(flp, "Regularize", vtxp->dtype(), scopep);
DfgVertexVar* const newp = m_dfg.makeNewVar(flp, name, vtxp->dtype(), scopep);
++m_nTmps;
// Replace vertex with the variable, make it drive the variable
vtxp->replaceWith(newp);
newp->srcp(vtxp);
+30 -77
View File
@@ -58,6 +58,8 @@ DfgArraySel* makeVertex<DfgArraySel, AstArraySel>(const AstArraySel* nodep, DfgG
class AstToDfgConverter final : public VNVisitor {
// NODE STATE
// AstNodeExpr/AstVar/AstVarScope::user2p -> DfgVertex* for this Node
// AstVar::user3() -> int temporary counter for variable
const VNUser3InUse m_user3InUse;
// STATE
DfgGraph& m_dfg; // The graph being built
@@ -72,6 +74,8 @@ class AstToDfgConverter final : public VNVisitor {
bool m_foundUnhandled = false; // Found node not implemented as DFG or not implemented 'visit'
bool m_converting = false; // We are trying to convert some logic at the moment
size_t m_nUnpack = 0; // Sequence numbers for temporaries
// METHODS
// Allocate a new non-variable vertex, add it to the currently synthesized logic
@@ -253,9 +257,9 @@ class AstToDfgConverter final : public VNVisitor {
// Represents a DFG assignment contributed by the AST assignment with the above 'lhsp'.
// There might be multiple of these if 'lhsp' is a concatenation.
struct Assignment final {
DfgVertexSplice* m_lhsp; // Splice vertex being driven
uint32_t m_idx; // Position (bit or array index) within m_lhsp being driven
DfgVertex* m_rhsp; // Vertex driving that position
DfgVertexSplice* m_lhsp;
uint32_t m_idx;
DfgVertex* m_rhsp;
Assignment() = delete;
Assignment(DfgVertexSplice* lhsp, uint32_t idx, DfgVertex* rhsp)
: m_lhsp{lhsp}
@@ -269,7 +273,8 @@ class AstToDfgConverter final : public VNVisitor {
// Assigning compound expressions to a concatenated LHS requires a temporary
// to avoid multiple use of the expression
if (VN_IS(lhsp, Concat) && !vtxp->is<DfgVertexVar>() && !vtxp->is<DfgConst>()) {
DfgVertexVar* const tmpp = createTmp(*m_logicp, flp, vtxp->dtype(), "Unpack");
const size_t n = ++m_nUnpack;
DfgVertexVar* const tmpp = createTmp(*m_logicp, flp, vtxp->dtype(), "Unpack", n);
tmpp->srcp(vtxp);
vtxp = tmpp;
}
@@ -491,8 +496,9 @@ public:
// Create temporay variable capable of holding the given type
DfgVertexVar* createTmp(DfgLogic& logic, FileLine* flp, const DfgDataType& dtype,
const std::string& prefix) {
DfgVertexVar* const vtxp = m_dfg.makeNewVar(flp, prefix, dtype, logic.scopep());
const std::string& prefix, size_t tmpCount) {
const std::string name = m_dfg.makeUniqueName(prefix, tmpCount);
DfgVertexVar* const vtxp = m_dfg.makeNewVar(flp, name, dtype, logic.scopep());
logic.synth().emplace_back(vtxp);
vtxp->vscp()->varp()->isInternal(true);
vtxp->tmpForp(vtxp->vscp());
@@ -505,7 +511,8 @@ public:
FileLine* const flp = astVarp->fileline();
const DfgDataType& dtype = *DfgDataType::fromAst(astVarp->dtypep());
const std::string prfx = prefix + "_" + astVarp->name();
DfgVertexVar* const vtxp = createTmp(logic, flp, dtype, prfx);
const size_t tmpCount = astVarp->user3Inc();
DfgVertexVar* const vtxp = createTmp(logic, flp, dtype, prfx, tmpCount);
vtxp->tmpForp(vscp);
return vtxp;
}
@@ -617,6 +624,8 @@ class AstToDfgSynthesize final {
DfgGraph& m_dfg; // The graph being built
V3DfgSynthesisContext& m_ctx; // The context for stats
AstToDfgConverter m_converter; // The convert instance to use for each construct
size_t m_nBranchCond = 0; // Sequence numbers for temporaries
size_t m_nPathPred = 0; // Sequence numbers for temporaries
DfgWorklist m_toRevert{m_dfg}; // We need a worklist for reverting synthesis
// STATE - for current DfgLogic being synthesized
@@ -692,28 +701,6 @@ class AstToDfgSynthesize final {
return drivers;
}
// Returns true if the driver supplies a complete array element.
static bool driverCoversWholeElement(const Driver& driver) {
const DfgUnitArray* const unitp = driver.m_vtxp->cast<DfgUnitArray>();
if (!unitp) return false;
if (DfgVertexSplice* const splicep = unitp->srcp()->cast<DfgVertexSplice>()) {
return splicep->wholep();
}
return true;
}
// Unlike 'wholep', several element drivers can collectively cover an array.
static bool spliceCoversWhole(DfgVertexSplice* const splicep) {
if (splicep->wholep()) return true;
if (splicep->isPacked()) return false;
uint32_t next = 0;
for (const Driver& driver : gatherDrivers(splicep)) {
if (driver.m_lo != next || !driverCoversWholeElement(driver)) return false;
next = driver.m_hi + 1;
}
return next == splicep->size();
}
// Returns true if the driver cone contains any variable introduced by
// tristate lowering. Used to distinguish intentional tristate contributor
// overlap from accidental multidrive.
@@ -1326,42 +1313,6 @@ class AstToDfgSynthesize final {
return propagatedDrivers;
}
// Propagate whole elements with a linear walk over the sorted driver lists.
// Reads use the previous value temporary, preserving assignment-version bindings.
// Partial elements and non-unit drivers retain the nonsynthesized-process fallback.
bool computePropagatedArrayDrivers(const std::vector<Driver>& newDrivers,
DfgVertexVar* const oldp,
std::vector<Driver>& propagatedDrivers) {
// Bound quadratic vertex growth: array drivers cannot be coalesced.
static constexpr uint32_t MAX_ARRAY_ELEMENTS = 32;
if (oldp->size() > MAX_ARRAY_ELEMENTS) return false;
for (const Driver& driver : newDrivers) {
if (!driverCoversWholeElement(driver)) return false;
}
const std::vector<Driver> oldDrivers = gatherDrivers(oldp->srcp()->as<DfgVertexSplice>());
UASSERT_OBJ(!oldDrivers.empty(), oldp, "Should have a proper driver");
for (const Driver& driver : oldDrivers) {
if (!driverCoversWholeElement(driver)) return false;
}
propagatedDrivers.reserve(oldDrivers.size());
auto nIt = newDrivers.begin();
for (const Driver& oDriver : oldDrivers) {
while (nIt != newDrivers.end() && nIt->m_lo < oDriver.m_lo) ++nIt;
if (nIt != newDrivers.end() && nIt->m_lo == oDriver.m_lo) continue;
FileLine* const flp = oDriver.m_flp;
const DfgUnitArray* const oldUnitp = oDriver.m_vtxp->as<DfgUnitArray>();
DfgArraySel* const selp = make<DfgArraySel>(flp, oldUnitp->srcp()->dtype());
selp->fromp(oldp);
selp->bitp(make<DfgConst>(flp, static_cast<size_t>(VL_IDATASIZE), oDriver.m_lo));
DfgUnitArray* const newUnitp = make<DfgUnitArray>(flp, oldUnitp->dtype());
newUnitp->srcp(selp);
propagatedDrivers.emplace_back(newUnitp, oDriver.m_lo, flp);
}
return true;
}
// Given the drivers of a variable after converting a single statement
// 'newp', add drivers from 'oldp' that were not reassigned be drivers
// in newp. This computes the total result of all previous assignments.
@@ -1377,25 +1328,25 @@ class AstToDfgSynthesize final {
// If the old value is the real variable we just computed the new value for,
// then it is the circular feedback into the synthesized block, add it as default driver.
if (oldp->vscp() == vscp) {
if (!spliceCoversWhole(nSplicep)) newp->defaultp(oldp);
if (!nSplicep->wholep()) newp->defaultp(oldp);
return true;
}
UASSERT_OBJ(oldp->srcp(), vscp, "Previously assigned variable has no driver");
// Can't do arrays yet
if (!newp->isPacked()) {
++m_ctx.m_synt.nonSynArray;
return false;
}
// Gather drivers of 'newp' - they are in incresing range order with no overlaps
UASSERT_OBJ(!newp->defaultp(), newp, "Converted value should not have default");
std::vector<Driver> nDrivers = gatherDrivers(newp->srcp()->as<DfgVertexSplice>());
UASSERT_OBJ(!nDrivers.empty(), newp, "Should have a proper driver");
// Additional drivers of 'newp' propagated from 'oldp'
std::vector<Driver> pDrivers;
if (newp->isPacked()) {
pDrivers = computePropagatedDrivers(nDrivers, oldp);
} else if (!computePropagatedArrayDrivers(nDrivers, oldp, pDrivers)) {
++m_ctx.m_synt.nonSynArray;
return false;
}
std::vector<Driver> pDrivers = computePropagatedDrivers(nDrivers, oldp);
if (!pDrivers.empty()) {
// Need to merge propagated sources, so reset the splice
@@ -1406,13 +1357,13 @@ class AstToDfgSynthesize final {
std::merge(nDrivers.begin(), nDrivers.end(), pDrivers.begin(), pDrivers.end(),
std::back_inserter(drivers));
// Coalesce adjacent ranges
if (newp->isPacked()) coalesceDrivers(drivers);
coalesceDrivers(drivers);
// Reinsert drivers in order
for (const Driver& d : drivers) nSplicep->addDriver(d.m_vtxp, d.m_lo, d.m_flp);
}
// If the old had a default, add to the new one too, unless redundant
if (oldp->defaultp() && !spliceCoversWhole(nSplicep)) newp->defaultp(oldp->defaultp());
if (oldp->defaultp() && !nSplicep->wholep()) newp->defaultp(oldp->defaultp());
// Done
return true;
@@ -1530,11 +1481,12 @@ class AstToDfgSynthesize final {
return resp;
}();
size_t n = m_nPathPred++; // Sequence number for temporaries
const DfgDataType& dtype = predp->dtype();
const auto mkTmp = [&](FileLine* flp, const char* name, DfgVertex* srcp) {
const std::string prefix = "_BB" + std::to_string(bb.id()) + "_" + name;
DfgVertexVar* const tmpp = m_converter.createTmp(*m_logicp, flp, dtype, prefix);
DfgVertexVar* const tmpp = m_converter.createTmp(*m_logicp, flp, dtype, prefix, n);
tmpp->srcp(srcp);
return tmpp;
};
@@ -1707,7 +1659,8 @@ class AstToDfgSynthesize final {
FileLine* const flp = condp->fileline();
const DfgDataType& dtype = condp->dtype();
const std::string prefix = "_BB" + std::to_string(bb.id()) + "_Cond";
DfgVertexVar* const vp = m_converter.createTmp(*m_logicp, flp, dtype, prefix);
const size_t n = m_nBranchCond++;
DfgVertexVar* const vp = m_converter.createTmp(*m_logicp, flp, dtype, prefix, n);
vp->srcp(condp);
m_bbToCondp[bb] = vp;
}

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