mirror of
https://github.com/verilator/verilator.git
synced 2026-10-05 17:43:31 +02:00
Compare commits
214
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb3a916d0c | ||
|
|
23012752c3 | ||
|
|
19254541ee | ||
|
|
8cc1ad620e | ||
|
|
37c3475759 | ||
|
|
b77b711377 | ||
|
|
4e194cc3ec | ||
|
|
19a3d2d6b3 | ||
|
|
3e96aba1d6 | ||
|
|
ec2f3cb2d4 | ||
|
|
c8171ae37b | ||
|
|
256468e8ae | ||
|
|
9f72509635 | ||
|
|
5c9a804e8b | ||
|
|
e7abfa391b | ||
|
|
9b9d690024 | ||
|
|
b080950f76 | ||
|
|
a278652439 | ||
|
|
9dac976008 | ||
|
|
e4cc6cadca | ||
|
|
93e9660ea5 | ||
|
|
5ba2a4836d | ||
|
|
bdfb2e8db1 | ||
|
|
c5b6f4c80f | ||
|
|
0fc4310048 | ||
|
|
e04f350109 | ||
|
|
33a6d05382 | ||
|
|
ea536a54b8 | ||
|
|
71c6638ccf | ||
|
|
5ac4a7eb22 | ||
|
|
f8bf67111e | ||
|
|
368f59d5f2 | ||
|
|
2a82047e4e | ||
|
|
6438d977fa | ||
|
|
af1c668b99 | ||
|
|
5458bd2e77 | ||
|
|
61289bc80b | ||
|
|
1edf8f986a | ||
|
|
226cf2f850 | ||
|
|
d4e4b1d795 | ||
|
|
055b870e11 | ||
|
|
c686a25f54 | ||
|
|
1f0e80dbd6 | ||
|
|
a419e91574 | ||
|
|
0993179e7e | ||
|
|
9637c62dfe | ||
|
|
39796ca954 | ||
|
|
7f9a8695c1 | ||
|
|
feb1f64be8 | ||
|
|
68439fbb72 | ||
|
|
21bae44b03 | ||
|
|
bbdc2cce08 | ||
|
|
c2479ce807 | ||
|
|
3fd0948cf2 | ||
|
|
38f011ed2c | ||
|
|
58fc34e6df | ||
|
|
9d9d367302 | ||
|
|
647b2056ed | ||
|
|
2c32da73c3 | ||
|
|
b3139df707 | ||
|
|
da4903f721 | ||
|
|
640607a0d0 | ||
|
|
f83ed971e2 | ||
|
|
b8068fad46 | ||
|
|
bafdae1847 | ||
|
|
bde1b98ee0 | ||
|
|
a949674a6b | ||
|
|
510ae7b38b | ||
|
|
f5f9ddef9c | ||
|
|
4ab229e659 | ||
|
|
2c441d853c | ||
|
|
c30604222f | ||
|
|
a933e723c2 | ||
|
|
6956befd29 | ||
|
|
f691cd1936 | ||
|
|
626333df02 | ||
|
|
43c3c88c38 | ||
|
|
9e2c30faab | ||
|
|
348b9b6209 | ||
|
|
d234caafc1 | ||
|
|
eec929e40e | ||
|
|
d1eb78f4e8 | ||
|
|
4c4d9cc7d2 | ||
|
|
98250f6cd2 | ||
|
|
f2114d3dc3 | ||
|
|
33d63ac0b0 | ||
|
|
435af6afe6 | ||
|
|
f6ff5b4cfb | ||
|
|
4e29ecbf44 | ||
|
|
8b663f9c84 | ||
|
|
3a76b29cb3 | ||
|
|
2e5e34d426 | ||
|
|
0e0ce2be52 | ||
|
|
765d05acf8 | ||
|
|
ba8e0a1437 | ||
|
|
84dec259c8 | ||
|
|
f2dbf210a8 | ||
|
|
1c94fbb5d6 | ||
|
|
4202862472 | ||
|
|
02546791da | ||
|
|
9e03fed56b | ||
|
|
1aa5ab2e3a | ||
|
|
88082a4e96 | ||
|
|
976ec63531 | ||
|
|
26dd04c020 | ||
|
|
3d386f8fa5 | ||
|
|
7fd68b58ca | ||
|
|
729a51e26e | ||
|
|
f82cdbeaa7 | ||
|
|
e6ecf3a014 | ||
|
|
e9f60f3282 | ||
|
|
f458e40060 | ||
|
|
ceb7dde938 | ||
|
|
a99ced0ed4 | ||
|
|
e9a24647d8 | ||
|
|
9401876de5 | ||
|
|
492a8f7b67 | ||
|
|
fe4b3bdec4 | ||
|
|
39e2d8e853 | ||
|
|
525d1fade3 | ||
|
|
6eced23e84 | ||
|
|
0ab3915de6 | ||
|
|
d637b665ce | ||
|
|
f04eb8de81 | ||
|
|
08c9032927 | ||
|
|
4110d13131 | ||
|
|
5dab8ab576 | ||
|
|
e63ca70d92 | ||
|
|
f883b1c858 | ||
|
|
dc179243eb | ||
|
|
a1e6252e77 | ||
|
|
e71d4e7fe5 | ||
|
|
9e6af08c84 | ||
|
|
a50aea7709 | ||
|
|
9ca90f414c | ||
|
|
ba1961c70b | ||
|
|
5883a5a40d | ||
|
|
d321a5fa5d | ||
|
|
2180cfd635 | ||
|
|
232d3ecc0c | ||
|
|
3884b3ddf3 | ||
|
|
f954f4fd1d | ||
|
|
46774fded9 | ||
|
|
3c05eef216 | ||
|
|
e5384c7b68 | ||
|
|
33d1e4e685 | ||
|
|
652495a932 | ||
|
|
bef6c8afa0 | ||
|
|
77e9938188 | ||
|
|
8f0bd99449 | ||
|
|
c4957ef206 | ||
|
|
2ee6f7435d | ||
|
|
e71f6f3b7a | ||
|
|
c545552cc1 | ||
|
|
fcb2a22d8e | ||
|
|
8f6d6cd13b | ||
|
|
dfa4ace3dd | ||
|
|
d30487e0f7 | ||
|
|
dc946aee2b | ||
|
|
9e3ec98461 | ||
|
|
c8b8c79ee9 | ||
|
|
67e6d09768 | ||
|
|
3444d57929 | ||
|
|
8c6dd7a164 | ||
|
|
18ad801887 | ||
|
|
87983308c7 | ||
|
|
23a4c8ef46 | ||
|
|
acb1333a85 | ||
|
|
c266da882d | ||
|
|
ba68360819 | ||
|
|
6999fe0f29 | ||
|
|
8fbfc82cc2 | ||
|
|
b0b4714a9d | ||
|
|
ee51f322c7 | ||
|
|
f6801633f8 | ||
|
|
f2fcacfe3d | ||
|
|
0b661a00c1 | ||
|
|
7400d28e6d | ||
|
|
36aa3e88c7 | ||
|
|
a9f959ff28 | ||
|
|
b75ceeb121 | ||
|
|
d797c2009b | ||
|
|
014c9820de | ||
|
|
1cefe95c85 | ||
|
|
a93317b6cc | ||
|
|
724a42e31d | ||
|
|
830cb633e5 | ||
|
|
ccb4ab9fe3 | ||
|
|
079422d6ae | ||
|
|
ce482bb087 | ||
|
|
064ecbcb82 | ||
|
|
c25f287779 | ||
|
|
5f38b797f3 | ||
|
|
3fd16ed498 | ||
|
|
1f9c9681a7 | ||
|
|
1c4c6c9e9d | ||
|
|
2d0eaab5df | ||
|
|
bc7fd47908 | ||
|
|
1aa87913c8 | ||
|
|
e2caf971de | ||
|
|
08cadc034d | ||
|
|
9f66980355 | ||
|
|
d8a3208bea | ||
|
|
40c102b4f8 | ||
|
|
573c2e61a6 | ||
|
|
282b898b21 | ||
|
|
195d0db009 | ||
|
|
45dbc90343 | ||
|
|
529d98918a | ||
|
|
c9e3f59f26 | ||
|
|
3e30f4476a | ||
|
|
e0e0011b36 | ||
|
|
a9ddb4fab3 | ||
|
|
3343f4d6c8 |
@@ -0,0 +1,62 @@
|
||||
---
|
||||
# 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
|
||||
@@ -20,28 +20,77 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
concurrency:
|
||||
# At most 1 job per branch. Auto cancel all but scheduled jobs
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
# 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'
|
||||
}}
|
||||
cancel-in-progress: ${{ github.event_name != 'schedule' }}
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Build
|
||||
start:
|
||||
name: Start
|
||||
# Only run scheduled jobs if explicitly enabled for that repo (e.g.: not on forks)
|
||||
# Only run pull request jobs if labelled as needing an coverage run
|
||||
# Only run pull request jobs if labelled as needing a coverage run
|
||||
# Always run workflow dispatch jobs
|
||||
if: |
|
||||
(github.event_name == 'schedule'
|
||||
&& vars.ENABLE_SCHEDULED_JOBS == 'true') ||
|
||||
(github.event_name == 'pull_request'
|
||||
&& contains(github.event.pull_request.labels.*.name, 'pr: dev-coverage')) ||
|
||||
&& ((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')))) ||
|
||||
(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-24.04
|
||||
runs-on: ubuntu-26.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
|
||||
@@ -55,7 +104,7 @@ jobs:
|
||||
archive: ${{ needs.build.outputs.archive }}
|
||||
cc: gcc
|
||||
dev-gcov: true
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
suite: ${{ matrix.test }}${{ matrix.num }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -69,7 +118,7 @@ jobs:
|
||||
name: Publish results to codecov.io
|
||||
needs: test
|
||||
if: ${{ contains(needs.*.result, 'success') && !cancelled() }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
@@ -88,7 +137,7 @@ jobs:
|
||||
find obj_coverage -type f | paste -sd, | sed "s/^/files=/" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7
|
||||
with:
|
||||
disable_file_fixes: true
|
||||
disable_search: true
|
||||
@@ -102,7 +151,7 @@ jobs:
|
||||
name: Prepare HTML report
|
||||
needs: [build, test]
|
||||
if: ${{ contains(needs.*.result, 'success') && !cancelled() }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
steps:
|
||||
- name: Download repository archive
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
@@ -150,6 +199,16 @@ 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
|
||||
@@ -163,7 +222,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-24.04
|
||||
runs-on: ubuntu-slim
|
||||
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@1f40c72289eff860ee54a304f1438e3cff362e0a # v4
|
||||
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # 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@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
|
||||
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7
|
||||
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
context: ${{ env.build_context }}
|
||||
|
||||
@@ -15,16 +15,36 @@ 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: ${{ secrets.GITHUB_TOKEN }}
|
||||
token: ${{ steps.generate-token.outputs.token || secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install packages for build
|
||||
uses: ./repo/.github/actions/install-deps
|
||||
@@ -32,9 +52,18 @@ jobs:
|
||||
stage: format
|
||||
|
||||
- name: Configure git identity
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
APP_SLUG: ${{ steps.generate-token.outputs.app-slug }}
|
||||
run: |
|
||||
git config --global user.email "[email protected]"
|
||||
git config --global user.name "github action"
|
||||
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
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
@@ -55,6 +84,6 @@ jobs:
|
||||
- name: Push
|
||||
run: |-
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
git commit . -m "Apply 'make format' [ci skip]" &&
|
||||
git commit . -m "Apply 'make format'" &&
|
||||
git push origin
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
---
|
||||
# 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' }}
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
# 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"
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
# 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 }}"
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
# 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 }}"
|
||||
@@ -2,13 +2,14 @@
|
||||
# DESCRIPTION: Github actions config
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: Regressions
|
||||
name: Regression
|
||||
|
||||
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
|
||||
@@ -22,14 +23,77 @@ defaults:
|
||||
working-directory: repo
|
||||
|
||||
concurrency:
|
||||
# At most 1 job per branch. Auto cancel on pull requests and on all forks
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
# 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'
|
||||
}}
|
||||
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
|
||||
@@ -38,6 +102,7 @@ jobs:
|
||||
|
||||
build-2604-clang:
|
||||
name: Build | 26.04 | clang
|
||||
needs: start
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: clang
|
||||
@@ -47,6 +112,7 @@ jobs:
|
||||
|
||||
build-2404-gcc:
|
||||
name: Build | 24.04 | gcc
|
||||
needs: start
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: gcc
|
||||
@@ -55,6 +121,7 @@ jobs:
|
||||
|
||||
build-2404-clang:
|
||||
name: Build | 24.04 | clang
|
||||
needs: start
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: clang
|
||||
@@ -63,22 +130,25 @@ 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-15-clang:
|
||||
name: Build | macos-15 | clang
|
||||
build-macos-26-clang:
|
||||
name: Build | macos-26 | clang
|
||||
needs: start
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: clang
|
||||
runs-on: macos-15
|
||||
runs-on: macos-26
|
||||
sha: ${{ github.sha }}
|
||||
|
||||
build-windows:
|
||||
name: Build | windows-2025-vs2026 | msvc
|
||||
needs: start
|
||||
runs-on: windows-2025-vs2026
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
@@ -167,4 +237,29 @@ 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) }}'
|
||||
@@ -36,7 +36,8 @@ jobs:
|
||||
- name: Set up Python venv
|
||||
uses: ./repo/.github/actions/setup-venv
|
||||
with:
|
||||
save: ${{ github.ref == 'refs/heads/master' }}
|
||||
# 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' }}
|
||||
|
||||
- name: Lint
|
||||
run: |-
|
||||
|
||||
@@ -92,7 +92,8 @@ jobs:
|
||||
- name: Set up Python venv
|
||||
uses: ./repo/.github/actions/setup-venv
|
||||
with:
|
||||
save: ${{ github.ref == 'refs/heads/master' }}
|
||||
# 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' }}
|
||||
|
||||
- name: Test
|
||||
id: run-test
|
||||
@@ -125,7 +126,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.ref == 'refs/heads/master' && 7 || 3 }}
|
||||
retention-days: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/master' && 7 || 3 }}
|
||||
|
||||
- name: Fail job if a test failed
|
||||
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
|
||||
|
||||
@@ -22,8 +22,36 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
concurrency:
|
||||
# At most 1 job per branch. Auto cancel all but scheduled jobs
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
# 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'
|
||||
}}
|
||||
cancel-in-progress: ${{ github.event_name != 'schedule' }}
|
||||
|
||||
jobs:
|
||||
@@ -36,14 +64,30 @@ jobs:
|
||||
(github.event_name == 'schedule'
|
||||
&& vars.ENABLE_SCHEDULED_JOBS == 'true') ||
|
||||
(github.event_name == 'pull_request'
|
||||
&& contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter')) ||
|
||||
&& ((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')))) ||
|
||||
(github.event_name == 'workflow_dispatch') ||
|
||||
(github.event_name == 'push')
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.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
|
||||
|
||||
@@ -70,7 +114,7 @@ jobs:
|
||||
cc: gcc
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
sha: ${{ github.sha }}
|
||||
|
||||
build-clang-new:
|
||||
@@ -81,7 +125,7 @@ jobs:
|
||||
cc: clang
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
sha: ${{ github.sha }}
|
||||
|
||||
build-gcc-old:
|
||||
@@ -93,7 +137,7 @@ jobs:
|
||||
cc: gcc
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
sha: ${{ needs.start.outputs.old-sha }}
|
||||
|
||||
build-clang-old:
|
||||
@@ -105,7 +149,7 @@ jobs:
|
||||
cc: clang
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
sha: ${{ needs.start.outputs.old-sha }}
|
||||
|
||||
run-gcc:
|
||||
@@ -121,7 +165,7 @@ jobs:
|
||||
uses: ./.github/workflows/reusable-rtlmeter-run.yml
|
||||
with:
|
||||
tag: gcc
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
cc: gcc
|
||||
verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }}
|
||||
verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }}
|
||||
@@ -148,7 +192,7 @@ jobs:
|
||||
uses: ./.github/workflows/reusable-rtlmeter-run.yml
|
||||
with:
|
||||
tag: clang
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
cc: clang
|
||||
verilator-archive-new: ${{ needs.build-clang-new.outputs.archive }}
|
||||
verilator-archive-old: ${{ needs.build-clang-old.outputs.archive }}
|
||||
@@ -175,7 +219,7 @@ jobs:
|
||||
uses: ./.github/workflows/reusable-rtlmeter-run.yml
|
||||
with:
|
||||
tag: gcc-hier
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
cc: gcc
|
||||
verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }}
|
||||
verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }}
|
||||
@@ -200,7 +244,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-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
outputs:
|
||||
# The run tags, derived from the 'run-*' dependency job names
|
||||
run-tags: ${{ steps.tags.outputs.tags }}
|
||||
@@ -283,7 +327,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-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
steps:
|
||||
- name: Download combined results
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
@@ -334,7 +378,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-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
actions: read
|
||||
steps:
|
||||
@@ -391,7 +435,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-24.04
|
||||
runs-on: ubuntu-26.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 with `make test`. The complete suite requires
|
||||
- Run the full regression tests 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 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 tests 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,6 +143,7 @@ The API you choose determines which test must accompany the change.
|
||||
|
||||
## Commits
|
||||
|
||||
- Subject line is short and imperative and conventionally ends with the PR number:
|
||||
`Support property case (#7721)`. A body is optional and common for non-trivial
|
||||
changes.
|
||||
- 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.
|
||||
|
||||
+37
-9
@@ -49,19 +49,18 @@ if(WIN32)
|
||||
if(DEFINED ENV{WIN_FLEX_BISON})
|
||||
set(WIN_FLEX_BISON "$ENV{WIN_FLEX_BISON}")
|
||||
endif()
|
||||
if(IS_DIRECTORY ${WIN_FLEX_BISON})
|
||||
list(APPEND CMAKE_PREFIX_PATH ${WIN_FLEX_BISON})
|
||||
else()
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"The path specified by WIN_FLEX_BISON environment variable is not an existing folder: ${WIN_FLEX_BISON}"
|
||||
)
|
||||
endif()
|
||||
if(NOT WIN_FLEX_BISON)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"Please install https://github.com/lexxmark/winflexbison and set WIN_FLEX_BISON environment variable. Please use install cmake target after a successful build."
|
||||
)
|
||||
elseif(IS_DIRECTORY "${WIN_FLEX_BISON}")
|
||||
list(APPEND CMAKE_PREFIX_PATH "${WIN_FLEX_BISON}")
|
||||
else()
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"The path specified by WIN_FLEX_BISON environment variable is not an existing folder: ${WIN_FLEX_BISON}"
|
||||
)
|
||||
endif()
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
endif()
|
||||
@@ -93,7 +92,36 @@ if(OBJCACHE_ENABLED)
|
||||
endif()
|
||||
|
||||
find_package(BISON)
|
||||
find_package(FLEX)
|
||||
# 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)
|
||||
|
||||
# Build
|
||||
#set_property(GLOBAL PROPERTY JOB_POOLS one_job=1)
|
||||
|
||||
@@ -18,16 +18,32 @@ Verilator 5.053 devel
|
||||
* Add covergroup runtime registry (#8134). [Matthew Ballance]
|
||||
* Add initial support for solo `binsof` (#8298). [Marco Bartoli]
|
||||
* Add `-fno-dead-members` and optimize dead class member functions (#8830).
|
||||
* Add `--coverage-max-bins` and `--coverage-max-real-bins` (#8499). [Marco Bartoli]
|
||||
* Remove deprecated `--structs-packed`.
|
||||
* Change internal module inlining to after V3Scope (#1539) (#8453). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Change JSON dumps to suppress empty strings, etc.
|
||||
* Change `--protect` to use SHA512 per IEEE 1735.
|
||||
* Support multi-dimensional module instance arrays (#2675) (#8476). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Support VPI interface references (#8081). [Todd Strader]
|
||||
* Support nested array and associative array element member access in constraint 1/4 - pre-cleanup (#8237). [Kamil Danecki, Antmicro Ltd.]
|
||||
* Support unique constraint inside std::randomize (#8241). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Support nested array and associative array element member access in constraint (#8244). [Kamil Danecki, Antmicro Ltd.]
|
||||
* Support rand dynamic arrays used inside `with` clause of randomize (#8246). [Kamil Danecki, Antmicro Ltd.]
|
||||
* Support multiplicity in SVA shapes that use NFA rings (#8267). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support non-overlapped property implications correctly (#8268). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support logic and set operations on binsof (#8306). [Marco Bartoli]
|
||||
* Support `illegal_bins` and `ignore_bins` for cross bins (#8372). [Marco Bartoli]
|
||||
* Support `iff` in covergroup bins (#8406) (#8407). [Marco Bartoli]
|
||||
* Support enclosing cross identifiers in bin selections (#8416). [Marco Bartoli]
|
||||
* Support dynamic cross layouts and fix covergroup bin exclusions priority (#8424). [Marco Bartoli]
|
||||
* Support automatic bins for enum coverpoints (#8462) (#8464). [David Harris]
|
||||
* Support empty queue literals as function and task arguments (#8501) (#8502). [Francesco Urbani]
|
||||
* Support covergroup fixed-size bin arrays (#8514). [Marco Bartoli]
|
||||
* Support wildcard bins (#8516). [Marco Bartoli]
|
||||
* Support coverpoint bin `with` filters (#8518). [Marco Bartoli]
|
||||
* Support waiting on clocking block event through modport (#8403) (#8530). [Saqib Khan]
|
||||
* Optimize NBA shadow copies for partial writes (#8266). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize recursion depth in DFG circular driver tracing (#8272). [Drew Risinger]
|
||||
* Optimize DFG synthesis symbol table ordering (#8282). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize redundant vertex cache lookups in DFG peephole (#8283). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DFG vertex iteration callbacks (#8284). [Geza Lore, Testorrent USA, Inc.]
|
||||
@@ -37,24 +53,109 @@ Verilator 5.053 devel
|
||||
* Optimize netlist teardown when about to exit (#8294). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DFG algorithms with an open addressing hash table (#8307). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize data-type type chain recursion (#8310). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DFG vertex input edge storage (#8313). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize successive whole-element array assignments (#8316). [Patrick Creighton]
|
||||
* Optimize unnecessary per-vertex allocations in NFA lowering (#8319). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize duplicate lookup in NFA property recursion guard (#8321). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize NBA assertions by keeping all writers before readers (#8326). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize wide four-state constant operations in V3Number (#8333). [CatalpaEel]
|
||||
* Optimize split pass (#8385). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize covergroup bin metadata lookup (#8404). [Marco Bartoli]
|
||||
* Optimize dead ports (#8426). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize instance port connections by aliasing rather than by assignment (#8427). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DFG temporary sharing across module instances (#8456). [Michael Bedford Taylor]
|
||||
* Optimize modport resolution with many interface instances (#8496) (#8497). [Deanyone Su]
|
||||
* Optimize covergroup bin generation (#8499). [Marco Bartoli]
|
||||
* Optimize through-package variables in DFG. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize assertion control queries (#8547). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize sched-initial/static/final split to once per sub-function (#8549). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Optimize exhaustive case if-chains with an unconditional else (#8554). [Nick Brereton]
|
||||
* Move module inlining after V3Scope (#8453) (#1539). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix array slice crash when used as a bare value (#5132) (#8262). [Marco Frank]
|
||||
* Fix function reference into generate case block (#5312) (#8532). [Hongseok Choi]
|
||||
* Fix pattern type matching against nested aggregates (#7304).
|
||||
* Fix covergroup-internal references multi-threaded ordering (#7779) (#8255). [Matthew Ballance]
|
||||
* Fix typedef through a parameterized sub-interface (#7943 repair) (#8124) (#8128). [em2machine]
|
||||
* Fix detecting non-inlined awaits in V3Delayed.cpp (#7982). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Fix phased `solve...before` diversity (#8189) (#8194). [Aditya Shevade]
|
||||
* Fix array-size constraint on arrays inside base class (#8219). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix multidimensional packed array type handling (#8235). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix library method argument references (#8252). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix CMake to call Verilator Perl wrapper (#8269) (#8270) (#8311). [Drew Risinger]
|
||||
* Fix real values and unenforced rand/randc type-eligibility rules in constraints (#8273) (#8275) (#8276). [Aditya Shevade]
|
||||
* Fix assignment pattern default fill of an array of unpacked structs (#8289). [Nick Brereton]
|
||||
* Fix escape strings in pattern formatting (#8297). [Marco Bartoli]
|
||||
* Fix DECLFILENAME warning on nested declarations (#8302). [Martijn Wobbes]
|
||||
* Fix cache of boundary vertices in DFG cycle-breaking (#8308). [Shriyans Sahoo]
|
||||
* Fix property and sequence line coverage (#8314). [Andrii, Antmicro Ltd.]
|
||||
* Fix wide typed pattern formatting primitives (#8325). [Marco Bartoli]
|
||||
* Fix circular class inheritance hang (#8324) (#8545). [Saqib Khan]
|
||||
* Fix V3InstrCount with recursive functions (#8327) (#8361). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix parameterized class nested in a parameterized interface (#8328) (#8329). [Kristof Marien]
|
||||
* Fix format of signed enums (#8338). [Marco Bartoli]
|
||||
* 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
|
||||
|
||||
+7
-4
@@ -710,8 +710,10 @@ override undefine COVERAGE_BASE # Use the above variabels instead
|
||||
|
||||
# 'fastcov' setup
|
||||
FASTCOV := nodist/fastcov.py
|
||||
FASTCOV_OPT := -j $(shell nproc)
|
||||
FASTCOV_OPT += --gcov $(GCOV)
|
||||
FASTCOV_JOBS ?= $(shell nproc)
|
||||
FASTCOV_OPT := -j $(FASTCOV_JOBS)
|
||||
# gcov does not free memory between files, so run it in batches via a wrapper
|
||||
FASTCOV_OPT += --gcov nodist/gcov_batch
|
||||
FASTCOV_OPT += --lcov
|
||||
FASTCOV_OPT += --process-gcno
|
||||
FASTCOV_OPT += --branch-coverage
|
||||
@@ -763,6 +765,7 @@ 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
|
||||
@@ -784,7 +787,7 @@ $(COVERAGE_DIR)/verilator.info: $(GCNO_FILES) $(GCDA_FILES)
|
||||
@echo "# fastcov: combining all .gcda files into lcov .info"
|
||||
@echo "####################################################################"
|
||||
@mkdir -p $(COVERAGE_DIR)
|
||||
/usr/bin/time -f "That took %E" \
|
||||
GCOV=$(GCOV) /usr/bin/time -f "That took %E" \
|
||||
$(FASTCOV) $(FASTCOV_OPT) --output $@
|
||||
|
||||
# Filter combined .info file for patch coverage
|
||||
@@ -794,8 +797,8 @@ $(COVERAGE_DIR)/verilator-patch.info: $(COVERAGE_DIR)/verilator.info
|
||||
@echo "####################################################################"
|
||||
rm -f $(COVERAGE_DIR)/empty-patch
|
||||
git diff $(COVERAGE_REF_BASE) -- include src > $(COVERAGE_DIR)/filter.patch
|
||||
[ -s $(COVERAGE_DIR)/filter.patch ] || touch $(COVERAGE_DIR)/empty-patch
|
||||
$(FASTCOV) -C $^ --lcov -o $@ --diff-filter $(COVERAGE_DIR)/filter.patch
|
||||
grep -q '^DA:' $@ || touch $(COVERAGE_DIR)/empty-patch
|
||||
|
||||
# Build coverage report
|
||||
$(COVERAGE_DIR)/report/index.html: $(COVERAGE_DIR)/verilator$(if $(COVERAGE_REF_BASE),-patch).info
|
||||
|
||||
+11
-11
@@ -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/build-test.yml?branch=master&label=regressions
|
||||
:target: https://github.com/verilator/verilator/actions/workflows/build-test.yml
|
||||
.. |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
|
||||
|
||||
.. |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
|
||||
- Accepts Verilog or SystemVerilog, including UVM
|
||||
- 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,
|
||||
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
|
||||
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.
|
||||
|
||||
Performance
|
||||
===========
|
||||
|
||||
@@ -419,6 +419,8 @@ 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,6 +79,13 @@ 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"
|
||||
|
||||
@@ -88,6 +95,9 @@ 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,3 +98,36 @@ 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
|
||||
|
||||
@@ -25,6 +25,8 @@ 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),
|
||||
@@ -59,6 +61,8 @@ 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),
|
||||
@@ -92,6 +96,8 @@ CASES = {
|
||||
("BlackParrot:1x1:*", 4),
|
||||
("BlackParrot:2x2:*", 16),
|
||||
("BlackParrot:4x4:*", 25),
|
||||
("HammerBlade:16x8:*", 5),
|
||||
("HammerBlade:2x1:*", 3),
|
||||
("NVDLA:*", 25),
|
||||
("OpenPiton:16x16:dhry", 16),
|
||||
("OpenPiton:1x1:*", 4),
|
||||
@@ -110,6 +116,8 @@ 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),
|
||||
@@ -142,6 +150,8 @@ 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),
|
||||
@@ -173,6 +183,8 @@ CASES_PR = {
|
||||
"gcc-hier": [
|
||||
("BlackParrot:1x1:*", 4),
|
||||
("BlackParrot:4x4:*", 25),
|
||||
("HammerBlade:16x8:*", 5),
|
||||
("HammerBlade:2x1:*", 3),
|
||||
("NVDLA:*", 25),
|
||||
("OpenPiton:16x16:dhry", 16),
|
||||
("OpenPiton:1x1:*", 4),
|
||||
|
||||
@@ -29,6 +29,7 @@ RUN apt-get update \
|
||||
python3 \
|
||||
zlib1g \
|
||||
zlib1g-dev \
|
||||
liblz4-dev \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
@@ -264,6 +264,7 @@ 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])
|
||||
@@ -271,6 +272,22 @@ 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])
|
||||
|
||||
+19
-1
@@ -7,6 +7,7 @@ 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
|
||||
@@ -57,16 +58,19 @@ Chykon
|
||||
Congcong Cai
|
||||
Conor McCullough
|
||||
Cookie
|
||||
cynicalmindset
|
||||
Dan Ruelas-Petrisko
|
||||
Daniel Bates
|
||||
Danny Oler
|
||||
Dave Sargeant
|
||||
David Garau
|
||||
David Harris
|
||||
David Horton
|
||||
David Ledger
|
||||
David Metz
|
||||
David Stanford
|
||||
David Turner
|
||||
Deanyone Su
|
||||
dependabot[bot]
|
||||
Dercury
|
||||
Demin Han
|
||||
@@ -79,6 +83,7 @@ Drew Risinger
|
||||
Drew Taussig
|
||||
Driss Hafdi
|
||||
Edgar E. Iglesias
|
||||
Edmund Lam
|
||||
Eddie Rydell
|
||||
em2machine
|
||||
emmettifelts
|
||||
@@ -94,6 +99,7 @@ Fan Shupei
|
||||
february cozzocrea
|
||||
Felix Neumärker
|
||||
Felix Yan
|
||||
Francesco Urbani
|
||||
Frans Skarman
|
||||
Fuad Ismail
|
||||
Furqan Nadir
|
||||
@@ -115,6 +121,7 @@ Han Qi
|
||||
Harald Heckmann
|
||||
Hennadii Chernyshchyk
|
||||
hjsanjana
|
||||
Hongseok Choi
|
||||
Howard Su
|
||||
Huang Rui
|
||||
Huanghuang Zhou
|
||||
@@ -147,6 +154,7 @@ jeffrey.song
|
||||
Jens Yuechao Liu
|
||||
Jeremy Bennett
|
||||
Jesse Taube
|
||||
Jesús Alberto López Soto
|
||||
Jevin Sweval
|
||||
Jiacheng Qian
|
||||
Jiamin Zhu
|
||||
@@ -217,6 +225,7 @@ Marshal Qiao
|
||||
Martijn Wobbes
|
||||
Martin Schmidt
|
||||
Martin Stadler
|
||||
Martin Velay
|
||||
Mateusz Gancarz
|
||||
Matt Stroud
|
||||
Matthew Ballance
|
||||
@@ -242,9 +251,11 @@ Natan Kreimer
|
||||
Nathan Graybeal
|
||||
Nathan Kohagen
|
||||
Nathan Myers
|
||||
Nazar Zibilyuk
|
||||
Nick Brereton
|
||||
Nikolai Kumar
|
||||
Nikolay Puzanov
|
||||
Noam Gallmann
|
||||
Nolan Poe
|
||||
Oleh Maksymenko
|
||||
Patrick Creighton
|
||||
@@ -287,6 +298,7 @@ Ryan Ziegler
|
||||
Ryszard Rozak
|
||||
Saksham Gupta
|
||||
Samuel Riedel
|
||||
Saqib Khan
|
||||
Sean Cross
|
||||
Sebastien Van Cauwenberghe
|
||||
Secturion
|
||||
@@ -297,6 +309,7 @@ Seth Pellegrino
|
||||
Shashvat Prabhu
|
||||
Shogo Yamazaki
|
||||
Shou-Li Hsu
|
||||
Shriyans Sahoo
|
||||
spomatasmd
|
||||
Srinivasan Venkataramanan
|
||||
Stefan Wallentowitz
|
||||
@@ -305,8 +318,10 @@ Steven Hugg
|
||||
Stuart Morris
|
||||
sumpster
|
||||
Szymon Gizler
|
||||
Szymon Jędras
|
||||
Sören Tempel
|
||||
Sumanth Kadiyala
|
||||
Sunimali Rathnayake
|
||||
Teng Huang
|
||||
Thomas Aldrian
|
||||
Thomas Brown
|
||||
@@ -327,9 +342,11 @@ 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
|
||||
@@ -345,6 +362,7 @@ Wolfgang Mayerwieser
|
||||
Xi Zhang
|
||||
Yan Xu
|
||||
Yangyu Chen
|
||||
yash
|
||||
Yilin Li
|
||||
Yilou Wang
|
||||
Yinan Xu
|
||||
@@ -363,4 +381,4 @@ Zixi Li
|
||||
Zubin Jain
|
||||
Àlex Torregrosa
|
||||
Ícaro Lima
|
||||
Sunimali Rathnayake
|
||||
ÍÍ>> Please insert into sorted position, and see header as to agreement
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.. comment: generated by t_lint_didnotconverge_bad
|
||||
.. code-block::
|
||||
|
||||
-V{t#,#} 'stl' region trigger index 0 is active: @([hybrid] a)
|
||||
-V{t#,#} 'stl' region trigger index 0 is active: @([hybrid] b)
|
||||
%Error-DIDNOTCONVERGE: Settle region did not converge after '--converge-limit' of 10000 tries
|
||||
|
||||
@@ -40,6 +40,30 @@ 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
|
||||
@@ -207,6 +231,9 @@ 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
|
||||
-------------------------
|
||||
|
||||
@@ -316,6 +316,33 @@ 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
|
||||
@@ -813,6 +840,10 @@ 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
|
||||
@@ -821,6 +852,11 @@ 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
|
||||
@@ -2268,7 +2304,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 run gets a different randomization sequence with
|
||||
each regression test 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.
|
||||
@@ -2297,7 +2333,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 run gets a different randomization sequence with
|
||||
each regression test 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 environment variable FLEX_INCLUDE to the directory containing
|
||||
the CMake variable FLEX_INCLUDE_DIR to the directory containing
|
||||
FlexLexer.h and ensure that flex/bison is available within the PATH.
|
||||
|
||||
To obtain Verilator sources download
|
||||
|
||||
@@ -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, regressions, it is usually worth spending extra
|
||||
cases, for example, regression testing, 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
|
||||
|
||||
@@ -162,6 +162,18 @@ Hierarchy blocks have some limitations, including:
|
||||
|
||||
- Delays are not allowed in hierarchy blocks.
|
||||
|
||||
- Setting type parameters of a hierarchy block is unsupported.
|
||||
|
||||
- A ``defparam`` setting a parameter inside a hierarchy block is
|
||||
unsupported.
|
||||
|
||||
- String parameter values of a hierarchy block containing a newline, a
|
||||
double quote, ``/*``, or whitespace followed by ``//`` are unsupported.
|
||||
|
||||
- Values that are equal but differ in width or signedness (``5`` and
|
||||
``8'd5``), including relative to the default value, are unsupported for a
|
||||
hierarchy block parameter declared without a data type.
|
||||
|
||||
But, the following usage is supported:
|
||||
|
||||
- Nested hierarchy blocks. A hierarchy block may instantiate other
|
||||
|
||||
+2
-11
@@ -1031,15 +1031,6 @@ 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
|
||||
++++++++++++++++++++
|
||||
|
||||
@@ -1645,7 +1636,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 may exhaust resources on some Linux systems,
|
||||
- Running regression tests 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
|
||||
@@ -2422,7 +2413,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 regressions. See also
|
||||
seconds. Intended for use only in automated regression testing. See also
|
||||
``--rerun``, and ``--verbose`` which is not the opposite of ``--quiet``.
|
||||
|
||||
.. option:: --rerun
|
||||
|
||||
@@ -305,6 +305,7 @@ Mahmoudy
|
||||
Makefile
|
||||
Makefiles
|
||||
Maksymenko
|
||||
Manzoni
|
||||
Marche
|
||||
Markley
|
||||
Marquet
|
||||
@@ -346,6 +347,7 @@ Narcis
|
||||
Nassim
|
||||
Nauticus
|
||||
Nayak
|
||||
Nazar
|
||||
Nenakhov
|
||||
Newgard
|
||||
Nigam
|
||||
@@ -588,6 +590,7 @@ Yurii
|
||||
Zaruba
|
||||
Zhang
|
||||
Zhi
|
||||
Zibilyuk
|
||||
abirkmanis
|
||||
accessor
|
||||
accessors
|
||||
@@ -719,6 +722,7 @@ deduplication
|
||||
defenv
|
||||
defname
|
||||
defparam
|
||||
dehash
|
||||
demangling
|
||||
dep
|
||||
deparametrized
|
||||
@@ -738,6 +742,7 @@ devel
|
||||
difftree
|
||||
dir
|
||||
disambiguates
|
||||
discoverability
|
||||
displayb
|
||||
distcc
|
||||
doxygen
|
||||
@@ -1134,6 +1139,7 @@ shortint
|
||||
shortreal
|
||||
signame
|
||||
signedness
|
||||
slp
|
||||
sp
|
||||
specparam
|
||||
splitme
|
||||
@@ -1258,6 +1264,7 @@ vcddiff
|
||||
vcoverage
|
||||
vdhotre
|
||||
vec
|
||||
vectorize
|
||||
ventana
|
||||
venv
|
||||
ver
|
||||
|
||||
+44
-61
@@ -1180,23 +1180,28 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
|
||||
if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override
|
||||
} else if (formatAttr == VL_VFORMATATTR_ENUM
|
||||
|| formatAttr == VL_VFORMATATTR_ENUM_SIGNED) {
|
||||
// Always <= VL_QUADSIZE; emit uses non-ENUM format for wider enums
|
||||
const int numericAttr = formatAttr == VL_VFORMATATTR_ENUM_SIGNED
|
||||
? VL_VFORMATATTR_SIGNED
|
||||
: VL_VFORMATATTR_UNSIGNED;
|
||||
lbits = va_arg(ap, int);
|
||||
ld = VL_VA_ARG_Q_(ap, lbits);
|
||||
strwide.resize(2);
|
||||
WDataOutP strwidep = WDataOutP::external(strwide.data());
|
||||
VL_SET_WQ(strwidep, ld);
|
||||
lwp = strwidep;
|
||||
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);
|
||||
}
|
||||
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 (enump && !enump->empty()) {
|
||||
if (fmt != 'p' && fmt != 's') {
|
||||
formatAttr = numericAttr;
|
||||
} else if (enump && !enump->empty()) {
|
||||
formatAttr = (fmt == 'p') ? VL_VFORMATATTR_COMPLEX : VL_VFORMATATTR_STRING;
|
||||
if (fmt == 'd') formatAttr = numericAttr;
|
||||
thingp = const_cast<std::string*>(enump);
|
||||
} else if (fmt == 'p' && widthSet && width == 0) {
|
||||
output += "'h";
|
||||
@@ -1690,7 +1695,8 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
_vl_vsss_read_str(fp, floc, fromp, fstr,
|
||||
std::back_insert_iterator<std::string>{t_tmp},
|
||||
"0123456789+-xXzZ?_");
|
||||
if (!t_tmp[0]) goto done;
|
||||
t_tmp.erase(std::remove(t_tmp.begin(), t_tmp.end(), '_'), t_tmp.end());
|
||||
if (t_tmp.empty()) goto done;
|
||||
if (formatAttr == VL_VFORMATATTR_SIGNED) {
|
||||
QData ld = 0;
|
||||
std::sscanf(t_tmp.c_str(), "%30" PRIu64, &ld);
|
||||
@@ -1827,8 +1833,8 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
return got;
|
||||
|
||||
done:
|
||||
// Scan stopped early, return parsed or EOF
|
||||
if (_vl_vsss_eof(fp, floc)) return -1;
|
||||
// Scan stopped early; EOF only if input ended before the first conversion
|
||||
if (got == 0 && _vl_vsss_eof(fp, floc)) return -1;
|
||||
return got;
|
||||
}
|
||||
|
||||
@@ -2396,8 +2402,10 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
|
||||
VL_ZERO_W(rbits, rwp);
|
||||
switch (std::tolower(fmt)) {
|
||||
case 'd': {
|
||||
std::string digits{dp};
|
||||
digits.erase(std::remove(digits.begin(), digits.end(), '_'), digits.end());
|
||||
int64_t lld = 0;
|
||||
std::sscanf(dp, "%30" PRId64, &lld);
|
||||
std::sscanf(digits.c_str(), "%30" PRId64, &lld);
|
||||
VL_SET_WQ(rwp, lld);
|
||||
break;
|
||||
}
|
||||
@@ -3123,32 +3131,16 @@ VerilatedContext::Serialized::Serialized() {
|
||||
|
||||
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
|
||||
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
||||
if (assertCtlsLocked()) return;
|
||||
if (m_ns.m_assertCtlsLocked) return;
|
||||
// Set all assert and directive types when true, clear otherwise.
|
||||
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
|
||||
}
|
||||
bool VerilatedContext::assertOnGet(VerilatedAssertType_t type,
|
||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
|
||||
return assertCtlGet(VerilatedAssertCtlQuery::ASSERT_CTL_ON, type, directive);
|
||||
}
|
||||
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
if (assertCtlsLocked()) return;
|
||||
m_s.m_assertOn |= assertOnMask(types, directives);
|
||||
}
|
||||
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
if (assertCtlsLocked()) return;
|
||||
m_s.m_assertOn &= ~assertOnMask(types, directives);
|
||||
}
|
||||
bool VerilatedContext::assertCtlsLocked() const VL_MT_SAFE { return m_ns.m_assertCtlsLocked; }
|
||||
void VerilatedContext::assertCtlsLocked(bool flag) VL_MT_SAFE { m_ns.m_assertCtlsLocked = flag; }
|
||||
void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the
|
||||
// selected bits until Unlock; On/Off/Kill leave locked bits unchanged.
|
||||
// +verilator+assert+lock freezes everything, including Lock/Unlock itself.
|
||||
if (assertCtlsLocked()) return;
|
||||
if (m_ns.m_assertCtlsLocked) return;
|
||||
const uint32_t mask = assertOnMask(types, directives);
|
||||
const uint32_t lockedMask = mask & ~m_s.m_assertLock;
|
||||
switch (controlType) {
|
||||
@@ -3381,6 +3373,15 @@ 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");
|
||||
|
||||
@@ -3391,14 +3392,10 @@ void VerilatedContext::threads(unsigned n) {
|
||||
}
|
||||
|
||||
m_useNumaAssign = true;
|
||||
m_threadsSet = true;
|
||||
if (m_threads == n) return; // To avoid unnecessary warnings
|
||||
m_threads = n;
|
||||
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);
|
||||
}
|
||||
warnThreadsOversubscribed(m_threads);
|
||||
}
|
||||
|
||||
void VerilatedContext::useNumaAssign(bool flag) { m_useNumaAssign = flag; }
|
||||
@@ -3464,7 +3461,15 @@ void VerilatedContext::addModel(const VerilatedModel* modelp) {
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVirtualBase* VerilatedContext::threadPoolp() {
|
||||
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);
|
||||
}
|
||||
if (m_threads == 1) return nullptr;
|
||||
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
|
||||
return m_threadPool.get();
|
||||
@@ -3580,7 +3585,7 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||
std::string str;
|
||||
uint64_t u64;
|
||||
if (arg == "+verilator+assert+lock") {
|
||||
assertCtlsLocked(true);
|
||||
m_ns.m_assertCtlsLocked = true;
|
||||
} else if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
|
||||
coverageFilename(str);
|
||||
} else if (arg == "+verilator+debug") {
|
||||
@@ -4180,28 +4185,6 @@ 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;
|
||||
@@ -4495,7 +4478,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. See IEEE 1800-2023 35.5.3.");
|
||||
+ "'context', or missing svSetScope (IEEE 1800-2023 35.5.3)");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+67
-51
@@ -261,7 +261,7 @@ class VL_SCOPED_CAPABILITY VerilatedLockGuard final {
|
||||
VL_UNCOPYABLE(VerilatedLockGuard);
|
||||
|
||||
private:
|
||||
VerilatedMutex& m_mutexr;
|
||||
VerilatedMutex& m_mutexr; // Mutex protecting the guard
|
||||
|
||||
public:
|
||||
/// Construct and hold given mutex lock until destruction or unlock()
|
||||
@@ -305,6 +305,34 @@ 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;
|
||||
|
||||
//=========================================================================
|
||||
@@ -380,8 +408,7 @@ 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;
|
||||
// Whether this is the top level model during profiling
|
||||
bool m_profTopLevel = false;
|
||||
bool m_profTopLevel = false; // Top level model during profiling
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -483,8 +510,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 inline uint32_t assertOnMask(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_PURE;
|
||||
static constexpr 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;
|
||||
@@ -502,20 +529,18 @@ 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
|
||||
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.
|
||||
// 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_assertLock{0}; // Locked assertion bits (IEEE 1800-2023 20.11
|
||||
// Lock/Unlock); same layout as m_assertOn. While
|
||||
// a bit is locked, On/Off/Kill leave it unchanged.
|
||||
std::atomic<uint32_t> m_assertPassOnVacuous{
|
||||
std::numeric_limits<uint32_t>::max()}; // Enabled vacuous pass actions
|
||||
std::atomic<uint32_t> m_assertPassOnNonvacuous{
|
||||
std::numeric_limits<uint32_t>::max()}; // Enabled nonvacuous pass actions
|
||||
std::atomic<uint32_t> m_assertFailOn{
|
||||
std::numeric_limits<uint32_t>::max()}; // Enabled fail actions
|
||||
// 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::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
|
||||
@@ -549,8 +574,7 @@ 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}; // When true, all assertion-control updates are ignored
|
||||
std::atomic<bool> m_assertCtlsLocked{false}; // 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
|
||||
@@ -584,6 +608,8 @@ 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
|
||||
@@ -607,9 +633,8 @@ 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;
|
||||
uint64_t m_magic = MAGIC; // Magic to check for bad construction
|
||||
|
||||
private:
|
||||
// CONSTRUCTORS
|
||||
@@ -626,28 +651,9 @@ public:
|
||||
bool assertOn() const VL_MT_SAFE;
|
||||
/// Enable all assertion types
|
||||
void assertOn(bool flag) VL_MT_SAFE;
|
||||
/// Get enabled status for given assertion types
|
||||
bool assertOnGet(VerilatedAssertType_t type,
|
||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
|
||||
/// Set enabled status for given assertion types
|
||||
void assertOnSet(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||
/// Clear enabled status for given assertion types
|
||||
void assertOnClear(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||
/// Return if assertion-control updates are locked. When locked, RTL assert
|
||||
// control statements ($asserton/$assertoff/$assertcontrol) are ignored, as
|
||||
// are updates from the C++ API.
|
||||
bool assertCtlsLocked() const VL_MT_SAFE;
|
||||
/// Lock/unlock assertion-control updates.
|
||||
void assertCtlsLocked(bool flag) VL_MT_SAFE;
|
||||
/// Apply assertion control for given control, assertion, and directive types
|
||||
void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||
/// Get assertion-control runtime state. Boolean queries return 0/1, Kill returns
|
||||
/// the generation count.
|
||||
inline uint32_t assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
|
||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
|
||||
/// Return if calculating of unused signals (for traces)
|
||||
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
|
||||
/// Enable calculation of unused signals (for traces)
|
||||
@@ -823,6 +829,10 @@ 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 {
|
||||
@@ -831,7 +841,9 @@ public:
|
||||
|
||||
// Internal: Model and thread setup
|
||||
void addModel(const VerilatedModel* modelp);
|
||||
VerilatedVirtualBase* threadPoolp();
|
||||
// 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);
|
||||
void prepareClone();
|
||||
VerilatedVirtualBase* threadPoolpOnClone();
|
||||
VerilatedVirtualBase*
|
||||
@@ -1329,8 +1341,9 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
|
||||
}
|
||||
|
||||
// Defined here, not in-class: VL_CLOG2_I / VL_FATAL_MT (verilated_funcs.h) are not yet in scope
|
||||
uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_PURE {
|
||||
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) {
|
||||
@@ -1338,21 +1351,24 @@ uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
uint32_t
|
||||
VerilatedContext::assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
|
||||
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
|
||||
const uint32_t mask = assertOnMask(type, directive);
|
||||
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 & mask) != 0;
|
||||
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)];
|
||||
return m_s.m_assertKill[VL_CLOG2_I(mask)].load();
|
||||
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
|
||||
return (m_s.m_assertPassOnVacuous & mask) != 0;
|
||||
return (m_s.m_assertPassOnVacuous.load() & 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;
|
||||
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;
|
||||
|
||||
@@ -99,6 +99,16 @@ 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,26 +39,34 @@ 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':'cross_bins', 'short':'Cb', 'group':0, 'default':'', 'descr':'Comma-separated per-dimension bin names for cross coverage points'")
|
||||
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':'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"
|
||||
@@ -77,14 +85,18 @@ 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,6 +28,8 @@
|
||||
#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
|
||||
@@ -50,6 +52,9 @@ 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;
|
||||
@@ -62,6 +67,12 @@ 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
|
||||
|
||||
+1488
-61
File diff suppressed because it is too large
Load Diff
+221
-45
@@ -41,6 +41,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedCovContext;
|
||||
@@ -49,6 +50,15 @@ 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
|
||||
@@ -99,6 +109,10 @@ 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"
|
||||
@@ -115,15 +129,27 @@ 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() = default;
|
||||
VlCoverpoint();
|
||||
~VlCoverpoint() override;
|
||||
|
||||
// METHODS
|
||||
// ---- configuration (from generated constructor) ----
|
||||
@@ -135,13 +161,78 @@ public:
|
||||
int line, int col) {
|
||||
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
|
||||
}
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
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);
|
||||
|
||||
// ---- 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.
|
||||
|
||||
@@ -152,21 +243,17 @@ public:
|
||||
std::string normalBinName(uint32_t crossIdx) const; // name of the crossIdx-th Normal bin
|
||||
|
||||
// ---- VlCoverpointIf ----
|
||||
uint32_t binCount() const override { return m_total; }
|
||||
/// Bins removed for having no value (IEEE 1800-2023 19.11.1) are not reported.
|
||||
uint32_t binCount() const override;
|
||||
std::string binName(uint32_t i) const override;
|
||||
// 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(); }
|
||||
// Deliberately not on VlCoverpointIf: only coverage-database registration needs it.
|
||||
VlCovBinKind binKind(uint32_t i) const { return namerFor(reportedBin(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 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;
|
||||
}
|
||||
for (const uint32_t bin : m_crossToBin) {
|
||||
if (m_counts[bin] >= m_atLeast) ++numCovered;
|
||||
}
|
||||
covered = numCovered;
|
||||
total = m_normal;
|
||||
@@ -203,6 +290,9 @@ 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); }
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -213,7 +303,7 @@ public:
|
||||
/// 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 owns the fixed arrays. This shared core does not allocate bin
|
||||
/// VlCoverCrossT and VlCoverCrossDyn own their storage. This shared core does not allocate bin
|
||||
/// storage, and its borrowed storage pointers remain valid for the instance.
|
||||
|
||||
class VlCoverCross VL_NOT_FINAL : public VlCoverpointIf {
|
||||
@@ -225,14 +315,16 @@ protected:
|
||||
uint32_t stride; // Flat-index stride
|
||||
};
|
||||
struct Bin final {
|
||||
const uint64_t* selectionp; // Slice of the fixed selection storage
|
||||
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
|
||||
const uint32_t* wordIndicesp = nullptr; // Slice of the packed 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
|
||||
@@ -241,8 +333,8 @@ protected:
|
||||
};
|
||||
template <typename T>
|
||||
class View final {
|
||||
T* m_beginp;
|
||||
T* m_endp;
|
||||
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)
|
||||
@@ -261,6 +353,7 @@ protected:
|
||||
View<uint32_t> binWords; // Nonzero selection words, grouped by bin
|
||||
uint64_t* selectionp; // [bins.size() * ceil(m_numAutoBins / 64)]
|
||||
uint32_t numBins = 0; // Bins configured by addBin()
|
||||
uint32_t normalBins = 0; // Explicit bins contributing to coverage
|
||||
uint32_t minBinWords = 0; // Minimum nonzero-word count across explicit bins
|
||||
uint32_t numTouchedWords = 0; // Active prefix of wordsp[].touchedWord
|
||||
};
|
||||
@@ -278,7 +371,7 @@ private:
|
||||
// storable anyway: m_flatCountsp alone would need 16GB.
|
||||
uint32_t m_numAutoBins = 0; // Product of per-dim Normal bin counts
|
||||
uint32_t m_numCovered = 0; // Distinct bins hit >= 1 (maintained incrementally)
|
||||
Dimension* m_dimensionsp = nullptr; // [m_dims], owned by VlCoverCrossT
|
||||
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
|
||||
|
||||
@@ -289,6 +382,7 @@ private:
|
||||
void incrementAuto(uint32_t idx) {
|
||||
if (m_flatCountsp[idx]++ == 0) ++m_numCovered;
|
||||
}
|
||||
void incrementBin(Bin& bin);
|
||||
template <bool T_RecordHits>
|
||||
void incrementTuple(uint32_t idx) {
|
||||
Explicit& data = *m_explicitp;
|
||||
@@ -325,20 +419,28 @@ protected:
|
||||
m_flatCountsp = countsp;
|
||||
m_explicitp = explicitp;
|
||||
}
|
||||
void shape(uint32_t dims, uint32_t tuples) {
|
||||
m_dims = dims;
|
||||
m_numAutoBins = tuples;
|
||||
}
|
||||
void addBinImpl(VlCovBinKind kind, const uint64_t* selectionp, uint32_t words,
|
||||
const char* namep, const char* filep, int line, int col, uint32_t iffIndex);
|
||||
|
||||
public:
|
||||
VL_UNCOPYABLE(VlCoverCross);
|
||||
|
||||
// METHODS
|
||||
// ---- configuration (from generated constructor, after coverpoints init'd) ----
|
||||
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
|
||||
int line, int col);
|
||||
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(std::initializer_list<uint64_t> selection, const char* namep, const char* filep,
|
||||
int line, int col);
|
||||
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.
|
||||
void finalizeBins();
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
virtual void finalizeBins();
|
||||
/// Register the bins in the coverage database; see VlCoverpoint::registerBins().
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page, uint32_t itemWeight,
|
||||
uint32_t groupWeight);
|
||||
|
||||
// ---- hot path (from generated sample(), after all coverpoints sampled) ----
|
||||
/// Sample automatic and explicit bins, optionally applying per-bin iff guards.
|
||||
@@ -346,7 +448,7 @@ public:
|
||||
void sample(const bool* binIffs = nullptr);
|
||||
|
||||
// ---- VlCoverpointIf ----
|
||||
// Explicit bins precede retained automatic bins; all are Normal bins.
|
||||
// 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())
|
||||
@@ -355,7 +457,8 @@ public:
|
||||
std::string binName(uint32_t i) const override;
|
||||
void coverageParts(double& covered, double& total) const override {
|
||||
covered = m_numCovered;
|
||||
total = binCount();
|
||||
total = hasExplicitBins() ? m_explicitp->normalBins + m_explicitp->autoBins.size()
|
||||
: m_numAutoBins;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -404,6 +507,36 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Construction-time cross layout over finalized coverpoints, sharing the sampling core.
|
||||
class VlCoverCrossDyn final : public VlCoverCross {
|
||||
class Layout;
|
||||
std::unique_ptr<Layout> m_layoutp; // Owned cross storage and construction-time selections
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCoverCrossDyn();
|
||||
~VlCoverCrossDyn() override;
|
||||
|
||||
// METHODS
|
||||
/// Initialize after all feeding coverpoints have finalized their live bins.
|
||||
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
|
||||
int line, int col) override;
|
||||
/// Build cross-bin selections in postfix order.
|
||||
void selectAll();
|
||||
/// Start a binsof term over the live bins declared in [first, end) of dimension 'dim'.
|
||||
void selectDim(uint32_t dim, uint32_t first, uint32_t end, bool negated, bool intersect);
|
||||
void selectRange(QData lo, QData hi);
|
||||
void selectRangeW(WDataInP lop, WDataInP hip);
|
||||
void selectDimEnd();
|
||||
void selectAnd();
|
||||
void selectOr();
|
||||
// Save a selection without renumbering guards when empty bins are removed.
|
||||
void selectBin(VlCovBinKind kind, const char* namep, const char* filep, int line, int col,
|
||||
uint32_t iffIndex);
|
||||
/// Apply cross exclusions and bind finalized storage to the sampling core.
|
||||
void finalizeBins() override;
|
||||
};
|
||||
|
||||
class VlCovergroupType;
|
||||
|
||||
//=============================================================================
|
||||
@@ -428,6 +561,12 @@ class VlCovergroupInst final {
|
||||
uint32_t m_slot = 0; // Index into m_typep->m_insts; unlink-by-swap rewrites
|
||||
#endif
|
||||
uint32_t m_attachCount = 1; // SV handles bound here; 1 from construction
|
||||
// option.weight of the SV object that created this node, while that object
|
||||
// lives; borrowed through VlCovInstHandle::lendWeight().
|
||||
const IData* m_weightp = nullptr;
|
||||
IData m_loadedWeight = 1; // Last option.weight loaded through m_weightp
|
||||
int32_t m_weight = 1; // Weight in use, never negative; kept once the object is gone
|
||||
VlFileLineDebug m_fileline; // Covergroup declaration, where a negative weight is reported
|
||||
bool m_retained = false; // VM_COVERAGE: dead, but kept for registered count pointers
|
||||
|
||||
// Reads m_items to fold the residue; owns m_slot and m_retained.
|
||||
@@ -454,6 +593,7 @@ public:
|
||||
m_items.emplace_back(cxp);
|
||||
return cxp; // borrowed by the generated class
|
||||
}
|
||||
VlCoverCrossDyn* addCrossDyn();
|
||||
|
||||
// ---- attach counting (from VlCovInstHandle) ----
|
||||
void attachInc() { ++m_attachCount; }
|
||||
@@ -462,35 +602,49 @@ public:
|
||||
// here, and because it frees 'this'.
|
||||
bool attachDec() { return --m_attachCount == 0; }
|
||||
|
||||
// ---- instance weight (from VlCovInstHandle) ----
|
||||
void lendWeight(const IData* weightp, VlFileLineDebug fileline) {
|
||||
m_weightp = weightp;
|
||||
m_fileline = fileline;
|
||||
loadWeight();
|
||||
}
|
||||
// The lending object is being destroyed. Its members may already be gone, so
|
||||
// the weight is not read again; the last loaded value stays in effect.
|
||||
void unlendWeight(const IData* weightp) {
|
||||
if (m_weightp == weightp) m_weightp = nullptr;
|
||||
}
|
||||
/// Load option.weight from the lending object. SV writes the member directly
|
||||
/// (assignments, ref and output arguments, $value$plusargs, ...), so this is
|
||||
/// where a new value is seen, and checked once: a negative weight is reported as
|
||||
/// an error, and counts as zero.
|
||||
void loadWeight();
|
||||
/// Weight of this instance in its type's coverage (option.weight, IEEE
|
||||
/// 1800-2023 19.11.3), as last loaded; never negative.
|
||||
int32_t weight() const { return m_weight; }
|
||||
|
||||
// ---- introspection ----
|
||||
VlCovergroupType* typep() const { return m_typep; }
|
||||
uint32_t instId() const { return m_instId; }
|
||||
// True once retired but kept alive because the coverage database holds raw
|
||||
// pointers into this node's bin counts (VM_COVERAGE); see retire().
|
||||
bool retained() const { return m_retained; }
|
||||
// Sum of the instance's items' covered/total bin counts. Matches what the
|
||||
// generated get_inst_coverage() computes; see foldResidue().
|
||||
void coverageParts(double& covered, double& total) const {
|
||||
covered = 0.0;
|
||||
total = 0.0;
|
||||
for (const auto& itemp : m_items) {
|
||||
double c = 0.0;
|
||||
double t = 0.0;
|
||||
itemp->coverageParts(c, t);
|
||||
covered += c;
|
||||
total += t;
|
||||
}
|
||||
}
|
||||
/// 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. Weight is 1 everywhere until option.weight is plumbed.
|
||||
/// 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 coverage, each in 0..100
|
||||
double sumCoverage = 0.0; // Sigma of per-instance weight * coverage (0..100)
|
||||
double sumWeight = 0.0; // Sigma of per-instance weight
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -506,11 +660,13 @@ class VlCovergroupType final {
|
||||
uint32_t m_createdInsts = 0; // Instances ever created; never decremented
|
||||
uint32_t m_nextInstId = 0; // Monotonic; slots are reused, ids never are
|
||||
VlCovRetiredAvg m_retired; // Contribution of every instance that has died
|
||||
IData m_loadedTypeWeight = 1; // Last type_option.weight loaded by coverage()
|
||||
int32_t m_typeWeight = 1; // type_option.weight in use, never negative
|
||||
|
||||
// PRIVATE METHODS
|
||||
// Harvest instp's contribution into m_retired. Must run before instp is
|
||||
// unlinked: it reads the instance's items.
|
||||
void foldResidue(const VlCovergroupInst* instp);
|
||||
void foldResidue(VlCovergroupInst* instp);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -526,6 +682,12 @@ public:
|
||||
// True if any node here still has an SV handle bound to it, and so can be
|
||||
// retired again after the registry is destroyed. See ~VlCovRegistry.
|
||||
bool anyAttached() const;
|
||||
/// Type coverage, as returned by get_coverage(), in 0..100: the average of
|
||||
/// every instance's coverage, weighted by its option.weight (IEEE 1800-2023
|
||||
/// 19.11.3, type_option.merge_instances false). typeWeight is
|
||||
/// type_option.weight, which decides the result when no instance contributes;
|
||||
/// like option.weight, it is checked as it is loaded.
|
||||
double coverage(IData typeWeight, VlFileLineDebug fileline);
|
||||
|
||||
// ---- introspection ----
|
||||
// Test and debug only; generated code never calls these, and SV reaches them
|
||||
@@ -540,7 +702,8 @@ public:
|
||||
uint32_t createdInstanceCount() const { return m_createdInsts; }
|
||||
// Instances that have died and contributed to the residue.
|
||||
uint32_t retiredInstanceCount() const { return static_cast<uint32_t>(m_retired.count); }
|
||||
// Mean coverage over the retired instances only, in 0..100; -1.0 if none.
|
||||
// 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;
|
||||
};
|
||||
|
||||
@@ -558,6 +721,7 @@ class VlCovRegistry final : public VerilatedVirtualBase {
|
||||
|
||||
// PRIVATE METHODS
|
||||
VlCovergroupType* findType(const char* typeName) const; // nullptr if unknown
|
||||
VlCovergroupType* findOrCreateType(const char* typeName);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -570,6 +734,9 @@ public:
|
||||
// generated covergroup class name, already --protect-ids obfuscated, and is
|
||||
// the same string that keys the coverage database's hier/page.
|
||||
VlCovergroupInst* newCovergroupInst(const char* typeName);
|
||||
/// Type coverage of a covergroup type (get_coverage()); see
|
||||
/// VlCovergroupType::coverage(). typeWeight is its type_option.weight.
|
||||
double typeCoverage(const char* typeName, IData typeWeight, VlFileLineDebug fileline);
|
||||
|
||||
// ---- introspection (see VlCovergroupType) ----
|
||||
// typeName is the obfuscated generated name, so a test using these under
|
||||
@@ -595,18 +762,21 @@ public:
|
||||
class VlCovInstHandle final {
|
||||
// MEMBERS
|
||||
VlCovergroupInst* m_p = nullptr; // Attach-counted; the registry owns the node
|
||||
const IData* m_weightp = nullptr; // Owning object's option.weight, if lent to m_p
|
||||
|
||||
// PRIVATE METHODS
|
||||
// Drop one attach count, retiring the node if that was the last handle.
|
||||
// Nothing may touch instp afterwards: retire() may have freed it.
|
||||
static void release(VlCovergroupInst* instp) {
|
||||
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
|
||||
@@ -615,12 +785,18 @@ public:
|
||||
// Deleted, not implemented: nothing generates an assignment, and the
|
||||
// implicit one would copy m_p raw -- no attachInc, no release.
|
||||
VlCovInstHandle& operator=(const VlCovInstHandle&) = delete;
|
||||
~VlCovInstHandle() { release(m_p); }
|
||||
~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; }
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
int m_bitMsb = 0;
|
||||
int m_elemWidth = 0;
|
||||
int m_bitLsb = 0; // Bit's LSB
|
||||
int m_bitMsb = 0; // Bit's MSB
|
||||
int m_elemWidth = 0; // Element width
|
||||
};
|
||||
|
||||
std::vector<Entry> m_entries; // Sorted by msb, non-overlapping
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
// This specialization works for both VlQueue<CData> (and similar) as well
|
||||
// as VlQueue<VlWide<N>>.
|
||||
// These overloads work for both VlQueue<CData> (and similar) as well
|
||||
// as VlQueue<VlWide<N>>, including queues with different element types.
|
||||
template <typename T>
|
||||
inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/, int srcElementBits,
|
||||
int dstElementBits) {
|
||||
@@ -2971,16 +2971,32 @@ inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/, int
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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) {
|
||||
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> srcCopy = from;
|
||||
VlQueue<T_Src> srcCopy = from;
|
||||
|
||||
// Initialize all elements to zero using appropriate method
|
||||
q.renew(dstSize);
|
||||
|
||||
@@ -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;
|
||||
mutable VerilatedMutex m_mutex; // Mutex protecting traces
|
||||
// Map from thread id to &t_trace of given thread
|
||||
std::map<uint32_t, ExecutionTrace*> m_traceps VL_GUARDED_BY(m_mutex);
|
||||
|
||||
|
||||
@@ -808,7 +808,7 @@ bool VlRandomizer::next_check_only(VlRNGReseeds& rngr) { return nextRandomize(rn
|
||||
bool VlRandomizer::next(VlRNGReseeds& rngr) { return nextRandomize(rngr, false); }
|
||||
|
||||
bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) {
|
||||
if (!checkOnly && m_vars.empty() && m_unique_arrays.empty()) return true;
|
||||
if (!checkOnly && m_vars.empty() && m_uniqueArrays.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,21 +843,27 @@ bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) {
|
||||
|
||||
std::vector<std::string> VlRandomizer::buildUniqueExprs() const {
|
||||
std::vector<std::string> exprs;
|
||||
if (m_unique_arrays.empty()) return exprs;
|
||||
if (m_uniqueArrays.empty()) return exprs;
|
||||
const auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
||||
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;
|
||||
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;
|
||||
std::ostringstream os;
|
||||
os << "(__Vbv (distinct ";
|
||||
var.emitGetValue(os);
|
||||
os << "))";
|
||||
os << "(__Vbv (distinct " << varsString.str() << "))";
|
||||
exprs.push_back(os.str());
|
||||
}
|
||||
return exprs;
|
||||
@@ -1606,7 +1612,7 @@ void VlRandomizer::clearConstraints() {
|
||||
m_constraints_line.clear();
|
||||
m_solveBefore.clear();
|
||||
m_softConstraints.clear();
|
||||
m_unique_arrays.clear(); // Re-registered by constraint setup
|
||||
m_uniqueArrays.clear(); // Re-registered by constraint setup
|
||||
// Keep m_vars for class member randomization
|
||||
}
|
||||
|
||||
|
||||
+200
-16
@@ -264,7 +264,8 @@ class VlRandomizer VL_NOT_FINAL {
|
||||
std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back)
|
||||
// variables
|
||||
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
|
||||
std::vector<std::string> m_unique_arrays; // Arrays whose elements must be distinct
|
||||
// Arrays whose elements must be distinct grouped by unique constraint
|
||||
std::map<uint32_t, std::unordered_set<std::string>> m_uniqueArrays;
|
||||
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
|
||||
@@ -491,7 +492,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);
|
||||
modifyMembers(var, var.memberIndices(), name, randmodeIdx);
|
||||
}
|
||||
|
||||
// Register queue of non-struct types
|
||||
@@ -516,7 +517,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()) {
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
record_struct_arr(var, name, dimension, {}, {}, randmodeIdx);
|
||||
}
|
||||
// Register unpacked array of non-struct types
|
||||
template <typename T, std::size_t N_Depth>
|
||||
@@ -542,7 +543,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()) {
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
record_struct_arr(var, name, dimension, {}, {}, randmodeIdx);
|
||||
}
|
||||
|
||||
// Register associative array of non-struct types
|
||||
@@ -570,7 +571,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()) {
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
record_struct_arr(var, name, dimension, {}, {}, randmodeIdx);
|
||||
}
|
||||
|
||||
// --- Record Arrays: flat and struct ---
|
||||
@@ -585,10 +586,23 @@ 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) { m_unique_arrays.push_back(name); }
|
||||
void rand_unique(const std::string& name, const uint32_t key) {
|
||||
m_uniqueArrays[key].insert(name);
|
||||
}
|
||||
|
||||
// Recursively record all elements in an unpacked array
|
||||
template <typename T, std::size_t N_Depth>
|
||||
@@ -606,6 +620,15 @@ 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,
|
||||
@@ -621,6 +644,15 @@ 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,
|
||||
@@ -651,11 +683,27 @@ 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::vector<IData> indices, std::vector<size_t> idxWidths,
|
||||
std::uint32_t randmodeIdx) {
|
||||
std::ostringstream oss;
|
||||
for (size_t i = 0; i < indices.size(); ++i) {
|
||||
oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0')
|
||||
@@ -663,43 +711,75 @@ public:
|
||||
if (i < indices.size() - 1) oss << ".";
|
||||
}
|
||||
write_var(var, 1ULL,
|
||||
oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL);
|
||||
oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL,
|
||||
randmodeIdx);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
update_struct_arr(T& var, const std::string& name) {
|
||||
update_var(var, name.c_str());
|
||||
}
|
||||
|
||||
// Recursively process VlUnpacked of structs
|
||||
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::vector<IData> indices, std::vector<size_t> idxWidths,
|
||||
std::uint32_t randmodeIdx) {
|
||||
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);
|
||||
record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths,
|
||||
randmodeIdx);
|
||||
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::vector<IData> indices, std::vector<size_t> idxWidths,
|
||||
std::uint32_t randmodeIdx) {
|
||||
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);
|
||||
record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths,
|
||||
randmodeIdx);
|
||||
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) {
|
||||
const std::vector<size_t>& idxWidths, std::uint32_t randmodeIdx) {
|
||||
if ((dimension > 0) && (!var.empty())) {
|
||||
for (auto it = var.begin(); it != var.end(); ++it) {
|
||||
const T_Key& key = it->first;
|
||||
@@ -717,23 +797,127 @@ 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);
|
||||
idxWidths, randmodeIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
void modifyMembers(T& obj, std::index_sequence<I...>, const std::string& baseName,
|
||||
std::uint32_t randmodeIdx) {
|
||||
// Use the indices to access each member via std::get
|
||||
(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]),
|
||||
(baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I],
|
||||
randmodeIdx),
|
||||
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] == '\\') {
|
||||
|
||||
+56
-46
@@ -99,46 +99,6 @@ 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 {
|
||||
@@ -243,6 +203,56 @@ 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;
|
||||
@@ -253,7 +263,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;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
int at_least;
|
||||
@@ -266,7 +276,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_covergroup_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
int at_least;
|
||||
@@ -275,7 +285,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_coverpoint_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
int at_least;
|
||||
@@ -284,7 +294,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_cross_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
bit strobe;
|
||||
@@ -294,14 +304,14 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
||||
} vl_covergroup_type_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
real real_interval;
|
||||
} vl_coverpoint_type_options_t;
|
||||
|
||||
typedef struct {
|
||||
int weight;
|
||||
int weight = 1;
|
||||
int goal;
|
||||
string comment;
|
||||
} vl_cross_type_options_t;
|
||||
|
||||
@@ -45,11 +45,11 @@ lint_off -rule WIDTHTRUNC `VLT_UVM_FILES
|
||||
// Context-sensitive waivers
|
||||
lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case ({is_R, is_W})*"
|
||||
lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case(orig_severity)*"
|
||||
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_callback*"
|
||||
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_component*"
|
||||
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_event*"
|
||||
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_report_object*"
|
||||
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_sequence_item*"
|
||||
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_callback*"
|
||||
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_component*"
|
||||
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_event*"
|
||||
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_report_object*"
|
||||
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_sequence_item*"
|
||||
lint_off -rule IGNOREDRETURN `VLT_UVM_FILES -match "* uvm_core_state_callback::add*"
|
||||
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (abstractions[i])*"
|
||||
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (lock_list[i])*"
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
|
||||
// See also V3Ast::VNumRange
|
||||
class VerilatedRange final {
|
||||
int m_left = 0;
|
||||
int m_right = 0;
|
||||
int m_left = 0; // Left side of range (pre-':')
|
||||
int m_right = 0; // Right side of range (post-':')
|
||||
|
||||
protected:
|
||||
friend class VerilatedVarProps;
|
||||
@@ -158,7 +158,18 @@ public:
|
||||
VerilatedVarFlags vldir() const {
|
||||
return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR);
|
||||
}
|
||||
uint32_t entSize() const VL_MT_SAFE;
|
||||
uint32_t entSize() const VL_MT_SAFE {
|
||||
if (m_entSize) return m_entSize;
|
||||
switch (vltype()) {
|
||||
case VLVT_PTR: return sizeof(void*);
|
||||
case VLVT_UINT8: return sizeof(CData);
|
||||
case VLVT_UINT16: return sizeof(SData);
|
||||
case VLVT_UINT32: return sizeof(IData);
|
||||
case VLVT_UINT64: return sizeof(QData);
|
||||
case VLVT_WDATA: return VL_WORDS_I(entBits()) * sizeof(IData);
|
||||
default: return 0; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
uint32_t entBits() const VL_MT_SAFE {
|
||||
uint32_t bits = 1;
|
||||
for (auto it : m_packed) bits *= it.elements();
|
||||
@@ -214,7 +225,11 @@ public:
|
||||
: 0;
|
||||
}
|
||||
// Total size in bytes (note DPI limited to 4GB)
|
||||
size_t totalSize() const;
|
||||
size_t totalSize() const {
|
||||
size_t size = entSize();
|
||||
for (int udim = 0; udim < udims(); ++udim) size *= m_unpacked[udim].elements();
|
||||
return size;
|
||||
}
|
||||
// Adjust a data pointer to access a given array element, NULL if something goes bad
|
||||
void* datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE;
|
||||
};
|
||||
@@ -268,7 +283,7 @@ class VerilatedVar final : public VerilatedVarProps {
|
||||
m_forceControlSignals; // Force control signals
|
||||
|
||||
protected:
|
||||
const bool m_isParam;
|
||||
const bool m_isParam; // From a parameter
|
||||
friend class VerilatedScope;
|
||||
// CONSTRUCTORS
|
||||
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
||||
|
||||
@@ -140,7 +140,7 @@ class VlWorkerThread final {
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
mutable VerilatedMutex m_mutex;
|
||||
mutable VerilatedMutex m_mutex; // Protection 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,8 +296,11 @@ void VlProcess::forkSyncOnKillClear(VlForkSyncState* forkSyncp) {
|
||||
}
|
||||
|
||||
void VlProcess::state(int s) {
|
||||
if (s == KILLED && m_state != KILLED && m_state != FINISHED && m_forkSyncOnKillp
|
||||
&& !m_forkSyncOnKillDone) {
|
||||
if (completed()) {
|
||||
// We can't change the sttaus of completed process
|
||||
return;
|
||||
}
|
||||
if (s == KILLED && m_forkSyncOnKillp && !m_forkSyncOnKillDone) {
|
||||
m_forkSyncOnKillDone = true;
|
||||
m_state = s;
|
||||
m_forkSyncOnKillp->done();
|
||||
|
||||
@@ -64,36 +64,6 @@
|
||||
// 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
|
||||
@@ -431,7 +401,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;
|
||||
bool m_inited = false; // Initialization complete
|
||||
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;
|
||||
const dumpCb_t m_dumpCb;
|
||||
const cleanupCb_t m_cleanupCb;
|
||||
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 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,6 +32,7 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
@@ -1084,6 +1085,15 @@ 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;
|
||||
|
||||
+163
-135
@@ -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* varDatap() const { return m_varp->datap(); }
|
||||
CData* varCDatap() const {
|
||||
virtual void* readDatap() const { return m_varp->datap(); }
|
||||
CData* readCDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8););
|
||||
return reinterpret_cast<CData*>(varDatap());
|
||||
return static_cast<CData*>(readDatap());
|
||||
}
|
||||
SData* varSDatap() const {
|
||||
SData* readSDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16););
|
||||
return reinterpret_cast<SData*>(varDatap());
|
||||
return static_cast<SData*>(readDatap());
|
||||
}
|
||||
IData* varIDatap() const {
|
||||
IData* readIDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32););
|
||||
return reinterpret_cast<IData*>(varDatap());
|
||||
return static_cast<IData*>(readDatap());
|
||||
}
|
||||
QData* varQDatap() const {
|
||||
QData* readQDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64););
|
||||
return reinterpret_cast<QData*>(varDatap());
|
||||
return static_cast<QData*>(readDatap());
|
||||
}
|
||||
EData* varEDatap() const {
|
||||
EData* readEDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA););
|
||||
return reinterpret_cast<EData*>(varDatap());
|
||||
return static_cast<EData*>(readDatap());
|
||||
}
|
||||
double* varRealDatap() const {
|
||||
double* readRealDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL););
|
||||
return reinterpret_cast<double*>(varDatap());
|
||||
return static_cast<double*>(readDatap());
|
||||
}
|
||||
std::string* varStringDatap() const {
|
||||
std::string* readStringDatap() const {
|
||||
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING););
|
||||
return reinterpret_cast<std::string*>(varDatap());
|
||||
return static_cast<std::string*>(readDatap());
|
||||
}
|
||||
virtual uint32_t bitOffset() const { return 0; }
|
||||
};
|
||||
@@ -412,6 +412,9 @@ 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) {
|
||||
@@ -517,7 +520,7 @@ public:
|
||||
const std::string localName = _vl_vpi_member_local_name(memberVarp->name());
|
||||
|
||||
return new VerilatedVpioVar{memberVarp, scopep(),
|
||||
static_cast<uint8_t*>(varDatap()) + offset, localName,
|
||||
static_cast<uint8_t*>(readDatap()) + offset, localName,
|
||||
std::string{fullname()} + memberName.substr(parentLen)};
|
||||
}
|
||||
uint32_t type() const override {
|
||||
@@ -542,7 +545,7 @@ public:
|
||||
return m_fullname.c_str();
|
||||
}
|
||||
uint8_t* prevDatap() const { return m_prevDatap; }
|
||||
void* varDatap() const override { return m_varDatap; }
|
||||
void* readDatap() const override { return m_varDatap; }
|
||||
void createPrevDatap() {
|
||||
if (VL_UNLIKELY(!m_prevDatap)) {
|
||||
m_prevDatap = new uint8_t[entSize()];
|
||||
@@ -1094,22 +1097,13 @@ public:
|
||||
case vpiOctStrVal: // FALLTHRU
|
||||
case vpiDecStrVal: // FALLTHRU
|
||||
case vpiHexStrVal: // FALLTHRU
|
||||
case vpiStringVal: {
|
||||
if (VL_UNLIKELY(!valuep->value.str)) return false;
|
||||
break;
|
||||
}
|
||||
case vpiStringVal: // FALLTHRU
|
||||
case vpiScalarVal: // FALLTHRU
|
||||
case vpiIntVal: // FALLTHRU
|
||||
case vpiRealVal: break;
|
||||
case vpiVectorVal: {
|
||||
if (VL_UNLIKELY(!valuep->value.vector)) return false;
|
||||
break;
|
||||
case vpiRealVal: // FALLTHRU
|
||||
case vpiVectorVal: return true;
|
||||
default: return false;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1291,8 +1285,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->varDatap())),
|
||||
*(varop->prevDatap()), varop->varDatap(), varop->prevDatap(),
|
||||
varop->fullname(), *(static_cast<CData*>(varop->readDatap())),
|
||||
*(varop->prevDatap()), varop->readDatap(), varop->prevDatap(),
|
||||
varop->entSize()););
|
||||
if (varop->bitSize() == 1) {
|
||||
T* const prevDatap = reinterpret_cast<T*>(
|
||||
@@ -1303,7 +1297,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->varDatap(), varop->entSize()) != 0;
|
||||
return std::memcmp(varop->prevDatap(), varop->readDatap(), varop->entSize()) != 0;
|
||||
}
|
||||
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) {
|
||||
switch (varop->varp()->vltype()) {
|
||||
@@ -1332,9 +1326,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->varDatap(), varop->entSize()) == 0);
|
||||
assert(std::memcmp(varop->prevDatap(), varop->readDatap(), varop->entSize()) == 0);
|
||||
} else {
|
||||
std::memcpy(varop->prevDatap(), varop->varDatap(), varop->entSize());
|
||||
std::memcpy(varop->prevDatap(), varop->readDatap(), varop->entSize());
|
||||
}
|
||||
}
|
||||
static void updatePrev(const VerilatedVpioVar* const varop) {
|
||||
@@ -1375,7 +1369,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->varDatap()))););
|
||||
*(static_cast<CData*>(varop->readDatap()))););
|
||||
update.insert(varop);
|
||||
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
||||
(ho.cb_rtnp())(ho.cb_datap());
|
||||
@@ -1726,9 +1720,9 @@ VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseSignal
|
||||
// assert(forceEnableSignalVop->entSize() == baseSignalVop->entSize());
|
||||
assert(forceValueSignalVop->entSize() == baseSignalVop->entSize());
|
||||
assert(forceReadSignalVop->entSize() == baseSignalVop->entSize());
|
||||
assert(forceEnableSignalVop->varDatap() == forceEnableSignalVarp->datap());
|
||||
assert(forceValueSignalVop->varDatap() == forceValueSignalVarp->datap());
|
||||
assert(forceReadSignalVop->varDatap() == forceReadSignalVarp->datap());
|
||||
assert(forceEnableSignalVop->readDatap() == forceEnableSignalVarp->datap());
|
||||
assert(forceValueSignalVop->readDatap() == forceValueSignalVarp->datap());
|
||||
assert(forceReadSignalVop->readDatap() == forceReadSignalVarp->datap());
|
||||
#endif // VL_DEBUG
|
||||
|
||||
return VerilatedVpiImp::ForceControlSignalVops{
|
||||
@@ -1758,9 +1752,9 @@ double VerilatedVpiImp::getReadDataWord(const VerilatedVpioVar* baseSignalVop,
|
||||
const VerilatedVpioVar* forceEnableSignalVop,
|
||||
const VerilatedVpioVar* forceValueSignalVop,
|
||||
size_t /*bitCount*/, size_t /*bitOffset*/) {
|
||||
const double baseSignalData = *baseSignalVop->varRealDatap();
|
||||
const bool forceEnableData = *forceEnableSignalVop->varCDatap();
|
||||
const double forceValueData = *forceValueSignalVop->varRealDatap();
|
||||
const double baseSignalData = *baseSignalVop->readRealDatap();
|
||||
const bool forceEnableData = *forceEnableSignalVop->readCDatap();
|
||||
const double forceValueData = *forceValueSignalVop->readRealDatap();
|
||||
const double readData = forceEnableData ? forceValueData : baseSignalData;
|
||||
return readData;
|
||||
}
|
||||
@@ -2805,7 +2799,7 @@ _vl_vpi_handle_indexed_member_from_scope(const VerilatedScope* const scopep,
|
||||
VerilatedVpioVar* baseVop
|
||||
= fullnameOverride.empty()
|
||||
? new VerilatedVpioVar{baseVarp, varScopep}
|
||||
: new VerilatedVpioVar{baseVarp, varScopep, baseVarp->datap(),
|
||||
: new VerilatedVpioVar{baseVarp, varScopep,
|
||||
_vl_vpi_member_local_name(baseVarp->name()),
|
||||
fullnameOverride};
|
||||
VerilatedVpioVar* vop = _vl_vpi_handle_apply_indices(baseVop, indices);
|
||||
@@ -2977,10 +2971,9 @@ 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, varp->datap(),
|
||||
_vl_vpi_member_local_name(varp->name()), fullnameOverride})
|
||||
->castVpiHandle();
|
||||
resultHandle = (new VerilatedVpioVar{varp, scopep, _vl_vpi_member_local_name(varp->name()),
|
||||
fullnameOverride})
|
||||
->castVpiHandle();
|
||||
} else {
|
||||
resultHandle = (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
|
||||
}
|
||||
@@ -3377,6 +3370,18 @@ 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) {
|
||||
@@ -3436,7 +3441,7 @@ VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t
|
||||
bitCount, varBits - addOffset});
|
||||
|
||||
VarAccessInfo<T> info;
|
||||
info.m_datap = reinterpret_cast<T*>(vop->varDatap());
|
||||
info.m_datap = static_cast<T*>(vop->readDatap());
|
||||
if (vop->varp()->vltype() == VLVT_WDATA) {
|
||||
assert(sizeof(T) == sizeof(EData));
|
||||
assert(bitCount <= wordBits);
|
||||
@@ -3484,8 +3489,7 @@ 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) {
|
||||
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
||||
return vl_vpi_get_word_gen(info);
|
||||
return vl_vpi_get_word_gen(vl_vpi_var_access_info<T>(vop, bitCount, addOffset));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -3503,8 +3507,7 @@ 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) {
|
||||
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
||||
vl_vpi_put_word_gen(info, word);
|
||||
vl_vpi_put_word_gen(vl_vpi_var_access_info<T>(vop, bitCount, addOffset), word);
|
||||
}
|
||||
|
||||
// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds
|
||||
@@ -3541,7 +3544,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->varDatap();
|
||||
void* const varDatap = vop->readDatap();
|
||||
|
||||
if (!vl_check_format(vop, valuep, true)) return;
|
||||
// string data type is dynamic and may vary in size during simulation
|
||||
@@ -3582,7 +3585,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 = reinterpret_cast<CData*>(varDatap);
|
||||
const CData* datap = static_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;
|
||||
@@ -3627,10 +3630,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 = reinterpret_cast<char*>(varDatap);
|
||||
valuep->value.str = static_cast<char*>(varDatap);
|
||||
return;
|
||||
}
|
||||
t_outDynamicStr = *vop->varStringDatap();
|
||||
t_outDynamicStr = *vop->readStringDatap();
|
||||
valuep->value.str = const_cast<char*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
} else {
|
||||
@@ -3648,7 +3651,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->varRealDatap());
|
||||
valuep->value.real = *(vop->readRealDatap());
|
||||
return;
|
||||
} else if (valuep->format == vpiScalarVal) {
|
||||
valuep->value.scalar = vl_vpi_get_word(vop, 32, 0) ? vpi1 : vpi0;
|
||||
@@ -3739,7 +3742,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->varDatap()););
|
||||
baseSignalVop->varp()->datap(), baseSignalVop->readDatap()););
|
||||
|
||||
if (VL_UNLIKELY(!baseSignalVop->varp()->isPublicRW())) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__,
|
||||
@@ -3758,6 +3761,16 @@ 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_(
|
||||
@@ -3817,7 +3830,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->varRealDatap() = readData;
|
||||
*forceReadSignalVop->readRealDatap() = readData;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3922,7 +3935,6 @@ 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)
|
||||
@@ -3939,7 +3951,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 = reinterpret_cast<CData*>(valueVop->varDatap());
|
||||
CData* const datap = static_cast<CData*>(valueVop->readDatap());
|
||||
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;
|
||||
@@ -4021,7 +4033,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->varStringDatap()) = valuep->value.str;
|
||||
*(baseSignalVop->readStringDatap()) = valuep->value.str;
|
||||
return object;
|
||||
}
|
||||
const int chars = VL_BYTES_I(varBits);
|
||||
@@ -4038,7 +4050,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->varRealDatap()) = valuep->value.real;
|
||||
*(valueVop->readRealDatap()) = valuep->value.real;
|
||||
if (baseSignalVop->varp()->isForceable()) updateVforceRd();
|
||||
return object;
|
||||
}
|
||||
@@ -4083,7 +4095,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:; // LCOV_EXCL_LINE
|
||||
default:;
|
||||
}
|
||||
break;
|
||||
case vpiRawTwoStateVal:
|
||||
@@ -4101,7 +4113,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:; // LCOV_EXCL_LINE
|
||||
default:;
|
||||
}
|
||||
break;
|
||||
case vpiLongIntVal:
|
||||
@@ -4110,7 +4122,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:; // LCOV_EXCL_LINE
|
||||
default:;
|
||||
}
|
||||
break;
|
||||
default:;
|
||||
@@ -4309,10 +4321,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->varCDatap(), shortintsp);
|
||||
vop->readCDatap(), shortintsp);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->varSDatap(), shortintsp);
|
||||
vop->readSDatap(), shortintsp);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4324,13 +4336,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->varCDatap(), integersp);
|
||||
vop->readCDatap(), integersp);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->varSDatap(), integersp);
|
||||
vop->readSDatap(), integersp);
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->varIDatap(), integersp);
|
||||
vop->readIDatap(), integersp);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4342,16 +4354,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->varCDatap(), longintsp);
|
||||
vop->readCDatap(), longintsp);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->varSDatap(), longintsp);
|
||||
vop->readSDatap(), longintsp);
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->varIDatap(), longintsp);
|
||||
vop->readIDatap(), longintsp);
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->varQDatap(), longintsp);
|
||||
vop->readQDatap(), longintsp);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4363,19 +4375,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->varCDatap(), vectorsp);
|
||||
vop->readCDatap(), vectorsp);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->varSDatap(), vectorsp);
|
||||
vop->readSDatap(), vectorsp);
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->varIDatap(), vectorsp);
|
||||
vop->readIDatap(), vectorsp);
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->varQDatap(), vectorsp);
|
||||
vop->readQDatap(), vectorsp);
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
|
||||
vop->varEDatap(), vectorsp);
|
||||
vop->readEDatap(), vectorsp);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4387,19 +4399,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->varCDatap(), valuep);
|
||||
vop->readCDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vop->varSDatap(), valuep);
|
||||
vop->readSDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vop->varIDatap(), valuep);
|
||||
vop->readIDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vop->varQDatap(), valuep);
|
||||
vop->readQDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vop->varEDatap(), valuep);
|
||||
vop->readEDatap(), valuep);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4411,19 +4423,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->varCDatap(), valuep);
|
||||
vop->readCDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
||||
vop->varSDatap(), valuep);
|
||||
vop->readSDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
||||
vop->varIDatap(), valuep);
|
||||
vop->readIDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
||||
vop->varQDatap(), valuep);
|
||||
vop->readQDatap(), valuep);
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
|
||||
vop->varEDatap(), valuep);
|
||||
vop->readEDatap(), valuep);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -4486,17 +4498,8 @@ 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
|
||||
@@ -4508,114 +4511,129 @@ 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->varCDatap());
|
||||
vop->readCDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
|
||||
vop->varSDatap());
|
||||
vop->readSDatap());
|
||||
return;
|
||||
}
|
||||
|
||||
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->varCDatap());
|
||||
vop->readCDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
|
||||
vop->varSDatap());
|
||||
vop->readSDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
|
||||
vop->varIDatap());
|
||||
vop->readIDatap());
|
||||
return;
|
||||
}
|
||||
|
||||
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->varCDatap());
|
||||
vop->readCDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
|
||||
vop->varSDatap());
|
||||
vop->readSDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
|
||||
vop->varIDatap());
|
||||
vop->readIDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
|
||||
vop->varQDatap());
|
||||
vop->readQDatap());
|
||||
return;
|
||||
}
|
||||
|
||||
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->varCDatap());
|
||||
vectorsp, vop->readCDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vectorsp, vop->varSDatap());
|
||||
vectorsp, vop->readSDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vectorsp, vop->varIDatap());
|
||||
vectorsp, vop->readIDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vectorsp, vop->varQDatap());
|
||||
vectorsp, vop->readQDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
|
||||
vectorsp, vop->varEDatap());
|
||||
vectorsp, vop->readEDatap());
|
||||
return;
|
||||
}
|
||||
|
||||
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->varCDatap());
|
||||
vop->readCDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
||||
vop->varSDatap());
|
||||
vop->readSDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
||||
vop->varIDatap());
|
||||
vop->readIDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
||||
vop->varQDatap());
|
||||
vop->readQDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
|
||||
vop->varEDatap());
|
||||
vop->readEDatap());
|
||||
return;
|
||||
}
|
||||
|
||||
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->varCDatap());
|
||||
vop->readCDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT16) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
||||
vop->varSDatap());
|
||||
vop->readSDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT32) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
||||
vop->varIDatap());
|
||||
vop->readIDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_UINT64) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
||||
vop->varQDatap());
|
||||
vop->readQDatap());
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
|
||||
vop->varEDatap());
|
||||
vop->readEDatap());
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Reached only if vl_check_array_format and this dispatch drift apart
|
||||
// LCOV_EXCL_START
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for '%s'",
|
||||
__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,
|
||||
@@ -4670,6 +4688,16 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -322,8 +322,10 @@
|
||||
// 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
|
||||
|
||||
@@ -541,10 +543,12 @@ 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)))
|
||||
@@ -746,7 +750,7 @@ namespace vlstd {
|
||||
|
||||
template <typename T>
|
||||
struct reverse_wrapper final {
|
||||
const T& m_v;
|
||||
const T& m_v; // Pre-wrapped iterator
|
||||
|
||||
explicit reverse_wrapper(const T& a_v)
|
||||
: m_v(a_v) {} // Need () constructor
|
||||
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# DESCRIPTION: Verilator: gcov wrapper for fastcov, bounding gcov memory usage
|
||||
#
|
||||
# Usage: GCOV=<gcov> nodist/gcov_batch [gcov options] <files>
|
||||
#
|
||||
# fastcov passes all coverage files of a worker to a single gcov process, but
|
||||
# gcov does not free memory between input files, which can exhaust memory with
|
||||
# many files. This wrapper runs the gcov in $GCOV on batches of at most 16
|
||||
# files at a time instead. The output is the same as if all files were passed
|
||||
# to a single gcov.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
gcov="${GCOV:-gcov}"
|
||||
batch=16
|
||||
|
||||
opts=()
|
||||
files=()
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-*) opts+=("$arg") ;;
|
||||
*) files+=("$arg") ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# No files, e.g.: 'gcov -v'
|
||||
if [ ${#files[@]} -eq 0 ]; then
|
||||
exec "$gcov" "${opts[@]}"
|
||||
fi
|
||||
|
||||
status=0
|
||||
for ((i = 0; i < ${#files[@]}; i += batch)); do
|
||||
"$gcov" "${opts[@]}" "${files[@]:i:batch}" || status=$?
|
||||
done
|
||||
exit $status
|
||||
+12
-1
@@ -68,6 +68,7 @@ set(HEADERS
|
||||
V3Combine.h
|
||||
V3Common.h
|
||||
V3Const.h
|
||||
V3ConstPool.h
|
||||
V3Container.h
|
||||
V3Control.h
|
||||
V3Coverage.h
|
||||
@@ -237,6 +238,7 @@ set(COMMON_SOURCES
|
||||
V3Clock.cpp
|
||||
V3Combine.cpp
|
||||
V3Common.cpp
|
||||
V3ConstPool.cpp
|
||||
V3Control.cpp
|
||||
V3Const__gen.cpp
|
||||
V3Coverage.cpp
|
||||
@@ -565,9 +567,18 @@ 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}
|
||||
|
||||
+4
-19
@@ -35,25 +35,10 @@ endif
|
||||
|
||||
UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
|
||||
|
||||
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
|
||||
# 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 --)
|
||||
|
||||
#*********************************************************************
|
||||
|
||||
|
||||
+6
-5
@@ -187,7 +187,6 @@ maintainer-copy::
|
||||
#### Top executable
|
||||
|
||||
RAW_OBJS = \
|
||||
V3Const__gen.o \
|
||||
V3Error.o \
|
||||
V3FileLine.o \
|
||||
V3Graph.o \
|
||||
@@ -213,6 +212,7 @@ RAW_OBJS_PCH_ASTMT = \
|
||||
V3Ast.o \
|
||||
V3AstNodes.o \
|
||||
V3Broken.o \
|
||||
V3ConstPool.o \
|
||||
V3Control.o \
|
||||
V3EmitCBase.o \
|
||||
V3EmitCConstPool.o \
|
||||
@@ -253,6 +253,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3Clock.o \
|
||||
V3Combine.o \
|
||||
V3Common.o \
|
||||
V3Const__gen.o \
|
||||
V3Coverage.o \
|
||||
V3Covergroup.o \
|
||||
V3CoverageJoin.o \
|
||||
@@ -410,8 +411,6 @@ $(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
|
||||
@@ -431,7 +430,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 $(srcdir)/$(basename $(1)).cpp -o $(1)
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstMT.h${CFG_GCH_IF_CLANG} -c $$< -o $(1)
|
||||
|
||||
endef
|
||||
|
||||
@@ -439,7 +438,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 $(srcdir)/$(basename $(1)).cpp -o $(1)
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstNoMT.h${CFG_GCH_IF_CLANG} -c $$< -o $(1)
|
||||
|
||||
endef
|
||||
|
||||
@@ -489,6 +488,8 @@ header_cc: $(addsuffix __header_cc.o, $(basename $(HEADER_CC_H)))
|
||||
%__header_cc.cpp: %.h
|
||||
$(PYTHON3) $(srcdir)/../bin/verilator_includer -DVL_MT_DISABLED_CODE_UNIT=1 $^ > $@
|
||||
|
||||
.SECONDARY: V3Const__gen.cpp $(addsuffix __header_cc.cpp, $(basename $(HEADER_CC_H)))
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
######################################################################
|
||||
|
||||
+2
-2
@@ -689,8 +689,8 @@ class CovergroupEventBindVisitor final : public VNVisitor {
|
||||
public:
|
||||
CovergroupEventBindVisitor(AstSenTree* eventp, AstClass* classp, AstVarScope* instancep)
|
||||
: m_instancep{instancep} {
|
||||
classp->foreachMember([&](AstClass* const, AstVar* const varp) {
|
||||
if (!varp->isStatic()) m_memberps.emplace(varp);
|
||||
classp->foreachMember([&](AstClass* const, AstVar* const varp) { //
|
||||
m_memberps.emplace(varp);
|
||||
});
|
||||
iterate(eventp);
|
||||
}
|
||||
|
||||
+24
-18
@@ -154,6 +154,13 @@ 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,
|
||||
@@ -324,6 +331,7 @@ 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;
|
||||
@@ -334,11 +342,6 @@ 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"};
|
||||
@@ -362,8 +365,9 @@ class AssertVisitor final : public VNVisitor {
|
||||
case VAssertDirectiveType::COVER:
|
||||
case VAssertDirectiveType::ASSUME: {
|
||||
if (v3Global.opt.assertOn()) {
|
||||
return new AstCExpr{fl, AstCExpr::Pure{},
|
||||
assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
|
||||
return new AstCExpr{
|
||||
fl, AstCExpr::Pure{},
|
||||
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
|
||||
}
|
||||
return new AstConst{fl, AstConst::BitFalse{}};
|
||||
}
|
||||
@@ -390,15 +394,17 @@ 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{},
|
||||
assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1};
|
||||
return new AstCExpr{
|
||||
fl, AstCExpr::Pure{},
|
||||
V3AssertCommon::assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1};
|
||||
}
|
||||
static AstNodeExpr* assertFailOnCond(FileLine* fl, VAssertType type,
|
||||
VAssertDirectiveType directiveType) {
|
||||
if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}};
|
||||
if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}};
|
||||
return new AstCExpr{fl, AstCExpr::Pure{},
|
||||
assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 1};
|
||||
return new AstCExpr{
|
||||
fl, AstCExpr::Pure{},
|
||||
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 1};
|
||||
}
|
||||
string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message,
|
||||
VDisplayType severity) {
|
||||
@@ -749,11 +755,11 @@ class AssertVisitor final : public VNVisitor {
|
||||
// reaches zero.
|
||||
matchCountp->unlinkFrBack();
|
||||
AstVar* const remainingp = new AstVar{
|
||||
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});
|
||||
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};
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(
|
||||
new AstLoopTest{flp, loopp, new AstVarRef{flp, remainingp, VAccess::READ}});
|
||||
@@ -763,8 +769,8 @@ class AssertVisitor final : public VNVisitor {
|
||||
new AstSub{flp, new AstVarRef{flp, remainingp, VAccess::READ},
|
||||
new AstConst{flp, AstConst::WidthedValue{},
|
||||
remainingp->dtypep()->width(), 1}}});
|
||||
replayp->addStmtsp(loopp);
|
||||
passsp = replayp;
|
||||
replaysp->addNext(loopp);
|
||||
passsp = replaysp;
|
||||
}
|
||||
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
|
||||
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
|
||||
|
||||
@@ -28,6 +28,8 @@ 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 {
|
||||
|
||||
+74
-98
@@ -243,12 +243,6 @@ static AstNodeExpr* sampled(AstNodeExpr* exprp) {
|
||||
return new AstSampled{exprp->fileline(), exprp, exprp->dtypep(), true};
|
||||
}
|
||||
|
||||
static string assertCtlGetCall(const char* query, VAssertType type,
|
||||
VAssertDirectiveType directiveType) {
|
||||
return "vlSymsp->_vm_contextp__->assertCtlGet(VerilatedAssertCtlQuery::"s + query + ", "s
|
||||
+ std::to_string(type) + ", "s + std::to_string(directiveType) + ")"s;
|
||||
}
|
||||
|
||||
static const char* assertPassOnQuery(bool vacuous) {
|
||||
static constexpr const char* queries[2]
|
||||
= {"ASSERT_CTL_PASS_ON_NONVACUOUS", "ASSERT_CTL_PASS_ON_VACUOUS"};
|
||||
@@ -259,13 +253,14 @@ static AstNodeExpr* assertOnCond(FileLine* flp, VAssertType type,
|
||||
VAssertDirectiveType directiveType) {
|
||||
if (!v3Global.opt.assertOn()) { return new AstConst{flp, AstConst::BitFalse{}}; }
|
||||
return new AstCExpr{flp, AstCExpr::Pure{},
|
||||
assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
|
||||
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
|
||||
}
|
||||
|
||||
static AstNodeExpr* assertKillGet(FileLine* flp, VAssertType type,
|
||||
VAssertDirectiveType directiveType) {
|
||||
return new AstCExpr{flp, AstCExpr::Pure{},
|
||||
assertCtlGetCall("ASSERT_CTL_KILL", type, directiveType), 32};
|
||||
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_KILL", type, directiveType),
|
||||
32};
|
||||
}
|
||||
|
||||
static string assertActionControlPrefix(VAssertDirectiveType directiveType) {
|
||||
@@ -280,19 +275,21 @@ static string assertActionControlPrefix(VAssertDirectiveType directiveType) {
|
||||
|
||||
static AstNodeExpr* assertPassOnCond(FileLine* flp, VAssertType type,
|
||||
VAssertDirectiveType directiveType, bool vacuous) {
|
||||
return new AstCExpr{flp, AstCExpr::Pure{},
|
||||
assertActionControlPrefix(directiveType)
|
||||
+ assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType)
|
||||
+ "))"s,
|
||||
1};
|
||||
return new AstCExpr{
|
||||
flp, AstCExpr::Pure{},
|
||||
assertActionControlPrefix(directiveType)
|
||||
+ V3AssertCommon::assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType)
|
||||
+ "))"s,
|
||||
1};
|
||||
}
|
||||
|
||||
static AstNodeExpr* assertFailOnCond(FileLine* flp, VAssertType type,
|
||||
VAssertDirectiveType directiveType) {
|
||||
return new AstCExpr{flp, AstCExpr::Pure{},
|
||||
assertActionControlPrefix(directiveType)
|
||||
+ assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType) + "))"s,
|
||||
1};
|
||||
return new AstCExpr{
|
||||
flp, AstCExpr::Pure{},
|
||||
assertActionControlPrefix(directiveType)
|
||||
+ V3AssertCommon::assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType) + "))"s,
|
||||
1};
|
||||
}
|
||||
|
||||
static AstIf* newPassOnIf(FileLine* flp, AstNodeExpr* firep, AstNode* bodyp, VAssertType type,
|
||||
@@ -335,6 +332,7 @@ class SvaNfaBuilder final {
|
||||
bool m_isCoverSeq = false;
|
||||
// Unsupported endpoint topology must reject, not ignore, or the wait hangs
|
||||
bool m_isSeqEvent = false;
|
||||
bool m_inSequencePrefix = false; // Prefix composition does not preserve every endpoint
|
||||
|
||||
struct RangeDelayRejectInfo final {
|
||||
SvaStateVertex* startp = nullptr;
|
||||
@@ -585,10 +583,13 @@ class SvaNfaBuilder final {
|
||||
// Build NFA for an SExpr. finalCond = RHS (not yet added as a vertex).
|
||||
// isTopLevelStep: marks outermost required boolean check as rejectOnFail.
|
||||
// Apply a range delay `##[M:N]` to currentp. Returns true on success. On
|
||||
// failure, sets outErrorEmitted per semantic-error policy and returns false.
|
||||
bool applyRangeDelay(AstDelay* delayp, AstNodeExpr* rhsExprp, SvaStateVertex*& currentp,
|
||||
std::vector<SvaStateVertex*>& midSources, FileLine* flp,
|
||||
bool& outErrorEmitted, RangeDelayRejectInfo* rangeRejectInfop = nullptr) {
|
||||
// failure, emits a diagnostic and returns false.
|
||||
bool applyRangeDelay(AstSExpr* sexprp, SvaStateVertex*& currentp,
|
||||
std::vector<SvaStateVertex*>& midSources,
|
||||
RangeDelayRejectInfo* rangeRejectInfop = nullptr) {
|
||||
FileLine* const flp = sexprp->fileline();
|
||||
AstDelay* const delayp = VN_AS(sexprp->delayp(), Delay);
|
||||
AstNodeExpr* const rhsExprp = sexprp->exprp();
|
||||
const unsigned minDelay = getConstUInt(delayp->lhsp());
|
||||
if (delayp->isUnbounded()) {
|
||||
// `##[M:$]`: wait M cycles, then retain every matured attempt in a
|
||||
@@ -613,62 +614,42 @@ class SvaNfaBuilder final {
|
||||
}
|
||||
const unsigned range = maxDelay - minDelay;
|
||||
currentp = addDelayChain(currentp, minDelay, flp);
|
||||
// kChainLimit bounds per-attempt unrolled vertices. Above this, a
|
||||
// ring buffer (constant-size state) is used instead, so the vertex
|
||||
// count is O(1) in range regardless of user input; no adversarial N
|
||||
// blowup is possible.
|
||||
constexpr unsigned kChainLimit = 256;
|
||||
// IEEE 1800-2023 16.14.3: only a small bounded range before a plain
|
||||
// boolean enumerates every end-of-match below. The large-range ring uses
|
||||
// first-match clearing and the nested-sequence merge collapses ends, so
|
||||
// reject those for a cover sequence rather than under-count.
|
||||
if (m_isCoverSeq && (range > kChainLimit || VN_IS(rhsExprp, SExpr))) {
|
||||
const bool multiCycleRhs = rhsExprp->isMultiCycleSva();
|
||||
const AstNodeExpr* const preExprp = sexprp->preExprp();
|
||||
// Prefix and nested-sequence composition can lose endpoints or their multiplicity
|
||||
// required by cover sequence (IEEE 1800-2023 16.14.3), regardless of the range width.
|
||||
if (m_isCoverSeq
|
||||
&& (multiCycleRhs || m_inSequencePrefix
|
||||
|| (preExprp && preExprp->isMultiCycleSva()))) {
|
||||
warnEndpointUnsupported(flp, "this ranged cycle delay");
|
||||
outErrorEmitted = true;
|
||||
return false;
|
||||
}
|
||||
if (range > kChainLimit) {
|
||||
currentp = addDelayChain(currentp, range + 1U, flp, false,
|
||||
rhsExprp->isMultiCycleSva() ? nullptr : rhsExprp);
|
||||
} else if (VN_IS(rhsExprp, SExpr)) {
|
||||
// Nested-SExpr RHS: merge all [M,N] positions. Candidate-local misses
|
||||
// are not assertion rejects while a later position can still match.
|
||||
if (m_inSequencePrefix) {
|
||||
flp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Bounded ranged cycle delay in a sequence prefix");
|
||||
return false;
|
||||
}
|
||||
if (multiCycleRhs) {
|
||||
// Launch a candidate at every eligible tick. Candidate-local misses are not
|
||||
// assertion rejects while a later position can still match.
|
||||
if (rangeRejectInfop) {
|
||||
const int rhsLen = fixedLength(rhsExprp);
|
||||
if (rhsLen >= 0) *rangeRejectInfop = {currentp, range, rhsLen};
|
||||
}
|
||||
SvaStateVertex* const tailp = addDelayChain(currentp, range + 1U, flp, false);
|
||||
SvaStateVertex* const mergeVtxp = scopedCreateVertex();
|
||||
mergeVtxp->m_isUnbounded = true;
|
||||
guardedLink(currentp, mergeVtxp, flp);
|
||||
for (unsigned i = 0; i < range; ++i) {
|
||||
SvaStateVertex* const nextVtxp = scopedCreateVertex();
|
||||
guardedEdge(currentp, nextVtxp, flp);
|
||||
guardedLink(nextVtxp, mergeVtxp, flp);
|
||||
currentp = nextVtxp;
|
||||
}
|
||||
guardedLink(tailp, mergeVtxp, flp);
|
||||
currentp = mergeVtxp;
|
||||
m_inUnboundedScope = true;
|
||||
} else {
|
||||
// Pure boolean RHS: register chain. Each mid-position links to
|
||||
// match (match-only); last position is the reject source.
|
||||
// For cover_sequence (IEEE 1800-2023 16.14.3) the advance edge is
|
||||
// unconditional so every (start, end) pair fires independently --
|
||||
// dropping NOT(b) turns "first-match-wins" into "every end fires".
|
||||
AstVar* const hoistVarp
|
||||
= m_isCoverSeq ? nullptr : tryHoistSampled(rhsExprp, flp, range);
|
||||
// The first eligible tick is explicit. The ring retains attempts for the
|
||||
// remaining ticks and exposes the final tick for rejection. Only properties
|
||||
// clear on success, since cover sequence counts every endpoint.
|
||||
midSources.push_back(currentp);
|
||||
for (unsigned i = 0; i < range; ++i) {
|
||||
SvaStateVertex* const nextVtxp = scopedCreateVertex();
|
||||
if (m_isCoverSeq) {
|
||||
guardedEdge(currentp, nextVtxp, flp);
|
||||
} else {
|
||||
AstNodeExpr* const notExprp
|
||||
= new AstLogNot{flp, sampledRefOrClone(hoistVarp, rhsExprp, flp)};
|
||||
guardedEdge(currentp, nextVtxp, notExprp, flp);
|
||||
}
|
||||
if (i < range - 1) midSources.push_back(nextVtxp);
|
||||
currentp = nextVtxp;
|
||||
}
|
||||
currentp = addDelayChain(currentp, range + 1U, flp, false,
|
||||
m_isCoverSeq ? nullptr : rhsExprp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -687,14 +668,11 @@ class SvaNfaBuilder final {
|
||||
= result.finalCondp ? sampled(result.finalCondp->cloneTreePure(false)) : nullptr;
|
||||
SvaStateVertex* const successNowp = scopedCreateVertex();
|
||||
guardedLink(srcp, successNowp, condp, flp);
|
||||
SvaStateVertex* stagep = successNowp;
|
||||
guardedLink(stagep, expiryMatchp, flp);
|
||||
for (unsigned i = 0; i < info.range; ++i) {
|
||||
SvaStateVertex* const nextp = scopedCreateVertex();
|
||||
guardedEdge(stagep, nextp, flp);
|
||||
stagep = nextp;
|
||||
guardedLink(stagep, expiryMatchp, flp);
|
||||
}
|
||||
// Retain a success through the latest deadline it can satisfy.
|
||||
SvaStateVertex* const historyp
|
||||
= addDelayChain(successNowp, info.range + 1U, flp, false);
|
||||
guardedLink(successNowp, expiryMatchp, flp);
|
||||
guardedLink(historyp, expiryMatchp, flp);
|
||||
}
|
||||
|
||||
SvaStateVertex* const sinkVtxp = m_graph.createStateVertex();
|
||||
@@ -715,6 +693,8 @@ class SvaNfaBuilder final {
|
||||
// Handle LHS (preExpr)
|
||||
SvaStateVertex* currentp = entryVtxp;
|
||||
if (AstNodeExpr* const preExprp = sexprp->preExprp()) {
|
||||
VL_RESTORER(m_inSequencePrefix);
|
||||
m_inSequencePrefix = true;
|
||||
const BuildResult pre = buildExpr(preExprp, currentp, isTopLevelStep);
|
||||
if (!pre.valid()) return BuildResult::fail(pre.errorEmitted); // LCOV_EXCL_LINE
|
||||
if (pre.finalCondp) {
|
||||
@@ -737,10 +717,9 @@ class SvaNfaBuilder final {
|
||||
RangeDelayRejectInfo rangeRejectInfo;
|
||||
const bool addRangeReject = isTopLevelStep && !m_inUnboundedScope;
|
||||
if (delayp->isRangeDelay()) {
|
||||
bool errorEmitted = false;
|
||||
if (!applyRangeDelay(delayp, sexprp->exprp(), currentp, rangeMidSources, flp,
|
||||
errorEmitted, addRangeReject ? &rangeRejectInfo : nullptr)) {
|
||||
return BuildResult::fail(errorEmitted);
|
||||
if (!applyRangeDelay(sexprp, currentp, rangeMidSources,
|
||||
addRangeReject ? &rangeRejectInfo : nullptr)) {
|
||||
return BuildResult::failWithError();
|
||||
}
|
||||
} else {
|
||||
const unsigned delayCycles = getConstUInt(delayp->lhsp());
|
||||
@@ -1857,7 +1836,7 @@ class SvaNfaLowering final {
|
||||
};
|
||||
UASSERT_OBJ(size > 0, idxExprp, "Ring size must be positive");
|
||||
if (size == 1) {
|
||||
idxExprp->deleteTree();
|
||||
VL_DO_DANGLING(idxExprp->deleteTree(), idxExprp);
|
||||
return u32Const(0);
|
||||
}
|
||||
// idx == size - 1 ? 0 : idx + 1
|
||||
@@ -2912,6 +2891,7 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||
V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable names
|
||||
V3UniqueNames m_disableCntNames{"__VnfaDis"}; // Disable-iff counter names
|
||||
V3UniqueNames m_propTempNames{"__VnfaSampled"}; // Hoisted $sampled(propp) temps
|
||||
V3UniqueNames m_failCountNames{"__VnfaRemainingFailCount"}; // Fail replay counter names
|
||||
std::set<const AstProperty*> m_inliningProps; // Recursion guard for inlineNamedProperty
|
||||
|
||||
template <typename T_Node>
|
||||
@@ -3294,13 +3274,12 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||
// IEEE 1800-2023 16.12 requires one action-block evaluation per failed
|
||||
// thread. AstAssert handles the first, so replay the rest here.
|
||||
AstVar* const remainingFailCountVarp
|
||||
= new AstVar{flp, VVarType::BLOCKTEMP, "__VnfaRemainingFailCount",
|
||||
= new AstVar{flp, VVarType::MODULETEMP, m_failCountNames.get(""),
|
||||
m_modp->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
remainingFailCountVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
AstBegin* const replayBlockp = new AstBegin{flp, "", remainingFailCountVarp, true};
|
||||
replayBlockp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, remainingFailCountVarp, VAccess::WRITE},
|
||||
threadFailCountp});
|
||||
remainingFailCountVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
m_modp->addStmtsp(remainingFailCountVarp);
|
||||
AstNode* const replayStmtsp = new AstAssign{
|
||||
flp, new AstVarRef{flp, remainingFailCountVarp, VAccess::WRITE}, threadFailCountp};
|
||||
AstLoop* const replayLoopp = new AstLoop{flp};
|
||||
replayLoopp->addStmtsp(new AstLoopTest{
|
||||
flp, replayLoopp,
|
||||
@@ -3314,9 +3293,9 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||
replayLoopp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, remainingFailCountVarp, VAccess::WRITE},
|
||||
decrementedFailCountp});
|
||||
replayBlockp->addStmtsp(replayLoopp);
|
||||
replayStmtsp->addNext(replayLoopp);
|
||||
m_modp->addStmtsp(
|
||||
new AstAlways{flp, VAlwaysKwd::ALWAYS, threadFailReplaySenTreep, replayBlockp});
|
||||
new AstAlways{flp, VAlwaysKwd::ALWAYS, threadFailReplaySenTreep, replayStmtsp});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3343,10 +3322,10 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||
// Recursively walk a consequent. Returns cycle length consumed and
|
||||
// substitutes each VarRef to a captured local var with $past(rhs, K)
|
||||
// (or rhs inline when K == 0). Reports E_UNSUPPORTED on non-constant
|
||||
// delays or composite sequence operators.
|
||||
int walkSubstituteMatchItems(AstNodeExpr* nodep, unsigned K,
|
||||
const std::unordered_map<const AstVar*, AstNodeExpr*>& matchItems,
|
||||
bool& errorEmitted) {
|
||||
// delays or composite sequence operators and returns -1 on failure.
|
||||
int
|
||||
walkSubstituteMatchItems(AstNodeExpr* nodep, unsigned K,
|
||||
const std::unordered_map<const AstVar*, AstNodeExpr*>& matchItems) {
|
||||
if (AstSExpr* const sexprp = VN_CAST(nodep, SExpr)) {
|
||||
// IEEE 1800-2023 16.9.2: cycle_delay's lhsp is a constant_expression
|
||||
// and the delay form in a sequence is always `##N`, folded by
|
||||
@@ -3359,25 +3338,23 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||
sexprp->v3warn(E_UNSUPPORTED, "Unsupported: property local variable used across "
|
||||
"non-constant cycle delay in consequent"
|
||||
" (IEEE 1800-2023 16.10)");
|
||||
errorEmitted = true;
|
||||
return -1;
|
||||
}
|
||||
const unsigned delayCycles = VN_AS(delayp->lhsp(), Const)->toUInt();
|
||||
int preLen = 0;
|
||||
if (AstNodeExpr* const prep = sexprp->preExprp()) {
|
||||
preLen = walkSubstituteMatchItems(prep, K, matchItems, errorEmitted);
|
||||
if (errorEmitted) return -1;
|
||||
preLen = walkSubstituteMatchItems(prep, K, matchItems);
|
||||
if (preLen < 0) return -1;
|
||||
}
|
||||
const int bodyLen = walkSubstituteMatchItems(sexprp->exprp(), K + preLen + delayCycles,
|
||||
matchItems, errorEmitted);
|
||||
if (errorEmitted) return -1;
|
||||
const int bodyLen
|
||||
= walkSubstituteMatchItems(sexprp->exprp(), K + preLen + delayCycles, matchItems);
|
||||
if (bodyLen < 0) return -1;
|
||||
return preLen + delayCycles + bodyLen;
|
||||
}
|
||||
if (nodep->isMultiCycleSva()) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: property local variable used across "
|
||||
"composite sequence operator in consequent"
|
||||
" (IEEE 1800-2023 16.10)");
|
||||
errorEmitted = true;
|
||||
return -1;
|
||||
}
|
||||
std::vector<AstVarRef*> refs;
|
||||
@@ -3409,12 +3386,11 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||
matchItems[lhsRefp->varp()] = assignp->rhsp();
|
||||
}
|
||||
const unsigned startK = parts.isOverlapped ? 0 : 1;
|
||||
bool errorEmitted = false;
|
||||
walkSubstituteMatchItems(seqBodyp, startK, matchItems, errorEmitted);
|
||||
const int length = walkSubstituteMatchItems(seqBodyp, startK, matchItems);
|
||||
// Match-item substitution / strip mutates ancestor purity. Release
|
||||
// builds don't auto-clear caches on edits, so refresh here.
|
||||
VIsCached::clearCacheTree();
|
||||
if (errorEmitted) return true;
|
||||
if (length < 0) return true;
|
||||
AstNodeExpr* const antBoolp = exprStmtp->resultp()->unlinkFrBack();
|
||||
exprStmtp->replaceWith(antBoolp);
|
||||
VL_DO_DANGLING(pushDeletep(exprStmtp), exprStmtp);
|
||||
|
||||
+261
-60
@@ -32,6 +32,36 @@
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Clocking blocks of clocking items
|
||||
|
||||
class AssertPreClockingVisitor final : public VNVisitorConst {
|
||||
// Captures the clocking block of each clocking item before AssertPreVisitor, which needs it
|
||||
// at synchronous drives, often visited before the clocking block, e.g. in a parent module
|
||||
// NODE STATE
|
||||
// AstClockingItem::user3p() // AstClocking*. Clocking block of the item, user3 claimed by
|
||||
// // AssertPreVisitor
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); }
|
||||
void visit(AstNodeModule* nodep) override { iterateAndNextConstNull(nodep->stmtsp()); }
|
||||
void visit(AstGenBlock* nodep) override { iterateAndNextConstNull(nodep->itemsp()); }
|
||||
void visit(AstClocking* nodep) override {
|
||||
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (AstClockingItem* const citemp = VN_CAST(itemp, ClockingItem)) {
|
||||
citemp->user3p(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clocking blocks are only in modules and their generate blocks
|
||||
void visit(AstNode*) override {}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit AssertPreClockingVisitor(AstNetlist* nodep) { iterateConst(nodep); }
|
||||
~AssertPreClockingVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Assert class functions
|
||||
|
||||
@@ -40,10 +70,21 @@ class AssertPreVisitor final : public VNVisitor {
|
||||
// Eventually inlines calls to sequences, properties, etc.
|
||||
// We're not parsing the tree, or anything more complicated.
|
||||
private:
|
||||
// TYPES
|
||||
struct SynchDrive final {
|
||||
AstClockingItem* itemp; // Clocking item of the driven clockvar
|
||||
AstNodeExpr* refp; // Reference to the driven clockvar
|
||||
};
|
||||
|
||||
// NODE STATE
|
||||
// AstClockingItem::user1p() // AstVar*. varp() of ClockingItem after unlink
|
||||
// AstClockingItem::user2p() // AstVar*. Flag set by drives of output clockvar
|
||||
// AstClockingItem::user3p() // AstClocking*. Clocking block of the item, captured
|
||||
// // by AssertPreClockingVisitor
|
||||
// AstPExpr::user1() // bool. Created from AstUntil
|
||||
const VNUser1InUse m_inuser1;
|
||||
const VNUser2InUse m_inuser2;
|
||||
const VNUser3InUse m_inuser3;
|
||||
// STATE
|
||||
// Current context:
|
||||
AstNetlist* const m_netlistp = nullptr; // Current netlist
|
||||
@@ -68,17 +109,123 @@ private:
|
||||
V3UniqueNames m_gotoRepNames{"__VgotoRep"}; // Goto repetition counter name generator
|
||||
V3UniqueNames m_nonConsRepNames{"__VnonConsRep"}; // Nonconsecutive rep name generator
|
||||
V3UniqueNames m_disableCntNames{"__VdisableCnt"}; // Disable condition counter name generator
|
||||
V3UniqueNames m_blockNames{"__VassertBlock"}; // Names of blocks with temporaries
|
||||
V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable name generator
|
||||
V3UniqueNames m_activeNames{"__VassertsActive"}; // Active asserts map name generator
|
||||
V3UniqueNames m_drivenNames{"__VclockingDriven"}; // Clockvar drive flag name generator
|
||||
bool m_inAssign = false; // True if in an AssignNode
|
||||
bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS
|
||||
bool m_inSynchDrive = false; // True if in synchronous drive
|
||||
bool m_inConcatAssign = false; // True if in assignment to a concatenation
|
||||
std::vector<SynchDrive> m_drives; // Clockvars written by the synchronous drive
|
||||
bool m_hasCycleDelay = false; // True if node has cycle delay beneath
|
||||
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
|
||||
std::vector<AstSequence*> m_seqsToCleanup; // Sequences to clean up after traversal
|
||||
|
||||
// METHODS
|
||||
|
||||
// Flag set by each drive of an output clockvar, added to the module with its clocking item
|
||||
AstVar* getCreateDrivenVarp(AstClockingItem* itemp) {
|
||||
if (!itemp->user2p()) {
|
||||
AstVar* const varp = new AstVar{itemp->fileline(), VVarType::MODULETEMP,
|
||||
m_drivenNames.get(itemp), itemp->findBitDType()};
|
||||
varp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
itemp->user2p(varp);
|
||||
}
|
||||
return VN_AS(itemp->user2p(), Var);
|
||||
}
|
||||
// Reference to a variable in the scope of a driven clockvar, made like the clockvar's
|
||||
static AstNodeExpr* newDriveRefp(FileLine* flp, const SynchDrive& drive, AstVar* varp,
|
||||
const VAccess& access) {
|
||||
if (const AstVarXRef* const xrefp = VN_CAST(drive.refp, VarXRef)) {
|
||||
return new AstVarXRef{flp, varp, xrefp->dotted(), access};
|
||||
} else if (const AstMemberSel* const selp = VN_CAST(drive.refp, MemberSel)) {
|
||||
// The interface expression is evaluated again, so it must not have side effects;
|
||||
// cloneTreePure warns if it has any
|
||||
AstMemberSel* const newSelp
|
||||
= new AstMemberSel{flp, selp->fromp()->cloneTreePure(false), varp};
|
||||
newSelp->access(access);
|
||||
return newSelp;
|
||||
}
|
||||
return new AstVarRef{flp, varp, access};
|
||||
}
|
||||
// Assignment setting the drive flag of a driven clockvar
|
||||
AstAssign* newDrivenSetp(FileLine* flp, const SynchDrive& drive) {
|
||||
AstVar* const varp = getCreateDrivenVarp(drive.itemp);
|
||||
AstAssign* const setp = new AstAssign{flp, newDriveRefp(flp, drive, varp, VAccess::WRITE),
|
||||
new AstConst{flp, AstConst::BitTrue{}}};
|
||||
setp->user1(true);
|
||||
return setp;
|
||||
}
|
||||
// Clocking block of a clocking item
|
||||
static AstClocking* clockingOf(const AstClockingItem* itemp) {
|
||||
UASSERT_OBJ(itemp->user3p(), itemp, "Clocking item not captured in a clocking block");
|
||||
return VN_AS(itemp->user3p(), Clocking);
|
||||
}
|
||||
// Clocking event of a driven clockvar, referenced from the drive like the clockvar, or
|
||||
// nullptr if unsupported, which is reported on the drive's cycle delay
|
||||
AstSenItem* newDriveSensesp(AstDelay* delayp, const SynchDrive& drive) {
|
||||
AstSenItem* const origp = clockingOf(drive.itemp)->sensesp();
|
||||
if (VN_IS(drive.refp, VarRef)) return origp->cloneTree(false);
|
||||
if (origp->exists([](const AstVarXRef*) { return true; })) {
|
||||
// The reference is relative to the clocking block
|
||||
delayp->v3warn(E_UNSUPPORTED, "Unsupported: cycle delay in synchronous drive to"
|
||||
" clockvar in another scope, whose clocking event"
|
||||
" has a hierarchical reference");
|
||||
return nullptr;
|
||||
}
|
||||
const AstMemberSel* const selp = VN_CAST(drive.refp, MemberSel);
|
||||
// Processes outside classes evaluate their events also when not waiting on them,
|
||||
// possibly before the interface reference is set, so the event is guarded against a
|
||||
// null reference, which needs an integral value; scheduling does not guard it
|
||||
const bool guardNull = selp && !VN_IS(m_modp, Class);
|
||||
if (guardNull && !origp->sensp()->dtypep()->skipRefp()->isIntegralOrPacked()) {
|
||||
delayp->v3warn(E_UNSUPPORTED, "Unsupported: cycle delay in synchronous drive through"
|
||||
" an interface reference outside a class, to clocking"
|
||||
" block with non-integral clocking event");
|
||||
return nullptr;
|
||||
}
|
||||
// Interface declaring the clocking block, if driven through an interface reference
|
||||
AstIface* const ifacep
|
||||
= selp ? VN_AS(selp->fromp()->dtypep()->skipRefp(), IfaceRefDType)->ifaceViaCellp()
|
||||
: nullptr;
|
||||
AstSenItem* const sensesp = origp->cloneTree(false);
|
||||
std::vector<AstVarRef*> refps;
|
||||
sensesp->foreach([&](AstVarRef* refp) {
|
||||
if (!refp->classOrPackagep()) refps.push_back(refp);
|
||||
});
|
||||
for (AstVarRef* const refp : refps) {
|
||||
AstVar* const varp = refp->varp();
|
||||
if (ifacep) {
|
||||
// Mark the variable as read through an interface reference, as V3Width does for
|
||||
// a member select. The mark is the interface declaring the variable, whose
|
||||
// instances all share it, so drives through references to any instances agree.
|
||||
UASSERT_OBJ(!varp->sensIfacep() || varp->sensIfacep() == ifacep, refp,
|
||||
"Variable read through references to different interfaces");
|
||||
varp->sensIfacep(ifacep);
|
||||
}
|
||||
refp->replaceWith(newDriveRefp(refp->fileline(), drive, varp, refp->access()));
|
||||
VL_DO_DANGLING(pushDeletep(refp), refp);
|
||||
}
|
||||
if (guardNull) guardNullIface(sensesp, selp->fromp());
|
||||
return sensesp;
|
||||
}
|
||||
// Make an integral clocking event through an interface reference false while the reference
|
||||
// is null
|
||||
static void guardNullIface(AstSenItem* sensesp, AstNodeExpr* fromp) {
|
||||
FileLine* const flp = sensesp->fileline();
|
||||
AstNodeExpr* const sensp = sensesp->sensp()->unlinkFrBack();
|
||||
sensesp->sensp(new AstCond{flp, newNotNullp(flp, fromp), sensp,
|
||||
new AstConst{flp, AstConst::DTyped{}, sensp->dtypep()}});
|
||||
if (AstNodeExpr* const condp = sensesp->condp()) {
|
||||
condp->unlinkFrBack();
|
||||
sensesp->condp(new AstLogAnd{flp, newNotNullp(flp, fromp), condp});
|
||||
}
|
||||
}
|
||||
static AstNodeExpr* newNotNullp(FileLine* flp, AstNodeExpr* fromp) {
|
||||
return new AstNeq{flp, fromp->cloneTreePure(false), new AstConst{flp, AstConst::Null{}}};
|
||||
}
|
||||
|
||||
static void checkSamplingFuncDType(AstNodeExpr* nodep, const AstNode* exprp) {
|
||||
const AstNodeDType* const dtypep = exprp->dtypep()->skipRefp();
|
||||
if (!dtypep->isIntegralOrPacked()) {
|
||||
@@ -272,13 +419,21 @@ private:
|
||||
// It has to be converted to a list of ModportClockingVarRefs,
|
||||
// because clocking blocks are removed in this pass
|
||||
for (AstNode* itemp = nodep->clockingp()->itemsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const AstClockingItem* citemp = VN_CAST(itemp, ClockingItem)) {
|
||||
if (AstClockingItem* const citemp = VN_CAST(itemp, ClockingItem)) {
|
||||
if (AstVar* const varp
|
||||
= citemp->varp() ? citemp->varp() : VN_AS(citemp->user1p(), Var)) {
|
||||
AstModportVarRef* const modVarp = new AstModportVarRef{
|
||||
nodep->fileline(), varp->name(), citemp->direction()};
|
||||
modVarp->varp(varp);
|
||||
nodep->addNextHere(modVarp);
|
||||
if (citemp->direction().isOutput()) {
|
||||
// Drives through the modport set the drive flag
|
||||
AstVar* const drivenVarp = getCreateDrivenVarp(citemp);
|
||||
AstModportVarRef* const drivenRefp = new AstModportVarRef{
|
||||
nodep->fileline(), drivenVarp->name(), VDirection::OUTPUT};
|
||||
drivenRefp->varp(drivenVarp);
|
||||
nodep->addNextHere(drivenRefp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,6 +446,8 @@ private:
|
||||
// Unused item
|
||||
return;
|
||||
}
|
||||
// Flag set by drives of an output clockvar, possibly visited before this item
|
||||
if (nodep->direction().isOutput()) m_modp->addStmtsp(getCreateDrivenVarp(nodep));
|
||||
FileLine* const flp = nodep->fileline();
|
||||
V3Const::constifyEdit(nodep->skewp());
|
||||
if (!VN_IS(nodep->skewp(), Const)) {
|
||||
@@ -317,32 +474,26 @@ private:
|
||||
AstInitialStatic* const initClockvarp = new AstInitialStatic{
|
||||
flp, new AstAssign{flp, skewedWriteRefp, exprp->cloneTreePure(false)}};
|
||||
m_modp->addStmtsp(initClockvarp);
|
||||
// A var to keep the previous value of the clockvar
|
||||
AstVar* const prevVarp = new AstVar{
|
||||
flp, VVarType::MODULETEMP, "__Vclocking_prev__" + varp->name(), exprp->dtypep()};
|
||||
prevVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
AstInitialStatic* const initPrevClockvarp = new AstInitialStatic{
|
||||
flp, new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
|
||||
skewedReadRefp->cloneTreePure(false)}};
|
||||
m_modp->addStmtsp(prevVarp);
|
||||
m_modp->addStmtsp(initPrevClockvarp);
|
||||
// Assign the clockvar to the actual var; only do it if the clockvar's value has
|
||||
// changed
|
||||
// Each drive sets a flag, so the signal is also assigned when driven with the same
|
||||
// value again, e.g. after another assignment to the signal (IEEE 1800-2023 14.16.2)
|
||||
AstVar* const drivenVarp = getCreateDrivenVarp(nodep);
|
||||
m_modp->addStmtsp(new AstInitialStatic{
|
||||
flp, new AstAssign{flp, new AstVarRef{flp, drivenVarp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitFalse{}}}});
|
||||
// Assign the clockvar to the actual var if it was driven
|
||||
AstAssign* const assignp
|
||||
= new AstAssign{flp, exprp->cloneTreePure(false), skewedReadRefp};
|
||||
AstIf* const ifp
|
||||
= new AstIf{flp,
|
||||
new AstNeq{flp, new AstVarRef{flp, prevVarp, VAccess::READ},
|
||||
skewedReadRefp->cloneTreePure(false)},
|
||||
assignp};
|
||||
ifp->addThensp(new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
|
||||
skewedReadRefp->cloneTree(false)});
|
||||
= new AstIf{flp, new AstVarRef{flp, drivenVarp, VAccess::READ},
|
||||
new AstAssign{flp, new AstVarRef{flp, drivenVarp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitFalse{}}}};
|
||||
ifp->addThensp(assignp);
|
||||
if (skewp->isZero()) {
|
||||
// Drive the var in Re-NBA (IEEE 1800-2023 14.16)
|
||||
AstSenTree* senTreep
|
||||
= new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)};
|
||||
senTreep->addSensesp(
|
||||
new AstSenItem{flp, VEdgeType::ET_CHANGED, skewedReadRefp->cloneTree(false)});
|
||||
senTreep->addSensesp(new AstSenItem{
|
||||
flp, VEdgeType::ET_CHANGED, new AstVarRef{flp, drivenVarp, VAccess::READ}});
|
||||
AstCMethodHard* const trigp = new AstCMethodHard{
|
||||
nodep->fileline(),
|
||||
new AstVarRef{flp, m_clockingp->ensureEventp(), VAccess::READ},
|
||||
@@ -447,12 +598,12 @@ private:
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
|
||||
const AstConst* const constp = VN_CAST(valuep, Const);
|
||||
if (!constp) {
|
||||
if (!constp && !m_inSynchDrive) {
|
||||
// V3AssertNfa handles non-const delays before this pass and
|
||||
// replaces the property; this branch should never be reached.
|
||||
nodep->v3fatalSrc("Non-constant cycle delay in assertion: "
|
||||
"should have been caught by V3AssertNfa");
|
||||
} else if (constp->isZero()) {
|
||||
} else if (constp && constp->isZero()) {
|
||||
VL_DO_DANGLING(pushDeletep(valuep), valuep);
|
||||
if (m_inSynchDrive) {
|
||||
// ##0 has no effect in synchronous drives (IEEE 1800-2023 14.11)
|
||||
@@ -489,20 +640,36 @@ private:
|
||||
return;
|
||||
}
|
||||
AstSenItem* sensesp = nullptr;
|
||||
if (!m_defaultClockingp) {
|
||||
if (!m_drives.empty()) {
|
||||
if (m_inConcatAssign) {
|
||||
// Also the clockvars may be of different clocking blocks
|
||||
nodep->v3error("Cycle delays cannot be used in synchronous drives to a"
|
||||
" concatenation (IEEE 1800-2023 14.16)");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(valuep), valuep);
|
||||
return;
|
||||
}
|
||||
// Count the cycles of the driven clockvar's clocking block (IEEE 1800-2023 14.16)
|
||||
sensesp = newDriveSensesp(nodep, m_drives.front());
|
||||
if (!sensesp) {
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(valuep), valuep);
|
||||
return;
|
||||
}
|
||||
} else if (!m_defaultClockingp) {
|
||||
if (!m_pexprp) {
|
||||
nodep->v3error("Usage of cycle delays requires default clocking"
|
||||
" (IEEE 1800-2023 14.11)");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(valuep->deleteTree(), valuep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(valuep), valuep);
|
||||
return;
|
||||
}
|
||||
sensesp = m_senip;
|
||||
sensesp = m_senip->cloneTree(false);
|
||||
} else {
|
||||
sensesp = m_defaultClockingp->sensesp();
|
||||
sensesp = m_defaultClockingp->sensesp()->cloneTree(false);
|
||||
}
|
||||
AstEventControl* const controlp = new AstEventControl{
|
||||
nodep->fileline(), new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr};
|
||||
AstEventControl* const controlp
|
||||
= new AstEventControl{nodep->fileline(), new AstSenTree{flp, sensesp}, nullptr};
|
||||
const std::string delayName = m_cycleDlyNames.get(nodep);
|
||||
AstNodeExpr* throughoutp
|
||||
= nodep->throughoutp() ? nodep->throughoutp()->unlinkFrBack() : nullptr;
|
||||
@@ -511,6 +678,15 @@ private:
|
||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true};
|
||||
// A non-constant delay of a synchronous drive is counted at runtime, waiting for none if
|
||||
// zero, like a constant '##0' in a synchronous drive. The count is integral of 64 bits,
|
||||
// as V3Width makes delays, or real, which is rounded (IEEE 1800-2023 6.12.1)
|
||||
if (valuep->isDouble()) {
|
||||
valuep = new AstRToIRoundS{flp, valuep};
|
||||
valuep->dtypeFrom(cntVarp);
|
||||
} else {
|
||||
valuep = new AstSel{flp, valuep, 0, cntVarp->width()};
|
||||
}
|
||||
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
|
||||
|
||||
// Throughout: create flag tracking whether condition held every tick
|
||||
@@ -562,8 +738,7 @@ private:
|
||||
}
|
||||
if (m_disableSeqIfp && remainp) {
|
||||
AstIf* const disableSeqIfp = m_disableSeqIfp->cloneTree(false);
|
||||
// Keep continuation statements in a proper statement-list container.
|
||||
disableSeqIfp->addThensp(new AstBegin{flp, "", remainp, true});
|
||||
disableSeqIfp->addThensp(remainp);
|
||||
remainp = disableSeqIfp;
|
||||
}
|
||||
if (remainp) {
|
||||
@@ -620,7 +795,10 @@ private:
|
||||
nodep->v3error("Only non-blocking assignments can write "
|
||||
"to clockvars (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
if (m_inAssign) m_inSynchDrive = true;
|
||||
if (m_inAssign) {
|
||||
m_inSynchDrive = true;
|
||||
m_drives.push_back({itemp, nodep});
|
||||
}
|
||||
} else if (itemp->direction() == VDirection::INPUT) {
|
||||
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
|
||||
}
|
||||
@@ -641,7 +819,10 @@ private:
|
||||
nodep->v3error("Only non-blocking assignments can write "
|
||||
"to clockvars (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
if (m_inAssign) m_inSynchDrive = true;
|
||||
if (m_inAssign) {
|
||||
m_inSynchDrive = true;
|
||||
m_drives.push_back({itemp, nodep});
|
||||
}
|
||||
} else if (itemp->direction() == VDirection::INPUT) {
|
||||
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
|
||||
}
|
||||
@@ -652,22 +833,44 @@ private:
|
||||
if (nodep->user1()) return;
|
||||
VL_RESTORER(m_inAssign);
|
||||
VL_RESTORER(m_inSynchDrive);
|
||||
VL_RESTORER(m_inConcatAssign);
|
||||
VL_RESTORER_CLEAR(m_drives);
|
||||
m_inAssign = true;
|
||||
m_inSynchDrive = false;
|
||||
{
|
||||
// The braces of a concatenation are a replication, by one
|
||||
const AstNodeExpr* lhsp = nodep->lhsp();
|
||||
if (const AstReplicate* const repp = VN_CAST(lhsp, Replicate)) lhsp = repp->srcp();
|
||||
m_inConcatAssign = VN_IS(lhsp, Concat);
|
||||
}
|
||||
{
|
||||
VL_RESTORER(m_inAssignDlyLhs);
|
||||
m_inAssignDlyLhs = VN_IS(nodep, AssignDly);
|
||||
iterate(nodep->lhsp());
|
||||
}
|
||||
iterate(nodep->rhsp());
|
||||
if (nodep->timingControlp()) {
|
||||
iterate(nodep->timingControlp());
|
||||
} else if (m_inSynchDrive) {
|
||||
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()) {
|
||||
AstAssign* const assignp = new AstAssign{
|
||||
nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()};
|
||||
assignp->user1(true);
|
||||
nodep->replaceWith(assignp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
nodep = assignp;
|
||||
}
|
||||
// Note the drives when they take effect, after the cycle delay if any. Each clockvar
|
||||
// of the LHS is driven, e.g. in a concatenation which other simulators accept, although
|
||||
// IEEE 1800-2023 14.16 does not allow it
|
||||
AstNode* setsp = nullptr;
|
||||
for (const SynchDrive& drive : m_drives) {
|
||||
setsp = AstNode::addNext(setsp, newDrivenSetp(nodep->fileline(), drive));
|
||||
}
|
||||
if (!setsp) return;
|
||||
if (AstBegin* const beginp = VN_CAST(nodep->timingControlp(), Begin)) {
|
||||
beginp->addStmtsp(setsp);
|
||||
} else {
|
||||
nodep->addNextHere(setsp);
|
||||
}
|
||||
}
|
||||
void visit(AstAlways* nodep) override {
|
||||
@@ -878,7 +1081,7 @@ private:
|
||||
= getProcessAssocArrayDelete(new AstVarRef{flp, activep, VAccess::WRITE});
|
||||
|
||||
// Main assertion block
|
||||
AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true};
|
||||
AstBegin* const bodyp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
|
||||
bodyp->addStmtsp(incrementp);
|
||||
bodyp->addStmtsp(loopp);
|
||||
bodyp->addStmtsp(clausep);
|
||||
@@ -907,7 +1110,7 @@ private:
|
||||
new AstConst{flp, 1}}});
|
||||
|
||||
// Final assertion block
|
||||
AstBegin* const finalp = new AstBegin{flp, "", nullptr, true};
|
||||
AstBegin* const finalp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
|
||||
finalp->addStmtsp(activeCountp);
|
||||
finalp->addStmtsp(initActiveCountp);
|
||||
finalp->addStmtsp(finalLoopp);
|
||||
@@ -1073,15 +1276,13 @@ 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
|
||||
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});
|
||||
AstNode* const failsp = new AstPExprClause{flp, false};
|
||||
failsp->addNext(setDone());
|
||||
windowp->addStmtsp(new AstIf{flp, exprp->cloneTreePure(false), failsp});
|
||||
// if (rhs) { pass; done = 1; } -- consequent true at this !expr endpoint
|
||||
AstBegin* const passBlockp = new AstBegin{flp, "", nullptr, true};
|
||||
passBlockp->addStmtsp(new AstPExprClause{flp, true});
|
||||
passBlockp->addStmtsp(setDone());
|
||||
windowp->addStmtsp(new AstIf{flp, rhsp, passBlockp});
|
||||
AstNode* const passsp = new AstPExprClause{flp, true};
|
||||
passsp->addNext(setDone());
|
||||
windowp->addStmtsp(new AstIf{flp, rhsp, passsp});
|
||||
// @(clk) -- advance to next cycle in window
|
||||
windowp->addStmtsp(
|
||||
new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
|
||||
@@ -1295,7 +1496,7 @@ private:
|
||||
// */ }
|
||||
AstBegin* const bodyp = pexprp->bodyp();
|
||||
AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext();
|
||||
AstIf* const guardp = new AstIf{flp, condp, new AstBegin{flp, "", origStmtsp, true}};
|
||||
AstIf* const guardp = new AstIf{flp, condp, origStmtsp};
|
||||
bodyp->addStmtsp(guardp);
|
||||
nodep->replaceWith(pexprp);
|
||||
// Don't iterate pexprp here -- it was already iterated when created
|
||||
@@ -1369,10 +1570,9 @@ private:
|
||||
loopp->addStmtsp(new AstLoopTest{
|
||||
flp, loopp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}});
|
||||
{
|
||||
AstBegin* const passp = new AstBegin{flp, "", nullptr, true};
|
||||
passp->addStmtsp(new AstPExprClause{flp});
|
||||
passp->addStmtsp(decrementVar);
|
||||
passp->addStmtsp(setDone());
|
||||
AstNode* const passp = new AstPExprClause{flp};
|
||||
passp->addNext(decrementVar);
|
||||
passp->addNext(setDone());
|
||||
AstNodeExpr* passCondp = rhsp;
|
||||
if (nodep->isOverlapping()) {
|
||||
passCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), passCondp};
|
||||
@@ -1380,10 +1580,9 @@ private:
|
||||
loopp->addStmtsp(new AstIf{flp, passCondp, passp});
|
||||
}
|
||||
{
|
||||
AstBegin* const failp = new AstBegin{flp, "", nullptr, true};
|
||||
failp->addStmtsp(new AstPExprClause{flp, false});
|
||||
failp->addStmtsp(decrementVar->cloneTree(false));
|
||||
failp->addStmtsp(setDone());
|
||||
AstNode* const failp = new AstPExprClause{flp, false};
|
||||
failp->addNext(decrementVar->cloneTree(false));
|
||||
failp->addNext(setDone());
|
||||
loopp->addStmtsp(new AstIf{
|
||||
flp,
|
||||
new AstLogAnd{flp,
|
||||
@@ -1397,7 +1596,7 @@ private:
|
||||
flp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}, delayp});
|
||||
|
||||
// Main assertion block
|
||||
AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true};
|
||||
AstBegin* const bodyp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
|
||||
bodyp->addStmtsp(donep);
|
||||
bodyp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, donep, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitFalse{}}});
|
||||
@@ -1429,7 +1628,7 @@ private:
|
||||
new AstConst{flp, 1}}});
|
||||
|
||||
// Final assertion block
|
||||
AstBegin* const finalp = new AstBegin{flp, "", nullptr, true};
|
||||
AstBegin* const finalp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
|
||||
finalp->addStmtsp(activeCountp);
|
||||
finalp->addStmtsp(initActiveCountp);
|
||||
finalp->addStmtsp(finalLoopp);
|
||||
@@ -1460,7 +1659,7 @@ private:
|
||||
AstNodeExpr* const passCondp
|
||||
= nodep->isOverlapping() ? new AstLogAnd{flp, lhsp->cloneTreePure(false), rhsCopyp}
|
||||
: rhsCopyp;
|
||||
AstBegin* const beginp = new AstBegin{flp, "", loopp, true};
|
||||
AstBegin* const beginp = new AstBegin{flp, m_blockNames.get(""), loopp, true};
|
||||
beginp->addStmtsp(
|
||||
new AstIf{flp, passCondp, new AstPExprClause{flp}, new AstPExprClause{flp, false}});
|
||||
|
||||
@@ -1549,7 +1748,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);
|
||||
pushDeletep(m_disablep); // m_disablep used later
|
||||
}
|
||||
FileLine* const flp = nodep->fileline();
|
||||
// Add counter which counts times the condition turned true
|
||||
@@ -1578,6 +1777,7 @@ 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);
|
||||
@@ -1587,7 +1787,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);
|
||||
pushDeletep(m_disableSeqIfp); // m_disableSeqIfp used later
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -1623,6 +1823,7 @@ public:
|
||||
// CONSTRUCTORS
|
||||
explicit AssertPreVisitor(AstNetlist* nodep)
|
||||
: m_netlistp{nodep} {
|
||||
{ AssertPreClockingVisitor{nodep}; }
|
||||
// Process
|
||||
iterate(nodep);
|
||||
// Fix up varref names
|
||||
|
||||
+5
-1
@@ -208,6 +208,10 @@ 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
|
||||
@@ -361,7 +365,7 @@ string AstNode::vpiName(const string& namein) {
|
||||
|
||||
string AstNode::prettyTypeName() const {
|
||||
if (name() == "") return typeName();
|
||||
return std::string{typeName()} + " '" + prettyName() + "'";
|
||||
return std::string{typeName()} + " '" + prettyNameMsg() + "'";
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
+7
-2
@@ -56,6 +56,7 @@
|
||||
// Forward declarations
|
||||
class V3Graph;
|
||||
class ExecMTask;
|
||||
class VIfaceCaptureTag;
|
||||
|
||||
//######################################################################
|
||||
|
||||
@@ -391,7 +392,7 @@ protected:
|
||||
RELINK_OP4
|
||||
};
|
||||
AstNode* m_oldp = nullptr; // The old node that was linked to this point in the tree
|
||||
AstNode* m_backp = nullptr;
|
||||
AstNode* m_backp = nullptr; // Saved AstNode::m_backp of the unlinked node, to relink
|
||||
AstNode** m_iterpp = nullptr;
|
||||
RelinkWhatEn m_chg = RELINK_BAD;
|
||||
|
||||
@@ -658,6 +659,7 @@ 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)
|
||||
@@ -668,7 +670,10 @@ public:
|
||||
static string encodeNumber(int64_t num); // Encode number into internal C representation
|
||||
static string vcdName(const string& namein); // Name for printing out to vcd files
|
||||
string prettyName() const { return prettyName(name()); }
|
||||
string prettyNameQ() const { return prettyNameQ(name()); }
|
||||
// Name for messages to the user, by default prettyName(), but friendlier for some nodes,
|
||||
// such as a specialized class, whose name() is internal
|
||||
virtual string prettyNameMsg() const { return prettyName(); }
|
||||
string prettyNameQ() const { return "'"s + prettyNameMsg() + "'"; } // Quoted, for messages
|
||||
string verilogName() const { return vpiName(origName()); } // Decoded original Verilog name
|
||||
// "VARREF" for error messages (NOT dtype's pretty name)
|
||||
string prettyTypeName() const;
|
||||
|
||||
+248
-148
@@ -75,11 +75,11 @@ public:
|
||||
};
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"RD", "WR", "RW", "--"};
|
||||
static constexpr const char* const names[] = {"RD", "WR", "RW", "--"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* arrow() const {
|
||||
static const char* const names[] = {"[RV] <-", "[LV] =>", "[LRV] <=>", "--"};
|
||||
static constexpr const char* const names[] = {"[RV] <-", "[LV] =>", "[LRV] <=>", "--"};
|
||||
return names[m_e];
|
||||
}
|
||||
VAccess()
|
||||
@@ -119,7 +119,7 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
static constexpr const char* const names[]
|
||||
= {"always", "always_ff", "always_latch", "always_comb", "cont_assign"};
|
||||
return names[m_e];
|
||||
}
|
||||
@@ -160,18 +160,18 @@ public:
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
// IEEE 1800-2023 20.11
|
||||
static const char* const names[] = {"",
|
||||
"",
|
||||
"",
|
||||
"$asserton",
|
||||
"$assertoff",
|
||||
"$assertkill",
|
||||
"$assertpasson",
|
||||
"$assertpassoff",
|
||||
"$assertfailon",
|
||||
"$assertfailoff",
|
||||
"$assertnonvacuouson",
|
||||
"$assertvacuousoff"};
|
||||
static constexpr const char* const names[] = {"",
|
||||
"",
|
||||
"",
|
||||
"$asserton",
|
||||
"$assertoff",
|
||||
"$assertkill",
|
||||
"$assertpasson",
|
||||
"$assertpassoff",
|
||||
"$assertfailon",
|
||||
"$assertfailoff",
|
||||
"$assertnonvacuouson",
|
||||
"$assertvacuousoff"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
@@ -311,7 +311,7 @@ public:
|
||||
constexpr VPropStrength(en _e)
|
||||
: m_e{_e} {}
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"default", "weak", "strong"};
|
||||
static constexpr const char* const names[] = {"default", "weak", "strong"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
@@ -379,7 +379,7 @@ public:
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
// clang-format off
|
||||
static const char* const names[] = {
|
||||
static constexpr const char* const names[] = {
|
||||
"%E-AT",
|
||||
"DIM_BITS", "DIM_BITS_OR_NUMBER", "DIM_DIMENSIONS",
|
||||
"DIM_HIGH", "DIM_INCREMENT", "DIM_LEFT",
|
||||
@@ -520,69 +520,69 @@ public:
|
||||
};
|
||||
enum en m_e;
|
||||
const char* ascii() const VL_MT_SAFE {
|
||||
static const char* const names[] = {"%E-unk",
|
||||
"bit",
|
||||
"byte",
|
||||
"chandle",
|
||||
"void",
|
||||
"event",
|
||||
"int",
|
||||
"integer",
|
||||
"logic",
|
||||
"longint",
|
||||
"real",
|
||||
"shortint",
|
||||
"time",
|
||||
"string",
|
||||
"untyped",
|
||||
"VerilatedScope*",
|
||||
"char*",
|
||||
"VlMTaskState",
|
||||
"VlDelayScheduler",
|
||||
"VlTriggerScheduler",
|
||||
"VlDynamicTriggerScheduler",
|
||||
"VlFork",
|
||||
"VlProcessRef",
|
||||
"VlRandomizer",
|
||||
"VlStdRandomizer",
|
||||
"VlCovInstHandle",
|
||||
"IData",
|
||||
"QData",
|
||||
"LOGIC_IMPLICIT",
|
||||
" MAX"};
|
||||
static constexpr const char* const names[] = {"%E-unk",
|
||||
"bit",
|
||||
"byte",
|
||||
"chandle",
|
||||
"void",
|
||||
"event",
|
||||
"int",
|
||||
"integer",
|
||||
"logic",
|
||||
"longint",
|
||||
"real",
|
||||
"shortint",
|
||||
"time",
|
||||
"string",
|
||||
"untyped",
|
||||
"VerilatedScope*",
|
||||
"char*",
|
||||
"VlMTaskState",
|
||||
"VlDelayScheduler",
|
||||
"VlTriggerScheduler",
|
||||
"VlDynamicTriggerScheduler",
|
||||
"VlFork",
|
||||
"VlProcessRef",
|
||||
"VlRandomizer",
|
||||
"VlStdRandomizer",
|
||||
"VlCovInstHandle",
|
||||
"IData",
|
||||
"QData",
|
||||
"LOGIC_IMPLICIT",
|
||||
" MAX"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* dpiType() const {
|
||||
static const char* const names[] = {"%E-unk",
|
||||
"svBit",
|
||||
"char",
|
||||
"void*",
|
||||
"void",
|
||||
"char",
|
||||
"int",
|
||||
"%E-integer",
|
||||
"svLogic",
|
||||
"long long",
|
||||
"double",
|
||||
"short",
|
||||
"%E-time",
|
||||
"const char*",
|
||||
"%E-untyped",
|
||||
"dpiScope",
|
||||
"const char*",
|
||||
"%E-mtaskstate",
|
||||
"%E-dly-sched",
|
||||
"%E-trig-sched",
|
||||
"%E-dyn-sched",
|
||||
"%E-fork",
|
||||
"%E-proc-ref",
|
||||
"%E-rand-gen",
|
||||
"%E-stdrand-gen",
|
||||
"%E-cg-insthandle",
|
||||
"IData",
|
||||
"QData",
|
||||
"%E-logic-implct",
|
||||
" MAX"};
|
||||
static constexpr const char* const names[] = {"%E-unk",
|
||||
"svBit",
|
||||
"char",
|
||||
"void*",
|
||||
"void",
|
||||
"char",
|
||||
"int",
|
||||
"%E-integer",
|
||||
"svLogic",
|
||||
"long long",
|
||||
"double",
|
||||
"short",
|
||||
"%E-time",
|
||||
"const char*",
|
||||
"%E-untyped",
|
||||
"dpiScope",
|
||||
"const char*",
|
||||
"%E-mtaskstate",
|
||||
"%E-dly-sched",
|
||||
"%E-trig-sched",
|
||||
"%E-dyn-sched",
|
||||
"%E-fork",
|
||||
"%E-proc-ref",
|
||||
"%E-rand-gen",
|
||||
"%E-stdrand-gen",
|
||||
"%E-cg-insthandle",
|
||||
"IData",
|
||||
"QData",
|
||||
"%E-logic-implct",
|
||||
" MAX"};
|
||||
return names[m_e];
|
||||
}
|
||||
static void selfTest() {
|
||||
@@ -694,7 +694,7 @@ public:
|
||||
|
||||
const char* traceSigType() const {
|
||||
// VerilatedTraceSigType to used in trace signal declaration
|
||||
static const char* const lut[] = {
|
||||
static constexpr const char* const lut[] = {
|
||||
/* UNKNOWN: */ "", // Should not be traced
|
||||
/* BIT: */ "BIT",
|
||||
/* BYTE: */ "BYTE",
|
||||
@@ -754,7 +754,7 @@ public:
|
||||
explicit VBoolOrUnknown(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"FALSE", "TRUE", "UNK"};
|
||||
static constexpr const char* const names[] = {"FALSE", "TRUE", "UNK"};
|
||||
return names[m_e];
|
||||
}
|
||||
bool isKnown() const { return m_e != BU_UNKNOWN; }
|
||||
@@ -798,11 +798,11 @@ public:
|
||||
return m_e;
|
||||
}
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"", "VL_LIKELY", "VL_UNLIKELY"};
|
||||
static constexpr const char* const names[] = {"", "VL_LIKELY", "VL_UNLIKELY"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* asciiShort() const {
|
||||
static const char* const names[] = {"", "L", "!L"};
|
||||
static constexpr const char* const names[] = {"", "L", "!L"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
@@ -874,18 +874,56 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
|
||||
macro(COVERGROUP_ADD_BIN, "addBin", false, "r+") \
|
||||
macro(COVERGROUP_ADD_COVERPOINT, "addCoverpoint", false, "") \
|
||||
macro(COVERGROUP_ADD_CROSS, "addCross", false, "") \
|
||||
macro(COVERGROUP_ADD_CROSS_DYN, "addCrossDyn", false, "") \
|
||||
macro(COVERGROUP_ADD_NUMBERED_NAMER, "addNumberedNamer", false, "r+") \
|
||||
macro(COVERGROUP_ADD_SINGLE_NAMER, "addSingleNamer", false, "r+") \
|
||||
macro(COVERGROUP_ATTACH, "attach", false, "r") \
|
||||
macro(COVERGROUP_CLEAR_HIT_LIST, "clearHitList", false, "") \
|
||||
macro(COVERGROUP_COVERAGE_PARTS, "coverageParts", false, "TODO") \
|
||||
macro(COVERGROUP_COVERAGE, "coverage", false, "") \
|
||||
macro(COVERGROUP_FINALIZE_BINS, "finalizeBins", false, "") \
|
||||
macro(COVERGROUP_INCREMENT_BIN, "incrementBin", false, "r") \
|
||||
macro(COVERGROUP_INIT, "init", false, "r+") \
|
||||
macro(COVERGROUP_INST_P, "p", PURE, "") \
|
||||
macro(COVERGROUP_LEND_WEIGHT, "lendWeight", false, "rr") \
|
||||
macro(COVERGROUP_RECORD_HIT, "recordHit", false, "r") \
|
||||
macro(COVERGROUP_REGISTER_BINS, "registerBins", false, "rr") \
|
||||
macro(COVERGROUP_REGISTER_BINS, "registerBins", false, "rrrr") \
|
||||
macro(COVERGROUP_SAMPLE, "sample", false, "") \
|
||||
macro(COVERGROUP_SAMPLE_IFFS, "sample", false, "r") \
|
||||
macro(COVERGROUP_SELECT_ALL, "selectAll", false, "") \
|
||||
macro(COVERGROUP_SELECT_AND, "selectAnd", false, "") \
|
||||
macro(COVERGROUP_SELECT_BIN, "selectBin", false, "r+") \
|
||||
macro(COVERGROUP_SELECT_DIM, "selectDim", false, "r+") \
|
||||
macro(COVERGROUP_SELECT_DIM_END, "selectDimEnd", false, "") \
|
||||
macro(COVERGROUP_SELECT_OR, "selectOr", false, "") \
|
||||
macro(COVERGROUP_SELECT_RANGE, "selectRange", false, "rr") \
|
||||
macro(COVERGROUP_SELECT_RANGE_W, "selectRangeW", false, "rr") \
|
||||
macro(COVERGROUP_SIZED_END, "sizedEnd", PURE, "r") \
|
||||
macro(COVERGROUP_SIZED_FINISH, "sizedFinish", false, "r+") \
|
||||
macro(COVERGROUP_SIZED_FIRST, "sizedFirst", PURE, "r") \
|
||||
macro(COVERGROUP_SIZED_RANGE, "sizedRange", false, "rr") \
|
||||
macro(COVERGROUP_SIZED_RANGE_W, "sizedRangeW", false, "rr") \
|
||||
macro(COVERGROUP_SIZED_SAMPLE, "sizedSample", false, "rrr") \
|
||||
macro(COVERGROUP_SIZED_SAMPLE_W, "sizedSampleW", false, "rrr") \
|
||||
macro(COVERGROUP_TYPE_COVERAGE, "typeCoverage", false, "rrr") \
|
||||
macro(COVERGROUP_VALUE_EXCLUDED, "valueExcluded", PURE, "r") \
|
||||
macro(COVERGROUP_VALUE_EXCLUDED_W, "valueExcludedW", PURE, "r") \
|
||||
macro(COVERGROUP_VALUE_FINALIZE, "valueFinalize", false, "") \
|
||||
macro(COVERGROUP_VALUE_PATTERNS, "valuePatterns", false, "r") \
|
||||
macro(COVERGROUP_VALUE_RANGES, "valueRanges", false, "r") \
|
||||
macro(COVERGROUP_VALUE_RELEASE, "valueRelease", false, "") \
|
||||
macro(COVERGROUP_VALUE_RUNS, "valueRuns", false, "r") \
|
||||
macro(COVERGROUP_VALUE_TRANSITIONS, "valueTransitions", false, "r") \
|
||||
macro(COVERGROUP_VALUE_TYPE, "valueType", false, "rr") \
|
||||
macro(COVERGROUP_WEIGHT, "weight", false, "rr") \
|
||||
macro(COVERGROUP_WITH_BEGIN, "withBegin", false, "rr") \
|
||||
macro(COVERGROUP_WITH_FINISH, "withFinish", false, "r+") \
|
||||
macro(COVERGROUP_WITH_HI, "withHi", false, "") \
|
||||
macro(COVERGROUP_WITH_HI_W, "withHiW", false, "w") \
|
||||
macro(COVERGROUP_WITH_LO, "withLo", false, "") \
|
||||
macro(COVERGROUP_WITH_LO_W, "withLoW", false, "w") \
|
||||
macro(COVERGROUP_WITH_NEXT, "withNext", false, "") \
|
||||
macro(COVERGROUP_WITH_RUN, "withRun", false, "rr") \
|
||||
macro(COVERGROUP_WITH_RUN_W, "withRunW", false, "rr") \
|
||||
macro(DYN_AT_WRITE_APPEND, "atWriteAppend", false, "r") \
|
||||
macro(DYN_AT_WRITE_APPEND_BACK, "atWriteAppendBack", false, "r") \
|
||||
macro(DYN_CLEAR, "clear", false, "") \
|
||||
@@ -928,10 +966,11 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
|
||||
macro(RANDOMIZER_DISABLE_SOFT, "disable_soft", false, "r") \
|
||||
macro(RANDOMIZER_HARD, "hard", false, "r+") \
|
||||
macro(RANDOMIZER_SOFT, "soft", false, "rrrr") \
|
||||
macro(RANDOMIZER_UNIQUE, "rand_unique", false, "r") \
|
||||
macro(RANDOMIZER_UNIQUE, "rand_unique", false, "rr") \
|
||||
macro(RANDOMIZER_MARK_RANDC, "markRandc", false, "r") \
|
||||
macro(RANDOMIZER_SOLVE_BEFORE, "solveBefore", false, "rr") \
|
||||
macro(RANDOMIZER_PIN_VAR, "pin_var", false, "rrr") \
|
||||
macro(RANDOMIZER_UPDATE_VAR, "update_var", false, "TODO") \
|
||||
macro(RANDOMIZER_WRITE_VAR, "write_var", false, "TODO") \
|
||||
macro(RANDOMIZER_SET_VAR_DISABLED, "set_var_disabled", false, "r") \
|
||||
macro(RANDOMIZER_CLEAR_VAR_DISABLED, "clear_var_disabled", false, "r") \
|
||||
@@ -979,7 +1018,7 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const VL_PURE {
|
||||
static const char* const values[] = {
|
||||
static constexpr const char* const values[] = {
|
||||
#define VL_CMETHOD_NAME(id, method, pure, args) method,
|
||||
FOR_EACH_CMETHOD(VL_CMETHOD_NAME)
|
||||
#undef VL_CMETHOD_NAME
|
||||
@@ -988,7 +1027,7 @@ public:
|
||||
return values[m_e];
|
||||
}
|
||||
bool isPure() const VL_PURE {
|
||||
static const bool values[] = {
|
||||
static constexpr bool values[] = {
|
||||
#define VL_CMETHOD_PURE(id, method, pure, args) pure,
|
||||
FOR_EACH_CMETHOD(VL_CMETHOD_PURE)
|
||||
#undef VL_CMETHOD_PURE
|
||||
@@ -997,7 +1036,7 @@ public:
|
||||
return values[m_e];
|
||||
}
|
||||
const char* args() const VL_PURE {
|
||||
static const char* const values[] = {
|
||||
static constexpr const char* const values[] = {
|
||||
#define VL_CMETHOD_ARGS(id, method, pure, args) args,
|
||||
FOR_EACH_CMETHOD(VL_CMETHOD_ARGS)
|
||||
#undef VL_CMETHOD_ARGS
|
||||
@@ -1067,7 +1106,7 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const VL_PURE {
|
||||
static const char* const names[] = {"none", "ctor_var_reset_call"};
|
||||
static constexpr const char* const names[] = {"none", "ctor_var_reset_call"};
|
||||
return names[m_e];
|
||||
}
|
||||
bool isNone() const { return m_e == NONE; }
|
||||
@@ -1103,7 +1142,7 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const VL_PURE {
|
||||
static const char* const names[]
|
||||
static constexpr const char* const names[]
|
||||
= {"CASE", "CASEX", "CASEZ", "CASEINSIDE", "CASEMATCHES", "RANDSEQUENCE"};
|
||||
return names[m_e];
|
||||
}
|
||||
@@ -1130,7 +1169,7 @@ public:
|
||||
};
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
static constexpr const char* const names[]
|
||||
= {"UNSUPPORTED", "SAMEISH", "COMPATIBLE", "ENUM_EXPLICIT",
|
||||
"ENUM_IMPLICIT", "DYNAMIC_CLASS", "INCOMPATIBLE"};
|
||||
return names[m_e];
|
||||
@@ -1160,7 +1199,8 @@ class VCoverBinsType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
BINS_ARRAY, // Array of bins with user-speciifed size
|
||||
BINS_AUTO, // Auto-sized array of bins (eg auto_bin_max)
|
||||
BINS_AUTO, // Automatic bins of a 'bins auto[N]' declaration
|
||||
BINS_AUTO_IMPLICIT, // Automatic bins of a coverpoint without bins (IEEE 1800-2023 19.5.3)
|
||||
BINS_DEFAULT, // Default bin
|
||||
BINS_IGNORE, // Ignore bin
|
||||
BINS_ILLEGAL, // Illegal bin
|
||||
@@ -1176,8 +1216,15 @@ public:
|
||||
: m_e{_e} {}
|
||||
constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
= {"user", "array", "auto", "ignore", "illegal", "default", "wildcard", "transition"};
|
||||
static constexpr const char* const names[]
|
||||
= {"array", "auto", "auto_implicit", "default", "ignore_bins",
|
||||
"illegal_bins", "transition", "bins", "wildcard"};
|
||||
return names[m_e];
|
||||
}
|
||||
// Keyword declaring the bins, without any 'wildcard' prefix
|
||||
const char* verilogKwd() const {
|
||||
static constexpr const char* const names[] = {
|
||||
"bins", "bins", "bins", "bins", "ignore_bins", "illegal_bins", "bins", "bins", "bins"};
|
||||
return names[m_e];
|
||||
}
|
||||
// VlCovBinKind enumerator naming the bin's set
|
||||
@@ -1231,23 +1278,23 @@ public:
|
||||
constexpr VCoverOptionType(en _e)
|
||||
: m_e{_e} {}
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"weight",
|
||||
"goal",
|
||||
"at_least",
|
||||
"auto_bin_max",
|
||||
"per_instance",
|
||||
"comment",
|
||||
"name",
|
||||
"cross_num_print_missing",
|
||||
"cross_retain_auto_bins",
|
||||
"detect_overlap",
|
||||
"get_inst_coverage",
|
||||
"strobe",
|
||||
"merge_instances",
|
||||
"distribute_first",
|
||||
"real_interval",
|
||||
"cross_auto_bin_max",
|
||||
"unknown"};
|
||||
static constexpr const char* const names[] = {"weight",
|
||||
"goal",
|
||||
"at_least",
|
||||
"auto_bin_max",
|
||||
"per_instance",
|
||||
"comment",
|
||||
"name",
|
||||
"cross_num_print_missing",
|
||||
"cross_retain_auto_bins",
|
||||
"detect_overlap",
|
||||
"get_inst_coverage",
|
||||
"strobe",
|
||||
"merge_instances",
|
||||
"distribute_first",
|
||||
"real_interval",
|
||||
"cross_auto_bin_max",
|
||||
"unknown"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
@@ -1275,11 +1322,12 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // LCOV_EXCL_STOP // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"", "[*]", "[->]", "[=]"};
|
||||
static constexpr const char* const names[] = {"", "[*]", "[->]", "[=]"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* asciiJson() const {
|
||||
static const char* const names[] = {"\"none\"", "\"consec\"", "\"goto\"", "\"noncons\""};
|
||||
static constexpr const char* const names[]
|
||||
= {"\"none\"", "\"consec\"", "\"goto\"", "\"noncons\""};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
@@ -1299,11 +1347,13 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const VL_MT_SAFE { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"NONE", "INPUT", "OUTPUT", "INOUT", "REF", "CONSTREF"};
|
||||
static constexpr const char* const names[]
|
||||
= {"NONE", "INPUT", "OUTPUT", "INOUT", "REF", "CONSTREF"};
|
||||
return names[m_e];
|
||||
}
|
||||
string verilogKwd() const {
|
||||
static const char* const names[] = {"", "input", "output", "inout", "ref", "const ref"};
|
||||
static constexpr const char* const names[]
|
||||
= {"", "input", "output", "inout", "ref", "const ref"};
|
||||
return names[m_e];
|
||||
}
|
||||
string prettyName() const { return verilogKwd(); }
|
||||
@@ -1314,6 +1364,7 @@ public:
|
||||
bool isNonOutput() const {
|
||||
return m_e == INPUT || m_e == INOUT || m_e == REF || m_e == CONSTREF;
|
||||
}
|
||||
bool isOutput() const { return m_e == OUTPUT; }
|
||||
bool isReadOnly() const VL_MT_SAFE { return m_e == INPUT || m_e == CONSTREF; }
|
||||
bool isWritable() const VL_MT_SAFE { return m_e == OUTPUT || m_e == INOUT || m_e == REF; }
|
||||
bool isRef() const VL_MT_SAFE { return m_e == REF; }
|
||||
@@ -1328,6 +1379,17 @@ public:
|
||||
}
|
||||
return "VerilatedTraceSigDirection::NONE";
|
||||
}
|
||||
VAccess pinAccess() const {
|
||||
switch (m_e) {
|
||||
case NONE: return VAccess::NOCHANGE;
|
||||
case INPUT:
|
||||
case CONSTREF: return VAccess::READ;
|
||||
case OUTPUT: return VAccess::WRITE;
|
||||
case INOUT:
|
||||
case REF: return VAccess::READWRITE;
|
||||
}
|
||||
v3fatalSrc("Unhandled VDirection");
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VDirection& lhs, const VDirection& rhs) VL_MT_SAFE {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
@@ -1368,7 +1430,7 @@ public:
|
||||
bool addNewline() const { return m_e != DT_WRITE; }
|
||||
bool needScopeTracking() const { return m_e != DT_DISPLAY && m_e != DT_WRITE; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
static constexpr const char* const names[]
|
||||
= {"display", "write", "monitor", "strobe", "info", "error", "warning", "fatal"};
|
||||
return names[m_e];
|
||||
}
|
||||
@@ -1394,8 +1456,9 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"$dumpfile", "$dumpvars", "$dumpall", "$dumpflush",
|
||||
"$dumplimit", "$dumpoff", "$dumpon"};
|
||||
static constexpr const char* const names[]
|
||||
= {"$dumpfile", "$dumpvars", "$dumpall", "$dumpflush",
|
||||
"$dumplimit", "$dumpoff", "$dumpon"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
@@ -1433,7 +1496,7 @@ public:
|
||||
};
|
||||
enum en m_e;
|
||||
bool clockedStmt() const {
|
||||
static const bool clocked[] = {
|
||||
static constexpr bool clocked[] = {
|
||||
true, // ET_CHANGED
|
||||
true, // ET_BOTHEDGE
|
||||
true, // ET_POSEDGE
|
||||
@@ -1463,13 +1526,13 @@ public:
|
||||
}
|
||||
}
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
static constexpr const char* const names[]
|
||||
= {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE", "ET_INITIAL_NBA",
|
||||
"COMBO", "COMBO_STAR", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* verilogKwd() const {
|
||||
static const char* const names[] = {
|
||||
static constexpr const char* const names[] = {
|
||||
"[changed]", "edge", "posedge", "negedge", "[event]", "[true]", "[initial_nba]",
|
||||
"*", "*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
|
||||
return names[m_e];
|
||||
@@ -1532,7 +1595,7 @@ public:
|
||||
enum en m_e;
|
||||
|
||||
const char* ascii() const {
|
||||
static const char* const values[] = {
|
||||
static constexpr const char* const values[] = {
|
||||
#define VL_EVAL_NAME(id, iterated, triggers, first, slow) #id,
|
||||
FOR_EACH_EVAL(VL_EVAL_NAME)
|
||||
#undef VL_EVAL_NAME
|
||||
@@ -1541,7 +1604,7 @@ public:
|
||||
return values[m_e];
|
||||
}
|
||||
bool isIterated() const {
|
||||
static const bool values[] = {
|
||||
static constexpr bool values[] = {
|
||||
#define VL_EVAL_IS_ITERATED(id, iterated, triggers, first, slow) iterated,
|
||||
FOR_EACH_EVAL(VL_EVAL_IS_ITERATED)
|
||||
#undef VL_EVAL_IS_ITERATED
|
||||
@@ -1550,7 +1613,7 @@ public:
|
||||
return values[m_e];
|
||||
}
|
||||
bool hasTriggers() const {
|
||||
static const bool values[] = {
|
||||
static constexpr bool values[] = {
|
||||
#define VL_EVAL_HAS_TRIGGERS(id, iterated, triggers, first, slow) triggers,
|
||||
FOR_EACH_EVAL(VL_EVAL_HAS_TRIGGERS)
|
||||
#undef VL_EVAL_HAS_TRIGGERS
|
||||
@@ -1559,7 +1622,7 @@ public:
|
||||
return values[m_e];
|
||||
}
|
||||
bool firstIteration() const {
|
||||
static const bool values[] = {
|
||||
static constexpr bool values[] = {
|
||||
#define VL_EVAL_FIRST(id, iterated, triggers, first, slow) first,
|
||||
FOR_EACH_EVAL(VL_EVAL_FIRST)
|
||||
#undef VL_EVAL_FIRST
|
||||
@@ -1568,7 +1631,7 @@ public:
|
||||
return values[m_e];
|
||||
}
|
||||
bool slow() const {
|
||||
static const bool values[] = {
|
||||
static constexpr bool values[] = {
|
||||
#define VL_EVAL_SLOW(id, iterated, triggers, first, slow) slow,
|
||||
FOR_EACH_EVAL(VL_EVAL_SLOW)
|
||||
#undef VL_EVAL_SLOW
|
||||
@@ -1611,7 +1674,7 @@ public:
|
||||
enum en : uint8_t { NONE, ENUM, STRUCT, UNION, CLASS, INTERFACE_CLASS, GENERIC_INTERFACE };
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
static constexpr const char* const names[]
|
||||
= {"none", "enum", "struct", "union", "class", "interface class", "generic interface"};
|
||||
return names[m_e];
|
||||
}
|
||||
@@ -1681,11 +1744,11 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"JOIN", "JOIN_ANY", "JOIN_NONE"};
|
||||
static constexpr const char* const names[] = {"JOIN", "JOIN_ANY", "JOIN_NONE"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* verilogKwd() const {
|
||||
static const char* const names[] = {"join", "join_any", "join_none"};
|
||||
static constexpr const char* const names[] = {"join", "join_any", "join_none"};
|
||||
return names[m_e];
|
||||
}
|
||||
bool join() const { return m_e == JOIN; }
|
||||
@@ -1714,7 +1777,8 @@ public:
|
||||
}; // Static propagated from above
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"NONE", "VAUTOM", "VAUTOMI", "VSTATIC", "VSTATICI"};
|
||||
static constexpr const char* const names[]
|
||||
= {"NONE", "VAUTOM", "VAUTOMI", "VSTATIC", "VSTATICI"};
|
||||
return names[m_e];
|
||||
}
|
||||
VLifetime()
|
||||
@@ -1752,8 +1816,36 @@ inline std::ostream& operator<<(std::ostream& os, const VLifetime& rhs) {
|
||||
|
||||
class VNumRange final {
|
||||
public:
|
||||
int m_left = 0;
|
||||
int m_right = 0;
|
||||
// Iterable sequence of integers
|
||||
class Sequence final {
|
||||
class Iterator final {
|
||||
int m_index; // Current index
|
||||
const int m_inc; // Step, +1 or -1
|
||||
public:
|
||||
Iterator(int index, int inc)
|
||||
: m_index{index}
|
||||
, m_inc{inc} {}
|
||||
int operator*() const { return m_index; }
|
||||
Iterator& operator++() {
|
||||
m_index += m_inc;
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const Iterator& that) const { return m_index != that.m_index; }
|
||||
};
|
||||
const int m_begin; // First index
|
||||
const int m_end; // One past the last index
|
||||
const int m_inc; // Step, +1 or -1
|
||||
public:
|
||||
Sequence(int first, int last, int inc)
|
||||
: m_begin{first}
|
||||
, m_end{last + inc}
|
||||
, m_inc{inc} {}
|
||||
Iterator begin() const { return Iterator{m_begin, m_inc}; }
|
||||
Iterator end() const { return Iterator{m_end, m_inc}; }
|
||||
};
|
||||
|
||||
int m_left = 0; // Left side of range (pre-':')
|
||||
int m_right = 0; // Right side of range (post-':')
|
||||
bool m_ranged = false; // Has a range
|
||||
bool operator==(const VNumRange& rhs) const {
|
||||
return m_left == rhs.m_left && m_right == rhs.m_right && m_ranged == rhs.m_ranged;
|
||||
@@ -1791,6 +1883,14 @@ public:
|
||||
return m_left > m_right ? m_right : m_left;
|
||||
} // How to show a declaration
|
||||
int leftToRightInc() const { return ascending() ? 1 : -1; }
|
||||
// Iterate indices from left() to right() inclusive: 'for (int i : range.seqLeftToRight())'
|
||||
Sequence seqLeftToRight() const { return {m_left, m_right, leftToRightInc()}; }
|
||||
// Iterate indices from right() to left() inclusive: 'for (int i : range.seqRightToLeft())'
|
||||
Sequence seqRightToLeft() const { return {m_right, m_left, -leftToRightInc()}; }
|
||||
// Iterate indices from lo() to hi() inclusive: 'for (int i : range.seqLoToHi())'
|
||||
Sequence seqLoToHi() const { return {lo(), hi(), 1}; }
|
||||
// Iterate indices from hi() to lo() inclusive: 'for (int i : range.seqHiToLo())'
|
||||
Sequence seqHiToLo() const { return {hi(), lo(), -1}; }
|
||||
int elements() const VL_MT_SAFE { return hi() - lo() + 1; }
|
||||
bool ranged() const { return m_ranged; }
|
||||
bool ascending() const { return m_left < m_right; }
|
||||
@@ -1834,7 +1934,7 @@ public:
|
||||
};
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {
|
||||
static constexpr const char* const names[] = {
|
||||
"COVERAGE_BLOCK_OFF", //
|
||||
"HIER_BLOCK", //
|
||||
"HIER_PARAMS", //
|
||||
@@ -1882,7 +1982,7 @@ public:
|
||||
};
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"NONE", "RAND", "RANDC", "RAND_INLINE"};
|
||||
static constexpr const char* const names[] = {"NONE", "RAND", "RANDC", "RAND_INLINE"};
|
||||
return names[m_e];
|
||||
}
|
||||
VRandAttr()
|
||||
@@ -1949,7 +2049,7 @@ public:
|
||||
};
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"UNSIGNED", "SIGNED", "NOSIGN"};
|
||||
static constexpr const char* const names[] = {"UNSIGNED", "SIGNED", "NOSIGN"};
|
||||
return names[m_e];
|
||||
}
|
||||
VSigning()
|
||||
@@ -1988,7 +2088,7 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
static constexpr const char* const names[]
|
||||
= {"highz", "small", "medium", "weak", "large", "pull", "strong", "supply"};
|
||||
return names[m_e];
|
||||
}
|
||||
@@ -2018,13 +2118,13 @@ public:
|
||||
};
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"`systemc_ctor", //
|
||||
"`systemc_dtor", //
|
||||
"`systemc_header", //
|
||||
"`systemc_header_post", //
|
||||
"`systemc_implementation", //
|
||||
"`systemc_imp_header", //
|
||||
"`systemc_interface"};
|
||||
static constexpr const char* const names[] = {"`systemc_ctor", //
|
||||
"`systemc_dtor", //
|
||||
"`systemc_header", //
|
||||
"`systemc_header_post", //
|
||||
"`systemc_implementation", //
|
||||
"`systemc_imp_header", //
|
||||
"`systemc_interface"};
|
||||
return names[m_e];
|
||||
}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
@@ -2053,7 +2153,7 @@ public:
|
||||
: m_e{_e} {}
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
static constexpr const char* const names[]
|
||||
= {"ARRAY_PACKED", "ARRAY_UNPACKED", "SCOPE_MODULE", "SCOPE_INTERFACE",
|
||||
"STRUCT_PACKED", "STRUCT_UNPACKED", "UNION_PACKED"};
|
||||
return names[m_e];
|
||||
@@ -2091,11 +2191,11 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"CONSTANT", "FULL", "CHANGE"};
|
||||
static constexpr const char* const names[] = {"CONSTANT", "FULL", "CHANGE"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* func_prefix() const {
|
||||
static const char* const names[] = {"trace_const", "trace_full", "trace_chg"};
|
||||
static constexpr const char* const names[] = {"trace_const", "trace_full", "trace_chg"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
@@ -2128,7 +2228,7 @@ public:
|
||||
constexpr operator en() const { return m_e; }
|
||||
bool containsAny(VUseType other) const { return m_e & other.m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"INT_FWD", "INT_INC", "INT_FWD_INC"};
|
||||
static constexpr const char* const names[] = {"INT_FWD", "INT_INC", "INT_FWD_INC"};
|
||||
return names[m_e - 1];
|
||||
}
|
||||
};
|
||||
@@ -2181,7 +2281,7 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
static constexpr const char* const names[]
|
||||
= {"?", "GPARAM", "LPARAM", "SPECPARAM", "GENVAR", "VAR",
|
||||
"SUPPLY0", "SUPPLY1", "WIRE", "WREAL", "TRIAND", "TRIOR",
|
||||
"TRIWIRE", "TRI0", "TRI1", "PORT", "BLOCKTEMP", "MODULETEMP",
|
||||
@@ -2220,7 +2320,7 @@ public:
|
||||
|
||||
const char* traceSigKind() const {
|
||||
// VerilatedTraceSigKind to used in trace signal declaration
|
||||
static const char* const lut[] = {
|
||||
static constexpr const char* const lut[] = {
|
||||
/* UNKNOWN: */ "", // Should not be traced
|
||||
/* GPARAM: */ "PARAMETER",
|
||||
/* LPARAM: */ "PARAMETER",
|
||||
|
||||
+5
-2
@@ -149,6 +149,9 @@ 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} {}
|
||||
@@ -165,10 +168,10 @@ bool AstVar::sameNode(const AstNode* samep) const {
|
||||
&& covergroupRefMember() == asamep->covergroupRefMember();
|
||||
}
|
||||
|
||||
AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp)
|
||||
AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarRef* matchp)
|
||||
: ASTGEN_SUPER_MatchMasked(fl) {
|
||||
this->lhsp(lhsp);
|
||||
this->matchp(new AstVarRef{fl, matchp, VAccess::READ});
|
||||
this->matchp(matchp);
|
||||
dtypeSetUInt32();
|
||||
}
|
||||
|
||||
|
||||
+52
-30
@@ -103,6 +103,9 @@ public:
|
||||
return const_cast<AstNodeDType*>(
|
||||
static_cast<const AstNodeDType*>(this)->skipRefIterp(true, false));
|
||||
}
|
||||
// (Slow) Recurse over MemberDType|ParamTypeDType|RefDType|ConstDType to EnumDType,
|
||||
// Returns null if not resolved
|
||||
const AstNodeDType* skipRefToEnumOrNullp() const { return skipRefIterp(true, false, false); }
|
||||
// (Slow) Recurse over MemberDType|ParamTypeDType|RefDType to other type
|
||||
const AstNodeDType* skipRefToNonRefp() const { return skipRefIterp(false, false); }
|
||||
AstNodeDType* skipRefToNonRefp() {
|
||||
@@ -250,8 +253,9 @@ 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;
|
||||
bool m_packed;
|
||||
string m_typedefName; // Typedef's dtypeName() once moved off the typedef declaring it
|
||||
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
|
||||
bool m_packed; // Packed struct/union, else unpacked
|
||||
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
|
||||
@@ -267,6 +271,7 @@ protected:
|
||||
AstNodeUOrStructDType(const AstNodeUOrStructDType& other)
|
||||
: AstNodeDType{other}
|
||||
, m_name{other.m_name}
|
||||
, m_typedefName{other.m_typedefName}
|
||||
, m_uniqueNum{uniqueNumInc()}
|
||||
, m_packed{other.m_packed}
|
||||
, m_isFourstate{other.m_isFourstate} {}
|
||||
@@ -294,6 +299,8 @@ public:
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
// Keep the name the typedef declaring it gives, as it moves off it to the type table
|
||||
void typedefName(const string& name) { m_typedefName = name; }
|
||||
bool packed() const VL_MT_SAFE { return m_packed; }
|
||||
void packed(bool flag) { m_packed = flag; }
|
||||
// packed() but as don't support unpacked, presently all structs
|
||||
@@ -319,7 +326,7 @@ public:
|
||||
class AstEnumItem final : public AstNode {
|
||||
// @astgen op1 := rangep : Optional[AstRange] // Range for name appending
|
||||
// @astgen op2 := valuep : Optional[AstNodeExpr]
|
||||
string m_name;
|
||||
string m_name; // Name of item
|
||||
|
||||
public:
|
||||
// Parents: ENUM
|
||||
@@ -611,6 +618,7 @@ public:
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
string prettyNameMsg() const override;
|
||||
string name() const override VL_MT_STABLE;
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
int widthAlignBytes() const override { return 0; }
|
||||
@@ -691,22 +699,24 @@ public:
|
||||
bool isCompound() const override { return false; }
|
||||
};
|
||||
class AstCoverCrossDType final : public AstNodeDType {
|
||||
// Borrowed pointer to VlCoverCrossT<dimensions, tuples, bins, autoBins, binWords>.
|
||||
// Borrowed pointer to VlCoverCrossT<...> or construction-time VlCoverCrossDyn.
|
||||
const uint32_t m_dimensions;
|
||||
const uint32_t m_tuples;
|
||||
const uint32_t m_bins;
|
||||
const uint32_t m_autoBins;
|
||||
const uint64_t m_binWords;
|
||||
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)
|
||||
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_binWords{binWords}
|
||||
, m_dynamic{dynamic} {
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverCrossDType;
|
||||
@@ -714,11 +724,12 @@ public:
|
||||
BROKEN_RTN(m_dimensions == 0);
|
||||
return nullptr;
|
||||
}
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
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();
|
||||
}
|
||||
&& 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;
|
||||
@@ -728,6 +739,7 @@ public:
|
||||
uint32_t bins() const { return m_bins; }
|
||||
uint32_t autoBins() const { return m_autoBins; }
|
||||
uint64_t binWords() const { return m_binWords; }
|
||||
bool isDynamic() const { return m_dynamic; }
|
||||
string cppTemplateArgs() const;
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
int widthAlignBytes() const override { return sizeof(void*); }
|
||||
@@ -767,8 +779,8 @@ class AstDefImplicitDType final : public AstNodeDType {
|
||||
// This allows "var enum {...} a,b" to share the enum definition for both variables
|
||||
// After link, these become typedefs
|
||||
// @astgen op1 := childDTypep : Optional[AstNodeDType]
|
||||
string m_name;
|
||||
const int m_uniqueNum;
|
||||
string m_name; // Data type name
|
||||
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
|
||||
|
||||
public:
|
||||
AstDefImplicitDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp)
|
||||
@@ -878,14 +890,9 @@ 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;
|
||||
// dist-ast-dump-suppress // Skip dumping cache
|
||||
TableMap m_tableMap; // Created table for V3Width only to remove duplicates
|
||||
string m_typedefName; // Typedef's dtypeName() once moved off the typedef declaring it
|
||||
const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
|
||||
|
||||
public:
|
||||
AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstEnumItem* itemsp)
|
||||
@@ -900,6 +907,7 @@ public:
|
||||
AstEnumDType(const AstEnumDType& other)
|
||||
: AstNodeDType{other}
|
||||
, m_name{other.m_name}
|
||||
, m_typedefName{other.m_typedefName}
|
||||
, m_uniqueNum{uniqueNumInc()} {}
|
||||
ASTGEN_MEMBERS_AstEnumDType;
|
||||
|
||||
@@ -919,6 +927,8 @@ public:
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
// Keep the name the typedef declaring it gives, as it moves off it to the type table
|
||||
void typedefName(const string& name) { m_typedefName = name; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
@@ -933,8 +943,6 @@ 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 {
|
||||
@@ -983,7 +991,6 @@ 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)
|
||||
@@ -1014,12 +1021,18 @@ public:
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
|
||||
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();
|
||||
}
|
||||
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 +1067,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;
|
||||
bool m_constrainedRand = false; // Member has a constraint expression
|
||||
VRandAttr m_rand; // Randomizability of this member (rand, randc, etc)
|
||||
public:
|
||||
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
|
||||
@@ -1078,6 +1091,7 @@ public:
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
bool hasDType() const override VL_MT_SAFE { return true; }
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
@@ -1161,6 +1175,7 @@ public:
|
||||
ASTGEN_MEMBERS_AstParamTypeDType;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return dtypep() ? dtypep() : childDTypep();
|
||||
@@ -1275,6 +1290,9 @@ 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)
|
||||
@@ -1301,12 +1319,13 @@ public:
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override {
|
||||
return subDTypep()->similarDType(samep->subDTypep());
|
||||
}
|
||||
const char* broken() const override;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
string prettyDTypeName(bool full) const override {
|
||||
return subDTypep() ? prettyName(subDTypep()->prettyDTypeName(full)) : prettyName();
|
||||
return subDTypep() ? subDTypep()->prettyDTypeName(full) : prettyName();
|
||||
}
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE {
|
||||
return subDTypep() ? subDTypep()->basicp() : nullptr;
|
||||
@@ -1325,6 +1344,8 @@ 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;
|
||||
@@ -1496,6 +1517,7 @@ public:
|
||||
bool sameNode(const AstNode* samep) const override;
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
|
||||
+36
-16
@@ -66,13 +66,16 @@ 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; }
|
||||
|
||||
// TODO: consolidate cLValueTargetp, isLValue, baseFromp
|
||||
// If the expression is a valid C++ LValue, return the target reference, else nullptr
|
||||
// This always returns either AstVarRef, AstMemberSel, or nullptr
|
||||
AstNodeExpr* cLValueTargetp();
|
||||
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;
|
||||
bool isLValue() const { return getVAccessRecurse().isWriteOrRW(); }
|
||||
// Return base var (or const) nodep dereferences
|
||||
AstNode* baseFromp(bool overMembers);
|
||||
|
||||
@@ -529,7 +532,6 @@ public:
|
||||
}
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
static AstNodeVarRef* varRefLValueRecurse(AstNode* nodep);
|
||||
};
|
||||
|
||||
// === Concrete node types =====================================================
|
||||
@@ -750,7 +752,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
|
||||
bool m_pure = false; // Pure optimizable
|
||||
VIsCached m_purity; // Pure optimizable
|
||||
bool m_usePtr = false; // Use '->' not '.'
|
||||
public:
|
||||
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VCMethod method, AstNodeExpr* pinsp = nullptr)
|
||||
@@ -758,7 +760,6 @@ public:
|
||||
, m_method{method} {
|
||||
this->fromp(fromp);
|
||||
addPinsp(pinsp);
|
||||
setPurity();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCMethodHard;
|
||||
void dump(std::ostream& str) const override;
|
||||
@@ -768,7 +769,10 @@ public:
|
||||
const AstCMethodHard* const asamep = VN_DBG_AS(samep, CMethodHard);
|
||||
return (m_method == asamep->m_method);
|
||||
}
|
||||
bool isPure() override { return m_pure; }
|
||||
bool isPure() override {
|
||||
if (!m_purity.isCached()) m_purity.set(getPurity());
|
||||
return m_purity.get();
|
||||
}
|
||||
int instrCount() const override;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -779,7 +783,7 @@ public:
|
||||
void method(VCMethod value) { m_method = value; }
|
||||
|
||||
private:
|
||||
void setPurity();
|
||||
bool getPurity();
|
||||
};
|
||||
class AstCReset final : public AstNodeExpr {
|
||||
// Reset variable at startup
|
||||
@@ -1249,6 +1253,10 @@ public:
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool isEqAllOnes() const { return num().isEqAllOnes(width()); }
|
||||
bool isEqAllOnesV() const { return num().isEqAllOnes(widthMinV()); }
|
||||
// Whether a parameter declared without a data type takes the same type from this value
|
||||
// as from 'samep': both real, both string, or both integral with the same width and
|
||||
// signedness. Unlike AstNodeDType::similarDType, ignores whether two-state or four-state.
|
||||
bool sameValueType(const AstConst* samep) const;
|
||||
// Parse string and create appropriate type of AstConst.
|
||||
// May return nullptr on parse failure.
|
||||
static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
|
||||
@@ -1438,6 +1446,8 @@ class AstEnumItemRef final : public AstNodeExpr {
|
||||
// @astgen ptr := m_itemp : Optional[AstEnumItem] // [AfterLink] Pointer to item
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in
|
||||
string m_name; // Name of enum (for param relink)
|
||||
string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
|
||||
bool m_containsGenBlock = false; // Contains gen block reference
|
||||
public:
|
||||
AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
|
||||
: ASTGEN_SUPER_EnumItemRef(fl)
|
||||
@@ -1454,10 +1464,15 @@ public:
|
||||
int instrCount() const override { return 0; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstEnumItemRef* const sp = VN_DBG_AS(samep, EnumItemRef);
|
||||
return itemp() == sp->itemp();
|
||||
return itemp() == sp->itemp() && dotted() == sp->dotted()
|
||||
&& containsGenBlock() == sp->containsGenBlock();
|
||||
}
|
||||
AstEnumItem* itemp() const VL_MT_STABLE { return m_itemp; }
|
||||
void itemp(AstEnumItem* nodep) { m_itemp = nodep; }
|
||||
string dotted() const { return m_dotted; }
|
||||
void dotted(const string& dotted) { m_dotted = dotted; }
|
||||
bool containsGenBlock() const { return m_containsGenBlock; }
|
||||
void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
@@ -1471,7 +1486,7 @@ class AstExprStmt final : public AstNodeExpr {
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
// @astgen op2 := resultp : AstNodeExpr
|
||||
private:
|
||||
bool m_hasResult = true;
|
||||
bool m_hasResult = true; // Returns result via resultp()
|
||||
|
||||
public:
|
||||
AstExprStmt(FileLine* fl, AstNode* stmtsp, AstNodeExpr* resultp)
|
||||
@@ -1903,7 +1918,7 @@ class AstMatchMasked final : public AstNodeExpr {
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
// @astgen op2 := matchp : AstVarRef
|
||||
public:
|
||||
inline AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp);
|
||||
inline AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarRef* matchp);
|
||||
ASTGEN_MEMBERS_AstMatchMasked;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { return "VL_MATCHMASKED_%lq(%lw, %li, %ri)"; }
|
||||
@@ -2031,7 +2046,7 @@ class AstParseRef final : public AstNodeExpr {
|
||||
// @astgen op1 := lhsp : Optional[AstNodeExpr]
|
||||
// @astgen op2 := ftaskrefp : Optional[AstNodeFTaskRef]
|
||||
|
||||
string m_name;
|
||||
string m_name; // Name of the variable/function/task
|
||||
|
||||
public:
|
||||
AstParseRef(FileLine* fl, const string& name, AstNodeExpr* lhsp = nullptr,
|
||||
@@ -2404,6 +2419,7 @@ 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:
|
||||
@@ -2426,6 +2442,7 @@ 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; }
|
||||
@@ -2631,7 +2648,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;
|
||||
std::string m_scopeEntr; // Scope path for the DPI import/export context name
|
||||
bool m_dpiExport = false; // Is for dpiExport
|
||||
const bool m_forFormat; // Is for a format %m
|
||||
static std::string scopeNameFormatter(const std::string& text);
|
||||
@@ -6458,6 +6475,7 @@ 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)
|
||||
@@ -6475,6 +6493,8 @@ 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; }
|
||||
|
||||
+170
-74
@@ -44,6 +44,7 @@ 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)
|
||||
@@ -65,6 +66,8 @@ 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; }
|
||||
@@ -74,7 +77,8 @@ 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());
|
||||
&& comment() == asamep->comment() && page() == asamep->page()
|
||||
&& perInstance() == asamep->perInstance());
|
||||
}
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; }
|
||||
@@ -114,6 +118,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
bool m_dpiPure : 1; // DPI import pure (vs. virtual pure)
|
||||
bool m_keepAlive : 1; // Disable dead function elimination
|
||||
bool m_needsSyms : 1; // Requires vlSymsp argument
|
||||
bool m_pureVirtual : 1; // Pure virtual
|
||||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
bool m_static : 1; // Static method in class
|
||||
@@ -147,6 +152,7 @@ protected:
|
||||
, m_isHideProtected{false}
|
||||
, m_dpiPure{false}
|
||||
, m_keepAlive{false}
|
||||
, m_needsSyms{true}
|
||||
, m_pureVirtual{false}
|
||||
, m_recursive{false}
|
||||
, m_static{false}
|
||||
@@ -216,6 +222,8 @@ public:
|
||||
void dpiPure(bool flag) { m_dpiPure = flag; }
|
||||
bool keepAlive() const { return m_keepAlive; }
|
||||
void keepAlive(bool flag) { m_keepAlive = flag; }
|
||||
bool needsSyms() const { return m_needsSyms; }
|
||||
void needsSyms(bool flag) { m_needsSyms = flag; }
|
||||
bool pureVirtual() const { return m_pureVirtual; }
|
||||
void pureVirtual(bool flag) { m_pureVirtual = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
@@ -350,6 +358,8 @@ 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; }
|
||||
@@ -690,8 +700,10 @@ 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
|
||||
// @astgen op3 := rangep : List[AstRange] // Range(s) for arrayed instances; multi-dim chains
|
||||
// via nextp()
|
||||
// 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 ptr := m_modp : Optional[AstNodeModule] // [AfterLink] Pointer to module instanced
|
||||
@@ -699,6 +711,7 @@ class AstCell final : public AstNode {
|
||||
string m_name; // Cell name
|
||||
string m_origName; // Original name before dot addition
|
||||
string m_modName; // Module the cell instances
|
||||
int m_arrayIdx = -1; // Row-major position in instance array, from the left (V3Param-V3Width)
|
||||
bool m_hasIfaceVar : 1; // True if a Var has been created for this cell
|
||||
bool m_recursive : 1; // Self-recursive module
|
||||
bool m_trace : 1; // Trace this cell
|
||||
@@ -733,6 +746,8 @@ 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; }
|
||||
@@ -786,6 +801,9 @@ 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
|
||||
@@ -977,40 +995,6 @@ 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]
|
||||
@@ -1058,12 +1042,13 @@ 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 either an auto-sized array of values or transitions
|
||||
bool m_isArray = false; // Bin is an array: of a bin per value or transition, or of
|
||||
// arraySizep() bins
|
||||
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)
|
||||
bool isWildcard = false, AstNodeExpr* iffp = nullptr)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_binsType{isIllegal ? VCoverBinsType::BINS_ILLEGAL
|
||||
@@ -1072,12 +1057,14 @@ public:
|
||||
: VCoverBinsType::BINS_USER))}
|
||||
, m_isWildcard{isWildcard} {
|
||||
addRangesp(rangesp);
|
||||
this->iffp(iffp);
|
||||
}
|
||||
// Constructor for automatic bins
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep,
|
||||
VCoverBinsType type = VCoverBinsType::BINS_AUTO)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_binsType{VCoverBinsType::BINS_AUTO}
|
||||
, m_binsType{type}
|
||||
, m_isArray{true} {
|
||||
this->arraySizep(arraySizep);
|
||||
}
|
||||
@@ -1136,16 +1123,40 @@ class AstCoverCrossBin final : public AstNode {
|
||||
// @astgen op1 := selectp : Optional[AstNode] // Null for unsupported selections
|
||||
// @astgen op2 := iffp : Optional[AstNodeExpr]
|
||||
const string m_name; // Declared cross bin name
|
||||
// dist-ast-dump-suppress // Bin kind is shown by verilogKwd() in emitted Verilog.
|
||||
const VCoverBinsType m_binsType; // Normal, ignore, or illegal bin
|
||||
|
||||
public:
|
||||
AstCoverCrossBin(FileLine* fl, const string& name, AstNode* selectp, AstNodeExpr* iffp)
|
||||
AstCoverCrossBin(FileLine* fl, const string& name, AstNode* selectp, AstNodeExpr* iffp,
|
||||
VCoverBinsType binsType = VCoverBinsType::BINS_USER)
|
||||
: ASTGEN_SUPER_CoverCrossBin(fl)
|
||||
, m_name{name} {
|
||||
, 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
|
||||
@@ -1173,17 +1184,46 @@ 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, VCoverOptionType optType, AstNodeExpr* valuep)
|
||||
AstCoverOption(FileLine* fl, bool typeOption, VCoverOptionType optType, AstNodeExpr* valuep)
|
||||
: ASTGEN_SUPER_CoverOption(fl)
|
||||
, m_optType{optType} {
|
||||
, m_optType{optType}
|
||||
, m_typeOption{typeOption} {
|
||||
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]
|
||||
@@ -1214,6 +1254,20 @@ 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
|
||||
@@ -1521,7 +1575,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_constPoolp : AstConstPool // Reference to constant pool, for faster lookup
|
||||
// @astgen ptr := m_constPoolPkgp : AstPackage // Constant pool package
|
||||
// @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
|
||||
@@ -1542,6 +1596,8 @@ 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
|
||||
@@ -1563,10 +1619,14 @@ public:
|
||||
return modulesp(); // First one in the list, for now
|
||||
}
|
||||
AstTypeTable* typeTablep() { return m_typeTablep; }
|
||||
AstConstPool* constPoolp() { return m_constPoolp; }
|
||||
string astConstOrigParamName(const AstConst* nodep) const;
|
||||
void astConstOrigParamName(const AstConst* nodep, const string& name);
|
||||
void astConstOrigParamNameErase(const AstConst* nodep);
|
||||
// Find the module a node is in, remembering what it passed on the way.
|
||||
const AstNodeModule* containingModule(const AstNode* nodep);
|
||||
// Forget remembered modules, as the tree has moved.
|
||||
void clearContainingModules() { m_containingModules.clear(); }
|
||||
AstPackage* constPoolPkgp() const { return m_constPoolPkgp; }
|
||||
AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; }
|
||||
void dollarUnitPkgp(AstPackage* const packagep) { m_dollarUnitPkgp = packagep; }
|
||||
AstCFunc* evalFuncp(VEval eval) const { return m_evalFuncps[eval]; }
|
||||
@@ -1832,6 +1892,7 @@ public:
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string prettyNameMsg() const override;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool sameNode(const AstNode* samep) const override;
|
||||
@@ -1839,7 +1900,9 @@ 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);
|
||||
@@ -2078,10 +2141,10 @@ class AstTypedef final : public AstNode {
|
||||
// @astgen op1 := childDTypep : Optional[AstNodeDType]
|
||||
// @astgen op4 := attrsp : List[AstNode] // Attributes during early parse
|
||||
|
||||
string m_name;
|
||||
string m_name; // Name of the typedef
|
||||
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;
|
||||
bool m_attrPublic = false; // Marked with public; keep even if unused
|
||||
bool m_isHideLocal : 1; // Verilog local
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
bool m_isUnderClass : 1; // Underneath class
|
||||
@@ -2111,6 +2174,9 @@ public:
|
||||
ASTGEN_MEMBERS_AstTypedef;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
// Name of the type it declares, with the scope declaring it, so with the values of the
|
||||
// parameters of the scope, e.g. '$unit::Cls#(8)::s_t'
|
||||
string dtypeName() const;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
virtual AstNodeDType* subDTypep() const VL_MT_STABLE {
|
||||
return dtypep() ? dtypep() : childDTypep();
|
||||
@@ -2247,11 +2313,11 @@ class AstVar final : public AstNode {
|
||||
bool m_attrSFormat : 1; // User sformat attribute
|
||||
bool m_attrSplitVar : 1; // declared with split_var metacomment
|
||||
bool m_attrFsmState : 1; // declared with fsm_state metacomment
|
||||
bool m_attrFsmRegisterWrapper : 1; // connected to an fsm_register_wrapper instance
|
||||
bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment
|
||||
bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment
|
||||
bool m_constPoolEntry : 1; // Constant pool variable
|
||||
bool m_covergroupRefMember : 1; // Persistent covergroup ref/const ref argument
|
||||
bool m_embeddedCovergroup : 1; // Instance variable an embedded covergroup declares
|
||||
bool m_attrFileDescr : 1; // File descriptor
|
||||
bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration
|
||||
bool m_icoMaybeWritten : 1; // Design might write this input signal - for ico change detect
|
||||
@@ -2259,10 +2325,11 @@ class AstVar final : public AstNode {
|
||||
bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
|
||||
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
|
||||
bool m_hasUserInit : 1; // Has initial assignment by user at parse time
|
||||
bool m_isStatic : 1; // Static C variable (for Verilog see instead lifetime())
|
||||
bool m_isPulldown : 1; // Tri0
|
||||
bool m_isPullup : 1; // Tri1
|
||||
bool m_isIfaceParent : 1; // dtype is reference to interface present in this module
|
||||
bool m_isIfaceArraySplit : 1; // Interface array that was split into element variables by
|
||||
// V3Param, until V3Width removes it
|
||||
bool m_isInternal : 1; // Internal state, don't add to method pinter
|
||||
bool m_isIfaceParam : 1; // Parameter belongs to an interface/modport
|
||||
bool m_isDpiOpenArray : 1; // DPI import open array
|
||||
@@ -2312,11 +2379,11 @@ class AstVar final : public AstNode {
|
||||
m_attrSFormat = false;
|
||||
m_attrSplitVar = false;
|
||||
m_attrFsmState = false;
|
||||
m_attrFsmRegisterWrapper = false;
|
||||
m_attrFsmResetArc = false;
|
||||
m_attrFsmArcInclCond = false;
|
||||
m_constPoolEntry = false;
|
||||
m_covergroupRefMember = false;
|
||||
m_embeddedCovergroup = false;
|
||||
m_attrFileDescr = false;
|
||||
m_gotNansiType = false;
|
||||
m_icoMaybeWritten = false;
|
||||
@@ -2324,10 +2391,10 @@ class AstVar final : public AstNode {
|
||||
m_isContinuously = false;
|
||||
m_hasStrengthAssignment = false;
|
||||
m_hasUserInit = false;
|
||||
m_isStatic = false;
|
||||
m_isPulldown = false;
|
||||
m_isPullup = false;
|
||||
m_isIfaceParent = false;
|
||||
m_isIfaceArraySplit = false;
|
||||
m_isInternal = false;
|
||||
m_isIfaceParam = false;
|
||||
m_isDpiOpenArray = false;
|
||||
@@ -2440,10 +2507,10 @@ public:
|
||||
string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument
|
||||
string dpiTmpVarType(const string& varName) const;
|
||||
// Return Verilator internal type for argument: CData, SData, IData, WData
|
||||
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
|
||||
bool asRef = false, bool constRef = false) const;
|
||||
string vlArgType(bool named, bool forReturn, bool forFunc, bool asRef = false,
|
||||
bool constRef = false) const;
|
||||
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
|
||||
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
||||
string vlEnumDir(bool forMember = false) 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(); }
|
||||
@@ -2463,13 +2530,16 @@ public:
|
||||
void attrSFormat(bool flag) { m_attrSFormat = flag; }
|
||||
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
|
||||
void attrFsmState(bool flag) { m_attrFsmState = flag; }
|
||||
void attrFsmRegisterWrapper(bool flag) { m_attrFsmRegisterWrapper = flag; }
|
||||
void attrFsmResetArc(bool flag) { m_attrFsmResetArc = flag; }
|
||||
void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; }
|
||||
bool constPoolEntry() const { return m_constPoolEntry; }
|
||||
void setConstPoolEntry() { m_constPoolEntry = true; }
|
||||
bool covergroupRefMember() const { return m_covergroupRefMember; }
|
||||
void covergroupRefMember(bool flag) { m_covergroupRefMember = flag; }
|
||||
// Instance variable an embedded covergroup declares, of the covergroup's anonymous type
|
||||
// (IEEE 1800-2023 19.4)
|
||||
bool embeddedCovergroup() const { return m_embeddedCovergroup; }
|
||||
void embeddedCovergroup(bool flag) { m_embeddedCovergroup = flag; }
|
||||
void rand(const VRandAttr flag) { m_rand = flag; }
|
||||
void usedParam(bool flag) { m_usedParam = flag; }
|
||||
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
|
||||
@@ -2488,8 +2558,8 @@ public:
|
||||
void primaryIO(bool flag) { m_primaryIO = flag; }
|
||||
void isConst(bool flag) { m_isConst = flag; }
|
||||
void isContinuously(bool flag) { m_isContinuously = flag; }
|
||||
void isStatic(bool flag) { m_isStatic = flag; }
|
||||
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
|
||||
void isIfaceArraySplit(bool flag) { m_isIfaceArraySplit = flag; }
|
||||
void isInternal(bool flag) { m_isInternal = flag; }
|
||||
void isIfaceParam(bool flag) { m_isIfaceParam = flag; }
|
||||
void funcLocal(bool flag) {
|
||||
@@ -2577,6 +2647,7 @@ 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(); }
|
||||
@@ -2619,7 +2690,6 @@ public:
|
||||
bool isRand() const { return m_rand.isRand(); }
|
||||
bool isRandC() const { return m_rand.isRandC(); }
|
||||
bool isConst() const VL_MT_SAFE { return m_isConst; }
|
||||
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
|
||||
bool isLatched() const { return m_isLatched; }
|
||||
bool isFuncLocal() const { return m_funcLocal; }
|
||||
bool isFuncLocalSticky() const { return m_funcLocalSticky; }
|
||||
@@ -2632,7 +2702,6 @@ 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; }
|
||||
@@ -2656,13 +2725,6 @@ 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());
|
||||
@@ -2705,6 +2767,7 @@ 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; }
|
||||
@@ -2718,10 +2781,10 @@ class AstCoverOtherDecl final : public AstNodeCoverDecl {
|
||||
// Coverage analysis point declaration
|
||||
// Used for other than toggle types of coverage
|
||||
string m_linescov;
|
||||
string m_fsmVar;
|
||||
string m_fsmFrom;
|
||||
string m_fsmTo;
|
||||
string m_fsmTag;
|
||||
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)
|
||||
int m_offset; // Offset column numbers to uniq-ify IFs
|
||||
public:
|
||||
AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment,
|
||||
@@ -2998,8 +3061,12 @@ class AstClass final : public AstNodeModule {
|
||||
// MEMBERS
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
|
||||
// @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class
|
||||
string m_dtypeNameFull; // dtypeName(true) once frozen, as parameters may then be gone
|
||||
string m_dtypeNameShort; // dtypeName(false) once frozen, as parameters may then be gone
|
||||
uint32_t m_declTokenNum; // Declaration token number
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
bool m_hasRandVarsUpdate = false; // Has updateRandVars method,
|
||||
// which updates pointers to rand variables in clone()
|
||||
bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?)
|
||||
bool m_extended = false; // Is extension or extended by other classes
|
||||
bool m_interfaceClass = false; // Interface class
|
||||
@@ -3007,9 +3074,14 @@ class AstClass final : public AstNodeModule {
|
||||
bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class
|
||||
bool m_virtual = false; // Virtual class
|
||||
bool m_printedFrom = false; // This class is printed from i.e. is used as format arg.
|
||||
// Mutable, as only a recursion guard of the const dtypeName(), so also not dumped
|
||||
mutable bool m_dtypeNameBusy = false; // In dtypeName(), which a parameter may lead back to
|
||||
// Covergroup options (when m_covergroup is true)
|
||||
int m_cgAutoBinMax = -1; // option.auto_bin_max value (-1 = not set, use default 64)
|
||||
|
||||
string dtypeNameCalc(bool full) const; // dtypeName() as computed from the tree
|
||||
string dtypeNameScope() const; // Prefix of dtypeName(true) for the scope declaring the class
|
||||
|
||||
public:
|
||||
AstClass(FileLine* fl, const string& name, const string& libname)
|
||||
: ASTGEN_SUPER_Class(fl, name, libname)
|
||||
@@ -3028,6 +3100,8 @@ 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; }
|
||||
@@ -3042,6 +3116,16 @@ public:
|
||||
void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; }
|
||||
void markPrintedFrom() { m_printedFrom = true; }
|
||||
bool isPrintedFrom() const { return m_printedFrom; }
|
||||
// Name of the class as a data type, with the values of its parameters, e.g. 'Cls#(int,5)'.
|
||||
// With 'full', as for $typename (IEEE 1800-2023 20.6.1), prefixed with the scope declaring
|
||||
// the class, e.g. '$unit::Cls#(int,5)', and with the types of parameters in full.
|
||||
string dtypeName(bool full) const;
|
||||
// Fix dtypeName(), as V3WidthCommit moves parameter types to the type table
|
||||
void dtypeNameFreeze();
|
||||
// Whether dtypeName() is fixed, as for a class elaborated from the design
|
||||
bool dtypeNameFrozen() const { return !m_dtypeNameFull.empty(); }
|
||||
// Named by dtypeName(), as name() is internal for a specialization, unless still a template
|
||||
string prettyNameMsg() const override { return hasGParam() ? prettyName() : dtypeName(false); }
|
||||
// Covergroup options accessors
|
||||
int cgAutoBinMax() const { return m_cgAutoBinMax; }
|
||||
void cgAutoBinMax(int value) { m_cgAutoBinMax = value; }
|
||||
@@ -3111,6 +3195,8 @@ public:
|
||||
};
|
||||
class AstIface final : public AstNodeModule {
|
||||
// An interface declaration
|
||||
string m_dtypeNameFull; // dtypeName(true) once frozen, as parameter types may then be gone
|
||||
string m_dtypeNameShort; // dtypeName(false) once frozen, as parameter types may then be gone
|
||||
bool m_hasVirtualRef = false; // There exists a virtual interface reference for this interface
|
||||
public:
|
||||
AstIface(FileLine* fl, const string& name, const string& libname)
|
||||
@@ -3124,6 +3210,10 @@ public:
|
||||
bool timescaleMatters() const override { return false; }
|
||||
bool hasVirtualRef() const { return m_hasVirtualRef; }
|
||||
void setHasVirtualRef() { m_hasVirtualRef = true; }
|
||||
// Name of the interface as a data type, with the values of its parameters, e.g. 'ifc#(8)'
|
||||
string dtypeName(bool full) const;
|
||||
// Fix dtypeName(), as V3WidthCommit moves parameter types to the type table
|
||||
void dtypeNameFreeze();
|
||||
};
|
||||
class AstModule final : public AstNodeModule {
|
||||
// A module declaration
|
||||
@@ -3174,9 +3264,7 @@ public:
|
||||
: ASTGEN_SUPER_Package(fl, name, libname) {}
|
||||
ASTGEN_MEMBERS_AstPackage;
|
||||
string verilogKwd() const override { return "package"; }
|
||||
bool timescaleMatters() const override { return !isDollarUnit(); }
|
||||
static string dollarUnitName() { return AstNode::encodeName("$unit"); }
|
||||
bool isDollarUnit() const { return name() == dollarUnitName(); }
|
||||
bool timescaleMatters() const override { return !isDollarUnit() && !isConstPool(); }
|
||||
};
|
||||
class AstPrimitive final : public AstNodeModule {
|
||||
// A primitive declaration
|
||||
@@ -3328,6 +3416,14 @@ public:
|
||||
}
|
||||
int elementsConst() const VL_MT_STABLE { return hiConst() - loConst() + 1; }
|
||||
bool ascending() const { return leftConst() < rightConst(); }
|
||||
// Iterate indices from left to right inclusive, see VNumRange::seqLeftToRight()
|
||||
VNumRange::Sequence seqLeftToRight() const {
|
||||
return VNumRange{leftConst(), rightConst()}.seqLeftToRight();
|
||||
}
|
||||
// Iterate indices from right to left inclusive, see VNumRange::seqRightToLeft()
|
||||
VNumRange::Sequence seqRightToLeft() const {
|
||||
return VNumRange{leftConst(), rightConst()}.seqRightToLeft();
|
||||
}
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
virtual string emitC() { V3ERROR_NA_RETURN(""); }
|
||||
|
||||
+1
-30
@@ -549,29 +549,7 @@ public:
|
||||
bool isOutputter() override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
AstNodeCoverDecl* declp() const { return m_declp; } // Where defined
|
||||
};
|
||||
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
|
||||
void declp(AstNodeCoverDecl* nodep) { m_declp = nodep; }
|
||||
};
|
||||
class AstDeassign final : public AstNodeStmt {
|
||||
// Procedural 'deassign' statement
|
||||
@@ -1221,13 +1199,6 @@ 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:
|
||||
|
||||
+375
-280
File diff suppressed because it is too large
Load Diff
+11
-12
@@ -26,6 +26,8 @@
|
||||
|
||||
#include "V3Broken.h"
|
||||
|
||||
#include "V3ConstPool.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
@@ -234,18 +236,11 @@ private:
|
||||
const char* dp = descrp;
|
||||
for (AstNodeExpr* argp = argsp; argp; argp = VN_AS(argp->nextp(), NodeExpr)) {
|
||||
if (argp->fileline()->erroringOn()) return; // Intentionally skip all checks
|
||||
const AstNodeExpr* const lvalp = argp->cLValueTargetp();
|
||||
const VAccess access = [&]() -> VAccess {
|
||||
if (const AstVarRef* const varrefp = VN_CAST(lvalp, VarRef)) {
|
||||
return varrefp->access();
|
||||
}
|
||||
if (const AstMemberSel* const memberselp = VN_CAST(lvalp, MemberSel)) {
|
||||
return memberselp->access();
|
||||
}
|
||||
UASSERT_OBJ(!lvalp, argp, "Unknown LValue expression");
|
||||
// Not an LValue, so it's read-only
|
||||
return VAccess::READ;
|
||||
}();
|
||||
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':
|
||||
@@ -439,6 +434,10 @@ 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,6 +152,7 @@ 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;
|
||||
@@ -167,12 +168,12 @@ class CCtorsVisitor final : public VNVisitor {
|
||||
|
||||
if (v3Global.opt.coverage()) {
|
||||
V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE};
|
||||
for (AstNode* np = nodep->stmtsp(); np; np = np->nextp()) {
|
||||
for (AstNode *np = nodep->stmtsp(), *nextp; np; np = nextp) {
|
||||
nextp = 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());
|
||||
}
|
||||
}
|
||||
@@ -219,7 +220,6 @@ class CCtorsVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstConstPool*) override {}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
|
||||
@@ -86,6 +86,7 @@ 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);
|
||||
@@ -115,6 +116,7 @@ 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};
|
||||
|
||||
+24
-16
@@ -38,6 +38,7 @@
|
||||
|
||||
#include "V3Case.h"
|
||||
|
||||
#include "V3ConstPool.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
@@ -738,18 +739,15 @@ class CaseVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
VL_DO_DANGLING(tablep->deleteTree(), tablep); // findConst clones
|
||||
return vscp;
|
||||
return refp;
|
||||
}();
|
||||
|
||||
// Create the lookup table reference and index
|
||||
AstNodeExpr* const tableRefp
|
||||
= tableVscp ? static_cast<AstNodeExpr*>(new AstVarRef{flp, tableVscp, VAccess::READ})
|
||||
: static_cast<AstNodeExpr*>(tablep);
|
||||
// Create the lookup index
|
||||
AstNodeExpr* const caseExprp
|
||||
= new AstExtend{flp, nodep->exprp()->cloneTreePure(false), 32};
|
||||
AstNodeExpr* const scalep = new AstConst{flp, entryWidth};
|
||||
@@ -855,15 +853,14 @@ class CaseVisitor final : public VNVisitor {
|
||||
}
|
||||
|
||||
// Create the tables
|
||||
AstVarScope* const matchVscp = v3Global.rootp()->constPoolp()->findConst(matchp, true);
|
||||
AstVarScope* const tableVscp = v3Global.rootp()->constPoolp()->findTable(tablep);
|
||||
AstVarRef* const matchRefp = V3ConstPool::findConst(matchp);
|
||||
AstVarRef* const tableRefp = V3ConstPool::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, matchVscp};
|
||||
AstMatchMasked* const indexp = new AstMatchMasked{flp, caseExprp, matchRefp};
|
||||
AstNodeExpr* const entryp = new AstArraySel{flp, tableRefp, indexp};
|
||||
|
||||
return connectDecoderOutputs(nodep, entryp, "__VcaseDecoderOut");
|
||||
@@ -1004,11 +1001,22 @@ class CaseVisitor final : public VNVisitor {
|
||||
itemp->addCondsp(newCondp);
|
||||
}
|
||||
|
||||
// If there was no default, add a empty one, this greatly simplifies below code
|
||||
// If there was no default, make one up, this greatly simplifies below code
|
||||
// and constant propagation will just eliminate it for us later.
|
||||
if (!hasDefault) {
|
||||
nodep->addItemsp(new AstCaseItem{
|
||||
nodep->fileline(), new AstConst{nodep->fileline(), AstConst::BitTrue{}}, nullptr});
|
||||
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});
|
||||
}
|
||||
}
|
||||
|
||||
// Now build the IF statement tree
|
||||
|
||||
@@ -282,6 +282,11 @@ class CleanVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
ensureClean(nodep->condp());
|
||||
}
|
||||
void visit(AstSFormatArg* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->exprp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstSFormatF* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->exprsp());
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ class ClockVisitor final : public VNVisitor {
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
void visit(AstSenTree* nodep) override {
|
||||
pushDeletep(nodep->unlinkFrBack()); // No longer needed
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); // 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;
|
||||
std::list<AstCFunc*> m_slow;
|
||||
std::list<AstCFunc*> m_fast; // Functions that are not slow
|
||||
std::list<AstCFunc*> m_slow; // Functions marked for slow files
|
||||
};
|
||||
|
||||
// STATE
|
||||
|
||||
@@ -318,7 +318,6 @@ class ToStringVisitor final : public VNVisitorConst {
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstConstPool*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
public:
|
||||
|
||||
+27
-23
@@ -110,12 +110,12 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
||||
|
||||
class LeafInfo final { // Leaf node (either AstConst or AstVarRef)
|
||||
// MEMBERS
|
||||
bool m_polarity = true;
|
||||
bool m_polarity = true; // Invert result due to NOT
|
||||
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;
|
||||
const AstConst* m_constp = nullptr;
|
||||
AstVarRef* m_refp = nullptr; // Leaf's variable reference
|
||||
const AstConst* m_constp = nullptr; // Leaf's constant
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -187,9 +187,9 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
||||
};
|
||||
|
||||
struct BitPolarityEntry final { // Found bit polarity during iterate()
|
||||
LeafInfo m_info;
|
||||
bool m_polarity = false;
|
||||
int m_bit = 0;
|
||||
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
|
||||
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;
|
||||
int m_lsb;
|
||||
bool m_polarity; // Polarity the frozen node must match
|
||||
int m_lsb; // LSB position of the frozen node
|
||||
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;
|
||||
const size_t m_polaritiesSize;
|
||||
const size_t m_frozenSize;
|
||||
const unsigned m_ops;
|
||||
const bool m_polarity;
|
||||
bool m_restore = true;
|
||||
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
|
||||
|
||||
public:
|
||||
explicit Restorer(ConstBitOpTreeVisitor& visitor)
|
||||
@@ -1015,14 +1015,10 @@ class ConstVisitor final : public VNVisitor {
|
||||
const bool packReal) {
|
||||
const AstNodeDType* const dtypep = fromp->dtypep()->skipRefp();
|
||||
if (const AstUnpackArrayDType* const unpackDtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
const int left = unpackDtypep->left();
|
||||
const int right = unpackDtypep->right();
|
||||
const int step = left <= right ? 1 : -1;
|
||||
for (int idx = left;; idx += step) {
|
||||
for (const int idx : unpackDtypep->declRange().seqLeftToRight()) {
|
||||
AstArraySel* const selp
|
||||
= new AstArraySel{fromp->fileline(), fromp->cloneTreePure(false), idx};
|
||||
collectFixedAggregateTerms(selp, termps, packReal);
|
||||
if (idx == right) break;
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(fromp), fromp);
|
||||
} else if (const AstNodeUOrStructDType* const sdtypep
|
||||
@@ -1463,11 +1459,12 @@ 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
|
||||
// operand, V3Expand can create shifts wider than their inputs. Must use width(),
|
||||
// not widthMin(), as bits above widthMin() are not guaranteed to be zero.
|
||||
if (AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) {
|
||||
return scp->num().fitsInUInt()
|
||||
&& (lsb + scp->num().toUInt()
|
||||
>= static_cast<uint32_t>(shiftp->lhsp()->widthMin()));
|
||||
>= static_cast<uint32_t>(shiftp->lhsp()->width()));
|
||||
}
|
||||
}
|
||||
if (const AstMul* const mulp = VN_CAST(rhsp, Mul)) {
|
||||
@@ -3969,7 +3966,12 @@ class ConstVisitor final : public VNVisitor {
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return true;
|
||||
}
|
||||
void visit(AstSFormatArg* nodep) override { iterateChildren(nodep); }
|
||||
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(AstSFormatF* nodep) override {
|
||||
// Substitute constants into displays. The main point of this is to
|
||||
// simplify assertion methodologies which call functions with display's.
|
||||
@@ -4033,7 +4035,9 @@ class ConstVisitor final : public VNVisitor {
|
||||
: VFormatAttr{};
|
||||
if (VN_IS(subargp, Const)) { // Convert it
|
||||
const string out
|
||||
= constNumV(subargp).displayed(nodep, fmt, formatAttr);
|
||||
= formatAttr.isEnum()
|
||||
? constNumV(subargp).displayedEnum(fargp, fmt)
|
||||
: constNumV(subargp).displayed(nodep, fmt, formatAttr);
|
||||
UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
|
||||
<< subargp);
|
||||
// fmt = out w/ replace % with %% as it must later when
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Constant pool
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3PchAstMT.h"
|
||||
|
||||
#include "V3ConstPool.h"
|
||||
|
||||
//######################################################################
|
||||
// V3ConstPool
|
||||
|
||||
bool V3ConstPool::equalMap(const AstInitArray* ap, const AstInitArray* bp) {
|
||||
// Associative array initializers must have equivalent types, defaults, and entries.
|
||||
// As in 'equalTable', compare by value, rather than by tree structure.
|
||||
const AstAssocArrayDType* const aDTypep = VN_AS(ap->dtypep(), AssocArrayDType);
|
||||
const AstAssocArrayDType* const bDTypep = VN_AS(bp->dtypep(), AssocArrayDType);
|
||||
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false;
|
||||
if (!aDTypep->keyDTypep()->sameTree(bDTypep->keyDTypep())) return false;
|
||||
// The default is optional, but must be the same if present
|
||||
const AstNode* const aDefaultp = ap->defaultp();
|
||||
const AstNode* const bDefaultp = bp->defaultp();
|
||||
UASSERT_OBJ(!aDefaultp || VN_IS(aDefaultp, Const), ap, "Const pool map default not Const");
|
||||
UASSERT_OBJ(!bDefaultp || VN_IS(bDefaultp, Const), bp, "Const pool map default not Const");
|
||||
if (!aDefaultp != !bDefaultp) return false;
|
||||
if (aDefaultp && !aDefaultp->sameTree(bDefaultp)) return false;
|
||||
// Compare the entries, which are ordered by key. Note an entry explicitly set to the
|
||||
// default value makes maps compare different, which is safe, but misses merging them.
|
||||
const AstInitArray::KeyItemMap& aMap = ap->map();
|
||||
const AstInitArray::KeyItemMap& bMap = bp->map();
|
||||
if (aMap.size() != bMap.size()) return false;
|
||||
auto bIt = bMap.cbegin();
|
||||
for (const auto& aItem : aMap) {
|
||||
const AstNode* const aValuep = aItem.second->valuep();
|
||||
const AstNode* const bValuep = bIt->second->valuep();
|
||||
UASSERT_OBJ(VN_IS(aValuep, Const), aValuep, "Const pool map item not Const");
|
||||
UASSERT_OBJ(VN_IS(bValuep, Const), bValuep, "Const pool map item not Const");
|
||||
if (aItem.first != bIt->first) return false;
|
||||
if (!aValuep->sameTree(bValuep)) return false;
|
||||
++bIt;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool V3ConstPool::equalTable(const AstInitArray* ap, const AstInitArray* bp) {
|
||||
// Unpacked array initializers must have equivalent values
|
||||
// Note, sadly we can't just call ap->sameTree(pb), because both:
|
||||
// - the dtypes might be different instances
|
||||
// - the default/inititem children might be in different order yet still yield the same table
|
||||
// See note in AstInitArray::same about the same. This function instead compares by initializer
|
||||
// value, rather than by tree structure.
|
||||
const AstUnpackArrayDType* const aDTypep = VN_AS(ap->dtypep(), UnpackArrayDType);
|
||||
const AstUnpackArrayDType* const bDTypep = VN_AS(bp->dtypep(), UnpackArrayDType);
|
||||
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false;
|
||||
// Compare the range by value, as the bounds might be differently typed constants
|
||||
if (!(aDTypep->declRange() == bDTypep->declRange())) return false;
|
||||
// Compare initializer arrays by value. Note this is only called when they hash the same.
|
||||
const uint64_t size = aDTypep->elementsConst();
|
||||
for (uint64_t n = 0; n < size; ++n) {
|
||||
const AstNode* const valAp = ap->getIndexDefaultedValuep(n);
|
||||
const AstNode* const valBp = bp->getIndexDefaultedValuep(n);
|
||||
UASSERT_OBJ(VN_IS(valAp, Const), valAp, "Const pool table item not Const");
|
||||
UASSERT_OBJ(VN_IS(valBp, Const), valBp, "Const pool table item not Const");
|
||||
if (!valAp->sameTree(valBp)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t V3ConstPool::hashTable(const AstInitArray* initp) {
|
||||
// Hash what 'equalTable' compares: the element type, the range by value, and the values
|
||||
const AstUnpackArrayDType* const dtypep = VN_AS(initp->dtypep(), UnpackArrayDType);
|
||||
V3Hash hash = V3Hasher::uncachedHash(dtypep->subDTypep());
|
||||
hash += dtypep->left();
|
||||
hash += dtypep->right();
|
||||
const uint64_t size = dtypep->elementsConst();
|
||||
for (uint64_t n = 0; n < size; ++n) {
|
||||
hash += V3Hasher::uncachedHash(initp->getIndexDefaultedValuep(n));
|
||||
}
|
||||
return hash.value();
|
||||
}
|
||||
|
||||
template <typename T_Set, typename T_Init>
|
||||
AstVarRef* V3ConstPool::findOrCreate(T_Set& set, uint32_t& nextr, const char* prefixp,
|
||||
T_Init* initp) {
|
||||
// Rebuild the cache if invalidated
|
||||
if (VL_UNLIKELY(!m_cacheValid)) {
|
||||
m_cacheValid = true;
|
||||
m_scopep = nullptr;
|
||||
m_consts.clear();
|
||||
m_maps.clear();
|
||||
m_tables.clear();
|
||||
m_varScopes.clear();
|
||||
AstPackage* const pkgp = v3Global.rootp()->constPoolPkgp();
|
||||
for (AstNode* nodep = pkgp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (AstScope* const scopep = VN_CAST(nodep, Scope)) {
|
||||
if (m_stage == Stage::SCOPED) m_scopep = scopep;
|
||||
continue;
|
||||
}
|
||||
// Otherwise must be a variable
|
||||
AstVar* const varp = VN_AS(nodep, Var);
|
||||
UASSERT_OBJ(varp->constPoolEntry(), varp, "Unmarked variable in constant pool");
|
||||
if (VN_IS(varp->valuep(), Const)) {
|
||||
m_consts.insert(varp);
|
||||
} else if (const AstInitArray* const arrayp = VN_CAST(varp->valuep(), InitArray)) {
|
||||
if (VN_IS(arrayp->dtypep(), AssocArrayDType)) {
|
||||
m_maps.insert(varp);
|
||||
} else {
|
||||
m_tables.insert(varp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_scopep) {
|
||||
for (AstVarScope* vscp = m_scopep->varsp(); vscp;
|
||||
vscp = VN_AS(vscp->nextp(), VarScope)) {
|
||||
m_varScopes.insert({vscp->varp(), vscp});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileLine* const flp = initp->fileline();
|
||||
AstNodeDType* const dtypep = initp->dtypep();
|
||||
|
||||
// Look up/create the variable with this value
|
||||
const auto pair = set.insertLazy(initp, [&]() {
|
||||
const std::string name = prefixp + std::to_string(nextr++);
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtypep};
|
||||
varp->setConstPoolEntry();
|
||||
varp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
varp->isConst(true);
|
||||
varp->valuep(initp->cloneTree(false));
|
||||
v3Global.rootp()->constPoolPkgp()->addStmtsp(varp);
|
||||
return varp;
|
||||
});
|
||||
AstVar* const varp = *pair.first;
|
||||
|
||||
// Create a read reference to the entry, of the right form for the current stage
|
||||
AstVarRef* refp = nullptr;
|
||||
switch (m_stage) {
|
||||
case Stage::UNSCOPED: {
|
||||
// Before V3Scope, reference via the package, as any other package variable
|
||||
AstPackage* const pkgp = v3Global.rootp()->constPoolPkgp();
|
||||
refp = new AstVarRef{flp, pkgp, varp, VAccess::READ};
|
||||
break;
|
||||
}
|
||||
case Stage::SCOPED: {
|
||||
// While scoped, reference the VarScope, creating it for a new entry, or if removed
|
||||
const auto vscpPair = m_varScopes.insertLazy(varp, [&]() {
|
||||
AstVarScope* const vscp = new AstVarScope{varp->fileline(), m_scopep, varp};
|
||||
m_scopep->addVarsp(vscp);
|
||||
return std::pair<const AstVar*, AstVarScope*>{varp, vscp};
|
||||
});
|
||||
refp = new AstVarRef{flp, vscpPair.first->second, VAccess::READ};
|
||||
break;
|
||||
}
|
||||
case Stage::DESCOPED: {
|
||||
// After V3Descope, no VarScope is needed
|
||||
refp = new AstVarRef{flp, varp, VAccess::READ};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The entry might have been created with a different, but compatible dtype
|
||||
refp->dtypep(dtypep);
|
||||
return refp;
|
||||
}
|
||||
|
||||
AstVarRef* V3ConstPool::findConst(AstConst* initp) {
|
||||
V3ConstPool& self = instance();
|
||||
return self.findOrCreate(self.m_consts, self.m_nextConst, "CONST_", initp);
|
||||
}
|
||||
|
||||
AstVarRef* V3ConstPool::findMap(AstInitArray* initp) {
|
||||
UASSERT_OBJ(VN_IS(initp->dtypep(), AssocArrayDType), initp,
|
||||
"Const pool map must have associative array dtype");
|
||||
V3ConstPool& self = instance();
|
||||
return self.findOrCreate(self.m_maps, self.m_nextMap, "MAP_", initp);
|
||||
}
|
||||
|
||||
AstVarRef* V3ConstPool::findTable(AstInitArray* initp) {
|
||||
UASSERT_OBJ(VN_IS(initp->dtypep(), UnpackArrayDType), initp,
|
||||
"Const pool table must have unpacked array dtype");
|
||||
V3ConstPool& self = instance();
|
||||
return self.findOrCreate(self.m_tables, self.m_nextTable, "TABLE_", initp);
|
||||
}
|
||||
|
||||
AstVarRef* V3ConstPool::find(AstNodeExpr* initp) {
|
||||
if (AstConst* const constp = VN_CAST(initp, Const)) return findConst(constp);
|
||||
if (AstInitArray* const arrayp = VN_CAST(initp, InitArray)) {
|
||||
if (VN_IS(arrayp->dtypep(), AssocArrayDType)) return findMap(arrayp);
|
||||
if (VN_IS(arrayp->dtypep(), UnpackArrayDType)) return findTable(arrayp);
|
||||
}
|
||||
initp->v3fatalSrc("Unhandled constant pool initializer");
|
||||
return nullptr; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
const char* V3ConstPool::broken() {
|
||||
const V3ConstPool& self = instance();
|
||||
if (!self.m_cacheValid) return nullptr;
|
||||
BROKEN_RTN(self.m_scopep && !self.m_scopep->brokeExists());
|
||||
for (const AstVar* const varp : self.m_consts) BROKEN_RTN(!varp->brokeExists());
|
||||
for (const AstVar* const varp : self.m_maps) BROKEN_RTN(!varp->brokeExists());
|
||||
for (const AstVar* const varp : self.m_tables) BROKEN_RTN(!varp->brokeExists());
|
||||
for (const auto& pair : self.m_varScopes) {
|
||||
BROKEN_RTN(!pair.first->brokeExists());
|
||||
BROKEN_RTN(!pair.second->brokeExists());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Constant pool
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// V3ConstPool finds or creates entries in the constant pool package
|
||||
// (AstNetlist::constPoolPkgp()), which holds read-only static data, i.e.:
|
||||
// constants, maps and tables, shared across the whole design. Entries are
|
||||
// ordinary package variables, deduplicated by value.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3CONSTPOOL_H_
|
||||
#define VERILATOR_V3CONSTPOOL_H_
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3HashTable.h"
|
||||
#include "V3Hasher.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3ConstPool final {
|
||||
friend class V3Global; // Owns the instance, needs constructor access
|
||||
|
||||
// TYPES
|
||||
// Scoping stage of the design, which determines how entries are referenced
|
||||
enum class Stage : uint8_t {
|
||||
UNSCOPED, // Before V3Scope: reference via the package
|
||||
SCOPED, // Between V3Scope and V3Descope: reference via the VarScope
|
||||
DESCOPED // After V3Descope: plain reference
|
||||
};
|
||||
// Hash and equality of constant entries in 'm_consts'
|
||||
struct ConstHash final {
|
||||
size_t operator()(const AstVar* varp) const {
|
||||
return (*this)(VN_AS(varp->valuep(), Const));
|
||||
}
|
||||
size_t operator()(const AstConst* initp) const { return initp->num().toHash().value(); }
|
||||
};
|
||||
struct ConstEqual final {
|
||||
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
|
||||
return (*this)(ap, VN_AS(bp->valuep(), Const)); // Only on collision while rebuild
|
||||
} // LCOV_EXCL_STOP
|
||||
bool operator()(const AstVar* varp, const AstConst* initp) const {
|
||||
// Compare by value, which also checks the width. The dtype is ignored.
|
||||
return VN_AS(varp->valuep(), Const)->num().isCaseEq(initp->num());
|
||||
}
|
||||
};
|
||||
// Hash and equality of associative array entries in 'm_maps'
|
||||
struct MapHash final {
|
||||
size_t operator()(const AstVar* varp) const {
|
||||
return (*this)(VN_AS(varp->valuep(), InitArray));
|
||||
}
|
||||
size_t operator()(const AstInitArray* initp) const {
|
||||
return V3Hasher::uncachedHash(initp).value();
|
||||
}
|
||||
};
|
||||
struct MapEqual final {
|
||||
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
|
||||
return (*this)(ap, VN_AS(bp->valuep(), InitArray)); // Only on collision while rebuild
|
||||
} // LCOV_EXCL_STOP
|
||||
bool operator()(const AstVar* varp, const AstInitArray* initp) const {
|
||||
return equalMap(VN_AS(varp->valuep(), InitArray), initp);
|
||||
}
|
||||
};
|
||||
// Hash and equality of unpacked array entries in 'm_tables'
|
||||
struct TableHash final {
|
||||
size_t operator()(const AstVar* varp) const {
|
||||
return (*this)(VN_AS(varp->valuep(), InitArray));
|
||||
}
|
||||
size_t operator()(const AstInitArray* initp) const { //
|
||||
return hashTable(initp);
|
||||
}
|
||||
};
|
||||
struct TableEqual final {
|
||||
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
|
||||
return (*this)(ap, VN_AS(bp->valuep(), InitArray)); // Only on collision while rebuild
|
||||
} // LCOV_EXCL_STOP
|
||||
bool operator()(const AstVar* varp, const AstInitArray* initp) const {
|
||||
return equalTable(VN_AS(varp->valuep(), InitArray), initp);
|
||||
}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
Stage m_stage = Stage::UNSCOPED; // Scoping stage of the design, see setScoped()/setDescoped()
|
||||
bool m_cacheValid = false; // Cache below is up to date
|
||||
AstScope* m_scopep = nullptr; // Scope of the constant pool, between V3Scope and V3Descope
|
||||
V3HashSet<AstVar*, ConstHash, ConstEqual> m_consts; // Packed constants
|
||||
V3HashSet<AstVar*, MapHash, MapEqual> m_maps; // Associative array constants (maps)
|
||||
V3HashSet<AstVar*, TableHash, TableEqual> m_tables; // Unpacked array constants (tables)
|
||||
V3HashMap<const AstVar*, AstVarScope*> m_varScopes; // VarScope of each entry iff m_scopep
|
||||
uint32_t m_nextConst = 0; // Sequence number for naming
|
||||
uint32_t m_nextMap = 0; // Sequence number for naming
|
||||
uint32_t m_nextTable = 0; // Sequence number for naming
|
||||
|
||||
// METHODS
|
||||
static V3ConstPool& instance() { return *v3Global.constPoolp(); }
|
||||
static bool equalMap(const AstInitArray* ap, const AstInitArray* bp);
|
||||
static bool equalTable(const AstInitArray* ap, const AstInitArray* bp);
|
||||
static size_t hashTable(const AstInitArray* initp);
|
||||
// Find an entry equal to the given initializer in the given set, or create one named from
|
||||
// the given prefix and sequence number, and return a read reference to it
|
||||
template <typename T_Set, typename T_Init>
|
||||
AstVarRef* findOrCreate(T_Set& set, uint32_t& nextr, const char* prefixp, T_Init* initp);
|
||||
|
||||
// CONSTRUCTORS - only V3Global creates and deletes the instance
|
||||
V3ConstPool() = default;
|
||||
~V3ConstPool() = default;
|
||||
VL_UNCOPYABLE(V3ConstPool);
|
||||
VL_UNMOVABLE(V3ConstPool);
|
||||
|
||||
public:
|
||||
// STATIC METHODS
|
||||
// Find a constant packed variable with the given value, or create one if one does not
|
||||
// already exist, and return a read reference to it.
|
||||
// The reference has the same dtype as initp.
|
||||
// The referenced variable *might* have a different, but compatible dtype.
|
||||
static AstVarRef* findConst(AstConst* initp);
|
||||
// Find a constant associative array (map) with the given value, or create one if one does
|
||||
// not already exist, and return a read reference to it.
|
||||
// The reference has the same dtype as initp.
|
||||
// The referenced variable *might* have a different, but compatible dtype.
|
||||
static AstVarRef* findMap(AstInitArray* initp);
|
||||
// Find a constant unpacked array (table) with the given value, or create one if one does
|
||||
// not already exist, and return a read reference to it.
|
||||
// The reference has the same dtype as initp.
|
||||
// The referenced variable *might* have a different, but compatible dtype.
|
||||
static AstVarRef* findTable(AstInitArray* initp);
|
||||
// Dispatch to the above based on the kind of initializer.
|
||||
static AstVarRef* find(AstNodeExpr* initp);
|
||||
// Invalidate the lookup cache. This must be called after deleting any Vars from the pool.
|
||||
static void invalidateCache() { instance().m_cacheValid = false; }
|
||||
// Notify that the design has been scoped, called by V3Scope
|
||||
static void setScoped() {
|
||||
V3ConstPool& self = instance();
|
||||
UASSERT(self.m_stage == Stage::UNSCOPED, "Constant pool scoped twice");
|
||||
self.m_stage = Stage::SCOPED;
|
||||
invalidateCache();
|
||||
}
|
||||
// Notify that the design has been descoped, called by V3Descope
|
||||
static void setDescoped() {
|
||||
V3ConstPool& self = instance();
|
||||
UASSERT(self.m_stage == Stage::SCOPED, "Constant pool descoped when not scoped");
|
||||
self.m_stage = Stage::DESCOPED;
|
||||
invalidateCache();
|
||||
}
|
||||
// Check the lookup cache refers only to existing nodes, for V3Broken
|
||||
static const char* broken();
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
+6
-6
@@ -709,8 +709,8 @@ public:
|
||||
// Tracks what matches are known to hit against V3ControlScopeTraceEntries
|
||||
class V3ControlScopeTraceEntryMatch final {
|
||||
public:
|
||||
const V3ControlScopeTraceEntry* m_entryp;
|
||||
const string m_scopepart;
|
||||
const V3ControlScopeTraceEntry* m_entryp; // Entry the match refers to
|
||||
const string m_scopepart; // Scope prefix being matched against the entry
|
||||
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;
|
||||
FileLine* const m_flp;
|
||||
const int m_workers; // Number of workers requested for the hierarchical block
|
||||
FileLine* const m_flp; // Location the worker count was specified at
|
||||
|
||||
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;
|
||||
uint8_t m_mode = NONE; // Kind of profile_data records is currently active
|
||||
std::unordered_map<string, V3ControlResolverHierWorkerEntry> m_hierWorkers;
|
||||
FileLine* m_profileFileLine = nullptr;
|
||||
FileLine* m_profileFileLine = nullptr; // Location profile_data was read from
|
||||
|
||||
V3ControlResolver() = default;
|
||||
~V3ControlResolver() = default;
|
||||
|
||||
+4
-3
@@ -199,6 +199,7 @@ 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);
|
||||
@@ -460,7 +461,7 @@ class CoverageVisitor final : public VNVisitor {
|
||||
if (const AstBasicDType* const basicp = VN_CAST(dtypep, BasicDType)) {
|
||||
toggleVarBottom(above, varp, basicp->nrange());
|
||||
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
|
||||
for (const int index_docs : adtypep->declRange().seqLoToHi()) {
|
||||
const int index_code = index_docs - adtypep->lo();
|
||||
ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]",
|
||||
new AstArraySel{varp->fileline(),
|
||||
@@ -471,7 +472,7 @@ class CoverageVisitor final : public VNVisitor {
|
||||
newent.cleanup();
|
||||
}
|
||||
} else if (const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType)) {
|
||||
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
|
||||
for (const int index_docs : adtypep->declRange().seqLoToHi()) {
|
||||
const AstNodeDType* const subtypep = adtypep->subDTypep()->skipRefp();
|
||||
const int index_code = index_docs - adtypep->lo();
|
||||
ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]",
|
||||
@@ -1053,7 +1054,7 @@ class CoverageVisitor final : public VNVisitor {
|
||||
unrolledp = new T_Oper{fl, selp, unrolledp};
|
||||
}
|
||||
iterate(unrolledp);
|
||||
pushDeletep(unrolledp);
|
||||
VL_DO_DANGLING(pushDeletep(unrolledp), unrolledp);
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
lineTrack(nodep);
|
||||
|
||||
+2349
-773
File diff suppressed because it is too large
Load Diff
+47
-52
@@ -37,6 +37,7 @@
|
||||
|
||||
#include "V3Dead.h"
|
||||
|
||||
#include "V3ConstPool.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
@@ -246,6 +247,7 @@ class DeadVisitor final : public VNVisitor {
|
||||
// Entire Netlist:
|
||||
// AstNodeModule::user1() -> int. Count of number of cells referencing this module.
|
||||
// AstVar::user1() -> int. Count of number of references
|
||||
// AstVar::user2() -> bool. Is a formal function argument
|
||||
// AstVarScope::user1() -> int. Count of number of references
|
||||
// AstNodeDType::user1() -> int. Count of number of references
|
||||
const VNUser1InUse m_inuser1;
|
||||
@@ -279,6 +281,7 @@ class DeadVisitor final : public VNVisitor {
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstForeachHeader* m_foreachHeaderp = nullptr; // Current foreach header
|
||||
AstNode* m_containingFTaskRefp = nullptr; // Parent of ftaskref (e.g. task/module)
|
||||
bool m_inFTask = false; // Under an AstNodeFTask
|
||||
|
||||
// STATE - Statistic tracking
|
||||
VDouble0 m_statFTasksDemoted;
|
||||
@@ -316,6 +319,12 @@ class DeadVisitor final : public VNVisitor {
|
||||
m_dtypeElimsp.push_back(nodep);
|
||||
}
|
||||
if (VN_IS(m_modp, Package) || VN_IS(m_modp, Class)) m_dtypePkgsp.emplace(nodep, m_modp);
|
||||
// A struct type moved to the type table refers to its package via classOrPackagep
|
||||
if (const AstNodeUOrStructDType* const sdtypep = VN_CAST(nodep, NodeUOrStructDType)) {
|
||||
if (sdtypep->classOrPackagep() && sdtypep->classOrPackagep() != m_modp) {
|
||||
m_dtypePkgsp.emplace(nodep, sdtypep->classOrPackagep());
|
||||
}
|
||||
}
|
||||
if (AstNode* const subnodep = nodep->virtRefDTypep()) subnodep->user1Inc();
|
||||
if (AstNode* const subnodep = nodep->virtRefDType2p()) subnodep->user1Inc();
|
||||
}
|
||||
@@ -351,6 +360,16 @@ 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);
|
||||
@@ -359,6 +378,8 @@ 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);
|
||||
}
|
||||
@@ -400,6 +421,11 @@ class DeadVisitor final : public VNVisitor {
|
||||
checkAll(nodep);
|
||||
needsTask(nodep->ftaskp(), m_containingFTaskRefp);
|
||||
}
|
||||
void visit(AstModportVarRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->varp()) nodep->varp()->user1Inc();
|
||||
}
|
||||
void visit(AstRefDType* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkDType(nodep);
|
||||
@@ -505,6 +531,11 @@ 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 {
|
||||
@@ -544,7 +575,9 @@ class DeadVisitor final : public VNVisitor {
|
||||
m_graph.findNewRemovableVertex(nodep, removable);
|
||||
//
|
||||
VL_RESTORER(m_containingFTaskRefp);
|
||||
VL_RESTORER(m_inFTask);
|
||||
m_containingFTaskRefp = nodep;
|
||||
m_inFTask = true;
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->isVirtual()) m_graph.funcVirtual(nodep);
|
||||
@@ -641,7 +674,8 @@ class DeadVisitor final : public VNVisitor {
|
||||
nextmodp = VN_AS(modp->nextp(), NodeModule);
|
||||
// Keep $unit until m_elimCells stages. Note v3Global.opt.serializeOnly()
|
||||
// won't reach this stage, and will always have an empty $unit. That's ok.
|
||||
const bool keep = !m_elimCells && modp == v3Global.rootp()->dollarUnitPkgp();
|
||||
// The constant pool is always kept, entries might be created later.
|
||||
const bool keep = (!m_elimCells && modp->isDollarUnit()) || modp->isConstPool();
|
||||
if (modp->dead() || (!modp->isTop() && modp->user1() == 0 && !keep)) {
|
||||
// > 2 because L1 is the wrapper, L2 is the top user module
|
||||
UINFO(4, " Dead module " << modp);
|
||||
@@ -652,9 +686,7 @@ class DeadVisitor final : public VNVisitor {
|
||||
cellp->modp()->user1Inc(-1);
|
||||
});
|
||||
}
|
||||
if (modp == v3Global.rootp()->dollarUnitPkgp()) {
|
||||
v3Global.rootp()->dollarUnitPkgp(nullptr);
|
||||
}
|
||||
if (modp->isDollarUnit()) v3Global.rootp()->dollarUnitPkgp(nullptr);
|
||||
deleting(modp);
|
||||
retry = true;
|
||||
}
|
||||
@@ -663,7 +695,7 @@ class DeadVisitor final : public VNVisitor {
|
||||
}
|
||||
bool mightElimVar(const AstVar* nodep) const {
|
||||
if (nodep->isSigPublic()) return false; // Can't elim publics!
|
||||
if (nodep->isIO() || nodep->isClassMember() || nodep->sensIfacep()) return false;
|
||||
if (nodep->isPrimaryIO() || nodep->isClassMember() || nodep->sensIfacep()) return false;
|
||||
if (nodep->isTemp() && !nodep->isTrace()) return true;
|
||||
return m_elimUserVars; // Post-Trace can kill most anything
|
||||
}
|
||||
@@ -715,6 +747,8 @@ 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
|
||||
@@ -767,48 +801,10 @@ 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 elimTopIfaces, bool elimTasks)
|
||||
bool elimCells, bool elimTasks)
|
||||
: m_elimUserVars{elimUserVars}
|
||||
, m_elimDTypes{elimDTypes}
|
||||
, m_elimCells{elimCells}
|
||||
@@ -843,7 +839,6 @@ 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
|
||||
@@ -852,7 +847,7 @@ public:
|
||||
// We may have removed some datatypes, cleanup
|
||||
nodep->typeTablep()->repairCache();
|
||||
VIsCached::clearCacheTree(); // Removing assignments may affect isPure
|
||||
nodep->constPoolp()->rebuildVarScopesAndCache();
|
||||
V3ConstPool::invalidateCache(); // Might have deleted constant pool entries
|
||||
}
|
||||
~DeadVisitor() override {
|
||||
V3Stats::addStatSum("Optimizations, FTasks, virtual-to-nonvirtual demotion",
|
||||
@@ -870,32 +865,32 @@ public:
|
||||
|
||||
void V3Dead::deadifyModules(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ // node, elimUserVars, elimDTypes, elimScopes, elimCells, elimTopIfaces
|
||||
DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported(), false};
|
||||
{ // node, elimUserVars, elimDTypes, elimScopes, elimCells, elimTasks
|
||||
DeadVisitor{nodep, false, false, false, false, false};
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeEitherLevel() >= 6);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ DeadVisitor{nodep, false, true, false, false, false, true}; } // Destruct before checking
|
||||
{ DeadVisitor{nodep, false, true, 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, false}; } // Destruct before checking
|
||||
{ DeadVisitor{nodep, false, true, true, 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, false, true}; } // Destruct before checking
|
||||
{ DeadVisitor{nodep, true, true, false, true, true}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ DeadVisitor{nodep, true, true, true, true, false, true}; } // Destruct before checking
|
||||
{ DeadVisitor{nodep, true, true, true, true, true}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
+41
-15
@@ -300,6 +300,7 @@ 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)
|
||||
@@ -820,7 +821,7 @@ class DelayedVisitor final : public VNVisitor {
|
||||
m_prevVscp = vscp;
|
||||
|
||||
// Delete original NBA
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
|
||||
// Scheme::FlagUnique
|
||||
@@ -870,7 +871,7 @@ class DelayedVisitor final : public VNVisitor {
|
||||
ifp->addThensp(new AstAssign{flp, capturedLhsp, capturedRhsp});
|
||||
|
||||
// Delete original NBA
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
|
||||
// Scheme::ValueQueuePartial/Scheme::ValueQueueWhole
|
||||
@@ -1020,7 +1021,7 @@ class DelayedVisitor final : public VNVisitor {
|
||||
nodep->addHereThisAsNext(callp->makeStmt());
|
||||
|
||||
// Delete original NBA
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
|
||||
// Record where a variable is assigned
|
||||
@@ -1190,6 +1191,7 @@ 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);
|
||||
@@ -1241,6 +1243,8 @@ 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);
|
||||
}
|
||||
@@ -1266,8 +1270,15 @@ class DelayedVisitor final : public VNVisitor {
|
||||
cstmtp->add(");");
|
||||
|
||||
AstNode* newp = cstmtp;
|
||||
if (nodep->isDelayed()) {
|
||||
const AstVarRef* const vrefp = VN_AS(eventp, VarRef);
|
||||
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()) {
|
||||
const std::string newvarname = "__Vdly__" + vrefp->varp()->shortName();
|
||||
AstVarScope* const dlyvscp
|
||||
= createTemp(flp, vrefp->varScopep()->scopep(), newvarname, 1);
|
||||
@@ -1276,20 +1287,35 @@ 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);
|
||||
ifp->addThensp(newp);
|
||||
}
|
||||
AstIf* const ifp = new AstIf{flp, dlyRef(VAccess::READ)};
|
||||
postp->addStmtsp(ifp);
|
||||
|
||||
UASSERT_OBJ(m_activep, nodep, "No active to handle FireEvent");
|
||||
AstActive* const activep = new AstActive{flp, "nba-event", m_activep->sentreep()};
|
||||
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);
|
||||
m_activep->addNextHere(activep);
|
||||
activep->addStmtsp(prep);
|
||||
activep->addStmtsp(postp);
|
||||
|
||||
newp = new AstAssign{flp, dlyRef(VAccess::WRITE),
|
||||
|
||||
+4
-1
@@ -110,7 +110,10 @@ 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");
|
||||
if (m_cfuncp->isStatic()) {
|
||||
// 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()) {
|
||||
UINFO(5, "Mark non-public due to " << nodep);
|
||||
m_cfuncp->isStatic(false);
|
||||
}
|
||||
|
||||
+4
-3
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "V3Descope.h"
|
||||
|
||||
#include "V3ConstPool.h"
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
@@ -159,8 +160,7 @@ 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->isWritable() ? VAccess::WRITE : VAccess::READ};
|
||||
portp->fileline(), portp, portp->direction().pinAccess()};
|
||||
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() == v3Global.rootp()->constPoolp()->modp()) {
|
||||
} else if (scopep->modp()->isConstPool()) {
|
||||
// Reference to constant pool value need no self pointer
|
||||
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
|
||||
} else {
|
||||
@@ -295,5 +295,6 @@ public:
|
||||
void V3Descope::descopeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ DescopeVisitor{nodep}; } // Destruct before checking
|
||||
V3ConstPool::setDescoped();
|
||||
V3Global::dumpCheckGlobalTree("descope", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
+35
-25
@@ -21,6 +21,7 @@
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
@@ -31,6 +32,8 @@ 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); });
|
||||
}
|
||||
|
||||
@@ -98,26 +101,31 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& prefix,
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
// Create AstVarScope
|
||||
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
|
||||
// Add to scope
|
||||
@@ -473,8 +481,11 @@ DfgGraph::neighborhood(const std::vector<const DfgVertex*>& vtxps, size_t n) con
|
||||
//------------------------------------------------------------------------------
|
||||
// DfgVertex
|
||||
|
||||
DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dt)
|
||||
: m_filelinep{flp}
|
||||
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}
|
||||
, m_dtype{dt}
|
||||
, m_type{type} {
|
||||
dfg.addVertex(*this);
|
||||
@@ -791,19 +802,18 @@ 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,
|
||||
// also make sure it's not the constant pool scope, which is special.
|
||||
// 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.
|
||||
AstScope* foundp = nullptr;
|
||||
foreachSource([&](DfgVertex& src) {
|
||||
AstScope* const scp = src.scopep(cache, true);
|
||||
if (scp != rootp && scp != constPoolp) {
|
||||
if (scp != rootp && !VN_IS(scp->modp(), Package)) {
|
||||
foundp = scp;
|
||||
return true;
|
||||
}
|
||||
|
||||
+68
-27
@@ -46,6 +46,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
@@ -120,6 +121,28 @@ 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 {
|
||||
@@ -128,10 +151,12 @@ 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
|
||||
std::vector<std::unique_ptr<DfgEdge>> m_inputps; // Input edges, as vector, for fast indexing
|
||||
DfgEdge* const m_inlineInputsp; // Input edges stored inline in the vertex (iff fixed arity)
|
||||
uint32_t m_nInputs; // Number of input edges
|
||||
DfgEdge::List m_sinks; // List of sink edges of this vertex
|
||||
|
||||
FileLine* const m_filelinep; // Source location
|
||||
@@ -161,25 +186,22 @@ public:
|
||||
|
||||
protected:
|
||||
// CONSTRUCTOR
|
||||
DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dt) VL_MT_DISABLED;
|
||||
DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataType& dt,
|
||||
DfgEdge* inlineInputsp, uint32_t nInputs) VL_MT_DISABLED;
|
||||
// Use unlinkDelete instead
|
||||
virtual ~DfgVertex() VL_MT_DISABLED = default;
|
||||
|
||||
// Create a new input edge and return it
|
||||
DfgEdge* newInput() {
|
||||
m_inputps.emplace_back(new DfgEdge{this});
|
||||
return m_inputps.back().get();
|
||||
}
|
||||
private:
|
||||
// Get input edge 'i'
|
||||
inline DfgEdge* inputEdgep(size_t i) const;
|
||||
|
||||
public:
|
||||
// Get input 'i'
|
||||
DfgVertex* inputp(size_t i) const { return m_inputps[i]->srcp(); }
|
||||
DfgVertex* inputp(size_t i) const { return inputEdgep(i)->srcp(); }
|
||||
// Relink input 'i'
|
||||
void inputp(size_t i, DfgVertex* vtxp) { m_inputps[i]->relinkSrcp(vtxp); }
|
||||
void inputp(size_t i, DfgVertex* vtxp) { inputEdgep(i)->relinkSrcp(vtxp); }
|
||||
// The number of inputs this vertex has. Some might be unconnected.
|
||||
size_t nInputs() const { return m_inputps.size(); }
|
||||
// Unlink all inputs and reset to no inputs - use very carefully
|
||||
void resetInputs() { m_inputps.clear(); }
|
||||
size_t nInputs() const { return m_nInputs; }
|
||||
|
||||
// The type of this vertex
|
||||
VDfgType type() const { return m_type; }
|
||||
@@ -252,8 +274,8 @@ public:
|
||||
bool foreachSource(T_Callable&& f) {
|
||||
static_assert(vlstd::is_invocable_r<bool, T_Callable, DfgVertex&>::value,
|
||||
"T_Callable 'f' must have a signature compatible with 'bool(DfgVertex&)'");
|
||||
for (const std::unique_ptr<DfgEdge>& edgep : m_inputps) {
|
||||
if (DfgVertex* const srcp = edgep->srcp()) {
|
||||
for (size_t i = 0; i < m_nInputs; ++i) {
|
||||
if (DfgVertex* const srcp = inputEdgep(i)->srcp()) {
|
||||
if (f(*srcp)) return true;
|
||||
}
|
||||
}
|
||||
@@ -268,8 +290,8 @@ public:
|
||||
static_assert(
|
||||
vlstd::is_invocable_r<bool, T_Callable, const DfgVertex&>::value,
|
||||
"T_Callable 'f' must have a signature compatible with 'bool(const DfgVertex&)'");
|
||||
for (const std::unique_ptr<DfgEdge>& edgep : m_inputps) {
|
||||
if (const DfgVertex* const srcp = edgep->srcp()) {
|
||||
for (size_t i = 0; i < m_nInputs; ++i) {
|
||||
if (const DfgVertex* const srcp = inputEdgep(i)->srcp()) {
|
||||
if (f(*srcp)) return true;
|
||||
}
|
||||
}
|
||||
@@ -408,6 +430,19 @@ 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.
|
||||
@@ -506,14 +541,12 @@ public:
|
||||
// DfgVertexVar instances representing the same Ast variable are unified.
|
||||
void mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) VL_MT_DISABLED;
|
||||
|
||||
// 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&,
|
||||
// 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&,
|
||||
AstScope*) VL_MT_DISABLED;
|
||||
|
||||
// Split this graph into individual components (unique sub-graphs with no edges between them).
|
||||
@@ -573,14 +606,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)) {
|
||||
// Regular module supported
|
||||
if (VN_IS(modp, Module) || VN_IS(modp, Package)) {
|
||||
// Regular modules and packages 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 (package, class, etc) not supported
|
||||
return false; // Anything else (class, etc) not supported
|
||||
}
|
||||
if (DfgVertexVar::hasRWRefs(vscp)) return false; // Referenced via READWRITE references
|
||||
// Check the AstVar
|
||||
@@ -826,6 +859,14 @@ 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;
|
||||
|
||||
+242
-186
@@ -152,9 +152,9 @@ class TraceDriver final : public DfgVisitor {
|
||||
// TYPES
|
||||
// Key for caching the result of a trace
|
||||
struct CacheKey final {
|
||||
DfgVertex* m_vtxp;
|
||||
uint32_t m_lsb;
|
||||
uint32_t m_msb;
|
||||
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
|
||||
|
||||
CacheKey() = delete;
|
||||
CacheKey(DfgVertex* vtxp, uint32_t lsb, uint32_t msb)
|
||||
@@ -187,11 +187,20 @@ 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
|
||||
// Result of tracing the currently visited Vertex. Use SET_RESULT below!
|
||||
std::vector<DfgVertex**> m_pendingps; // Pending pass-through cache entries across trace calls
|
||||
// Result of tracing the currently visited Vertex. Use RETURN_RESULT below!
|
||||
DfgVertex* m_resp = nullptr;
|
||||
DfgVertex* m_defaultp = nullptr; // When tracing a variable, this is its 'defaultp', if any
|
||||
// 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
|
||||
// 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
|
||||
@@ -230,12 +239,10 @@ 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, name, vtxp->dtype(), scopep);
|
||||
DfgVertexVar* const varp = m_dfg.makeNewVar(flp, prefix, vtxp->dtype(), scopep);
|
||||
varp->vscp()->varp()->isInternal(true);
|
||||
varp->tmpForp(varp->vscp());
|
||||
m_sccInfo.add(*varp, 0);
|
||||
@@ -243,60 +250,108 @@ class TraceDriver final : public DfgVisitor {
|
||||
}
|
||||
|
||||
// Trace drivers of the given packed vertex, at the given bit range.
|
||||
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");
|
||||
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;
|
||||
|
||||
// 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;
|
||||
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");
|
||||
|
||||
// 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.
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
} 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;
|
||||
resp = respr;
|
||||
break;
|
||||
}
|
||||
|
||||
// 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(respr, vtxp, "Tracing driver failed for " << vtxp->typeName());
|
||||
UASSERT_OBJ(respr->width() == (msb - lsb + 1), vtxp, "Wrong result width");
|
||||
return respr;
|
||||
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;
|
||||
}
|
||||
|
||||
// Trace drivers of the given array vertex, at the current m_idxs, m_msb, m_lsb.
|
||||
@@ -375,27 +430,59 @@ class TraceDriver final : public DfgVisitor {
|
||||
return resp;
|
||||
}
|
||||
|
||||
// Use this macro to set the result in 'visit' methods. This also emits
|
||||
// Use this macro to return the result from '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 SET_RESULT(vtxp) \
|
||||
#define TRACE_RESULT_COVER() \
|
||||
do { \
|
||||
m_resp = vtxp; \
|
||||
if (VL_UNLIKELY(m_lineCoverageFile.is_open())) m_lineCoverageFile << __LINE__ << '\n'; \
|
||||
} while (false)
|
||||
#else
|
||||
#define SET_RESULT(vtxp) m_resp = vtxp;
|
||||
#define TRACE_RESULT_COVER() \
|
||||
do { \
|
||||
} while (false)
|
||||
#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;
|
||||
DfgVertex* m_vtxp; // Vertex driving this range
|
||||
uint32_t m_lsb; // LSB of driven range (internal, not Verilog)
|
||||
uint32_t m_msb; // MSB of driven range (internal, not Verilog)
|
||||
Driver() = delete;
|
||||
@@ -407,25 +494,27 @@ 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 = m_defaultp;
|
||||
const bool done = vtxp->foreachDriver([&](DfgVertex& src, uint32_t lsb) {
|
||||
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) {
|
||||
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, trace that and we are done
|
||||
SET_RESULT(trace(&src, m_msb - lsb, m_lsb - lsb));
|
||||
// Driver covers whole search range, we are done gathering
|
||||
coverp = &src;
|
||||
coverLsb = lsb;
|
||||
return true;
|
||||
});
|
||||
if (done) return;
|
||||
|
||||
// Trace the driver covering the whole searched range, if there is one
|
||||
if (coverp) RETURN_RESULT_TAIL(coverp, m_msb - coverLsb, m_lsb - coverLsb);
|
||||
|
||||
// Trace the default driver if no other drivers cover the searched range
|
||||
if (tryWholeDefault) {
|
||||
SET_RESULT(trace(m_defaultp, m_msb, m_lsb));
|
||||
return;
|
||||
}
|
||||
if (tryWholeDefault) RETURN_RESULT_TAIL(defaultp, m_msb, m_lsb);
|
||||
|
||||
// Hard case: We need to combine multiple drivers to produce the searched bit range
|
||||
|
||||
@@ -444,8 +533,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(m_defaultp, vtxp, "Should have a default driver if needs tracing");
|
||||
termps.emplace_back(trace(m_defaultp, driver.m_lsb - 1, lsb));
|
||||
UASSERT_OBJ(defaultp, vtxp, "Should have a default driver if needs tracing");
|
||||
termps.emplace_back(trace(defaultp, driver.m_lsb - 1, lsb));
|
||||
lsb = driver.m_lsb;
|
||||
}
|
||||
// Driver covers searched range, pick the needed/available bits
|
||||
@@ -454,8 +543,8 @@ class TraceDriver final : public DfgVisitor {
|
||||
lsb = lim + 1;
|
||||
}
|
||||
if (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));
|
||||
UASSERT_OBJ(defaultp, vtxp, "Should have a default driver if needs tracing");
|
||||
termps.emplace_back(trace(defaultp, m_msb, lsb));
|
||||
}
|
||||
|
||||
// The earlier cheks cover the case when either a whole driver or the default covers
|
||||
@@ -471,33 +560,46 @@ class TraceDriver final : public DfgVisitor {
|
||||
catp->lhsp(termp);
|
||||
resp = catp;
|
||||
}
|
||||
SET_RESULT(resp);
|
||||
RETURN_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)
|
||||
if (DfgVertex* const driverp = vtxp->driverAt(m_idxs.back())) {
|
||||
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;
|
||||
}
|
||||
// Consume this index, then trace the element value
|
||||
SET_RESULT(tracePopIdx(driverp->as<DfgUnitArray>()->srcp()));
|
||||
return;
|
||||
RETURN_RESULT(tracePopIdx(srcp));
|
||||
}
|
||||
// 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));
|
||||
// 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));
|
||||
}
|
||||
|
||||
void visit(DfgVertexVar* vtxp) override {
|
||||
UASSERT_OBJ(!vtxp->isVolatile(), vtxp, "Should not trace through volatile variable");
|
||||
VL_RESTORER(m_defaultp);
|
||||
m_defaultp = vtxp->defaultp();
|
||||
DfgVertex* const drvp = vtxp->srcp() ? vtxp->srcp() : m_defaultp;
|
||||
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;
|
||||
}
|
||||
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).
|
||||
SET_RESULT(m_idxs.empty() ? trace(drvp, m_msb, m_lsb) : traceSameIdx(drvp));
|
||||
RETURN_RESULT(m_idxs.empty() ? trace(drvp, m_msb, m_lsb) : traceSameIdx(drvp));
|
||||
}
|
||||
|
||||
void visit(DfgArraySel* vtxp) override {
|
||||
@@ -505,8 +607,7 @@ 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>()) {
|
||||
SET_RESULT(tracePushIdx(vtxp->fromp(), idxp->toU32()));
|
||||
return;
|
||||
RETURN_RESULT(tracePushIdx(vtxp->fromp(), idxp->toU32()));
|
||||
}
|
||||
|
||||
// If index is not constant, independence was proven only if the 'fromp' is
|
||||
@@ -519,13 +620,13 @@ class TraceDriver final : public DfgVisitor {
|
||||
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
selp->fromp(resp);
|
||||
selp->lsb(m_lsb);
|
||||
SET_RESULT(selp);
|
||||
RETURN_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");
|
||||
SET_RESULT(tracePopIdx(vtxp->srcp()));
|
||||
RETURN_RESULT(tracePopIdx(vtxp->srcp()));
|
||||
}
|
||||
|
||||
void visit(DfgConcat* vtxp) override {
|
||||
@@ -533,20 +634,14 @@ 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) {
|
||||
SET_RESULT(trace(rhsp, m_msb, m_lsb));
|
||||
return;
|
||||
}
|
||||
if (rWidth > m_msb) RETURN_RESULT_TAIL(rhsp, m_msb, m_lsb);
|
||||
// If the traced bits are wholly in the LHS
|
||||
if (m_lsb >= rWidth) {
|
||||
SET_RESULT(trace(lhsp, m_msb - rWidth, m_lsb - rWidth));
|
||||
return;
|
||||
}
|
||||
if (m_lsb >= rWidth) RETURN_RESULT_TAIL(lhsp, m_msb - rWidth, m_lsb - rWidth);
|
||||
// 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));
|
||||
SET_RESULT(resp);
|
||||
RETURN_RESULT(resp);
|
||||
}
|
||||
|
||||
void visit(DfgRep* vtxp) override {
|
||||
@@ -559,91 +654,75 @@ class TraceDriver final : public DfgVisitor {
|
||||
DfgSel* const resp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
resp->fromp(repp);
|
||||
resp->lsb(m_lsb);
|
||||
SET_RESULT(resp);
|
||||
return;
|
||||
RETURN_RESULT(resp);
|
||||
}
|
||||
// If the requested bits are within the same repliacted word
|
||||
if (m_msb / sWidth == m_lsb / sWidth) {
|
||||
SET_RESULT(trace(srcp, m_msb % sWidth, m_lsb % sWidth));
|
||||
return;
|
||||
RETURN_RESULT_TAIL(srcp, m_msb % sWidth, m_lsb % sWidth);
|
||||
}
|
||||
// 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));
|
||||
SET_RESULT(resp);
|
||||
RETURN_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) {
|
||||
SET_RESULT(trace(srcp, m_msb, m_lsb));
|
||||
return;
|
||||
}
|
||||
if (sWidth > m_msb) RETURN_RESULT_TAIL(srcp, m_msb, m_lsb);
|
||||
// If the traced bits are wholly in the extension
|
||||
if (m_lsb >= sWidth) {
|
||||
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
return;
|
||||
}
|
||||
if (m_lsb >= sWidth) RETURN_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
// 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));
|
||||
SET_RESULT(resp);
|
||||
RETURN_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) {
|
||||
SET_RESULT(trace(srcp, m_msb, m_lsb));
|
||||
return;
|
||||
}
|
||||
if (sWidth > m_msb) RETURN_RESULT_TAIL(srcp, m_msb, m_lsb);
|
||||
// If the traced bits are wholly in the extension
|
||||
if (m_lsb >= sWidth) {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
// 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));
|
||||
SET_RESULT(resp);
|
||||
RETURN_RESULT(resp);
|
||||
}
|
||||
|
||||
void visit(DfgSel* vtxp) override {
|
||||
const uint32_t lsb = vtxp->lsb();
|
||||
SET_RESULT(trace(vtxp->srcp(), m_msb + lsb, m_lsb + lsb));
|
||||
RETURN_RESULT_TAIL(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));
|
||||
SET_RESULT(resp);
|
||||
RETURN_RESULT(resp);
|
||||
}
|
||||
|
||||
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(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(DfgShiftRS* vtxp) override {
|
||||
DfgVertex* const lhsp = vtxp->lhsp();
|
||||
@@ -653,34 +732,28 @@ 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) {
|
||||
SET_RESULT(trace(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt));
|
||||
return;
|
||||
RETURN_RESULT_TAIL(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt);
|
||||
}
|
||||
// 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));
|
||||
SET_RESULT(resp);
|
||||
return;
|
||||
RETURN_RESULT(resp);
|
||||
}
|
||||
// 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));
|
||||
SET_RESULT(resp);
|
||||
return;
|
||||
RETURN_RESULT(resp);
|
||||
}
|
||||
|
||||
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) {
|
||||
SET_RESULT(shiftrsp);
|
||||
return;
|
||||
}
|
||||
if (m_msb == vtxp->lhsp()->width() - 1) RETURN_RESULT(shiftrsp);
|
||||
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
selp->fromp(shiftrsp);
|
||||
selp->lsb(0);
|
||||
SET_RESULT(selp);
|
||||
RETURN_RESULT(selp);
|
||||
}
|
||||
|
||||
void visit(DfgShiftR* vtxp) override {
|
||||
@@ -691,32 +764,24 @@ 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) {
|
||||
SET_RESULT(trace(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt));
|
||||
return;
|
||||
RETURN_RESULT_TAIL(lhsp, m_msb + shiftAmnt, m_lsb + shiftAmnt);
|
||||
}
|
||||
// If the traced bits are wholly in the extension
|
||||
if (m_lsb >= lowerWidth) {
|
||||
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
return;
|
||||
}
|
||||
if (m_lsb >= lowerWidth) RETURN_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
// 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));
|
||||
SET_RESULT(resp);
|
||||
return;
|
||||
RETURN_RESULT(resp);
|
||||
}
|
||||
|
||||
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) {
|
||||
SET_RESULT(shiftrp);
|
||||
return;
|
||||
}
|
||||
if (m_msb == vtxp->lhsp()->width() - 1) RETURN_RESULT(shiftrp);
|
||||
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
selp->fromp(shiftrp);
|
||||
selp->lsb(0);
|
||||
SET_RESULT(selp);
|
||||
RETURN_RESULT(selp);
|
||||
}
|
||||
|
||||
void visit(DfgShiftL* vtxp) override {
|
||||
@@ -727,33 +792,25 @@ 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) {
|
||||
SET_RESULT(trace(lhsp, m_msb - shiftAmnt, m_lsb - shiftAmnt));
|
||||
return;
|
||||
RETURN_RESULT_TAIL(lhsp, m_msb - shiftAmnt, m_lsb - shiftAmnt);
|
||||
}
|
||||
// If the traced bits are wholly in the extension
|
||||
if (lowerWidth > m_msb) {
|
||||
SET_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
return;
|
||||
}
|
||||
if (lowerWidth > m_msb) RETURN_RESULT(make<DfgConst>(vtxp, m_msb - m_lsb + 1));
|
||||
// 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));
|
||||
SET_RESULT(resp);
|
||||
return;
|
||||
RETURN_RESULT(resp);
|
||||
}
|
||||
|
||||
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) {
|
||||
SET_RESULT(shiftlp);
|
||||
return;
|
||||
}
|
||||
if (m_lsb == 0) RETURN_RESULT(shiftlp);
|
||||
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
selp->fromp(shiftlp);
|
||||
selp->lsb(m_lsb);
|
||||
SET_RESULT(selp);
|
||||
RETURN_RESULT(selp);
|
||||
}
|
||||
|
||||
void visit(DfgCond* vtxp) override {
|
||||
@@ -761,7 +818,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));
|
||||
SET_RESULT(resp);
|
||||
RETURN_RESULT(resp);
|
||||
}
|
||||
|
||||
void visit(DfgMatchMasked* vtxp) override {
|
||||
@@ -771,10 +828,12 @@ class TraceDriver final : public DfgVisitor {
|
||||
DfgSel* const selp = make<DfgSel>(vtxp, m_msb - m_lsb + 1);
|
||||
selp->fromp(resp);
|
||||
selp->lsb(m_lsb);
|
||||
SET_RESULT(selp);
|
||||
RETURN_RESULT(selp);
|
||||
}
|
||||
|
||||
#undef SET_RESULT
|
||||
#undef RETURN_RESULT
|
||||
#undef TRACE_RESULT_COVER
|
||||
#undef RETURN_RESULT_TAIL
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
@@ -1593,9 +1652,6 @@ 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");
|
||||
|
||||
|
||||
+12
-11
@@ -409,17 +409,18 @@ public:
|
||||
|
||||
// Sub contexts - keep sorted by type
|
||||
V3DfgAstToDfgContext m_ast2DfgContext;
|
||||
V3DfgBinToOneHotContext m_binToOneHotContext;
|
||||
V3DfgBreakCyclesContext m_breakCyclesContext;
|
||||
V3DfgCseContext m_cseContext0{"1st"};
|
||||
V3DfgCseContext m_cseContext1{"2nd"};
|
||||
V3DfgDfgToAstContext m_dfg2AstContext;
|
||||
V3DfgPeepholeContext m_peepholeContext;
|
||||
V3DfgPushDownSelsContext m_pushDownSelsContext;
|
||||
V3DfgRegularizeContext m_regularizeContext;
|
||||
V3DfgRemoveSelectsContext m_removeSelectsContext;
|
||||
V3DfgRemoveUnobservableContext m_removeUnobservableContext;
|
||||
V3DfgSynthesisContext m_synthContext;
|
||||
V3DfgBinToOneHotContext m_binToOneHotContext; // Statistics context for V3DfgBinToOneHot
|
||||
V3DfgBreakCyclesContext m_breakCyclesContext; // Statistics context for V3DfgBreakCycles
|
||||
V3DfgCseContext m_cseContext0{"1st"}; // Statistics context for CSE stage 1
|
||||
V3DfgCseContext m_cseContext1{"2nd"}; // Statistics context for CSE stage 2
|
||||
V3DfgDfgToAstContext m_dfg2AstContext; // Statistics context for V3DfgDfgToAst
|
||||
V3DfgPeepholeContext m_peepholeContext; // Statistics context for V3DfgPeephole
|
||||
V3DfgPushDownSelsContext m_pushDownSelsContext; // Statistics context for V3DfgPushDownSels
|
||||
V3DfgRegularizeContext m_regularizeContext; // Statistics context for V3DfgRegularize
|
||||
V3DfgRemoveSelectsContext m_removeSelectsContext; // Stats context for V3DfgRemoveSelects
|
||||
V3DfgRemoveUnobservableContext
|
||||
m_removeUnobservableContext; // Stats context for V3DfgRemoveUnobservable
|
||||
V3DfgSynthesisContext m_synthContext; // Statistics context for V3DfgSynthesize
|
||||
|
||||
// CONSTRUCTOR
|
||||
V3DfgContext() = default;
|
||||
|
||||
@@ -242,7 +242,7 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
AstNodeExpr* const lhsp = convertDfgVertexToAstNodeExpr(vtxp->lhsp());
|
||||
AstVarScope* const matchp = vtxp->matchp()->as<DfgVertexVar>()->vscp();
|
||||
m_resultp = new AstMatchMasked{flp, lhsp, matchp};
|
||||
m_resultp = new AstMatchMasked{flp, lhsp, new AstVarRef{flp, matchp, VAccess::READ}};
|
||||
}
|
||||
|
||||
void visit(DfgRep* vtxp) override {
|
||||
|
||||
@@ -82,7 +82,7 @@ class DataflowOptimize final {
|
||||
const bool hasExtRd = //
|
||||
varp->isPrimaryIO() // Top level port - readable
|
||||
|| varp->isSigUserRdPublic() // Readable by user
|
||||
|| varp->constPoolEntry() // Stored in AstConstPool hashmap, but read only
|
||||
|| varp->constPoolEntry() // Held in V3ConstPool lookup cache, keep
|
||||
;
|
||||
const bool hasExtWr = //
|
||||
(varp->isPrimaryIO() && varp->isNonOutput()) // Top level port - writable
|
||||
@@ -183,13 +183,6 @@ class DataflowOptimize final {
|
||||
}
|
||||
|
||||
DataflowOptimize(AstNetlist* netlistp) {
|
||||
// Mark interfaces that might be referenced by a virtual interface
|
||||
if (v3Global.hasVirtIfaces()) {
|
||||
netlistp->typeTablep()->foreach([](const AstIfaceRefDType* nodep) {
|
||||
if (!nodep->isVirtual()) return;
|
||||
nodep->ifaceViaCellp()->setHasVirtualRef();
|
||||
});
|
||||
}
|
||||
// Mark variables with external references
|
||||
markExternallyReferencedVariables(netlistp);
|
||||
// Dump stage stats
|
||||
|
||||
+3
-10
@@ -344,9 +344,6 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
// Fast path exit if we surely don't need to convet anything
|
||||
if (nTerms < TERM_LIMIT) return;
|
||||
|
||||
// Sequence numbers for name generation
|
||||
size_t nTables = 0;
|
||||
|
||||
DfgVertex::ScopeCache scopeCache;
|
||||
|
||||
// Create decoders for each srcp
|
||||
@@ -419,8 +416,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
// If there is an existing result variable, use that
|
||||
if (DfgVertexVar* const vp = srcp->getResultVar()) return vp->as<DfgVarPacked>();
|
||||
// Otherwise create a new variable
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables);
|
||||
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, name, idxDType, scopep);
|
||||
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, "BinToOneHot_Idx", idxDType, scopep);
|
||||
vtxp->vscp()->varp()->isInternal(true);
|
||||
vtxp->srcp(srcp);
|
||||
return vtxp->as<DfgVarPacked>();
|
||||
@@ -428,8 +424,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
AstVarScope* const idxVscp = idxVtxp->vscp();
|
||||
// The previous index variable - we don't need a vertex for this
|
||||
AstVarScope* const preVscp = [&]() {
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Pre", nTables);
|
||||
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, name, idxDType, scopep);
|
||||
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, "BinToOneHot_Pre", idxDType, scopep);
|
||||
AstVarScope* const vscp = vtxp->vscp();
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
|
||||
vscp->varp()->isInternal(true);
|
||||
@@ -439,15 +434,13 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
}();
|
||||
// The table variable
|
||||
DfgVarArray* const tabVtxp = [&]() {
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Tab", nTables);
|
||||
DfgVertexVar* const varp = dfg.makeNewVar(flp, name, tabDType, scopep);
|
||||
DfgVertexVar* const varp = dfg.makeNewVar(flp, "BinToOneHot_Tab", tabDType, scopep);
|
||||
varp->vscp()->varp()->isInternal(true);
|
||||
varp->vscp()->varp()->noReset(true);
|
||||
varp->setHasModWrRefs();
|
||||
return varp->as<DfgVarArray>();
|
||||
}();
|
||||
|
||||
++nTables;
|
||||
++ctx.m_decodersCreated;
|
||||
|
||||
// Initialize 'tab' and 'pre' variables statically
|
||||
|
||||
@@ -209,7 +209,6 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||
DfgVertex* m_vtxp = nullptr; // Currently considered vertex
|
||||
size_t m_currentGeneration = 0; // Current generation number
|
||||
size_t m_lastId = 0; // Last unique vertex ID assigned
|
||||
size_t m_nTemps = 0; // Number of temporary variables created
|
||||
// Scope for transient temporariy variables cerated in this pass. They should all be
|
||||
// eliminated wihtin this pass, so anything should be ok, pick the top scope as easy to find.
|
||||
AstScope* const m_tmpScopep = v3Global.rootp()->topScopep()->scopep();
|
||||
@@ -2181,9 +2180,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||
DfgSplicePacked* const sp = new DfgSplicePacked{m_dfg, flp, vtxp->dtype()};
|
||||
m_vInfo[sp].m_id = ++m_lastId;
|
||||
sp->addDriver(catp, lsb, flp);
|
||||
const std::string name = m_dfg.makeUniqueName("PeepholeNarrow", m_nTemps++);
|
||||
DfgVertexVar* const varp
|
||||
= m_dfg.makeNewVar(flp, name, vtxp->dtype(), m_tmpScopep);
|
||||
= m_dfg.makeNewVar(flp, "PeepholeNarrow", vtxp->dtype(), m_tmpScopep);
|
||||
varp->tmpForp(varp->vscp());
|
||||
m_vInfo[varp].m_id = ++m_lastId;
|
||||
varp->vscp()->varp()->isInternal(true);
|
||||
|
||||
@@ -30,7 +30,6 @@ class DfgRegularize final {
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The graph being processed
|
||||
V3DfgRegularizeContext& m_ctx; // The optimization context for stats
|
||||
size_t m_nTmps = 0; // Number of temporaries added to this graph - for variable names only
|
||||
VNDeleter m_deleter; // Deletes replacement nodes at the end
|
||||
|
||||
// METHODS
|
||||
@@ -208,11 +207,10 @@ class DfgRegularize final {
|
||||
if (!needsTemporary(*vtxp, *vtxp)) continue;
|
||||
// Need to create an intermediate variable
|
||||
++m_ctx.m_temporariesIntroduced;
|
||||
const std::string name = m_dfg.makeUniqueName("Regularize", m_nTmps);
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
AstScope* const scopep = vtxp->scopep(scopeCache);
|
||||
DfgVertexVar* const newp = m_dfg.makeNewVar(flp, name, vtxp->dtype(), scopep);
|
||||
++m_nTmps;
|
||||
DfgVertexVar* const newp
|
||||
= m_dfg.makeNewVar(flp, "Regularize", vtxp->dtype(), scopep);
|
||||
// Replace vertex with the variable, make it drive the variable
|
||||
vtxp->replaceWith(newp);
|
||||
newp->srcp(vtxp);
|
||||
|
||||
+77
-30
@@ -58,8 +58,6 @@ DfgArraySel* makeVertex<DfgArraySel, AstArraySel>(const AstArraySel* nodep, DfgG
|
||||
class AstToDfgConverter final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// AstNodeExpr/AstVar/AstVarScope::user2p -> DfgVertex* for this Node
|
||||
// AstVar::user3() -> int temporary counter for variable
|
||||
const VNUser3InUse m_user3InUse;
|
||||
|
||||
// STATE
|
||||
DfgGraph& m_dfg; // The graph being built
|
||||
@@ -74,8 +72,6 @@ class AstToDfgConverter final : public VNVisitor {
|
||||
bool m_foundUnhandled = false; // Found node not implemented as DFG or not implemented 'visit'
|
||||
bool m_converting = false; // We are trying to convert some logic at the moment
|
||||
|
||||
size_t m_nUnpack = 0; // Sequence numbers for temporaries
|
||||
|
||||
// METHODS
|
||||
|
||||
// Allocate a new non-variable vertex, add it to the currently synthesized logic
|
||||
@@ -257,9 +253,9 @@ class AstToDfgConverter final : public VNVisitor {
|
||||
// Represents a DFG assignment contributed by the AST assignment with the above 'lhsp'.
|
||||
// There might be multiple of these if 'lhsp' is a concatenation.
|
||||
struct Assignment final {
|
||||
DfgVertexSplice* m_lhsp;
|
||||
uint32_t m_idx;
|
||||
DfgVertex* m_rhsp;
|
||||
DfgVertexSplice* m_lhsp; // Splice vertex being driven
|
||||
uint32_t m_idx; // Position (bit or array index) within m_lhsp being driven
|
||||
DfgVertex* m_rhsp; // Vertex driving that position
|
||||
Assignment() = delete;
|
||||
Assignment(DfgVertexSplice* lhsp, uint32_t idx, DfgVertex* rhsp)
|
||||
: m_lhsp{lhsp}
|
||||
@@ -273,8 +269,7 @@ class AstToDfgConverter final : public VNVisitor {
|
||||
// Assigning compound expressions to a concatenated LHS requires a temporary
|
||||
// to avoid multiple use of the expression
|
||||
if (VN_IS(lhsp, Concat) && !vtxp->is<DfgVertexVar>() && !vtxp->is<DfgConst>()) {
|
||||
const size_t n = ++m_nUnpack;
|
||||
DfgVertexVar* const tmpp = createTmp(*m_logicp, flp, vtxp->dtype(), "Unpack", n);
|
||||
DfgVertexVar* const tmpp = createTmp(*m_logicp, flp, vtxp->dtype(), "Unpack");
|
||||
tmpp->srcp(vtxp);
|
||||
vtxp = tmpp;
|
||||
}
|
||||
@@ -496,9 +491,8 @@ public:
|
||||
|
||||
// Create temporay variable capable of holding the given type
|
||||
DfgVertexVar* createTmp(DfgLogic& logic, FileLine* flp, const DfgDataType& dtype,
|
||||
const std::string& prefix, size_t tmpCount) {
|
||||
const std::string name = m_dfg.makeUniqueName(prefix, tmpCount);
|
||||
DfgVertexVar* const vtxp = m_dfg.makeNewVar(flp, name, dtype, logic.scopep());
|
||||
const std::string& prefix) {
|
||||
DfgVertexVar* const vtxp = m_dfg.makeNewVar(flp, prefix, dtype, logic.scopep());
|
||||
logic.synth().emplace_back(vtxp);
|
||||
vtxp->vscp()->varp()->isInternal(true);
|
||||
vtxp->tmpForp(vtxp->vscp());
|
||||
@@ -511,8 +505,7 @@ public:
|
||||
FileLine* const flp = astVarp->fileline();
|
||||
const DfgDataType& dtype = *DfgDataType::fromAst(astVarp->dtypep());
|
||||
const std::string prfx = prefix + "_" + astVarp->name();
|
||||
const size_t tmpCount = astVarp->user3Inc();
|
||||
DfgVertexVar* const vtxp = createTmp(logic, flp, dtype, prfx, tmpCount);
|
||||
DfgVertexVar* const vtxp = createTmp(logic, flp, dtype, prfx);
|
||||
vtxp->tmpForp(vscp);
|
||||
return vtxp;
|
||||
}
|
||||
@@ -624,8 +617,6 @@ class AstToDfgSynthesize final {
|
||||
DfgGraph& m_dfg; // The graph being built
|
||||
V3DfgSynthesisContext& m_ctx; // The context for stats
|
||||
AstToDfgConverter m_converter; // The convert instance to use for each construct
|
||||
size_t m_nBranchCond = 0; // Sequence numbers for temporaries
|
||||
size_t m_nPathPred = 0; // Sequence numbers for temporaries
|
||||
DfgWorklist m_toRevert{m_dfg}; // We need a worklist for reverting synthesis
|
||||
|
||||
// STATE - for current DfgLogic being synthesized
|
||||
@@ -701,6 +692,28 @@ class AstToDfgSynthesize final {
|
||||
return drivers;
|
||||
}
|
||||
|
||||
// Returns true if the driver supplies a complete array element.
|
||||
static bool driverCoversWholeElement(const Driver& driver) {
|
||||
const DfgUnitArray* const unitp = driver.m_vtxp->cast<DfgUnitArray>();
|
||||
if (!unitp) return false;
|
||||
if (DfgVertexSplice* const splicep = unitp->srcp()->cast<DfgVertexSplice>()) {
|
||||
return splicep->wholep();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unlike 'wholep', several element drivers can collectively cover an array.
|
||||
static bool spliceCoversWhole(DfgVertexSplice* const splicep) {
|
||||
if (splicep->wholep()) return true;
|
||||
if (splicep->isPacked()) return false;
|
||||
uint32_t next = 0;
|
||||
for (const Driver& driver : gatherDrivers(splicep)) {
|
||||
if (driver.m_lo != next || !driverCoversWholeElement(driver)) return false;
|
||||
next = driver.m_hi + 1;
|
||||
}
|
||||
return next == splicep->size();
|
||||
}
|
||||
|
||||
// Returns true if the driver cone contains any variable introduced by
|
||||
// tristate lowering. Used to distinguish intentional tristate contributor
|
||||
// overlap from accidental multidrive.
|
||||
@@ -1313,6 +1326,42 @@ class AstToDfgSynthesize final {
|
||||
return propagatedDrivers;
|
||||
}
|
||||
|
||||
// Propagate whole elements with a linear walk over the sorted driver lists.
|
||||
// Reads use the previous value temporary, preserving assignment-version bindings.
|
||||
// Partial elements and non-unit drivers retain the nonsynthesized-process fallback.
|
||||
bool computePropagatedArrayDrivers(const std::vector<Driver>& newDrivers,
|
||||
DfgVertexVar* const oldp,
|
||||
std::vector<Driver>& propagatedDrivers) {
|
||||
// Bound quadratic vertex growth: array drivers cannot be coalesced.
|
||||
static constexpr uint32_t MAX_ARRAY_ELEMENTS = 32;
|
||||
if (oldp->size() > MAX_ARRAY_ELEMENTS) return false;
|
||||
for (const Driver& driver : newDrivers) {
|
||||
if (!driverCoversWholeElement(driver)) return false;
|
||||
}
|
||||
const std::vector<Driver> oldDrivers = gatherDrivers(oldp->srcp()->as<DfgVertexSplice>());
|
||||
UASSERT_OBJ(!oldDrivers.empty(), oldp, "Should have a proper driver");
|
||||
for (const Driver& driver : oldDrivers) {
|
||||
if (!driverCoversWholeElement(driver)) return false;
|
||||
}
|
||||
|
||||
propagatedDrivers.reserve(oldDrivers.size());
|
||||
auto nIt = newDrivers.begin();
|
||||
for (const Driver& oDriver : oldDrivers) {
|
||||
while (nIt != newDrivers.end() && nIt->m_lo < oDriver.m_lo) ++nIt;
|
||||
if (nIt != newDrivers.end() && nIt->m_lo == oDriver.m_lo) continue;
|
||||
|
||||
FileLine* const flp = oDriver.m_flp;
|
||||
const DfgUnitArray* const oldUnitp = oDriver.m_vtxp->as<DfgUnitArray>();
|
||||
DfgArraySel* const selp = make<DfgArraySel>(flp, oldUnitp->srcp()->dtype());
|
||||
selp->fromp(oldp);
|
||||
selp->bitp(make<DfgConst>(flp, static_cast<size_t>(VL_IDATASIZE), oDriver.m_lo));
|
||||
DfgUnitArray* const newUnitp = make<DfgUnitArray>(flp, oldUnitp->dtype());
|
||||
newUnitp->srcp(selp);
|
||||
propagatedDrivers.emplace_back(newUnitp, oDriver.m_lo, flp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Given the drivers of a variable after converting a single statement
|
||||
// 'newp', add drivers from 'oldp' that were not reassigned be drivers
|
||||
// in newp. This computes the total result of all previous assignments.
|
||||
@@ -1328,25 +1377,25 @@ class AstToDfgSynthesize final {
|
||||
// If the old value is the real variable we just computed the new value for,
|
||||
// then it is the circular feedback into the synthesized block, add it as default driver.
|
||||
if (oldp->vscp() == vscp) {
|
||||
if (!nSplicep->wholep()) newp->defaultp(oldp);
|
||||
if (!spliceCoversWhole(nSplicep)) newp->defaultp(oldp);
|
||||
return true;
|
||||
}
|
||||
|
||||
UASSERT_OBJ(oldp->srcp(), vscp, "Previously assigned variable has no driver");
|
||||
|
||||
// Can't do arrays yet
|
||||
if (!newp->isPacked()) {
|
||||
++m_ctx.m_synt.nonSynArray;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gather drivers of 'newp' - they are in incresing range order with no overlaps
|
||||
UASSERT_OBJ(!newp->defaultp(), newp, "Converted value should not have default");
|
||||
std::vector<Driver> nDrivers = gatherDrivers(newp->srcp()->as<DfgVertexSplice>());
|
||||
UASSERT_OBJ(!nDrivers.empty(), newp, "Should have a proper driver");
|
||||
|
||||
// Additional drivers of 'newp' propagated from 'oldp'
|
||||
std::vector<Driver> pDrivers = computePropagatedDrivers(nDrivers, oldp);
|
||||
std::vector<Driver> pDrivers;
|
||||
if (newp->isPacked()) {
|
||||
pDrivers = computePropagatedDrivers(nDrivers, oldp);
|
||||
} else if (!computePropagatedArrayDrivers(nDrivers, oldp, pDrivers)) {
|
||||
++m_ctx.m_synt.nonSynArray;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pDrivers.empty()) {
|
||||
// Need to merge propagated sources, so reset the splice
|
||||
@@ -1357,13 +1406,13 @@ class AstToDfgSynthesize final {
|
||||
std::merge(nDrivers.begin(), nDrivers.end(), pDrivers.begin(), pDrivers.end(),
|
||||
std::back_inserter(drivers));
|
||||
// Coalesce adjacent ranges
|
||||
coalesceDrivers(drivers);
|
||||
if (newp->isPacked()) coalesceDrivers(drivers);
|
||||
// Reinsert drivers in order
|
||||
for (const Driver& d : drivers) nSplicep->addDriver(d.m_vtxp, d.m_lo, d.m_flp);
|
||||
}
|
||||
|
||||
// If the old had a default, add to the new one too, unless redundant
|
||||
if (oldp->defaultp() && !nSplicep->wholep()) newp->defaultp(oldp->defaultp());
|
||||
if (oldp->defaultp() && !spliceCoversWhole(nSplicep)) newp->defaultp(oldp->defaultp());
|
||||
|
||||
// Done
|
||||
return true;
|
||||
@@ -1481,12 +1530,11 @@ class AstToDfgSynthesize final {
|
||||
return resp;
|
||||
}();
|
||||
|
||||
size_t n = m_nPathPred++; // Sequence number for temporaries
|
||||
const DfgDataType& dtype = predp->dtype();
|
||||
|
||||
const auto mkTmp = [&](FileLine* flp, const char* name, DfgVertex* srcp) {
|
||||
const std::string prefix = "_BB" + std::to_string(bb.id()) + "_" + name;
|
||||
DfgVertexVar* const tmpp = m_converter.createTmp(*m_logicp, flp, dtype, prefix, n);
|
||||
DfgVertexVar* const tmpp = m_converter.createTmp(*m_logicp, flp, dtype, prefix);
|
||||
tmpp->srcp(srcp);
|
||||
return tmpp;
|
||||
};
|
||||
@@ -1659,8 +1707,7 @@ class AstToDfgSynthesize final {
|
||||
FileLine* const flp = condp->fileline();
|
||||
const DfgDataType& dtype = condp->dtype();
|
||||
const std::string prefix = "_BB" + std::to_string(bb.id()) + "_Cond";
|
||||
const size_t n = m_nBranchCond++;
|
||||
DfgVertexVar* const vp = m_converter.createTmp(*m_logicp, flp, dtype, prefix, n);
|
||||
DfgVertexVar* const vp = m_converter.createTmp(*m_logicp, flp, dtype, prefix);
|
||||
vp->srcp(condp);
|
||||
m_bbToCondp[bb] = vp;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user