mirror of
https://github.com/verilator/verilator.git
synced 2026-10-02 16:13:34 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
983a06a16f | ||
|
|
3d2421f3bf | ||
|
|
28c006ee8c | ||
|
|
193b1d2c9f | ||
|
|
147fe4ee9f | ||
|
|
c233a39052 | ||
|
|
1264184fbb | ||
|
|
5d1992ef05 | ||
|
|
f4369ed694 | ||
|
|
7e7b6d7fb8 | ||
|
|
d915745073 | ||
|
|
0a9d9db5ae | ||
|
|
318b1c1a34 | ||
|
|
0dc93c1d5c | ||
|
|
ac3f30ed6c | ||
|
|
447ab12cec | ||
|
|
2cb1a8de73 | ||
|
|
1d79f5c59b | ||
|
|
065f36ab56 | ||
|
|
c561fe8baf | ||
|
|
2b4852048d | ||
|
|
0144e3ebec | ||
|
|
d410e551f8 | ||
|
|
344f87abe3 | ||
|
|
57c816f906 | ||
|
|
82e1dbe162 | ||
|
|
e6b0bdd4df | ||
|
|
4f13c4d1bb | ||
|
|
c37ec19751 | ||
|
|
aaaf8e75af | ||
|
|
8cc31c6cf1 | ||
|
|
8468af1a20 | ||
|
|
db39d70c75 | ||
|
|
04fff7ebd8 | ||
|
|
b154af87a1 | ||
|
|
8b7480806a | ||
|
|
e29f5ad559 | ||
|
|
4618fd5a43 | ||
|
|
ed979c6aa1 | ||
|
|
141c5da3f9 | ||
|
|
0185ee5df3 | ||
|
|
27c08a65e1 | ||
|
|
de408a5eb7 | ||
|
|
fa3c072dd1 | ||
|
|
aac4b7090a | ||
|
|
f0d664530d | ||
|
|
702140bf93 | ||
|
|
2075db3db0 | ||
|
|
b7fec513cf | ||
|
|
78e7758022 | ||
|
|
818318ddd5 | ||
|
|
39eea781bc | ||
|
|
c4f7bd6a04 | ||
|
|
dd03d0f3c9 | ||
|
|
369ce6af2c | ||
|
|
f83817510d | ||
|
|
bfcd779cfb | ||
|
|
e69c380205 | ||
|
|
61e14aa827 | ||
|
|
0c6c83e278 | ||
|
|
6f52208b68 |
@@ -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) }}'
|
||||
@@ -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
|
||||
|
||||
@@ -64,10 +64,10 @@ jobs:
|
||||
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4
|
||||
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4
|
||||
with:
|
||||
buildkitd-flags: --debug
|
||||
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Build and Push to Docker
|
||||
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
|
||||
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
context: ${{ env.build_context }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -69,11 +69,11 @@ jobs:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deploy-1
|
||||
continue-on-error: true
|
||||
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
|
||||
- name: Deploy to GitHub Pages (retry)
|
||||
id: deploy-2
|
||||
if: steps.deploy-1.outcome == 'failure'
|
||||
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
|
||||
|
||||
notify:
|
||||
name: Notify
|
||||
|
||||
@@ -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' }}
|
||||
@@ -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"
|
||||
@@ -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 }}"
|
||||
@@ -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 }}"
|
||||
@@ -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: |-
|
||||
|
||||
@@ -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() }}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -72,7 +72,7 @@ top-of-file comment.
|
||||
- Build in the source tree: `autoconf && ./configure && make -j8`. Configure with
|
||||
`--enable-ccwarn` so a new compiler warning stops the build.
|
||||
- Run one test from the repository root: `test_regress/t/t_<name>.py`.
|
||||
- Run the full regression tests with `make test`. The complete suite requires
|
||||
- Run the full regression with `make test`. The complete suite requires
|
||||
configuring with `--enable-longtests` (works on every OS, including macOS).
|
||||
|
||||
______________________________________________________________________
|
||||
@@ -93,8 +93,8 @@ ______________________________________________________________________
|
||||
- [ ] Ran `make format` (clang-format), `make cppcheck`, and `make lint-py`;
|
||||
self-reviewed the diff for leftover debug code, stale comments, and
|
||||
copy-paste errors.
|
||||
- [ ] Ran the full regression tests on at least one OS before submitting. Partial
|
||||
runs are fine during development, but the submitted PR is expected to pass every
|
||||
- [ ] Ran the full regression on at least one OS before submitting. Partial runs
|
||||
are fine during development, but the submitted PR is expected to pass every
|
||||
test.
|
||||
- [ ] Validated the code changes against recommendations in the AGENTS.md files.
|
||||
- [ ] Did not edit `docs/CONTRIBUTORS` (humans only) or `Changes` (maintainer
|
||||
@@ -143,7 +143,6 @@ The API you choose determines which test must accompany the change.
|
||||
|
||||
## Commits
|
||||
|
||||
- Subject line is short and imperative and conventionally ends with the
|
||||
issue number being fixed. No need for the PR number this will be added
|
||||
during git squash. e.g `Support property case (#7721)`. The commit body message is
|
||||
optional and common for non-trivial changes.
|
||||
- Subject line is short and imperative and conventionally ends with the PR number:
|
||||
`Support property case (#7721)`. A body is optional and common for non-trivial
|
||||
changes.
|
||||
|
||||
+2
-31
@@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15)
|
||||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||
project(
|
||||
Verilator
|
||||
VERSION 5.053
|
||||
VERSION 5.052
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
@@ -93,36 +93,7 @@ if(OBJCACHE_ENABLED)
|
||||
endif()
|
||||
|
||||
find_package(BISON)
|
||||
# Prefer the header installed with the selected generator, including through
|
||||
# symlinks such as Homebrew's bin/flex. Explicit cache entries take precedence.
|
||||
find_program(
|
||||
FLEX_EXECUTABLE
|
||||
NAMES flex win-flex win_flex
|
||||
DOC "Path to the flex executable"
|
||||
)
|
||||
if(NOT FLEX_EXECUTABLE)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"Cannot find Flex; set FLEX_EXECUTABLE to its executable path"
|
||||
)
|
||||
endif()
|
||||
get_filename_component(flex_real_executable "${FLEX_EXECUTABLE}" REALPATH)
|
||||
get_filename_component(flex_bin_dir "${flex_real_executable}" DIRECTORY)
|
||||
find_path(
|
||||
FLEX_INCLUDE_DIR
|
||||
FlexLexer.h
|
||||
PATHS "${flex_bin_dir}/../include" "${flex_bin_dir}"
|
||||
NO_DEFAULT_PATH
|
||||
DOC "Path to the flex headers"
|
||||
)
|
||||
find_path(FLEX_INCLUDE_DIR FlexLexer.h)
|
||||
if(NOT FLEX_INCLUDE_DIR)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"Cannot find FlexLexer.h; set FLEX_INCLUDE_DIR to its directory"
|
||||
)
|
||||
endif()
|
||||
mark_as_advanced(FLEX_EXECUTABLE FLEX_INCLUDE_DIR)
|
||||
find_package(FLEX)
|
||||
|
||||
# Build
|
||||
#set_property(GLOBAL PROPERTY JOB_POOLS one_job=1)
|
||||
|
||||
@@ -10,154 +10,6 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested or implemented a given issue are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.053 devel
|
||||
==========================
|
||||
|
||||
**Other:**
|
||||
|
||||
* Add covergroup runtime registry (#8134). [Matthew Ballance]
|
||||
* Add initial support for solo `binsof` (#8298). [Marco Bartoli]
|
||||
* Add `-fno-dead-members` and optimize dead class member functions (#8830).
|
||||
* Add `--coverage-max-bins` and `--coverage-max-real-bins` (#8499). [Marco Bartoli]
|
||||
* Remove deprecated `--structs-packed`.
|
||||
* Change internal module inlining to after V3Scope (#1539) (#8453). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Change JSON dumps to suppress empty strings, etc.
|
||||
* Change `--protect` to use SHA512 per IEEE 1735.
|
||||
* Support multi-dimensional module instance arrays (#2675) (#8476). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Support VPI interface references (#8081). [Todd Strader]
|
||||
* Support nested array and associative array element member access in constraint 1/4 - pre-cleanup (#8237). [Kamil Danecki, Antmicro Ltd.]
|
||||
* Support unique constraint inside std::randomize (#8241). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Support nested array and associative array element member access in constraint (#8244). [Kamil Danecki, Antmicro Ltd.]
|
||||
* Support rand dynamic arrays used inside `with` clause of randomize (#8246). [Kamil Danecki, Antmicro Ltd.]
|
||||
* Support multiplicity in SVA shapes that use NFA rings (#8267). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support non-overlapped property implications correctly (#8268). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support logic and set operations on binsof (#8306). [Marco Bartoli]
|
||||
* Support `illegal_bins` and `ignore_bins` for cross bins (#8372). [Marco Bartoli]
|
||||
* Support `iff` in covergroup bins (#8406) (#8407). [Marco Bartoli]
|
||||
* Support enclosing cross identifiers in bin selections (#8416). [Marco Bartoli]
|
||||
* Support dynamic cross layouts and fix covergroup bin exclusions priority (#8424). [Marco Bartoli]
|
||||
* Support automatic bins for enum coverpoints (#8462) (#8464). [David Harris]
|
||||
* Support empty queue literals as function and task arguments (#8501) (#8502). [Francesco Urbani]
|
||||
* Support covergroup fixed-size bin arrays (#8514). [Marco Bartoli]
|
||||
* Support wildcard bins (#8516). [Marco Bartoli]
|
||||
* Support coverpoint bin `with` filters (#8518). [Marco Bartoli]
|
||||
* Support waiting on clocking block event through modport (#8403) (#8530). [Saqib Khan]
|
||||
* Optimize NBA shadow copies for partial writes (#8266). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize recursion depth in DFG circular driver tracing (#8272). [Drew Risinger]
|
||||
* Optimize DFG synthesis symbol table ordering (#8282). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize redundant vertex cache lookups in DFG peephole (#8283). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DFG vertex iteration callbacks (#8284). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize redundant memoization in DFG CSE (#8285). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DFG synthesis of simple continuous assignments (#8286). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize more selects in DFG (#8292). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize netlist teardown when about to exit (#8294). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DFG algorithms with an open addressing hash table (#8307). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize data-type type chain recursion (#8310). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DFG vertex input edge storage (#8313). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize successive whole-element array assignments (#8316). [Patrick Creighton]
|
||||
* Optimize unnecessary per-vertex allocations in NFA lowering (#8319). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize duplicate lookup in NFA property recursion guard (#8321). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize NBA assertions by keeping all writers before readers (#8326). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize wide four-state constant operations in V3Number (#8333). [CatalpaEel]
|
||||
* Optimize split pass (#8385). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize covergroup bin metadata lookup (#8404). [Marco Bartoli]
|
||||
* Optimize dead ports (#8426). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize instance port connections by aliasing rather than by assignment (#8427). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DFG temporary sharing across module instances (#8456). [Michael Bedford Taylor]
|
||||
* Optimize modport resolution with many interface instances (#8496) (#8497). [Deanyone Su]
|
||||
* Optimize covergroup bin generation (#8499). [Marco Bartoli]
|
||||
* Optimize through-package variables in DFG. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize assertion control queries (#8547). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize sched-initial/static/final split to once per sub-function (#8549). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Optimize exhaustive case if-chains with an unconditional else (#8554). [Nick Brereton]
|
||||
* Move module inlining after V3Scope (#8453) (#1539). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix array slice crash when used as a bare value (#5132) (#8262). [Marco Frank]
|
||||
* Fix function reference into generate case block (#5312) (#8532). [Hongseok Choi]
|
||||
* Fix pattern type matching against nested aggregates (#7304).
|
||||
* Fix covergroup-internal references multi-threaded ordering (#7779) (#8255). [Matthew Ballance]
|
||||
* Fix typedef through a parameterized sub-interface (#7943 repair) (#8124) (#8128). [em2machine]
|
||||
* Fix detecting non-inlined awaits in V3Delayed.cpp (#7982). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Fix phased `solve...before` diversity (#8189) (#8194). [Aditya Shevade]
|
||||
* Fix array-size constraint on arrays inside base class (#8219). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix multidimensional packed array type handling (#8235). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix library method argument references (#8252). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix CMake to call Verilator Perl wrapper (#8269) (#8270) (#8311). [Drew Risinger]
|
||||
* Fix real values and unenforced rand/randc type-eligibility rules in constraints (#8273) (#8275) (#8276). [Aditya Shevade]
|
||||
* Fix assignment pattern default fill of an array of unpacked structs (#8289). [Nick Brereton]
|
||||
* Fix escape strings in pattern formatting (#8297). [Marco Bartoli]
|
||||
* Fix DECLFILENAME warning on nested declarations (#8302). [Martijn Wobbes]
|
||||
* Fix cache of boundary vertices in DFG cycle-breaking (#8308). [Shriyans Sahoo]
|
||||
* Fix property and sequence line coverage (#8314). [Andrii, Antmicro Ltd.]
|
||||
* Fix wide typed pattern formatting primitives (#8325). [Marco Bartoli]
|
||||
* Fix circular class inheritance hang (#8324) (#8545). [Saqib Khan]
|
||||
* Fix V3InstrCount with recursive functions (#8327) (#8361). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix parameterized class nested in a parameterized interface (#8328) (#8329). [Kristof Marien]
|
||||
* Fix printing signed enums and wide enum names (#8338). [Marco Bartoli]
|
||||
* Fix ignoring cross_auto_bin_max instead of hard error (#8340). [Marco Bartoli]
|
||||
* Fix wide enum names (#8344) (#8362) (#8365) (#8366). [Marco Bartoli]
|
||||
* Fix class-qualified self reference to a typedef in a parameterized class (#8348) (#8349). [Dragon-Git]
|
||||
* Fix crash when elaborating a covergroup option through a forward class (#8351) (#8352). [Kristof Marien]
|
||||
* Fix missing implicit net for bufif/notif/nmos/pmos data input (#8353) (#8354). [Mike Murunov]
|
||||
* Fix %p format on class members (#8360). [Marco Bartoli]
|
||||
* Fix Docker compilation with --trace-fst (#8370). [Giuseppe Manzoni]
|
||||
* Fix NFA rings for bounded SVA delays (#8371). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix DFG driver tracing of partial assignments (#8377) (#8397). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix unique constraint with multiple arrays (#8378). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix access types of refs and inouts (#8380). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Fix copy constructor of classes with constraints (#8381). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix C++ clang 18 compiler with -fno-slp-vectorize (#8383). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix reevaluation of method calls in wait conditions (#8391). [Jesús Alberto López Soto]
|
||||
* Fix weighted dist constraints in presence of other constraints (#8392). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix error on non-existent interface ports (#8393). [Andrii Andrieiev, Antmicro Ltd.]
|
||||
* Fix compile-time blowup forcing large unpacked arrays (#8395). [Jonathan Drolet]
|
||||
* Fix false MULTIDRIVEN for writes in tasks never executed (#8401). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix $c() inside a DPI export (#8402). [Nazar Zibilyuk]
|
||||
* Fix covergroup constructor initialization (#8410). [Marco Bartoli]
|
||||
* Fix issues with configuring flex during build on MacOS (#8412). [Kaleb Barrett]
|
||||
* Fix interface modport resolution against non-scope symbol tables (#8420). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix FSM coverage hierarchy being emitted as an absolute path (#8421). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix MSYS header include search to be after toolchain headers (#8422). [Kaleb Barrett]
|
||||
* Fix losing coverage declarations if first in a module body (#8425). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix force read update sensitivity performance (#8431) (#8433). [Jonathan Drolet]
|
||||
* Fix streaming dynamic arrays with different element types failing to compile (#8434) (#8435). [Kristof Marien]
|
||||
* Fix internal error when task without C function is left in the tree (#8437). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix nested random dynamic arrays internal error (#8438) (#8439). [Kristof Marien]
|
||||
* Fix elaboration of classes reached through deferred references (#8440) (#8510). [Ethan Sifferman]
|
||||
* Fix VPI discoverability of forceable unpacked struct members (#8445). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix compound types in anonymous structs (#8449). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix internal error on covergroup in a specialized parameterized class (#8450) (#8452). [David Harris]
|
||||
* Fix data race in type handling (#8451). [Tynan McAuley]
|
||||
* Fix $sscanf/$fscanf returning -1 after a partial match (#8457) (#8459). [David Harris]
|
||||
* Fix covergroup '0/'1 bin values not filling to coverpoint width (#8458) (#8460). [David Harris]
|
||||
* Fix embedded covergroup calling an enclosing class method (#8461) (#8463). [David Harris]
|
||||
* Fix unsized condition not reduced to one bit (#8465) (#8466). [David Harris]
|
||||
* Fix comma emission for nested JSON scopes (#8469) (#8474). [Noam Gallmann]
|
||||
* Fix model failing when host has fewer cores than --threads (#8475). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix `##0` synchronous drive overriding later drives (#8482). [Marco Bartoli]
|
||||
* Fix disable fork and wait fork affecting pending NBA updates (#8483). [Marco Bartoli]
|
||||
* Fix nonblocking event trigger never firing in suspendable processes (#8485). [Marco Bartoli]
|
||||
* Fix setting status of finished process immediately (#8503). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix constant oversized shifts in V3Premit. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix semaphore waiting on killed process (#8505). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix mis-setting of process state from FINISHED to KILLED (#8506). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix rand_mode(0) not preserving dynamic array of struct values used in a dist constraint (#8508) (#8509). [Kristof Marien]
|
||||
* Fix duplicate struct declaration when inlining (#8515). [Ethan Sifferman]
|
||||
* Fix bear version detection if uninstalled. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix enums inside randomize() with (#8517). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix emitting public parameters (#8520). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix verilator_coverage report to align with get_coverage() (#8522) (#8524) (#8531). [Marco Bartoli]
|
||||
* Fix verilator_coverage reports rounding incomplete coverage to 100.0% (#8523). [Jordan Carlin]
|
||||
* Fix extern class method rejects matching virtual interface argument type (#8525) (#8526). [Kristof Marien]
|
||||
* Fix of queue pop return value access (#8529) (#8511). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Fix derived class construction resets inherited per-instance rand_mode state (#8527) (#8528). [Kristof Marien]
|
||||
* Fix crash on class handle passed to string port (#8536) (#8538). [Noam Gallmann]
|
||||
* Fix stack depth calculations over calls (#8544). [Nick Brereton]
|
||||
* Fix cascading errors after unsupported class dot reference in parameter value (#8551). [Martijn Wobbes, Antmicro Ltd.]
|
||||
* Fix vpi_put_value crash on NULL value pointer (#8553). [Nick Brereton]
|
||||
* Fix --hierarchical parameter specialization name collisions (#8555) (#8565). [Marco Bartoli]
|
||||
* Fix dehash of a suffix after a hashed name (#8556). [Ethan Sifferman]
|
||||
|
||||
|
||||
Verilator 5.052 2026-09-05
|
||||
==========================
|
||||
|
||||
@@ -2003,7 +1855,7 @@ Verilator 5.016 2023-09-16
|
||||
* Optimize Verilator executable size by refactoring error reporting routines (#4446). [Anthony Donlon]
|
||||
* Optimize Verilation runtime pointers and graphs (#4396) (#4397) (#4398). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Optimize preparations towards multithreaded Verilation (#4291) (#4463) (#4476) (#4477) (#4479). [Kamil Rakoczy, Antmicro Ltd]
|
||||
* Fix Windows filename format, etc (#3873) (#4421). [Anthony Donlon]
|
||||
* Fix Windows filename format, etc (#3873) (#4421). [Anthony Donlon].
|
||||
* Fix t_dist_cppstyle Perl performance issue (#4085). [Srinivasan Venkataramanan]
|
||||
* Fix using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon]
|
||||
* Fix false INFINITELOOP on forever..mailbox.get() (#4323). [Srinivasan Venkataramanan]
|
||||
@@ -3788,7 +3640,7 @@ Verilator 3.880 2015-12-19
|
||||
* Fix model restore crash. (#1013) [Jason McMullan]
|
||||
* Fix arrayed instances to unpacked of same size. (#1015) [Varun Koyyalagunta]
|
||||
* Fix slices of unpacked arrays with non-zero LSBs.
|
||||
* Fix ternary operation with unpacked array. (#1017) [Varun Koyyalagunta]
|
||||
* Fix ternary operation with unpacked array. (#1017) [Varun Koyyalagunta].
|
||||
|
||||
|
||||
Verilator 3.878 2015-11-01
|
||||
@@ -3878,7 +3730,7 @@ Verilator 3.870 2015-02-12
|
||||
|
||||
* Suppress COMBDLY when inside always_latch. (#864) [Iztok Jeras]
|
||||
* Support cast operator with expression size. (#865) [Iztok Jeras]
|
||||
* Add warning on slice selection out of bounds. (#875) [Cong Van Nguyen]
|
||||
* Add warning on slice selection out of bounds. (#875) [Cong Van Nguyen].
|
||||
* Fix member select error broke in 3.868. (#867) [Iztok Jeras]
|
||||
* Fix $sccanf from string. (#866) [David Pierce]
|
||||
* Fix VM_PARALLEL_BUILDS broke in 3.868. (#870) [Hiroki Honda]
|
||||
|
||||
@@ -747,7 +747,6 @@ FASTCOV_OPT += BROKEN_RTN
|
||||
FASTCOV_OPT += NUM_ASSERT
|
||||
FASTCOV_OPT += SELF_CHECK
|
||||
FASTCOV_OPT += UASSERT
|
||||
FASTCOV_OPT += UDEBUGONLY
|
||||
FASTCOV_OPT += UINFO
|
||||
FASTCOV_OPT += assert
|
||||
FASTCOV_OPT += 'if (VL_UNCOVERABLE'
|
||||
@@ -763,7 +762,6 @@ GENHTML_OPT += --rc branch_coverage=1
|
||||
GENHTML_OPT += --rc genhtml_hi_limit=100
|
||||
GENHTML_OPT += --ignore-errors negative
|
||||
GENHTML_OPT += --ignore-errors inconsistent
|
||||
GENHTML_OPT += --ignore-errors empty
|
||||
ifeq ($(COVERAGE_REF_BASE),)
|
||||
GENHTML_OPT += --header-title "Code coverage for Verilator $(shell git describe --dirty)"
|
||||
else
|
||||
|
||||
+11
-11
@@ -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
|
||||
===========
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -158,7 +158,7 @@ NEXTHIST_TEMPLATE
|
||||
# 'pr-notification' for a single run, as the artifacts endpoint lists
|
||||
# artifacts across all run attempts, and a re-run uploads a new one while
|
||||
# keeping the previous attempt's artifact.
|
||||
ARTIFACT_IDS=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts?name=pr-notification" --jq '.artifacts[].id')
|
||||
ARTIFACT_IDS=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
|
||||
|
||||
# Delete them all, so we only notify once
|
||||
for ARTIFACT_ID in ${ARTIFACT_IDS}; do
|
||||
|
||||
@@ -25,8 +25,6 @@ CASES = {
|
||||
("BlackParrot:2x2:*", 14),
|
||||
("BlackParrot:4x4:*", 25),
|
||||
("Caliptra:default:*", 19),
|
||||
("HammerBlade:16x8:*", 5),
|
||||
("HammerBlade:2x1:*", 3),
|
||||
("HummingbirdV2-E203:default:*", 3),
|
||||
("HummingbirdV2-E203:nodelay:*", 3),
|
||||
("NVDLA:*", 29),
|
||||
@@ -61,8 +59,6 @@ CASES = {
|
||||
("BlackParrot:2x2:*", 9),
|
||||
("BlackParrot:4x4:*", 12),
|
||||
("Caliptra:default:*", 16),
|
||||
("HammerBlade:16x8:*", 4),
|
||||
("HammerBlade:2x1:*", 2),
|
||||
("HummingbirdV2-E203:default:*", 10),
|
||||
("HummingbirdV2-E203:nodelay:*", 10),
|
||||
("NVDLA:*", 28),
|
||||
@@ -114,8 +110,6 @@ CASES_PR = {
|
||||
("BlackParrot:1x1:*", 5),
|
||||
("BlackParrot:4x4:*", 25),
|
||||
("Caliptra:default:*", 19),
|
||||
("HammerBlade:16x8:*", 5),
|
||||
("HammerBlade:2x1:*", 3),
|
||||
("NVDLA:*", 29),
|
||||
("HummingbirdV2-E203:default:*", 3),
|
||||
("HummingbirdV2-E203:nodelay:*", 3),
|
||||
@@ -148,8 +142,6 @@ CASES_PR = {
|
||||
("BlackParrot:1x1:*", 5),
|
||||
("BlackParrot:4x4:*", 12),
|
||||
("Caliptra:default:*", 16),
|
||||
("HammerBlade:16x8:*", 4),
|
||||
("HammerBlade:2x1:*", 2),
|
||||
("NVDLA:*", 28),
|
||||
("HummingbirdV2-E203:default:*", 10),
|
||||
("HummingbirdV2-E203:nodelay:*", 10),
|
||||
|
||||
@@ -29,7 +29,6 @@ RUN apt-get update \
|
||||
python3 \
|
||||
zlib1g \
|
||||
zlib1g-dev \
|
||||
liblz4-dev \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
+1
-18
@@ -12,7 +12,7 @@
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.053 devel],
|
||||
AC_INIT([Verilator],[5.052 2026-09-05],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
@@ -264,7 +264,6 @@ fi
|
||||
python3_version=$($PYTHON3 --version | head -1)
|
||||
AC_MSG_RESULT([$PYTHON3 --version = $python3_version])
|
||||
|
||||
AC_ARG_VAR([LEX], [Flex executable])
|
||||
AC_CHECK_PROG(LEX,flex,flex)
|
||||
if test "x$LEX" = "x" ; then
|
||||
AC_MSG_ERROR([Cannot find "flex" in your PATH, please install it])
|
||||
@@ -272,22 +271,6 @@ fi
|
||||
flex_version=$($LEX --version | head -1)
|
||||
AC_MSG_RESULT([$LEX --version = $flex_version])
|
||||
|
||||
# Use the header installed with the selected generator.
|
||||
flex_include=$($PYTHON3 -c '
|
||||
import pathlib
|
||||
import shutil
|
||||
import sys
|
||||
print(pathlib.Path(shutil.which(sys.argv@<:@1@:>@) or sys.argv@<:@1@:>@).resolve().parent.parent / "include")
|
||||
' "$LEX")
|
||||
if test -f "$flex_include/FlexLexer.h"; then
|
||||
if test -n "$MSYSTEM"; then
|
||||
# Search MSYS Flex headers after the active toolchain's system headers.
|
||||
CPPFLAGS="$CPPFLAGS -idirafter $flex_include"
|
||||
else
|
||||
CPPFLAGS="$CPPFLAGS -I$flex_include"
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_CHECK_PROG(YACC,bison,bison)
|
||||
if test "x$YACC" = "x" ; then
|
||||
AC_MSG_ERROR([Cannot find "bison" in your PATH, please install it])
|
||||
|
||||
+1
-22
@@ -7,7 +7,6 @@ contribution terms including the AI policy in ``docs/CONTRIBUTING.rst``.
|
||||
|
||||
Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
||||
|
||||
00>> Please insert into sorted position, and see header as to agreement
|
||||
24bit-xjkp
|
||||
404allen404
|
||||
Adam Bagley
|
||||
@@ -47,7 +46,6 @@ BRDR LIFE
|
||||
Brian Li
|
||||
Cameron Kirk
|
||||
Cameron Waite
|
||||
CatalpaEel
|
||||
Chih-Mao Chen
|
||||
Chris Bachhuber
|
||||
Chris Randall
|
||||
@@ -63,13 +61,11 @@ Daniel Bates
|
||||
Danny Oler
|
||||
Dave Sargeant
|
||||
David Garau
|
||||
David Harris
|
||||
David Horton
|
||||
David Ledger
|
||||
David Metz
|
||||
David Stanford
|
||||
David Turner
|
||||
Deanyone Su
|
||||
dependabot[bot]
|
||||
Dercury
|
||||
Demin Han
|
||||
@@ -78,11 +74,9 @@ Dominick Grochowina
|
||||
Don Williamson
|
||||
Dragon-Git
|
||||
Drew Ranck
|
||||
Drew Risinger
|
||||
Drew Taussig
|
||||
Driss Hafdi
|
||||
Edgar E. Iglesias
|
||||
Edmund Lam
|
||||
Eddie Rydell
|
||||
em2machine
|
||||
emmettifelts
|
||||
@@ -98,7 +92,6 @@ Fan Shupei
|
||||
february cozzocrea
|
||||
Felix Neumärker
|
||||
Felix Yan
|
||||
Francesco Urbani
|
||||
Frans Skarman
|
||||
Fuad Ismail
|
||||
Furqan Nadir
|
||||
@@ -120,7 +113,6 @@ Han Qi
|
||||
Harald Heckmann
|
||||
Hennadii Chernyshchyk
|
||||
hjsanjana
|
||||
Hongseok Choi
|
||||
Howard Su
|
||||
Huang Rui
|
||||
Huanghuang Zhou
|
||||
@@ -153,7 +145,6 @@ jeffrey.song
|
||||
Jens Yuechao Liu
|
||||
Jeremy Bennett
|
||||
Jesse Taube
|
||||
Jesús Alberto López Soto
|
||||
Jevin Sweval
|
||||
Jiacheng Qian
|
||||
Jiamin Zhu
|
||||
@@ -191,7 +182,6 @@ Keith Colbert
|
||||
Kevin Kiningham
|
||||
Kevin Nygaard
|
||||
Kornel Uriasz
|
||||
Kristof Marien
|
||||
Kritik Bhimani
|
||||
Krzysztof Bieganski
|
||||
Krzysztof Boronski
|
||||
@@ -215,13 +205,11 @@ Maciej Sobkowski
|
||||
Marcel Chang
|
||||
Marco Bartoli
|
||||
Marco Brambilla
|
||||
Marco Frank
|
||||
Marco Widmer
|
||||
Mariusz Glebocki
|
||||
Markus Krause
|
||||
Marlon James
|
||||
Marshal Qiao
|
||||
Martijn Wobbes
|
||||
Martin Schmidt
|
||||
Martin Stadler
|
||||
Mateusz Gancarz
|
||||
@@ -236,7 +224,6 @@ Michael Rogenmoser
|
||||
Michal Czyz
|
||||
Michaël Lefebvre
|
||||
Miguel Perez Andrade
|
||||
Mike Murunov
|
||||
Mike Popoloski
|
||||
Miodrag Milanović
|
||||
Mladen Slijepcevic
|
||||
@@ -249,11 +236,9 @@ Natan Kreimer
|
||||
Nathan Graybeal
|
||||
Nathan Kohagen
|
||||
Nathan Myers
|
||||
Nazar Zibilyuk
|
||||
Nick Brereton
|
||||
Nikolai Kumar
|
||||
Nikolay Puzanov
|
||||
Noam Gallmann
|
||||
Nolan Poe
|
||||
Oleh Maksymenko
|
||||
Patrick Creighton
|
||||
@@ -296,7 +281,6 @@ Ryan Ziegler
|
||||
Ryszard Rozak
|
||||
Saksham Gupta
|
||||
Samuel Riedel
|
||||
Saqib Khan
|
||||
Sean Cross
|
||||
Sebastien Van Cauwenberghe
|
||||
Secturion
|
||||
@@ -307,7 +291,6 @@ Seth Pellegrino
|
||||
Shashvat Prabhu
|
||||
Shogo Yamazaki
|
||||
Shou-Li Hsu
|
||||
Shriyans Sahoo
|
||||
spomatasmd
|
||||
Srinivasan Venkataramanan
|
||||
Stefan Wallentowitz
|
||||
@@ -316,10 +299,8 @@ Steven Hugg
|
||||
Stuart Morris
|
||||
sumpster
|
||||
Szymon Gizler
|
||||
Szymon Jędras
|
||||
Sören Tempel
|
||||
Sumanth Kadiyala
|
||||
Sunimali Rathnayake
|
||||
Teng Huang
|
||||
Thomas Aldrian
|
||||
Thomas Brown
|
||||
@@ -340,11 +321,9 @@ Tracy Narine
|
||||
Trung Nguyen
|
||||
Tudor Timi
|
||||
Tymoteusz Blazejczyk
|
||||
Tynan McAuley
|
||||
Tyrone Marhguy
|
||||
Udaya Raj Subedi
|
||||
Udi Finkelstein
|
||||
UnsignedByte
|
||||
Unai Martinez-Corral
|
||||
Valentin Atepalikhin
|
||||
Varun Koyyalagunta
|
||||
@@ -378,4 +357,4 @@ Zixi Li
|
||||
Zubin Jain
|
||||
Àlex Torregrosa
|
||||
Ícaro Lima
|
||||
ÍÍ>> Please insert into sorted position, and see header as to agreement
|
||||
Sunimali Rathnayake
|
||||
|
||||
@@ -1,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
|
||||
|
||||
@@ -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
|
||||
-------------------------
|
||||
|
||||
@@ -18,6 +18,14 @@ C++14 compiler support
|
||||
the Ubuntu LTS versions of GCC and clang use C++20 by default, estimated
|
||||
May 2028.)
|
||||
|
||||
`--structs-packed` option
|
||||
The :vlopt:`--structs-packed` option was introduced when Verilator was
|
||||
first implementing unpacked structs. That feature has been stable now
|
||||
for multiple years, so :vlopt:`--structs-packed` should no longer be
|
||||
used. Thus :vlopt:`--structs-packed` will change to a no-operation flag
|
||||
and the related :option:`UNPACKED` warning will never be issued no
|
||||
sooner than September 2026.
|
||||
|
||||
tcmalloc support
|
||||
Verilator currently supports the default malloc, tcmalloc, or jemalloc.
|
||||
As jemalloc has better performance, support for tcmalloc may be removed
|
||||
|
||||
@@ -316,33 +316,6 @@ Summary:
|
||||
|
||||
Enables basic block line coverage analysis. See :ref:`Line Coverage`.
|
||||
|
||||
.. option:: --coverage-max-bins <value>
|
||||
|
||||
Rarely needed. Specifies the maximum number of bins one covergroup bins
|
||||
declaration of an integral coverpoint may create: an array of bins such
|
||||
as ``bins b[] = {[0:$]}``, automatic bins ``bins auto[N]``, or the
|
||||
automatic bins of a coverpoint from ``option.auto_bin_max``. Defaults to
|
||||
1024, and may be at most 4294967295 (``2**32 - 1``).
|
||||
|
||||
A larger array of bins is ignored with a :option:`COVERIGN` warning, a
|
||||
larger ``bins auto[N]`` is an error, and a larger
|
||||
``option.auto_bin_max`` is reduced to this limit with a
|
||||
:option:`COVERIGN` warning. Increasing the limit increases the memory
|
||||
the model uses for the bins. See also
|
||||
:vlopt:`--coverage-max-real-bins`.
|
||||
|
||||
.. option:: --coverage-max-real-bins <value>
|
||||
|
||||
Rarely needed. Specifies the maximum number of values of one array of
|
||||
bins of a coverpoint of a ``real`` expression, such as
|
||||
``bins b[] = {[1:10]}``, each of which is a bin compared separately.
|
||||
Defaults to 1024, and may be at most 4294967295 (``2**32 - 1``).
|
||||
|
||||
A larger array of bins is ignored with a :option:`COVERIGN` warning.
|
||||
Increasing the limit increases the size of the generated code, which
|
||||
grows with the number of these bins. See also
|
||||
:vlopt:`--coverage-max-bins`.
|
||||
|
||||
.. option:: --coverage-max-width <width>
|
||||
|
||||
Rarely needed. Specify the maximum bit width of a signal subject to
|
||||
@@ -733,8 +706,6 @@ Summary:
|
||||
|
||||
.. option:: -fno-dead-cells
|
||||
|
||||
.. option:: -fno-dead-methods
|
||||
|
||||
.. option:: -fno-dedup
|
||||
|
||||
.. option:: -fno-dfg
|
||||
@@ -840,10 +811,6 @@ Summary:
|
||||
|
||||
.. option:: -fno-lift-expr
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions before 5.054: Disable lifting of expressions out of statements.
|
||||
|
||||
.. option:: -fno-localize
|
||||
|
||||
.. option:: -fno-merge-cond
|
||||
@@ -852,11 +819,6 @@ Summary:
|
||||
|
||||
.. option:: -fno-merge-const-pool
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions before 5.054: Disable merging constant pool entries that have
|
||||
the same value but different data types.
|
||||
|
||||
.. option:: -fno-reloop
|
||||
|
||||
.. option:: -fno-reorder
|
||||
@@ -1828,11 +1790,12 @@ Summary:
|
||||
|
||||
.. option:: --structs-packed
|
||||
|
||||
Removed in 5.054.
|
||||
Deprecated; discontinue use of this option.
|
||||
|
||||
Converted all unpacked structures to packed structures, Specifying this
|
||||
option allowed for backward compatibility with versions before Verilator
|
||||
5.006, when Verilator would always pack unpacked structures.
|
||||
Converts all unpacked structures to packed structures, and issues an
|
||||
:option:`UNPACKED` warning. Specifying this option allows for backward
|
||||
compatibility with versions before Verilator 5.006, when Verilator would
|
||||
always pack unpacked structures.
|
||||
|
||||
.. option:: -sv
|
||||
|
||||
@@ -2304,7 +2267,7 @@ Summary:
|
||||
If using `--x-assign unique`, use the
|
||||
:vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>`
|
||||
runtime option, and seed the runtime random number generator such that
|
||||
each regression test run gets a different randomization sequence with
|
||||
each regression run gets a different randomization sequence with
|
||||
:vlopt:`+verilator+seed+\<value\>`. You'll probably also want to print
|
||||
any seeds selected, and code to enable rerunning with that same seed,
|
||||
so you can reproduce bugs.
|
||||
@@ -2333,7 +2296,7 @@ Summary:
|
||||
If using `--x-initial unique`, use the
|
||||
:vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>`
|
||||
runtime option, and seed the runtime random number generator such that
|
||||
each regression test run gets a different randomization sequence with
|
||||
each regression run gets a different randomization sequence with
|
||||
:vlopt:`+verilator+seed+\<value\>`. You'll probably also want to print
|
||||
any seeds selected, and code to enable rerunning with that same seed,
|
||||
so you can reproduce bugs.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -174,7 +174,7 @@ once, after ``configure``:
|
||||
# Install dependencies
|
||||
sudo apt install python3-pip
|
||||
|
||||
# Create Python virtual environment in .venv:
|
||||
# Create Python virutal environment in .venv:
|
||||
make venv
|
||||
|
||||
# Or alternatively, to put it somewhere else:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -592,10 +592,6 @@ List Of Warnings
|
||||
``covergroup``, ``coverpoint``, and coverage options, and the
|
||||
construct was ignored.
|
||||
|
||||
This includes crosses whose normal-bin Cartesian product exceeds
|
||||
``2**32 - 1`` tuples. The limit is checked during Verilation for both
|
||||
automatic and explicit cross bins.
|
||||
|
||||
Disabling the :option:`UNSUPPORTED` error also disables this warning.
|
||||
|
||||
Ignoring this warning may make Verilator ignore lint checking on the
|
||||
@@ -2503,10 +2499,14 @@ List Of Warnings
|
||||
|
||||
.. option:: UNPACKED
|
||||
|
||||
Historical, never issued since version 5.004.
|
||||
Warns that unpacked structs and unions are not supported because
|
||||
:vlopt:`--structs-packed` was used, or by up through version 5.004.
|
||||
|
||||
Warned that unpacked structs and unions were not supported, or disabled
|
||||
by the since-removed `--structs-packed` option.
|
||||
Ignoring this warning will make Verilator treat the structure as packed,
|
||||
which may make Verilator simulations differ from other simulators. This
|
||||
downgrading may also result in what would typically be a legal unpacked
|
||||
struct/array inside an unpacked struct/array becoming an illegal
|
||||
unpacked struct/array inside a packed struct/array.
|
||||
|
||||
|
||||
.. option:: UNSATCONSTR
|
||||
|
||||
+11
-2
@@ -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
|
||||
|
||||
@@ -305,7 +305,6 @@ Mahmoudy
|
||||
Makefile
|
||||
Makefiles
|
||||
Maksymenko
|
||||
Manzoni
|
||||
Marche
|
||||
Markley
|
||||
Marquet
|
||||
@@ -347,7 +346,6 @@ Narcis
|
||||
Nassim
|
||||
Nauticus
|
||||
Nayak
|
||||
Nazar
|
||||
Nenakhov
|
||||
Newgard
|
||||
Nigam
|
||||
@@ -590,7 +588,6 @@ Yurii
|
||||
Zaruba
|
||||
Zhang
|
||||
Zhi
|
||||
Zibilyuk
|
||||
abirkmanis
|
||||
accessor
|
||||
accessors
|
||||
@@ -630,7 +627,6 @@ benchmarking
|
||||
biguint
|
||||
binToOneHot
|
||||
bindir
|
||||
binsof
|
||||
biops
|
||||
bisonpre
|
||||
bitOpTree
|
||||
@@ -722,7 +718,6 @@ deduplication
|
||||
defenv
|
||||
defname
|
||||
defparam
|
||||
dehash
|
||||
demangling
|
||||
dep
|
||||
deparametrized
|
||||
@@ -742,7 +737,6 @@ devel
|
||||
difftree
|
||||
dir
|
||||
disambiguates
|
||||
discoverability
|
||||
displayb
|
||||
distcc
|
||||
doxygen
|
||||
@@ -953,7 +947,6 @@ makefile
|
||||
makefiles
|
||||
malloc
|
||||
manpages
|
||||
memoization
|
||||
metacomment
|
||||
metacomments
|
||||
miree
|
||||
@@ -1139,7 +1132,6 @@ shortint
|
||||
shortreal
|
||||
signame
|
||||
signedness
|
||||
slp
|
||||
sp
|
||||
specparam
|
||||
splitme
|
||||
@@ -1190,7 +1182,6 @@ systemc
|
||||
taskify
|
||||
tcmalloc
|
||||
tcmalloc
|
||||
teardown
|
||||
tenghtt
|
||||
testbench
|
||||
testbenches
|
||||
@@ -1264,7 +1255,6 @@ vcddiff
|
||||
vcoverage
|
||||
vdhotre
|
||||
vec
|
||||
vectorize
|
||||
ventana
|
||||
venv
|
||||
ver
|
||||
|
||||
+95
-147
@@ -1178,29 +1178,19 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
|
||||
} else if (formatAttr == VL_VFORMATATTR_STRING) {
|
||||
thingp = va_arg(ap, std::string*);
|
||||
if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override
|
||||
} else if (formatAttr == VL_VFORMATATTR_ENUM
|
||||
|| formatAttr == VL_VFORMATATTR_ENUM_SIGNED) {
|
||||
const int numericAttr = formatAttr == VL_VFORMATATTR_ENUM_SIGNED
|
||||
? VL_VFORMATATTR_SIGNED
|
||||
: VL_VFORMATATTR_UNSIGNED;
|
||||
} else if (formatAttr == VL_VFORMATATTR_ENUM) {
|
||||
// Always <= VL_QUADSIZE; emit uses non-ENUM format for wider enums
|
||||
lbits = va_arg(ap, int);
|
||||
if (lbits <= VL_QUADSIZE) {
|
||||
ld = VL_VA_ARG_Q_(ap, lbits);
|
||||
strwide.resize(2);
|
||||
WDataOutP strwidep = WDataOutP::external(strwide.data());
|
||||
VL_SET_WQ(strwidep, ld);
|
||||
lwp = strwidep;
|
||||
} else {
|
||||
lwp = WDataInP::external(va_arg(ap, const EData*));
|
||||
ld = VL_SET_QW(lwp);
|
||||
}
|
||||
ld = VL_VA_ARG_Q_(ap, lbits);
|
||||
strwide.resize(2);
|
||||
WDataOutP strwidep = WDataOutP::external(strwide.data());
|
||||
VL_SET_WQ(strwidep, ld);
|
||||
lwp = strwidep;
|
||||
lsb = lbits - 1;
|
||||
++argn; // Enum value is followed by the generated name string argument
|
||||
static_cast<void>(va_arg(ap, int)); // VL_VFORMATATTR_STRING
|
||||
enump = va_arg(ap, std::string*);
|
||||
if (fmt != 'p' && fmt != 's') {
|
||||
formatAttr = numericAttr;
|
||||
} else if (enump && !enump->empty()) {
|
||||
if (enump && !enump->empty()) {
|
||||
formatAttr = (fmt == 'p') ? VL_VFORMATATTR_COMPLEX : VL_VFORMATATTR_STRING;
|
||||
thingp = const_cast<std::string*>(enump);
|
||||
} else if (fmt == 'p' && widthSet && width == 0) {
|
||||
@@ -1211,7 +1201,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
|
||||
if (fmt == 'p') width = 0;
|
||||
widthSet = true;
|
||||
fmt = 'd';
|
||||
formatAttr = numericAttr;
|
||||
formatAttr = VL_VFORMATATTR_UNSIGNED;
|
||||
}
|
||||
if (widthSet && width == 0) {
|
||||
while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb;
|
||||
@@ -1292,7 +1282,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
|
||||
output += t_tmp;
|
||||
} else if (formatAttr == VL_VFORMATATTR_STRING) {
|
||||
const std::string* const strp = static_cast<const std::string*>(thingp);
|
||||
output += VL_TO_STRING(*strp);
|
||||
output += '"' + *strp + '"';
|
||||
} else if (formatAttr == VL_VFORMATATTR_COMPLEX) {
|
||||
const std::string* const strp = static_cast<const std::string*>(thingp);
|
||||
output += *strp;
|
||||
@@ -1832,8 +1822,8 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
return got;
|
||||
|
||||
done:
|
||||
// Scan stopped early; EOF only if input ended before the first conversion
|
||||
if (got == 0 && _vl_vsss_eof(fp, floc)) return -1;
|
||||
// Scan stopped early, return parsed or EOF
|
||||
if (_vl_vsss_eof(fp, floc)) return -1;
|
||||
return got;
|
||||
}
|
||||
|
||||
@@ -2498,30 +2488,6 @@ std::string VL_TO_STRING(QData lhs) {
|
||||
std::string VL_TO_STRING(double lhs) {
|
||||
return VL_SFORMATF_N_NX("%g", 1, VL_VFORMATATTR_DOUBLE, lhs);
|
||||
}
|
||||
std::string VL_TO_STRING(const std::string& obj) VL_PURE {
|
||||
std::string out{"\""};
|
||||
out.reserve(obj.size() + 2);
|
||||
for (const unsigned char ch : obj) {
|
||||
switch (ch) {
|
||||
case '\n': out += "\\n"; break;
|
||||
case '\r': out += "\\r"; break;
|
||||
case '\t': out += "\\t"; break;
|
||||
case '"': out += "\\\""; break;
|
||||
case '\\': out += "\\\\"; break;
|
||||
default:
|
||||
if (std::isprint(ch)) {
|
||||
out += static_cast<char>(ch);
|
||||
} else {
|
||||
out += '\\';
|
||||
out += static_cast<char>('0' + ((ch >> 6) & 3));
|
||||
out += static_cast<char>('0' + ((ch >> 3) & 7));
|
||||
out += static_cast<char>('0' + (ch & 7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out + '"';
|
||||
}
|
||||
std::string VL_TO_STRING_W(int words, const WDataInP obj) {
|
||||
return VL_SFORMATF_N_NX("'h%0x", 1, VL_VFORMATATTR_UNSIGNED, words * VL_EDATASIZE, obj);
|
||||
}
|
||||
@@ -3128,16 +3094,41 @@ VerilatedContext::Serialized::Serialized() {
|
||||
|
||||
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
|
||||
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
||||
if (m_ns.m_assertCtlsLocked) return;
|
||||
if (assertCtlsLocked()) return;
|
||||
// Set all assert and directive types when true, clear otherwise.
|
||||
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
|
||||
}
|
||||
bool VerilatedContext::assertOnGet(VerilatedAssertType_t type,
|
||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
|
||||
return assertCtlGet(VerilatedAssertCtlQuery::ASSERT_CTL_ON, type, directive);
|
||||
}
|
||||
uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_PURE {
|
||||
// Place the directive bits at each selected assertion type's 3-bit group.
|
||||
uint32_t mask = 0;
|
||||
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
|
||||
if (VL_BITISSET_I(types, i)) mask |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
if (assertCtlsLocked()) return;
|
||||
m_s.m_assertOn |= assertOnMask(types, directives);
|
||||
}
|
||||
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
if (assertCtlsLocked()) return;
|
||||
m_s.m_assertOn &= ~assertOnMask(types, directives);
|
||||
}
|
||||
bool VerilatedContext::assertCtlsLocked() const VL_MT_SAFE { return m_ns.m_assertCtlsLocked; }
|
||||
void VerilatedContext::assertCtlsLocked(bool flag) VL_MT_SAFE { m_ns.m_assertCtlsLocked = flag; }
|
||||
void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the
|
||||
// selected bits until Unlock; On/Off/Kill leave locked bits unchanged.
|
||||
// +verilator+assert+lock freezes everything, including Lock/Unlock itself.
|
||||
if (m_ns.m_assertCtlsLocked) return;
|
||||
if (assertCtlsLocked()) return;
|
||||
const uint32_t mask = assertOnMask(types, directives);
|
||||
const uint32_t lockedMask = mask & ~m_s.m_assertLock;
|
||||
switch (controlType) {
|
||||
@@ -3187,6 +3178,26 @@ void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t typ
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
uint32_t
|
||||
VerilatedContext::assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
|
||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
|
||||
const uint32_t mask = assertOnMask(type, directive);
|
||||
if (!mask) return 0;
|
||||
switch (query) { // LCOV_EXCL_BR_LINE
|
||||
case VerilatedAssertCtlQuery::ASSERT_CTL_ON: return (m_s.m_assertOn & mask) != 0;
|
||||
case VerilatedAssertCtlQuery::ASSERT_CTL_KILL:
|
||||
assert(mask && (mask & (mask - 1)) == 0);
|
||||
return m_s.m_assertKill[VL_CLOG2_I(mask)];
|
||||
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
|
||||
return (m_s.m_assertPassOnVacuous & mask) != 0;
|
||||
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_NONVACUOUS:
|
||||
return (m_s.m_assertPassOnNonvacuous & mask) != 0;
|
||||
case VerilatedAssertCtlQuery::ASSERT_CTL_FAIL_ON: return (m_s.m_assertFailOn & mask) != 0;
|
||||
default: // LCOV_EXCL_START
|
||||
VL_FATAL_MT("", 0, "", "Internal: Bad assertCtlGet query");
|
||||
VL_UNREACHABLE;
|
||||
} // LCOV_EXCL_STOP
|
||||
}
|
||||
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_calcUnusedSigs = flag;
|
||||
@@ -3370,15 +3381,6 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
|
||||
return vl_time_str(timeprecision());
|
||||
}
|
||||
|
||||
static void warnThreadsOversubscribed(unsigned threads) VL_MT_SAFE {
|
||||
const unsigned threadsAvailableToProcess = VlOs::getProcessDefaultParallelism();
|
||||
if (threads > threadsAvailableToProcess) {
|
||||
VL_PRINTF_MT("%%Warning: Process has %u hardware threads available, but simulation thread "
|
||||
"count set to %u. This will likely cause significant slowdown.\n",
|
||||
threadsAvailableToProcess, threads);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedContext::threads(unsigned n) {
|
||||
if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "Simulation threads must be >= 1");
|
||||
|
||||
@@ -3389,10 +3391,14 @@ void VerilatedContext::threads(unsigned n) {
|
||||
}
|
||||
|
||||
m_useNumaAssign = true;
|
||||
m_threadsSet = true;
|
||||
if (m_threads == n) return; // To avoid unnecessary warnings
|
||||
m_threads = n;
|
||||
warnThreadsOversubscribed(m_threads);
|
||||
const unsigned threadsAvailableToProcess = VlOs::getProcessDefaultParallelism();
|
||||
if (m_threads > threadsAvailableToProcess) {
|
||||
VL_PRINTF_MT("%%Warning: Process has %u hardware threads available, but simulation thread "
|
||||
"count set to %u. This will likely cause significant slowdown.\n",
|
||||
threadsAvailableToProcess, m_threads);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedContext::useNumaAssign(bool flag) { m_useNumaAssign = flag; }
|
||||
@@ -3458,15 +3464,7 @@ void VerilatedContext::addModel(const VerilatedModel* modelp) {
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVirtualBase* VerilatedContext::threadPoolp(unsigned modelThreads) {
|
||||
// The thread count defaults to the number of threads available to the process, which may be
|
||||
// fewer than a model uses, e.g. a model Verilated with --threads 4 on a single core machine.
|
||||
// Oversubscribing is slow but works, so grow to fit the model, unless the user picked the
|
||||
// thread count themselves, in which case addModel() reports the mismatch instead.
|
||||
if (VL_UNLIKELY(modelThreads > m_threads) && !m_threadsSet && !m_threadPool) {
|
||||
m_threads = modelThreads;
|
||||
warnThreadsOversubscribed(m_threads);
|
||||
}
|
||||
VerilatedVirtualBase* VerilatedContext::threadPoolp() {
|
||||
if (m_threads == 1) return nullptr;
|
||||
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
|
||||
return m_threadPool.get();
|
||||
@@ -3582,7 +3580,7 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||
std::string str;
|
||||
uint64_t u64;
|
||||
if (arg == "+verilator+assert+lock") {
|
||||
m_ns.m_assertCtlsLocked = true;
|
||||
assertCtlsLocked(true);
|
||||
} else if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
|
||||
coverageFilename(str);
|
||||
} else if (arg == "+verilator+debug") {
|
||||
@@ -3747,17 +3745,11 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
|
||||
// VerilatedContext:: Methods - scopes
|
||||
|
||||
void VerilatedContext::scopesDump() const VL_MT_SAFE {
|
||||
{
|
||||
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
|
||||
VL_PRINTF_MT(" scopesDump:\n");
|
||||
for (const auto& i : m_impdatap->m_nameMap) {
|
||||
const VerilatedScope* const scopep = i.second;
|
||||
scopep->scopeDump();
|
||||
}
|
||||
}
|
||||
{
|
||||
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
|
||||
for (const auto& i : m_impdatap->m_ifaceRefMap) i.second.ifaceRefDump();
|
||||
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
|
||||
VL_PRINTF_MT(" scopesDump:\n");
|
||||
for (const auto& i : m_impdatap->m_nameMap) {
|
||||
const VerilatedScope* const scopep = i.second;
|
||||
scopep->scopeDump();
|
||||
}
|
||||
VL_PRINTF_MT("\n");
|
||||
}
|
||||
@@ -3787,31 +3779,6 @@ const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
|
||||
return &(impp()->m_impdatap->m_nameMap);
|
||||
}
|
||||
|
||||
void VerilatedContextImp::ifaceRefInsert(const VerilatedIfaceRef& ifaceRef) VL_MT_SAFE {
|
||||
// Slow ok - called once/interface-reference at construction
|
||||
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
|
||||
m_impdatap->m_ifaceRefMap.emplace(ifaceRef.fullname(), ifaceRef);
|
||||
}
|
||||
void VerilatedContextImp::ifaceRefErase(const std::string& fullname,
|
||||
const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/interface-reference at destruction
|
||||
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
|
||||
const auto it = m_impdatap->m_ifaceRefMap.find(fullname);
|
||||
// Models sharing an instance name collide on the key; only erase our own,
|
||||
// so tearing one down leaves another's live reference registered
|
||||
if (it != m_impdatap->m_ifaceRefMap.end() && it->second.scopep() == scopep) {
|
||||
m_impdatap->m_ifaceRefMap.erase(it);
|
||||
}
|
||||
}
|
||||
const VerilatedIfaceRef*
|
||||
VerilatedContext::ifaceRefFind(const char* namep) const VL_MT_SAFE_POSTINIT {
|
||||
// Thread safe only assuming this is called only after model construction completed
|
||||
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
|
||||
const auto& it = m_impdatap->m_ifaceRefMap.find(namep);
|
||||
if (VL_UNLIKELY(it == m_impdatap->m_ifaceRefMap.end())) return nullptr;
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VerilatedContext:: Methods - trace
|
||||
|
||||
@@ -4182,6 +4149,28 @@ std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { retu
|
||||
//======================================================================
|
||||
// VerilatedVar:: Methods
|
||||
|
||||
// cppcheck-suppress unusedFunction // Used by applications
|
||||
uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
|
||||
if (m_entSize) return m_entSize;
|
||||
uint32_t size = 1;
|
||||
switch (vltype()) {
|
||||
case VLVT_PTR: size = sizeof(void*); break;
|
||||
case VLVT_UINT8: size = sizeof(CData); break;
|
||||
case VLVT_UINT16: size = sizeof(SData); break;
|
||||
case VLVT_UINT32: size = sizeof(IData); break;
|
||||
case VLVT_UINT64: size = sizeof(QData); break;
|
||||
case VLVT_WDATA: size = VL_WORDS_I(entBits()) * sizeof(IData); break;
|
||||
default: size = 0; break; // LCOV_EXCL_LINE
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t VerilatedVarProps::totalSize() const {
|
||||
size_t size = entSize();
|
||||
for (int udim = 0; udim < udims(); ++udim) size *= m_unpacked[udim].elements();
|
||||
return size;
|
||||
}
|
||||
|
||||
void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr;
|
||||
if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr;
|
||||
@@ -4323,41 +4312,6 @@ void VerilatedScope::scopesConstructFromTable(const VlScopeTableEntry* entp, siz
|
||||
}
|
||||
}
|
||||
|
||||
// Prefix with the model instance name, as VerilatedScope's constructor does
|
||||
static std::string vl_ifaceRefFullname(const VerilatedSyms* symsp, const char* suffixp) {
|
||||
const char* const prefixp = symsp->name();
|
||||
std::string out{prefixp};
|
||||
if (*prefixp && *suffixp) out += '.';
|
||||
out += suffixp;
|
||||
return out;
|
||||
}
|
||||
|
||||
void VerilatedScope::ifaceRefsInsertFromTable(const VlIfaceRefTableEntry* entp, size_t n,
|
||||
VerilatedSyms* symsp) VL_MT_UNSAFE {
|
||||
// Use the model's own context; at destruction threadContextp() may be another's
|
||||
VerilatedContextImp* const impp = symsp->_vm_contextp__->impp();
|
||||
uint8_t* const base = reinterpret_cast<uint8_t*>(symsp);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const VlIfaceRefTableEntry& e = entp[i];
|
||||
const VerilatedScope* const scopep
|
||||
= *reinterpret_cast<VerilatedScope**>(base + e.ptrOffset);
|
||||
impp->ifaceRefInsert(
|
||||
VerilatedIfaceRef{scopep, e.namep, vl_ifaceRefFullname(symsp, e.suffixp), e.modportp});
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedScope::ifaceRefsEraseFromTable(const VlIfaceRefTableEntry* entp, size_t n,
|
||||
const VerilatedSyms* symsp) VL_MT_UNSAFE {
|
||||
VerilatedContextImp* const impp = symsp->_vm_contextp__->impp();
|
||||
uint8_t* const base = reinterpret_cast<uint8_t*>(const_cast<VerilatedSyms*>(symsp));
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const VlIfaceRefTableEntry& e = entp[i];
|
||||
const VerilatedScope* const scopep
|
||||
= *reinterpret_cast<VerilatedScope**>(base + e.ptrOffset);
|
||||
impp->ifaceRefErase(vl_ifaceRefFullname(symsp, e.suffixp), scopep);
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
|
||||
VerilatedVarType vltype, int vlflags, int udims,
|
||||
uint32_t entSize...) VL_MT_UNSAFE {
|
||||
@@ -4475,7 +4429,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
|
||||
// Slowpath - Called only when find has failed
|
||||
const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum)
|
||||
+ "' but scope wasn't set, perhaps due to dpi import call without "
|
||||
+ "'context', or missing svSetScope (IEEE 1800-2023 35.5.3)");
|
||||
+ "'context', or missing svSetScope. See IEEE 1800-2023 35.5.3.");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
@@ -4503,12 +4457,6 @@ void VerilatedScope::scopeDump() const {
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedIfaceRef::ifaceRefDump() const VL_MT_SAFE_POSTINIT {
|
||||
VL_PRINTF_MT(" IFACEREF %p: %s -> %s", this, fullname(), scopep()->name());
|
||||
if (hasModport()) VL_PRINTF_MT(".%s", modport());
|
||||
VL_PRINTF_MT("\n");
|
||||
}
|
||||
|
||||
void VerilatedHierarchy::add(const VerilatedScope* fromp, const VerilatedScope* top) {
|
||||
VerilatedImp::hierarchyAdd(fromp, top);
|
||||
}
|
||||
|
||||
+42
-141
@@ -100,9 +100,6 @@ class VerilatedFstC;
|
||||
class VerilatedFstSc;
|
||||
class VerilatedScope;
|
||||
class VerilatedScopeNameMap;
|
||||
class VerilatedIfaceRef;
|
||||
class VerilatedIfaceRefMap;
|
||||
struct VlIfaceRefTableEntry;
|
||||
template <typename, typename>
|
||||
class VerilatedTrace;
|
||||
class VerilatedTraceBaseC;
|
||||
@@ -112,7 +109,6 @@ class VerilatedVarNameMap;
|
||||
class VerilatedVcd;
|
||||
class VerilatedVcdC;
|
||||
class VerilatedVcdSc;
|
||||
class VlCovRegistry;
|
||||
|
||||
//=========================================================================
|
||||
// Basic types
|
||||
@@ -261,7 +257,7 @@ class VL_SCOPED_CAPABILITY VerilatedLockGuard final {
|
||||
VL_UNCOPYABLE(VerilatedLockGuard);
|
||||
|
||||
private:
|
||||
VerilatedMutex& m_mutexr; // Mutex protecting the guard
|
||||
VerilatedMutex& m_mutexr;
|
||||
|
||||
public:
|
||||
/// Construct and hold given mutex lock until destruction or unlock()
|
||||
@@ -305,34 +301,6 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
// Internals: VlFileLineDebug stores a SystemVerilog source code location. Used in
|
||||
// VlCoroutineHandle for debugging purposes, and to locate covergroup weight errors.
|
||||
|
||||
class VlFileLineDebug final {
|
||||
// MEMBERS
|
||||
#ifdef VL_DEBUG
|
||||
const char* m_filename = nullptr; // Filename from sources, nullptr for unlnown
|
||||
int m_lineno = 0; // Line number from sources
|
||||
#endif
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlFileLineDebug() = default;
|
||||
VlFileLineDebug(const char* filename, int lineno)
|
||||
#ifdef VL_DEBUG
|
||||
: m_filename{filename}
|
||||
, m_lineno{lineno}
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// METHODS
|
||||
#ifdef VL_DEBUG
|
||||
const char* filename() const { return m_filename; }
|
||||
int lineno() const { return m_lineno; }
|
||||
#endif
|
||||
};
|
||||
|
||||
class VlExecutionProfilerBase;
|
||||
|
||||
//=========================================================================
|
||||
@@ -408,7 +376,8 @@ class VerilatedEvalLoop final {
|
||||
const uint32_t m_convergeLimit; // --converge-limit from compiler command line
|
||||
// Where to record --prof-exec sections, or null if not profiling
|
||||
VlExecutionProfilerBase* m_profilerp = nullptr;
|
||||
bool m_profTopLevel = false; // Top level model during profiling
|
||||
// Whether this is the top level model during profiling
|
||||
bool m_profTopLevel = false;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -510,8 +479,8 @@ private:
|
||||
= ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits
|
||||
+ 1;
|
||||
// Build the assertion-control bit mask for the given assertion x directive types.
|
||||
static constexpr uint32_t assertOnMask(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_PURE;
|
||||
static uint32_t assertOnMask(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_PURE;
|
||||
static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1;
|
||||
// No termination request has stamped m_finishPendingTime yet
|
||||
static constexpr uint64_t TIME_UNSET = ~0ULL;
|
||||
@@ -529,18 +498,20 @@ protected:
|
||||
// No std::strings or pointers or will serialize badly!
|
||||
// Fast path
|
||||
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
|
||||
// Enabled assertions, for each VerilatedAssertType we store 3-bits, one for each directive
|
||||
// type. Last bit guards internal directive types.
|
||||
std::atomic<uint32_t> m_assertOn{std::numeric_limits<uint32_t>::max()};
|
||||
std::atomic<uint32_t> m_assertOn{
|
||||
std::numeric_limits<uint32_t>::max()}; // Enabled assertions,
|
||||
// for each VerilatedAssertType we store
|
||||
// 3-bits, one for each directive type. Last
|
||||
// bit guards internal directive types.
|
||||
std::atomic<uint32_t> m_assertLock{0}; // Locked assertion bits (IEEE 1800-2023 20.11
|
||||
// Lock/Unlock); same layout as m_assertOn. While
|
||||
// a bit is locked, On/Off/Kill leave it unchanged.
|
||||
// Enabled vacuous pass actions
|
||||
std::atomic<uint32_t> m_assertPassOnVacuous{std::numeric_limits<uint32_t>::max()};
|
||||
// Enabled nonvacuous pass actions
|
||||
std::atomic<uint32_t> m_assertPassOnNonvacuous{std::numeric_limits<uint32_t>::max()};
|
||||
// Enabled fail actions
|
||||
std::atomic<uint32_t> m_assertFailOn{std::numeric_limits<uint32_t>::max()};
|
||||
std::atomic<uint32_t> m_assertPassOnVacuous{
|
||||
std::numeric_limits<uint32_t>::max()}; // Enabled vacuous pass actions
|
||||
std::atomic<uint32_t> m_assertPassOnNonvacuous{
|
||||
std::numeric_limits<uint32_t>::max()}; // Enabled nonvacuous pass actions
|
||||
std::atomic<uint32_t> m_assertFailOn{
|
||||
std::numeric_limits<uint32_t>::max()}; // Enabled fail actions
|
||||
std::array<std::atomic<uint32_t>, ASSERT_CONTROL_SLOT_COUNT> m_assertKill{};
|
||||
bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated
|
||||
bool m_fatalOnError = true; // Fatal on $stop/non-fatal error
|
||||
@@ -574,7 +545,8 @@ protected:
|
||||
// A worker queues $finish before the main thread callback can set m_gotFinish.
|
||||
std::atomic<uint32_t> m_finishPending{0}; // Number of queued $finish callbacks
|
||||
std::atomic<uint64_t> m_finishPendingTime{TIME_UNSET}; // Time of the first callback
|
||||
std::atomic<bool> m_assertCtlsLocked{false}; // All assertion-control updates are ignored
|
||||
std::atomic<bool> m_assertCtlsLocked{
|
||||
false}; // When true, all assertion-control updates are ignored
|
||||
int m_stopReserved = 0; // Posted $stop requests not yet executed
|
||||
bool m_executingFinal = false; // Running generated final() code
|
||||
uint64_t m_profExecStart = 1; // +prof+exec+start time
|
||||
@@ -608,8 +580,6 @@ protected:
|
||||
const std::unique_ptr<VerilatedContextImpData> m_impdatap;
|
||||
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
|
||||
unsigned m_threads = VlOs::getProcessDefaultParallelism();
|
||||
// True if m_threads was set by the user, rather than being the default
|
||||
bool m_threadsSet = false;
|
||||
// Use numa automatic CPU-to-thread assignment
|
||||
bool m_useNumaAssign = false;
|
||||
// Number of threads in added models
|
||||
@@ -620,9 +590,6 @@ protected:
|
||||
std::unique_ptr<VerilatedVirtualBase> m_executionProfiler;
|
||||
// Coverage access
|
||||
std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep()
|
||||
// Covergroup type/instance nodes. Covergroup data is always collected,
|
||||
// independent of whether coverage data is recorded (--coverage).
|
||||
std::unique_ptr<VerilatedVirtualBase> m_covergroupsp; // Pointer for covergroupRegistryp()
|
||||
|
||||
// File I/O
|
||||
// Not serialized
|
||||
@@ -633,8 +600,9 @@ protected:
|
||||
// List of free descriptors in the MCT region [4, 32)
|
||||
std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex);
|
||||
|
||||
// Magic to check for bad construction
|
||||
static constexpr uint64_t MAGIC = 0xC35F9A6E5298EE6EULL; // SHA256 "VerilatedContext"
|
||||
uint64_t m_magic = MAGIC; // Magic to check for bad construction
|
||||
uint64_t m_magic = MAGIC;
|
||||
|
||||
private:
|
||||
// CONSTRUCTORS
|
||||
@@ -651,9 +619,28 @@ public:
|
||||
bool assertOn() const VL_MT_SAFE;
|
||||
/// Enable all assertion types
|
||||
void assertOn(bool flag) VL_MT_SAFE;
|
||||
/// Get enabled status for given assertion types
|
||||
bool assertOnGet(VerilatedAssertType_t type,
|
||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
|
||||
/// Set enabled status for given assertion types
|
||||
void assertOnSet(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||
/// Clear enabled status for given assertion types
|
||||
void assertOnClear(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||
/// Return if assertion-control updates are locked. When locked, RTL assert
|
||||
// control statements ($asserton/$assertoff/$assertcontrol) are ignored, as
|
||||
// are updates from the C++ API.
|
||||
bool assertCtlsLocked() const VL_MT_SAFE;
|
||||
/// Lock/unlock assertion-control updates.
|
||||
void assertCtlsLocked(bool flag) VL_MT_SAFE;
|
||||
/// Apply assertion control for given control, assertion, and directive types
|
||||
void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||
/// Get assertion-control runtime state. Boolean queries return 0/1, Kill returns
|
||||
/// the generation count.
|
||||
uint32_t assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
|
||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
|
||||
/// Return if calculating of unused signals (for traces)
|
||||
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
|
||||
/// Enable calculation of unused signals (for traces)
|
||||
@@ -672,8 +659,6 @@ public:
|
||||
/// Return VerilatedCovContext, allocate if needed
|
||||
/// Note if get unresolved reference then likely forgot to link verilated_cov.cpp
|
||||
VerilatedCovContext* coveragep() VL_MT_SAFE;
|
||||
/// Returns VlCovRegistry. Allocated on-demand
|
||||
VlCovRegistry* covergroupRegistryp() VL_MT_SAFE;
|
||||
/// Return debug level
|
||||
static inline int debug() VL_MT_SAFE; /// Set debug level
|
||||
/// Debug is currently global, but for forward compatibility have a per-context method
|
||||
@@ -829,10 +814,6 @@ public:
|
||||
bool stopRequestReserve(bool maybe) VL_MT_SAFE;
|
||||
void stopRequestRelease() VL_MT_SAFE;
|
||||
|
||||
// Internal: assertCtlGet() for generated code, with constant type and directive
|
||||
template <VerilatedAssertType_t T_Type, VerilatedAssertDirectiveType_t T_Directive>
|
||||
VL_ATTR_ALWINLINE uint32_t assertCtlGet(VerilatedAssertCtlQuery query) const VL_MT_SAFE;
|
||||
|
||||
// Internal: access to implementation class
|
||||
VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); }
|
||||
const VerilatedContextImp* impp() const VL_MT_SAFE {
|
||||
@@ -841,9 +822,7 @@ public:
|
||||
|
||||
// Internal: Model and thread setup
|
||||
void addModel(const VerilatedModel* modelp);
|
||||
// Get the thread pool, creating it if needed. 'modelThreads' is the parallelism of the model
|
||||
// being constructed, so that the context can grow to the number of threads it requires.
|
||||
VerilatedVirtualBase* threadPoolp(unsigned modelThreads = 1);
|
||||
VerilatedVirtualBase* threadPoolp();
|
||||
void prepareClone();
|
||||
VerilatedVirtualBase* threadPoolpOnClone();
|
||||
VerilatedVirtualBase*
|
||||
@@ -889,9 +868,6 @@ public:
|
||||
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
|
||||
const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
|
||||
|
||||
// Internal: Find interface reference by fully qualified path
|
||||
const VerilatedIfaceRef* ifaceRefFind(const char* namep) const VL_MT_SAFE_POSTINIT;
|
||||
|
||||
// Internal: Serialization setup
|
||||
static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
|
||||
void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
|
||||
@@ -921,32 +897,6 @@ public: // But for internal use only
|
||||
virtual const char* name() const = 0;
|
||||
};
|
||||
|
||||
// An interface reference port, and the concrete interface it is connected to.
|
||||
// Used for VPI; references are not scopes, so are not in VerilatedScopeNameMap.
|
||||
class VerilatedIfaceRef final {
|
||||
const VerilatedScope* m_scopep = nullptr; // Concrete interface referred to
|
||||
const char* m_namep = ""; // Name of the reference port
|
||||
// Fully qualified path; owned, as the instance name prefix is set at construction
|
||||
std::string m_fullname;
|
||||
const char* m_modportp = ""; // Modport name, or "" if none
|
||||
public:
|
||||
VerilatedIfaceRef() = default;
|
||||
VerilatedIfaceRef(const VerilatedScope* scopep, const char* namep, const std::string& fullname,
|
||||
const char* modportp)
|
||||
: m_scopep{scopep}
|
||||
, m_namep{namep}
|
||||
, m_fullname{fullname}
|
||||
, m_modportp{modportp} {}
|
||||
~VerilatedIfaceRef() = default;
|
||||
// ACCESSORS
|
||||
const VerilatedScope* scopep() const VL_MT_SAFE_POSTINIT { return m_scopep; }
|
||||
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
|
||||
const char* fullname() const VL_MT_SAFE_POSTINIT { return m_fullname.c_str(); }
|
||||
const char* modport() const VL_MT_SAFE_POSTINIT { return m_modportp; }
|
||||
bool hasModport() const VL_MT_SAFE_POSTINIT { return m_modportp[0] != '\0'; }
|
||||
void ifaceRefDump() const VL_MT_SAFE_POSTINIT;
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
// Verilator scope information class
|
||||
// Used for internal VPI implementation, and introspection into scopes
|
||||
@@ -958,9 +908,8 @@ public:
|
||||
enum Type : uint8_t {
|
||||
SCOPE_MODULE,
|
||||
SCOPE_OTHER,
|
||||
SCOPE_PACKAGE,
|
||||
SCOPE_INTERFACE
|
||||
}; // Type of a scope, currently only module, package and interface are interesting
|
||||
SCOPE_PACKAGE
|
||||
}; // Type of a scope, currently only module and package are interesting
|
||||
private:
|
||||
// Fastpath:
|
||||
VerilatedSyms* const m_symsp; // Symbol table
|
||||
@@ -993,10 +942,6 @@ public: // But internals only - called from verilated modules, VerilatedSyms
|
||||
void varsInsertFromTable(const VlVarTableEntry* entp, size_t n, void* basep) VL_MT_UNSAFE;
|
||||
static void scopesConstructFromTable(const VlScopeTableEntry* entp, size_t n,
|
||||
VerilatedSyms* symsp) VL_MT_UNSAFE;
|
||||
static void ifaceRefsInsertFromTable(const VlIfaceRefTableEntry* entp, size_t n,
|
||||
VerilatedSyms* symsp) VL_MT_UNSAFE;
|
||||
static void ifaceRefsEraseFromTable(const VlIfaceRefTableEntry* entp, size_t n,
|
||||
const VerilatedSyms* symsp) VL_MT_UNSAFE;
|
||||
// ACCESSORS
|
||||
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
|
||||
const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }
|
||||
@@ -1013,15 +958,6 @@ public: // But internals only - called from verilated modules, VerilatedSyms
|
||||
VerilatedContext* contextp() const { return m_symsp->_vm_contextp__; }
|
||||
};
|
||||
|
||||
// One interface reference, consumed by VerilatedScope::ifaceRefsInsertFromTable()
|
||||
struct VlIfaceRefTableEntry final {
|
||||
uint32_t ptrOffset; // offsetof of the referred-to __Vscopep_* member within the Syms object
|
||||
const char* namep; // Name of the reference port
|
||||
// Path within the model; as VlScopeTableEntry::namep, instance name prepended at construction
|
||||
const char* suffixp;
|
||||
const char* modportp; // Modport name, or "" if none
|
||||
};
|
||||
|
||||
// One scope, consumed by VerilatedScope::scopesConstructFromTable(); replaces
|
||||
// per-scope 'new VerilatedScope{...}' statements, which compiles faster at scale.
|
||||
struct VlScopeTableEntry final {
|
||||
@@ -1340,40 +1276,5 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Defined here, not in-class: VL_CLOG2_I / VL_FATAL_MT (verilated_funcs.h) are not yet in scope
|
||||
constexpr uint32_t
|
||||
VerilatedContext::assertOnMask(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_PURE {
|
||||
// Place the directive bits at each selected assertion type's 3-bit group.
|
||||
uint32_t mask = 0;
|
||||
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
|
||||
if (VL_BITISSET_I(types, i)) mask |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
template <VerilatedAssertType_t T_Type, VerilatedAssertDirectiveType_t T_Directive>
|
||||
uint32_t VerilatedContext::assertCtlGet(VerilatedAssertCtlQuery query) const VL_MT_SAFE {
|
||||
// A constexpr local forces compile-time evaluation of the mask, which a plain
|
||||
// assertOnMask() call does not get from GCC at -Os
|
||||
constexpr uint32_t mask = assertOnMask(T_Type, T_Directive);
|
||||
if (!mask) return 0;
|
||||
// Explicit load(): G++ -Os inlines it but not the implicit conversion.
|
||||
switch (query) { // LCOV_EXCL_BR_LINE
|
||||
case VerilatedAssertCtlQuery::ASSERT_CTL_ON: return (m_s.m_assertOn.load() & mask) != 0;
|
||||
case VerilatedAssertCtlQuery::ASSERT_CTL_KILL:
|
||||
assert(mask && (mask & (mask - 1)) == 0);
|
||||
return m_s.m_assertKill[VL_CLOG2_I(mask)].load();
|
||||
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
|
||||
return (m_s.m_assertPassOnVacuous.load() & mask) != 0;
|
||||
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_NONVACUOUS:
|
||||
return (m_s.m_assertPassOnNonvacuous.load() & mask) != 0;
|
||||
case VerilatedAssertCtlQuery::ASSERT_CTL_FAIL_ON:
|
||||
return (m_s.m_assertFailOn.load() & mask) != 0;
|
||||
default: // LCOV_EXCL_START
|
||||
VL_FATAL_MT("", 0, "", "Internal: Bad assertCtlGet query");
|
||||
VL_UNREACHABLE;
|
||||
} // LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
#undef VERILATOR_VERILATED_H_INTERNAL_
|
||||
#endif // Guard
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
+63
-1848
File diff suppressed because it is too large
Load Diff
+33
-579
@@ -32,16 +32,10 @@
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_cov_model.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedCovContext;
|
||||
@@ -50,15 +44,6 @@ class VerilatedCovContext;
|
||||
enum class VlCovBinNaming : uint8_t {
|
||||
Single, // "<name>" one bin
|
||||
Array, // "<name>[i]" bins b[N] value array
|
||||
Numbered, // "<name>_<i>" automatic bins of a coverpoint without bins
|
||||
Values, // "<name>[v]" bins b[] with a 'with' filter, a bin per value v
|
||||
};
|
||||
|
||||
// How the bins of a 'with' filter (IEEE 1800-2023 19.5.1.1) hold the values it keeps
|
||||
enum class VlCovBinGrouping : uint8_t {
|
||||
Single, // bins b = ...: one bin
|
||||
Values, // bins b[] = ...: a bin for each value, in value order
|
||||
Fixed, // bins b[N] = ...: distributed over N bins, as a sized array's
|
||||
};
|
||||
|
||||
// Specifies the naming scheme for a range of bins, allowing the
|
||||
@@ -109,10 +94,6 @@ public:
|
||||
// VlCoverpointT<MaxHits> adds the inline hit-list array and the incrementBin write
|
||||
// path; the cross holds VlCoverpoint* and reads via hitCount()/hitList().
|
||||
class VlCoverpoint VL_NOT_FINAL : public VlCoverpointIf {
|
||||
struct ValueData;
|
||||
std::unique_ptr<ValueData> m_valuesp; // Optional value metadata and exclusion state
|
||||
friend class VlCoverCrossDyn;
|
||||
|
||||
protected:
|
||||
// MEMBERS (protected so VlCoverpointT::incrementBin can update them)
|
||||
std::string m_hier; // "covergroup.coverpoint"
|
||||
@@ -129,27 +110,15 @@ protected:
|
||||
std::vector<uint32_t> m_crossToBin;
|
||||
uint32_t m_hitCount = 0; // entries valid in the hit list this sample
|
||||
|
||||
// PROTECTED METHODS
|
||||
// Normal bin: VlCoverpointT::incrementBin(), for the bins sizedSample() finds
|
||||
virtual void incrementNormalBin(uint32_t i) = 0;
|
||||
|
||||
private:
|
||||
// PRIVATE METHODS
|
||||
const VlCovNamer& namerFor(uint32_t i) const; // obtain the bin-specific name producer
|
||||
void addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming, const char* name,
|
||||
const char* file, int line, int col);
|
||||
// Declared bin index of the i-th bin reported through VlCoverpointIf
|
||||
uint32_t reportedBin(uint32_t i) const;
|
||||
std::string declaredBinName(uint32_t bin) const; // Name of a declared bin index
|
||||
bool liveBin(uint32_t bin) const; // Normal bin keeps a value outside the exclusions
|
||||
// Count a sample, if enabled, in a bin of a sized array holding the value, unless it is
|
||||
// 'last', the bin found before; set 'last'
|
||||
void sizedHit(VlCovBinKind kind, uint32_t bin, bool enabled, uint32_t& last);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCoverpoint();
|
||||
~VlCoverpoint() override;
|
||||
VlCoverpoint() = default;
|
||||
|
||||
// METHODS
|
||||
// ---- configuration (from generated constructor) ----
|
||||
@@ -161,78 +130,13 @@ public:
|
||||
int line, int col) {
|
||||
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
|
||||
}
|
||||
void addNumberedNamer(VlCovBinKind set, uint32_t count, const char* name, const char* file,
|
||||
int line, int col) {
|
||||
addNamer(set, count, VlCovBinNaming::Numbered, name, file, line, col);
|
||||
}
|
||||
/// Register the bins in the coverage database, with what verilator_coverage needs to
|
||||
/// compute coverage (IEEE 1800-2023 19.11): option.at_least, and the weights of the
|
||||
/// coverpoint, itemWeight, and of its covergroup, groupWeight. The weights are those of
|
||||
/// every instance, as the database merges the instances.
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page, uint32_t itemWeight,
|
||||
uint32_t groupWeight);
|
||||
|
||||
/// Configure construction-time value metadata for exclusions and cross selections.
|
||||
void valueType(uint32_t bits, bool isSigned);
|
||||
/// Describe bin values as {bin, low words, high words} entries, without enumerating them.
|
||||
void valueRanges(std::initializer_list<EData> entries);
|
||||
/// Describe runs of bins as {first bin, count, low words, span words, high words} entries:
|
||||
/// bin k of a run holds [low + k * (span + 1), low + k * (span + 1) + span], and its last
|
||||
/// bin extends to high.
|
||||
void valueRuns(std::initializer_list<EData> entries);
|
||||
/// Describe wildcard patterns as {bin, value words, mask words, low words, high words}.
|
||||
void valuePatterns(std::initializer_list<EData> entries);
|
||||
/// State exclusions do not remove values from these transition bins.
|
||||
void valueTransitions(std::initializer_list<uint32_t> bins);
|
||||
/// Apply exclusions and freeze the live Normal-bin index space used by crosses.
|
||||
void valueFinalize();
|
||||
/// Drop the per-bin values once every cross has been built; sampling needs only exclusions.
|
||||
void valueRelease();
|
||||
/// Test state exclusions independently of sampling-time iff guards.
|
||||
bool valueExcluded(QData value) const;
|
||||
bool valueExcludedW(WDataInP valuep) const;
|
||||
/// Add the coverpoint values lo..hi of the next range list element of a sized array of
|
||||
/// bins, in declaration order.
|
||||
void sizedRange(QData lo, QData hi);
|
||||
void sizedRangeW(WDataInP lop, WDataInP hip);
|
||||
/// Distribute the values sizedRange() added over the bins of the sized array 'name[count]'
|
||||
/// (IEEE 1800-2023 19.5.1). 'positive' is false for a count below one, which is invalid.
|
||||
/// At most 'limit' bins may hold values. Needs valueType(); bins append after those of
|
||||
/// init().
|
||||
void sizedFinish(VlCovBinKind kind, QData count, bool positive, uint32_t limit,
|
||||
const char* name, const char* file, int line, int col);
|
||||
/// Declared bins [sizedFirst(), sizedEnd()) of the sized array 'sized', counted in
|
||||
/// sizedFinish() order, for cross selections.
|
||||
uint32_t sizedFirst(uint32_t sized) const;
|
||||
uint32_t sizedEnd(uint32_t sized) const;
|
||||
/// Begin the bins of a 'with' filter (IEEE 1800-2023 19.5.1.1), whose candidates are the
|
||||
/// values sizedRange() added, and whose bins then count as a sized array's. At most
|
||||
/// 'limit' bins, or runs of values kept.
|
||||
void withBegin(VlCovBinGrouping grouping, uint32_t limit);
|
||||
/// Advance to the next run of candidates, withLo() to withHi(); false after the last, or
|
||||
/// once too many values are kept
|
||||
bool withNext();
|
||||
QData withLo() const;
|
||||
void withLoW(WDataOutP valuep) const;
|
||||
QData withHi() const;
|
||||
void withHiW(WDataOutP valuep) const;
|
||||
/// Keep the values lo..hi, in the order the filter kept them; false once too many are
|
||||
bool withRun(QData lo, QData hi);
|
||||
bool withRunW(WDataInP lop, WDataInP hip);
|
||||
/// Make the bins of the values kept, a sized array 'name[count]' for Fixed grouping (see
|
||||
/// sizedFinish())
|
||||
void withFinish(VlCovBinKind kind, QData count, bool positive, const char* name,
|
||||
const char* file, int line, int col);
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
|
||||
// ---- hot path (from generated sample()) ----
|
||||
// Clear the hit list at the start of each sample() for a cross-fed coverpoint.
|
||||
void clearHitList() { m_hitCount = 0; }
|
||||
// Ignore/Illegal/Default: count only; never propagates to cross coverage.
|
||||
void recordHit(uint32_t i) { ++m_counts[i]; }
|
||||
/// Count a sample in the bins of the sized array 'sized' holding the value, once each,
|
||||
/// if 'enabled'. True if a bin holds the value, enabled or not.
|
||||
bool sizedSample(uint32_t sized, QData value, bool enabled);
|
||||
bool sizedSampleW(uint32_t sized, WDataInP valuep, bool enabled);
|
||||
// incrementBin (Normal bin: count + hit-list append) lives in VlCoverpointT<MaxHits>,
|
||||
// where MaxHits is the gen-time max per-sample bin overlap.
|
||||
|
||||
@@ -243,17 +147,21 @@ public:
|
||||
std::string normalBinName(uint32_t crossIdx) const; // name of the crossIdx-th Normal bin
|
||||
|
||||
// ---- VlCoverpointIf ----
|
||||
/// Bins removed for having no value (IEEE 1800-2023 19.11.1) are not reported.
|
||||
uint32_t binCount() const override;
|
||||
uint32_t binCount() const override { return m_total; }
|
||||
std::string binName(uint32_t i) const override;
|
||||
// Deliberately not on VlCoverpointIf: only coverage-database registration needs it.
|
||||
VlCovBinKind binKind(uint32_t i) const { return namerFor(reportedBin(i)).set(); }
|
||||
// Deliberately not on VlCoverpointIf: only registerBins() needs it, via the
|
||||
// concrete coverpoint. A cross has all-Normal bins and exposes no kind, so the
|
||||
// interface omits it; add it back only if a writer needs it polymorphically.
|
||||
VlCovBinKind binKind(uint32_t i) const { return namerFor(i).set(); }
|
||||
void coverageParts(double& covered, double& total) const override {
|
||||
// Count Normal bins that reached option.at_least on demand, so the hot
|
||||
// path (incrementBin) stays a plain counter bump.
|
||||
uint32_t numCovered = 0;
|
||||
for (const uint32_t bin : m_crossToBin) {
|
||||
if (m_counts[bin] >= m_atLeast) ++numCovered;
|
||||
for (const VlCovNamer& nm : m_namers) {
|
||||
if (nm.set() != VlCovBinKind::KIND_NORMAL) continue;
|
||||
for (uint32_t i = nm.base(); i < nm.base() + nm.count(); ++i) {
|
||||
if (m_counts[i] >= m_atLeast) ++numCovered;
|
||||
}
|
||||
}
|
||||
covered = numCovered;
|
||||
total = m_normal;
|
||||
@@ -290,75 +198,16 @@ public:
|
||||
if (cx >= 0 && m_hitCount < MaxHits) m_hits[m_hitCount++] = static_cast<uint32_t>(cx);
|
||||
}
|
||||
const uint32_t* hitList() const override { return m_hits; }
|
||||
|
||||
protected:
|
||||
void incrementNormalBin(uint32_t i) override { incrementBin(i); }
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverCross
|
||||
/// Per-instance cross runtime. Holds flat uint32_t[] storage over the
|
||||
/// Per-instance auto cross runtime. Holds flat uint32_t[] storage over the
|
||||
/// Cartesian product of the feeding coverpoints' Normal bins. Each sample()
|
||||
/// walks only hit tuples, not the entire product. Bin names are
|
||||
/// built on demand for automatic bins; explicit bins select sets of tuples
|
||||
/// and replace the corresponding automatic cross bins. Explicit selections
|
||||
/// are intersected with hit-tuple words once per sample.
|
||||
/// VlCoverCrossT and VlCoverCrossDyn own their storage. This shared core does not allocate bin
|
||||
/// storage, and its borrowed storage pointers remain valid for the instance.
|
||||
/// walks the coverpoint hit lists (O(hits), not O(product)). Bin names are
|
||||
/// built on demand from the coverpoints, so no per-bin name is stored.
|
||||
|
||||
class VlCoverCross VL_NOT_FINAL : public VlCoverpointIf {
|
||||
protected:
|
||||
struct Dimension final {
|
||||
VlCoverpoint* cpp; // Feeding coverpoint
|
||||
const uint32_t* hitsp; // Hit list cached for Cartesian traversal
|
||||
uint32_t bins; // Normal bin count
|
||||
uint32_t stride; // Flat-index stride
|
||||
};
|
||||
struct Bin final {
|
||||
const uint64_t* selectionp; // Slice of the cross's selection storage
|
||||
const char* namep; // Explicit bin name
|
||||
const char* filep; // Bin declaration file
|
||||
const uint32_t* wordIndicesp = nullptr; // Slice of the packed selection-word indices
|
||||
int line; // Bin declaration line
|
||||
int col; // Bin declaration column
|
||||
VlCovBinKind kind = VlCovBinKind::KIND_NORMAL; // Normal, ignore, or illegal bin
|
||||
uint32_t count = 0; // Samples matching the selection and guard
|
||||
uint32_t numWords = 0; // Number of nonzero selection-word indices
|
||||
uint32_t iffIndex = 0; // Original guard index, including bins removed during finalization
|
||||
};
|
||||
struct Word final {
|
||||
uint64_t autoExcluded = 0; // Tuples replaced by explicit bins
|
||||
uint64_t hitBits = 0; // Selected hit tuples, cleared after each sample
|
||||
uint32_t touchedWord = 0; // Flat word ID, stored by touched-list position
|
||||
};
|
||||
template <typename T>
|
||||
class View final {
|
||||
T* m_beginp; // First element of the viewed slice
|
||||
T* m_endp; // One past the last element of the viewed slice
|
||||
|
||||
public:
|
||||
View(T* datap, uint64_t size)
|
||||
: m_beginp{datap}
|
||||
, m_endp{datap ? datap + size : nullptr} {}
|
||||
T& operator[](uint64_t i) const { return m_beginp[i]; }
|
||||
uint64_t size() const { return m_beginp == m_endp ? 0 : m_endp - m_beginp; }
|
||||
bool empty() const { return m_beginp == m_endp; }
|
||||
T* begin() const { return m_beginp; }
|
||||
T* end() const { return m_endp; }
|
||||
};
|
||||
struct Explicit final {
|
||||
View<Bin> bins; // Explicit bins in declaration order
|
||||
Word* wordsp; // Masks use flat word indices; touchedWord uses a dense prefix
|
||||
View<uint32_t> autoBins; // Retained flat indices
|
||||
View<uint32_t> binWords; // Nonzero selection words, grouped by bin
|
||||
uint64_t* selectionp; // [bins.size() * ceil(m_numAutoBins / 64)]
|
||||
uint32_t numBins = 0; // Bins configured by addBin()
|
||||
uint32_t normalBins = 0; // Explicit bins contributing to coverage
|
||||
uint32_t minBinWords = 0; // Minimum nonzero-word count across explicit bins
|
||||
uint32_t numTouchedWords = 0; // Active prefix of wordsp[].touchedWord
|
||||
};
|
||||
|
||||
private:
|
||||
class VlCoverCross final : public VlCoverpointIf {
|
||||
// MEMBERS
|
||||
std::string m_hier; // "covergroup.cross"
|
||||
const char* m_file = nullptr; // Cross declaration file (registration metadata)
|
||||
@@ -368,436 +217,41 @@ private:
|
||||
// Cross bin indexes are unsigned, like the coverpoint bin indexes they are
|
||||
// built from. init() fatals if the product would exceed UINT32_MAX, so every
|
||||
// index computed here provably fits. That bound is far beyond anything
|
||||
// storable anyway: m_flatCountsp alone would need 16GB.
|
||||
// storable anyway: m_flatCounts alone would need 16GB.
|
||||
uint32_t m_numAutoBins = 0; // Product of per-dim Normal bin counts
|
||||
uint32_t m_numCovered = 0; // Distinct bins hit >= 1 (maintained incrementally)
|
||||
Dimension* m_dimensionsp = nullptr; // [m_dims], owned by the concrete cross runtime
|
||||
uint32_t* m_flatCountsp = nullptr; // [m_numAutoBins] Per-bin hit counts
|
||||
Explicit* m_explicitp = nullptr; // Absent for automatic-only crosses
|
||||
std::vector<uint32_t> m_cpBinCounts; // [m_dims] Normal bin count per dimension
|
||||
std::vector<uint32_t> m_stride; // [m_dims] Flat-index stride per dimension
|
||||
std::vector<uint32_t> m_flatCounts; // [m_numAutoBins] Per-bin hit counts
|
||||
std::vector<VlCoverpoint*> m_cps; // Feeding coverpoints (the only name source)
|
||||
|
||||
// PRIVATE METHODS
|
||||
bool hasExplicitBins() const { return m_explicitp != nullptr; }
|
||||
template <bool T_Explicit, bool T_RecordHits = true>
|
||||
void iterateProduct(uint32_t dim, uint32_t baseIdx);
|
||||
void incrementAuto(uint32_t idx) {
|
||||
if (m_flatCountsp[idx]++ == 0) ++m_numCovered;
|
||||
}
|
||||
void incrementBin(Bin& bin);
|
||||
template <bool T_RecordHits>
|
||||
void iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx);
|
||||
void incrementTuple(uint32_t idx) {
|
||||
Explicit& data = *m_explicitp;
|
||||
const uint32_t wordIdx = idx / 64;
|
||||
Word& word = data.wordsp[wordIdx];
|
||||
if ((word.autoExcluded >> (idx % 64)) & 1U) {
|
||||
if (T_RecordHits) {
|
||||
if (!word.hitBits) { data.wordsp[data.numTouchedWords++].touchedWord = wordIdx; }
|
||||
word.hitBits |= uint64_t{1} << (idx % 64);
|
||||
}
|
||||
// Explicit selections consume automatic tuples independently of iff.
|
||||
return;
|
||||
}
|
||||
incrementAuto(idx);
|
||||
if (m_flatCounts[idx]++ == 0) ++m_numCovered;
|
||||
}
|
||||
template <bool T_ApplyIffs>
|
||||
void sampleSingleTuple(uint32_t idx, const bool* binIffs);
|
||||
template <bool T_ApplyIffs, uint32_t T_Touched, bool T_Dense>
|
||||
void sampleBins(const bool* binIffs);
|
||||
template <bool T_ApplyIffs, bool T_Dense>
|
||||
void sampleHitWords(const bool* binIffs);
|
||||
uint32_t autoIndex(uint32_t i) const {
|
||||
return hasExplicitBins() ? m_explicitp->autoBins[i] : i;
|
||||
}
|
||||
std::string autoBinName(uint32_t flat) const;
|
||||
|
||||
protected:
|
||||
// CONSTRUCTORS
|
||||
VlCoverCross(uint32_t dims, uint32_t tuples)
|
||||
: m_dims{dims}
|
||||
, m_numAutoBins{tuples} {}
|
||||
void bindStorage(Dimension* dimensionsp, uint32_t* countsp, Explicit* explicitp = nullptr) {
|
||||
m_dimensionsp = dimensionsp;
|
||||
m_flatCountsp = countsp;
|
||||
m_explicitp = explicitp;
|
||||
}
|
||||
void shape(uint32_t dims, uint32_t tuples) {
|
||||
m_dims = dims;
|
||||
m_numAutoBins = tuples;
|
||||
}
|
||||
void addBinImpl(VlCovBinKind kind, const uint64_t* selectionp, uint32_t words,
|
||||
const char* namep, const char* filep, int line, int col, uint32_t iffIndex);
|
||||
|
||||
public:
|
||||
VL_UNCOPYABLE(VlCoverCross);
|
||||
// CONSTRUCTORS
|
||||
VlCoverCross() = default;
|
||||
|
||||
// METHODS
|
||||
// ---- configuration (from generated constructor, after coverpoints init'd) ----
|
||||
virtual void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
|
||||
int line, int col);
|
||||
/// Add a cross bin using a verilation-time bitmap of selected Normal-bin tuples.
|
||||
void addBin(VlCovBinKind kind, std::initializer_list<uint64_t> selection, const char* namep,
|
||||
const char* filep, int line, int col);
|
||||
/// Retain only automatic cross bins not selected by any explicit bin.
|
||||
virtual void finalizeBins();
|
||||
/// Register the bins in the coverage database; see VlCoverpoint::registerBins().
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page, uint32_t itemWeight,
|
||||
uint32_t groupWeight);
|
||||
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
|
||||
int line, int col);
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
|
||||
// ---- hot path (from generated sample(), after all coverpoints sampled) ----
|
||||
/// Sample automatic and explicit bins, optionally applying per-bin iff guards.
|
||||
/// Reads the feeding coverpoints saved by init(), so the caller passes no coverpoints.
|
||||
void sample(const bool* binIffs = nullptr);
|
||||
void sample(VlCoverpoint* const* cps);
|
||||
|
||||
// ---- VlCoverpointIf ----
|
||||
// Explicit bins (including ignore/illegal) precede retained automatic bins.
|
||||
uint32_t binCount() const override {
|
||||
return hasExplicitBins()
|
||||
? static_cast<uint32_t>(m_explicitp->bins.size() + m_explicitp->autoBins.size())
|
||||
: m_numAutoBins;
|
||||
}
|
||||
std::string binName(uint32_t i) const override;
|
||||
// A cross is a coverpoint whose bins are the auto cross bins (all Normal).
|
||||
uint32_t binCount() const override { return m_numAutoBins; }
|
||||
std::string binName(uint32_t flat) const override;
|
||||
void coverageParts(double& covered, double& total) const override {
|
||||
covered = m_numCovered;
|
||||
total = hasExplicitBins() ? m_explicitp->normalBins + m_explicitp->autoBins.size()
|
||||
: m_numAutoBins;
|
||||
total = m_numAutoBins;
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverCrossT
|
||||
/// Cross storage with verilation-time dimensions and bin capacities. All bin
|
||||
/// data stays at the registry-owned object's address; no per-buffer allocations
|
||||
/// or per-shape copies of the sampling algorithm are needed.
|
||||
|
||||
template <uint32_t Dims, uint32_t Tuples, uint32_t Bins, uint32_t AutoBins, uint64_t BinWords>
|
||||
class VlCoverCrossT final : public VlCoverCross {
|
||||
static constexpr uint32_t WORDS = Tuples / 64 + (Tuples % 64 != 0);
|
||||
static_assert(Bins > 0, "Explicit cross storage requires bins");
|
||||
|
||||
std::array<Dimension, Dims> m_dimensions;
|
||||
std::array<uint32_t, Tuples> m_counts{};
|
||||
std::array<Bin, Bins> m_bins;
|
||||
std::array<Word, WORDS> m_words{};
|
||||
std::array<uint32_t, AutoBins> m_autoBins;
|
||||
std::array<uint32_t, BinWords> m_binWords;
|
||||
std::array<uint64_t, static_cast<uint64_t>(Bins) * WORDS> m_selections;
|
||||
Explicit m_explicit;
|
||||
|
||||
public:
|
||||
VlCoverCrossT()
|
||||
: VlCoverCross{Dims, Tuples}
|
||||
, m_explicit{{m_bins.data(), Bins},
|
||||
m_words.data(),
|
||||
{m_autoBins.data(), AutoBins},
|
||||
{m_binWords.data(), BinWords},
|
||||
m_selections.data()} {
|
||||
bindStorage(m_dimensions.data(), m_counts.data(), &m_explicit);
|
||||
}
|
||||
};
|
||||
|
||||
/// Automatic-only crosses omit every explicit-bin array and its bookkeeping.
|
||||
template <uint32_t Dims, uint32_t Tuples>
|
||||
class VlCoverCrossT<Dims, Tuples, 0, 0, 0> final : public VlCoverCross {
|
||||
std::array<Dimension, Dims> m_dimensions;
|
||||
std::array<uint32_t, Tuples> m_counts{};
|
||||
|
||||
public:
|
||||
VlCoverCrossT()
|
||||
: VlCoverCross{Dims, Tuples} {
|
||||
bindStorage(m_dimensions.data(), m_counts.data());
|
||||
}
|
||||
};
|
||||
|
||||
// Construction-time cross layout over finalized coverpoints, sharing the sampling core.
|
||||
class VlCoverCrossDyn final : public VlCoverCross {
|
||||
class Layout;
|
||||
std::unique_ptr<Layout> m_layoutp; // Owned cross storage and construction-time selections
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCoverCrossDyn();
|
||||
~VlCoverCrossDyn() override;
|
||||
|
||||
// METHODS
|
||||
/// Initialize after all feeding coverpoints have finalized their live bins.
|
||||
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
|
||||
int line, int col) override;
|
||||
/// Build cross-bin selections in postfix order.
|
||||
void selectAll();
|
||||
/// Start a binsof term over the live bins declared in [first, end) of dimension 'dim'.
|
||||
void selectDim(uint32_t dim, uint32_t first, uint32_t end, bool negated, bool intersect);
|
||||
void selectRange(QData lo, QData hi);
|
||||
void selectRangeW(WDataInP lop, WDataInP hip);
|
||||
void selectDimEnd();
|
||||
void selectAnd();
|
||||
void selectOr();
|
||||
// Save a selection without renumbering guards when empty bins are removed.
|
||||
void selectBin(VlCovBinKind kind, const char* namep, const char* filep, int line, int col,
|
||||
uint32_t iffIndex);
|
||||
/// Apply cross exclusions and bind finalized storage to the sampling core.
|
||||
void finalizeBins() override;
|
||||
};
|
||||
|
||||
class VlCovergroupType;
|
||||
|
||||
//=============================================================================
|
||||
// VlCovergroupInst
|
||||
/// One covergroup instance: owns the coverpoint/cross runtimes created by one
|
||||
/// SV 'new'. The generated class holds borrowed pointers to them, so the bins
|
||||
/// outlive the SV object -- the coverage database registers raw count pointers
|
||||
/// and reads them at write() time, long after the object may have been freed.
|
||||
///
|
||||
/// Attach-counted: every VlCovInstHandle bound here holds one count, and the
|
||||
/// node is retired (see VlCovergroupType::retire) when the last one drops.
|
||||
|
||||
class VlCovergroupInst final {
|
||||
// MEMBERS
|
||||
// Coverpoint and cross runtimes of this instance; creation == declaration order
|
||||
std::vector<std::unique_ptr<VlCoverpointIf>> m_items;
|
||||
VlCovergroupType* const m_typep; // Owning type; outlives this node
|
||||
const uint32_t m_instId; // Stable identity across churn; NOT the slot
|
||||
#if !VM_COVERAGE
|
||||
// Only retire()'s free path uses this; under VM_COVERAGE the node is never
|
||||
// unlinked, so the slot would be dead. VlCovergroupType sets it.
|
||||
uint32_t m_slot = 0; // Index into m_typep->m_insts; unlink-by-swap rewrites
|
||||
#endif
|
||||
uint32_t m_attachCount = 1; // SV handles bound here; 1 from construction
|
||||
// option.weight of the SV object that created this node, while that object
|
||||
// lives; borrowed through VlCovInstHandle::lendWeight().
|
||||
const IData* m_weightp = nullptr;
|
||||
IData m_loadedWeight = 1; // Last option.weight loaded through m_weightp
|
||||
int32_t m_weight = 1; // Weight in use, never negative; kept once the object is gone
|
||||
VlFileLineDebug m_fileline; // Covergroup declaration, where a negative weight is reported
|
||||
bool m_retained = false; // VM_COVERAGE: dead, but kept for registered count pointers
|
||||
|
||||
// Reads m_items to fold the residue; owns m_slot and m_retained.
|
||||
friend class VlCovergroupType;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCovergroupInst(VlCovergroupType* typep, uint32_t instId)
|
||||
: m_typep{typep}
|
||||
, m_instId{instId} {}
|
||||
VL_UNCOPYABLE(VlCovergroupInst);
|
||||
|
||||
// METHODS
|
||||
// ---- construction (from the generated covergroup constructor) ----
|
||||
template <uint32_t MaxHits>
|
||||
VlCoverpointT<MaxHits>* addCoverpoint() {
|
||||
VlCoverpointT<MaxHits>* const cpp = new VlCoverpointT<MaxHits>{};
|
||||
m_items.emplace_back(cpp);
|
||||
return cpp; // borrowed by the generated class
|
||||
}
|
||||
template <uint32_t Dims, uint32_t Tuples, uint32_t Bins, uint32_t AutoBins, uint64_t BinWords>
|
||||
VlCoverCrossT<Dims, Tuples, Bins, AutoBins, BinWords>* addCross() {
|
||||
auto* const cxp = new VlCoverCrossT<Dims, Tuples, Bins, AutoBins, BinWords>{};
|
||||
m_items.emplace_back(cxp);
|
||||
return cxp; // borrowed by the generated class
|
||||
}
|
||||
VlCoverCrossDyn* addCrossDyn();
|
||||
|
||||
// ---- attach counting (from VlCovInstHandle) ----
|
||||
void attachInc() { ++m_attachCount; }
|
||||
// Drops one handle; true if it was the last and the caller must retire the
|
||||
// node. Retiring is the caller's job because VlCovergroupType is incomplete
|
||||
// here, and because it frees 'this'.
|
||||
bool attachDec() { return --m_attachCount == 0; }
|
||||
|
||||
// ---- instance weight (from VlCovInstHandle) ----
|
||||
void lendWeight(const IData* weightp, VlFileLineDebug fileline) {
|
||||
m_weightp = weightp;
|
||||
m_fileline = fileline;
|
||||
loadWeight();
|
||||
}
|
||||
// The lending object is being destroyed. Its members may already be gone, so
|
||||
// the weight is not read again; the last loaded value stays in effect.
|
||||
void unlendWeight(const IData* weightp) {
|
||||
if (m_weightp == weightp) m_weightp = nullptr;
|
||||
}
|
||||
/// Load option.weight from the lending object. SV writes the member directly
|
||||
/// (assignments, ref and output arguments, $value$plusargs, ...), so this is
|
||||
/// where a new value is seen, and checked once: a negative weight is reported as
|
||||
/// an error, and counts as zero.
|
||||
void loadWeight();
|
||||
/// Weight of this instance in its type's coverage (option.weight, IEEE
|
||||
/// 1800-2023 19.11.3), as last loaded; never negative.
|
||||
int32_t weight() const { return m_weight; }
|
||||
|
||||
// ---- introspection ----
|
||||
VlCovergroupType* typep() const { return m_typep; }
|
||||
uint32_t instId() const { return m_instId; }
|
||||
// True once retired but kept alive because the coverage database holds raw
|
||||
// pointers into this node's bin counts (VM_COVERAGE); see retire().
|
||||
bool retained() const { return m_retained; }
|
||||
/// IEEE 1800-2023 19.11 sums over the items whose coverage has a nonzero
|
||||
/// denominator: {the sum of each item's option.weight times its coverage
|
||||
/// (0..100), the sum of those weights}.
|
||||
std::pair<double, double> coverageSums() const;
|
||||
/// Instance coverage, as returned by get_inst_coverage(), in 0..100.
|
||||
double coverage();
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCovRetiredAvg
|
||||
/// Per-type residue: what survives an instance's death. Fixed size, so it does
|
||||
/// not grow with churn. Each instance contributes with its option.weight.
|
||||
|
||||
struct VlCovRetiredAvg final {
|
||||
uint64_t count = 0; // Retired instances that contributed (nonzero denominator)
|
||||
double sumCoverage = 0.0; // Sigma of per-instance weight * coverage (0..100)
|
||||
double sumWeight = 0.0; // Sigma of per-instance weight
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCovergroupType
|
||||
/// One covergroup type: owns its live instances, in creation order, plus the
|
||||
/// residue of the ones that have died.
|
||||
|
||||
class VlCovergroupType final {
|
||||
// MEMBERS
|
||||
// Live nodes, and -- under VM_COVERAGE -- retired-but-retained ones. Slot
|
||||
// order is creation order only until the first unlink-by-swap.
|
||||
std::vector<std::unique_ptr<VlCovergroupInst>> m_insts;
|
||||
uint32_t m_createdInsts = 0; // Instances ever created; never decremented
|
||||
uint32_t m_nextInstId = 0; // Monotonic; slots are reused, ids never are
|
||||
VlCovRetiredAvg m_retired; // Contribution of every instance that has died
|
||||
IData m_loadedTypeWeight = 1; // Last type_option.weight loaded by coverage()
|
||||
int32_t m_typeWeight = 1; // type_option.weight in use, never negative
|
||||
|
||||
// PRIVATE METHODS
|
||||
// Harvest instp's contribution into m_retired. Must run before instp is
|
||||
// unlinked: it reads the instance's items.
|
||||
void foldResidue(VlCovergroupInst* instp);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCovergroupType() = default;
|
||||
VL_UNCOPYABLE(VlCovergroupType);
|
||||
|
||||
// METHODS
|
||||
VlCovergroupInst* newInstance();
|
||||
// Called when the last handle to instp drops. Folds the residue, then
|
||||
// unlinks and frees the node -- except under VM_COVERAGE, where the coverage
|
||||
// database still holds raw pointers into it and it is only marked retained.
|
||||
void retire(VlCovergroupInst* instp);
|
||||
// True if any node here still has an SV handle bound to it, and so can be
|
||||
// retired again after the registry is destroyed. See ~VlCovRegistry.
|
||||
bool anyAttached() const;
|
||||
/// Type coverage, as returned by get_coverage(), in 0..100: the average of
|
||||
/// every instance's coverage, weighted by its option.weight (IEEE 1800-2023
|
||||
/// 19.11.3, type_option.merge_instances false). typeWeight is
|
||||
/// type_option.weight, which decides the result when no instance contributes;
|
||||
/// like option.weight, it is checked as it is loaded.
|
||||
double coverage(IData typeWeight, VlFileLineDebug fileline);
|
||||
|
||||
// ---- introspection ----
|
||||
// Test and debug only; generated code never calls these, and SV reaches them
|
||||
// only via explicit $c. They let a regression test pin node accumulation
|
||||
// (otherwise visible only as memory growth) and the residue fold.
|
||||
//
|
||||
// Instance nodes still reachable from SV. Under VM_COVERAGE this is smaller
|
||||
// than m_insts.size(), which also holds retained (dead) nodes.
|
||||
uint32_t liveInstanceCount() const;
|
||||
// Instances ever created, live or not. Wraps after 4G instances, which no
|
||||
// introspection use cares about.
|
||||
uint32_t createdInstanceCount() const { return m_createdInsts; }
|
||||
// Instances that have died and contributed to the residue.
|
||||
uint32_t retiredInstanceCount() const { return static_cast<uint32_t>(m_retired.count); }
|
||||
// Weighted mean coverage over the retired instances only, in 0..100; -1.0 if
|
||||
// none contributed or their weights sum to zero.
|
||||
double retiredCoverage() const;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCovRegistry
|
||||
/// Every covergroup type and instance in one VerilatedContext. Owned by the
|
||||
/// VerilatedContext (not by the coverage database, which is only linked under
|
||||
/// --coverage and is a *consumer* of this data), reached through
|
||||
/// VerilatedContext::covergroupRegistryp().
|
||||
|
||||
class VlCovRegistry final : public VerilatedVirtualBase {
|
||||
// MEMBERS
|
||||
std::vector<std::unique_ptr<VlCovergroupType>> m_types; // Creation order
|
||||
std::unordered_map<std::string, VlCovergroupType*> m_byName; // Lookup, borrowed
|
||||
|
||||
// PRIVATE METHODS
|
||||
VlCovergroupType* findType(const char* typeName) const; // nullptr if unknown
|
||||
VlCovergroupType* findOrCreateType(const char* typeName);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCovRegistry() = default;
|
||||
~VlCovRegistry() override;
|
||||
VL_UNCOPYABLE(VlCovRegistry);
|
||||
|
||||
// METHODS
|
||||
// Find-or-create the type node, then add an instance to it. typeName is the
|
||||
// generated covergroup class name, already --protect-ids obfuscated, and is
|
||||
// the same string that keys the coverage database's hier/page.
|
||||
VlCovergroupInst* newCovergroupInst(const char* typeName);
|
||||
/// Type coverage of a covergroup type (get_coverage()); see
|
||||
/// VlCovergroupType::coverage(). typeWeight is its type_option.weight.
|
||||
double typeCoverage(const char* typeName, IData typeWeight, VlFileLineDebug fileline);
|
||||
|
||||
// ---- introspection (see VlCovergroupType) ----
|
||||
// typeName is the obfuscated generated name, so a test using these under
|
||||
// --protect-ids must pass the obfuscated string; the no-argument form does not.
|
||||
uint32_t liveInstanceCount() const; // Summed over every type
|
||||
uint32_t createdInstanceCount() const; // Summed over every type
|
||||
uint32_t liveInstanceCount(const char* typeName) const; // 0 if type unknown
|
||||
uint32_t createdInstanceCount(const char* typeName) const; // 0 if type unknown
|
||||
uint32_t retiredInstanceCount(const char* typeName) const; // 0 if type unknown
|
||||
double retiredCoverage(const char* typeName) const; // -1.0 if type unknown or none
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCovInstHandle
|
||||
/// The generated covergroup class's link to its instance node. Attach-counting:
|
||||
/// the registry owns the node, but the handles are what keep it reachable, and
|
||||
/// the last one to go retires it.
|
||||
///
|
||||
/// Must stay copyable: every generated clone() copy-constructs. A copy shares
|
||||
/// the node, and so the bin counts -- pre-existing covergroup-copy aliasing.
|
||||
/// Attach counting makes that lifetime-safe, not correct.
|
||||
|
||||
class VlCovInstHandle final {
|
||||
// MEMBERS
|
||||
VlCovergroupInst* m_p = nullptr; // Attach-counted; the registry owns the node
|
||||
const IData* m_weightp = nullptr; // Owning object's option.weight, if lent to m_p
|
||||
|
||||
// PRIVATE METHODS
|
||||
// Drop one attach count, retiring the node if that was the last handle.
|
||||
// Nothing may touch instp afterwards: retire() may have freed it.
|
||||
static void release(VlCovergroupInst* instp, const IData* weightp) {
|
||||
if (VL_UNCOVERABLE(!instp)) return; // Never attach()ed; codegen always does
|
||||
instp->unlendWeight(weightp);
|
||||
if (instp->attachDec()) instp->typep()->retire(instp);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCovInstHandle() = default;
|
||||
// The copy's owning object lends no weight; the node keeps reading the lender's.
|
||||
VlCovInstHandle(const VlCovInstHandle& o)
|
||||
: m_p{o.m_p} {
|
||||
if (VL_UNCOVERABLE(!m_p)) return; // Unbound source; see release above
|
||||
m_p->attachInc();
|
||||
}
|
||||
// Deleted, not implemented: nothing generates an assignment, and the
|
||||
// implicit one would copy m_p raw -- no attachInc, no release.
|
||||
VlCovInstHandle& operator=(const VlCovInstHandle&) = delete;
|
||||
~VlCovInstHandle() { release(m_p, m_weightp); }
|
||||
|
||||
// METHODS
|
||||
// Bind to a freshly created node, taking over the attach count of 1 it was
|
||||
// created with. Called once, from the generated covergroup constructor.
|
||||
void attach(VlCovergroupInst* p) { m_p = p; }
|
||||
// Let the node read the owning object's option.weight until this handle is
|
||||
// destroyed. Called once, from the generated constructor, after attach().
|
||||
void lendWeight(const IData* weightp, VlFileLineDebug fileline) {
|
||||
m_weightp = weightp;
|
||||
m_p->lendWeight(weightp, fileline);
|
||||
}
|
||||
VlCovergroupInst* p() const { return m_p; }
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
@@ -105,9 +105,9 @@ private:
|
||||
int m_msb; // Inclusive upper bit for scalar path or element index for unpacked
|
||||
int m_rhsLsb; // Destination index that maps to RHS index 0
|
||||
const void* m_rhsDatap; // Pointer to RHS storage
|
||||
int m_bitLsb = 0; // Bit's LSB
|
||||
int m_bitMsb = 0; // Bit's MSB
|
||||
int m_elemWidth = 0; // Element width
|
||||
int m_bitLsb = 0;
|
||||
int m_bitMsb = 0;
|
||||
int m_elemWidth = 0;
|
||||
};
|
||||
|
||||
std::vector<Entry> m_entries; // Sorted by msb, non-overlapping
|
||||
@@ -303,7 +303,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T readIndex(const T origVal, int index) const {
|
||||
T readIndex(T origVal, int index) const {
|
||||
if (m_entries.empty()) return origVal;
|
||||
|
||||
T result = origVal;
|
||||
|
||||
+10
-26
@@ -1438,8 +1438,8 @@ inline void _vl_insert_WI(WDataOutP iowp, IData ld, int hbit, int lbit, int rbit
|
||||
const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword
|
||||
iowp[lword] = (iowp[lword] & ~linsmask) | ((lde << loffset) & linsmask);
|
||||
// Prevent unsafe write where lword was final writable location and hword is
|
||||
// out-of-bounds. rbits==0 means the caller guarantees bounds.
|
||||
if (VL_LIKELY(!(rbits && hword >= VL_WORDS_I(rbits)))) {
|
||||
// out-of-bounds.
|
||||
if (VL_LIKELY(!(hword == rword && roffset == 0))) {
|
||||
iowp[hword]
|
||||
= (iowp[hword] & ~hinsmask) | ((lde >> nbitsonright) & (hinsmask & cleanmask));
|
||||
}
|
||||
@@ -2948,8 +2948,8 @@ inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide<N_Words>& elem) {
|
||||
for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; }
|
||||
}
|
||||
|
||||
// These overloads work for both VlQueue<CData> (and similar) as well
|
||||
// as VlQueue<VlWide<N>>, including queues with different element types.
|
||||
// This specialization works for both VlQueue<CData> (and similar) as well
|
||||
// as VlQueue<VlWide<N>>.
|
||||
template <typename T>
|
||||
inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/, int srcElementBits,
|
||||
int dstElementBits) {
|
||||
@@ -2971,32 +2971,16 @@ inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/, int
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the same-type overload separate for its direct-copy optimization and alias handling;
|
||||
// differently typed queues cannot alias.
|
||||
template <typename T_Dst, typename T_Src>
|
||||
inline typename std::enable_if<!std::is_same<T_Dst, T_Src>::value>::type
|
||||
VL_COPY_Q(VlQueue<T_Dst>& q, const VlQueue<T_Src>& from, int /*lbits*/, int srcElementBits,
|
||||
int dstElementBits) {
|
||||
const size_t srcTotalBits = from.size() * srcElementBits;
|
||||
const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits;
|
||||
q.renew(dstSize);
|
||||
for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); }
|
||||
for (size_t bitIndex = 0; bitIndex < srcTotalBits; ++bitIndex) {
|
||||
VL_SET_QUEUE_BIT(q, dstElementBits, bitIndex,
|
||||
VL_GET_QUEUE_BIT(from, srcElementBits, bitIndex));
|
||||
}
|
||||
}
|
||||
|
||||
// This works for both VlQueue<CData> (and similar) as well as VlQueue<VlWide<N>>,
|
||||
// including queues with different element types.
|
||||
template <typename T_Dst, typename T_Src>
|
||||
inline void VL_REVCOPY_Q(VlQueue<T_Dst>& q, const VlQueue<T_Src>& from, int lbits,
|
||||
int srcElementBits, int dstElementBits) {
|
||||
// This specialization works for both VlQueue<CData> (and similar) as well
|
||||
// as VlQueue<VlWide<N>>.
|
||||
template <typename T>
|
||||
inline void VL_REVCOPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int lbits, int srcElementBits,
|
||||
int dstElementBits) {
|
||||
const size_t srcTotalBits = from.size() * srcElementBits;
|
||||
const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits;
|
||||
|
||||
// Always make a copy to handle the case where q and from are the same queue
|
||||
VlQueue<T_Src> srcCopy = from;
|
||||
VlQueue<T> srcCopy = from;
|
||||
|
||||
// Initialize all elements to zero using appropriate method
|
||||
q.renew(dstSize);
|
||||
|
||||
@@ -207,11 +207,6 @@ protected:
|
||||
// Used by scopeInsert, scopeFind, scopeErase, scopeNameMap
|
||||
mutable VerilatedMutex m_nameMutex; // Protect m_nameMap
|
||||
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex);
|
||||
|
||||
// Map of <interface_reference_name, interface reference>
|
||||
// Used by ifaceRefInsert, ifaceRefFind, ifaceRefErase, ifaceRefMap
|
||||
mutable VerilatedMutex m_ifaceRefMutex; // Protect m_ifaceRefMap
|
||||
VerilatedIfaceRefMap m_ifaceRefMap VL_GUARDED_BY(m_ifaceRefMutex);
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
@@ -275,10 +270,6 @@ public: // But only for verilated*.cpp
|
||||
void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
|
||||
void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE;
|
||||
|
||||
// METHODS - interface references - INTERNAL only for verilated*.cpp
|
||||
void ifaceRefInsert(const VerilatedIfaceRef& ifaceRef) VL_MT_SAFE;
|
||||
void ifaceRefErase(const std::string& fullname, const VerilatedScope* scopep) VL_MT_SAFE;
|
||||
|
||||
// METHODS - file IO - INTERNAL only for verilated*.cpp
|
||||
|
||||
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -674,38 +674,10 @@ void VlRandomVar::set(const std::string& idx, const std::string& val) const {
|
||||
}
|
||||
}
|
||||
|
||||
void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits,
|
||||
const std::vector<std::string>* layerVarsp) {
|
||||
// layerVarsp scopes sampling to the current phase's own layer, so a
|
||||
// phased solve's diversity constraint can't be built entirely out of a
|
||||
// later phase's (still-unsolved) variable instead of this one's.
|
||||
std::vector<const VlRandomVar*>& vars = m_randomConstraintVars;
|
||||
vars.clear();
|
||||
int varBits = 0;
|
||||
if (layerVarsp) {
|
||||
for (const auto& name : *layerVarsp) {
|
||||
const auto it = m_vars.find(name);
|
||||
// buildSolveLayers() only ever adds a name after confirming
|
||||
// it's already in m_vars.
|
||||
assert(it != m_vars.end());
|
||||
vars.push_back(it->second.get());
|
||||
varBits += it->second->totalWidth();
|
||||
}
|
||||
} else {
|
||||
for (const auto& var : m_vars) {
|
||||
vars.push_back(var.second.get());
|
||||
varBits += var.second->totalWidth();
|
||||
}
|
||||
}
|
||||
|
||||
if (varBits == 0) {
|
||||
// Nothing to sample (e.g. a still-unsized queue/dynamic array, in a
|
||||
// layer or the whole class) -- tautology instead of the degenerate
|
||||
// empty-operand expression the loop below would otherwise build.
|
||||
os << "(= #b1 #b1)";
|
||||
return;
|
||||
}
|
||||
void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits) {
|
||||
const IData hash = VL_RANDOM_RNG_I(rngr) & ((1 << bits) - 1);
|
||||
int varBits = 0;
|
||||
for (const auto& var : m_vars) varBits += var.second->totalWidth();
|
||||
os << "(= #b";
|
||||
for (int i = bits - 1; i >= 0; i--) os << (VL_BITISSET_I(hash, i) ? '1' : '0');
|
||||
if (bits > 1) os << " (concat";
|
||||
@@ -713,12 +685,11 @@ void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits,
|
||||
IData varBitsLeft = varBits;
|
||||
IData varBitsWant = (varBits + 1) / 2;
|
||||
if (varBits > 2) os << " (bvxor";
|
||||
for (const auto& varp : vars) { // LCOV_EXCL_BR_LINE - reservoir-sampling below
|
||||
// always forces its last pick before exhausting vars
|
||||
for (int j = 0; j < varp->totalWidth(); j++, varBitsLeft--) {
|
||||
for (const auto& var : m_vars) {
|
||||
for (int j = 0; j < var.second->totalWidth(); j++, varBitsLeft--) {
|
||||
const bool doEmit = (VL_RANDOM_RNG_I(rngr) % varBitsLeft) < varBitsWant;
|
||||
if (doEmit) {
|
||||
varp->emitExtract(os, j);
|
||||
var.second->emitExtract(os, j);
|
||||
if (--varBitsWant == 0) break;
|
||||
}
|
||||
}
|
||||
@@ -808,7 +779,7 @@ bool VlRandomizer::next_check_only(VlRNGReseeds& rngr) { return nextRandomize(rn
|
||||
bool VlRandomizer::next(VlRNGReseeds& rngr) { return nextRandomize(rngr, false); }
|
||||
|
||||
bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) {
|
||||
if (!checkOnly && m_vars.empty() && m_uniqueArrays.empty()) return true;
|
||||
if (!checkOnly && m_vars.empty() && m_unique_arrays.empty()) return true;
|
||||
if (checkOnly && m_vars.empty()) return true; // No rand members: trivially SAT
|
||||
VlSolverSession& sess = s_solverSession;
|
||||
const VerilatedLockGuard lock{sess.m_mutex};
|
||||
@@ -843,27 +814,21 @@ bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) {
|
||||
|
||||
std::vector<std::string> VlRandomizer::buildUniqueExprs() const {
|
||||
std::vector<std::string> exprs;
|
||||
if (m_uniqueArrays.empty()) return exprs;
|
||||
if (m_unique_arrays.empty()) return exprs;
|
||||
const auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
||||
for (const auto& [key, arrNameVec] : m_uniqueArrays) {
|
||||
uint32_t matchingElementsCount = 0;
|
||||
std::ostringstream varsString;
|
||||
|
||||
for (const auto& baseName : arrNameVec) {
|
||||
const auto it = m_vars.find(baseName);
|
||||
if (it == m_vars.end()) continue;
|
||||
const VlRandomVar& var = *it->second;
|
||||
// Select the elements the array actually holds now, by their own index
|
||||
// or key, rather than by ordinal position
|
||||
var.setArrayInfo(arrVarsp);
|
||||
// 'distinct' needs at least two operands; fewer elements are trivially unique
|
||||
matchingElementsCount += var.countMatchingElements(*arrVarsp, baseName);
|
||||
var.emitGetValue(varsString);
|
||||
}
|
||||
|
||||
if (matchingElementsCount < 2) continue;
|
||||
for (const std::string& baseName : m_unique_arrays) {
|
||||
const auto it = m_vars.find(baseName);
|
||||
if (it == m_vars.end()) continue;
|
||||
const VlRandomVar& var = *it->second;
|
||||
// Select the elements the array actually holds now, by their own index
|
||||
// or key, rather than by ordinal position
|
||||
var.setArrayInfo(arrVarsp);
|
||||
// 'distinct' needs at least two operands; fewer elements are trivially unique
|
||||
if (var.countMatchingElements(*arrVarsp, baseName) < 2) continue;
|
||||
std::ostringstream os;
|
||||
os << "(__Vbv (distinct " << varsString.str() << "))";
|
||||
os << "(__Vbv (distinct ";
|
||||
var.emitGetValue(os);
|
||||
os << "))";
|
||||
exprs.push_back(os.str());
|
||||
}
|
||||
return exprs;
|
||||
@@ -1612,7 +1577,7 @@ void VlRandomizer::clearConstraints() {
|
||||
m_constraints_line.clear();
|
||||
m_solveBefore.clear();
|
||||
m_softConstraints.clear();
|
||||
m_uniqueArrays.clear(); // Re-registered by constraint setup
|
||||
m_unique_arrays.clear(); // Re-registered by constraint setup
|
||||
// Keep m_vars for class member randomization
|
||||
}
|
||||
|
||||
@@ -1625,7 +1590,7 @@ void VlRandomizer::clearAll() {
|
||||
m_randcConstraintHash = 0;
|
||||
}
|
||||
|
||||
void VlRandomizer::markRandc(const std::string& name) { m_randcVarNames.insert(name); }
|
||||
void VlRandomizer::markRandc(const char* name) { m_randcVarNames.insert(name); }
|
||||
|
||||
void VlRandomizer::solveBefore(const std::string& beforeName, const std::string& afterName) {
|
||||
m_solveBefore.emplace_back(beforeName, afterName);
|
||||
@@ -1783,9 +1748,8 @@ bool VlRandomizer::solvePhaseValues(VlSolverSession& sess, VlRNG& rngr,
|
||||
|
||||
// Try diversity: add random constraint, re-check. If sat, get
|
||||
// updated (more diverse) values. If unsat, keep baseline values.
|
||||
// Scoped to this layer's own vars -- see randomConstraint's comment.
|
||||
os << "(assert ";
|
||||
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN, &layerVars);
|
||||
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
|
||||
os << ")\n";
|
||||
os << "(check-sat)\n";
|
||||
if (sess.readStatus() == VlSolverStatus::SAT) {
|
||||
|
||||
+21
-208
@@ -259,13 +259,10 @@ class VlRandomizer VL_NOT_FINAL {
|
||||
m_constraints_line; // fileline content of the constraint for unsat constraints
|
||||
std::vector<std::string> m_softConstraints; // Soft constraints
|
||||
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
|
||||
// Scratch buffer for randomConstraint(), reused across calls
|
||||
std::vector<const VlRandomVar*> m_randomConstraintVars;
|
||||
std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back)
|
||||
// variables
|
||||
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
|
||||
// Arrays whose elements must be distinct grouped by unique constraint
|
||||
std::map<uint32_t, std::unordered_set<std::string>> m_uniqueArrays;
|
||||
std::vector<std::string> m_unique_arrays; // Arrays whose elements must be distinct
|
||||
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
|
||||
const VlQueue<CData>* m_static_randmodep = nullptr; // Static rand_mode state (shared)
|
||||
std::unordered_set<std::string> m_staticVars; // Names of static rand vars
|
||||
@@ -282,8 +279,7 @@ class VlRandomizer VL_NOT_FINAL {
|
||||
bool hasFrozenVar() const; // true if any var is currently rand_mode(0)-frozen
|
||||
|
||||
// PRIVATE METHODS
|
||||
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits,
|
||||
const std::vector<std::string>* layerVarsp = nullptr);
|
||||
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
|
||||
// Fetch the model and write it into the registered variables.
|
||||
bool applyModel(VlSolverSession& sess);
|
||||
bool parseModel(std::istream& is, size_t requested);
|
||||
@@ -470,16 +466,16 @@ public:
|
||||
|
||||
// Mark a variable as rand_mode-disabled: solver keeps it in m_vars
|
||||
// (so constraints still reference it) but skips write-back after solving.
|
||||
void set_var_disabled(const std::string& name) { m_disabledVars.insert(name); }
|
||||
void set_var_disabled(const char* name) { m_disabledVars.insert(name); }
|
||||
// Clear disabled state for a variable
|
||||
void clear_var_disabled(const std::string& name) { m_disabledVars.erase(name); }
|
||||
void clear_var_disabled(const char* name) { m_disabledVars.erase(name); }
|
||||
|
||||
// --- write_var to register variables ---
|
||||
// Register scalar variable (non-struct, basic type)
|
||||
template <typename T>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value,
|
||||
void>::type
|
||||
write_var(T& var, int width, const std::string& name, int dimension,
|
||||
write_var(T& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (m_vars.find(name) != m_vars.end()) return;
|
||||
// TODO: make_unique once VlRandomizer is per-instance not per-ref
|
||||
@@ -492,7 +488,7 @@ public:
|
||||
typename std::enable_if<VlIsCustomStruct<T>::value, void>::type
|
||||
write_var(T& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
modifyMembers(var, var.memberIndices(), name, randmodeIdx);
|
||||
modifyMembers(var, var.memberIndices(), name);
|
||||
}
|
||||
|
||||
// Register queue of non-struct types
|
||||
@@ -517,7 +513,7 @@ public:
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlQueue<T, N_MaxSize>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
record_struct_arr(var, name, dimension, {}, {}, randmodeIdx);
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
}
|
||||
// Register unpacked array of non-struct types
|
||||
template <typename T, std::size_t N_Depth>
|
||||
@@ -543,7 +539,7 @@ public:
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlUnpacked<T, N_Depth>& var, int /*width*/, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
record_struct_arr(var, name, dimension, {}, {}, randmodeIdx);
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
}
|
||||
|
||||
// Register associative array of non-struct types
|
||||
@@ -571,7 +567,7 @@ public:
|
||||
typename std::enable_if<VlContainsCustomStruct<T_Value>::value, void>::type
|
||||
write_var(VlAssocArray<T_Key, T_Value>& var, int /*width*/, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
record_struct_arr(var, name, dimension, {}, {}, randmodeIdx);
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
}
|
||||
|
||||
// --- Record Arrays: flat and struct ---
|
||||
@@ -586,23 +582,10 @@ public:
|
||||
++m_index;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<!std::is_class<T>::value || VlIsVlWide<T>::value, void>::type
|
||||
update_arr_table(T& var, const std::string& name, int /*dimension*/) {
|
||||
const std::string key = generateKey(name, m_index++);
|
||||
const auto it = m_arr_vars.find(key);
|
||||
assert(it != m_arr_vars.end());
|
||||
const ArrayInfo& arrayInfo = *it->second;
|
||||
it->second = std::make_shared<const ArrayInfo>(arrayInfo.m_name, &var, arrayInfo.m_index,
|
||||
arrayInfo.m_indices, arrayInfo.m_idxWidths);
|
||||
}
|
||||
|
||||
// This is the "Sender" API for the generated code.
|
||||
// The elements to make distinct are taken from the array element table at
|
||||
// solve time, so a container resized by the solver is handled correctly.
|
||||
void rand_unique(const std::string& name, const uint32_t key) {
|
||||
m_uniqueArrays[key].insert(name);
|
||||
}
|
||||
void rand_unique(const std::string& name) { m_unique_arrays.push_back(name); }
|
||||
|
||||
// Recursively record all elements in an unpacked array
|
||||
template <typename T, std::size_t N_Depth>
|
||||
@@ -620,15 +603,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively update pointers to all elements in an unpacked array
|
||||
template <typename T, std::size_t N_Depth>
|
||||
void update_arr_table(VlUnpacked<T, N_Depth>& var, const std::string& name, int dimension) {
|
||||
assert(dimension > 0);
|
||||
for (size_t i = 0; i < N_Depth; ++i) {
|
||||
update_arr_table(var[i], name + "[" + std::to_string(i) + "]", dimension - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively record all elements in a queue
|
||||
template <typename T, size_t N_MaxSize>
|
||||
void record_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
|
||||
@@ -644,15 +618,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively update pointers to all elements in a queue
|
||||
template <typename T, size_t N_MaxSize>
|
||||
void update_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension) {
|
||||
assert(dimension > 0);
|
||||
for (size_t i = 0; i < var.size(); ++i) {
|
||||
update_arr_table(var.atWrite(i), name + "[" + std::to_string(i) + "]", dimension - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively record all elements in an associative array
|
||||
template <typename T_Key, typename T_Value>
|
||||
void record_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
|
||||
@@ -683,27 +648,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively update pointers to all elements in an associative array
|
||||
template <typename T_Key, typename T_Value>
|
||||
void update_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
|
||||
int dimension) {
|
||||
assert(dimension > 0);
|
||||
for (auto it = var.begin(); it != var.end(); ++it) {
|
||||
const T_Key& key = it->first;
|
||||
std::string indexed_name;
|
||||
std::vector<size_t> integral_index;
|
||||
size_t idx_width = 0;
|
||||
process_key(key, indexed_name, integral_index, name, idx_width);
|
||||
update_arr_table(var.atWrite(key), indexed_name, dimension - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Register a single structArray element via write_var
|
||||
template <typename T>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
record_struct_arr(T& var, const std::string& name, int /*dimension*/,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths,
|
||||
std::uint32_t randmodeIdx) {
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
std::ostringstream oss;
|
||||
for (size_t i = 0; i < indices.size(); ++i) {
|
||||
oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0')
|
||||
@@ -711,75 +660,43 @@ public:
|
||||
if (i < indices.size() - 1) oss << ".";
|
||||
}
|
||||
write_var(var, 1ULL,
|
||||
oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL,
|
||||
randmodeIdx);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
update_struct_arr(T& var, const std::string& name) {
|
||||
update_var(var, name.c_str());
|
||||
oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL);
|
||||
}
|
||||
|
||||
// Recursively process VlUnpacked of structs
|
||||
template <typename T, std::size_t N_Depth>
|
||||
void record_struct_arr(VlUnpacked<T, N_Depth>& var, const std::string& name, int dimension,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths,
|
||||
std::uint32_t randmodeIdx) {
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
if (dimension > 0 && N_Depth != 0) {
|
||||
constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1);
|
||||
idxWidths.push_back(idx_width);
|
||||
for (size_t i = 0; i < N_Depth; ++i) {
|
||||
indices.push_back(i);
|
||||
record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths,
|
||||
randmodeIdx);
|
||||
record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths);
|
||||
indices.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N_Depth>
|
||||
void update_struct_arr(VlUnpacked<T, N_Depth>& var, const std::string& name) {
|
||||
constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1);
|
||||
for (size_t i = 0; i < N_Depth; ++i) {
|
||||
std::ostringstream oss;
|
||||
oss << name << "." << std::hex << std::setw(int(idx_width / 4)) << std::setfill('0')
|
||||
<< static_cast<int>(i);
|
||||
update_struct_arr(var[i], oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively process VlQueue of structs
|
||||
template <typename T, size_t N_MaxSize>
|
||||
void record_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths,
|
||||
std::uint32_t randmodeIdx) {
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
if ((dimension > 0) && (var.size() != 0)) {
|
||||
idxWidths.push_back(32);
|
||||
for (size_t i = 0; i < var.size(); ++i) {
|
||||
indices.push_back(i);
|
||||
record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths,
|
||||
randmodeIdx);
|
||||
record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths);
|
||||
indices.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, size_t N_MaxSize>
|
||||
void update_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name) {
|
||||
for (size_t i = 0; i < var.size(); ++i) {
|
||||
std::ostringstream oss;
|
||||
oss << name << "." << std::hex << std::setw(8) << std::setfill('0')
|
||||
<< static_cast<int>(i);
|
||||
update_struct_arr(var.atWrite(i), oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively process associative arrays of structs
|
||||
template <typename T_Key, typename T_Value>
|
||||
void record_struct_arr(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
|
||||
int dimension, const std::vector<IData>& indices,
|
||||
const std::vector<size_t>& idxWidths, std::uint32_t randmodeIdx) {
|
||||
const std::vector<size_t>& idxWidths) {
|
||||
if ((dimension > 0) && (!var.empty())) {
|
||||
for (auto it = var.begin(); it != var.end(); ++it) {
|
||||
const T_Key& key = it->first;
|
||||
@@ -797,127 +714,23 @@ public:
|
||||
std::string result = oss.str();
|
||||
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
|
||||
record_struct_arr(var.atWrite(key), name + "." + result, dimension - 1, indices,
|
||||
idxWidths, randmodeIdx);
|
||||
idxWidths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T_Key, typename T_Value>
|
||||
void update_struct_arr(VlAssocArray<T_Key, T_Value>& var, const std::string& name) {
|
||||
for (auto it = var.begin(); it != var.end(); ++it) {
|
||||
const T_Key& key = it->first;
|
||||
|
||||
std::string indexed_name;
|
||||
std::vector<size_t> integral_index;
|
||||
size_t idx_width = 0;
|
||||
|
||||
process_key(key, indexed_name, integral_index, name, idx_width);
|
||||
std::ostringstream oss;
|
||||
for (size_t i = 0; i < integral_index.size(); ++i) {
|
||||
oss << std::hex << static_cast<int>(integral_index[i]);
|
||||
}
|
||||
|
||||
std::string result = oss.str();
|
||||
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
|
||||
update_struct_arr(var.atWrite(key), name + "." + result);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Helper functions ---
|
||||
|
||||
// Helper: Register all members of a user-defined struct
|
||||
template <typename T, std::size_t... I>
|
||||
void modifyMembers(T& obj, std::index_sequence<I...>, const std::string& baseName,
|
||||
std::uint32_t randmodeIdx) {
|
||||
void modifyMembers(T& obj, std::index_sequence<I...>, const std::string& baseName) {
|
||||
// Use the indices to access each member via std::get
|
||||
(void)std::initializer_list<int>{
|
||||
(write_var(std::get<I>(obj.getMembers(obj)), obj.memberWidth()[I],
|
||||
(baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I],
|
||||
randmodeIdx),
|
||||
(baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I]),
|
||||
0)...};
|
||||
}
|
||||
|
||||
template <typename T, std::size_t... I>
|
||||
void updateMembers(T& obj, std::index_sequence<I...>, const std::string& baseName) {
|
||||
(void)std::initializer_list<int>{
|
||||
(update_var(std::get<I>(obj.getMembers(obj)),
|
||||
(baseName + "." + obj.memberNames()[I]).c_str()),
|
||||
0)...};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value,
|
||||
void>::type
|
||||
update_var(T& var, const char* name) {
|
||||
auto it = m_vars.find(name);
|
||||
assert(it != m_vars.end());
|
||||
it->second = std::make_shared<const VlRandomVar>(
|
||||
name, it->second->width(), &var, it->second->dimension(), it->second->randModeIdx());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<VlIsCustomStruct<T>::value, void>::type update_var(T& var,
|
||||
const char* name) {
|
||||
updateMembers(var, var.memberIndices(), name);
|
||||
}
|
||||
|
||||
template <typename T, size_t N_MaxSize>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
update_var(VlQueue<T, N_MaxSize>& var, const char* name) {
|
||||
auto it = m_vars.find(name);
|
||||
assert(it != m_vars.end());
|
||||
const int dimension = it->second->dimension();
|
||||
it->second = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T, N_MaxSize>>>(
|
||||
name, it->second->width(), &var, dimension, it->second->randModeIdx());
|
||||
m_index = 0;
|
||||
update_arr_table(var, name, dimension);
|
||||
}
|
||||
|
||||
template <typename T, size_t N_MaxSize>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
update_var(VlQueue<T, N_MaxSize>& var, const char* name) {
|
||||
update_struct_arr(var, name);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N_Depth>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
update_var(VlUnpacked<T, N_Depth>& var, const char* name) {
|
||||
auto it = m_vars.find(name);
|
||||
assert(it != m_vars.end());
|
||||
const int dimension = it->second->dimension();
|
||||
it->second = std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
|
||||
name, it->second->width(), &var, dimension, it->second->randModeIdx());
|
||||
m_index = 0;
|
||||
update_arr_table(var, name, dimension);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N_Depth>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
update_var(VlUnpacked<T, N_Depth>& var, const char* name) {
|
||||
update_struct_arr(var, name);
|
||||
}
|
||||
|
||||
template <typename T_Key, typename T_Value>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T_Value>::value, void>::type
|
||||
update_var(VlAssocArray<T_Key, T_Value>& var, const char* name) {
|
||||
auto it = m_vars.find(name);
|
||||
assert(it != m_vars.end());
|
||||
const int dimension = it->second->dimension();
|
||||
std::vector<size_t> keyWidths;
|
||||
VlRandomAssocKeyWidths<VlAssocArray<T_Key, T_Value>>::push(keyWidths);
|
||||
it->second
|
||||
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
|
||||
name, it->second->width(), &var, dimension, it->second->randModeIdx(), keyWidths);
|
||||
m_index = 0;
|
||||
update_arr_table(var, name, dimension);
|
||||
}
|
||||
|
||||
template <typename T_Key, typename T_Value>
|
||||
typename std::enable_if<VlContainsCustomStruct<T_Value>::value, void>::type
|
||||
update_var(VlAssocArray<T_Key, T_Value>& var, const char* name) {
|
||||
update_struct_arr(var, name);
|
||||
}
|
||||
|
||||
// Helper: Generate unique variable key from name and index
|
||||
static std::string generateKey(const std::string& name, int idx) {
|
||||
if (!name.empty() && name[0] == '\\') {
|
||||
@@ -952,7 +765,7 @@ public:
|
||||
void disable_soft(const std::string& varName);
|
||||
void clearConstraints();
|
||||
void clearAll(); // Clear both constraints and variables
|
||||
void markRandc(const std::string& name); // Mark variable as randc for cyclic tracking
|
||||
void markRandc(const char* name); // Mark variable as randc for cyclic tracking
|
||||
void solveBefore(const std::string& beforeName,
|
||||
const std::string& afterName); // Register solve-before ordering
|
||||
void set_randmode(const VlQueue<CData>& randmode) { m_randmodep = &randmode; }
|
||||
|
||||
+46
-56
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -72,13 +72,4 @@ public:
|
||||
~VerilatedHierarchyMap() = default;
|
||||
};
|
||||
|
||||
// Map of sorted interface reference names to the concrete interface they refer to
|
||||
// Keyed by value, as the full name is built at construction
|
||||
// This is a class instead of typedef/using to allow forward declaration in verilated.h
|
||||
class VerilatedIfaceRefMap final : public std::map<std::string, VerilatedIfaceRef, std::less<>> {
|
||||
public:
|
||||
VerilatedIfaceRefMap() = default;
|
||||
~VerilatedIfaceRefMap() = default;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
@@ -232,7 +231,7 @@ extern std::string VL_TO_STRING(SData lhs);
|
||||
extern std::string VL_TO_STRING(IData lhs);
|
||||
extern std::string VL_TO_STRING(QData lhs);
|
||||
extern std::string VL_TO_STRING(double lhs);
|
||||
extern std::string VL_TO_STRING(const std::string& obj) VL_PURE;
|
||||
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
|
||||
template <std::size_t N_Words>
|
||||
inline std::string VL_TO_STRING(const VlWide<N_Words>& obj) {
|
||||
return VL_TO_STRING_W(N_Words, obj);
|
||||
@@ -1085,15 +1084,6 @@ public:
|
||||
// CONSTRUCTORS
|
||||
// m_defaultValue isn't defaulted. Caller's constructor must do it.
|
||||
VlAssocArray() = default;
|
||||
// Construct with the given entries
|
||||
explicit VlAssocArray(std::initializer_list<std::pair<const T_Key, T_Value>> init)
|
||||
: m_map{init}
|
||||
, m_defaultValue{} {}
|
||||
// Construct with the given default value and entries
|
||||
explicit VlAssocArray(const T_Value& defaultValue,
|
||||
std::initializer_list<std::pair<const T_Key, T_Value>> init)
|
||||
: m_map{init}
|
||||
, m_defaultValue{defaultValue} {}
|
||||
~VlAssocArray() = default;
|
||||
VlAssocArray(const VlAssocArray&) = default;
|
||||
VlAssocArray(VlAssocArray&&) = default;
|
||||
@@ -1478,14 +1468,6 @@ public:
|
||||
|
||||
constexpr std::size_t size() const { return N_Depth; }
|
||||
|
||||
// Runtime slice v[loIdx +: N_Out], loIdx being an index into m_storage
|
||||
template <std::size_t N_Out>
|
||||
VlUnpacked<T_Value, N_Out> slice(int32_t loIdx) const {
|
||||
VlUnpacked<T_Value, N_Out> out;
|
||||
for (std::size_t i = 0; i < N_Out; ++i) out.m_storage[i] = m_storage[loIdx + i];
|
||||
return out;
|
||||
}
|
||||
|
||||
void fill(const T_Value& value) {
|
||||
std::fill(std::begin(m_storage), std::end(m_storage), value);
|
||||
}
|
||||
|
||||
+137
-307
@@ -253,34 +253,34 @@ public:
|
||||
if (m_fullname.empty()) m_fullname = std::string{m_scopep->name()} + '.' + m_varp->name();
|
||||
return m_fullname.c_str();
|
||||
}
|
||||
virtual void* readDatap() const { return m_varp->datap(); }
|
||||
CData* readCDatap() const {
|
||||
virtual void* varDatap() const { return m_varp->datap(); }
|
||||
CData* varCDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8););
|
||||
return static_cast<CData*>(readDatap());
|
||||
return reinterpret_cast<CData*>(varDatap());
|
||||
}
|
||||
SData* readSDatap() const {
|
||||
SData* varSDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16););
|
||||
return static_cast<SData*>(readDatap());
|
||||
return reinterpret_cast<SData*>(varDatap());
|
||||
}
|
||||
IData* readIDatap() const {
|
||||
IData* varIDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32););
|
||||
return static_cast<IData*>(readDatap());
|
||||
return reinterpret_cast<IData*>(varDatap());
|
||||
}
|
||||
QData* readQDatap() const {
|
||||
QData* varQDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64););
|
||||
return static_cast<QData*>(readDatap());
|
||||
return reinterpret_cast<QData*>(varDatap());
|
||||
}
|
||||
EData* readEDatap() const {
|
||||
EData* varEDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA););
|
||||
return static_cast<EData*>(readDatap());
|
||||
return reinterpret_cast<EData*>(varDatap());
|
||||
}
|
||||
double* readRealDatap() const {
|
||||
double* varRealDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL););
|
||||
return static_cast<double*>(readDatap());
|
||||
return reinterpret_cast<double*>(varDatap());
|
||||
}
|
||||
std::string* readStringDatap() const {
|
||||
std::string* varStringDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING););
|
||||
return static_cast<std::string*>(readDatap());
|
||||
return reinterpret_cast<std::string*>(varDatap());
|
||||
}
|
||||
virtual uint32_t bitOffset() const { return 0; }
|
||||
};
|
||||
@@ -363,13 +363,11 @@ protected:
|
||||
public:
|
||||
explicit VerilatedVpioScope(const VerilatedScope* scopep)
|
||||
: m_scopep{scopep} {
|
||||
m_fullname = vpiFullnamep(m_scopep->name());
|
||||
m_fullname = m_scopep->name();
|
||||
if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
|
||||
m_name = m_scopep->identifier();
|
||||
m_defname = m_scopep->defname();
|
||||
}
|
||||
static const char* vpiFullnamep(const char* namep) VL_PURE {
|
||||
return (std::strncmp(namep, "TOP.", 4) == 0) ? namep + 4 : namep;
|
||||
}
|
||||
~VerilatedVpioScope() override = default;
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
static VerilatedVpioScope* castp(vpiHandle h) {
|
||||
@@ -412,9 +410,6 @@ public:
|
||||
m_name = name;
|
||||
m_fullNameOverride = fullname;
|
||||
}
|
||||
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep,
|
||||
const std::string& name, const std::string& fullname)
|
||||
: VerilatedVpioVar{varp, scopep, varp->datap(), name, fullname} {}
|
||||
explicit VerilatedVpioVar(const VerilatedVpioVar* vop)
|
||||
: VerilatedVpioVarBase{vop} {
|
||||
if (vop) {
|
||||
@@ -520,7 +515,7 @@ public:
|
||||
const std::string localName = _vl_vpi_member_local_name(memberVarp->name());
|
||||
|
||||
return new VerilatedVpioVar{memberVarp, scopep(),
|
||||
static_cast<uint8_t*>(readDatap()) + offset, localName,
|
||||
static_cast<uint8_t*>(varDatap()) + offset, localName,
|
||||
std::string{fullname()} + memberName.substr(parentLen)};
|
||||
}
|
||||
uint32_t type() const override {
|
||||
@@ -545,7 +540,7 @@ public:
|
||||
return m_fullname.c_str();
|
||||
}
|
||||
uint8_t* prevDatap() const { return m_prevDatap; }
|
||||
void* readDatap() const override { return m_varDatap; }
|
||||
void* varDatap() const override { return m_varDatap; }
|
||||
void createPrevDatap() {
|
||||
if (VL_UNLIKELY(!m_prevDatap)) {
|
||||
m_prevDatap = new uint8_t[entSize()];
|
||||
@@ -726,17 +721,6 @@ public:
|
||||
uint32_t type() const override { return vpiModule; }
|
||||
};
|
||||
|
||||
class VerilatedVpioInterface final : public VerilatedVpioScope {
|
||||
public:
|
||||
explicit VerilatedVpioInterface(const VerilatedScope* scopep)
|
||||
: VerilatedVpioScope{scopep} {}
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
static VerilatedVpioInterface* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioInterface*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
uint32_t type() const override { return vpiInterface; }
|
||||
};
|
||||
|
||||
class VerilatedVpioModuleIter final : public VerilatedVpio {
|
||||
const std::vector<const VerilatedScope*>* m_vec;
|
||||
std::vector<const VerilatedScope*>::const_iterator m_it;
|
||||
@@ -794,8 +778,6 @@ public:
|
||||
return (new VerilatedVpioScope{modp})->castVpiHandle();
|
||||
} else if (itype == VerilatedScope::SCOPE_MODULE) {
|
||||
return (new VerilatedVpioModule{modp})->castVpiHandle();
|
||||
} else if (itype == VerilatedScope::SCOPE_INTERFACE) {
|
||||
return (new VerilatedVpioInterface{modp})->castVpiHandle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -821,90 +803,6 @@ public:
|
||||
uint32_t type() const override { return vpiPackage; }
|
||||
};
|
||||
|
||||
class VerilatedVpioModport final : public VerilatedVpio {
|
||||
const VerilatedScope* const m_scopep; // Interface the modport is within
|
||||
const char* const m_name; // Modport name
|
||||
const std::string m_fullname; // Interface full name + "." + modport name
|
||||
|
||||
public:
|
||||
VerilatedVpioModport(const VerilatedScope* scopep, const char* namep)
|
||||
: m_scopep{scopep}
|
||||
, m_name{namep}
|
||||
, m_fullname{std::string{VerilatedVpioScope::vpiFullnamep(scopep->name())} + "." + namep} {
|
||||
}
|
||||
~VerilatedVpioModport() override = default;
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
static VerilatedVpioModport* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioModport*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
uint32_t type() const override { return vpiModport; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
const char* name() const override { return m_name; }
|
||||
const char* fullname() const override { return m_fullname.c_str(); }
|
||||
// IEEE 1800-2023 37.15
|
||||
const char* defname() const override { return m_name; }
|
||||
};
|
||||
|
||||
class VerilatedVpioIfaceRef final : public VerilatedVpio {
|
||||
// Held by value, as a handle may outlive the model that registered it
|
||||
const VerilatedIfaceRef m_ifaceRef;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioIfaceRef(const VerilatedIfaceRef& ifaceRef)
|
||||
: m_ifaceRef{ifaceRef} {}
|
||||
~VerilatedVpioIfaceRef() override = default;
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
static VerilatedVpioIfaceRef* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioIfaceRef*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
uint32_t type() const override { return vpiRefObj; }
|
||||
const VerilatedIfaceRef* ifaceRefp() const { return &m_ifaceRef; }
|
||||
const char* name() const override { return m_ifaceRef.name(); }
|
||||
const char* fullname() const override {
|
||||
return VerilatedVpioScope::vpiFullnamep(m_ifaceRef.fullname());
|
||||
}
|
||||
// IEEE 1800-2023 37.15: modport name, else the interface definition name
|
||||
const char* defname() const override {
|
||||
return m_ifaceRef.hasModport() ? m_ifaceRef.modport() : m_ifaceRef.scopep()->defname();
|
||||
}
|
||||
vpiHandle actual() const {
|
||||
if (m_ifaceRef.hasModport()) {
|
||||
return (new VerilatedVpioModport{m_ifaceRef.scopep(), m_ifaceRef.modport()})
|
||||
->castVpiHandle();
|
||||
}
|
||||
return (new VerilatedVpioInterface{m_ifaceRef.scopep()})->castVpiHandle();
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioInterfaceIter final : public VerilatedVpio {
|
||||
const std::vector<const VerilatedScope*>* m_vec;
|
||||
std::vector<const VerilatedScope*>::const_iterator m_it;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioInterfaceIter(const std::vector<const VerilatedScope*>& vec)
|
||||
: m_vec{&vec} {
|
||||
m_it = m_vec->begin();
|
||||
}
|
||||
~VerilatedVpioInterfaceIter() override = default;
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
static VerilatedVpioInterfaceIter* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioInterfaceIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
uint32_t type() const override { return vpiIterator; }
|
||||
vpiHandle dovpi_scan() override {
|
||||
while (true) {
|
||||
if (m_it == m_vec->end()) {
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
}
|
||||
const VerilatedScope* const scopep = *m_it++;
|
||||
if (scopep->type() == VerilatedScope::SCOPE_INTERFACE) {
|
||||
return (new VerilatedVpioInterface{scopep})->castVpiHandle();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class VerilatedVpioInstanceIter final : public VerilatedVpio {
|
||||
const std::vector<const VerilatedScope*>* m_vec;
|
||||
std::vector<const VerilatedScope*>::const_iterator m_it;
|
||||
@@ -1097,13 +995,22 @@ public:
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: // FALLTHRU
|
||||
case vpiStringVal: {
|
||||
if (VL_UNLIKELY(!valuep->value.str)) return false;
|
||||
break;
|
||||
}
|
||||
case vpiScalarVal: // FALLTHRU
|
||||
case vpiIntVal: // FALLTHRU
|
||||
case vpiRealVal: // FALLTHRU
|
||||
case vpiVectorVal: return true;
|
||||
default: return false;
|
||||
case vpiRealVal: break;
|
||||
case vpiVectorVal: {
|
||||
if (VL_UNLIKELY(!valuep->value.vector)) return false;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1285,8 +1192,8 @@ public:
|
||||
template <typename T>
|
||||
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p size=%d\n",
|
||||
varop->fullname(), *(static_cast<CData*>(varop->readDatap())),
|
||||
*(varop->prevDatap()), varop->readDatap(), varop->prevDatap(),
|
||||
varop->fullname(), *(static_cast<CData*>(varop->varDatap())),
|
||||
*(varop->prevDatap()), varop->varDatap(), varop->prevDatap(),
|
||||
varop->entSize()););
|
||||
if (varop->bitSize() == 1) {
|
||||
T* const prevDatap = reinterpret_cast<T*>(
|
||||
@@ -1297,7 +1204,7 @@ public:
|
||||
prevInfo.m_datap = prevDatap;
|
||||
return vl_vpi_get_word_gen(currInfo) != vl_vpi_get_word_gen(prevInfo);
|
||||
}
|
||||
return std::memcmp(varop->prevDatap(), varop->readDatap(), varop->entSize()) != 0;
|
||||
return std::memcmp(varop->prevDatap(), varop->varDatap(), varop->entSize()) != 0;
|
||||
}
|
||||
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) {
|
||||
switch (varop->varp()->vltype()) {
|
||||
@@ -1326,9 +1233,9 @@ public:
|
||||
prevInfo.m_datap = prevDatap;
|
||||
const T currWord = vl_vpi_get_word_gen(currInfo);
|
||||
vl_vpi_put_word_gen(prevInfo, currWord);
|
||||
assert(std::memcmp(varop->prevDatap(), varop->readDatap(), varop->entSize()) == 0);
|
||||
assert(std::memcmp(varop->prevDatap(), varop->varDatap(), varop->entSize()) == 0);
|
||||
} else {
|
||||
std::memcpy(varop->prevDatap(), varop->readDatap(), varop->entSize());
|
||||
std::memcpy(varop->prevDatap(), varop->varDatap(), varop->entSize());
|
||||
}
|
||||
}
|
||||
static void updatePrev(const VerilatedVpioVar* const varop) {
|
||||
@@ -1369,7 +1276,7 @@ public:
|
||||
if (valueDiffersFromPrev(varop)) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
|
||||
ho.id(), varop->fullname(),
|
||||
*(static_cast<CData*>(varop->readDatap()))););
|
||||
*(static_cast<CData*>(varop->varDatap()))););
|
||||
update.insert(varop);
|
||||
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
||||
(ho.cb_rtnp())(ho.cb_datap());
|
||||
@@ -1720,9 +1627,9 @@ VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseSignal
|
||||
// assert(forceEnableSignalVop->entSize() == baseSignalVop->entSize());
|
||||
assert(forceValueSignalVop->entSize() == baseSignalVop->entSize());
|
||||
assert(forceReadSignalVop->entSize() == baseSignalVop->entSize());
|
||||
assert(forceEnableSignalVop->readDatap() == forceEnableSignalVarp->datap());
|
||||
assert(forceValueSignalVop->readDatap() == forceValueSignalVarp->datap());
|
||||
assert(forceReadSignalVop->readDatap() == forceReadSignalVarp->datap());
|
||||
assert(forceEnableSignalVop->varDatap() == forceEnableSignalVarp->datap());
|
||||
assert(forceValueSignalVop->varDatap() == forceValueSignalVarp->datap());
|
||||
assert(forceReadSignalVop->varDatap() == forceReadSignalVarp->datap());
|
||||
#endif // VL_DEBUG
|
||||
|
||||
return VerilatedVpiImp::ForceControlSignalVops{
|
||||
@@ -1752,9 +1659,9 @@ double VerilatedVpiImp::getReadDataWord(const VerilatedVpioVar* baseSignalVop,
|
||||
const VerilatedVpioVar* forceEnableSignalVop,
|
||||
const VerilatedVpioVar* forceValueSignalVop,
|
||||
size_t /*bitCount*/, size_t /*bitOffset*/) {
|
||||
const double baseSignalData = *baseSignalVop->readRealDatap();
|
||||
const bool forceEnableData = *forceEnableSignalVop->readCDatap();
|
||||
const double forceValueData = *forceValueSignalVop->readRealDatap();
|
||||
const double baseSignalData = *baseSignalVop->varRealDatap();
|
||||
const bool forceEnableData = *forceEnableSignalVop->varCDatap();
|
||||
const double forceValueData = *forceValueSignalVop->varRealDatap();
|
||||
const double readData = forceEnableData ? forceValueData : baseSignalData;
|
||||
return readData;
|
||||
}
|
||||
@@ -2092,9 +1999,6 @@ const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE {
|
||||
"vpiStmt"
|
||||
};
|
||||
// clang-format on
|
||||
// SystemVerilog relations are numbered far above the Verilog ones
|
||||
if (vpiVal == vpiActual) return "vpiActual";
|
||||
if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl) return strFromVpiObjType(vpiVal);
|
||||
if (vpiVal > vpiStmt || vpiVal < vpiCondition) return "*undefined*";
|
||||
return names[vpiVal - vpiCondition];
|
||||
}
|
||||
@@ -2498,8 +2402,6 @@ void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
|
||||
|
||||
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiCondition);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiStmt);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiActual);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiInterface);
|
||||
|
||||
SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbValueChange);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbAtEndOfSimTime);
|
||||
@@ -2799,7 +2701,7 @@ _vl_vpi_handle_indexed_member_from_scope(const VerilatedScope* const scopep,
|
||||
VerilatedVpioVar* baseVop
|
||||
= fullnameOverride.empty()
|
||||
? new VerilatedVpioVar{baseVarp, varScopep}
|
||||
: new VerilatedVpioVar{baseVarp, varScopep,
|
||||
: new VerilatedVpioVar{baseVarp, varScopep, baseVarp->datap(),
|
||||
_vl_vpi_member_local_name(baseVarp->name()),
|
||||
fullnameOverride};
|
||||
VerilatedVpioVar* vop = _vl_vpi_handle_apply_indices(baseVop, indices);
|
||||
@@ -2869,10 +2771,6 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
|
||||
const VerilatedVpioVar* const voVarp = VerilatedVpioVar::castp(scope);
|
||||
|
||||
// Not scopes, so no name resolves relative to them; must not fall through to
|
||||
// the unprefixed lookup below, which would resolve from the top level
|
||||
if (VerilatedVpioIfaceRef::castp(scope) || VerilatedVpioModport::castp(scope)) return nullptr;
|
||||
|
||||
if (0 == std::strncmp(scopeAndName.c_str(), "$root.", std::strlen("$root."))) {
|
||||
scopeAndName.erase(0, std::strlen("$root."));
|
||||
} else if (voScopep) {
|
||||
@@ -2900,15 +2798,8 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
if (scopep->type() == VerilatedScope::SCOPE_PACKAGE) {
|
||||
return (new VerilatedVpioPackage{scopep})->castVpiHandle();
|
||||
}
|
||||
if (scopep->type() == VerilatedScope::SCOPE_INTERFACE) {
|
||||
return (new VerilatedVpioInterface{scopep})->castVpiHandle();
|
||||
}
|
||||
return (new VerilatedVpioScope{scopep})->castVpiHandle();
|
||||
}
|
||||
if (const VerilatedIfaceRef* const ifaceRefp
|
||||
= Verilated::threadContextp()->ifaceRefFind(scopeAndName.c_str())) {
|
||||
return (new VerilatedVpioIfaceRef{*ifaceRefp})->castVpiHandle();
|
||||
}
|
||||
std::string basename = scopeAndName;
|
||||
std::string scopename;
|
||||
std::string::size_type prevpos = std::string::npos;
|
||||
@@ -2971,9 +2862,10 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
if (varp->isParam()) {
|
||||
resultHandle = (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
|
||||
} else if (!fullnameOverride.empty()) {
|
||||
resultHandle = (new VerilatedVpioVar{varp, scopep, _vl_vpi_member_local_name(varp->name()),
|
||||
fullnameOverride})
|
||||
->castVpiHandle();
|
||||
resultHandle
|
||||
= (new VerilatedVpioVar{varp, scopep, varp->datap(),
|
||||
_vl_vpi_member_local_name(varp->name()), fullnameOverride})
|
||||
->castVpiHandle();
|
||||
} else {
|
||||
resultHandle = (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
|
||||
}
|
||||
@@ -3067,24 +2959,6 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
const int32_t val = vop->index().back();
|
||||
return (new VerilatedVpioConst{val})->castVpiHandle();
|
||||
}
|
||||
case vpiActual: {
|
||||
if (const VerilatedVpioIfaceRef* const vop = VerilatedVpioIfaceRef::castp(object)) {
|
||||
return vop->actual();
|
||||
}
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
|
||||
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
|
||||
return nullptr;
|
||||
}
|
||||
case vpiInterface: {
|
||||
if (const VerilatedVpioModport* const vop = VerilatedVpioModport::castp(object)) {
|
||||
return (new VerilatedVpioInterface{vop->scopep()})->castVpiHandle();
|
||||
}
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
|
||||
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
|
||||
return nullptr;
|
||||
}
|
||||
case vpiScope: {
|
||||
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
@@ -3151,15 +3025,6 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
if (it == map->end()) return nullptr;
|
||||
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
|
||||
}
|
||||
case vpiInterface: {
|
||||
// IEEE 1800-2023 37.5: interfaces are a one-to-many of a module
|
||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
||||
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
|
||||
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
|
||||
if (it == map->end()) return nullptr;
|
||||
return ((new VerilatedVpioInterfaceIter{it->second})->castVpiHandle());
|
||||
}
|
||||
case vpiInternalScope: {
|
||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
||||
@@ -3370,18 +3235,6 @@ bool vl_check_format(const VerilatedVpioVarBase* vop, const p_vpi_value valuep,
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char* vl_put_value_null_member(const p_vpi_value valuep) {
|
||||
switch (valuep->format) {
|
||||
case vpiBinStrVal: // FALLTHRU
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: return valuep->value.str ? nullptr : "str";
|
||||
case vpiVectorVal: return valuep->value.vector ? nullptr : "vector";
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a VPI format that can be used to fully represent a signal of the given type
|
||||
PLI_INT32 vl_get_vltype_format(VerilatedVarType vlType) {
|
||||
switch (vlType) {
|
||||
@@ -3441,7 +3294,7 @@ VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t
|
||||
bitCount, varBits - addOffset});
|
||||
|
||||
VarAccessInfo<T> info;
|
||||
info.m_datap = static_cast<T*>(vop->readDatap());
|
||||
info.m_datap = reinterpret_cast<T*>(vop->varDatap());
|
||||
if (vop->varp()->vltype() == VLVT_WDATA) {
|
||||
assert(sizeof(T) == sizeof(EData));
|
||||
assert(bitCount <= wordBits);
|
||||
@@ -3489,7 +3342,8 @@ T vl_vpi_get_word_gen(VarAccessInfo<T> info) {
|
||||
|
||||
template <typename T>
|
||||
T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
|
||||
return vl_vpi_get_word_gen(vl_vpi_var_access_info<T>(vop, bitCount, addOffset));
|
||||
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
||||
return vl_vpi_get_word_gen(info);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -3507,7 +3361,8 @@ void vl_vpi_put_word_gen(VarAccessInfo<T> info, T word) {
|
||||
|
||||
template <typename T>
|
||||
void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) {
|
||||
vl_vpi_put_word_gen(vl_vpi_var_access_info<T>(vop, bitCount, addOffset), word);
|
||||
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
||||
vl_vpi_put_word_gen(info, word);
|
||||
}
|
||||
|
||||
// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds
|
||||
@@ -3544,7 +3399,7 @@ void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, s
|
||||
|
||||
void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
|
||||
const VerilatedVar* const varp = vop->varp();
|
||||
void* const varDatap = vop->readDatap();
|
||||
void* const varDatap = vop->varDatap();
|
||||
|
||||
if (!vl_check_format(vop, valuep, true)) return;
|
||||
// string data type is dynamic and may vary in size during simulation
|
||||
@@ -3585,7 +3440,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
|
||||
return;
|
||||
} else if (valuep->format == vpiBinStrVal) {
|
||||
t_outDynamicStr.resize(varBits);
|
||||
const CData* datap = static_cast<CData*>(varDatap);
|
||||
const CData* datap = reinterpret_cast<CData*>(varDatap);
|
||||
for (size_t i = 0; i < varBits; ++i) {
|
||||
const size_t pos = i + vop->bitOffset();
|
||||
const char val = (datap[pos >> 3] >> (pos & 7)) & 1;
|
||||
@@ -3630,10 +3485,10 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
|
||||
} else if (valuep->format == vpiStringVal) {
|
||||
if (varp->vltype() == VLVT_STRING) {
|
||||
if (varp->isParam()) {
|
||||
valuep->value.str = static_cast<char*>(varDatap);
|
||||
valuep->value.str = reinterpret_cast<char*>(varDatap);
|
||||
return;
|
||||
}
|
||||
t_outDynamicStr = *vop->readStringDatap();
|
||||
t_outDynamicStr = *vop->varStringDatap();
|
||||
valuep->value.str = const_cast<char*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
} else {
|
||||
@@ -3651,7 +3506,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
|
||||
valuep->value.integer = vl_vpi_get_word(vop, 32, 0);
|
||||
return;
|
||||
} else if (valuep->format == vpiRealVal) {
|
||||
valuep->value.real = *(vop->readRealDatap());
|
||||
valuep->value.real = *(vop->varRealDatap());
|
||||
return;
|
||||
} else if (valuep->format == vpiScalarVal) {
|
||||
valuep->value.scalar = vl_vpi_get_word(vop, 32, 0) ? vpi1 : vpi0;
|
||||
@@ -3742,7 +3597,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
baseSignalVop->fullname(), valuep->format,
|
||||
valuep->value.integer);
|
||||
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n",
|
||||
baseSignalVop->varp()->datap(), baseSignalVop->readDatap()););
|
||||
baseSignalVop->varp()->datap(), baseSignalVop->varDatap()););
|
||||
|
||||
if (VL_UNLIKELY(!baseSignalVop->varp()->isPublicRW())) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__,
|
||||
@@ -3761,16 +3616,6 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
return nullptr;
|
||||
}
|
||||
if (!vl_check_format(baseSignalVop, valuep, false)) return nullptr;
|
||||
// On release valuep is only written
|
||||
if (forceFlag != vpiReleaseFlag) {
|
||||
if (const char* const memberp = vl_put_value_null_member(valuep)) {
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Ignoring nullptr value.%s with format %s for '%s'", __func__,
|
||||
memberp, VerilatedVpiError::strFromVpiVal(valuep->format),
|
||||
baseSignalVop->fullname());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (delay_mode == vpiInertialDelay) {
|
||||
if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
|
||||
VL_VPI_WARNING_(
|
||||
@@ -3830,7 +3675,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
if (baseSignalVop->varp()->vltype() == VLVT_REAL) {
|
||||
const double readData = VerilatedVpiImp::getReadDataWord<double>(
|
||||
baseSignalVop, forceEnableSignalVop, forceValueSignalVop, 64, 0);
|
||||
*forceReadSignalVop->readRealDatap() = readData;
|
||||
*forceReadSignalVop->varRealDatap() = readData;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3935,6 +3780,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
}
|
||||
|
||||
if (valuep->format == vpiVectorVal) {
|
||||
if (VL_UNLIKELY(!valuep->value.vector)) return nullptr;
|
||||
if (valueVop->varp()->vltype() == VLVT_WDATA) {
|
||||
const int words = VL_WORDS_I(varBits);
|
||||
for (int i = 0; i < words; ++i)
|
||||
@@ -3951,7 +3797,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
}
|
||||
} else if (valuep->format == vpiBinStrVal) {
|
||||
const int len = std::strlen(valuep->value.str);
|
||||
CData* const datap = static_cast<CData*>(valueVop->readDatap());
|
||||
CData* const datap = reinterpret_cast<CData*>(valueVop->varDatap());
|
||||
for (int i = 0; i < varBits; ++i) {
|
||||
const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1');
|
||||
const size_t pos = valueVop->bitOffset() + i;
|
||||
@@ -4033,7 +3879,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
} else if (valuep->format == vpiStringVal) {
|
||||
if (valueVop->varp()->vltype() == VLVT_STRING) {
|
||||
// Does not use valueVop, because strings are not forceable anyway
|
||||
*(baseSignalVop->readStringDatap()) = valuep->value.str;
|
||||
*(baseSignalVop->varStringDatap()) = valuep->value.str;
|
||||
return object;
|
||||
}
|
||||
const int chars = VL_BYTES_I(varBits);
|
||||
@@ -4050,7 +3896,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
return object;
|
||||
} else if (valuep->format == vpiRealVal) {
|
||||
if (valueVop->varp()->vltype() == VLVT_REAL) {
|
||||
*(valueVop->readRealDatap()) = valuep->value.real;
|
||||
*(valueVop->varRealDatap()) = valuep->value.real;
|
||||
if (baseSignalVop->varp()->isForceable()) updateVforceRd();
|
||||
return object;
|
||||
}
|
||||
@@ -4095,7 +3941,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
|
||||
case VLVT_UINT8:
|
||||
case VLVT_UINT16:
|
||||
case VLVT_UINT32: return true;
|
||||
default:;
|
||||
default:; // LCOV_EXCL_LINE
|
||||
}
|
||||
break;
|
||||
case vpiRawTwoStateVal:
|
||||
@@ -4113,7 +3959,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
|
||||
switch (varp->vltype()) {
|
||||
case VLVT_UINT8:
|
||||
case VLVT_UINT16: return true;
|
||||
default:;
|
||||
default:; // LCOV_EXCL_LINE
|
||||
}
|
||||
break;
|
||||
case vpiLongIntVal:
|
||||
@@ -4122,7 +3968,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
|
||||
case VLVT_UINT16:
|
||||
case VLVT_UINT32:
|
||||
case VLVT_UINT64: return true;
|
||||
default:;
|
||||
default:; // LCOV_EXCL_LINE
|
||||
}
|
||||
break;
|
||||
default:;
|
||||
@@ -4321,10 +4167,10 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readCDatap(), shortintsp);
|
||||
vop->varCDatap(), shortintsp);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readSDatap(), shortintsp);
|
||||
vop->varSDatap(), shortintsp);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4336,13 +4182,13 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readCDatap(), integersp);
|
||||
vop->varCDatap(), integersp);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readSDatap(), integersp);
|
||||
vop->varSDatap(), integersp);
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readIDatap(), integersp);
|
||||
vop->varIDatap(), integersp);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4354,16 +4200,16 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readCDatap(), longintsp);
|
||||
vop->varCDatap(), longintsp);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readSDatap(), longintsp);
|
||||
vop->varSDatap(), longintsp);
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readIDatap(), longintsp);
|
||||
vop->varIDatap(), longintsp);
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readQDatap(), longintsp);
|
||||
vop->varQDatap(), longintsp);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4375,19 +4221,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readCDatap(), vectorsp);
|
||||
vop->varCDatap(), vectorsp);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readSDatap(), vectorsp);
|
||||
vop->varSDatap(), vectorsp);
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readIDatap(), vectorsp);
|
||||
vop->varIDatap(), vectorsp);
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readQDatap(), vectorsp);
|
||||
vop->varQDatap(), vectorsp);
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->readEDatap(), vectorsp);
|
||||
vop->varEDatap(), vectorsp);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4399,19 +4245,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vop->readCDatap(), valuep);
|
||||
vop->varCDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vop->readSDatap(), valuep);
|
||||
vop->varSDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vop->readIDatap(), valuep);
|
||||
vop->varIDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vop->readQDatap(), valuep);
|
||||
vop->varQDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vop->readEDatap(), valuep);
|
||||
vop->varEDatap(), valuep);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4423,19 +4269,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
||||
vop->readCDatap(), valuep);
|
||||
vop->varCDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
||||
vop->readSDatap(), valuep);
|
||||
vop->varSDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
||||
vop->readIDatap(), valuep);
|
||||
vop->varIDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
||||
vop->readQDatap(), valuep);
|
||||
vop->varQDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
||||
vop->readEDatap(), valuep);
|
||||
vop->varEDatap(), valuep);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4498,8 +4344,17 @@ void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
|
||||
void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const PLI_INT32* index_p,
|
||||
PLI_UINT32 num) {
|
||||
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
|
||||
|
||||
const VerilatedVar* const varp = vop->varp();
|
||||
|
||||
const int size = vop->size();
|
||||
if (VL_UNCOVERABLE(num > size)) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__,
|
||||
"%s: Requested elements to set (%u) exceed array size (%u)", __func__, num,
|
||||
size);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low();
|
||||
const int index
|
||||
@@ -4511,129 +4366,114 @@ void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
|
||||
vop->readCDatap());
|
||||
return;
|
||||
vop->varCDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
|
||||
vop->readSDatap());
|
||||
return;
|
||||
vop->varSDatap());
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (arrayvalue_p->format == vpiIntVal) {
|
||||
const PLI_UINT32* integersp = reinterpret_cast<PLI_UINT32*>(arrayvalue_p->value.integers);
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
|
||||
vop->readCDatap());
|
||||
return;
|
||||
vop->varCDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
|
||||
vop->readSDatap());
|
||||
return;
|
||||
vop->varSDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
|
||||
vop->readIDatap());
|
||||
return;
|
||||
vop->varIDatap());
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (arrayvalue_p->format == vpiLongIntVal) {
|
||||
const PLI_UINT64* longintsp = reinterpret_cast<PLI_UINT64*>(arrayvalue_p->value.longints);
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
|
||||
vop->readCDatap());
|
||||
return;
|
||||
vop->varCDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
|
||||
vop->readSDatap());
|
||||
return;
|
||||
vop->varSDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
|
||||
vop->readIDatap());
|
||||
return;
|
||||
vop->varIDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
|
||||
vop->readQDatap());
|
||||
return;
|
||||
vop->varQDatap());
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (arrayvalue_p->format == vpiVectorVal) {
|
||||
const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors;
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vectorsp, vop->readCDatap());
|
||||
return;
|
||||
vectorsp, vop->varCDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vectorsp, vop->readSDatap());
|
||||
return;
|
||||
vectorsp, vop->varSDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vectorsp, vop->readIDatap());
|
||||
return;
|
||||
vectorsp, vop->varIDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vectorsp, vop->readQDatap());
|
||||
return;
|
||||
vectorsp, vop->varQDatap());
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vectorsp, vop->readEDatap());
|
||||
return;
|
||||
vectorsp, vop->varEDatap());
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (arrayvalue_p->format == vpiRawFourStateVal) {
|
||||
const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals);
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
||||
vop->readCDatap());
|
||||
return;
|
||||
vop->varCDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
||||
vop->readSDatap());
|
||||
return;
|
||||
vop->varSDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
||||
vop->readIDatap());
|
||||
return;
|
||||
vop->varIDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
||||
vop->readQDatap());
|
||||
return;
|
||||
vop->varQDatap());
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
||||
vop->readEDatap());
|
||||
return;
|
||||
vop->varEDatap());
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (arrayvalue_p->format == vpiRawTwoStateVal) {
|
||||
const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals);
|
||||
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
||||
vop->readCDatap());
|
||||
return;
|
||||
vop->varCDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
||||
vop->readSDatap());
|
||||
return;
|
||||
vop->varSDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
||||
vop->readIDatap());
|
||||
return;
|
||||
vop->varIDatap());
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
||||
vop->readQDatap());
|
||||
return;
|
||||
vop->varQDatap());
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
||||
vop->readEDatap());
|
||||
return;
|
||||
vop->varEDatap());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Reached only if vl_check_array_format and this dispatch drift apart
|
||||
// LCOV_EXCL_START
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for '%s'",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(arrayvalue_p->format),
|
||||
vop->fullname());
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p,
|
||||
@@ -4688,16 +4528,6 @@ void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
|
||||
|
||||
const unsigned size = vop->size();
|
||||
if (VL_UNLIKELY(num > size)) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__,
|
||||
"%s: Requested elements to set (%u) exceed array size (%u)", __func__, num,
|
||||
size);
|
||||
return;
|
||||
}
|
||||
if (num == 0) return;
|
||||
vl_put_value_array(object, arrayvalue_p, index_p, num);
|
||||
}
|
||||
|
||||
|
||||
+2
-16
@@ -318,17 +318,6 @@
|
||||
# define VL_CONSTEXPR_CXX17
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
// C++-2020
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
# define VL_CONSTINIT_CXX20 constinit
|
||||
# define VL_NO_UNIQUE_ADDRESS_CXX20 [[no_unique_address]]
|
||||
#else
|
||||
# define VL_CONSTINIT_CXX20
|
||||
# define VL_NO_UNIQUE_ADDRESS_CXX20
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
// Optimization
|
||||
|
||||
@@ -463,8 +452,7 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
#define VL_VFORMATATTR_SIGNED '~' // (int widthMin, IData/VlWide/etc) Signed number; for %d showing sign
|
||||
#define VL_VFORMATATTR_COMPLEX '!' // (std::string*); for non-POD; e.g. struct, requires %p typically
|
||||
#define VL_VFORMATATTR_DOUBLE 'D' // (double); promote %p to %f
|
||||
#define VL_VFORMATATTR_ENUM 'E' // (width, IData/QData/const EData*, STRING, std::string* name)
|
||||
#define VL_VFORMATATTR_ENUM_SIGNED 'F' // Same arguments as ENUM, with a signed numeric value
|
||||
#define VL_VFORMATATTR_ENUM 'E' // (width, IData/QData, std::string* name); <= 64 bit enum with runtime %p/%s
|
||||
#define VL_VFORMATATTR_SCOPE 'M' // (char* name, char* scope); for scopes
|
||||
#define VL_VFORMATATTR_STRING 'S' // (char* name, char* scope); for scopes // (std::string*); for %p/%s
|
||||
#define VL_VFORMATATTR_TIMEUNIT 'T' // (int timeunit); timeunits passed from V3Emit to runtime
|
||||
@@ -543,12 +531,10 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
#define VL_EUL(n) VL_UL(n) // Make constant number EData sized
|
||||
|
||||
#define VL_BITWORD_I(bit) ((bit) / VL_IDATASIZE) ///< Word number for sv DPI vectors
|
||||
#define VL_BITWORD_Q(bit) ((bit) / VL_QUADSIZE) ///< Quadword number for a bit
|
||||
#define VL_BITWORD_E(bit) ((bit) >> VL_EDATASIZE_LOG2) ///< Word number for a wide quantity
|
||||
#define VL_BITBIT_I(bit) ((bit) & VL_SIZEBITS_I) ///< Bit number for a bit in a long
|
||||
#define VL_BITBIT_Q(bit) ((bit) & VL_SIZEBITS_Q) ///< Bit number for a bit in a quad
|
||||
#define VL_BITBIT_E(bit) ((bit) & VL_SIZEBITS_E) ///< Bit number for a bit in an EData
|
||||
#define VL_BIT_Q(bit) (1ULL << VL_BITBIT_Q(bit)) ///< Quad with only the given bit set
|
||||
|
||||
// Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
|
||||
#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit)))
|
||||
@@ -750,7 +736,7 @@ namespace vlstd {
|
||||
|
||||
template <typename T>
|
||||
struct reverse_wrapper final {
|
||||
const T& m_v; // Pre-wrapped iterator
|
||||
const T& m_v;
|
||||
|
||||
explicit reverse_wrapper(const T& a_v)
|
||||
: m_v(a_v) {} // Need () constructor
|
||||
|
||||
+1
-3
@@ -25,10 +25,8 @@ def message_section(msg: str) -> int:
|
||||
return 50
|
||||
if re.match(r'^Support', msg, flags=re.IGNORECASE):
|
||||
return 60
|
||||
if re.match(r'^Optimize', msg, flags=re.IGNORECASE):
|
||||
return 70
|
||||
if re.match(r'^Fix', msg, flags=re.IGNORECASE):
|
||||
return 80
|
||||
return 70
|
||||
if re.match(r'^(Internals|CI|Tests)', msg, flags=re.IGNORECASE):
|
||||
return -1
|
||||
if re.match(r'^Bump.* from .* to .*', msg, flags=re.IGNORECASE): # dependabot
|
||||
|
||||
@@ -12,23 +12,20 @@
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
# These flags tested against verible-v0.0-4192-g682a1d50
|
||||
# These flags tested against verible-v0.0-4080-ga0a8d8eb
|
||||
|
||||
verible-verilog-format \
|
||||
--inplace \
|
||||
\
|
||||
--wrap_end_else_clauses \
|
||||
--assignment_statement_alignment=flush-left \
|
||||
--case_items_alignment=flush-left \
|
||||
--class_member_variable_alignment=flush-left \
|
||||
--distribution_items_alignment=flush-left \
|
||||
--enum_assignment_statement_alignment=flush-left \
|
||||
--expand_coverpoints \
|
||||
--formal_parameters_alignment=flush-left \
|
||||
--module_net_variable_alignment=flush-left \
|
||||
--named_parameter_alignment=flush-left \
|
||||
--named_port_alignment=flush-left \
|
||||
--parameter_declaration_alignment=flush-left \
|
||||
--port_declarations_alignment=flush-left \
|
||||
--struct_union_members_alignment=flush-left \
|
||||
--wrap_end_else_clauses \
|
||||
$*
|
||||
|
||||
+1
-15
@@ -68,7 +68,6 @@ set(HEADERS
|
||||
V3Combine.h
|
||||
V3Common.h
|
||||
V3Const.h
|
||||
V3ConstPool.h
|
||||
V3Container.h
|
||||
V3Control.h
|
||||
V3Coverage.h
|
||||
@@ -115,7 +114,6 @@ set(HEADERS
|
||||
V3GraphPathChecker.h
|
||||
V3GraphStream.h
|
||||
V3Hash.h
|
||||
V3HashTable.h
|
||||
V3Hasher.h
|
||||
V3HierBlock.h
|
||||
V3Inline.h
|
||||
@@ -238,7 +236,6 @@ set(COMMON_SOURCES
|
||||
V3Clock.cpp
|
||||
V3Combine.cpp
|
||||
V3Common.cpp
|
||||
V3ConstPool.cpp
|
||||
V3Control.cpp
|
||||
V3Const__gen.cpp
|
||||
V3Coverage.cpp
|
||||
@@ -295,7 +292,6 @@ set(COMMON_SOURCES
|
||||
V3GraphPathChecker.cpp
|
||||
V3GraphTest.cpp
|
||||
V3Hash.cpp
|
||||
V3HashTable.cpp
|
||||
V3Hasher.cpp
|
||||
V3HierBlock.cpp
|
||||
V3Inline.cpp
|
||||
@@ -349,7 +345,6 @@ set(COMMON_SOURCES
|
||||
V3Sampled.cpp
|
||||
V3Sched.cpp
|
||||
V3SchedAcyclic.cpp
|
||||
V3SchedCovergroup.cpp
|
||||
V3SchedPartition.cpp
|
||||
V3SchedReplicate.cpp
|
||||
V3SchedTiming.cpp
|
||||
@@ -567,18 +562,9 @@ set_target_properties(
|
||||
MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL>
|
||||
#JOB_POOL_LINK one_job # Linking takes lots of resources
|
||||
INTERPROCEDURAL_OPTIMIZATION_RELEASE $<IF:MINGW,FALSE,TRUE>
|
||||
INCLUDE_DIRECTORIES ${FLEX_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
if(MINGW)
|
||||
# Search MSYS Flex headers after the active toolchain's system headers.
|
||||
target_compile_options(
|
||||
${verilator}
|
||||
PRIVATE -idirafter "${FLEX_INCLUDE_DIR}"
|
||||
)
|
||||
else()
|
||||
target_include_directories(${verilator} PRIVATE "${FLEX_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
add_dependencies(
|
||||
${verilator}
|
||||
V3Lexer_yy_cpp${CMAKE_BUILD_TYPE}
|
||||
|
||||
+19
-4
@@ -35,10 +35,25 @@ endif
|
||||
|
||||
UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
|
||||
|
||||
# Use bear if installed, and new enough to support the '--output FILE --' syntax
|
||||
BEAR := $(shell bear=$$(which bear 2>/dev/null) && tmp=$$(mktemp -d 2>/dev/null) \
|
||||
&& { "$$bear" --output "$$tmp/probe.json" -- true >/dev/null 2>&1 && echo "$$bear"; rm -rf "$$tmp"; })
|
||||
BEAR_OBJ_OPT := $(if $(BEAR),$(BEAR) --append --output obj_dbg/compile_commands.json --)
|
||||
ifeq (,$(wildcard obj_dbg/bear.o))
|
||||
ifneq (, $(shell which bear 2>/dev/null))
|
||||
BEAR := $(shell which bear)
|
||||
ifeq (, $(shell $(BEAR) --output obj_dbg/comptest.json -- true))
|
||||
$(shell which bear 2>/dev/null >obj_dbg/bear.o)
|
||||
else
|
||||
# unsupported version
|
||||
BEAR :=
|
||||
endif
|
||||
endif
|
||||
else
|
||||
BEAR := $(shell cat obj_dbg/bear.o)
|
||||
endif
|
||||
|
||||
ifneq ($(BEAR),)
|
||||
BEAR_OBJ_OPT := $(BEAR) --append --output obj_dbg/compile_commands.json --
|
||||
else
|
||||
BEAR_OBJ_OPT :=
|
||||
endif
|
||||
|
||||
#*********************************************************************
|
||||
|
||||
|
||||
+5
-8
@@ -187,6 +187,7 @@ maintainer-copy::
|
||||
#### Top executable
|
||||
|
||||
RAW_OBJS = \
|
||||
V3Const__gen.o \
|
||||
V3Error.o \
|
||||
V3FileLine.o \
|
||||
V3Graph.o \
|
||||
@@ -195,7 +196,6 @@ RAW_OBJS = \
|
||||
V3GraphPathChecker.o \
|
||||
V3GraphTest.o \
|
||||
V3Hash.o \
|
||||
V3HashTable.o \
|
||||
V3OptionParser.o \
|
||||
V3Os.o \
|
||||
V3ParseGrammar.o \
|
||||
@@ -212,7 +212,6 @@ RAW_OBJS_PCH_ASTMT = \
|
||||
V3Ast.o \
|
||||
V3AstNodes.o \
|
||||
V3Broken.o \
|
||||
V3ConstPool.o \
|
||||
V3Control.o \
|
||||
V3EmitCBase.o \
|
||||
V3EmitCConstPool.o \
|
||||
@@ -253,7 +252,6 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3Clock.o \
|
||||
V3Combine.o \
|
||||
V3Common.o \
|
||||
V3Const__gen.o \
|
||||
V3Coverage.o \
|
||||
V3Covergroup.o \
|
||||
V3CoverageJoin.o \
|
||||
@@ -332,7 +330,6 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3Sampled.o \
|
||||
V3Sched.o \
|
||||
V3SchedAcyclic.o \
|
||||
V3SchedCovergroup.o \
|
||||
V3SchedPartition.o \
|
||||
V3SchedReplicate.o \
|
||||
V3SchedTiming.o \
|
||||
@@ -411,6 +408,8 @@ $(TGT): $(PREDEP_H) $(OBJS)
|
||||
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
|
||||
$(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) $*.cpp
|
||||
|
||||
.SECONDARY:
|
||||
|
||||
%.gch: %
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH} -c $< -o $@
|
||||
%.o: %.cpp
|
||||
@@ -430,7 +429,7 @@ V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
|
||||
|
||||
define CXX_ASTMT_template
|
||||
$(1): $(basename $(1)).cpp V3PchAstMT.h.gch
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstMT.h${CFG_GCH_IF_CLANG} -c $$< -o $(1)
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstMT.h${CFG_GCH_IF_CLANG} -c $(srcdir)/$(basename $(1)).cpp -o $(1)
|
||||
|
||||
endef
|
||||
|
||||
@@ -438,7 +437,7 @@ $(foreach obj,$(RAW_OBJS_PCH_ASTMT),$(eval $(call CXX_ASTMT_template,$(obj))))
|
||||
|
||||
define CXX_ASTNOMT_template
|
||||
$(1): $(basename $(1)).cpp V3PchAstNoMT.h.gch
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstNoMT.h${CFG_GCH_IF_CLANG} -c $$< -o $(1)
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstNoMT.h${CFG_GCH_IF_CLANG} -c $(srcdir)/$(basename $(1)).cpp -o $(1)
|
||||
|
||||
endef
|
||||
|
||||
@@ -488,8 +487,6 @@ header_cc: $(addsuffix __header_cc.o, $(basename $(HEADER_CC_H)))
|
||||
%__header_cc.cpp: %.h
|
||||
$(PYTHON3) $(srcdir)/../bin/verilator_includer -DVL_MT_DISABLED_CODE_UNIT=1 $^ > $@
|
||||
|
||||
.SECONDARY: V3Const__gen.cpp $(addsuffix __header_cc.cpp, $(basename $(HEADER_CC_H)))
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
######################################################################
|
||||
|
||||
+22
-28
@@ -154,13 +154,6 @@ void V3AssertCommon::lowerSequenceEvents(AstNetlist* nodep) {
|
||||
V3Global::dumpCheckGlobalTree("assertseqevent", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
string V3AssertCommon::assertCtlGetCall(const char* query, VAssertType type,
|
||||
VAssertDirectiveType directiveType) {
|
||||
// Template arguments let the runtime build the type/directive mask at C++ compile time
|
||||
return "vlSymsp->_vm_contextp__->assertCtlGet<"s + std::to_string(type) + ", "s
|
||||
+ std::to_string(directiveType) + ">(VerilatedAssertCtlQuery::"s + query + ")"s;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// AssertDeFutureVisitor
|
||||
// If any AstFuture, then move all non-future varrefs to be one cycle behind,
|
||||
@@ -331,7 +324,6 @@ class AssertVisitor final : public VNVisitor {
|
||||
VDouble0 m_statLiftedCaseExprs; // Count of purified case expressions
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
V3UniqueNames m_caseTempNames{"__VCase"};
|
||||
V3UniqueNames m_matchCountNames{"__VnfaRemainingMatchCount"}; // Match replay counter names
|
||||
// Maps from (expression, senTree) to the AstAlways that computes its delayed values.
|
||||
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
|
||||
m_modExpr2Sen2DelayedAlwaysp;
|
||||
@@ -342,6 +334,11 @@ class AssertVisitor final : public VNVisitor {
|
||||
std::unordered_map<const AstAlways*, AstVar*> m_delayedAlways2TickTimep;
|
||||
|
||||
// METHODS
|
||||
static string assertCtlGetCall(const char* query, VAssertType type,
|
||||
VAssertDirectiveType directiveType) {
|
||||
return "vlSymsp->_vm_contextp__->assertCtlGet(VerilatedAssertCtlQuery::"s + query + ", "s
|
||||
+ std::to_string(type) + ", "s + std::to_string(directiveType) + ")"s;
|
||||
}
|
||||
static const char* assertPassOnQuery(bool vacuous) {
|
||||
static constexpr const char* queries[2]
|
||||
= {"ASSERT_CTL_PASS_ON_NONVACUOUS", "ASSERT_CTL_PASS_ON_VACUOUS"};
|
||||
@@ -365,9 +362,8 @@ class AssertVisitor final : public VNVisitor {
|
||||
case VAssertDirectiveType::COVER:
|
||||
case VAssertDirectiveType::ASSUME: {
|
||||
if (v3Global.opt.assertOn()) {
|
||||
return new AstCExpr{
|
||||
fl, AstCExpr::Pure{},
|
||||
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
|
||||
return new AstCExpr{fl, AstCExpr::Pure{},
|
||||
assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
|
||||
}
|
||||
return new AstConst{fl, AstConst::BitFalse{}};
|
||||
}
|
||||
@@ -394,17 +390,15 @@ class AssertVisitor final : public VNVisitor {
|
||||
VAssertDirectiveType directiveType, bool vacuous) {
|
||||
if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}};
|
||||
if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}};
|
||||
return new AstCExpr{
|
||||
fl, AstCExpr::Pure{},
|
||||
V3AssertCommon::assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1};
|
||||
return new AstCExpr{fl, AstCExpr::Pure{},
|
||||
assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1};
|
||||
}
|
||||
static AstNodeExpr* assertFailOnCond(FileLine* fl, VAssertType type,
|
||||
VAssertDirectiveType directiveType) {
|
||||
if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}};
|
||||
if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}};
|
||||
return new AstCExpr{
|
||||
fl, AstCExpr::Pure{},
|
||||
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 1};
|
||||
return new AstCExpr{fl, AstCExpr::Pure{},
|
||||
assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 1};
|
||||
}
|
||||
string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message,
|
||||
VDisplayType severity) {
|
||||
@@ -439,20 +433,20 @@ class AssertVisitor final : public VNVisitor {
|
||||
if (!m_monitorNumVarp) {
|
||||
m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
|
||||
nodep->findUInt64DType()};
|
||||
v3Global.rootp()->dollarUnitPkgp()->addStmtsp(m_monitorNumVarp);
|
||||
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp);
|
||||
}
|
||||
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access};
|
||||
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgp());
|
||||
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
||||
return varrefp;
|
||||
}
|
||||
AstVarRef* newMonitorOffVarRefp(const AstNode* nodep, VAccess access) {
|
||||
if (!m_monitorOffVarp) {
|
||||
m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
|
||||
nodep->findBitDType()};
|
||||
v3Global.rootp()->dollarUnitPkgp()->addStmtsp(m_monitorOffVarp);
|
||||
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp);
|
||||
}
|
||||
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access};
|
||||
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgp());
|
||||
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
||||
return varrefp;
|
||||
}
|
||||
static AstIf* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
|
||||
@@ -755,11 +749,11 @@ class AssertVisitor final : public VNVisitor {
|
||||
// reaches zero.
|
||||
matchCountp->unlinkFrBack();
|
||||
AstVar* const remainingp = new AstVar{
|
||||
flp, VVarType::MODULETEMP, m_matchCountNames.get(""), matchCountp->dtypep()};
|
||||
remainingp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
m_modp->addStmtsp(remainingp);
|
||||
AstNode* const replaysp
|
||||
= new AstAssign{flp, new AstVarRef{flp, remainingp, VAccess::WRITE}, matchCountp};
|
||||
flp, VVarType::BLOCKTEMP, "__VnfaRemainingMatchCount", matchCountp->dtypep()};
|
||||
remainingp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
AstBegin* const replayp = new AstBegin{flp, "", remainingp, true};
|
||||
replayp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, remainingp, VAccess::WRITE}, matchCountp});
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(
|
||||
new AstLoopTest{flp, loopp, new AstVarRef{flp, remainingp, VAccess::READ}});
|
||||
@@ -769,8 +763,8 @@ class AssertVisitor final : public VNVisitor {
|
||||
new AstSub{flp, new AstVarRef{flp, remainingp, VAccess::READ},
|
||||
new AstConst{flp, AstConst::WidthedValue{},
|
||||
remainingp->dtypep()->width(), 1}}});
|
||||
replaysp->addNext(loopp);
|
||||
passsp = replaysp;
|
||||
replayp->addStmtsp(loopp);
|
||||
passsp = replayp;
|
||||
}
|
||||
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
|
||||
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
|
||||
|
||||
@@ -28,8 +28,6 @@ class V3AssertCommon final {
|
||||
public:
|
||||
static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
static void lowerSequenceEvents(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
static string assertCtlGetCall(const char* query, VAssertType type,
|
||||
VAssertDirectiveType directiveType) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
class V3Assert final {
|
||||
|
||||
+228
-438
File diff suppressed because it is too large
Load Diff
+29
-27
@@ -68,7 +68,6 @@ private:
|
||||
V3UniqueNames m_gotoRepNames{"__VgotoRep"}; // Goto repetition counter name generator
|
||||
V3UniqueNames m_nonConsRepNames{"__VnonConsRep"}; // Nonconsecutive rep name generator
|
||||
V3UniqueNames m_disableCntNames{"__VdisableCnt"}; // Disable condition counter name generator
|
||||
V3UniqueNames m_blockNames{"__VassertBlock"}; // Names of blocks with temporaries
|
||||
V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable name generator
|
||||
V3UniqueNames m_activeNames{"__VassertsActive"}; // Active asserts map name generator
|
||||
bool m_inAssign = false; // True if in an AssignNode
|
||||
@@ -415,7 +414,6 @@ private:
|
||||
flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, VCMethod::DYN_POP,
|
||||
new AstTime{nodep->fileline(), m_modp->timeunit()}};
|
||||
popp->addPinsp(skewp->unlinkFrBack());
|
||||
refp->access(VAccess::READWRITE); // Only conditionally assigned
|
||||
popp->addPinsp(refp);
|
||||
popp->dtypeSetVoid();
|
||||
m_clockingp->addNextHere(
|
||||
@@ -563,7 +561,8 @@ private:
|
||||
}
|
||||
if (m_disableSeqIfp && remainp) {
|
||||
AstIf* const disableSeqIfp = m_disableSeqIfp->cloneTree(false);
|
||||
disableSeqIfp->addThensp(remainp);
|
||||
// Keep continuation statements in a proper statement-list container.
|
||||
disableSeqIfp->addThensp(new AstBegin{flp, "", remainp, true});
|
||||
remainp = disableSeqIfp;
|
||||
}
|
||||
if (remainp) {
|
||||
@@ -660,9 +659,9 @@ private:
|
||||
iterate(nodep->lhsp());
|
||||
}
|
||||
iterate(nodep->rhsp());
|
||||
if (nodep->timingControlp()) iterate(nodep->timingControlp());
|
||||
// A drive without a cycle delay, also after removing '##0', updates the clockvar now
|
||||
if (m_inSynchDrive && !nodep->timingControlp()) {
|
||||
if (nodep->timingControlp()) {
|
||||
iterate(nodep->timingControlp());
|
||||
} else if (m_inSynchDrive) {
|
||||
AstAssign* const assignp = new AstAssign{
|
||||
nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()};
|
||||
assignp->user1(true);
|
||||
@@ -878,7 +877,7 @@ private:
|
||||
= getProcessAssocArrayDelete(new AstVarRef{flp, activep, VAccess::WRITE});
|
||||
|
||||
// Main assertion block
|
||||
AstBegin* const bodyp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
|
||||
AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true};
|
||||
bodyp->addStmtsp(incrementp);
|
||||
bodyp->addStmtsp(loopp);
|
||||
bodyp->addStmtsp(clausep);
|
||||
@@ -907,7 +906,7 @@ private:
|
||||
new AstConst{flp, 1}}});
|
||||
|
||||
// Final assertion block
|
||||
AstBegin* const finalp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
|
||||
AstBegin* const finalp = new AstBegin{flp, "", nullptr, true};
|
||||
finalp->addStmtsp(activeCountp);
|
||||
finalp->addStmtsp(initActiveCountp);
|
||||
finalp->addStmtsp(finalLoopp);
|
||||
@@ -1073,13 +1072,15 @@ private:
|
||||
windowp->addStmtsp(new AstLoopTest{
|
||||
flp, windowp, new AstNot{flp, new AstVarRef{flp, doneVarp, VAccess::READ}}});
|
||||
// if (expr) { fail; done = 1; } -- window closed, expr true again
|
||||
AstNode* const failsp = new AstPExprClause{flp, false};
|
||||
failsp->addNext(setDone());
|
||||
windowp->addStmtsp(new AstIf{flp, exprp->cloneTreePure(false), failsp});
|
||||
AstBegin* const failBlockp = new AstBegin{flp, "", nullptr, true};
|
||||
failBlockp->addStmtsp(new AstPExprClause{flp, false});
|
||||
failBlockp->addStmtsp(setDone());
|
||||
windowp->addStmtsp(new AstIf{flp, exprp->cloneTreePure(false), failBlockp});
|
||||
// if (rhs) { pass; done = 1; } -- consequent true at this !expr endpoint
|
||||
AstNode* const passsp = new AstPExprClause{flp, true};
|
||||
passsp->addNext(setDone());
|
||||
windowp->addStmtsp(new AstIf{flp, rhsp, passsp});
|
||||
AstBegin* const passBlockp = new AstBegin{flp, "", nullptr, true};
|
||||
passBlockp->addStmtsp(new AstPExprClause{flp, true});
|
||||
passBlockp->addStmtsp(setDone());
|
||||
windowp->addStmtsp(new AstIf{flp, rhsp, passBlockp});
|
||||
// @(clk) -- advance to next cycle in window
|
||||
windowp->addStmtsp(
|
||||
new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
|
||||
@@ -1293,7 +1294,7 @@ private:
|
||||
// */ }
|
||||
AstBegin* const bodyp = pexprp->bodyp();
|
||||
AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext();
|
||||
AstIf* const guardp = new AstIf{flp, condp, origStmtsp};
|
||||
AstIf* const guardp = new AstIf{flp, condp, new AstBegin{flp, "", origStmtsp, true}};
|
||||
bodyp->addStmtsp(guardp);
|
||||
nodep->replaceWith(pexprp);
|
||||
// Don't iterate pexprp here -- it was already iterated when created
|
||||
@@ -1367,9 +1368,10 @@ private:
|
||||
loopp->addStmtsp(new AstLoopTest{
|
||||
flp, loopp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}});
|
||||
{
|
||||
AstNode* const passp = new AstPExprClause{flp};
|
||||
passp->addNext(decrementVar);
|
||||
passp->addNext(setDone());
|
||||
AstBegin* const passp = new AstBegin{flp, "", nullptr, true};
|
||||
passp->addStmtsp(new AstPExprClause{flp});
|
||||
passp->addStmtsp(decrementVar);
|
||||
passp->addStmtsp(setDone());
|
||||
AstNodeExpr* passCondp = rhsp;
|
||||
if (nodep->isOverlapping()) {
|
||||
passCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), passCondp};
|
||||
@@ -1377,9 +1379,10 @@ private:
|
||||
loopp->addStmtsp(new AstIf{flp, passCondp, passp});
|
||||
}
|
||||
{
|
||||
AstNode* const failp = new AstPExprClause{flp, false};
|
||||
failp->addNext(decrementVar->cloneTree(false));
|
||||
failp->addNext(setDone());
|
||||
AstBegin* const failp = new AstBegin{flp, "", nullptr, true};
|
||||
failp->addStmtsp(new AstPExprClause{flp, false});
|
||||
failp->addStmtsp(decrementVar->cloneTree(false));
|
||||
failp->addStmtsp(setDone());
|
||||
loopp->addStmtsp(new AstIf{
|
||||
flp,
|
||||
new AstLogAnd{flp,
|
||||
@@ -1393,7 +1396,7 @@ private:
|
||||
flp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}, delayp});
|
||||
|
||||
// Main assertion block
|
||||
AstBegin* const bodyp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
|
||||
AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true};
|
||||
bodyp->addStmtsp(donep);
|
||||
bodyp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, donep, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitFalse{}}});
|
||||
@@ -1425,7 +1428,7 @@ private:
|
||||
new AstConst{flp, 1}}});
|
||||
|
||||
// Final assertion block
|
||||
AstBegin* const finalp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
|
||||
AstBegin* const finalp = new AstBegin{flp, "", nullptr, true};
|
||||
finalp->addStmtsp(activeCountp);
|
||||
finalp->addStmtsp(initActiveCountp);
|
||||
finalp->addStmtsp(finalLoopp);
|
||||
@@ -1456,7 +1459,7 @@ private:
|
||||
AstNodeExpr* const passCondp
|
||||
= nodep->isOverlapping() ? new AstLogAnd{flp, lhsp->cloneTreePure(false), rhsCopyp}
|
||||
: rhsCopyp;
|
||||
AstBegin* const beginp = new AstBegin{flp, m_blockNames.get(""), loopp, true};
|
||||
AstBegin* const beginp = new AstBegin{flp, "", loopp, true};
|
||||
beginp->addStmtsp(
|
||||
new AstIf{flp, passCondp, new AstPExprClause{flp}, new AstPExprClause{flp, false}});
|
||||
|
||||
@@ -1545,7 +1548,7 @@ private:
|
||||
"Unsupported: $sampled inside disabled condition of a sequence");
|
||||
m_disablep = new AstConst{m_disablep->fileline(), AstConst::BitFalse{}};
|
||||
// always a copy is used, so remove it now
|
||||
pushDeletep(m_disablep); // m_disablep used later
|
||||
pushDeletep(m_disablep);
|
||||
}
|
||||
FileLine* const flp = nodep->fileline();
|
||||
// Add counter which counts times the condition turned true
|
||||
@@ -1574,7 +1577,6 @@ private:
|
||||
= new AstAssign{flp, new AstVarRef{flp, initialCntp, VAccess::WRITE},
|
||||
readCntRefp->cloneTree(false)};
|
||||
// Prepend to the sequence body to keep statement list structure valid.
|
||||
UASSERT_OBJ(!bodyp->name().empty(), bodyp, "Sequence body should be named");
|
||||
AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext();
|
||||
bodyp->addStmtsp(initialCntp);
|
||||
initialCntp->addNextHere(assignp);
|
||||
@@ -1584,7 +1586,7 @@ private:
|
||||
= new AstIf{flp, new AstEq{flp, new AstVarRef{flp, initialCntp, VAccess::READ},
|
||||
readCntRefp->cloneTree(false)}};
|
||||
// Delete it, because it is always copied before insetion to the AST
|
||||
pushDeletep(m_disableSeqIfp); // m_disableSeqIfp used later
|
||||
pushDeletep(m_disableSeqIfp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
+1
-5
@@ -66,7 +66,7 @@ VCMethod VCMethod::arrayMethod(const string& name) {
|
||||
|
||||
std::string VNUser::dumpStr(std::string (*fmtAddrp)(const void*)) const {
|
||||
#ifdef VL_USER_TYPE_CHECKS
|
||||
if (const uint64_t* const uip = std::get_if<uint64_t>(&m_u)) return "#"s + cvtToStr(*uip);
|
||||
if (const int* const uip = std::get_if<int>(&m_u)) return "#"s + cvtToStr(*uip);
|
||||
if (void* const* const upp = std::get_if<void*>(&m_u)) return fmtAddrp(*upp);
|
||||
return "";
|
||||
#else
|
||||
@@ -208,10 +208,6 @@ string AstNode::dedotName(const string& namein) {
|
||||
return pretty;
|
||||
}
|
||||
|
||||
string AstNode::nameNoArray(const string& namein) {
|
||||
return namein.substr(0, namein.find("__BRA__"));
|
||||
}
|
||||
|
||||
string AstNode::vcdName(const string& namein) {
|
||||
// VCD tracing expects space to separate hierarchy
|
||||
// Dots are reserved for dots the user put in the name
|
||||
|
||||
+40
-41
@@ -56,7 +56,6 @@
|
||||
// Forward declarations
|
||||
class V3Graph;
|
||||
class ExecMTask;
|
||||
class VIfaceCaptureTag;
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -152,11 +151,11 @@ class VNUser final {
|
||||
#ifdef VL_USER_TYPE_CHECKS
|
||||
// monostate is an unwritten / cleared slot. It can be read as either form
|
||||
// and yields nullptr/0.
|
||||
std::variant<std::monostate, uint64_t, void*> m_u;
|
||||
std::variant<std::monostate, int, void*> m_u;
|
||||
#else
|
||||
union {
|
||||
void* up;
|
||||
uint64_t uq;
|
||||
int ui;
|
||||
} m_u;
|
||||
#endif
|
||||
|
||||
@@ -165,7 +164,7 @@ public:
|
||||
VNUser() = default;
|
||||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VNUser(uint64_t i) {
|
||||
VNUser(int i) {
|
||||
// VNUser{0} represents the monostate
|
||||
if (i) m_u = i;
|
||||
}
|
||||
@@ -179,22 +178,22 @@ public:
|
||||
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE {
|
||||
if (std::holds_alternative<std::monostate>(m_u)) return nullptr;
|
||||
void* const* const upp = std::get_if<void*>(&m_u);
|
||||
UASSERT_STATIC(upp, "AstNode user() slot written as uint64_t, read as pointer");
|
||||
UASSERT_STATIC(upp, "AstNode user() slot written as int, read as pointer");
|
||||
return reinterpret_cast<T>(*upp);
|
||||
}
|
||||
uint64_t toUQuad() const {
|
||||
int toInt() const {
|
||||
if (std::holds_alternative<std::monostate>(m_u)) return 0;
|
||||
const uint64_t* const uip = std::get_if<uint64_t>(&m_u);
|
||||
UASSERT_STATIC(uip, "AstNode user() slot written as pointer, read as uint64_t");
|
||||
const int* const uip = std::get_if<int>(&m_u);
|
||||
UASSERT_STATIC(uip, "AstNode user() slot written as pointer, read as int");
|
||||
return *uip;
|
||||
}
|
||||
#else
|
||||
VNUser() = default;
|
||||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VNUser(uint64_t i) {
|
||||
VNUser(int i) {
|
||||
m_u.up = nullptr;
|
||||
m_u.uq = i;
|
||||
m_u.ui = i;
|
||||
}
|
||||
explicit VNUser(void* p) { m_u.up = p; }
|
||||
~VNUser() = default;
|
||||
@@ -203,7 +202,7 @@ public:
|
||||
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE {
|
||||
return reinterpret_cast<T>(m_u.up);
|
||||
}
|
||||
uint64_t toUQuad() const { return m_u.uq; }
|
||||
int toInt() const { return m_u.ui; }
|
||||
#endif
|
||||
VSymEnt* toSymEnt() const { return to<VSymEnt*>(); }
|
||||
AstNode* toNodep() const VL_MT_SAFE { return to<AstNode*>(); }
|
||||
@@ -392,7 +391,7 @@ protected:
|
||||
RELINK_OP4
|
||||
};
|
||||
AstNode* m_oldp = nullptr; // The old node that was linked to this point in the tree
|
||||
AstNode* m_backp = nullptr; // Saved AstNode::m_backp of the unlinked node, to relink
|
||||
AstNode* m_backp = nullptr;
|
||||
AstNode** m_iterpp = nullptr;
|
||||
RelinkWhatEn m_chg = RELINK_BAD;
|
||||
|
||||
@@ -493,14 +492,14 @@ class AstNode VL_NOT_FINAL {
|
||||
// This member ordering both allows 64 bit alignment and puts associated data together
|
||||
// (under VL_USER_TYPE_CHECKS a VNUser is larger than 64 bits, so this packing no
|
||||
// longer holds; that build trades node size for catching int/pointer confusion)
|
||||
VNUser m_user1u{nullptr}; // Contains any information the user iteration routine wants
|
||||
VNUser m_user1u{0}; // Contains any information the user iteration routine wants
|
||||
uint32_t m_user1Cnt = 0; // Mark of when userp was set
|
||||
uint32_t m_user2Cnt = 0; // Mark of when userp was set
|
||||
VNUser m_user2u{nullptr}; // Contains any information the user iteration routine wants
|
||||
VNUser m_user3u{nullptr}; // Contains any information the user iteration routine wants
|
||||
VNUser m_user2u{0}; // Contains any information the user iteration routine wants
|
||||
VNUser m_user3u{0}; // Contains any information the user iteration routine wants
|
||||
uint32_t m_user3Cnt = 0; // Mark of when userp was set
|
||||
uint32_t m_user4Cnt = 0; // Mark of when userp was set
|
||||
VNUser m_user4u{nullptr}; // Contains any information the user iteration routine wants
|
||||
VNUser m_user4u{0}; // Contains any information the user iteration routine wants
|
||||
|
||||
// METHODS
|
||||
void op1p(AstNode* nodep) {
|
||||
@@ -659,7 +658,6 @@ public:
|
||||
string origNameProtect() const; // origName with --protect-id applied
|
||||
string shortName() const; // Name with __PVT__ removed for concatenating scopes
|
||||
static string dedotName(const string& namein); // Name with dots removed
|
||||
static string nameNoArray(const string& namein); // Name with any array index removed
|
||||
static string prettyName(const string& namein) VL_PURE; // Name for printing out to the user
|
||||
static string vpiName(const string& namein); // Name for vpi access
|
||||
static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors)
|
||||
@@ -710,61 +708,61 @@ public:
|
||||
VNUser user1u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "user1p used without AstUserInUse");
|
||||
return ((m_user1Cnt == VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{nullptr});
|
||||
return ((m_user1Cnt == VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
|
||||
}
|
||||
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
|
||||
void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; }
|
||||
void user1p(void* userp) { user1u(VNUser{userp}); }
|
||||
void user1(uint64_t val) { user1u(VNUser{val}); }
|
||||
uint64_t user1() const { return user1u().toUQuad(); }
|
||||
uint64_t user1Inc(uint64_t val = 1) { const uint64_t v = user1(); user1(v + val); return v; }
|
||||
uint64_t user1Or(uint64_t val) { const uint64_t v = user1(); user1(v | val); return v; }
|
||||
uint64_t user1SetOnce() { const uint64_t v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
|
||||
void user1(int val) { user1u(VNUser{val}); }
|
||||
int user1() const { return user1u().toInt(); }
|
||||
int user1Inc(int val = 1) { const int v = user1(); user1(v + val); return v; }
|
||||
int user1Or(int val) { const int v = user1(); user1(v | val); return v; }
|
||||
int user1SetOnce() { const int v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
|
||||
static void user1ClearTree() { VNUser1InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user2u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "user2p used without AstUserInUse");
|
||||
return ((m_user2Cnt == VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{nullptr});
|
||||
return ((m_user2Cnt == VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
|
||||
}
|
||||
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
|
||||
void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; }
|
||||
void user2p(void* userp) { user2u(VNUser{userp}); }
|
||||
void user2(uint64_t val) { user2u(VNUser{val}); }
|
||||
uint64_t user2() const { return user2u().toUQuad(); }
|
||||
uint64_t user2Inc(uint64_t val = 1) { const uint64_t v = user2(); user2(v + val); return v; }
|
||||
uint64_t user2Or(uint64_t val) { const uint64_t v = user2(); user2(v | val); return v; }
|
||||
uint64_t user2SetOnce() { const uint64_t v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
|
||||
void user2(int val) { user2u(VNUser{val}); }
|
||||
int user2() const { return user2u().toInt(); }
|
||||
int user2Inc(int val = 1) { const int v = user2(); user2(v + val); return v; }
|
||||
int user2Or(int val) { const int v = user2(); user2(v | val); return v; }
|
||||
int user2SetOnce() { const int v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
|
||||
static void user2ClearTree() { VNUser2InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user3u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "user3p used without AstUserInUse");
|
||||
return ((m_user3Cnt == VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{nullptr});
|
||||
return ((m_user3Cnt == VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
|
||||
}
|
||||
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
|
||||
void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; }
|
||||
void user3p(void* userp) { user3u(VNUser{userp}); }
|
||||
void user3(uint64_t val) { user3u(VNUser{val}); }
|
||||
uint64_t user3() const { return user3u().toUQuad(); }
|
||||
uint64_t user3Inc(uint64_t val = 1) { const uint64_t v = user3(); user3(v + val); return v; }
|
||||
uint64_t user3Or(uint64_t val) { const uint64_t v = user3(); user3(v | val); return v; }
|
||||
uint64_t user3SetOnce() { const uint64_t v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
|
||||
void user3(int val) { user3u(VNUser{val}); }
|
||||
int user3() const { return user3u().toInt(); }
|
||||
int user3Inc(int val = 1) { const int v = user3(); user3(v + val); return v; }
|
||||
int user3Or(int val) { const int v = user3(); user3(v | val); return v; }
|
||||
int user3SetOnce() { const int v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
|
||||
static void user3ClearTree() { VNUser3InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user4u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "user4p used without AstUserInUse");
|
||||
return ((m_user4Cnt == VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{nullptr});
|
||||
return ((m_user4Cnt == VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
|
||||
}
|
||||
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
|
||||
void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; }
|
||||
void user4p(void* userp) { user4u(VNUser{userp}); }
|
||||
void user4(uint64_t val) { user4u(VNUser{val}); }
|
||||
uint64_t user4() const { return user4u().toUQuad(); }
|
||||
uint64_t user4Or(uint64_t val) { const uint64_t v = user4(); user4(v | val); return v; }
|
||||
uint64_t user4Inc(uint64_t val = 1) { const uint64_t v = user4(); user4(v + val); return v; }
|
||||
uint64_t user4SetOnce() { const uint64_t v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
|
||||
void user4(int val) { user4u(VNUser{val}); }
|
||||
int user4() const { return user4u().toInt(); }
|
||||
int user4Or(int val) { const int v = user4(); user4(v | val); return v; }
|
||||
int user4Inc(int val = 1) { const int v = user4(); user4(v + val); return v; }
|
||||
int user4SetOnce() { const int v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
|
||||
static void user4ClearTree() { VNUser4InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
// clang-format on
|
||||
|
||||
@@ -1037,6 +1035,7 @@ protected:
|
||||
AstNode* iterateSubtreeReturnEdits(VNVisitor& v);
|
||||
|
||||
static void dumpJsonNum(std::ostream& os, const std::string& name, int64_t val);
|
||||
static void dumpJsonBool(std::ostream& os, const std::string& name, bool val);
|
||||
static void dumpJsonBoolIf(std::ostream& os, const std::string& name, bool val);
|
||||
static void dumpJsonStr(std::ostream& os, const std::string& name, const std::string& val);
|
||||
static void dumpJsonPtr(std::ostream& os, const std::string& name, const AstNode* const valp);
|
||||
|
||||
+265
-400
File diff suppressed because it is too large
Load Diff
+2
-5
@@ -149,9 +149,6 @@ bool AstActive::hasCombo() const { return m_sentreep->hasCombo(); }
|
||||
bool AstActive::hasInitial() const { return m_sentreep->hasInitial(); }
|
||||
bool AstActive::hasStatic() const { return m_sentreep->hasStatic(); }
|
||||
|
||||
bool AstNodeModule::isDollarUnit() const { return this == v3Global.rootp()->dollarUnitPkgp(); }
|
||||
bool AstNodeModule::isConstPool() const { return this == v3Global.rootp()->constPoolPkgp(); }
|
||||
|
||||
AstAlways::AstAlways(AstAssignW* assignp)
|
||||
: ASTGEN_SUPER_Always(assignp->fileline(), assignp)
|
||||
, m_keyword{VAlwaysKwd::CONT_ASSIGN} {}
|
||||
@@ -168,10 +165,10 @@ bool AstVar::sameNode(const AstNode* samep) const {
|
||||
&& covergroupRefMember() == asamep->covergroupRefMember();
|
||||
}
|
||||
|
||||
AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarRef* matchp)
|
||||
AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp)
|
||||
: ASTGEN_SUPER_MatchMasked(fl) {
|
||||
this->lhsp(lhsp);
|
||||
this->matchp(matchp);
|
||||
this->matchp(new AstVarRef{fl, matchp, VAccess::READ});
|
||||
dtypeSetUInt32();
|
||||
}
|
||||
|
||||
|
||||
+22
-108
@@ -34,14 +34,10 @@ class AstNodeDType VL_NOT_FINAL : public AstNode {
|
||||
// Ideally width() would migrate to BasicDType as that's where it makes sense,
|
||||
// but it's currently so prevalent in the code we leave it here.
|
||||
// Note the below members are included in AstTypeTable::Key lookups
|
||||
// dist-ast-dump-suppress // Part of dumpSmall
|
||||
int m_width = 0; // (also in AstTypeTable::Key) Bit width of operation
|
||||
// (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation
|
||||
// dist-ast-dump-suppress // Part of dumpSmall
|
||||
int m_widthMin = 0;
|
||||
// dist-ast-dump-suppress // Part of dumpSmall
|
||||
int m_widthMin
|
||||
= 0; // (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation
|
||||
VSigning m_numeric; // (also in AstTypeTable::Key) Node is signed
|
||||
// dist-ast-dump-suppress // Part of dumpSmall
|
||||
bool m_generic = false; // Simple globally referenced type, don't garbage collect
|
||||
// Unique number assigned to each dtype during creation for IEEE matching
|
||||
static int s_uniqueNum;
|
||||
@@ -250,8 +246,8 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType {
|
||||
//
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Package emitted with
|
||||
string m_name; // Name from upper typedef, if any
|
||||
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
|
||||
bool m_packed; // Packed struct/union, else unpacked
|
||||
const int m_uniqueNum;
|
||||
bool m_packed;
|
||||
bool m_isFourstate = false; // V3Width computes; true if any member is 4-state
|
||||
bool m_constrainedRand = false; // True if struct has constraint expression
|
||||
bool m_emitToString = false; // Generate to_string() for this struct/union if set
|
||||
@@ -319,7 +315,7 @@ public:
|
||||
class AstEnumItem final : public AstNode {
|
||||
// @astgen op1 := rangep : Optional[AstRange] // Range for name appending
|
||||
// @astgen op2 := valuep : Optional[AstNodeExpr]
|
||||
string m_name; // Name of item
|
||||
string m_name;
|
||||
|
||||
public:
|
||||
// Parents: ENUM
|
||||
@@ -409,7 +405,6 @@ class AstBasicDType final : public AstNodeDType {
|
||||
// @astgen op1 := rangep : Optional[AstRange] // Range of variable
|
||||
struct Members final {
|
||||
VBasicDTypeKwd m_keyword; // (also in VBasicTypeKey) What keyword created basic type
|
||||
// dist-ast-dump-suppress // Part of dumpSmall
|
||||
VNumRange m_nrange; // (also in VBasicTypeKey) Numeric msb/lsb (if non-opaque keyword)
|
||||
bool operator==(const Members& rhs) const {
|
||||
return rhs.m_keyword == m_keyword && rhs.m_nrange == m_nrange;
|
||||
@@ -498,9 +493,6 @@ public:
|
||||
bool isStdRandomGenerator() const VL_MT_SAFE {
|
||||
return keyword() == VBasicDTypeKwd::RANDOM_STDGENERATOR;
|
||||
}
|
||||
bool isCovergroupInstHandle() const VL_MT_SAFE {
|
||||
return keyword() == VBasicDTypeKwd::COVERGROUP_INSTHANDLE;
|
||||
}
|
||||
bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
|
||||
bool isString() const VL_MT_STABLE { return keyword().isString(); }
|
||||
bool isZeroInit() const { return keyword().isZeroInit(); }
|
||||
@@ -690,89 +682,13 @@ public:
|
||||
int widthTotalBytes() const override { return 1; }
|
||||
bool isCompound() const override { return false; }
|
||||
};
|
||||
class AstCoverCrossDType final : public AstNodeDType {
|
||||
// Borrowed pointer to VlCoverCrossT<...> or construction-time VlCoverCrossDyn.
|
||||
const uint32_t m_dimensions;
|
||||
const uint32_t m_tuples; // Fixed capacity number of cross tuples (VlCoverCrossT's Tuples)
|
||||
const uint32_t m_bins; // Fixed capacity number of explicit bins (VlCoverCrossT's Bins)
|
||||
const uint32_t m_autoBins; // Fixed capacity number of auto bins (VlCoverCrossT's AutoBins)
|
||||
const uint64_t m_binWords; // Fixed capacity selection words (VlCoverCrossT's BinWords)
|
||||
const bool m_dynamic; // Bin layout is determined at covergroup construction
|
||||
|
||||
public:
|
||||
AstCoverCrossDType(FileLine* fl, uint32_t dimensions, uint32_t tuples, uint32_t bins,
|
||||
uint32_t autoBins, uint64_t binWords, bool dynamic = false)
|
||||
: ASTGEN_SUPER_CoverCrossDType(fl)
|
||||
, m_dimensions{dimensions}
|
||||
, m_tuples{tuples}
|
||||
, m_bins{bins}
|
||||
, m_autoBins{autoBins}
|
||||
, m_binWords{binWords}
|
||||
, m_dynamic{dynamic} {
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverCrossDType;
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(m_dimensions == 0);
|
||||
return nullptr;
|
||||
}
|
||||
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
|
||||
const AstCoverCrossDType* const sp = VN_DBG_AS(samep, CoverCrossDType);
|
||||
return dimensions() == sp->dimensions() && tuples() == sp->tuples() && bins() == sp->bins()
|
||||
&& autoBins() == sp->autoBins() && binWords() == sp->binWords()
|
||||
&& isDynamic() == sp->isDynamic();
|
||||
} // LCOV_EXCL_STOP
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
uint32_t dimensions() const { return m_dimensions; }
|
||||
uint32_t tuples() const { return m_tuples; }
|
||||
uint32_t bins() const { return m_bins; }
|
||||
uint32_t autoBins() const { return m_autoBins; }
|
||||
uint64_t binWords() const { return m_binWords; }
|
||||
bool isDynamic() const { return m_dynamic; }
|
||||
string cppTemplateArgs() const;
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
int widthAlignBytes() const override { return sizeof(void*); }
|
||||
int widthTotalBytes() const override { return sizeof(void*); }
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
class AstCoverpointDType final : public AstNodeDType {
|
||||
// Borrowed pointer to a covergroup coverpoint runtime, 'VlCoverpointT<hitBound>*'.
|
||||
// Follows pattern of AstQueueDType in capturing the compile-time max bin overlap
|
||||
// template argument.
|
||||
uint32_t m_hitBound; // VlCoverpointT<> template argument; hit list size, >= 1
|
||||
public:
|
||||
AstCoverpointDType(FileLine* fl, uint32_t hitBound)
|
||||
: ASTGEN_SUPER_CoverpointDType(fl)
|
||||
, m_hitBound{hitBound} {
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverpointDType;
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(m_hitBound < 1);
|
||||
return nullptr;
|
||||
}
|
||||
// V3Covergroup interns these one-per-hitBound into the type table, so identity is
|
||||
// equality; there is never a second node with the same bound to compare against.
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
// ACCESSORS
|
||||
uint32_t hitBound() const { return m_hitBound; }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
int widthAlignBytes() const override { return sizeof(void*); }
|
||||
int widthTotalBytes() const override { return sizeof(void*); }
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
class AstDefImplicitDType final : public AstNodeDType {
|
||||
// For parsing enum/struct/unions that are declared with a variable rather than typedef
|
||||
// This allows "var enum {...} a,b" to share the enum definition for both variables
|
||||
// After link, these become typedefs
|
||||
// @astgen op1 := childDTypep : Optional[AstNodeDType]
|
||||
string m_name; // Data type name
|
||||
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
|
||||
string m_name;
|
||||
const int m_uniqueNum;
|
||||
|
||||
public:
|
||||
AstDefImplicitDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp)
|
||||
@@ -882,8 +798,13 @@ class AstEnumDType final : public AstNodeDType {
|
||||
// @astgen op2 := itemsp : List[AstEnumItem]
|
||||
//
|
||||
// @astgen ptr := m_refDTypep : Optional[AstNodeDType] // Elements of this type (post-width)
|
||||
public:
|
||||
using TableMap = std::map<VAttrType, AstVar*>;
|
||||
|
||||
private:
|
||||
string m_name; // Name from upper typedef, if any
|
||||
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
|
||||
const int m_uniqueNum;
|
||||
TableMap m_tableMap; // Created table for V3Width only to remove duplicates
|
||||
|
||||
public:
|
||||
AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstEnumItem* itemsp)
|
||||
@@ -931,12 +852,13 @@ public:
|
||||
return count;
|
||||
}
|
||||
bool isCompound() const override { return false; }
|
||||
TableMap& tableMap() { return m_tableMap; }
|
||||
const TableMap& tableMap() const { return m_tableMap; }
|
||||
};
|
||||
|
||||
class AstIfaceGenericDType final : public AstNodeDType {
|
||||
// Generic interface that will be replaced with AstIfaceRefDType
|
||||
FileLine* m_modportFileline; // Where modport token was
|
||||
// dist-ast-dump-suppress // Part of name()
|
||||
string m_modportName; // "" = no modport
|
||||
public:
|
||||
explicit AstIfaceGenericDType(FileLine* fl)
|
||||
@@ -950,6 +872,8 @@ public:
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstIfaceGenericDType;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
bool hasDType() const override VL_MT_SAFE { return true; }
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
@@ -979,6 +903,7 @@ class AstIfaceRefDType final : public AstNodeDType {
|
||||
string m_cellName; // "" = no cell, such as when connects to 'input' iface
|
||||
string m_ifaceName; // Interface name
|
||||
string m_modportName; // "" = no modport
|
||||
bool m_portDecl = false; // Interface_port_declaration
|
||||
bool m_virtual = false; // True if virtual interface
|
||||
public:
|
||||
AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName)
|
||||
@@ -1010,16 +935,11 @@ public:
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override {
|
||||
// Each occurrence of a virtual interface type parses to its own node,
|
||||
// so pointer identity is insufficient; compare the referenced
|
||||
// interface instead, same equivalence as AstNode::computeCastable uses.
|
||||
const AstIfaceRefDType* const asamep = VN_DBG_AS(samep, IfaceRefDType);
|
||||
return ifaceViaCellp() && ifaceViaCellp() == asamep->ifaceViaCellp()
|
||||
&& modportName() == asamep->modportName() && isVirtual() == asamep->isVirtual();
|
||||
}
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
|
||||
int widthAlignBytes() const override { return 0; }
|
||||
int widthTotalBytes() const override { return 0; }
|
||||
bool isPortDecl() const { return m_portDecl; }
|
||||
void isPortDecl(bool flag) { m_portDecl = flag; }
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void isVirtual(bool flag) {
|
||||
m_virtual = flag;
|
||||
@@ -1054,7 +974,7 @@ class AstMemberDType final : public AstNodeDType {
|
||||
string m_name; // Name of variable
|
||||
string m_tag; // Holds the string of the verilator tag -- used in JSON output.
|
||||
int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member
|
||||
bool m_constrainedRand = false; // Member has a constraint expression
|
||||
bool m_constrainedRand = false;
|
||||
VRandAttr m_rand; // Randomizability of this member (rand, randc, etc)
|
||||
public:
|
||||
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
|
||||
@@ -1275,9 +1195,6 @@ class AstRefDType final : public AstNodeDType {
|
||||
// @astgen ptr := m_refDTypep : Optional[AstNodeDType] // Data type references
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in
|
||||
string m_name; // Name of an AstTypedef
|
||||
// Interface capture tag, owned by V3LinkDotIfaceCapture; cloning copies it
|
||||
const VIfaceCaptureTag* m_captureTagp = nullptr;
|
||||
|
||||
public:
|
||||
AstRefDType(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_RefDType(fl)
|
||||
@@ -1304,7 +1221,6 @@ public:
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override {
|
||||
return subDTypep()->similarDType(samep->subDTypep());
|
||||
}
|
||||
const char* broken() const override;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
@@ -1329,8 +1245,6 @@ public:
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
const VIfaceCaptureTag* captureTagp() const { return m_captureTagp; }
|
||||
void captureTagp(const VIfaceCaptureTag* tagp) { m_captureTagp = tagp; }
|
||||
bool isCompound() const override {
|
||||
v3fatalSrc("call isCompound on subdata type, not reference");
|
||||
return false;
|
||||
|
||||
+16
-45
@@ -66,18 +66,7 @@ public:
|
||||
bool isOpaque() const { return VN_IS(this, CvtPackString); }
|
||||
// True for SVA multi-cycle sequence nodes (SExpr, SConsRep, etc.)
|
||||
virtual bool isMultiCycleSva() const { return false; }
|
||||
const AstNodeExpr* getVAccessTargetRecurse() const;
|
||||
AstNodeExpr* getVAccessTargetRecurse() {
|
||||
return const_cast<AstNodeExpr*>( // casting constness away is safe since this function is
|
||||
// non-const itself therefore, caller guarantees that
|
||||
// this object is non-const
|
||||
static_cast<const AstNodeExpr*>(this)->getVAccessTargetRecurse());
|
||||
}
|
||||
VAccess getVAccessRecurse() const;
|
||||
// TODO: this actually means it's a write or RW, not that it's an LValue
|
||||
bool isLValue() const { return getVAccessRecurse().isWriteOrRW(); }
|
||||
// Return base var (or const) nodep dereferences
|
||||
AstNode* baseFromp(bool overMembers);
|
||||
bool isLValue() const;
|
||||
|
||||
// Wrap This expression into an AstStmtExpr to denote it occurs in statement position
|
||||
inline AstStmtExpr* makeStmt();
|
||||
@@ -200,7 +189,6 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeExpr {
|
||||
// @astgen op2 := argsp : List[AstNodeExpr] // Note: op1 used by some sub-types only
|
||||
//
|
||||
// @astgen ptr := m_funcp : AstCFunc // Function being called
|
||||
// dist-ast-dump-suppress // Too verbose
|
||||
string m_argTypes;
|
||||
bool m_superReference = false; // Called with super reference
|
||||
|
||||
@@ -228,7 +216,7 @@ public:
|
||||
void funcp(AstCFunc* funcp) { m_funcp = funcp; }
|
||||
string argTypes() const { return m_argTypes; }
|
||||
void argTypes(const string& str) { m_argTypes = str; }
|
||||
bool isUnlikely() const override { return m_funcp && m_funcp->isUnlikely(); }
|
||||
|
||||
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() final override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const final override { return true; }
|
||||
@@ -283,7 +271,6 @@ public:
|
||||
bool superReference() const { return m_superReference; }
|
||||
void superReference(bool flag) { m_superReference = flag; }
|
||||
bool isPure() override;
|
||||
bool isUnlikely() const override { return m_taskp && m_taskp->isUnlikely(); }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstNodeFTaskRef* const asamep = VN_DBG_AS(samep, NodeFTaskRef);
|
||||
return taskp() == asamep->taskp() //
|
||||
@@ -532,6 +519,7 @@ public:
|
||||
}
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
static AstNodeVarRef* varRefLValueRecurse(AstNode* nodep);
|
||||
};
|
||||
|
||||
// === Concrete node types =====================================================
|
||||
@@ -568,7 +556,6 @@ class AstWith final : public AstNode {
|
||||
private:
|
||||
// 'with (identifier_list) {...}' restricted form (IEEE 1800-2023 18.7).
|
||||
bool m_restricted = false;
|
||||
// dist-ast-dump-suppress // V3LinkDot temporary use only
|
||||
bool m_validated = false; // identifier_list typo / unused checks already run
|
||||
std::set<std::string> m_restrictedNames;
|
||||
|
||||
@@ -752,7 +739,7 @@ class AstCMethodHard final : public AstNodeExpr {
|
||||
// @astgen op2 := pinsp : List[AstNodeExpr] // Arguments
|
||||
// @astgen op3 := withp : Optional[AstWith] // With clause
|
||||
VCMethod m_method; // Which method to call
|
||||
VIsCached m_purity; // Pure optimizable
|
||||
bool m_pure = false; // Pure optimizable
|
||||
bool m_usePtr = false; // Use '->' not '.'
|
||||
public:
|
||||
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VCMethod method, AstNodeExpr* pinsp = nullptr)
|
||||
@@ -760,19 +747,15 @@ public:
|
||||
, m_method{method} {
|
||||
this->fromp(fromp);
|
||||
addPinsp(pinsp);
|
||||
setPurity();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCMethodHard;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return method().ascii(); }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstCMethodHard* const asamep = VN_DBG_AS(samep, CMethodHard);
|
||||
return (m_method == asamep->m_method);
|
||||
}
|
||||
bool isPure() override {
|
||||
if (!m_purity.isCached()) m_purity.set(getPurity());
|
||||
return m_purity.get();
|
||||
}
|
||||
bool isPure() override { return m_pure; }
|
||||
int instrCount() const override;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -783,7 +766,7 @@ public:
|
||||
void method(VCMethod value) { m_method = value; }
|
||||
|
||||
private:
|
||||
bool getPurity();
|
||||
void setPurity();
|
||||
};
|
||||
class AstCReset final : public AstNodeExpr {
|
||||
// Reset variable at startup
|
||||
@@ -1475,7 +1458,7 @@ class AstExprStmt final : public AstNodeExpr {
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
// @astgen op2 := resultp : AstNodeExpr
|
||||
private:
|
||||
bool m_hasResult = true; // Returns result via resultp()
|
||||
bool m_hasResult = true;
|
||||
|
||||
public:
|
||||
AstExprStmt(FileLine* fl, AstNode* stmtsp, AstNodeExpr* resultp)
|
||||
@@ -1486,8 +1469,6 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstExprStmt;
|
||||
// METHODS
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
@@ -1765,7 +1746,7 @@ public:
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstImplication final : public AstNodeExpr {
|
||||
// Implication |-> |=> (IEEE 1800-2023 16.12.7) and followed-by #-# #=#
|
||||
// Implication |-> |=> (IEEE 1800-2023 16.12.6) and followed-by #-# #=#
|
||||
// (IEEE 1800-2023 16.12.9). Antecedent-miss is vacuous-pass for implication
|
||||
// and non-vacuous-fail for followed-by, hence the separate flag.
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
@@ -1793,7 +1774,7 @@ public:
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool isMultiCycleSva() const override { return m_isFollowedBy || !m_isOverlapped; }
|
||||
bool isMultiCycleSva() const override { return m_isFollowedBy; }
|
||||
bool isOverlapped() const { return m_isOverlapped; }
|
||||
bool isFollowedBy() const { return m_isFollowedBy; }
|
||||
};
|
||||
@@ -1812,7 +1793,6 @@ public:
|
||||
using KeyItemMap = std::map<uint64_t, AstInitItem*>;
|
||||
|
||||
private:
|
||||
// dist-ast-dump-suppress // Dumped using dumpInitList
|
||||
KeyItemMap m_map; // Node value for each array index
|
||||
// METHODS
|
||||
void dumpInitList(std::ostream& str) const;
|
||||
@@ -1886,8 +1866,6 @@ public:
|
||||
, m_name{name}
|
||||
, m_index{index} {}
|
||||
ASTGEN_MEMBERS_AstLambdaArgRef;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
string emitVerilog() override { return name(); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -1907,7 +1885,7 @@ class AstMatchMasked final : public AstNodeExpr {
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
// @astgen op2 := matchp : AstVarRef
|
||||
public:
|
||||
inline AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarRef* matchp);
|
||||
inline AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp);
|
||||
ASTGEN_MEMBERS_AstMatchMasked;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { return "VL_MATCHMASKED_%lq(%lw, %li, %ri)"; }
|
||||
@@ -2035,7 +2013,7 @@ class AstParseRef final : public AstNodeExpr {
|
||||
// @astgen op1 := lhsp : Optional[AstNodeExpr]
|
||||
// @astgen op2 := ftaskrefp : Optional[AstNodeFTaskRef]
|
||||
|
||||
string m_name; // Name of the variable/function/task
|
||||
string m_name;
|
||||
|
||||
public:
|
||||
AstParseRef(FileLine* fl, const string& name, AstNodeExpr* lhsp = nullptr,
|
||||
@@ -2408,7 +2386,6 @@ class AstSFormatArg final : public AstNodeExpr {
|
||||
// used to pass to (potentially) runtime decoding of format arguments
|
||||
// PARENT: SFormatF (or next list of expressions)
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := namep : Optional[AstNodeExpr] // Runtime enum name lookup
|
||||
VFormatAttr m_formatAttr; // How to format expression
|
||||
|
||||
public:
|
||||
@@ -2431,7 +2408,6 @@ public:
|
||||
bool cleanOut() const override { return true; }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!VN_IS(backp(), SFormatF) && firstAbovep()); // In list under SFormatF
|
||||
BROKEN_RTN(formatAttr().isEnum() != static_cast<bool>(namep()));
|
||||
return nullptr;
|
||||
}
|
||||
VFormatAttr formatAttr() const { return m_formatAttr; }
|
||||
@@ -2637,7 +2613,7 @@ class AstScopeName final : public AstNodeExpr {
|
||||
// For display %m and DPI context imports
|
||||
// Parents: AstSFormatF, AstNodeFTaskRef, AstNodeFTask
|
||||
std::string m_scopeAttr;
|
||||
std::string m_scopeEntr; // Scope path for the DPI import/export context name
|
||||
std::string m_scopeEntr;
|
||||
bool m_dpiExport = false; // Is for dpiExport
|
||||
const bool m_forFormat; // Is for a format %m
|
||||
static std::string scopeNameFormatter(const std::string& text);
|
||||
@@ -3059,8 +3035,6 @@ public:
|
||||
addConstraintsp(constraintsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstWithParse;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
bool restricted() const { return m_restricted; }
|
||||
void restricted(bool flag) { m_restricted = flag; }
|
||||
@@ -4937,6 +4911,9 @@ public:
|
||||
bool isPredictOptimizable() const override { return true; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
// Special operators
|
||||
// Return base var (or const) nodep dereferences
|
||||
static AstNode* baseFromp(AstNode* nodep, bool overMembers);
|
||||
};
|
||||
class AstAssocSel final : public AstNodeSel {
|
||||
void init(const AstNode* fromp) {
|
||||
@@ -5189,8 +5166,6 @@ public:
|
||||
"not coded to create after dtypes resolved");
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSelBit;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
VAccess access() const { return m_access; }
|
||||
void access(const VAccess& flag) { m_access = flag; }
|
||||
};
|
||||
@@ -5455,7 +5430,6 @@ public:
|
||||
enum FmtType : int { ATOI = 10, ATOHEX = 16, ATOOCT = 8, ATOBIN = 2, ATOREAL = -1 };
|
||||
|
||||
private:
|
||||
// dist-ast-dump-suppress // Part of name()
|
||||
const FmtType m_fmtType; // Operation type
|
||||
public:
|
||||
AstAtoN(FileLine* fl, AstNodeExpr* lhsp, FmtType fmtType)
|
||||
@@ -6464,7 +6438,6 @@ class AstVarXRef final : public AstNodeVarRef {
|
||||
string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
|
||||
string m_inlinedDots; // Dotted hierarchy flattened out
|
||||
bool m_containsGenBlock = false; // Contains gen block reference
|
||||
bool m_readOnlyModport = false; // Linked via an input-only modport, until V3LinkLValue
|
||||
public:
|
||||
AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, nullptr, access)
|
||||
@@ -6482,8 +6455,6 @@ public:
|
||||
void inlinedDots(const string& flag) { m_inlinedDots = flag; }
|
||||
bool containsGenBlock() const { return m_containsGenBlock; }
|
||||
void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
|
||||
bool readOnlyModport() const { return m_readOnlyModport; }
|
||||
void readOnlyModport(const bool flag) { m_readOnlyModport = flag; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
|
||||
+84
-230
@@ -44,7 +44,6 @@ class AstNodeCoverDecl VL_NOT_FINAL : public AstNode {
|
||||
// Coverage counters are emitted in each module object, so duplicate
|
||||
// no-inline instances can keep independent counts for forcePerInstance.
|
||||
int m_localBinNum = 0; // Per-module coverage bin offset
|
||||
bool m_perInstance = false; // Don't clone during inlining, already per instance
|
||||
public:
|
||||
AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment)
|
||||
: AstNode(t, fl)
|
||||
@@ -66,8 +65,6 @@ public:
|
||||
void binNum(int flag) { m_binNum = flag; }
|
||||
int localBinNum() const { return m_localBinNum; }
|
||||
void localBinNum(int flag) { m_localBinNum = flag; }
|
||||
bool perInstance() const { return m_perInstance; }
|
||||
void perInstance(bool flag) { m_perInstance = flag; }
|
||||
virtual int size() const = 0;
|
||||
const string& comment() const { return m_text; } // text to insert in code
|
||||
const string& page() const { return m_page; }
|
||||
@@ -77,8 +74,7 @@ public:
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl);
|
||||
return (fileline() == asamep->fileline() && hier() == asamep->hier()
|
||||
&& comment() == asamep->comment() && page() == asamep->page()
|
||||
&& perInstance() == asamep->perInstance());
|
||||
&& comment() == asamep->comment() && page() == asamep->page());
|
||||
}
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; }
|
||||
@@ -98,7 +94,6 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
||||
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
||||
string m_name; // Name of task
|
||||
string m_cname; // Name of task if DPI import
|
||||
// dist-ast-dump-suppress // Not dumped due to verbosity
|
||||
string m_dpiCDecl; // Custom DPI-C function declaration
|
||||
string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8)
|
||||
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
|
||||
@@ -117,8 +112,6 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
||||
bool m_isHideLocal : 1; // Verilog local
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
bool m_dpiPure : 1; // DPI import pure (vs. virtual pure)
|
||||
bool m_keepAlive : 1; // Disable dead function elimination
|
||||
bool m_needsSyms : 1; // Requires vlSymsp argument
|
||||
bool m_pureVirtual : 1; // Pure virtual
|
||||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
bool m_static : 1; // Static method in class
|
||||
@@ -151,8 +144,6 @@ protected:
|
||||
, m_isHideLocal{false}
|
||||
, m_isHideProtected{false}
|
||||
, m_dpiPure{false}
|
||||
, m_keepAlive{false}
|
||||
, m_needsSyms{true}
|
||||
, m_pureVirtual{false}
|
||||
, m_recursive{false}
|
||||
, m_static{false}
|
||||
@@ -220,10 +211,6 @@ public:
|
||||
void isHideProtected(bool flag) { m_isHideProtected = flag; }
|
||||
bool dpiPure() const { return m_dpiPure; }
|
||||
void dpiPure(bool flag) { m_dpiPure = flag; }
|
||||
bool keepAlive() const { return m_keepAlive; }
|
||||
void keepAlive(bool flag) { m_keepAlive = flag; }
|
||||
bool needsSyms() const { return m_needsSyms; }
|
||||
void needsSyms(bool flag) { m_needsSyms = flag; }
|
||||
bool pureVirtual() const { return m_pureVirtual; }
|
||||
void pureVirtual(bool flag) { m_pureVirtual = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
@@ -306,8 +293,8 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
|
||||
// @astgen op2 := stmtsp : List[AstNode]
|
||||
string m_name; // Name of the module
|
||||
const string m_origName; // Name of the module, ignoring name() changes, for dot lookup
|
||||
// dist-ast-dump-suppress // For some user errors messages only, visible where used
|
||||
string m_someInstanceName; // Hierarchical name of some arbitrary instance of this module.
|
||||
// Used for user messages only.
|
||||
string m_libname; // Work library
|
||||
int m_depth = 0; // 1=top module, 2=cell off top, shared things low, for -depth options
|
||||
int m_level = 0; // 1=top module, 2=cell off top, shared things have high number
|
||||
@@ -325,6 +312,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
|
||||
bool m_hasParameterList : 1; // Has #() for parameter declaration
|
||||
bool m_hierBlock : 1; // Hierarchical Block marked by HIER_BLOCK pragma
|
||||
bool m_hierParams : 1; // Block containing params for parameterized hier blocks
|
||||
bool m_internal : 1; // Internally created
|
||||
bool m_recursive : 1; // Recursive module
|
||||
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
|
||||
bool m_parameterizedTemplate : 1; // True when at least one specialized clone exists;
|
||||
@@ -346,6 +334,7 @@ protected:
|
||||
, m_hasParameterList{false}
|
||||
, m_hierBlock{false}
|
||||
, m_hierParams{false}
|
||||
, m_internal{false}
|
||||
, m_recursive{false}
|
||||
, m_recursiveClone{false}
|
||||
, m_parameterizedTemplate{false}
|
||||
@@ -358,8 +347,6 @@ public:
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
virtual bool timescaleMatters() const = 0;
|
||||
inline bool isDollarUnit() const; // Is the $unit package
|
||||
inline bool isConstPool() const; // Is the constant pool package
|
||||
// ACCESSORS
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string origName() const override { return m_origName; }
|
||||
@@ -391,6 +378,8 @@ public:
|
||||
void hierBlock(bool flag) { m_hierBlock = flag; }
|
||||
bool hierParams() const { return m_hierParams; }
|
||||
void hierParams(bool flag) { m_hierParams = flag; }
|
||||
bool internal() const { return m_internal; }
|
||||
void internal(bool flag) { m_internal = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
void recursiveClone(bool flag) { m_recursiveClone = flag; }
|
||||
@@ -554,7 +543,6 @@ class AstCFunc final : public AstNode {
|
||||
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
|
||||
bool m_needProcess : 1; // Needs access to VlProcess of the caller
|
||||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
bool m_unlikely : 1; // Unlikely to get called (though still optimize unlike slow())
|
||||
bool m_noLife : 1; // Disable V3Life on this function - has multiple calls, and reads Syms
|
||||
// state
|
||||
bool m_isCovergroupSample : 1; // Automatic covergroup sample() function
|
||||
@@ -587,7 +575,6 @@ public:
|
||||
m_dpiImportPrototype = false;
|
||||
m_dpiImportWrapper = false;
|
||||
m_recursive = false;
|
||||
m_unlikely = false;
|
||||
m_noLife = false;
|
||||
m_isCovergroupSample = false;
|
||||
m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost
|
||||
@@ -666,8 +653,6 @@ public:
|
||||
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
void unlikely(bool flag) { m_unlikely = flag; }
|
||||
bool isUnlikely() const override { return m_unlikely; } // Note virtual override
|
||||
void noLife(bool flag) { m_noLife = flag; }
|
||||
bool noLife() const { return m_noLife; }
|
||||
bool isCovergroupSample() const { return m_isCovergroupSample; }
|
||||
@@ -700,18 +685,15 @@ class AstCell final : public AstNode {
|
||||
// A instantiation cell or interface call (don't know which until link)
|
||||
// @astgen op1 := pinsp : List[AstPin] // List of port assignments
|
||||
// @astgen op2 := paramsp : List[AstPin] // List of parameter assignments
|
||||
// Pre V3Param: Range(s) for arrayed instances.
|
||||
// From V3Param to V3Width: ranges of the arrayed cell this element cell was expanded from
|
||||
// After V3Width: nullptr
|
||||
// @astgen op3 := rangep : List[AstRange]
|
||||
// @astgen op4 := intfRefsp : List[AstIntfRef] // List of interface references, for tracing/VPI
|
||||
// @astgen op3 := rangep : List[AstRange] // Range(s) for arrayed instances; multi-dim chains
|
||||
// via nextp()
|
||||
// @astgen op4 := intfRefsp : List[AstIntfRef] // List of interface references, for tracing
|
||||
//
|
||||
// @astgen ptr := m_modp : Optional[AstNodeModule] // [AfterLink] Pointer to module instanced
|
||||
FileLine* m_modNameFileline; // Where module the cell instances token was
|
||||
string m_name; // Cell name
|
||||
string m_origName; // Original name before dot addition
|
||||
string m_modName; // Module the cell instances
|
||||
int m_arrayIdx = -1; // Row-major position in instance array, from the left (V3Param-V3Width)
|
||||
bool m_hasIfaceVar : 1; // True if a Var has been created for this cell
|
||||
bool m_recursive : 1; // Self-recursive module
|
||||
bool m_trace : 1; // Trace this cell
|
||||
@@ -746,8 +728,6 @@ public:
|
||||
FileLine* modNameFileline() const { return m_modNameFileline; }
|
||||
AstNodeModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated
|
||||
void modp(AstNodeModule* nodep) { m_modp = nodep; }
|
||||
int arrayIdx() const { return m_arrayIdx; }
|
||||
void arrayIdx(int idx) { m_arrayIdx = idx; }
|
||||
bool hasIfaceVar() const { return m_hasIfaceVar; }
|
||||
void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; }
|
||||
void trace(bool flag) { m_trace = flag; }
|
||||
@@ -801,9 +781,6 @@ public:
|
||||
string name() const override VL_MT_STABLE { return m_cellp->name(); }
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
|
||||
void scopep(AstScope* nodep) { m_scopep = nodep; }
|
||||
AstCellInline* cellp() const VL_MT_STABLE { return m_cellp; } // Pointer to the CellInline
|
||||
void cellp(AstCellInline* nodep) { m_cellp = nodep; }
|
||||
string origModName() const {
|
||||
return m_cellp->origModName();
|
||||
} // * = modp()->origName() before inlining
|
||||
@@ -912,8 +889,6 @@ public:
|
||||
}
|
||||
}
|
||||
ASTGEN_MEMBERS_AstClockingItem;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
VDirection direction() const { return m_direction; }
|
||||
AstClockingItem* outputp() const { return m_outputp; }
|
||||
void outputp(AstClockingItem* outputp) { m_outputp = outputp; }
|
||||
@@ -932,11 +907,11 @@ public:
|
||||
, m_libname{libname}
|
||||
, m_configname{cellname} {}
|
||||
ASTGEN_MEMBERS_AstConfig;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
std::string name() const override VL_MT_STABLE { return m_libname + "." + m_configname; }
|
||||
std::string libname() const VL_MT_STABLE { return m_libname; }
|
||||
std::string configname() const VL_MT_STABLE { return m_configname; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
class AstConfigCell final : public AstNode {
|
||||
// Parents: CONFIGRULE
|
||||
@@ -995,6 +970,40 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
class AstConstPool final : public AstNode {
|
||||
// Container for const static data
|
||||
// @astgen op1 := modulep : AstModule // m_modp below TODO: fix this mess
|
||||
//
|
||||
// @astgen ptr := m_modp : AstModule // The Module holding the Scope below ...
|
||||
// @astgen ptr := m_scopep : AstScope // Scope holding the constant variables
|
||||
std::unordered_multimap<uint32_t, AstVarScope*> m_tables; // Constant tables (unpacked arrays)
|
||||
std::unordered_multimap<uint32_t, AstVarScope*> m_consts; // Constant tables (scalars)
|
||||
|
||||
AstVarScope* createNewEntry(const string& name, AstNodeExpr* initp);
|
||||
|
||||
public:
|
||||
explicit AstConstPool(FileLine* fl);
|
||||
ASTGEN_MEMBERS_AstConstPool;
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
void cloneRelink() override { V3ERROR_NA; } // Not cloneable
|
||||
AstModule* modp() const { return m_modp; }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
|
||||
// Find a table (unpacked array) within the constant pool which is initialized with the
|
||||
// given value, or create one if one does not already exists. The returned VarScope *might*
|
||||
// have a different dtype than the given initp->dtypep(), including a different element type,
|
||||
// but it will always have the same size and element width. In contexts where this matters,
|
||||
// the caller must handle the dtype difference as appropriate.
|
||||
AstVarScope* findTable(AstInitArray* initp);
|
||||
// Find a constant within the constant pool which is initialized with the given value, or
|
||||
// create one if one does not already exists. If 'mergeDType' is true, then the returned
|
||||
// VarScope *might* have a different type than the given initp->dtypep(). In contexts where
|
||||
// this matters, the caller must handle the dtype difference as appropriate. If 'mergeDType' is
|
||||
// false, the returned VarScope will have _->dtypep()->sameTree(initp->dtypep()) return true.
|
||||
AstVarScope* findConst(AstConst* initp, bool mergeDType);
|
||||
// Rebuild hashes and missing variable scopes after potential removals
|
||||
void rebuildVarScopesAndCache();
|
||||
};
|
||||
class AstConstraint final : public AstNode {
|
||||
// Constraint
|
||||
// @astgen op1 := itemsp : List[AstNode]
|
||||
@@ -1042,13 +1051,12 @@ class AstCoverBin final : public AstNode {
|
||||
// @astgen op4 := transp : List[AstCoverTransSet]
|
||||
const string m_name; // Base name of the bin
|
||||
const VCoverBinsType m_binsType; // Bin type (eg AUTO, IGNORE, ILLEGAL)
|
||||
bool m_isArray = false; // Bin is an array: of a bin per value or transition, or of
|
||||
// arraySizep() bins
|
||||
bool m_isArray = false; // Bin is either an auto-sized array of values or transitions
|
||||
bool m_isWildcard = false; // Bin uses wildcard matching (independent of ignore/illegal)
|
||||
|
||||
public:
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal,
|
||||
bool isWildcard = false, AstNodeExpr* iffp = nullptr)
|
||||
bool isWildcard = false)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_binsType{isIllegal ? VCoverBinsType::BINS_ILLEGAL
|
||||
@@ -1057,14 +1065,12 @@ public:
|
||||
: VCoverBinsType::BINS_USER))}
|
||||
, m_isWildcard{isWildcard} {
|
||||
addRangesp(rangesp);
|
||||
this->iffp(iffp);
|
||||
}
|
||||
// Constructor for automatic bins
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep,
|
||||
VCoverBinsType type = VCoverBinsType::BINS_AUTO)
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_binsType{type}
|
||||
, m_binsType{VCoverBinsType::BINS_AUTO}
|
||||
, m_isArray{true} {
|
||||
this->arraySizep(arraySizep);
|
||||
}
|
||||
@@ -1092,138 +1098,21 @@ public:
|
||||
bool isArray() const { return m_isArray; }
|
||||
void isArray(bool flag) { m_isArray = flag; }
|
||||
};
|
||||
class AstCoverBinsof final : public AstNode {
|
||||
// A binsof selection of a coverpoint or one of its named bins
|
||||
// @astgen op1 := pointp : AstCoverpointRef
|
||||
// @astgen op2 := rangesp : List[AstNode] // Optional intersect value ranges
|
||||
string m_name; // Selected bin name, or empty for all bins of the coverpoint
|
||||
const bool m_isNegated; // Complement the selection within the cross product
|
||||
|
||||
public:
|
||||
AstCoverBinsof(FileLine* fl, AstCoverpointRef* pointp, bool isNegated = false,
|
||||
AstNode* rangesp = nullptr)
|
||||
: ASTGEN_SUPER_CoverBinsof(fl)
|
||||
, m_isNegated{isNegated} {
|
||||
this->pointp(pointp);
|
||||
addRangesp(rangesp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverBinsof;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
bool isNegated() const { return m_isNegated; }
|
||||
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
|
||||
const AstCoverBinsof* const asamep = VN_DBG_AS(samep, CoverBinsof);
|
||||
return m_name == asamep->m_name && m_isNegated == asamep->m_isNegated;
|
||||
} // LCOV_EXCL_STOP
|
||||
};
|
||||
class AstCoverCrossBin final : public AstNode {
|
||||
// A named cross bin and its selection expression
|
||||
// @astgen op1 := selectp : Optional[AstNode] // Null for unsupported selections
|
||||
// @astgen op2 := iffp : Optional[AstNodeExpr]
|
||||
const string m_name; // Declared cross bin name
|
||||
// dist-ast-dump-suppress // Bin kind is shown by verilogKwd() in emitted Verilog.
|
||||
const VCoverBinsType m_binsType; // Normal, ignore, or illegal bin
|
||||
|
||||
public:
|
||||
AstCoverCrossBin(FileLine* fl, const string& name, AstNode* selectp, AstNodeExpr* iffp,
|
||||
VCoverBinsType binsType = VCoverBinsType::BINS_USER)
|
||||
: ASTGEN_SUPER_CoverCrossBin(fl)
|
||||
, m_name{name}
|
||||
, m_binsType{binsType} {
|
||||
this->selectp(selectp);
|
||||
this->iffp(iffp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverCrossBin;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
string verilogKwd() const override;
|
||||
VCoverBinsType binsType() const { return m_binsType; }
|
||||
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
|
||||
const AstCoverCrossBin* const asamep = VN_DBG_AS(samep, CoverCrossBin);
|
||||
return m_name == asamep->m_name && m_binsType.m_e == asamep->m_binsType.m_e;
|
||||
} // LCOV_EXCL_STOP
|
||||
};
|
||||
class AstCoverCrossRef final : public AstNode {
|
||||
// The complete bin-tuple space of the enclosing cross
|
||||
const string m_name; // Referenced cross name
|
||||
|
||||
public:
|
||||
AstCoverCrossRef(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_CoverCrossRef(fl)
|
||||
, m_name{name} {}
|
||||
ASTGEN_MEMBERS_AstCoverCrossRef;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
|
||||
return m_name == VN_DBG_AS(samep, CoverCrossRef)->m_name;
|
||||
} // LCOV_EXCL_STOP
|
||||
};
|
||||
class AstCoverCrossSelect final : public AstNode {
|
||||
// Intersection or union of two cross-bin selections
|
||||
// @astgen op1 := lhsp : Optional[AstNode] // Null for an unsupported selection
|
||||
// @astgen op2 := rhsp : Optional[AstNode] // Null for an unsupported selection
|
||||
const bool m_isOr; // Union (||), rather than intersection (&&)
|
||||
|
||||
public:
|
||||
AstCoverCrossSelect(FileLine* fl, AstNode* lhsp, AstNode* rhsp, bool isOr)
|
||||
: ASTGEN_SUPER_CoverCrossSelect(fl)
|
||||
, m_isOr{isOr} {
|
||||
this->lhsp(lhsp);
|
||||
this->rhsp(rhsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverCrossSelect;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool isOr() const { return m_isOr; }
|
||||
string verilogKwd() const override { return isOr() ? "||" : "&&"; }
|
||||
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
|
||||
return m_isOr == VN_DBG_AS(samep, CoverCrossSelect)->m_isOr;
|
||||
} // LCOV_EXCL_STOP
|
||||
};
|
||||
class AstCoverOption final : public AstNode {
|
||||
// Coverage-option assignment
|
||||
// @astgen op1 := valuep : AstNodeExpr
|
||||
const VCoverOptionType m_optType; // Option being assigned
|
||||
const bool m_typeOption; // type_option (vs option)
|
||||
|
||||
public:
|
||||
AstCoverOption(FileLine* fl, bool typeOption, VCoverOptionType optType, AstNodeExpr* valuep)
|
||||
AstCoverOption(FileLine* fl, VCoverOptionType optType, AstNodeExpr* valuep)
|
||||
: ASTGEN_SUPER_CoverOption(fl)
|
||||
, m_optType{optType}
|
||||
, m_typeOption{typeOption} {
|
||||
, m_optType{optType} {
|
||||
this->valuep(valuep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverOption;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
VCoverOptionType optType() const { return m_optType; }
|
||||
bool typeOption() const { return m_typeOption; }
|
||||
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
|
||||
const AstCoverOption* const asamep = VN_DBG_AS(samep, CoverOption);
|
||||
return m_optType.m_e == asamep->m_optType.m_e && m_typeOption == asamep->m_typeOption;
|
||||
} // LCOV_EXCL_STOP
|
||||
};
|
||||
class AstCoverToggle final : public AstNode {
|
||||
// Toggle analysis of given signal
|
||||
// Parents: MODULE
|
||||
// @astgen op1 := incp : AstCoverInc
|
||||
// @astgen op2 := origp : AstNodeExpr
|
||||
// @astgen op3 := changep : AstNodeExpr
|
||||
public:
|
||||
AstCoverToggle(FileLine* fl, AstCoverInc* incp, AstNodeExpr* origp, AstNodeExpr* changep)
|
||||
: ASTGEN_SUPER_CoverToggle(fl) {
|
||||
this->incp(incp);
|
||||
this->origp(origp);
|
||||
this->changep(changep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverToggle;
|
||||
int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return true; }
|
||||
bool isOutputter() override {
|
||||
return false; // Though the AstCoverInc under this is an outputter
|
||||
}
|
||||
};
|
||||
class AstCoverTransItem final : public AstNode {
|
||||
// Represents a single transition item: value or value[*N] or value[->N] or value[=N]
|
||||
@@ -1254,20 +1143,6 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
class AstCoverWith final : public AstNode {
|
||||
// The values of a coverpoint bin for which a 'with' filter is true (IEEE 1800-2023
|
||||
// 19.5.1.1), the only range list element of its AstCoverBin
|
||||
// @astgen op1 := subp : List[AstNode] // Range list, or AstCoverpointRef for all values
|
||||
// @astgen op2 := filterp : AstNodeExpr
|
||||
// @astgen op3 := itemp : Optional[AstVar] // Candidate value 'item', from V3LinkParse
|
||||
public:
|
||||
AstCoverWith(FileLine* fl, AstNode* subp, AstNodeExpr* filterp)
|
||||
: ASTGEN_SUPER_CoverWith(fl) {
|
||||
addSubp(subp);
|
||||
this->filterp(filterp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverWith;
|
||||
};
|
||||
class AstCovergroup final : public AstNode {
|
||||
// Represents a covergroup declaration. V3LinkParse transforms this
|
||||
// into an AstClass with isCovergroup==true and attaches the clocking
|
||||
@@ -1383,8 +1258,6 @@ public:
|
||||
BROKEN_RTN(!fmtp());
|
||||
return nullptr;
|
||||
}
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
string verilogKwd() const override { return "$"s + string{displayType().ascii()}; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
@@ -1447,21 +1320,13 @@ public:
|
||||
};
|
||||
class AstIntfRef final : public AstNode {
|
||||
// An interface reference
|
||||
string m_name; // Hierarchical path of the reference
|
||||
string m_baseName; // Final component of m_name, i.e. the reference port name
|
||||
string m_modportName; // "" = no modport, else name of the modport referenced
|
||||
string m_name; // Name of the reference
|
||||
public:
|
||||
AstIntfRef(FileLine* fl, const string& name, const string& baseName, const string& modportName)
|
||||
AstIntfRef(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_IntfRef(fl)
|
||||
, m_name{name}
|
||||
, m_baseName{baseName}
|
||||
, m_modportName{modportName} {}
|
||||
ASTGEN_MEMBERS_AstIntfRef;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
, m_name{name} {}
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
string baseName() const { return m_baseName; }
|
||||
string modportName() const { return m_modportName; }
|
||||
ASTGEN_MEMBERS_AstIntfRef;
|
||||
};
|
||||
class AstLibrary final : public AstNode {
|
||||
// Parents: NETLIST
|
||||
@@ -1575,7 +1440,7 @@ class AstNetlist final : public AstNode {
|
||||
// @astgen op3 := miscsp : List[AstNode]
|
||||
//
|
||||
// @astgen ptr := m_typeTablep : AstTypeTable // Reference to type table, for faster lookup
|
||||
// @astgen ptr := m_constPoolPkgp : AstPackage // Constant pool package
|
||||
// @astgen ptr := m_constPoolp : AstConstPool // Reference to constant pool, for faster lookup
|
||||
// @astgen ptr := m_dollarUnitPkgp : Optional[AstPackage] // $unit
|
||||
// @astgen ptr := m_stdPackagep : Optional[AstPackage] // SystemVerilog std package
|
||||
// @astgen ptr := m_stdPackageProcessp : Optional[AstClass] // SystemVerilog std process class
|
||||
@@ -1596,8 +1461,6 @@ class AstNetlist final : public AstNode {
|
||||
// AstConst itself, as AstConst is a very common node and only a small fraction carry this
|
||||
// name.
|
||||
std::unordered_map<const AstConst*, string> m_constOrigParamNames;
|
||||
// Module each node is in, only good while the tree holds still
|
||||
std::unordered_map<const AstNode*, const AstNodeModule*> m_containingModules;
|
||||
// The model's evaluation entry point functions
|
||||
std::array<AstCFunc*, VEval::_ENUM_END> m_evalFuncps{};
|
||||
// The trigger dump function of each region if exists, otherwise nullptr
|
||||
@@ -1619,16 +1482,12 @@ public:
|
||||
return modulesp(); // First one in the list, for now
|
||||
}
|
||||
AstTypeTable* typeTablep() { return m_typeTablep; }
|
||||
AstConstPool* constPoolp() { return m_constPoolp; }
|
||||
string astConstOrigParamName(const AstConst* nodep) const;
|
||||
void astConstOrigParamName(const AstConst* nodep, const string& name);
|
||||
void astConstOrigParamNameErase(const AstConst* nodep);
|
||||
// Find the module a node is in, remembering what it passed on the way.
|
||||
const AstNodeModule* containingModule(const AstNode* nodep);
|
||||
// Forget remembered modules, as the tree has moved.
|
||||
void clearContainingModules() { m_containingModules.clear(); }
|
||||
AstPackage* constPoolPkgp() const { return m_constPoolPkgp; }
|
||||
AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; }
|
||||
void dollarUnitPkgp(AstPackage* const packagep) { m_dollarUnitPkgp = packagep; }
|
||||
AstPackage* dollarUnitPkgAddp();
|
||||
AstCFunc* evalFuncp(VEval eval) const { return m_evalFuncps[eval]; }
|
||||
void evalFuncp(VEval eval, AstCFunc* funcp) { m_evalFuncps[eval] = funcp; }
|
||||
AstCFunc* dumpTriggersFuncp(VEval eval) const { return m_dumpTriggersFuncps[eval]; }
|
||||
@@ -1899,9 +1758,7 @@ public:
|
||||
AstNodeModule* modp() const { return m_modp; }
|
||||
//
|
||||
AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; }
|
||||
void aboveScopep(AstScope* nodep) { m_aboveScopep = nodep; }
|
||||
AstCell* aboveCellp() const { return m_aboveCellp; }
|
||||
void aboveCellp(AstCell* nodep) { m_aboveCellp = nodep; }
|
||||
bool isTop() const VL_MT_SAFE { return aboveScopep() == nullptr; } // At top of hierarchy
|
||||
// Create new MODULETEMP variable under this scope
|
||||
AstVarScope* createTemp(const string& name, unsigned width);
|
||||
@@ -2058,11 +1915,8 @@ public:
|
||||
class AstTextBlock final : public AstNode {
|
||||
// Text block emitted into output, with some arbitrary nodes interspersed
|
||||
// @astgen op1 := nodesp : List[AstNode] // Nodes to print
|
||||
// dist-ast-dump-suppress // Omitting text blocks due to verbosity
|
||||
const std::string m_prefix; // Prefix to print before first element in 'nodesp'
|
||||
// dist-ast-dump-suppress // Omitting text blocks due to verbosity
|
||||
const std::string m_separator; // Separator to print between each element in 'nodesp'
|
||||
// dist-ast-dump-suppress // Omitting text blocks due to verbosity
|
||||
const std::string m_suffix; // Suffix to pring after last element in 'nodesp'
|
||||
public:
|
||||
explicit AstTextBlock(FileLine* fl, //
|
||||
@@ -2111,7 +1965,6 @@ class AstTypeTable final : public AstNode {
|
||||
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
|
||||
//
|
||||
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
|
||||
// dist-ast-dump-suppress // Link to other nodes
|
||||
DetailedMap m_detailedMap;
|
||||
|
||||
public:
|
||||
@@ -2140,10 +1993,10 @@ class AstTypedef final : public AstNode {
|
||||
// @astgen op1 := childDTypep : Optional[AstNodeDType]
|
||||
// @astgen op4 := attrsp : List[AstNode] // Attributes during early parse
|
||||
|
||||
string m_name; // Name of the typedef
|
||||
string m_name;
|
||||
string m_tag; // Holds the string of the verilator tag -- used in JSON output.
|
||||
uint32_t m_declTokenNum; // Declaration token number
|
||||
bool m_attrPublic = false; // Marked with public; keep even if unused
|
||||
bool m_attrPublic = false;
|
||||
bool m_isHideLocal : 1; // Verilog local
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
bool m_isUnderClass : 1; // Underneath class
|
||||
@@ -2309,6 +2162,7 @@ class AstVar final : public AstNode {
|
||||
bool m_attrSFormat : 1; // User sformat attribute
|
||||
bool m_attrSplitVar : 1; // declared with split_var metacomment
|
||||
bool m_attrFsmState : 1; // declared with fsm_state metacomment
|
||||
bool m_attrFsmRegisterWrapper : 1; // connected to an fsm_register_wrapper instance
|
||||
bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment
|
||||
bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment
|
||||
bool m_constPoolEntry : 1; // Constant pool variable
|
||||
@@ -2324,8 +2178,6 @@ class AstVar final : public AstNode {
|
||||
bool m_isPulldown : 1; // Tri0
|
||||
bool m_isPullup : 1; // Tri1
|
||||
bool m_isIfaceParent : 1; // dtype is reference to interface present in this module
|
||||
bool m_isIfaceArraySplit : 1; // Interface array that was split into element variables by
|
||||
// V3Param, until V3Width removes it
|
||||
bool m_isInternal : 1; // Internal state, don't add to method pinter
|
||||
bool m_isIfaceParam : 1; // Parameter belongs to an interface/modport
|
||||
bool m_isDpiOpenArray : 1; // DPI import open array
|
||||
@@ -2375,6 +2227,7 @@ class AstVar final : public AstNode {
|
||||
m_attrSFormat = false;
|
||||
m_attrSplitVar = false;
|
||||
m_attrFsmState = false;
|
||||
m_attrFsmRegisterWrapper = false;
|
||||
m_attrFsmResetArc = false;
|
||||
m_attrFsmArcInclCond = false;
|
||||
m_constPoolEntry = false;
|
||||
@@ -2390,7 +2243,6 @@ class AstVar final : public AstNode {
|
||||
m_isPulldown = false;
|
||||
m_isPullup = false;
|
||||
m_isIfaceParent = false;
|
||||
m_isIfaceArraySplit = false;
|
||||
m_isInternal = false;
|
||||
m_isIfaceParam = false;
|
||||
m_isDpiOpenArray = false;
|
||||
@@ -2506,7 +2358,7 @@ public:
|
||||
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
|
||||
bool asRef = false, bool constRef = false) const;
|
||||
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
|
||||
string vlEnumDir(bool forMember = false) const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
||||
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
||||
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
|
||||
void combineType(VVarType type);
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
@@ -2526,6 +2378,7 @@ public:
|
||||
void attrSFormat(bool flag) { m_attrSFormat = flag; }
|
||||
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
|
||||
void attrFsmState(bool flag) { m_attrFsmState = flag; }
|
||||
void attrFsmRegisterWrapper(bool flag) { m_attrFsmRegisterWrapper = flag; }
|
||||
void attrFsmResetArc(bool flag) { m_attrFsmResetArc = flag; }
|
||||
void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; }
|
||||
bool constPoolEntry() const { return m_constPoolEntry; }
|
||||
@@ -2552,7 +2405,6 @@ public:
|
||||
void isContinuously(bool flag) { m_isContinuously = flag; }
|
||||
void isStatic(bool flag) { m_isStatic = flag; }
|
||||
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
|
||||
void isIfaceArraySplit(bool flag) { m_isIfaceArraySplit = flag; }
|
||||
void isInternal(bool flag) { m_isInternal = flag; }
|
||||
void isIfaceParam(bool flag) { m_isIfaceParam = flag; }
|
||||
void funcLocal(bool flag) {
|
||||
@@ -2640,7 +2492,6 @@ public:
|
||||
m_varType = VVarType::IFACEREF;
|
||||
}
|
||||
bool isIfaceParent() const { return m_isIfaceParent; }
|
||||
bool isIfaceArraySplit() const { return m_isIfaceArraySplit; }
|
||||
bool isInternal() const { return m_isInternal; }
|
||||
bool isIfaceParam() const { return m_isIfaceParam; }
|
||||
bool isSignal() const { return varType().isSignal(); }
|
||||
@@ -2696,6 +2547,7 @@ public:
|
||||
bool attrSFormat() const { return m_attrSFormat; }
|
||||
bool attrSplitVar() const { return m_attrSplitVar; }
|
||||
bool attrFsmState() const { return m_attrFsmState; }
|
||||
bool attrFsmRegisterWrapper() const { return m_attrFsmRegisterWrapper; }
|
||||
bool attrFsmResetArc() const { return m_attrFsmResetArc; }
|
||||
bool attrFsmArcInclCond() const { return m_attrFsmArcInclCond; }
|
||||
AstIface* sensIfacep() const { return m_sensIfacep; }
|
||||
@@ -2719,6 +2571,13 @@ public:
|
||||
lifetime(fromp->lifetime());
|
||||
}
|
||||
void combineType(const AstVar* otherp);
|
||||
void inlineAttrReset(const string& name) {
|
||||
if (direction() == VDirection::INOUT && varType() == VVarType::WIRE) {
|
||||
m_varType = VVarType::TRIWIRE;
|
||||
}
|
||||
m_direction = VDirection::NONE;
|
||||
m_name = name;
|
||||
}
|
||||
bool needsCReset() const {
|
||||
return !isIfaceParent() && !isIfaceRef() && !noReset() && !isParam() && !isStatementTemp()
|
||||
&& !noCReset() && !(basicp() && basicp()->isEvent());
|
||||
@@ -2761,7 +2620,6 @@ public:
|
||||
bool sameNode(const AstNode* samep) const override;
|
||||
bool hasDType() const override VL_MT_SAFE { return true; }
|
||||
AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable
|
||||
void varp(AstVar* nodep) { m_varp = nodep; }
|
||||
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
|
||||
void scopep(AstScope* nodep) { m_scopep = nodep; }
|
||||
bool isTrace() const { return m_trace; }
|
||||
@@ -2775,10 +2633,10 @@ class AstCoverOtherDecl final : public AstNodeCoverDecl {
|
||||
// Coverage analysis point declaration
|
||||
// Used for other than toggle types of coverage
|
||||
string m_linescov;
|
||||
string m_fsmVar; // FSM state variable name
|
||||
string m_fsmFrom; // FSM source state label
|
||||
string m_fsmTo; // FSM destination state label
|
||||
string m_fsmTag; // FSM arc kind tag (e.g. reset, reset_include, default)
|
||||
string m_fsmVar;
|
||||
string m_fsmFrom;
|
||||
string m_fsmTo;
|
||||
string m_fsmTag;
|
||||
int m_offset; // Offset column numbers to uniq-ify IFs
|
||||
public:
|
||||
AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment,
|
||||
@@ -2804,7 +2662,7 @@ public:
|
||||
const string& fsmTag() const { return m_fsmTag; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl);
|
||||
return Super::sameNode(samep) && linescov() == asamep->linescov();
|
||||
return AstNodeCoverDecl::sameNode(samep) && linescov() == asamep->linescov();
|
||||
}
|
||||
};
|
||||
class AstCoverToggleDecl final : public AstNodeCoverDecl {
|
||||
@@ -2826,7 +2684,7 @@ public:
|
||||
const VNumRange& range() const { return m_range; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl);
|
||||
return Super::sameNode(samep) && range() == asamep->range();
|
||||
return AstNodeCoverDecl::sameNode(samep) && range() == asamep->range();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2944,8 +2802,8 @@ public:
|
||||
class AstCoverCross final : public AstNodeFuncCovItem {
|
||||
// @astgen op1 := itemsp : List[AstCoverpointRef]
|
||||
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
|
||||
// @astgen op3 := binsp : List[AstNode] // Parse: mixed cross bins/options;
|
||||
// // post-LinkParse: AstCoverCrossBin only
|
||||
// @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items;
|
||||
// // post-LinkParse: empty
|
||||
// @astgen op4 := iffp : Optional[AstNodeExpr] // Conditional sampling guard
|
||||
public:
|
||||
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp,
|
||||
@@ -3057,8 +2915,6 @@ class AstClass final : public AstNodeModule {
|
||||
// @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class
|
||||
uint32_t m_declTokenNum; // Declaration token number
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
bool m_hasRandVarsUpdate = false; // Has updateRandVars method,
|
||||
// which updates pointers to rand variables in clone()
|
||||
bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?)
|
||||
bool m_extended = false; // Is extension or extended by other classes
|
||||
bool m_interfaceClass = false; // Interface class
|
||||
@@ -3087,8 +2943,6 @@ public:
|
||||
void covergroupEnclosingClassp(AstClass* classp) { m_covergroupEnclosingClassp = classp; }
|
||||
AstNode* membersp() const VL_MT_STABLE { return stmtsp(); }
|
||||
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
|
||||
bool hasRandVarsUpdate() const { return m_hasRandVarsUpdate; }
|
||||
void hasRandVarsUpdate(bool flag) { m_hasRandVarsUpdate = flag; }
|
||||
bool isCovergroup() const { return m_covergroup; }
|
||||
void isCovergroup(bool flag) { m_covergroup = flag; }
|
||||
bool isExtended() const { return m_extended; }
|
||||
@@ -3177,11 +3031,9 @@ public:
|
||||
AstIface(FileLine* fl, const string& name, const string& libname)
|
||||
: ASTGEN_SUPER_Iface(fl, name, libname) {}
|
||||
ASTGEN_MEMBERS_AstIface;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string verilogKwd() const override { return "interface"; }
|
||||
// Interfaces have `timescale applicability but lots of code seems to
|
||||
// get false warnings if we enable this
|
||||
string verilogKwd() const override { return "interface"; }
|
||||
bool timescaleMatters() const override { return false; }
|
||||
bool hasVirtualRef() const { return m_hasVirtualRef; }
|
||||
void setHasVirtualRef() { m_hasVirtualRef = true; }
|
||||
@@ -3235,7 +3087,9 @@ public:
|
||||
: ASTGEN_SUPER_Package(fl, name, libname) {}
|
||||
ASTGEN_MEMBERS_AstPackage;
|
||||
string verilogKwd() const override { return "package"; }
|
||||
bool timescaleMatters() const override { return !isDollarUnit() && !isConstPool(); }
|
||||
bool timescaleMatters() const override { return !isDollarUnit(); }
|
||||
static string dollarUnitName() { return AstNode::encodeName("$unit"); }
|
||||
bool isDollarUnit() const { return name() == dollarUnitName(); }
|
||||
};
|
||||
class AstPrimitive final : public AstNodeModule {
|
||||
// A primitive declaration
|
||||
|
||||
+41
-13
@@ -413,16 +413,16 @@ class AstCase final : public AstNodeStmt {
|
||||
// @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression
|
||||
// @astgen op2 := itemsp : List[AstCaseItem]
|
||||
// @astgen op3 := notParallelp : List[AstNode] // assertion code for non-full case's
|
||||
VCaseType m_caseType; // 0=case, 1=casex, 2=casez
|
||||
VCaseType m_casex; // 0=case, 1=casex, 2=casez
|
||||
bool m_fullPragma = false; // Synthesis full_case
|
||||
bool m_parallelPragma = false; // Synthesis parallel_case
|
||||
bool m_uniquePragma = false; // unique case
|
||||
bool m_unique0Pragma = false; // unique0 case
|
||||
bool m_priorityPragma = false; // priority case
|
||||
public:
|
||||
AstCase(FileLine* fl, VCaseType caseType, AstNodeExpr* exprp, AstCaseItem* itemsp)
|
||||
AstCase(FileLine* fl, VCaseType casex, AstNodeExpr* exprp, AstCaseItem* itemsp)
|
||||
: ASTGEN_SUPER_Case(fl)
|
||||
, m_caseType{caseType} {
|
||||
, m_casex{casex} {
|
||||
this->exprp(exprp);
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
@@ -432,15 +432,15 @@ public:
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
return m_caseType == VN_DBG_AS(samep, Case)->m_caseType;
|
||||
return m_casex == VN_DBG_AS(samep, Case)->m_casex;
|
||||
}
|
||||
bool casex() const { return m_caseType == VCaseType::CT_CASEX; }
|
||||
bool casez() const { return m_caseType == VCaseType::CT_CASEZ; }
|
||||
bool caseInside() const { return m_caseType == VCaseType::CT_CASEINSIDE; }
|
||||
bool caseMatches() const { return m_caseType == VCaseType::CT_CASEMATCHES; }
|
||||
bool caseSimple() const { return m_caseType == VCaseType::CT_CASE; }
|
||||
void caseInsideSet() { m_caseType = VCaseType::CT_CASEINSIDE; }
|
||||
void caseMatchesSet() { m_caseType = VCaseType::CT_CASEMATCHES; }
|
||||
bool casex() const { return m_casex == VCaseType::CT_CASEX; }
|
||||
bool casez() const { return m_casex == VCaseType::CT_CASEZ; }
|
||||
bool caseInside() const { return m_casex == VCaseType::CT_CASEINSIDE; }
|
||||
bool caseMatches() const { return m_casex == VCaseType::CT_CASEMATCHES; }
|
||||
bool caseSimple() const { return m_casex == VCaseType::CT_CASE; }
|
||||
void caseInsideSet() { m_casex = VCaseType::CT_CASEINSIDE; }
|
||||
void caseMatchesSet() { m_casex = VCaseType::CT_CASEMATCHES; }
|
||||
bool fullPragma() const { return m_fullPragma; }
|
||||
void fullPragma(bool flag) { m_fullPragma = flag; }
|
||||
bool parallelPragma() const { return m_parallelPragma; }
|
||||
@@ -549,7 +549,29 @@ public:
|
||||
bool isOutputter() override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
AstNodeCoverDecl* declp() const { return m_declp; } // Where defined
|
||||
void declp(AstNodeCoverDecl* nodep) { m_declp = nodep; }
|
||||
};
|
||||
class AstCoverToggle final : public AstNodeStmt {
|
||||
// Toggle analysis of given signal
|
||||
// Parents: MODULE
|
||||
// @astgen op1 := incp : AstCoverInc
|
||||
// @astgen op2 := origp : AstNodeExpr
|
||||
// @astgen op3 := changep : AstNodeExpr
|
||||
public:
|
||||
AstCoverToggle(FileLine* fl, AstCoverInc* incp, AstNodeExpr* origp, AstNodeExpr* changep)
|
||||
: ASTGEN_SUPER_CoverToggle(fl) {
|
||||
this->incp(incp);
|
||||
this->origp(origp);
|
||||
this->changep(changep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverToggle;
|
||||
int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return true; }
|
||||
bool isOutputter() override {
|
||||
return false; // Though the AstCoverInc under this is an outputter
|
||||
}
|
||||
// but isPure() true
|
||||
};
|
||||
class AstDeassign final : public AstNodeStmt {
|
||||
// Procedural 'deassign' statement
|
||||
@@ -1199,6 +1221,13 @@ public:
|
||||
ASTGEN_MEMBERS_AstSetuphold;
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstSplitPlaceholder final : public AstNodeStmt {
|
||||
public:
|
||||
// Dummy node used within V3Split; never exists outside of V3Split.
|
||||
explicit AstSplitPlaceholder(FileLine* fl)
|
||||
: ASTGEN_SUPER_SplitPlaceholder(fl) {}
|
||||
ASTGEN_MEMBERS_AstSplitPlaceholder;
|
||||
};
|
||||
class AstStackTraceT final : public AstNodeStmt {
|
||||
// $stacktrace used as task
|
||||
public:
|
||||
@@ -1311,7 +1340,6 @@ class AstTraceDecl final : public AstNodeStmt {
|
||||
const VVarType m_varType; // Type of variable (for localparam vs. param)
|
||||
const VDirection m_declDirection; // Declared direction input/output etc
|
||||
const bool m_inDtypeFunc; // Trace decl inside type init function
|
||||
// dist-ast-dump-suppress // Not stable and of low value
|
||||
int m_codeInc{0}; // Code increment for type
|
||||
public:
|
||||
AstTraceDecl(FileLine* fl, const string& showname,
|
||||
|
||||
+3727
-3667
File diff suppressed because it is too large
Load Diff
@@ -26,8 +26,6 @@
|
||||
|
||||
#include "V3Broken.h"
|
||||
|
||||
#include "V3ConstPool.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
@@ -229,45 +227,6 @@ private:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static void checkArgRefs(AstNodeExpr* nodep, const char* descrp, AstNodeExpr* argsp) {
|
||||
if (!std::strcmp(descrp, "TODO")) return; // Skip if not yet checked
|
||||
|
||||
// Check each argument
|
||||
const char* dp = descrp;
|
||||
for (AstNodeExpr* argp = argsp; argp; argp = VN_AS(argp->nextp(), NodeExpr)) {
|
||||
if (argp->fileline()->erroringOn()) return; // Intentionally skip all checks
|
||||
const AstNodeExpr* const lvalp = argp->getVAccessTargetRecurse();
|
||||
const VAccess access
|
||||
= lvalp ? lvalp->getVAccessRecurse() // Since we call it on a result of above call
|
||||
// there should be no recursion at all
|
||||
: VAccess{VAccess::READ};
|
||||
if (dp[0] == '+') --dp; // Repeats the entry before it
|
||||
switch (dp[0]) {
|
||||
case 'r':
|
||||
UASSERT_OBJ(access.isReadOnly(), argp,
|
||||
"Input argument of library call is not a read-only expression");
|
||||
break;
|
||||
case 'w':
|
||||
UASSERT_OBJ(lvalp, argp, //
|
||||
"Output argument of library call is not a valid C++ LValue");
|
||||
UASSERT_OBJ(access.isWriteOnly(), argp,
|
||||
"Output argument of library call is not write-only");
|
||||
break;
|
||||
case 'm':
|
||||
UASSERT_OBJ(lvalp, argp, //
|
||||
"Inout argument of library call is not a valid C++ LValue");
|
||||
UASSERT_OBJ(access.isRW(), argp,
|
||||
"Inout argument of library call is not read-write");
|
||||
break;
|
||||
default:
|
||||
UASSERT_OBJ(dp[0], argp, "Unexpected trailing arguments to library call");
|
||||
break; // LCOV_EXCL_LINE
|
||||
}
|
||||
++dp;
|
||||
}
|
||||
UASSERT_OBJ(!dp[0] || dp[0] == '+', nodep, "Insufficient arguments to library call");
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
processEnter(nodep);
|
||||
@@ -332,7 +291,6 @@ private:
|
||||
void visit(AstCMethodHard* nodep) override {
|
||||
++m_nCalls;
|
||||
processAndIterate(nodep);
|
||||
checkArgRefs(nodep, nodep->method().args(), nodep->pinsp());
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
++m_nCalls;
|
||||
@@ -434,10 +392,6 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
|
||||
// Check every node in tree
|
||||
const BrokenCheckVisitor cvisitor{nodep};
|
||||
|
||||
// Check the constant pool lookup cache refers only to nodes in the tree
|
||||
const char* const whyp = V3ConstPool::broken();
|
||||
UASSERT_OBJ(!whyp, nodep, "Broken constant pool cache: " << whyp);
|
||||
|
||||
s_allocTable.checkForLeaks();
|
||||
s_linkableTable.clear();
|
||||
s_brokenCntGlobal.inc();
|
||||
|
||||
+3
-3
@@ -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:
|
||||
|
||||
@@ -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
@@ -38,7 +38,6 @@
|
||||
|
||||
#include "V3Case.h"
|
||||
|
||||
#include "V3ConstPool.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
@@ -739,15 +738,18 @@ class CaseVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
// Create the lookup table reference, in the constant pool unless using an inline table
|
||||
AstNodeExpr* const tableRefp = [&]() -> AstNodeExpr* {
|
||||
if (isTinyTable) return tablep;
|
||||
AstVarRef* const refp = V3ConstPool::findConst(tablep);
|
||||
// Create the table in the constant pool, unless using an inline table
|
||||
AstVarScope* const tableVscp = [&]() -> AstVarScope* {
|
||||
if (isTinyTable) return nullptr;
|
||||
AstVarScope* vscp = v3Global.rootp()->constPoolp()->findConst(tablep, true);
|
||||
VL_DO_DANGLING(tablep->deleteTree(), tablep); // findConst clones
|
||||
return refp;
|
||||
return vscp;
|
||||
}();
|
||||
|
||||
// Create the lookup index
|
||||
// Create the lookup table reference and index
|
||||
AstNodeExpr* const tableRefp
|
||||
= tableVscp ? static_cast<AstNodeExpr*>(new AstVarRef{flp, tableVscp, VAccess::READ})
|
||||
: static_cast<AstNodeExpr*>(tablep);
|
||||
AstNodeExpr* const caseExprp
|
||||
= new AstExtend{flp, nodep->exprp()->cloneTreePure(false), 32};
|
||||
AstNodeExpr* const scalep = new AstConst{flp, entryWidth};
|
||||
@@ -853,14 +855,15 @@ class CaseVisitor final : public VNVisitor {
|
||||
}
|
||||
|
||||
// Create the tables
|
||||
AstVarRef* const matchRefp = V3ConstPool::findConst(matchp);
|
||||
AstVarRef* const tableRefp = V3ConstPool::findTable(tablep);
|
||||
AstVarScope* const matchVscp = v3Global.rootp()->constPoolp()->findConst(matchp, true);
|
||||
AstVarScope* const tableVscp = v3Global.rootp()->constPoolp()->findTable(tablep);
|
||||
VL_DO_DANGLING(matchp->deleteTree(), matchp);
|
||||
VL_DO_DANGLING(tablep->deleteTree(), tablep);
|
||||
|
||||
// AstMatchMasked produces the index of the matching entry
|
||||
AstNodeExpr* const tableRefp = new AstVarRef{flp, tableVscp, VAccess::READ};
|
||||
AstNodeExpr* const caseExprp = nodep->exprp()->cloneTreePure(false);
|
||||
AstMatchMasked* const indexp = new AstMatchMasked{flp, caseExprp, matchRefp};
|
||||
AstMatchMasked* const indexp = new AstMatchMasked{flp, caseExprp, matchVscp};
|
||||
AstNodeExpr* const entryp = new AstArraySel{flp, tableRefp, indexp};
|
||||
|
||||
return connectDecoderOutputs(nodep, entryp, "__VcaseDecoderOut");
|
||||
@@ -1001,22 +1004,11 @@ class CaseVisitor final : public VNVisitor {
|
||||
itemp->addCondsp(newCondp);
|
||||
}
|
||||
|
||||
// If there was no default, make one up, this greatly simplifies below code
|
||||
// If there was no default, add a empty one, this greatly simplifies below code
|
||||
// and constant propagation will just eliminate it for us later.
|
||||
if (!hasDefault) {
|
||||
if (m_caseDetailsValid && m_caseDetails.exhaustive
|
||||
&& !m_caseDetails.exhaustiveOverEnumOnly) {
|
||||
// Case is proven exhaustive, so the last is reached only when its test holds,
|
||||
// make it unconditional
|
||||
AstCaseItem* const lastp = VN_AS(nodep->itemsp()->lastp(), CaseItem);
|
||||
pushDeletep(lastp->condsp()->unlinkFrBackWithNext());
|
||||
lastp->addCondsp(new AstConst{lastp->fileline(), AstConst::BitTrue{}});
|
||||
} else {
|
||||
// Otherwise needs an explicit empty default
|
||||
nodep->addItemsp(new AstCaseItem{
|
||||
nodep->fileline(), new AstConst{nodep->fileline(), AstConst::BitTrue{}},
|
||||
nullptr});
|
||||
}
|
||||
nodep->addItemsp(new AstCaseItem{
|
||||
nodep->fileline(), new AstConst{nodep->fileline(), AstConst::BitTrue{}}, nullptr});
|
||||
}
|
||||
|
||||
// Now build the IF statement tree
|
||||
|
||||
+12
-21
@@ -18,8 +18,7 @@
|
||||
// Each module:
|
||||
// For each expression, if it requires a clean operand,
|
||||
// and the operand is dirty, insert a CLEAN node.
|
||||
// Resize operands (but not variables, variable selects, or formatting metadata)
|
||||
// to C++ 32/64/wide types.
|
||||
// Resize operands (but not variables or variable selects) to C++ 32/64/wide types.
|
||||
// Copy all width() values to widthMin() so RANGE, etc can still see orig widths
|
||||
//
|
||||
//*************************************************************************
|
||||
@@ -85,7 +84,6 @@ class CleanVisitor final : public VNVisitor {
|
||||
|| VN_IS(nodep, ConsPackMember) //
|
||||
|| VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
||||
|| VN_IS(nodep, NodeSel) // Array selects should reflect variable widths
|
||||
|| VN_IS(nodep, SFormatArg) // Retain the logical argument type, not storage width
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|
||||
@@ -93,8 +91,6 @@ class CleanVisitor final : public VNVisitor {
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), StreamDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), CoverCrossDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), CoverpointDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
|
||||
} else {
|
||||
const AstNodeUOrStructDType* const dtypep
|
||||
@@ -142,12 +138,12 @@ class CleanVisitor final : public VNVisitor {
|
||||
computeCppWidth(nodep);
|
||||
if (!isClean(nodep)) insertClean(nodep);
|
||||
}
|
||||
void ensureCleanAndNext(AstNode* nodep) {
|
||||
void ensureCleanAndNext(AstNodeExpr* nodep) {
|
||||
// Editing list, careful looping!
|
||||
for (AstNode* argp = nodep; argp;) {
|
||||
AstNode* const nextp = argp->nextp();
|
||||
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
|
||||
argp = nextp;
|
||||
for (AstNodeExpr* exprp = nodep; exprp;) {
|
||||
AstNodeExpr* const nextp = VN_AS(exprp->nextp(), NodeExpr);
|
||||
ensureClean(exprp);
|
||||
exprp = nextp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +247,9 @@ class CleanVisitor final : public VNVisitor {
|
||||
setClean(nodep, false);
|
||||
// We always clean, as we don't trust those pesky users.
|
||||
if (!VN_IS(nodep->backp(), And)) insertClean(nodep);
|
||||
ensureCleanAndNext(nodep->nodesp());
|
||||
for (AstNode* argp = nodep->nodesp(); argp; argp = argp->nextp()) {
|
||||
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
|
||||
}
|
||||
}
|
||||
void visit(AstTraceDecl* nodep) override {} // Nothing to do here
|
||||
void visit(AstTraceInc* nodep) override {
|
||||
@@ -282,23 +280,16 @@ class CleanVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
ensureClean(nodep->condp());
|
||||
}
|
||||
void visit(AstSFormatArg* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->exprp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstSFormatF* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->exprsp());
|
||||
setClean(nodep, true); // generates a string, so not relevant
|
||||
}
|
||||
void visit(AstCStmt* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->nodesp());
|
||||
}
|
||||
void visit(AstCStmtUser* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->nodesp());
|
||||
for (AstNode* argp = nodep->nodesp(); argp; argp = argp->nextp()) {
|
||||
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
|
||||
}
|
||||
}
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
|
||||
+1
-1
@@ -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
@@ -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
|
||||
|
||||
@@ -318,6 +318,7 @@ class ToStringVisitor final : public VNVisitorConst {
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstConstPool*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
public:
|
||||
|
||||
+18
-26
@@ -110,12 +110,12 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
||||
|
||||
class LeafInfo final { // Leaf node (either AstConst or AstVarRef)
|
||||
// MEMBERS
|
||||
bool m_polarity = true; // Invert result due to NOT
|
||||
bool m_polarity = true;
|
||||
int m_lsb = 0; // LSB of actually used bit of m_refp->varp()
|
||||
int m_msb = 0; // MSB of actually used bit of m_refp->varp()
|
||||
int m_wordIdx = -1; // -1 means AstWordSel is not used.
|
||||
AstVarRef* m_refp = nullptr; // Leaf's variable reference
|
||||
const AstConst* m_constp = nullptr; // Leaf's constant
|
||||
AstVarRef* m_refp = nullptr;
|
||||
const AstConst* m_constp = nullptr;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -187,9 +187,9 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
||||
};
|
||||
|
||||
struct BitPolarityEntry final { // Found bit polarity during iterate()
|
||||
LeafInfo m_info; // Leaf (variable or constant) bit polarity was found on
|
||||
bool m_polarity = false; // Polarity the bit must have to match
|
||||
int m_bit = 0; // Bit index within the leaf that was tested
|
||||
LeafInfo m_info;
|
||||
bool m_polarity = false;
|
||||
int m_bit = 0;
|
||||
BitPolarityEntry(const LeafInfo& info, bool pol, int bit)
|
||||
: m_info{info}
|
||||
, m_polarity{pol}
|
||||
@@ -198,8 +198,8 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
||||
};
|
||||
|
||||
struct FrozenNodeInfo final { // Context when a frozen node is found
|
||||
bool m_polarity; // Polarity the frozen node must match
|
||||
int m_lsb; // LSB position of the frozen node
|
||||
bool m_polarity;
|
||||
int m_lsb;
|
||||
bool operator<(const FrozenNodeInfo& other) const {
|
||||
if (m_lsb != other.m_lsb) return m_lsb < other.m_lsb;
|
||||
return m_polarity < other.m_polarity;
|
||||
@@ -207,12 +207,12 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
||||
};
|
||||
|
||||
class Restorer final { // Restore the original state unless disableRestore() is called
|
||||
ConstBitOpTreeVisitor& m_visitor; // Visitor whose state is saved and restored
|
||||
const size_t m_polaritiesSize; // Saved m_visitor.m_bitPolarities size to truncate back to
|
||||
const size_t m_frozenSize; // Saved m_visitor.m_frozenNodes size to truncate back to
|
||||
const unsigned m_ops; // Saved m_visitor.m_ops to restore
|
||||
const bool m_polarity; // Saved m_visitor.m_polarity to restore
|
||||
bool m_restore = true; // Whether the destructor still needs to restore state
|
||||
ConstBitOpTreeVisitor& m_visitor;
|
||||
const size_t m_polaritiesSize;
|
||||
const size_t m_frozenSize;
|
||||
const unsigned m_ops;
|
||||
const bool m_polarity;
|
||||
bool m_restore = true;
|
||||
|
||||
public:
|
||||
explicit Restorer(ConstBitOpTreeVisitor& visitor)
|
||||
@@ -1463,12 +1463,11 @@ class ConstVisitor final : public VNVisitor {
|
||||
}
|
||||
if (const AstShiftR* const shiftp = VN_CAST(rhsp, ShiftR)) {
|
||||
// 'a >> S' forces the high S bits to zero. Check against the width of the shifted
|
||||
// operand, V3Expand can create shifts wider than their inputs. Must use width(),
|
||||
// not widthMin(), as bits above widthMin() are not guaranteed to be zero.
|
||||
// operand, V3Expand can create shifts wider than their inputs
|
||||
if (AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) {
|
||||
return scp->num().fitsInUInt()
|
||||
&& (lsb + scp->num().toUInt()
|
||||
>= static_cast<uint32_t>(shiftp->lhsp()->width()));
|
||||
>= static_cast<uint32_t>(shiftp->lhsp()->widthMin()));
|
||||
}
|
||||
}
|
||||
if (const AstMul* const mulp = VN_CAST(rhsp, Mul)) {
|
||||
@@ -3970,12 +3969,7 @@ class ConstVisitor final : public VNVisitor {
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return true;
|
||||
}
|
||||
void visit(AstSFormatArg* nodep) override {
|
||||
// Skip namep(): requiring its runtime lookup to be constant can reject valid
|
||||
// enum-valued constant-function calls in parameters. displayedEnum() resolves
|
||||
// the name from the folded exprp() value and enum dtype instead.
|
||||
iterateAndNextNull(nodep->exprp());
|
||||
}
|
||||
void visit(AstSFormatArg* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstSFormatF* nodep) override {
|
||||
// Substitute constants into displays. The main point of this is to
|
||||
// simplify assertion methodologies which call functions with display's.
|
||||
@@ -4039,9 +4033,7 @@ class ConstVisitor final : public VNVisitor {
|
||||
: VFormatAttr{};
|
||||
if (VN_IS(subargp, Const)) { // Convert it
|
||||
const string out
|
||||
= formatAttr.isEnum()
|
||||
? constNumV(subargp).displayedEnum(fargp, fmt)
|
||||
: constNumV(subargp).displayed(nodep, fmt, formatAttr);
|
||||
= constNumV(subargp).displayed(nodep, fmt, formatAttr);
|
||||
UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
|
||||
<< subargp);
|
||||
// fmt = out w/ replace % with %% as it must later when
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Constant pool
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3PchAstMT.h"
|
||||
|
||||
#include "V3ConstPool.h"
|
||||
|
||||
//######################################################################
|
||||
// V3ConstPool
|
||||
|
||||
bool V3ConstPool::equalMap(const AstInitArray* ap, const AstInitArray* bp) {
|
||||
// Associative array initializers must have equivalent types, defaults, and entries.
|
||||
// As in 'equalTable', compare by value, rather than by tree structure.
|
||||
const AstAssocArrayDType* const aDTypep = VN_AS(ap->dtypep(), AssocArrayDType);
|
||||
const AstAssocArrayDType* const bDTypep = VN_AS(bp->dtypep(), AssocArrayDType);
|
||||
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false;
|
||||
if (!aDTypep->keyDTypep()->sameTree(bDTypep->keyDTypep())) return false;
|
||||
// The default is optional, but must be the same if present
|
||||
const AstNode* const aDefaultp = ap->defaultp();
|
||||
const AstNode* const bDefaultp = bp->defaultp();
|
||||
UASSERT_OBJ(!aDefaultp || VN_IS(aDefaultp, Const), ap, "Const pool map default not Const");
|
||||
UASSERT_OBJ(!bDefaultp || VN_IS(bDefaultp, Const), bp, "Const pool map default not Const");
|
||||
if (!aDefaultp != !bDefaultp) return false;
|
||||
if (aDefaultp && !aDefaultp->sameTree(bDefaultp)) return false;
|
||||
// Compare the entries, which are ordered by key. Note an entry explicitly set to the
|
||||
// default value makes maps compare different, which is safe, but misses merging them.
|
||||
const AstInitArray::KeyItemMap& aMap = ap->map();
|
||||
const AstInitArray::KeyItemMap& bMap = bp->map();
|
||||
if (aMap.size() != bMap.size()) return false;
|
||||
auto bIt = bMap.cbegin();
|
||||
for (const auto& aItem : aMap) {
|
||||
const AstNode* const aValuep = aItem.second->valuep();
|
||||
const AstNode* const bValuep = bIt->second->valuep();
|
||||
UASSERT_OBJ(VN_IS(aValuep, Const), aValuep, "Const pool map item not Const");
|
||||
UASSERT_OBJ(VN_IS(bValuep, Const), bValuep, "Const pool map item not Const");
|
||||
if (aItem.first != bIt->first) return false;
|
||||
if (!aValuep->sameTree(bValuep)) return false;
|
||||
++bIt;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool V3ConstPool::equalTable(const AstInitArray* ap, const AstInitArray* bp) {
|
||||
// Unpacked array initializers must have equivalent values
|
||||
// Note, sadly we can't just call ap->sameTree(pb), because both:
|
||||
// - the dtypes might be different instances
|
||||
// - the default/inititem children might be in different order yet still yield the same table
|
||||
// See note in AstInitArray::same about the same. This function instead compares by initializer
|
||||
// value, rather than by tree structure.
|
||||
const AstUnpackArrayDType* const aDTypep = VN_AS(ap->dtypep(), UnpackArrayDType);
|
||||
const AstUnpackArrayDType* const bDTypep = VN_AS(bp->dtypep(), UnpackArrayDType);
|
||||
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false;
|
||||
// Compare the range by value, as the bounds might be differently typed constants
|
||||
if (!(aDTypep->declRange() == bDTypep->declRange())) return false;
|
||||
// Compare initializer arrays by value. Note this is only called when they hash the same.
|
||||
const uint64_t size = aDTypep->elementsConst();
|
||||
for (uint64_t n = 0; n < size; ++n) {
|
||||
const AstNode* const valAp = ap->getIndexDefaultedValuep(n);
|
||||
const AstNode* const valBp = bp->getIndexDefaultedValuep(n);
|
||||
UASSERT_OBJ(VN_IS(valAp, Const), valAp, "Const pool table item not Const");
|
||||
UASSERT_OBJ(VN_IS(valBp, Const), valBp, "Const pool table item not Const");
|
||||
if (!valAp->sameTree(valBp)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t V3ConstPool::hashTable(const AstInitArray* initp) {
|
||||
// Hash what 'equalTable' compares: the element type, the range by value, and the values
|
||||
const AstUnpackArrayDType* const dtypep = VN_AS(initp->dtypep(), UnpackArrayDType);
|
||||
V3Hash hash = V3Hasher::uncachedHash(dtypep->subDTypep());
|
||||
hash += dtypep->left();
|
||||
hash += dtypep->right();
|
||||
const uint64_t size = dtypep->elementsConst();
|
||||
for (uint64_t n = 0; n < size; ++n) {
|
||||
hash += V3Hasher::uncachedHash(initp->getIndexDefaultedValuep(n));
|
||||
}
|
||||
return hash.value();
|
||||
}
|
||||
|
||||
template <typename T_Set, typename T_Init>
|
||||
AstVarRef* V3ConstPool::findOrCreate(T_Set& set, uint32_t& nextr, const char* prefixp,
|
||||
T_Init* initp) {
|
||||
// Rebuild the cache if invalidated
|
||||
if (VL_UNLIKELY(!m_cacheValid)) {
|
||||
m_cacheValid = true;
|
||||
m_scopep = nullptr;
|
||||
m_consts.clear();
|
||||
m_maps.clear();
|
||||
m_tables.clear();
|
||||
m_varScopes.clear();
|
||||
AstPackage* const pkgp = v3Global.rootp()->constPoolPkgp();
|
||||
for (AstNode* nodep = pkgp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (AstScope* const scopep = VN_CAST(nodep, Scope)) {
|
||||
if (m_stage == Stage::SCOPED) m_scopep = scopep;
|
||||
continue;
|
||||
}
|
||||
// Otherwise must be a variable
|
||||
AstVar* const varp = VN_AS(nodep, Var);
|
||||
UASSERT_OBJ(varp->constPoolEntry(), varp, "Unmarked variable in constant pool");
|
||||
if (VN_IS(varp->valuep(), Const)) {
|
||||
m_consts.insert(varp);
|
||||
} else if (const AstInitArray* const arrayp = VN_CAST(varp->valuep(), InitArray)) {
|
||||
if (VN_IS(arrayp->dtypep(), AssocArrayDType)) {
|
||||
m_maps.insert(varp);
|
||||
} else {
|
||||
m_tables.insert(varp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_scopep) {
|
||||
for (AstVarScope* vscp = m_scopep->varsp(); vscp;
|
||||
vscp = VN_AS(vscp->nextp(), VarScope)) {
|
||||
m_varScopes.insert({vscp->varp(), vscp});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileLine* const flp = initp->fileline();
|
||||
AstNodeDType* const dtypep = initp->dtypep();
|
||||
|
||||
// Look up/create the variable with this value
|
||||
const auto pair = set.insertLazy(initp, [&]() {
|
||||
const std::string name = prefixp + std::to_string(nextr++);
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtypep};
|
||||
varp->setConstPoolEntry();
|
||||
varp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
varp->isConst(true);
|
||||
varp->isStatic(true);
|
||||
varp->valuep(initp->cloneTree(false));
|
||||
v3Global.rootp()->constPoolPkgp()->addStmtsp(varp);
|
||||
return varp;
|
||||
});
|
||||
AstVar* const varp = *pair.first;
|
||||
|
||||
// Create a read reference to the entry, of the right form for the current stage
|
||||
AstVarRef* refp = nullptr;
|
||||
switch (m_stage) {
|
||||
case Stage::UNSCOPED: {
|
||||
// Before V3Scope, reference via the package, as any other package variable
|
||||
AstPackage* const pkgp = v3Global.rootp()->constPoolPkgp();
|
||||
refp = new AstVarRef{flp, pkgp, varp, VAccess::READ};
|
||||
break;
|
||||
}
|
||||
case Stage::SCOPED: {
|
||||
// While scoped, reference the VarScope, creating it for a new entry, or if removed
|
||||
const auto vscpPair = m_varScopes.insertLazy(varp, [&]() {
|
||||
AstVarScope* const vscp = new AstVarScope{varp->fileline(), m_scopep, varp};
|
||||
m_scopep->addVarsp(vscp);
|
||||
return std::pair<const AstVar*, AstVarScope*>{varp, vscp};
|
||||
});
|
||||
refp = new AstVarRef{flp, vscpPair.first->second, VAccess::READ};
|
||||
break;
|
||||
}
|
||||
case Stage::DESCOPED: {
|
||||
// After V3Descope, no VarScope is needed
|
||||
refp = new AstVarRef{flp, varp, VAccess::READ};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The entry might have been created with a different, but compatible dtype
|
||||
refp->dtypep(dtypep);
|
||||
return refp;
|
||||
}
|
||||
|
||||
AstVarRef* V3ConstPool::findConst(AstConst* initp) {
|
||||
V3ConstPool& self = instance();
|
||||
return self.findOrCreate(self.m_consts, self.m_nextConst, "CONST_", initp);
|
||||
}
|
||||
|
||||
AstVarRef* V3ConstPool::findMap(AstInitArray* initp) {
|
||||
UASSERT_OBJ(VN_IS(initp->dtypep(), AssocArrayDType), initp,
|
||||
"Const pool map must have associative array dtype");
|
||||
V3ConstPool& self = instance();
|
||||
return self.findOrCreate(self.m_maps, self.m_nextMap, "MAP_", initp);
|
||||
}
|
||||
|
||||
AstVarRef* V3ConstPool::findTable(AstInitArray* initp) {
|
||||
UASSERT_OBJ(VN_IS(initp->dtypep(), UnpackArrayDType), initp,
|
||||
"Const pool table must have unpacked array dtype");
|
||||
V3ConstPool& self = instance();
|
||||
return self.findOrCreate(self.m_tables, self.m_nextTable, "TABLE_", initp);
|
||||
}
|
||||
|
||||
AstVarRef* V3ConstPool::find(AstNodeExpr* initp) {
|
||||
if (AstConst* const constp = VN_CAST(initp, Const)) return findConst(constp);
|
||||
if (AstInitArray* const arrayp = VN_CAST(initp, InitArray)) {
|
||||
if (VN_IS(arrayp->dtypep(), AssocArrayDType)) return findMap(arrayp);
|
||||
if (VN_IS(arrayp->dtypep(), UnpackArrayDType)) return findTable(arrayp);
|
||||
}
|
||||
initp->v3fatalSrc("Unhandled constant pool initializer");
|
||||
return nullptr; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
const char* V3ConstPool::broken() {
|
||||
const V3ConstPool& self = instance();
|
||||
if (!self.m_cacheValid) return nullptr;
|
||||
BROKEN_RTN(self.m_scopep && !self.m_scopep->brokeExists());
|
||||
for (const AstVar* const varp : self.m_consts) BROKEN_RTN(!varp->brokeExists());
|
||||
for (const AstVar* const varp : self.m_maps) BROKEN_RTN(!varp->brokeExists());
|
||||
for (const AstVar* const varp : self.m_tables) BROKEN_RTN(!varp->brokeExists());
|
||||
for (const auto& pair : self.m_varScopes) {
|
||||
BROKEN_RTN(!pair.first->brokeExists());
|
||||
BROKEN_RTN(!pair.second->brokeExists());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -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
|
||||
@@ -41,15 +41,6 @@ public:
|
||||
if (itFoundPair.second) m_keys.push_back(key);
|
||||
return itFoundPair.second;
|
||||
}
|
||||
template <typename T>
|
||||
void insert(T begin, const T end) {
|
||||
static_assert(std::is_same<typename std::iterator_traits<T>::value_type, T_Key>::value,
|
||||
"T must be an iterator with value type T_Key");
|
||||
while (begin != end) {
|
||||
if (m_keySet.insert(*begin).second) m_keys.push_back(*begin);
|
||||
++begin;
|
||||
}
|
||||
}
|
||||
void clear() {
|
||||
m_keys.clear();
|
||||
m_keySet.clear();
|
||||
@@ -57,7 +48,6 @@ public:
|
||||
|
||||
// ACCESSORS
|
||||
bool empty() const { return m_keys.empty(); }
|
||||
size_t size() const { return m_keys.size(); }
|
||||
bool exists(const T_Key& key) const { return m_keySet.find(key) != m_keySet.end(); }
|
||||
|
||||
// ITERATORS
|
||||
|
||||
+36
-36
@@ -491,41 +491,41 @@ public:
|
||||
IgnIndices results;
|
||||
int nextChange = 0;
|
||||
tree.find(0, results, nextChange);
|
||||
UASSERT_SELFTEST(results.size(), 0);
|
||||
UASSERT_SELFTEST(nextChange, 10);
|
||||
UASSERT_SELFTEST(const size_t, results.size(), 0);
|
||||
UASSERT_SELFTEST(const int, nextChange, 10);
|
||||
tree.find(10, results, nextChange);
|
||||
UASSERT_SELFTEST(results.size(), 2);
|
||||
UASSERT_SELFTEST(results[0], 0);
|
||||
UASSERT_SELFTEST(results[1], 3);
|
||||
UASSERT_SELFTEST(nextChange, 11);
|
||||
UASSERT_SELFTEST(const size_t, results.size(), 2);
|
||||
UASSERT_SELFTEST(const int, results[0], 0);
|
||||
UASSERT_SELFTEST(const int, results[1], 3);
|
||||
UASSERT_SELFTEST(const int, nextChange, 11);
|
||||
tree.find(11, results, nextChange);
|
||||
UASSERT_SELFTEST(results.size(), 1);
|
||||
UASSERT_SELFTEST(results[0], 3);
|
||||
UASSERT_SELFTEST(nextChange, 15); // Center, or would be 20
|
||||
UASSERT_SELFTEST(const size_t, results.size(), 1);
|
||||
UASSERT_SELFTEST(const int, results[0], 3);
|
||||
UASSERT_SELFTEST(const int, nextChange, 15); // Center, or would be 20
|
||||
tree.find(20, results, nextChange);
|
||||
UASSERT_SELFTEST(results.size(), 3);
|
||||
UASSERT_SELFTEST(results[0], 1);
|
||||
UASSERT_SELFTEST(results[1], 3);
|
||||
UASSERT_SELFTEST(results[2], 4);
|
||||
UASSERT_SELFTEST(nextChange, 21);
|
||||
UASSERT_SELFTEST(const size_t, results.size(), 3);
|
||||
UASSERT_SELFTEST(const int, results[0], 1);
|
||||
UASSERT_SELFTEST(const int, results[1], 3);
|
||||
UASSERT_SELFTEST(const int, results[2], 4);
|
||||
UASSERT_SELFTEST(const int, nextChange, 21);
|
||||
tree.find(21, results, nextChange);
|
||||
UASSERT_SELFTEST(results.size(), 2);
|
||||
UASSERT_SELFTEST(results[0], 3);
|
||||
UASSERT_SELFTEST(results[1], 4);
|
||||
UASSERT_SELFTEST(nextChange, 25); // Center, or would be 30
|
||||
UASSERT_SELFTEST(const size_t, results.size(), 2);
|
||||
UASSERT_SELFTEST(const int, results[0], 3);
|
||||
UASSERT_SELFTEST(const int, results[1], 4);
|
||||
UASSERT_SELFTEST(const int, nextChange, 25); // Center, or would be 30
|
||||
tree.find(30, results, nextChange);
|
||||
UASSERT_SELFTEST(results.size(), 2);
|
||||
UASSERT_SELFTEST(results[0], 3);
|
||||
UASSERT_SELFTEST(results[1], 4);
|
||||
UASSERT_SELFTEST(nextChange, 31);
|
||||
UASSERT_SELFTEST(const size_t, results.size(), 2);
|
||||
UASSERT_SELFTEST(const int, results[0], 3);
|
||||
UASSERT_SELFTEST(const int, results[1], 4);
|
||||
UASSERT_SELFTEST(const int, nextChange, 31);
|
||||
tree.find(40, results, nextChange);
|
||||
UASSERT_SELFTEST(results.size(), 2);
|
||||
UASSERT_SELFTEST(results[0], 2);
|
||||
UASSERT_SELFTEST(results[1], 4);
|
||||
UASSERT_SELFTEST(nextChange, 41);
|
||||
UASSERT_SELFTEST(const size_t, results.size(), 2);
|
||||
UASSERT_SELFTEST(const int, results[0], 2);
|
||||
UASSERT_SELFTEST(const int, results[1], 4);
|
||||
UASSERT_SELFTEST(const int, nextChange, 41);
|
||||
tree.find(41, results, nextChange);
|
||||
UASSERT_SELFTEST(results.size(), 0);
|
||||
UASSERT_SELFTEST(nextChange, std::numeric_limits<int>::max());
|
||||
UASSERT_SELFTEST(const size_t, results.size(), 0);
|
||||
UASSERT_SELFTEST(const int, nextChange, std::numeric_limits<int>::max());
|
||||
//
|
||||
points = {{0, 0}};
|
||||
for (const auto& it : points) {
|
||||
@@ -536,8 +536,8 @@ public:
|
||||
tree.build(data);
|
||||
//
|
||||
tree.find(50, results, nextChange);
|
||||
UASSERT_SELFTEST(results.size(), 1);
|
||||
UASSERT_SELFTEST(results[0], 5);
|
||||
UASSERT_SELFTEST(const size_t, results.size(), 1);
|
||||
UASSERT_SELFTEST(const int, results[0], 5);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -709,8 +709,8 @@ public:
|
||||
// Tracks what matches are known to hit against V3ControlScopeTraceEntries
|
||||
class V3ControlScopeTraceEntryMatch final {
|
||||
public:
|
||||
const V3ControlScopeTraceEntry* m_entryp; // Entry the match refers to
|
||||
const string m_scopepart; // Scope prefix being matched against the entry
|
||||
const V3ControlScopeTraceEntry* m_entryp;
|
||||
const string m_scopepart;
|
||||
V3ControlScopeTraceEntryMatch(const V3ControlScopeTraceEntry* entryp, const string& scopepart)
|
||||
: m_entryp{entryp}
|
||||
, m_scopepart{scopepart} {}
|
||||
@@ -780,8 +780,8 @@ public:
|
||||
// Resolve modules and files in the design
|
||||
|
||||
class V3ControlResolverHierWorkerEntry final {
|
||||
const int m_workers; // Number of workers requested for the hierarchical block
|
||||
FileLine* const m_flp; // Location the worker count was specified at
|
||||
const int m_workers;
|
||||
FileLine* const m_flp;
|
||||
|
||||
public:
|
||||
explicit V3ControlResolverHierWorkerEntry(int workers, FileLine* flp)
|
||||
@@ -798,9 +798,9 @@ class V3ControlResolver final {
|
||||
V3ControlScopeTraceResolver m_scopeTraces; // Regexp to trace enables
|
||||
std::unordered_map<string, std::unordered_map<string, uint64_t>>
|
||||
m_profileData; // Access to profile_data records
|
||||
uint8_t m_mode = NONE; // Kind of profile_data records is currently active
|
||||
uint8_t m_mode = NONE;
|
||||
std::unordered_map<string, V3ControlResolverHierWorkerEntry> m_hierWorkers;
|
||||
FileLine* m_profileFileLine = nullptr; // Location profile_data was read from
|
||||
FileLine* m_profileFileLine = nullptr;
|
||||
|
||||
V3ControlResolver() = default;
|
||||
~V3ControlResolver() = default;
|
||||
|
||||
+2
-13
@@ -199,7 +199,6 @@ class CoverageVisitor final : public VNVisitor {
|
||||
AstVar* const varp = new AstVar{fl_nowarn, VVarType::MODULETEMP, trace_var_name,
|
||||
incp->findUInt32DType()};
|
||||
varp->setIgnoreSchedWrite(); // Ignore the increment output, so no UNOPTFLAT
|
||||
varp->lifetime(VLifetime::STATIC_IMPLICIT);
|
||||
varp->trace(true);
|
||||
m_modp->addStmtsp(varp);
|
||||
UINFO(5, "New coverage trace: " << varp);
|
||||
@@ -284,7 +283,7 @@ class CoverageVisitor final : public VNVisitor {
|
||||
VL_RESTORER_COPY(m_funcTemps);
|
||||
createHandle(nodep);
|
||||
m_modp = nodep;
|
||||
m_state.m_inModOff = nodep->isTop(); // Already made top shell, no coverage for it
|
||||
m_state.m_inModOff = false; // Haven't made top shell, so tops are real tops
|
||||
if (!origModp) {
|
||||
// No blocks cross (non-nested) modules, so save some memory
|
||||
m_varnames.clear();
|
||||
@@ -1054,7 +1053,7 @@ class CoverageVisitor final : public VNVisitor {
|
||||
unrolledp = new T_Oper{fl, selp, unrolledp};
|
||||
}
|
||||
iterate(unrolledp);
|
||||
VL_DO_DANGLING(pushDeletep(unrolledp), unrolledp);
|
||||
pushDeletep(unrolledp);
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
lineTrack(nodep);
|
||||
@@ -1115,16 +1114,6 @@ class CoverageVisitor final : public VNVisitor {
|
||||
}
|
||||
|
||||
// VISITORS - BOTH
|
||||
void visit(AstProperty* nodep) override {
|
||||
VL_RESTORER(m_state);
|
||||
m_state.m_on = false;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstSequence* nodep) override {
|
||||
VL_RESTORER(m_state);
|
||||
m_state.m_on = false;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
lineTrack(nodep);
|
||||
|
||||
+578
-2911
File diff suppressed because it is too large
Load Diff
+72
-322
@@ -37,8 +37,6 @@
|
||||
|
||||
#include "V3Dead.h"
|
||||
|
||||
#include "V3ConstPool.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <queue>
|
||||
@@ -46,199 +44,6 @@
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Dead tracking graph
|
||||
|
||||
class DeadVertex final : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(DeadVertex, V3GraphVertex)
|
||||
AstNode* const m_nodep; // Node that created vertex
|
||||
uint64_t m_workPos = 0; // Position on DeadWorkList, 0 = not on list
|
||||
bool m_removable = false; // Subject to dead removal
|
||||
bool m_isVirtual = false; // Virtual node, indirection
|
||||
|
||||
public:
|
||||
DeadVertex(V3Graph* graphp, AstNode* nodep)
|
||||
: V3GraphVertex{graphp}
|
||||
, m_nodep{nodep} {}
|
||||
~DeadVertex() override = default;
|
||||
string dotShape() const override {
|
||||
return isVirtual() ? "diamond" : removable() ? "rectangle" : "ellipse";
|
||||
}
|
||||
AstNode* nodep() const VL_MT_STABLE { return m_nodep; }
|
||||
string name() const override VL_MT_STABLE {
|
||||
return (isVirtual() ? "[VIRT] "
|
||||
: removable() ? "[R] "s
|
||||
: "[NR] ")
|
||||
+ nodep()->typeName() + ' ' + cvtToHex(nodep()) + ' ' + nodep()->name();
|
||||
}
|
||||
bool removable() const { return m_removable; }
|
||||
void removable(bool flag) { m_removable = flag; }
|
||||
bool isVirtual() const { return m_isVirtual; }
|
||||
void isVirtual(bool flag) { m_isVirtual = flag; }
|
||||
uint64_t workPos() const { return m_workPos; }
|
||||
void workPos(uint64_t value) { m_workPos = value; }
|
||||
};
|
||||
|
||||
// Work list which keeps ordering of elements (so stable), adds always to
|
||||
// the end of the queue, and allows arbitrary removal
|
||||
class DeadWorkList final {
|
||||
// MEMBERS
|
||||
static uint64_t s_sequence; // Sequence number, inc each push
|
||||
std::map<uint64_t, DeadVertex*> m_works; // Work list by sequence
|
||||
public:
|
||||
// METHODS
|
||||
bool empty() const { return m_works.empty(); }
|
||||
void push(DeadVertex* vtxp) {
|
||||
if (vtxp->workPos()) return; // Already on list
|
||||
const uint64_t id = ++s_sequence;
|
||||
vtxp->workPos(id);
|
||||
m_works.emplace(id, vtxp);
|
||||
UINFO(9, "Worklist.push " << vtxp);
|
||||
}
|
||||
void erase(DeadVertex* vtxp) {
|
||||
const uint64_t id = vtxp->workPos();
|
||||
if (!id) return;
|
||||
UINFO(9, "Worklist.erase " << vtxp);
|
||||
const auto it = m_works.find(id);
|
||||
UASSERT_OBJ(it != m_works.end(), vtxp->nodep(),
|
||||
"vertex thought to be on work list but not");
|
||||
m_works.erase(it);
|
||||
vtxp->workPos(0);
|
||||
}
|
||||
DeadVertex* getPopFront() {
|
||||
UDEBUGONLY(UASSERT(!empty(), "Front invalid to call on empty list"););
|
||||
const auto it = m_works.begin();
|
||||
DeadVertex* const vtxp = it->second;
|
||||
m_works.erase(it);
|
||||
vtxp->workPos(0);
|
||||
UINFO(9, "Worklist.getPopFront " << vtxp);
|
||||
return vtxp;
|
||||
}
|
||||
};
|
||||
|
||||
uint64_t DeadWorkList::s_sequence = 0;
|
||||
|
||||
class DeadGraph final : public V3Graph {
|
||||
// NODE STATE
|
||||
// AstNodeFTask::user2p() -> DeadVertex* for this node
|
||||
// See const VNUser2InUse m_inuser2; inside DeadVisitor
|
||||
// MEMBERS
|
||||
DeadWorkList m_funcs; // Functions eligble for deletion
|
||||
// Each virtual vertex by the ftask name()
|
||||
std::unordered_map<std::string, DeadVertex*> m_virtualVtxsp;
|
||||
|
||||
void newEdge(DeadVertex* fromp, DeadVertex* top) {
|
||||
new V3GraphEdge{this, fromp, top, 1, false};
|
||||
if (top->removable()) m_funcs.erase(top); // Now has an input edge
|
||||
}
|
||||
void pushWorkMaybe(DeadVertex* vtxp, bool allowSize1) {
|
||||
if (vtxp->removable()) {
|
||||
if (VN_IS(vtxp->nodep(), NodeFTask)
|
||||
&& (vtxp->inEmpty() || (allowSize1 && vtxp->inSize1())))
|
||||
m_funcs.push(vtxp);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
DeadGraph() = default;
|
||||
~DeadGraph() override = default;
|
||||
DeadVertex* findNewVertex(AstNode* nodep) {
|
||||
DeadVertex* vtxp = nodep->user2u().to<DeadVertex*>();
|
||||
if (!vtxp) {
|
||||
vtxp = new DeadVertex{this, nodep};
|
||||
nodep->user2p(vtxp);
|
||||
pushWorkMaybe(vtxp, false);
|
||||
}
|
||||
UASSERT_OBJ(vtxp->nodep() == nodep, nodep, "Vertex points at different node");
|
||||
return vtxp;
|
||||
}
|
||||
DeadVertex* findVirtualVertex(AstNode* nodep) {
|
||||
const auto it = m_virtualVtxsp.find(nodep->name());
|
||||
DeadVertex* vtxp;
|
||||
if (it != m_virtualVtxsp.end()) {
|
||||
vtxp = it->second;
|
||||
} else {
|
||||
vtxp = new DeadVertex{this, nodep};
|
||||
vtxp->removable(true);
|
||||
vtxp->isVirtual(true);
|
||||
pushWorkMaybe(vtxp, false);
|
||||
m_virtualVtxsp.emplace(nodep->name(), vtxp);
|
||||
}
|
||||
return vtxp;
|
||||
}
|
||||
void findNewRemovableVertex(AstNode* nodep, bool removable) {
|
||||
DeadVertex* const vtxp = findNewVertex(nodep);
|
||||
// Wasn't removable before (due to earlier insert), make removable now
|
||||
if (removable && !vtxp->removable()) {
|
||||
vtxp->removable(true);
|
||||
pushWorkMaybe(vtxp, false);
|
||||
}
|
||||
}
|
||||
void deleteNodeVertex(AstNode* nodep) {
|
||||
if (DeadVertex* const vtxp = nodep->user2u().to<DeadVertex*>()) deleteVertex(vtxp);
|
||||
}
|
||||
void deleteVertex(DeadVertex* vtxp) {
|
||||
UINFO(9, "Delete vertex " << vtxp->name());
|
||||
// Mark all about-to-empty downstream vertices onto worklist
|
||||
for (const V3GraphEdge& oedge : vtxp->outEdges()) {
|
||||
DeadVertex* const toVtxp = static_cast<DeadVertex*>(oedge.top());
|
||||
pushWorkMaybe(toVtxp, true); // size1 ok as about to delete below
|
||||
}
|
||||
// Node shouldn't be looking at user2p later as node being deleting, but in case
|
||||
if (!vtxp->isVirtual()) vtxp->nodep()->user2p(nullptr);
|
||||
if (vtxp->removable()) m_funcs.erase(vtxp);
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(this), vtxp);
|
||||
}
|
||||
// This only tracks usage dependancy, not "containership",
|
||||
// When all needs disappear the related node is eligble for deletion
|
||||
void needs(AstNode* nodep, AstNode* parentp) {
|
||||
if (parentp == nodep) return; // No need for tracking needs itself (recursion)
|
||||
UINFO(9, "Edge node " << nodep << " -> " << parentp);
|
||||
DeadVertex* const parentVtxp = findNewVertex(parentp);
|
||||
DeadVertex* const nodeVtxp = findNewVertex(nodep);
|
||||
UINFO(9, "Edge need " << parentVtxp << " -> " << nodeVtxp);
|
||||
newEdge(parentVtxp, nodeVtxp);
|
||||
}
|
||||
void needsVirtual(AstNodeFTask* nodep, AstNode* parentp) {
|
||||
// Virtual call can be to any function in the call hierarchy.
|
||||
// For simplicity rather than tracking possible multiple base
|
||||
// classes (due to 'implements' classes there can be more than
|
||||
// one), we simply assume all virtual functions of the same name
|
||||
// can call any other virtual function of the same name
|
||||
// Track via an intermediate node.
|
||||
// All calling parents' verticies -> Virtual Vertex -> all ftasks verticies
|
||||
if (parentp == nodep) return; // No need for tracking needs itself (recursion)
|
||||
DeadVertex* const parentVtxp = findNewVertex(parentp);
|
||||
DeadVertex* const virtualVtxp = findVirtualVertex(nodep);
|
||||
UINFO(9, "Edge needVirtual " << parentVtxp << " -> " << virtualVtxp);
|
||||
newEdge(parentVtxp, virtualVtxp);
|
||||
}
|
||||
void funcVirtual(AstNodeFTask* nodep) {
|
||||
// Virtual Vertex -> all ftasks verticies
|
||||
DeadVertex* const virtualVtxp = findVirtualVertex(nodep);
|
||||
DeadVertex* const nodeVtxp = findNewVertex(nodep);
|
||||
UINFO(9, "Edge funcvirtual " << virtualVtxp << " -> " << nodeVtxp);
|
||||
newEdge(virtualVtxp, nodeVtxp);
|
||||
}
|
||||
bool funcsEmpty() const { return m_funcs.empty(); }
|
||||
AstNode* funcsGetPopFront() {
|
||||
DeadVertex* const vtxp = m_funcs.getPopFront();
|
||||
UASSERT_OBJ(vtxp->inEmpty(), vtxp->nodep(), "Non-empty node on work list");
|
||||
UASSERT_OBJ(vtxp->removable(), vtxp->nodep(), "Non-removable node on work list");
|
||||
if (vtxp->isVirtual()) {
|
||||
// Emptied (no inbound edge) virtual wrapper; all
|
||||
// destinations verticies are now unused too (e.g. all virtual
|
||||
// functions of this name may be deleted)
|
||||
UINFO(9, "Removing virtual " << vtxp);
|
||||
deleteVertex(vtxp);
|
||||
return nullptr; // Caller will search again
|
||||
}
|
||||
AstNode* const nodep = vtxp->nodep();
|
||||
return nodep;
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Dead state, as a visitor of each AstNode
|
||||
|
||||
@@ -247,11 +52,10 @@ class DeadVisitor final : public VNVisitor {
|
||||
// Entire Netlist:
|
||||
// AstNodeModule::user1() -> int. Count of number of cells referencing this module.
|
||||
// AstVar::user1() -> int. Count of number of references
|
||||
// AstVar::user2() -> bool. Is a formal function argument
|
||||
// AstVarScope::user1() -> int. Count of number of references
|
||||
// AstNodeDType::user1() -> int. Count of number of references
|
||||
// AstNodeFTask::user1() -> int. Count of number of references (via AstNodeFTaskRefs)
|
||||
const VNUser1InUse m_inuser1;
|
||||
const VNUser2InUse m_inuser2; // For usage information see DeadGraph
|
||||
|
||||
// TYPES
|
||||
using AssignMap = std::multimap<AstVarScope*, AstNodeAssign*>;
|
||||
@@ -260,9 +64,6 @@ class DeadVisitor final : public VNVisitor {
|
||||
const bool m_elimUserVars; // Allow removal of user's vars
|
||||
const bool m_elimDTypes; // Allow removal of DTypes
|
||||
const bool m_elimCells; // Allow removal of Cells
|
||||
|
||||
DeadGraph m_graph; // Tracking graph
|
||||
|
||||
// List of all encountered to avoid another loop through tree
|
||||
std::vector<AstVar*> m_varsp;
|
||||
std::vector<AstNode*> m_dtypeElimsp; // Data types might eliminate
|
||||
@@ -272,6 +73,7 @@ class DeadVisitor final : public VNVisitor {
|
||||
std::vector<AstCell*> m_cellsp;
|
||||
std::vector<AstClass*> m_classesp;
|
||||
std::vector<AstTypedef*> m_typedefsp;
|
||||
std::queue<AstNodeFTask*> m_tasksp; // All the tasks that could be removed if not called
|
||||
AssignMap m_assignMap; // List of all simple assignments for each variable
|
||||
bool m_sideEffect = false; // Side effects discovered in assign RHS
|
||||
|
||||
@@ -280,20 +82,14 @@ class DeadVisitor final : public VNVisitor {
|
||||
AstNodeDType* m_curDTypep = nullptr; // Current NodeDType
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstForeachHeader* m_foreachHeaderp = nullptr; // Current foreach header
|
||||
AstNode* m_containingFTaskRefp = nullptr; // Parent of ftaskref (e.g. task/module)
|
||||
bool m_inFTask = false; // Under an AstNodeFTask
|
||||
|
||||
// STATE - Statistic tracking
|
||||
VDouble0 m_statFTasksDemoted;
|
||||
VDouble0 m_statFTasksMDeadified;
|
||||
VDouble0 m_statFTasksNMDeadified;
|
||||
VDouble0 m_statFTasksVirtDeadified;
|
||||
VDouble0 m_statFTasksDeadified;
|
||||
|
||||
// METHODS
|
||||
|
||||
void deleting(AstNode* nodep) {
|
||||
UINFO(9, " deleting " << nodep);
|
||||
m_graph.deleteNodeVertex(nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
|
||||
@@ -322,22 +118,12 @@ class DeadVisitor final : public VNVisitor {
|
||||
if (AstNode* const subnodep = nodep->virtRefDTypep()) subnodep->user1Inc();
|
||||
if (AstNode* const subnodep = nodep->virtRefDType2p()) subnodep->user1Inc();
|
||||
}
|
||||
void needsTask(AstNodeFTask* taskp, AstNode* containerp) {
|
||||
if (!taskp) return; // Unlinked
|
||||
if (taskp->isVirtual()) {
|
||||
m_graph.needsVirtual(taskp, containerp);
|
||||
} else {
|
||||
m_graph.needs(taskp, containerp);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
if (m_modp) m_modp->user1Inc(); // e.g. Class under Package
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = nodep;
|
||||
VL_RESTORER(m_containingFTaskRefp);
|
||||
m_containingFTaskRefp = nodep;
|
||||
if (nodep->dead()) return;
|
||||
if (nodep->modPublic()) m_modp->user1Inc();
|
||||
iterateChildren(nodep);
|
||||
@@ -354,16 +140,6 @@ class DeadVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
||||
// Keep formal arguments
|
||||
for (AstVar* varp = nodep->argsp(); varp; varp = VN_AS(varp->nextp(), Var)) {
|
||||
varp->user1Inc();
|
||||
varp->user2(1);
|
||||
}
|
||||
}
|
||||
void visit(AstPin* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->modVarp()) nodep->modVarp()->user1Inc();
|
||||
}
|
||||
void visit(AstScope* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
@@ -372,8 +148,6 @@ class DeadVisitor final : public VNVisitor {
|
||||
// Class packages might have no children, but need to remain as
|
||||
// long as the class they refer to is needed
|
||||
if (VN_IS(m_modp, Class) || VN_IS(m_modp, ClassPackage)) nodep->user1Inc();
|
||||
// The constant pool scope must remain for entries created later
|
||||
if (m_modp->isConstPool()) nodep->user1Inc();
|
||||
if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp()) {
|
||||
m_scopesp.push_back(nodep);
|
||||
}
|
||||
@@ -401,7 +175,7 @@ class DeadVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
if (!m_sideEffect && !nodep->isPure()) m_sideEffect = true;
|
||||
checkAll(nodep);
|
||||
needsTask(nodep->taskp(), m_containingFTaskRefp);
|
||||
if (nodep->taskp()) nodep->taskp()->user1Inc();
|
||||
if (nodep->classOrPackagep()) {
|
||||
if (m_elimCells) {
|
||||
nodep->classOrPackagep(nullptr);
|
||||
@@ -413,12 +187,7 @@ class DeadVisitor final : public VNVisitor {
|
||||
void visit(AstModportFTaskRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
needsTask(nodep->ftaskp(), m_containingFTaskRefp);
|
||||
}
|
||||
void visit(AstModportVarRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->varp()) nodep->varp()->user1Inc();
|
||||
if (nodep->ftaskp()) nodep->ftaskp()->user1Inc();
|
||||
}
|
||||
void visit(AstRefDType* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
@@ -525,11 +294,6 @@ class DeadVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (m_foreachHeaderp) nodep->user1Inc();
|
||||
// Keep formal arguments
|
||||
if (m_inFTask && nodep->isIO()) {
|
||||
nodep->user1Inc();
|
||||
nodep->user2(1);
|
||||
}
|
||||
if (mightElimVar(nodep)) {
|
||||
m_varsp.push_back(nodep);
|
||||
} else {
|
||||
@@ -563,21 +327,13 @@ class DeadVisitor final : public VNVisitor {
|
||||
if (assignInAssign) m_sideEffect = true; // Parent assign shouldn't optimize
|
||||
}
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
const bool removable = !(nodep->taskPublic() || nodep->dpiExport() || nodep->dpiImport()
|
||||
|| nodep->keepAlive() || nodep->isConstructor()
|
||||
|| (!v3Global.opt.fDeadMethods() && nodep->classMethod()));
|
||||
m_graph.findNewRemovableVertex(nodep, removable);
|
||||
//
|
||||
VL_RESTORER(m_containingFTaskRefp);
|
||||
VL_RESTORER(m_inFTask);
|
||||
m_containingFTaskRefp = nodep;
|
||||
m_inFTask = true;
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->isVirtual()) m_graph.funcVirtual(nodep);
|
||||
if (!removable) {
|
||||
if (nodep->taskPublic() || nodep->dpiExport() || nodep->dpiImport()) {
|
||||
if (m_modp && !m_modp->dead() && !m_modp->verilatorLib())
|
||||
m_modp->user1Inc(); // Keep container
|
||||
} else {
|
||||
m_tasksp.push(nodep);
|
||||
}
|
||||
if (nodep->classOrPackagep()) {
|
||||
if (m_elimCells) {
|
||||
@@ -618,41 +374,19 @@ class DeadVisitor final : public VNVisitor {
|
||||
}
|
||||
|
||||
void deadCheckTasks() {
|
||||
while (!m_graph.funcsEmpty()) {
|
||||
AstNode* const nodep = m_graph.funcsGetPopFront();
|
||||
if (!nodep) continue;
|
||||
UINFO(9, "Dead " << nodep);
|
||||
if (AstNodeFTask* const taskp = VN_CAST(nodep, NodeFTask)) {
|
||||
if (taskp->isVirtual()) {
|
||||
++m_statFTasksVirtDeadified;
|
||||
} else if (taskp->classMethod()) {
|
||||
++m_statFTasksMDeadified;
|
||||
} else {
|
||||
++m_statFTasksNMDeadified;
|
||||
}
|
||||
}
|
||||
deleting(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
void deadCheckDemote() {
|
||||
for (V3GraphVertex& gvtx : m_graph.vertices()) {
|
||||
DeadVertex* const vtxp = gvtx.cast<DeadVertex>();
|
||||
// A isVirtual vertex with single out means there's only one target virtual function
|
||||
// that can be virtually called, so can make it non-virtual for faster execution
|
||||
// (UVM benefits from this)
|
||||
if (!vtxp->outSize1()) continue;
|
||||
if (!vtxp->isVirtual()) continue;
|
||||
for (V3GraphEdge& edge : vtxp->outEdges()) { // Always a single one
|
||||
AstNode* const nodep = edge.top()->as<DeadVertex>()->nodep();
|
||||
AstNodeFTask* const funcp = VN_AS(nodep, NodeFTask);
|
||||
UASSERT_OBJ(funcp->isVirtual(), funcp,
|
||||
"Only virtual ftasks should be under DeadVirtualVertex");
|
||||
if (v3Global.opt.fDeadMethods()) {
|
||||
funcp->isVirtual(false);
|
||||
UINFO(9, "Demote to non-virtual " << funcp);
|
||||
++m_statFTasksDemoted;
|
||||
}
|
||||
while (!m_tasksp.empty()) {
|
||||
AstNodeFTask* taskp = m_tasksp.front();
|
||||
m_tasksp.pop();
|
||||
if (taskp->user1() == 0 && !taskp->classMethod()) {
|
||||
taskp->foreach([this](AstNodeFTaskRef* ftaskrefp) {
|
||||
AstNodeFTask* task2p = ftaskrefp->taskp();
|
||||
if (!task2p) return;
|
||||
task2p->user1Inc(-1);
|
||||
if (task2p->user1() == 0) m_tasksp.push(task2p);
|
||||
});
|
||||
taskp->user1(-1); // we don't want to try deleting twice
|
||||
deleting(taskp);
|
||||
++m_statFTasksDeadified;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -666,11 +400,7 @@ class DeadVisitor final : public VNVisitor {
|
||||
AstNodeModule* nextmodp;
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp = nextmodp) {
|
||||
nextmodp = VN_AS(modp->nextp(), NodeModule);
|
||||
// Keep $unit until m_elimCells stages. Note v3Global.opt.serializeOnly()
|
||||
// won't reach this stage, and will always have an empty $unit. That's ok.
|
||||
// The constant pool is always kept, entries might be created later.
|
||||
const bool keep = (!m_elimCells && modp->isDollarUnit()) || modp->isConstPool();
|
||||
if (modp->dead() || (!modp->isTop() && modp->user1() == 0 && !keep)) {
|
||||
if (modp->dead() || (!modp->isTop() && modp->user1() == 0 && !modp->internal())) {
|
||||
// > 2 because L1 is the wrapper, L2 is the top user module
|
||||
UINFO(4, " Dead module " << modp);
|
||||
// And its children may now be killable too; correct counts
|
||||
@@ -680,7 +410,6 @@ class DeadVisitor final : public VNVisitor {
|
||||
cellp->modp()->user1Inc(-1);
|
||||
});
|
||||
}
|
||||
if (modp->isDollarUnit()) v3Global.rootp()->dollarUnitPkgp(nullptr);
|
||||
deleting(modp);
|
||||
retry = true;
|
||||
}
|
||||
@@ -689,7 +418,7 @@ class DeadVisitor final : public VNVisitor {
|
||||
}
|
||||
bool mightElimVar(const AstVar* nodep) const {
|
||||
if (nodep->isSigPublic()) return false; // Can't elim publics!
|
||||
if (nodep->isPrimaryIO() || nodep->isClassMember() || nodep->sensIfacep()) return false;
|
||||
if (nodep->isIO() || nodep->isClassMember() || nodep->sensIfacep()) return false;
|
||||
if (nodep->isTemp() && !nodep->isTrace()) return true;
|
||||
return m_elimUserVars; // Post-Trace can kill most anything
|
||||
}
|
||||
@@ -741,8 +470,6 @@ class DeadVisitor final : public VNVisitor {
|
||||
void deadCheckVar() {
|
||||
// Delete any unused varscopes
|
||||
for (AstVarScope* vscp : m_vscsp) {
|
||||
// Keep formal arguments
|
||||
if (vscp->varp()->user2()) continue;
|
||||
if (vscp->user1() == 0) {
|
||||
UINFO(4, " Dead " << vscp);
|
||||
const std::pair<AssignMap::iterator, AssignMap::iterator> eqrange
|
||||
@@ -795,17 +522,53 @@ class DeadVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
// cppcheck-suppress constParameterPointer
|
||||
void preserveTopIfaces(AstNetlist* rootp) {
|
||||
// cppcheck-suppress constVariablePointer
|
||||
for (AstNodeModule* modp = rootp->modulesp(); modp && modp->isTop();
|
||||
modp = VN_AS(modp->nextp(), NodeModule)) {
|
||||
for (AstNode* subnodep = modp->stmtsp(); subnodep; subnodep = subnodep->nextp()) {
|
||||
if (AstVar* const varp = VN_CAST(subnodep, Var)) {
|
||||
if (varp->isIfaceRef()) {
|
||||
const AstNodeDType* const subtypep = varp->subDTypep();
|
||||
const AstIfaceRefDType* ifacerefp = nullptr;
|
||||
if (VN_IS(subtypep, IfaceRefDType)) {
|
||||
ifacerefp = VN_AS(varp->subDTypep(), IfaceRefDType);
|
||||
} else if (VN_IS(subtypep, BracketArrayDType)) {
|
||||
const AstBracketArrayDType* const arrp
|
||||
= VN_AS(subtypep, BracketArrayDType);
|
||||
const AstNodeDType* const arrsubtypep = arrp->subDTypep();
|
||||
if (VN_IS(arrsubtypep, IfaceRefDType)) {
|
||||
ifacerefp = VN_AS(arrsubtypep, IfaceRefDType);
|
||||
}
|
||||
} else if (VN_IS(subtypep, UnpackArrayDType)) {
|
||||
const AstUnpackArrayDType* const arrp
|
||||
= VN_AS(subtypep, UnpackArrayDType);
|
||||
const AstNodeDType* const arrsubtypep = arrp->subDTypep();
|
||||
if (VN_IS(arrsubtypep, IfaceRefDType)) {
|
||||
ifacerefp = VN_AS(arrsubtypep, IfaceRefDType);
|
||||
}
|
||||
}
|
||||
|
||||
if (ifacerefp && !ifacerefp->cellp()
|
||||
&& (ifacerefp->ifacep()->user1() == 0)) {
|
||||
ifacerefp->ifacep()->user1(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes,
|
||||
bool elimCells, bool elimTasks)
|
||||
bool elimCells, bool elimTopIfaces, bool elimTasks)
|
||||
: m_elimUserVars{elimUserVars}
|
||||
, m_elimDTypes{elimDTypes}
|
||||
, m_elimCells{elimCells}
|
||||
, m_containingFTaskRefp{nodep} {
|
||||
, m_elimCells{elimCells} {
|
||||
// Prepare to remove some datatypes
|
||||
nodep->typeTablep()->clearCache();
|
||||
|
||||
// Operate on whole netlist
|
||||
iterate(nodep);
|
||||
|
||||
@@ -819,11 +582,6 @@ public:
|
||||
if (itr.first->user1()) itr.second->user1Inc();
|
||||
}
|
||||
|
||||
// Simplify redundant edges (e.g. function calls another function many times)
|
||||
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
|
||||
if (dumpGraphLevel() >= 9 || debug() >= 9)
|
||||
m_graph.dumpDotFilePrefixed("dead_graph", false);
|
||||
|
||||
if (elimTasks) deadCheckTasks();
|
||||
deadCheckTypedefs();
|
||||
deadCheckVar();
|
||||
@@ -833,24 +591,16 @@ public:
|
||||
if (elimCells) deadCheckCells();
|
||||
deadCheckClasses();
|
||||
// Modules after vars, because might be vars we delete inside a mod we delete
|
||||
if (!elimTopIfaces) preserveTopIfaces(nodep);
|
||||
deadCheckMod();
|
||||
|
||||
// After deleting as much as can, demote some virtual functions
|
||||
if (elimTasks) deadCheckDemote();
|
||||
|
||||
// We may have removed some datatypes, cleanup
|
||||
nodep->typeTablep()->repairCache();
|
||||
VIsCached::clearCacheTree(); // Removing assignments may affect isPure
|
||||
V3ConstPool::invalidateCache(); // Might have deleted constant pool entries
|
||||
nodep->constPoolp()->rebuildVarScopesAndCache();
|
||||
}
|
||||
~DeadVisitor() override {
|
||||
V3Stats::addStatSum("Optimizations, FTasks, virtual-to-nonvirtual demotion",
|
||||
m_statFTasksDemoted);
|
||||
V3Stats::addStatSum("Optimizations, FTasks, deadified, methods", m_statFTasksMDeadified);
|
||||
V3Stats::addStatSum("Optimizations, FTasks, deadified, non-methods",
|
||||
m_statFTasksNMDeadified);
|
||||
V3Stats::addStatSum("Optimizations, FTasks, deadified, virtual",
|
||||
m_statFTasksVirtDeadified);
|
||||
V3Stats::addStatSum("Optimizations, deadified FTasks", m_statFTasksDeadified);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -859,32 +609,32 @@ public:
|
||||
|
||||
void V3Dead::deadifyModules(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ // node, elimUserVars, elimDTypes, elimScopes, elimCells, elimTasks
|
||||
DeadVisitor{nodep, false, false, false, false, false};
|
||||
{ // node, elimUserVars, elimDTypes, elimScopes, elimCells, elimTopIfaces
|
||||
DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported(), false};
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeEitherLevel() >= 6);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ DeadVisitor{nodep, false, true, false, false, true}; } // Destruct before checking
|
||||
{ DeadVisitor{nodep, false, true, false, false, false, true}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ DeadVisitor{nodep, false, true, true, false, false}; } // Destruct before checking
|
||||
{ DeadVisitor{nodep, false, true, true, false, false, false}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ DeadVisitor{nodep, true, true, false, true, true}; } // Destruct before checking
|
||||
{ DeadVisitor{nodep, true, true, false, true, false, true}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ DeadVisitor{nodep, true, true, true, true, true}; } // Destruct before checking
|
||||
{ DeadVisitor{nodep, true, true, true, true, false, true}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
+23
-141
@@ -118,7 +118,6 @@
|
||||
#include "V3Delayed.h"
|
||||
|
||||
#include "V3AstUserAllocator.h"
|
||||
#include "V3ClassGraph.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
@@ -251,7 +250,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
// AstVar::user1() -> bool. Set true if already issued MULTIDRIVEN warning
|
||||
// AstVarRef::user1() -> bool. Set true if target of NBA
|
||||
// AstAssignDly::user1() -> bool. Set true if already visited
|
||||
// AstCFunc::user1() -> AstUser1Allocator. See `m_cfuncsCache` below
|
||||
// AstAssignDly::user2p() -> AstVarScope*: Scope this AstAssignDelay is under
|
||||
// AstNodeModule::user1p() -> std::unorded_map<std::string, AstVar*> temp map via m_varMap
|
||||
// AstScope::user1() -> int: Temporary counter for this scope
|
||||
@@ -261,24 +259,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
const VNUser1InUse m_user1InUse;
|
||||
const VNUser2InUse m_user2InUse;
|
||||
const VNUser3InUse m_user3InUse;
|
||||
|
||||
struct CFuncCache final {
|
||||
VInsertionSet<AstSenTree*> m_timingDomains; // What shall be added to m_timingDomains
|
||||
std::set<AstCFunc*>
|
||||
m_includes; // CFuncs whose CFuncCache shall be included into this - this is used to
|
||||
// break cycles: A->B->A (instead of visiting A while it is still begin
|
||||
// visited B just marks that it includes A)
|
||||
enum State : uint8_t {
|
||||
UNINITIALIZED = 0, // Not initialized members are empty
|
||||
VISITING, // Visiting - needed for breaking recursion
|
||||
INITIALIZED, // Members contains correct values
|
||||
} m_state // Current state of Cache
|
||||
= UNINITIALIZED;
|
||||
};
|
||||
|
||||
// Caches what should be added to m_timingDomains because of calls to the AstCFunc (with
|
||||
// recursive check of other AstCFuncs called from inside)
|
||||
AstUser1Allocator<AstCFunc, CFuncCache> m_cfuncsCache;
|
||||
AstUser1Allocator<AstNodeModule, std::unordered_map<std::string, AstVar*>> m_varMap;
|
||||
AstUser1Allocator<AstVarScope, VarScopeInfo> m_vscpInfo;
|
||||
AstUser3Allocator<AstVarScope, std::vector<WriteReference>> m_writeRefs;
|
||||
@@ -286,10 +266,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
// STATE - across all visitors
|
||||
VInsertionSet<AstSenTree*> m_timingDomains; // Timing resume domains
|
||||
|
||||
const std::unique_ptr<V3ClassGraph>
|
||||
m_classGraphp; // class graph to get possibly called functions from a virtual call
|
||||
std::vector<const AstCFunc*> m_callStack; // Current callstack of AstCFuncs
|
||||
|
||||
// STATE - for current visit position (use VL_RESTORER)
|
||||
AstActive* m_activep = nullptr; // Current activate
|
||||
const AstCFunc* m_cfuncp = nullptr; // Current public C Function
|
||||
@@ -300,7 +276,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
|
||||
bool m_inNonCombLogic = false; // We are in non-combinational logic
|
||||
bool m_needsInitialTrigger = false; // Whether a NodeProcedure needs a initial trigger
|
||||
std::vector<AstSenTree*> m_nbaEventSenTreeps; // Sensitivities of '->>' in the process
|
||||
AstVarRef* m_currNbaLhsRefp = nullptr; // Current NBA LHS variable reference
|
||||
|
||||
// STATE - during NBA conversion (after visit)
|
||||
@@ -318,7 +293,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
VDouble0 m_nSchemeValueQueuesPartial; // Number of variables using Scheme::ValueQueuePartial
|
||||
VDouble0 m_nSharedSetFlags; // "Set" flags actually shared by Scheme::FlagShared variables
|
||||
VDouble0 m_nInitialNBA; // Number of procedural blocks with initial NBA
|
||||
VDouble0 m_nonInlinedCAwaitsWithSenTree; // Count uses of not inlined co_awaits
|
||||
|
||||
// METHODS
|
||||
|
||||
@@ -589,17 +563,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void addCFuncCachedValues(const AstCFunc* const cfuncp,
|
||||
std::unordered_set<const AstCFunc*>& visited) {
|
||||
if (!visited.insert(cfuncp).second) return;
|
||||
CFuncCache& value = m_cfuncsCache(cfuncp);
|
||||
m_timingDomains.insert(value.m_timingDomains.begin(), value.m_timingDomains.end());
|
||||
m_nonInlinedCAwaitsWithSenTree += value.m_timingDomains.size();
|
||||
for (const AstCFunc* const includedp : value.m_includes) {
|
||||
addCFuncCachedValues(includedp, visited);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a temporary variable in the given 'scopep', with the given 'name', and with 'dtypep'
|
||||
// type, with the bits selected by 'sLsbp'/'sWidthp' set to 'valuep', other bits set to zero.
|
||||
// Insert new statements before 'insertp'.
|
||||
@@ -821,7 +784,7 @@ class DelayedVisitor final : public VNVisitor {
|
||||
m_prevVscp = vscp;
|
||||
|
||||
// Delete original NBA
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
}
|
||||
|
||||
// Scheme::FlagUnique
|
||||
@@ -871,7 +834,7 @@ class DelayedVisitor final : public VNVisitor {
|
||||
ifp->addThensp(new AstAssign{flp, capturedLhsp, capturedRhsp});
|
||||
|
||||
// Delete original NBA
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
}
|
||||
|
||||
// Scheme::ValueQueuePartial/Scheme::ValueQueueWhole
|
||||
@@ -902,9 +865,8 @@ class DelayedVisitor final : public VNVisitor {
|
||||
activep->addStmtsp(postp);
|
||||
// Add the commit
|
||||
AstCMethodHard* const callp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, queueVscp, VAccess::READWRITE}, VCMethod::NBA_COMMIT};
|
||||
flp, new AstVarRef{flp, queueVscp, VAccess::READWRITE}, VCMethod::SCHED_COMMIT};
|
||||
callp->dtypeSetVoid();
|
||||
// TODO: this is a partial update, so must be READWRITE, but that breaks scheduling
|
||||
callp->addPinsp(new AstVarRef{flp, vscp, VAccess::WRITE});
|
||||
postp->addStmtsp(callp->makeStmt());
|
||||
}
|
||||
@@ -1013,7 +975,7 @@ class DelayedVisitor final : public VNVisitor {
|
||||
// Enqueue the update at the site of the original NBA
|
||||
AstCMethodHard* const callp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, vscpInfo.valueQueueKit().vscp, VAccess::READWRITE},
|
||||
VCMethod::NBA_ENQUEUE};
|
||||
VCMethod::SCHED_ENQUEUE};
|
||||
callp->dtypeSetVoid();
|
||||
callp->addPinsp(valuep);
|
||||
if (partial) callp->addPinsp(maskp);
|
||||
@@ -1021,7 +983,7 @@ class DelayedVisitor final : public VNVisitor {
|
||||
nodep->addHereThisAsNext(callp->makeStmt());
|
||||
|
||||
// Delete original NBA
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
}
|
||||
|
||||
// Record where a variable is assigned
|
||||
@@ -1045,43 +1007,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
return procedurep && procedurep->sentreep();
|
||||
}
|
||||
|
||||
// Visit AstCFunc from a AstNodeCCall - this is made into a separate quasi-visitor because
|
||||
// AstCFunc that is not called from the code (e.g.: DPI exports) does not need to be visited
|
||||
// this way. Also, not visiting such AstCFuncs allows to avoid caching results for them which
|
||||
// this function does - which could lead to excessive memory usage
|
||||
void visitCalledCFunc(AstCFunc* const nodep) {
|
||||
CFuncCache& value = m_cfuncsCache(nodep);
|
||||
switch (value.m_state) {
|
||||
case CFuncCache::UNINITIALIZED: {
|
||||
// Save current state
|
||||
VL_RESTORER_CLEAR(m_timingDomains);
|
||||
|
||||
// Visit
|
||||
value.m_state = CFuncCache::VISITING;
|
||||
m_callStack.push_back(nodep);
|
||||
{
|
||||
VL_RESTORER(m_cfuncp);
|
||||
m_cfuncp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_callStack.pop_back();
|
||||
value.m_state = CFuncCache::INITIALIZED;
|
||||
|
||||
// Save a cache
|
||||
std::swap(m_timingDomains, value.m_timingDomains);
|
||||
} break;
|
||||
case CFuncCache::VISITING: {
|
||||
for (size_t i = m_callStack.size() - 1; m_callStack.at(i) != nodep; --i) {
|
||||
m_cfuncsCache(m_callStack[i]).m_includes.insert(nodep);
|
||||
}
|
||||
return; // Break recursion
|
||||
}
|
||||
case CFuncCache::INITIALIZED: break;
|
||||
}
|
||||
std::unordered_set<const AstCFunc*> visited;
|
||||
addCFuncCachedValues(nodep, visited);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNetlist* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
@@ -1178,6 +1103,11 @@ class DelayedVisitor final : public VNVisitor {
|
||||
m_scopep = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
m_cfuncp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstActive* nodep) override {
|
||||
UASSERT_OBJ(!m_activep, nodep, "Should not nest");
|
||||
VL_RESTORER(m_activep);
|
||||
@@ -1191,7 +1121,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
}
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_needsInitialTrigger);
|
||||
VL_RESTORER_CLEAR(m_nbaEventSenTreeps);
|
||||
const size_t firstNBAAddedIndex = m_nbas.size();
|
||||
{
|
||||
VL_RESTORER(m_inSuspendableOrFork);
|
||||
@@ -1243,8 +1172,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
for (size_t i = firstNBAAddedIndex; i < m_nbas.size(); ++i) {
|
||||
m_vscpInfo(m_nbas[i].vscp).addSensitivity(senItemp);
|
||||
}
|
||||
for (AstSenTree* const senTreep : m_nbaEventSenTreeps)
|
||||
senTreep->addSensesp(senItemp->cloneTree(true));
|
||||
// Done with these
|
||||
VL_DO_DANGLING(senItemp->deleteTree(), senItemp);
|
||||
}
|
||||
@@ -1255,7 +1182,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
}
|
||||
void visit(AstCAwait* nodep) override {
|
||||
if (nodep->sentreep()) m_timingDomains.insert(nodep->sentreep());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstFireEvent* nodep) override {
|
||||
UASSERT_OBJ(v3Global.hasEvents(), nodep, "Inconsistent");
|
||||
@@ -1270,15 +1196,8 @@ class DelayedVisitor final : public VNVisitor {
|
||||
cstmtp->add(");");
|
||||
|
||||
AstNode* newp = cstmtp;
|
||||
const AstVarRef* const vrefp = VN_CAST(eventp, VarRef);
|
||||
if (nodep->isDelayed() && (m_cfuncp || !vrefp)) {
|
||||
// V3Timing converts these with --timing
|
||||
nodep->v3warn(E_NOTIMING, "Nonblocking event trigger "
|
||||
<< (m_cfuncp ? "in a non-inlined function/task"
|
||||
: "of a class or interface member or "
|
||||
"array element")
|
||||
<< " requires --timing");
|
||||
} else if (nodep->isDelayed()) {
|
||||
if (nodep->isDelayed()) {
|
||||
const AstVarRef* const vrefp = VN_AS(eventp, VarRef);
|
||||
const std::string newvarname = "__Vdly__" + vrefp->varp()->shortName();
|
||||
AstVarScope* const dlyvscp
|
||||
= createTemp(flp, vrefp->varScopep()->scopep(), newvarname, 1);
|
||||
@@ -1287,35 +1206,20 @@ class DelayedVisitor final : public VNVisitor {
|
||||
return new AstVarRef{flp, dlyvscp, access};
|
||||
};
|
||||
|
||||
AstAlwaysPre* const prep = new AstAlwaysPre{flp};
|
||||
prep->addStmtsp(new AstAssign{flp, dlyRef(VAccess::WRITE),
|
||||
new AstConst{flp, AstConst::BitFalse{}}});
|
||||
AstAlwaysPost* const postp = new AstAlwaysPost{flp};
|
||||
AstIf* const ifp = new AstIf{flp, dlyRef(VAccess::READ)};
|
||||
postp->addStmtsp(ifp);
|
||||
{
|
||||
AstIf* const ifp = new AstIf{flp, dlyRef(VAccess::READ)};
|
||||
postp->addStmtsp(ifp);
|
||||
ifp->addThensp(newp);
|
||||
}
|
||||
|
||||
UASSERT_OBJ(m_activep, nodep, "No active to handle FireEvent");
|
||||
AstSenTree* senTreep = m_activep->sentreep();
|
||||
AstActive* activep = nullptr;
|
||||
if (m_inSuspendableOrFork && (senTreep->hasInitial() || senTreep->hasClocked())) {
|
||||
// Suspendable code can trigger whenever it resumes, so fire when its NBAs are
|
||||
// committed (see visit(AstNodeProcedure*)), clearing the flag like
|
||||
// Scheme::FlagUnique
|
||||
senTreep = new AstSenTree{
|
||||
flp, senTreep->hasClocked() ? senTreep->sensesp()->cloneTree(true) : nullptr};
|
||||
m_nbaEventSenTreeps.push_back(senTreep);
|
||||
m_needsInitialTrigger |= m_timingDomains.empty();
|
||||
dlyvscp->varp()->setIgnorePostWrite();
|
||||
ifp->addThensp(new AstAssign{flp, dlyRef(VAccess::WRITE),
|
||||
new AstConst{flp, AstConst::BitFalse{}}});
|
||||
activep = new AstActive{flp, "nba-event", senTreep};
|
||||
activep->senTreeStorep(senTreep);
|
||||
} else {
|
||||
activep = new AstActive{flp, "nba-event", senTreep};
|
||||
AstAlwaysPre* const prep = new AstAlwaysPre{flp};
|
||||
prep->addStmtsp(new AstAssign{flp, dlyRef(VAccess::WRITE),
|
||||
new AstConst{flp, AstConst::BitFalse{}}});
|
||||
activep->addStmtsp(prep);
|
||||
}
|
||||
ifp->addThensp(newp);
|
||||
AstActive* const activep = new AstActive{flp, "nba-event", m_activep->sentreep()};
|
||||
m_activep->addNextHere(activep);
|
||||
activep->addStmtsp(prep);
|
||||
activep->addStmtsp(postp);
|
||||
|
||||
newp = new AstAssign{flp, dlyRef(VAccess::WRITE),
|
||||
@@ -1433,24 +1337,6 @@ class DelayedVisitor final : public VNVisitor {
|
||||
m_inLoop = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeCCall* const nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// We need to visit bodies of non-inlined functions
|
||||
const auto& cfuncps = m_classGraphp->getCallPossibleCFuncs(nodep);
|
||||
if (cfuncps.empty()) {
|
||||
visitCalledCFunc(nodep->funcp());
|
||||
} else {
|
||||
for (AstCFunc* const cfuncp : cfuncps) visitCalledCFunc(cfuncp);
|
||||
}
|
||||
}
|
||||
void visit(AstCFunc* const nodep) override {
|
||||
const auto& value = m_cfuncsCache(nodep);
|
||||
// Check whether it was already visited by visitCalledCFunc()
|
||||
if (value.m_state != CFuncCache::UNINITIALIZED) return;
|
||||
VL_RESTORER(m_cfuncp);
|
||||
m_cfuncp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Pre/Post logic are created here and their content need no further changes, so ignore.
|
||||
void visit(AstAlwaysPre*) override {}
|
||||
@@ -1461,10 +1347,7 @@ class DelayedVisitor final : public VNVisitor {
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit DelayedVisitor(AstNetlist* nodep)
|
||||
: m_classGraphp{V3ClassGraph::build(nodep)} {
|
||||
iterate(nodep);
|
||||
}
|
||||
explicit DelayedVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
~DelayedVisitor() override {
|
||||
V3Stats::addStat("NBA, variables using ShadowVar scheme", m_nSchemeShadowVar);
|
||||
V3Stats::addStat("NBA, variables using ShadowVarMasked scheme", m_nSchemeShadowVarMasked);
|
||||
@@ -1475,7 +1358,6 @@ public:
|
||||
m_nSchemeValueQueuesPartial);
|
||||
V3Stats::addStat("Optimizations, NBA flags shared", m_nSharedSetFlags);
|
||||
V3Stats::addStat("Procedures needing initial NBA trigger", m_nInitialNBA);
|
||||
V3Stats::addStat("Non-inlined co_awaits with SenTree", m_nonInlinedCAwaitsWithSenTree);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+1
-4
@@ -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
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "V3Descope.h"
|
||||
|
||||
#include "V3ConstPool.h"
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
@@ -160,7 +159,8 @@ class DescopeVisitor final : public VNVisitor {
|
||||
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
|
||||
if (portp->isIO() && !portp->isFuncReturn()) {
|
||||
AstVarRef* const newp = new AstVarRef{
|
||||
portp->fileline(), portp, portp->direction().pinAccess()};
|
||||
portp->fileline(), portp,
|
||||
portp->isWritable() ? VAccess::WRITE : VAccess::READ};
|
||||
argsp = AstNode::addNext(argsp, newp);
|
||||
}
|
||||
}
|
||||
@@ -236,7 +236,7 @@ class DescopeVisitor final : public VNVisitor {
|
||||
if (varp->isFuncLocal()) {
|
||||
// Reference to function locals need no self pointer
|
||||
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
|
||||
} else if (scopep->modp()->isConstPool()) {
|
||||
} else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) {
|
||||
// Reference to constant pool value need no self pointer
|
||||
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
|
||||
} else {
|
||||
@@ -295,6 +295,5 @@ public:
|
||||
void V3Descope::descopeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ DescopeVisitor{nodep}; } // Destruct before checking
|
||||
V3ConstPool::setDescoped();
|
||||
V3Global::dumpCheckGlobalTree("descope", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
+25
-76
@@ -21,7 +21,6 @@
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
@@ -32,8 +31,6 @@ DfgGraph::DfgGraph(const string& name)
|
||||
: m_name{name} {}
|
||||
|
||||
DfgGraph::~DfgGraph() {
|
||||
V3Stats::addStatSum("Optimizations, DFG, temporary declarations reused",
|
||||
static_cast<double>(m_tempDeclarationsReused));
|
||||
forEachVertex([&](DfgVertex& vtx) { vtx.unlinkDelete(*this); });
|
||||
}
|
||||
|
||||
@@ -101,31 +98,26 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
|
||||
}
|
||||
}
|
||||
|
||||
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& prefix,
|
||||
const DfgDataType& dtype, AstScope* scopep) {
|
||||
// AstVar declarations outlive all DFG graphs. Splitting or merging graphs
|
||||
// does not transfer slots: each graph creates globally unique declarations.
|
||||
TempDeclarations& temps = m_temporaries[scopep->modp()][{prefix, dtype.astDtypep()}];
|
||||
const size_t slot = temps.m_scopeCounts[scopep]++;
|
||||
AstVar* varp;
|
||||
if (slot == temps.m_declps.size()) {
|
||||
// Construct the name stub on the first new declaration in this graph
|
||||
if (m_tmpNameStub.empty()) {
|
||||
// Use the hash of the graph name (avoid long names and non-identifiers)
|
||||
const std::string hash = V3Hash{m_name}.toString();
|
||||
// Graph hashes may collide, so track multiplicity to keep names globally unique
|
||||
static std::unordered_map<std::string, uint32_t> s_multiplicity;
|
||||
m_tmpNameStub += '_' + hash + '_' + std::to_string(s_multiplicity[hash]++) + '_';
|
||||
}
|
||||
const std::string varName
|
||||
= "__Vdfg" + prefix + m_tmpNameStub + std::to_string(m_tmpNameCount++);
|
||||
varp = new AstVar{flp, VVarType::MODULETEMP, varName, dtype.astDtypep()};
|
||||
scopep->modp()->addStmtsp(varp);
|
||||
temps.m_declps.emplace_back(varp);
|
||||
} else {
|
||||
varp = temps.m_declps[slot];
|
||||
++m_tempDeclarationsReused;
|
||||
std::string DfgGraph::makeUniqueName(const std::string& prefix, size_t n) {
|
||||
// Construct the tmpNameStub if we have not done so yet
|
||||
if (m_tmpNameStub.empty()) {
|
||||
// Use the hash of the graph name (avoid long names and non-identifiers)
|
||||
const std::string hash = V3Hash{m_name}.toString();
|
||||
// We need to keep every variable globally unique, and graph hashed
|
||||
// names might not be, so keep a static table to track multiplicity
|
||||
static std::unordered_map<std::string, uint32_t> s_multiplicity;
|
||||
m_tmpNameStub += '_' + hash + '_' + std::to_string(s_multiplicity[hash]++) + '_';
|
||||
}
|
||||
// Assemble the globally unique name
|
||||
return "__Vdfg" + prefix + m_tmpNameStub + std::to_string(n);
|
||||
}
|
||||
|
||||
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name,
|
||||
const DfgDataType& dtype, AstScope* scopep) {
|
||||
// Create AstVar
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtype.astDtypep()};
|
||||
// Add AstVar to the scope's module
|
||||
scopep->modp()->addStmtsp(varp);
|
||||
// Create AstVarScope
|
||||
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
|
||||
// Add to scope
|
||||
@@ -481,11 +473,8 @@ DfgGraph::neighborhood(const std::vector<const DfgVertex*>& vtxps, size_t n) con
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgVertex
|
||||
|
||||
DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dt,
|
||||
DfgEdge* inlineInputsp, uint32_t nInputs)
|
||||
: m_inlineInputsp{inlineInputsp}
|
||||
, m_nInputs{nInputs}
|
||||
, m_filelinep{flp}
|
||||
DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dt)
|
||||
: m_filelinep{flp}
|
||||
, m_dtype{dt}
|
||||
, m_type{type} {
|
||||
dfg.addVertex(*this);
|
||||
@@ -802,18 +791,19 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED
|
||||
}
|
||||
|
||||
AstScope* const rootp = v3Global.rootp()->topScopep()->scopep();
|
||||
AstScope* const constPoolp = v3Global.rootp()->constPoolp()->scopep();
|
||||
|
||||
// Note: the recursive invocation can cause a re-hash but that will not invalidate references
|
||||
AstScope*& resultr = cache[this];
|
||||
if (!resultr) {
|
||||
// Mark to prevent infinite recursion on circular graphs - should never be called on such
|
||||
resultr = reinterpret_cast<AstScope*>(1);
|
||||
// Find scope based on sources, falling back on the root scope. Never use a package
|
||||
// scope, including the constant pool, so new variables are not created in packages.
|
||||
// Find scope based on sources, falling back on the root scope,
|
||||
// also make sure it's not the constant pool scope, which is special.
|
||||
AstScope* foundp = nullptr;
|
||||
foreachSource([&](DfgVertex& src) {
|
||||
AstScope* const scp = src.scopep(cache, true);
|
||||
if (scp != rootp && !VN_IS(scp->modp(), Package)) {
|
||||
if (scp != rootp && scp != constPoolp) {
|
||||
foundp = scp;
|
||||
return true;
|
||||
}
|
||||
@@ -839,47 +829,6 @@ void DfgVertex::unlinkDelete(DfgGraph& dfg) {
|
||||
delete this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgVertexVar
|
||||
|
||||
std::pair<DfgVertex*, uint32_t> DfgVertexVar::driverOfRange(uint32_t lo, uint32_t size) {
|
||||
DfgVertex* const srcp = this->srcp();
|
||||
// Not driven at all
|
||||
if (!srcp) return {nullptr, 0};
|
||||
// If volatile, can have other drivers
|
||||
if (isVolatile()) return {nullptr, 0};
|
||||
// Don't inline CReset
|
||||
if (srcp->is<DfgCReset>()) return {nullptr, 0};
|
||||
|
||||
// If not driven via a splice, then it is driven whole, at the same offsets
|
||||
DfgVertexSplice* const splicep = srcp->cast<DfgVertexSplice>();
|
||||
if (!splicep) return {srcp, lo};
|
||||
|
||||
// Find the driver that covers the whole searched range, if there is a single one
|
||||
const uint32_t hi = lo + size - 1;
|
||||
DfgVertex* driverp = nullptr;
|
||||
uint32_t driverLo = 0;
|
||||
bool useDefault = defaultp();
|
||||
splicep->foreachDriver([&](DfgVertex& src, const uint32_t dLo) {
|
||||
const uint32_t dHi = dLo + src.size() - 1;
|
||||
// Note whether it overlaps the searched range, so the default cannot be used
|
||||
if (dLo <= hi && lo <= dHi) useDefault = false;
|
||||
// If it does not cover the whole searched range, move on
|
||||
if (lo < dLo || dHi < hi) return false;
|
||||
// Save the driver that covers the whole searched range
|
||||
driverp = &src;
|
||||
driverLo = dLo;
|
||||
return true;
|
||||
});
|
||||
|
||||
// If a single driver covers the searched range, it is the one
|
||||
if (driverp) return {driverp, lo - driverLo};
|
||||
// Otherwise the default driver is responsible for it, if nothing else overlaps it
|
||||
if (useDefault) return {defaultp(), lo};
|
||||
// Not driven by a single vertex
|
||||
return {nullptr, 0};
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Renders the canonical pattern S-expression for a single DfgVertex
|
||||
|
||||
|
||||
+31
-87
@@ -46,11 +46,9 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifndef VL_NOT_FINAL
|
||||
@@ -121,28 +119,6 @@ public:
|
||||
inline void relinkSrcp(DfgVertex* srcp);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Input edge storage of a fixed arity vertices, embedded in the vertex itself.
|
||||
template <uint32_t N_Edges>
|
||||
class DfgInlineEdgeStorage final {
|
||||
static_assert(N_Edges > 0, "'DfgInlineEdgeStorage' must hold at least one edge");
|
||||
VL_UNCOPYABLE(DfgInlineEdgeStorage);
|
||||
VL_UNMOVABLE(DfgInlineEdgeStorage);
|
||||
|
||||
public:
|
||||
union { // Union, for manual memory management, so DfgEdge need no implicit constructor
|
||||
DfgEdge m_edges[N_Edges]; // The input edges of the owning vertex
|
||||
};
|
||||
|
||||
explicit DfgInlineEdgeStorage(DfgVertex* vtxp) {
|
||||
for (uint32_t i = 0; i < N_Edges; ++i) new (&m_edges[i]) DfgEdge{vtxp};
|
||||
}
|
||||
~DfgInlineEdgeStorage() {
|
||||
for (uint32_t i = 0; i < N_Edges; ++i) m_edges[i].~DfgEdge();
|
||||
}
|
||||
DfgInlineEdgeStorage() = delete;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dataflow graph vertex
|
||||
class DfgVertex VL_NOT_FINAL {
|
||||
@@ -151,12 +127,10 @@ class DfgVertex VL_NOT_FINAL {
|
||||
friend class DfgVisitor;
|
||||
template <typename, bool>
|
||||
friend class DfgUserMap;
|
||||
friend class DfgVertexVariadic;
|
||||
|
||||
// STATE
|
||||
V3ListLinks<DfgVertex> m_links; // V3List links in the DfgGraph
|
||||
DfgEdge* const m_inlineInputsp; // Input edges stored inline in the vertex (iff fixed arity)
|
||||
uint32_t m_nInputs; // Number of input edges
|
||||
std::vector<std::unique_ptr<DfgEdge>> m_inputps; // Input edges, as vector, for fast indexing
|
||||
DfgEdge::List m_sinks; // List of sink edges of this vertex
|
||||
|
||||
FileLine* const m_filelinep; // Source location
|
||||
@@ -186,22 +160,25 @@ public:
|
||||
|
||||
protected:
|
||||
// CONSTRUCTOR
|
||||
DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dt,
|
||||
DfgEdge* inlineInputsp, uint32_t nInputs) VL_MT_DISABLED;
|
||||
DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dt) VL_MT_DISABLED;
|
||||
// Use unlinkDelete instead
|
||||
virtual ~DfgVertex() VL_MT_DISABLED = default;
|
||||
|
||||
private:
|
||||
// Get input edge 'i'
|
||||
inline DfgEdge* inputEdgep(size_t i) const;
|
||||
// Create a new input edge and return it
|
||||
DfgEdge* newInput() {
|
||||
m_inputps.emplace_back(new DfgEdge{this});
|
||||
return m_inputps.back().get();
|
||||
}
|
||||
|
||||
public:
|
||||
// Get input 'i'
|
||||
DfgVertex* inputp(size_t i) const { return inputEdgep(i)->srcp(); }
|
||||
DfgVertex* inputp(size_t i) const { return m_inputps[i]->srcp(); }
|
||||
// Relink input 'i'
|
||||
void inputp(size_t i, DfgVertex* vtxp) { inputEdgep(i)->relinkSrcp(vtxp); }
|
||||
void inputp(size_t i, DfgVertex* vtxp) { m_inputps[i]->relinkSrcp(vtxp); }
|
||||
// The number of inputs this vertex has. Some might be unconnected.
|
||||
size_t nInputs() const { return m_nInputs; }
|
||||
size_t nInputs() const { return m_inputps.size(); }
|
||||
// Unlink all inputs and reset to no inputs - use very carefully
|
||||
void resetInputs() { m_inputps.clear(); }
|
||||
|
||||
// The type of this vertex
|
||||
VDfgType type() const { return m_type; }
|
||||
@@ -270,12 +247,9 @@ public:
|
||||
// Calls given function 'f' for each source vertex of this vertex. If 'f'
|
||||
// returns true, further sources are not iterated and this method returns
|
||||
// true itself. Unconnected source edges are not iterated.
|
||||
template <typename T_Callable>
|
||||
bool foreachSource(T_Callable&& f) {
|
||||
static_assert(vlstd::is_invocable_r<bool, T_Callable, DfgVertex&>::value,
|
||||
"T_Callable 'f' must have a signature compatible with 'bool(DfgVertex&)'");
|
||||
for (size_t i = 0; i < m_nInputs; ++i) {
|
||||
if (DfgVertex* const srcp = inputEdgep(i)->srcp()) {
|
||||
bool foreachSource(std::function<bool(DfgVertex&)> f) {
|
||||
for (const std::unique_ptr<DfgEdge>& edgep : m_inputps) {
|
||||
if (DfgVertex* const srcp = edgep->srcp()) {
|
||||
if (f(*srcp)) return true;
|
||||
}
|
||||
}
|
||||
@@ -285,13 +259,9 @@ public:
|
||||
// Calls given function 'f' for each source vertex of this vertex. If 'f'
|
||||
// returns true, further sources are not iterated and this method returns
|
||||
// true itself. Unconnected source edges are not iterated.
|
||||
template <typename T_Callable>
|
||||
bool foreachSource(T_Callable&& f) const {
|
||||
static_assert(
|
||||
vlstd::is_invocable_r<bool, T_Callable, const DfgVertex&>::value,
|
||||
"T_Callable 'f' must have a signature compatible with 'bool(const DfgVertex&)'");
|
||||
for (size_t i = 0; i < m_nInputs; ++i) {
|
||||
if (const DfgVertex* const srcp = inputEdgep(i)->srcp()) {
|
||||
bool foreachSource(std::function<bool(const DfgVertex&)> f) const {
|
||||
for (const std::unique_ptr<DfgEdge>& edgep : m_inputps) {
|
||||
if (DfgVertex* const srcp = edgep->srcp()) {
|
||||
if (f(*srcp)) return true;
|
||||
}
|
||||
}
|
||||
@@ -302,10 +272,7 @@ public:
|
||||
// returns true, further sinks are not iterated and this method returns
|
||||
// true itself. Unlinking/deleting the given sink during iteration is safe,
|
||||
// but not other sinks of this vertex.
|
||||
template <typename T_Callable>
|
||||
bool foreachSink(T_Callable&& f) {
|
||||
static_assert(vlstd::is_invocable_r<bool, T_Callable, DfgVertex&>::value,
|
||||
"T_Callable 'f' must have a signature compatible with 'bool(DfgVertex&)'");
|
||||
bool foreachSink(std::function<bool(DfgVertex&)> f) {
|
||||
for (const DfgEdge* const edgep : m_sinks.unlinkable()) {
|
||||
if (f(*edgep->dstp())) return true;
|
||||
}
|
||||
@@ -315,11 +282,7 @@ public:
|
||||
// Calls given function 'f' for each sink vertex of this vertex. If 'f'
|
||||
// returns true, further sinks are not iterated and this method returns
|
||||
// true itself.
|
||||
template <typename T_Callable>
|
||||
bool foreachSink(T_Callable&& f) const {
|
||||
static_assert(
|
||||
vlstd::is_invocable_r<bool, T_Callable, const DfgVertex&>::value,
|
||||
"T_Callable 'f' must have a signature compatible with 'bool(const DfgVertex&)'");
|
||||
bool foreachSink(std::function<bool(const DfgVertex&)> f) const {
|
||||
for (const DfgEdge& edge : m_sinks) {
|
||||
if (f(*edge.dstp())) return true;
|
||||
}
|
||||
@@ -430,19 +393,6 @@ class DfgGraph final {
|
||||
size_t m_size = 0; // Number of vertices in the graph
|
||||
const std::string m_name; // Name of graph - need not be unique
|
||||
std::string m_tmpNameStub{""}; // Name stub for temporary variables - computed lazy
|
||||
size_t m_tmpNameCount = 0; // Sequence number for newly created temporary declarations
|
||||
|
||||
// Slots are local to this graph and keyed by module, prefix and type.
|
||||
// Different prefixes may carry different AstVar attributes, but temporaries
|
||||
// with the same prefix may share an AstVar, so they must have identical ones.
|
||||
// Each scope consumes each slot at most once.
|
||||
struct TempDeclarations final {
|
||||
std::map<AstScope*, size_t> m_scopeCounts; // Next slot for each instance
|
||||
std::vector<AstVar*> m_declps; // Declarations indexed by slot
|
||||
};
|
||||
std::map<AstNodeModule*, std::map<std::pair<std::string, AstNodeDType*>, TempDeclarations>>
|
||||
m_temporaries; // Shared slots indexed by module, purpose, and type
|
||||
uint64_t m_tempDeclarationsReused = 0; // Declarations shared across instance scopes
|
||||
|
||||
// The only way to access thes is via DfgUserMap, so mutable is appropriate,
|
||||
// the map can change while the graph is const.
|
||||
@@ -541,12 +491,14 @@ public:
|
||||
// DfgVertexVar instances representing the same Ast variable are unified.
|
||||
void mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) VL_MT_DISABLED;
|
||||
|
||||
// Create a new scoped variable. Instances of a module share temporary
|
||||
// declarations of the same prefix and type, but have independent storage.
|
||||
// Each scope uses a declaration at most once; new declarations get unique names.
|
||||
// As the AstVar may be shared, callers must set identical AstVar attributes
|
||||
// on all temporaries created with the same prefix.
|
||||
DfgVertexVar* makeNewVar(FileLine*, const std::string& prefix, const DfgDataType&,
|
||||
// Genarete a unique name. The provided 'prefix' and 'n' values will be part of the name, and
|
||||
// must be unique (as a pair) in each invocation for this graph.
|
||||
std::string makeUniqueName(const std::string& prefix, size_t n) VL_MT_DISABLED;
|
||||
|
||||
// Create a new variable with the given name and data type. For a Scoped
|
||||
// Dfg, the AstScope where the corresponding AstVarScope will be inserted
|
||||
// must be provided
|
||||
DfgVertexVar* makeNewVar(FileLine*, const std::string& name, const DfgDataType&,
|
||||
AstScope*) VL_MT_DISABLED;
|
||||
|
||||
// Split this graph into individual components (unique sub-graphs with no edges between them).
|
||||
@@ -606,14 +558,14 @@ namespace V3Dfg {
|
||||
// Returns true if variable can be represented in the graph
|
||||
inline bool isSupported(const AstVarScope* vscp) {
|
||||
const AstNodeModule* const modp = vscp->scopep()->modp();
|
||||
if (VN_IS(modp, Module) || VN_IS(modp, Package)) {
|
||||
// Regular modules and packages supported
|
||||
if (VN_IS(modp, Module)) {
|
||||
// Regular module supported
|
||||
} else if (const AstIface* const ifacep = VN_CAST(modp, Iface)) {
|
||||
// Interfaces supported if there are no virtual interfaces for
|
||||
// them, otherwise they cannot be resovled statically.
|
||||
if (ifacep->hasVirtualRef()) return false;
|
||||
} else {
|
||||
return false; // Anything else (class, etc) not supported
|
||||
return false; // Anything else (package, class, etc) not supported
|
||||
}
|
||||
if (DfgVertexVar::hasRWRefs(vscp)) return false; // Referenced via READWRITE references
|
||||
// Check the AstVar
|
||||
@@ -859,14 +811,6 @@ void DfgEdge::relinkSrcp(DfgVertex* srcp) {
|
||||
|
||||
// DfgVertex {{{
|
||||
|
||||
DfgEdge* DfgVertex::inputEdgep(size_t i) const {
|
||||
UDEBUGONLY(UASSERT_OBJ(i < m_nInputs, this, "Input index out of range"););
|
||||
if (VL_LIKELY(m_inlineInputsp)) return m_inlineInputsp + i;
|
||||
// 'm_inlineInputsp' is null exactly for a DfgVertexVariadic
|
||||
UDEBUGONLY(UASSERT_OBJ(is<DfgVertexVariadic>(), this, "Vertex without input edge storage"););
|
||||
return static_cast<const DfgVertexVariadic*>(this)->m_edgeps[i].get();
|
||||
}
|
||||
|
||||
bool DfgVertex::isCheaperThanLoad() const {
|
||||
// Constants
|
||||
if (is<DfgConst>()) return true;
|
||||
|
||||
+186
-242
@@ -152,9 +152,9 @@ class TraceDriver final : public DfgVisitor {
|
||||
// TYPES
|
||||
// Key for caching the result of a trace
|
||||
struct CacheKey final {
|
||||
DfgVertex* m_vtxp; // Vertex being traced
|
||||
uint32_t m_lsb; // LSB of the range within m_vtxp being traced
|
||||
uint32_t m_msb; // MSB of the range within m_vtxp being traced
|
||||
DfgVertex* m_vtxp;
|
||||
uint32_t m_lsb;
|
||||
uint32_t m_msb;
|
||||
|
||||
CacheKey() = delete;
|
||||
CacheKey(DfgVertex* vtxp, uint32_t lsb, uint32_t msb)
|
||||
@@ -187,20 +187,11 @@ class TraceDriver final : public DfgVisitor {
|
||||
uint32_t m_lsb = 0; // LSB to extract from the currently visited Vertex
|
||||
uint32_t m_msb = 0; // MSB to extract from the currently visited Vertex
|
||||
std::vector<uint32_t> m_idxs; // Indices to extract from the currently visited Vertex
|
||||
std::vector<DfgVertex**> m_pendingps; // Pending pass-through cache entries across trace calls
|
||||
// Result of tracing the currently visited Vertex. Use RETURN_RESULT below!
|
||||
// Result of tracing the currently visited Vertex. Use SET_RESULT below!
|
||||
DfgVertex* m_resp = nullptr;
|
||||
// Set instead of 'm_resp' when the result of tracing the currently visited Vertex is
|
||||
// exactly the result of tracing 'm_tailp' at the updated [m_msb:m_lsb]. 'trace'
|
||||
// resolves these iteratively. Use RETURN_RESULT_TAIL below!
|
||||
DfgVertex* m_tailp = nullptr;
|
||||
// When tracing a splice, this is the corresponding 'defaultp' of the variable it drives
|
||||
DfgVertex* m_defaultp = nullptr;
|
||||
DfgVertex* m_splicep = nullptr; // The splice vertex being traced, just for assertions
|
||||
DfgVertex* m_defaultp = nullptr; // When tracing a variable, this is its 'defaultp', if any
|
||||
// Result cache for reusing already traced vertices
|
||||
std::unordered_map<CacheKey, DfgVertex*, CacheKey::Hash, CacheKey::Equal> m_cache;
|
||||
// Cache for boundary vertices (where m_sccInfo.get(*vtxp) != m_component)
|
||||
std::unordered_map<CacheKey, DfgVertex*, CacheKey::Hash, CacheKey::Equal> m_boundaryCache;
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
std::ofstream m_lineCoverageFile; // Line coverage file, just for testing
|
||||
@@ -239,10 +230,12 @@ class TraceDriver final : public DfgVisitor {
|
||||
|
||||
// Create temporary capable of holding the result of 'vtxp'
|
||||
DfgVertexVar* createTmp(const char* prefix, DfgVertex* vtxp) {
|
||||
AstNode* nodep = v3Global.rootp();
|
||||
const std::string name = m_dfg.makeUniqueName(prefix, nodep->user2Inc());
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
DfgVertex::ScopeCache scopeCache;
|
||||
AstScope* const scopep = vtxp->scopep(scopeCache);
|
||||
DfgVertexVar* const varp = m_dfg.makeNewVar(flp, prefix, vtxp->dtype(), scopep);
|
||||
DfgVertexVar* const varp = m_dfg.makeNewVar(flp, name, vtxp->dtype(), scopep);
|
||||
varp->vscp()->varp()->isInternal(true);
|
||||
varp->tmpForp(varp->vscp());
|
||||
m_sccInfo.add(*varp, 0);
|
||||
@@ -250,108 +243,60 @@ class TraceDriver final : public DfgVisitor {
|
||||
}
|
||||
|
||||
// Trace drivers of the given packed vertex, at the given bit range.
|
||||
DfgVertex* trace(DfgVertex* vtxp, uint32_t msb, uint32_t lsb) {
|
||||
// Many vertices just pass the traced bit range through to one of their sources
|
||||
// (see RETURN_RESULT_TAIL). Chains of such vertices can be tens of thousands of
|
||||
// vertices long in large designs (e.g.: a wide concatenation), so resolve them
|
||||
// in this loop instead of by recursion, which would overflow the C++ stack.
|
||||
// 'm_pendingps' holds the cache entries of the vertices on the current chain. They
|
||||
// all yield the same result, which is filled in when the chain is resolved.
|
||||
// Note: references into 'm_cache' are stable across the insertions below.
|
||||
DfgVertex* const startVtxp = vtxp;
|
||||
const uint32_t startMsb = msb;
|
||||
const uint32_t startLsb = lsb;
|
||||
const size_t pendingBegin = m_pendingps.size();
|
||||
DfgVertex* resp = nullptr;
|
||||
DfgVertex* trace(DfgVertex* const vtxp, const uint32_t msb, const uint32_t lsb) {
|
||||
UASSERT_OBJ(vtxp->isPacked(), vtxp, "Can only trace packed type vertices");
|
||||
UASSERT_OBJ(vtxp->size() > msb, vtxp, "Traced Vertex too narrow");
|
||||
|
||||
while (true) {
|
||||
UASSERT_OBJ(vtxp->isPacked(), vtxp, "Can only trace packed type vertices");
|
||||
UASSERT_OBJ(vtxp->size() > msb, vtxp, "Traced Vertex too narrow");
|
||||
UASSERT_OBJ(!m_defaultp || vtxp == m_splicep, vtxp, "Tracing wrong vertex");
|
||||
// Get the cache entry, which is the resulting driver that is not part of
|
||||
// the same component as vtxp
|
||||
DfgVertex*& respr = m_cache
|
||||
.emplace(std::piecewise_construct, //
|
||||
std::forward_as_tuple(vtxp, msb, lsb), //
|
||||
std::forward_as_tuple(nullptr))
|
||||
.first->second;
|
||||
|
||||
// Trace the vertex
|
||||
if (respr) {
|
||||
// If already traced this vtxp/msb/lsb, just use the result.
|
||||
// This is important to avoid combinatorial explosion when the
|
||||
// same sub-expression is needed multiple times.
|
||||
} else if (m_sccInfo.get(*vtxp) != m_component) {
|
||||
// If the currently traced vertex is in a different component,
|
||||
// then we found what we were looking for. But if it's a splice
|
||||
// with a corresponding default, we need to keep going as the
|
||||
// splice does not fully define the value we are seeking.
|
||||
if (m_sccInfo.get(*vtxp) != m_component && !m_defaultp) {
|
||||
DfgVertex*& respr
|
||||
= m_boundaryCache
|
||||
.emplace(std::piecewise_construct, std::forward_as_tuple(vtxp, msb, lsb),
|
||||
std::forward_as_tuple(nullptr))
|
||||
.first->second;
|
||||
if (!respr) {
|
||||
respr = vtxp;
|
||||
// If the result is a splice, we need to insert a temporary for it
|
||||
// as a splice cannot be fed into arbitray logic
|
||||
if (DfgVertexSplice* const splicep = respr->cast<DfgVertexSplice>()) {
|
||||
DfgVertexVar* const tmpp = createTmp("TraceDriver", splicep);
|
||||
// Note: we can't do 'splicep->replaceWith(tmpp)', as other
|
||||
// variable sinks of the splice might have a defaultp driver.
|
||||
tmpp->srcp(splicep);
|
||||
respr = tmpp;
|
||||
}
|
||||
// Apply a Sel to extract the relevant bits if only a part is needed
|
||||
if (msb != respr->width() - 1 || lsb != 0) {
|
||||
DfgSel* const selp = make<DfgSel>(respr, msb - lsb + 1);
|
||||
selp->fromp(respr);
|
||||
selp->lsb(lsb);
|
||||
respr = selp;
|
||||
}
|
||||
}
|
||||
resp = respr;
|
||||
break;
|
||||
// then we found what we were looking for.
|
||||
respr = vtxp;
|
||||
// If the result is a splice, we need to insert a temporary for it
|
||||
// as a splice cannot be fed into arbitray logic
|
||||
if (DfgVertexSplice* const splicep = respr->cast<DfgVertexSplice>()) {
|
||||
DfgVertexVar* const tmpp = createTmp("TraceDriver", splicep);
|
||||
// Note: we can't do 'splicep->replaceWith(tmpp)', as other
|
||||
// variable sinks of the splice might have a defaultp driver.
|
||||
tmpp->srcp(splicep);
|
||||
respr = tmpp;
|
||||
}
|
||||
|
||||
// Normal cache for intra-component traces
|
||||
DfgVertex*& respr = m_cache
|
||||
.emplace(std::piecewise_construct, //
|
||||
std::forward_as_tuple(vtxp, msb, lsb), //
|
||||
std::forward_as_tuple(nullptr))
|
||||
.first->second;
|
||||
|
||||
// Trace the vertex
|
||||
if (respr) {
|
||||
// If already traced this vtxp/msb/lsb, just use the result.
|
||||
// This is important to avoid combinatorial explosion when the
|
||||
// same sub-expression is needed multiple times.
|
||||
} else {
|
||||
// Otherwise visit the vertex to trace it
|
||||
VL_RESTORER(m_msb);
|
||||
VL_RESTORER(m_lsb);
|
||||
VL_RESTORER_CLEAR(m_idxs);
|
||||
VL_RESTORER(m_resp);
|
||||
VL_RESTORER(m_tailp);
|
||||
m_msb = msb;
|
||||
m_lsb = lsb;
|
||||
m_resp = nullptr;
|
||||
m_tailp = nullptr;
|
||||
iterate(vtxp);
|
||||
UASSERT_OBJ((m_resp != nullptr) != (m_tailp != nullptr), vtxp,
|
||||
"Expected exactly one trace result");
|
||||
if (m_tailp) {
|
||||
// This vertex just passes the range through, so continue with its
|
||||
// source, at the updated range. Its cache entry is filled in when
|
||||
// the chain is resolved.
|
||||
m_pendingps.push_back(&respr);
|
||||
vtxp = m_tailp;
|
||||
msb = m_msb;
|
||||
lsb = m_lsb;
|
||||
continue;
|
||||
}
|
||||
respr = m_resp;
|
||||
// Apply a Sel to extract the relevant bits if only a part is needed
|
||||
if (msb != respr->width() - 1 || lsb != 0) {
|
||||
DfgSel* const selp = make<DfgSel>(respr, msb - lsb + 1);
|
||||
selp->fromp(respr);
|
||||
selp->lsb(lsb);
|
||||
respr = selp;
|
||||
}
|
||||
resp = respr;
|
||||
break;
|
||||
} else {
|
||||
// Otherwise visit the vertex to trace it
|
||||
VL_RESTORER(m_msb);
|
||||
VL_RESTORER(m_lsb);
|
||||
VL_RESTORER_CLEAR(m_idxs);
|
||||
VL_RESTORER(m_resp);
|
||||
m_msb = msb;
|
||||
m_lsb = lsb;
|
||||
m_resp = nullptr;
|
||||
iterate(vtxp);
|
||||
respr = m_resp;
|
||||
}
|
||||
|
||||
// We only ever trace drivers of bits that are known to be independent
|
||||
// of the cycles, so we should always be able to find an acyclic driver.
|
||||
UASSERT_OBJ(resp, startVtxp, "Tracing driver failed for " << startVtxp->typeName());
|
||||
UASSERT_OBJ(resp->width() == (startMsb - startLsb + 1), startVtxp, "Wrong result width");
|
||||
// Every vertex on the pass-through chain resolves to the same driver
|
||||
for (size_t i = pendingBegin; i < m_pendingps.size(); ++i) *m_pendingps[i] = resp;
|
||||
m_pendingps.resize(pendingBegin);
|
||||
return resp;
|
||||
UASSERT_OBJ(respr, vtxp, "Tracing driver failed for " << vtxp->typeName());
|
||||
UASSERT_OBJ(respr->width() == (msb - lsb + 1), vtxp, "Wrong result width");
|
||||
return respr;
|
||||
}
|
||||
|
||||
// Trace drivers of the given array vertex, at the current m_idxs, m_msb, m_lsb.
|
||||
@@ -430,59 +375,27 @@ class TraceDriver final : public DfgVisitor {
|
||||
return resp;
|
||||
}
|
||||
|
||||
// Use this macro to return the result from 'visit' methods. This also emits
|
||||
// Use this macro to set the result in 'visit' methods. This also emits
|
||||
// a line to m_lineCoverageFile for testing.
|
||||
// TODO: Use C++20 std::source_location instead of a macro
|
||||
#ifdef VL_DEBUG
|
||||
#define TRACE_RESULT_COVER() \
|
||||
#define SET_RESULT(vtxp) \
|
||||
do { \
|
||||
m_resp = vtxp; \
|
||||
if (VL_UNLIKELY(m_lineCoverageFile.is_open())) m_lineCoverageFile << __LINE__ << '\n'; \
|
||||
} while (false)
|
||||
#else
|
||||
#define TRACE_RESULT_COVER() \
|
||||
do { \
|
||||
} while (false)
|
||||
#define SET_RESULT(vtxp) m_resp = vtxp;
|
||||
#endif
|
||||
|
||||
#define RETURN_RESULT(vtxp) \
|
||||
do { \
|
||||
m_resp = (vtxp); \
|
||||
TRACE_RESULT_COVER(); \
|
||||
return; \
|
||||
} while (false)
|
||||
|
||||
// Use this macro in 'visit' methods when the result is exactly the result of tracing
|
||||
// 'srcp' at [smsb:slsb]. 'trace' then continues iteratively instead of recursing, so
|
||||
// arbitrarily long chains of such pass-through vertices do not consume C++ stack.
|
||||
// Note this overwrites 'm_msb'/'m_lsb', which is fine as the visitor returns
|
||||
// immediately. Only use this when no state set up by the visitor (e.g.: 'm_defaultp')
|
||||
// has to stay live while 'srcp' is traced, as that happens after the visitor returned.
|
||||
#define RETURN_RESULT_TAIL(srcp, smsb, slsb) \
|
||||
do { \
|
||||
/* Evaluate all arguments before assigning, as they usually read 'm_msb'/'m_lsb' */ \
|
||||
DfgVertex* const tailp = (srcp); \
|
||||
const uint32_t tailMsb = (smsb); \
|
||||
const uint32_t tailLsb = (slsb); \
|
||||
m_tailp = tailp; \
|
||||
m_msb = tailMsb; \
|
||||
m_lsb = tailLsb; \
|
||||
TRACE_RESULT_COVER(); \
|
||||
return; \
|
||||
} while (false)
|
||||
|
||||
// VISITORS
|
||||
void visit(DfgVertex* vtxp) override { // LCOV_EXCL_START
|
||||
vtxp->v3fatalSrc("TraceDriver - Unhandled vertex type: " << vtxp->typeName());
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
void visit(DfgSplicePacked* vtxp) override {
|
||||
UASSERT_OBJ(m_splicep == vtxp, vtxp, "Unexpected trace of DfgSplicePacked");
|
||||
DfgVertex* defaultp = m_defaultp;
|
||||
m_defaultp = nullptr;
|
||||
m_splicep = nullptr;
|
||||
|
||||
struct Driver final {
|
||||
DfgVertex* m_vtxp; // Vertex driving this range
|
||||
DfgVertex* m_vtxp;
|
||||
uint32_t m_lsb; // LSB of driven range (internal, not Verilog)
|
||||
uint32_t m_msb; // MSB of driven range (internal, not Verilog)
|
||||
Driver() = delete;
|
||||
@@ -494,27 +407,25 @@ class TraceDriver final : public DfgVisitor {
|
||||
std::vector<Driver> drivers;
|
||||
|
||||
// Look at all the drivers, one might cover the whole range, but also gather all drivers
|
||||
bool tryWholeDefault = defaultp;
|
||||
DfgVertex* coverp = nullptr; // Driver covering the whole searched range, if any
|
||||
uint32_t coverLsb = 0; // LSB of the range driven by 'coverp'
|
||||
vtxp->foreachDriver([&](DfgVertex& src, uint32_t lsb) {
|
||||
bool tryWholeDefault = m_defaultp;
|
||||
const bool done = vtxp->foreachDriver([&](DfgVertex& src, uint32_t lsb) {
|
||||
const uint32_t msb = lsb + src.width() - 1;
|
||||
drivers.emplace_back(&src, lsb, msb);
|
||||
// Check if this driver covers any of the bits, then we can't use whole default
|
||||
if (m_msb >= lsb && msb >= m_lsb) tryWholeDefault = false;
|
||||
// If it does not cover the whole searched bit range, move on
|
||||
if (m_lsb < lsb || msb < m_msb) return false;
|
||||
// Driver covers whole search range, we are done gathering
|
||||
coverp = &src;
|
||||
coverLsb = lsb;
|
||||
// Driver covers whole search range, trace that and we are done
|
||||
SET_RESULT(trace(&src, m_msb - lsb, m_lsb - lsb));
|
||||
return true;
|
||||
});
|
||||
|
||||
// Trace the driver covering the whole searched range, if there is one
|
||||
if (coverp) RETURN_RESULT_TAIL(coverp, m_msb - coverLsb, m_lsb - coverLsb);
|
||||
if (done) return;
|
||||
|
||||
// Trace the default driver if no other drivers cover the searched range
|
||||
if (tryWholeDefault) RETURN_RESULT_TAIL(defaultp, m_msb, m_lsb);
|
||||
if (tryWholeDefault) {
|
||||
SET_RESULT(trace(m_defaultp, m_msb, m_lsb));
|
||||
return;
|
||||
}
|
||||
|
||||
// Hard case: We need to combine multiple drivers to produce the searched bit range
|
||||
|
||||
@@ -533,8 +444,8 @@ class TraceDriver final : public DfgVisitor {
|
||||
if (driver.m_lsb > m_msb) break;
|
||||
// Gap below this driver, trace default to fill it
|
||||
if (driver.m_lsb > lsb) {
|
||||
UASSERT_OBJ(defaultp, vtxp, "Should have a default driver if needs tracing");
|
||||
termps.emplace_back(trace(defaultp, driver.m_lsb - 1, lsb));
|
||||
UASSERT_OBJ(m_defaultp, vtxp, "Should have a default driver if needs tracing");
|
||||
termps.emplace_back(trace(m_defaultp, driver.m_lsb - 1, lsb));
|
||||
lsb = driver.m_lsb;
|
||||
}
|
||||
// Driver covers searched range, pick the needed/available bits
|
||||
@@ -543,8 +454,8 @@ class TraceDriver final : public DfgVisitor {
|
||||
lsb = lim + 1;
|
||||
}
|
||||
if (m_msb >= lsb) {
|
||||
UASSERT_OBJ(defaultp, vtxp, "Should have a default driver if needs tracing");
|
||||
termps.emplace_back(trace(defaultp, m_msb, lsb));
|
||||
UASSERT_OBJ(m_defaultp, vtxp, "Should have a default driver if needs tracing");
|
||||
termps.emplace_back(trace(m_defaultp, m_msb, lsb));
|
||||
}
|
||||
|
||||
// The earlier cheks cover the case when either a whole driver or the default covers
|
||||
@@ -560,46 +471,33 @@ class TraceDriver final : public DfgVisitor {
|
||||
catp->lhsp(termp);
|
||||
resp = catp;
|
||||
}
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
}
|
||||
|
||||
void visit(DfgSpliceArray* vtxp) override {
|
||||
UASSERT_OBJ(m_splicep == vtxp, vtxp, "Unexpected trace of DfgSpliceArray");
|
||||
DfgVertex* const defaultp = m_defaultp;
|
||||
m_defaultp = nullptr;
|
||||
m_splicep = nullptr;
|
||||
|
||||
// Explicit per-element driver (a UnitArray wrapping the element value)
|
||||
const uint32_t idx = m_idxs.back();
|
||||
if (DfgVertex* const driverp = vtxp->driverAt(idx)) {
|
||||
DfgVertex* const srcp = driverp->as<DfgUnitArray>()->srcp();
|
||||
if (srcp->is<DfgVertexSplice>()) {
|
||||
// Partial-element propagation is rejected during synthesis.
|
||||
UASSERT_OBJ(!defaultp, vtxp, "Array default with partial element driver");
|
||||
m_splicep = srcp;
|
||||
}
|
||||
if (DfgVertex* const driverp = vtxp->driverAt(m_idxs.back())) {
|
||||
// Consume this index, then trace the element value
|
||||
RETURN_RESULT(tracePopIdx(srcp));
|
||||
SET_RESULT(tracePopIdx(driverp->as<DfgUnitArray>()->srcp()));
|
||||
return;
|
||||
}
|
||||
// An element not driven explicitly comes from the default array at the same index.
|
||||
UASSERT_OBJ(defaultp, vtxp, "Independent array element should have a driver or default");
|
||||
RETURN_RESULT(traceSameIdx(defaultp));
|
||||
// TODO: this is unreachable, as syntheis can't create it today.
|
||||
// // Element not driven explicitly, so it comes from the default array. Keep the
|
||||
// // index pending (the default is the whole array, indexed the same way) and
|
||||
// // continue tracing it.
|
||||
// UASSERT_OBJ(m_defaultp, vtxp, "Independent array element should have a driver or
|
||||
// default"); SET RESULT(traceSameIdx(m_defaultp));
|
||||
}
|
||||
|
||||
void visit(DfgVertexVar* vtxp) override {
|
||||
UASSERT_OBJ(!vtxp->isVolatile(), vtxp, "Should not trace through volatile variable");
|
||||
DfgVertex* const srcp = vtxp->srcp();
|
||||
DfgVertex* const defaultp = vtxp->defaultp();
|
||||
DfgVertex* const drvp = srcp ? srcp : defaultp;
|
||||
// If we are about to trace a splice, set the defaultp to the corresponding default
|
||||
if (srcp && srcp->is<DfgVertexSplice>()) {
|
||||
m_defaultp = defaultp;
|
||||
m_splicep = srcp;
|
||||
}
|
||||
VL_RESTORER(m_defaultp);
|
||||
m_defaultp = vtxp->defaultp();
|
||||
DfgVertex* const drvp = vtxp->srcp() ? vtxp->srcp() : m_defaultp;
|
||||
UASSERT_OBJ(drvp, vtxp, "Should not have to trace undriven variable");
|
||||
// Packed variable: trace the driver. Array variable: continue navigating it at
|
||||
// the pending element (both at the same bit range).
|
||||
RETURN_RESULT(m_idxs.empty() ? trace(drvp, m_msb, m_lsb) : traceSameIdx(drvp));
|
||||
SET_RESULT(m_idxs.empty() ? trace(drvp, m_msb, m_lsb) : traceSameIdx(drvp));
|
||||
}
|
||||
|
||||
void visit(DfgArraySel* vtxp) override {
|
||||
@@ -607,7 +505,8 @@ class TraceDriver final : public DfgVisitor {
|
||||
// structure of 'fromp'. This handles arbitrarily nested (multi-dimensional)
|
||||
// arrays, as each nested ArraySel pushes a further index.
|
||||
if (const DfgConst* const idxp = vtxp->bitp()->cast<DfgConst>()) {
|
||||
RETURN_RESULT(tracePushIdx(vtxp->fromp(), idxp->toU32()));
|
||||
SET_RESULT(tracePushIdx(vtxp->fromp(), idxp->toU32()));
|
||||
return;
|
||||
}
|
||||
|
||||
// If index is not constant, independence was proven only if the 'fromp' is
|
||||
@@ -620,13 +519,13 @@ class TraceDriver final : public DfgVisitor {
|
||||
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
selp->fromp(resp);
|
||||
selp->lsb(m_lsb);
|
||||
RETURN_RESULT(selp);
|
||||
SET_RESULT(selp);
|
||||
}
|
||||
|
||||
void visit(DfgUnitArray* vtxp) override {
|
||||
// Single-element array adapter, the pending index must be 0, unwrap the element
|
||||
UASSERT_OBJ(m_idxs.back() == 0, vtxp, "UnitArray element index should be 0");
|
||||
RETURN_RESULT(tracePopIdx(vtxp->srcp()));
|
||||
SET_RESULT(tracePopIdx(vtxp->srcp()));
|
||||
}
|
||||
|
||||
void visit(DfgConcat* vtxp) override {
|
||||
@@ -634,14 +533,20 @@ class TraceDriver final : public DfgVisitor {
|
||||
DfgVertex* const lhsp = vtxp->lhsp();
|
||||
const uint32_t rWidth = rhsp->width();
|
||||
// If the traced bits are wholly in the RHS
|
||||
if (rWidth > m_msb) RETURN_RESULT_TAIL(rhsp, m_msb, m_lsb);
|
||||
if (rWidth > m_msb) {
|
||||
SET_RESULT(trace(rhsp, m_msb, m_lsb));
|
||||
return;
|
||||
}
|
||||
// If the traced bits are wholly in the LHS
|
||||
if (m_lsb >= rWidth) RETURN_RESULT_TAIL(lhsp, m_msb - rWidth, m_lsb - rWidth);
|
||||
if (m_lsb >= rWidth) {
|
||||
SET_RESULT(trace(lhsp, m_msb - rWidth, m_lsb - rWidth));
|
||||
return;
|
||||
}
|
||||
// The traced bit spans both sides, trace both
|
||||
DfgConcat* const resp = make<DfgConcat>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->rhsp(trace(rhsp, rWidth - 1, m_lsb));
|
||||
resp->lhsp(trace(lhsp, m_msb - rWidth, 0));
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
}
|
||||
|
||||
void visit(DfgRep* vtxp) override {
|
||||
@@ -654,75 +559,91 @@ class TraceDriver final : public DfgVisitor {
|
||||
DfgSel* const resp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->fromp(repp);
|
||||
resp->lsb(m_lsb);
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
return;
|
||||
}
|
||||
// If the requested bits are within the same repliacted word
|
||||
if (m_msb / sWidth == m_lsb / sWidth) {
|
||||
RETURN_RESULT_TAIL(srcp, m_msb % sWidth, m_lsb % sWidth);
|
||||
SET_RESULT(trace(srcp, m_msb % sWidth, m_lsb % sWidth));
|
||||
return;
|
||||
}
|
||||
// The requested bits span two replicated words
|
||||
DfgConcat* const resp = make<DfgConcat>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->rhsp(trace(srcp, sWidth - 1, m_lsb % sWidth));
|
||||
resp->lhsp(trace(srcp, m_msb % sWidth, 0));
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
}
|
||||
|
||||
void visit(DfgExtend* vtxp) override {
|
||||
DfgVertex* const srcp = vtxp->srcp();
|
||||
const uint32_t sWidth = srcp->width();
|
||||
// If the traced bits are wholly in the input
|
||||
if (sWidth > m_msb) RETURN_RESULT_TAIL(srcp, m_msb, m_lsb);
|
||||
if (sWidth > m_msb) {
|
||||
SET_RESULT(trace(srcp, m_msb, m_lsb));
|
||||
return;
|
||||
}
|
||||
// If the traced bits are wholly in the extension
|
||||
if (m_lsb >= sWidth) RETURN_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
if (m_lsb >= sWidth) {
|
||||
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
return;
|
||||
}
|
||||
// The traced bits span both sides
|
||||
DfgExtend* const resp = make<DfgExtend>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->srcp(trace(srcp, sWidth - 1, m_lsb));
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
}
|
||||
|
||||
void visit(DfgExtendS* vtxp) override {
|
||||
DfgVertex* const srcp = vtxp->srcp();
|
||||
const uint32_t sWidth = srcp->width();
|
||||
// If the traced bits are wholly in the input
|
||||
if (sWidth > m_msb) RETURN_RESULT_TAIL(srcp, m_msb, m_lsb);
|
||||
if (sWidth > m_msb) {
|
||||
SET_RESULT(trace(srcp, m_msb, m_lsb));
|
||||
return;
|
||||
}
|
||||
// If the traced bits are wholly in the extension
|
||||
if (m_lsb >= sWidth) {
|
||||
if (m_msb == m_lsb) RETURN_RESULT_TAIL(srcp, sWidth - 1, sWidth - 1);
|
||||
DfgExtendS* const resp = make<DfgExtendS>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->srcp(trace(srcp, sWidth - 1, sWidth - 1));
|
||||
RETURN_RESULT(resp);
|
||||
DfgVertex* const sp = trace(srcp, sWidth - 1, sWidth - 1);
|
||||
if (m_msb == m_lsb) {
|
||||
SET_RESULT(sp);
|
||||
} else {
|
||||
DfgExtendS* const resp = make<DfgExtendS>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->srcp(sp);
|
||||
SET_RESULT(resp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// The traced bits span both sides
|
||||
DfgExtendS* const resp = make<DfgExtendS>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->srcp(trace(srcp, sWidth - 1, m_lsb));
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
}
|
||||
|
||||
void visit(DfgSel* vtxp) override {
|
||||
const uint32_t lsb = vtxp->lsb();
|
||||
RETURN_RESULT_TAIL(vtxp->srcp(), m_msb + lsb, m_lsb + lsb);
|
||||
SET_RESULT(trace(vtxp->srcp(), m_msb + lsb, m_lsb + lsb));
|
||||
}
|
||||
|
||||
void visit(DfgNot* vtxp) override {
|
||||
DfgNot* const resp = make<DfgNot>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->srcp(trace(vtxp->srcp(), m_msb, m_lsb));
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
}
|
||||
|
||||
void visit(DfgAnd* vtxp) override { RETURN_RESULT(traceBitwiseBinary(vtxp)); }
|
||||
void visit(DfgOr* vtxp) override { RETURN_RESULT(traceBitwiseBinary(vtxp)); }
|
||||
void visit(DfgXor* vtxp) override { RETURN_RESULT(traceBitwiseBinary(vtxp)); }
|
||||
void visit(DfgAdd* vtxp) override { RETURN_RESULT(traceAddSub(vtxp)); }
|
||||
void visit(DfgSub* vtxp) override { RETURN_RESULT(traceAddSub(vtxp)); }
|
||||
void visit(DfgRedAnd* vtxp) override { RETURN_RESULT(traceReduction(vtxp)); }
|
||||
void visit(DfgRedOr* vtxp) override { RETURN_RESULT(traceReduction(vtxp)); }
|
||||
void visit(DfgRedXor* vtxp) override { RETURN_RESULT(traceReduction(vtxp)); }
|
||||
void visit(DfgEq* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
|
||||
void visit(DfgNeq* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
|
||||
void visit(DfgLt* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
|
||||
void visit(DfgLte* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
|
||||
void visit(DfgGt* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
|
||||
void visit(DfgGte* vtxp) override { RETURN_RESULT(traceCmp(vtxp)); }
|
||||
void visit(DfgAnd* vtxp) override { SET_RESULT(traceBitwiseBinary(vtxp)); }
|
||||
void visit(DfgOr* vtxp) override { SET_RESULT(traceBitwiseBinary(vtxp)); }
|
||||
void visit(DfgXor* vtxp) override { SET_RESULT(traceBitwiseBinary(vtxp)); }
|
||||
void visit(DfgAdd* vtxp) override { SET_RESULT(traceAddSub(vtxp)); }
|
||||
void visit(DfgSub* vtxp) override { SET_RESULT(traceAddSub(vtxp)); }
|
||||
void visit(DfgRedAnd* vtxp) override { SET_RESULT(traceReduction(vtxp)); }
|
||||
void visit(DfgRedOr* vtxp) override { SET_RESULT(traceReduction(vtxp)); }
|
||||
void visit(DfgRedXor* vtxp) override { SET_RESULT(traceReduction(vtxp)); }
|
||||
void visit(DfgEq* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
|
||||
void visit(DfgNeq* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
|
||||
void visit(DfgLt* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
|
||||
void visit(DfgLte* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
|
||||
void visit(DfgGt* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
|
||||
void visit(DfgGte* vtxp) override { SET_RESULT(traceCmp(vtxp)); }
|
||||
|
||||
void visit(DfgShiftRS* vtxp) override {
|
||||
DfgVertex* const lhsp = vtxp->lhsp();
|
||||
@@ -732,28 +653,34 @@ class TraceDriver final : public DfgVisitor {
|
||||
const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? 0 : vtxp->width() - shiftAmnt;
|
||||
// If the traced bits are wholly in the input
|
||||
if (lowerWidth > m_msb) {
|
||||
RETURN_RESULT_TAIL(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt);
|
||||
SET_RESULT(trace(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt));
|
||||
return;
|
||||
}
|
||||
// If the traced bits are wholly in the extension
|
||||
if (m_lsb >= lowerWidth) {
|
||||
DfgExtendS* const resp = make<DfgExtendS>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->srcp(trace(lhsp, lhsp->width() - 1, lhsp->width() - 1));
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
return;
|
||||
}
|
||||
// The traced bits span both sides
|
||||
DfgExtendS* const resp = make<DfgExtendS>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->srcp(trace(lhsp, lowerWidth - 1 + shiftAmnt, m_lsb + shiftAmnt));
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
DfgShiftRS* const shiftrsp = make<DfgShiftRS>(vtxp, vtxp->lhsp()->width() - m_lsb);
|
||||
shiftrsp->rhsp(trace(vtxp->rhsp(), vtxp->rhsp()->width() - 1, 0));
|
||||
shiftrsp->lhsp(trace(vtxp->lhsp(), vtxp->lhsp()->width() - 1, m_lsb));
|
||||
if (m_msb == vtxp->lhsp()->width() - 1) RETURN_RESULT(shiftrsp);
|
||||
if (m_msb == vtxp->lhsp()->width() - 1) {
|
||||
SET_RESULT(shiftrsp);
|
||||
return;
|
||||
}
|
||||
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
selp->fromp(shiftrsp);
|
||||
selp->lsb(0);
|
||||
RETURN_RESULT(selp);
|
||||
SET_RESULT(selp);
|
||||
}
|
||||
|
||||
void visit(DfgShiftR* vtxp) override {
|
||||
@@ -764,24 +691,32 @@ class TraceDriver final : public DfgVisitor {
|
||||
const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? 0 : vtxp->width() - shiftAmnt;
|
||||
// If the traced bits are wholly in the input
|
||||
if (lowerWidth > m_msb) {
|
||||
RETURN_RESULT_TAIL(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt);
|
||||
SET_RESULT(trace(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt));
|
||||
return;
|
||||
}
|
||||
// If the traced bits are wholly in the extension
|
||||
if (m_lsb >= lowerWidth) RETURN_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
if (m_lsb >= lowerWidth) {
|
||||
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
return;
|
||||
}
|
||||
// The traced bits span both sides
|
||||
DfgExtend* const resp = make<DfgExtend>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->srcp(trace(lhsp, lowerWidth - 1 + shiftAmnt, m_lsb + shiftAmnt));
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
DfgShiftR* const shiftrp = make<DfgShiftR>(vtxp, vtxp->lhsp()->width() - m_lsb);
|
||||
shiftrp->rhsp(trace(vtxp->rhsp(), vtxp->rhsp()->width() - 1, 0));
|
||||
shiftrp->lhsp(trace(vtxp->lhsp(), vtxp->lhsp()->width() - 1, m_lsb));
|
||||
if (m_msb == vtxp->lhsp()->width() - 1) RETURN_RESULT(shiftrp);
|
||||
if (m_msb == vtxp->lhsp()->width() - 1) {
|
||||
SET_RESULT(shiftrp);
|
||||
return;
|
||||
}
|
||||
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
selp->fromp(shiftrp);
|
||||
selp->lsb(0);
|
||||
RETURN_RESULT(selp);
|
||||
SET_RESULT(selp);
|
||||
}
|
||||
|
||||
void visit(DfgShiftL* vtxp) override {
|
||||
@@ -792,25 +727,33 @@ class TraceDriver final : public DfgVisitor {
|
||||
const uint32_t lowerWidth = shiftAmnt > vtxp->width() ? vtxp->width() : shiftAmnt;
|
||||
// If the traced bits are wholly in the input
|
||||
if (m_lsb >= lowerWidth) {
|
||||
RETURN_RESULT_TAIL(lhsp, m_msb - shiftAmnt, m_lsb - shiftAmnt);
|
||||
SET_RESULT(trace(lhsp, m_msb - shiftAmnt, m_lsb - shiftAmnt));
|
||||
return;
|
||||
}
|
||||
// If the traced bits are wholly in the extension
|
||||
if (lowerWidth > m_msb) RETURN_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
if (lowerWidth > m_msb) {
|
||||
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
return;
|
||||
}
|
||||
// The traced bits span both sides
|
||||
DfgConcat* const resp = make<DfgConcat>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->rhsp(make<DfgConst>(vtxp, resp->width() - (m_msb - lowerWidth + 1)));
|
||||
resp->lhsp(trace(lhsp, m_msb - shiftAmnt, lowerWidth - shiftAmnt));
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
DfgShiftL* const shiftlp = make<DfgShiftL>(vtxp, m_msb + 1);
|
||||
shiftlp->rhsp(trace(vtxp->rhsp(), vtxp->rhsp()->width() - 1, 0));
|
||||
shiftlp->lhsp(trace(vtxp->lhsp(), m_msb, 0));
|
||||
if (m_lsb == 0) RETURN_RESULT(shiftlp);
|
||||
if (m_lsb == 0) {
|
||||
SET_RESULT(shiftlp);
|
||||
return;
|
||||
}
|
||||
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
selp->fromp(shiftlp);
|
||||
selp->lsb(m_lsb);
|
||||
RETURN_RESULT(selp);
|
||||
SET_RESULT(selp);
|
||||
}
|
||||
|
||||
void visit(DfgCond* vtxp) override {
|
||||
@@ -818,7 +761,7 @@ class TraceDriver final : public DfgVisitor {
|
||||
resp->condp(trace(vtxp->condp(), vtxp->condp()->width() - 1, 0));
|
||||
resp->thenp(trace(vtxp->thenp(), m_msb, m_lsb));
|
||||
resp->elsep(trace(vtxp->elsep(), m_msb, m_lsb));
|
||||
RETURN_RESULT(resp);
|
||||
SET_RESULT(resp);
|
||||
}
|
||||
|
||||
void visit(DfgMatchMasked* vtxp) override {
|
||||
@@ -828,12 +771,10 @@ class TraceDriver final : public DfgVisitor {
|
||||
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
selp->fromp(resp);
|
||||
selp->lsb(m_lsb);
|
||||
RETURN_RESULT(selp);
|
||||
SET_RESULT(selp);
|
||||
}
|
||||
|
||||
#undef RETURN_RESULT
|
||||
#undef TRACE_RESULT_COVER
|
||||
#undef RETURN_RESULT_TAIL
|
||||
#undef SET_RESULT
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
@@ -1652,6 +1593,9 @@ void breakCycles(DfgGraph& dfg, V3DfgBreakCyclesContext& ctx) {
|
||||
if (dumpDfgLevel() >= level) dfg.dumpDotFilePrefixed("breakCycles-" + name);
|
||||
};
|
||||
|
||||
// AstNetlist/AstNodeModule user2 used as sequence numbers for temporaries
|
||||
const VNUser2InUse user2InUse;
|
||||
|
||||
// Show input for debugging
|
||||
dump(7, dfg, "input");
|
||||
|
||||
|
||||
+182
-139
@@ -25,11 +25,8 @@
|
||||
#ifndef VERILATOR_V3DFGCACHE_H_
|
||||
#define VERILATOR_V3DFGCACHE_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Dfg.h"
|
||||
#include "V3DfgDataType.h"
|
||||
#include "V3HashTable.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
@@ -55,107 +52,137 @@ struct V3DfgCacheType<Vertex, CacheBase, VertexBase, Cache, Pairs...> final {
|
||||
|
||||
class V3DfgCache final {
|
||||
// TYPES
|
||||
// Hashing and comparison of the cached vertices. Each takes either a vertex, or the
|
||||
// parts a vertex would be created from, so a lookup needs no vertex and no key object.
|
||||
class KeySel final {
|
||||
const DfgDataType& m_dtype;
|
||||
const DfgVertex* const m_fromp;
|
||||
const uint32_t m_lsb;
|
||||
|
||||
// DfgSel
|
||||
struct HashSel final {
|
||||
size_t operator()(const DfgSel* vtxp) const {
|
||||
return operator()(vtxp->dtype(), vtxp->fromp(), vtxp->lsb());
|
||||
}
|
||||
size_t operator()(const DfgDataType& dtype, const DfgVertex* fromp, uint32_t lsb) const {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash = dtype.hash();
|
||||
hash += vertexHash(fromp);
|
||||
hash += lsb;
|
||||
return hash.value();
|
||||
}
|
||||
};
|
||||
struct EqualSel final {
|
||||
bool operator()(const DfgSel* ap, const DfgSel* bp) const {
|
||||
return operator()(ap, bp->dtype(), bp->fromp(), bp->lsb());
|
||||
}
|
||||
bool operator()(const DfgSel* vtxp, const DfgDataType& dtype, const DfgVertex* fromp,
|
||||
uint32_t lsb) const {
|
||||
return vtxp->lsb() == lsb && vtxp->dtype() == dtype
|
||||
&& vertexEqual(vtxp->fromp(), fromp);
|
||||
}
|
||||
public:
|
||||
KeySel(const DfgDataType& dtype, DfgVertex* fromp, uint32_t lsb)
|
||||
: m_dtype{dtype}
|
||||
, m_fromp{fromp}
|
||||
, m_lsb{lsb} {}
|
||||
explicit KeySel(const DfgSel* vtxp)
|
||||
: m_dtype{vtxp->dtype()}
|
||||
, m_fromp{vtxp->fromp()}
|
||||
, m_lsb{vtxp->lsb()} {}
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeySel& key) const {
|
||||
// cppcheck-suppress unreadVariable // cppcheck bug
|
||||
V3Hash hash = key.m_dtype.hash();
|
||||
hash += vertexHash(key.m_fromp);
|
||||
hash += key.m_lsb;
|
||||
return hash.value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal final {
|
||||
bool operator()(const KeySel& a, const KeySel& b) const {
|
||||
return a.m_lsb == b.m_lsb && a.m_dtype == b.m_dtype
|
||||
&& vertexEqual(a.m_fromp, b.m_fromp);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// DfgVertexUnary
|
||||
struct HashUnary final {
|
||||
size_t operator()(const DfgVertexUnary* vtxp) const {
|
||||
return operator()(vtxp->dtype(), vtxp->inputp(0));
|
||||
}
|
||||
size_t operator()(const DfgDataType& dtype, const DfgVertex* source0p) const {
|
||||
V3Hash hash = dtype.hash();
|
||||
hash += vertexHash(source0p);
|
||||
return hash.value();
|
||||
}
|
||||
};
|
||||
struct EqualUnary final {
|
||||
bool operator()(const DfgVertexUnary* ap, const DfgVertexUnary* bp) const {
|
||||
return operator()(ap, bp->dtype(), bp->inputp(0));
|
||||
}
|
||||
bool operator()(const DfgVertexUnary* vtxp, const DfgDataType& dtype,
|
||||
const DfgVertex* source0p) const {
|
||||
return vtxp->dtype() == dtype && vertexEqual(vtxp->inputp(0), source0p);
|
||||
}
|
||||
class KeyUnary final {
|
||||
const DfgDataType& m_dtype;
|
||||
const DfgVertex* const m_source0p;
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
KeyUnary(const DfgDataType& dtype, DfgVertex* source0p)
|
||||
: m_dtype{dtype}
|
||||
, m_source0p{source0p} {}
|
||||
explicit KeyUnary(const DfgVertexUnary* vtxp)
|
||||
: m_dtype{vtxp->dtype()}
|
||||
, m_source0p{vtxp->inputp(0)} {}
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeyUnary& key) const { //
|
||||
V3Hash hash = key.m_dtype.hash();
|
||||
hash += vertexHash(key.m_source0p);
|
||||
return hash.value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal final {
|
||||
bool operator()(const KeyUnary& a, const KeyUnary& b) const {
|
||||
return a.m_dtype == b.m_dtype && vertexEqual(a.m_source0p, b.m_source0p);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// DfgVertexBinary
|
||||
struct HashBinary final {
|
||||
size_t operator()(const DfgVertexBinary* vtxp) const {
|
||||
return operator()(vtxp->dtype(), vtxp->inputp(0), vtxp->inputp(1));
|
||||
}
|
||||
size_t operator()(const DfgDataType& dtype, const DfgVertex* source0p,
|
||||
const DfgVertex* source1p) const {
|
||||
V3Hash hash = dtype.hash();
|
||||
hash += vertexHash(source0p);
|
||||
hash += vertexHash(source1p);
|
||||
return hash.value();
|
||||
}
|
||||
};
|
||||
struct EqualBinary final {
|
||||
bool operator()(const DfgVertexBinary* ap, const DfgVertexBinary* bp) const {
|
||||
return operator()(ap, bp->dtype(), bp->inputp(0), bp->inputp(1));
|
||||
}
|
||||
bool operator()(const DfgVertexBinary* vtxp, const DfgDataType& dtype,
|
||||
const DfgVertex* source0p, const DfgVertex* source1p) const {
|
||||
return vtxp->dtype() == dtype && vertexEqual(vtxp->inputp(0), source0p)
|
||||
&& vertexEqual(vtxp->inputp(1), source1p);
|
||||
}
|
||||
class KeyBinary final {
|
||||
const DfgDataType& m_dtype;
|
||||
const DfgVertex* const m_source0p;
|
||||
const DfgVertex* const m_source1p;
|
||||
|
||||
public:
|
||||
KeyBinary(const DfgDataType& dtype, DfgVertex* source0p, DfgVertex* source1p)
|
||||
: m_dtype{dtype}
|
||||
, m_source0p{source0p}
|
||||
, m_source1p{source1p} {}
|
||||
explicit KeyBinary(const DfgVertexBinary* vtxp)
|
||||
: m_dtype{vtxp->dtype()}
|
||||
, m_source0p{vtxp->inputp(0)}
|
||||
, m_source1p{vtxp->inputp(1)} {}
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeyBinary& key) const {
|
||||
V3Hash hash = key.m_dtype.hash();
|
||||
hash += vertexHash(key.m_source0p);
|
||||
hash += vertexHash(key.m_source1p);
|
||||
return hash.value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal final {
|
||||
bool operator()(const KeyBinary& a, const KeyBinary& b) const {
|
||||
return a.m_dtype == b.m_dtype && vertexEqual(a.m_source0p, b.m_source0p)
|
||||
&& vertexEqual(a.m_source1p, b.m_source1p);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// DfgVertexTernary
|
||||
struct HashTernary final {
|
||||
size_t operator()(const DfgVertexTernary* vtxp) const {
|
||||
return operator()(vtxp->dtype(), vtxp->inputp(0), vtxp->inputp(1), vtxp->inputp(2));
|
||||
}
|
||||
size_t operator()(const DfgDataType& dtype, const DfgVertex* source0p,
|
||||
const DfgVertex* source1p, const DfgVertex* source2p) const {
|
||||
V3Hash hash = dtype.hash();
|
||||
hash += vertexHash(source0p);
|
||||
hash += vertexHash(source1p);
|
||||
hash += vertexHash(source2p);
|
||||
return hash.value();
|
||||
}
|
||||
class KeyTernary final {
|
||||
const DfgDataType& m_dtype;
|
||||
const DfgVertex* const m_source0p;
|
||||
const DfgVertex* const m_source1p;
|
||||
const DfgVertex* const m_source2p;
|
||||
|
||||
public:
|
||||
KeyTernary(const DfgDataType& dtype, DfgVertex* source0p, DfgVertex* source1p,
|
||||
DfgVertex* source2p)
|
||||
: m_dtype{dtype}
|
||||
, m_source0p{source0p}
|
||||
, m_source1p{source1p}
|
||||
, m_source2p{source2p} {}
|
||||
explicit KeyTernary(const DfgVertexTernary* vtxp)
|
||||
: m_dtype{vtxp->dtype()}
|
||||
, m_source0p{vtxp->inputp(0)}
|
||||
, m_source1p{vtxp->inputp(1)}
|
||||
, m_source2p{vtxp->inputp(2)} {}
|
||||
|
||||
struct Hash final {
|
||||
size_t operator()(const KeyTernary& key) const {
|
||||
V3Hash hash = key.m_dtype.hash();
|
||||
hash += vertexHash(key.m_source0p);
|
||||
hash += vertexHash(key.m_source1p);
|
||||
hash += vertexHash(key.m_source2p);
|
||||
return hash.value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal final {
|
||||
bool operator()(const KeyTernary& a, const KeyTernary& b) const {
|
||||
return a.m_dtype == b.m_dtype && vertexEqual(a.m_source0p, b.m_source0p)
|
||||
&& vertexEqual(a.m_source1p, b.m_source1p)
|
||||
&& vertexEqual(a.m_source2p, b.m_source2p);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct EqualTernary final {
|
||||
bool operator()(const DfgVertexTernary* ap, const DfgVertexTernary* bp) const {
|
||||
return operator()(ap, bp->dtype(), bp->inputp(0), bp->inputp(1), bp->inputp(2));
|
||||
}
|
||||
bool operator()(const DfgVertexTernary* vtxp, const DfgDataType& dtype,
|
||||
const DfgVertex* source0p, const DfgVertex* source1p,
|
||||
const DfgVertex* source2p) const {
|
||||
return vtxp->dtype() == dtype && vertexEqual(vtxp->inputp(0), source0p)
|
||||
&& vertexEqual(vtxp->inputp(1), source1p)
|
||||
&& vertexEqual(vtxp->inputp(2), source2p);
|
||||
}
|
||||
};
|
||||
|
||||
// Base class of vertex caches
|
||||
class CacheBase VL_NOT_FINAL {
|
||||
protected:
|
||||
// These set the operands of a new vertex
|
||||
@@ -183,68 +210,86 @@ class V3DfgCache final {
|
||||
public:
|
||||
// CacheBase does not cache anything
|
||||
virtual DfgVertex* cache(DfgVertex*) { return nullptr; }
|
||||
virtual void invalidate(DfgVertex*) {}
|
||||
virtual void invalidate(const DfgVertex*) {}
|
||||
};
|
||||
|
||||
template <typename T_Vertex, typename T_Hash, typename T_Equal>
|
||||
template <typename T_Key, typename T_Vertex>
|
||||
class Cache final : public CacheBase {
|
||||
static_assert(std::is_base_of<DfgVertex, T_Vertex>::value, "T_Vertex must be a DfgVertex");
|
||||
// TYPES
|
||||
using Hash = typename T_Key::Hash;
|
||||
using Equal = typename T_Key::Equal;
|
||||
using Map = std::unordered_map<T_Key, T_Vertex*, Hash, Equal>;
|
||||
|
||||
// STATE
|
||||
V3HashSet<T_Vertex*, T_Hash, T_Equal> m_set;
|
||||
Map m_map;
|
||||
|
||||
// METHODS
|
||||
|
||||
// These return a reference to the mapped entry, inserting a nullptr if not yet exists
|
||||
|
||||
template <typename... T_Args>
|
||||
T_Vertex*& entry(T_Args&&... args) {
|
||||
const T_Key key{std::forward<T_Args>(args)...};
|
||||
return m_map[key];
|
||||
}
|
||||
template <typename... T_Args>
|
||||
typename Map::iterator find(T_Args&&... args) {
|
||||
const T_Key key{std::forward<T_Args>(args)...};
|
||||
return m_map.find(key);
|
||||
}
|
||||
|
||||
public:
|
||||
// Add an existing vertex to the cache. If an equivalent but different vertex exists,
|
||||
// it is returned and the cache is not updated. Returns nullptr if the vertex is inserted.
|
||||
// Add an existing vertex to the cache. If an equivalent exists,
|
||||
// it is returned and the cache is not updated.
|
||||
DfgVertex* cache(DfgVertex* vtxp) override {
|
||||
UDEBUGONLY(UASSERT_OBJ(vtxp->is<T_Vertex>(), vtxp, "Vertex is wrong type"););
|
||||
T_Vertex* const typedp = static_cast<T_Vertex*>(vtxp);
|
||||
T_Vertex* const cachedp = *m_set.insert(typedp).first;
|
||||
return cachedp != vtxp ? cachedp : nullptr;
|
||||
UASSERT_OBJ(vtxp->is<T_Vertex>(), vtxp, "Vertex is wrong type");
|
||||
T_Vertex*& entrypr = entry(static_cast<const T_Vertex*>(vtxp));
|
||||
if (entrypr && entrypr != vtxp) return entrypr;
|
||||
entrypr = static_cast<T_Vertex*>(vtxp);
|
||||
return nullptr;
|
||||
}
|
||||
// Remove an existing vertex from the cache, if it is the cached vertex, otherwise no-op
|
||||
void invalidate(DfgVertex* vtxp) override {
|
||||
UDEBUGONLY(UASSERT_OBJ(vtxp->is<T_Vertex>(), vtxp, "Vertex is wrong type"););
|
||||
T_Vertex* const typedp = static_cast<T_Vertex*>(vtxp);
|
||||
const auto it = m_set.find(typedp);
|
||||
if (it != m_set.end() && *it == typedp) m_set.erase(it);
|
||||
void invalidate(const DfgVertex* vtxp) override {
|
||||
UASSERT_OBJ(vtxp->is<T_Vertex>(), vtxp, "Vertex is wrong type");
|
||||
const auto it = find(static_cast<const T_Vertex*>(vtxp));
|
||||
if (it != m_map.end() && it->second == vtxp) m_map.erase(it);
|
||||
}
|
||||
|
||||
// Get vertex with given operands, return nullptr if not in cache
|
||||
template <typename Vertex, typename... Operands>
|
||||
Vertex* get(const DfgDataType& dtype, Operands... operands) {
|
||||
const auto it = m_set.find(dtype, operands...);
|
||||
return it != m_set.end() ? static_cast<Vertex*>(*it) : nullptr;
|
||||
const auto it = find(dtype, operands...);
|
||||
return it != m_map.end() ? static_cast<Vertex*>(it->second) : nullptr;
|
||||
}
|
||||
// Get vertex with given operands, if does not exist, create it
|
||||
|
||||
// Get or create (and insert) vertex with given operands
|
||||
template <typename Vertex, typename... Operands>
|
||||
Vertex* getOrCreate(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype,
|
||||
Operands... operands) {
|
||||
const auto pair = m_set.insertLazy(dtype, operands..., [&]() -> T_Vertex* {
|
||||
Vertex* const newp = new Vertex{dfg, flp, dtype};
|
||||
T_Vertex*& entryr = entry(dtype, operands...);
|
||||
if (!entryr) {
|
||||
T_Vertex* const newp = new Vertex{dfg, flp, dtype};
|
||||
setOperands(newp, operands...);
|
||||
return newp;
|
||||
});
|
||||
T_Vertex* const vtxp = *pair.first;
|
||||
UDEBUGONLY(UASSERT_OBJ(vtxp->template is<Vertex>(), vtxp, "Vertex is wrong type"););
|
||||
return static_cast<Vertex*>(vtxp);
|
||||
entryr = newp;
|
||||
}
|
||||
return static_cast<Vertex*>(entryr);
|
||||
}
|
||||
};
|
||||
|
||||
// Map from Vertex type to cache type
|
||||
// clang-format off
|
||||
template <typename Vertex>
|
||||
using CacheType = typename V3DfgCacheType<Vertex, CacheBase,
|
||||
DfgSel, /* -> */ Cache<DfgSel, HashSel, EqualSel>,
|
||||
DfgVertexUnary, /* -> */ Cache<DfgVertexUnary, HashUnary, EqualUnary>,
|
||||
DfgVertexBinary, /* -> */ Cache<DfgVertexBinary, HashBinary, EqualBinary>,
|
||||
DfgVertexTernary, /* -> */ Cache<DfgVertexTernary, HashTernary, EqualTernary>
|
||||
>::Type;
|
||||
// clang-format on
|
||||
|
||||
using CacheType =
|
||||
typename V3DfgCacheType<Vertex, CacheBase, //
|
||||
DfgSel, Cache<KeySel, DfgSel>, //
|
||||
DfgVertexUnary, Cache<KeyUnary, DfgVertexUnary>, //
|
||||
DfgVertexBinary, Cache<KeyBinary, DfgVertexBinary>, //
|
||||
DfgVertexTernary, Cache<KeyTernary, DfgVertexTernary> //
|
||||
>::Type;
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The DfgGraph we are caching the vertices of
|
||||
|
||||
// The per type caches
|
||||
// The per type caches
|
||||
#define VERTEX_CACHE_DECLARE_CACHE(t) CacheType<t> m_cache##t;
|
||||
FOREACH_DFG_VERTEX_TYPE(VERTEX_CACHE_DECLARE_CACHE)
|
||||
#undef VERTEX_CACHE_DECLARE_CACHE
|
||||
@@ -283,23 +328,21 @@ class V3DfgCache final {
|
||||
}
|
||||
|
||||
public:
|
||||
// Note: the cache starts out empty. If the caller wants existing vertices
|
||||
// to be found, it must add them itself by calling 'cache' on each.
|
||||
explicit V3DfgCache(DfgGraph& dfg)
|
||||
: m_dfg{dfg} {
|
||||
// Initialize the type to cache lookup table
|
||||
// Initialize the type to cache lookup table
|
||||
#define VERTEX_CACHE_DECLARE_CACHE_PTR(t) m_vtxType2Cachep[t::dfgType()] = &m_cache##t;
|
||||
FOREACH_DFG_VERTEX_TYPE(VERTEX_CACHE_DECLARE_CACHE_PTR)
|
||||
FOREACH_DFG_VERTEX_TYPE(VERTEX_CACHE_DECLARE_CACHE_PTR)
|
||||
#undef VERTEX_CACHE_DECLARE_CACHE_PTR
|
||||
}
|
||||
|
||||
// Add an existing vertex to the cache. If an equivalent (but different) already exists,
|
||||
// it is returned and the cache is not updated.
|
||||
DfgVertex
|
||||
* cache(DfgVertex * vtxp) {
|
||||
return m_vtxType2Cachep[vtxp->type()]->cache(vtxp);
|
||||
// Add all operation vertices to the cache
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) cache(&vtx);
|
||||
}
|
||||
|
||||
// Add an existing vertex to the cache. If an equivalent (but different) already exists,
|
||||
// it is returned and the cache is not updated.
|
||||
DfgVertex* cache(DfgVertex* vtxp) { return m_vtxType2Cachep[vtxp->type()]->cache(vtxp); }
|
||||
|
||||
// Remove an exiting vertex, it is the cached vertex.
|
||||
void invalidate(DfgVertex* vtxp) { m_vtxType2Cachep[vtxp->type()]->invalidate(vtxp); }
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user