mirror of
https://github.com/verilator/verilator.git
synced 2026-10-03 00:23:35 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
983a06a16f | ||
|
|
ea338be98e | ||
|
|
c8a8f02c09 | ||
|
|
b1c06fdb09 | ||
|
|
18095ac924 | ||
|
|
efc390a37d | ||
|
|
228635918e | ||
|
|
c917f04c65 | ||
|
|
c4a4f35ec4 | ||
|
|
e012f7c79d | ||
|
|
80907b1e26 | ||
|
|
a5f4f7eade | ||
|
|
c7aa95439c | ||
|
|
a3932e259b | ||
|
|
e6cee9d54c | ||
|
|
f694e9e1e8 | ||
|
|
667f64813f | ||
|
|
67818a3430 | ||
|
|
1c2fa7234d | ||
|
|
be83d1393d | ||
|
|
9474a01d54 | ||
|
|
c81be029a6 | ||
|
|
3c121c0a2e | ||
|
|
0f96460042 | ||
|
|
648035df72 | ||
|
|
dcf45da1c0 | ||
|
|
d2920a627d | ||
|
|
2954756190 | ||
|
|
c70439961f | ||
|
|
ab20edbbaf | ||
|
|
297713df8b | ||
|
|
6a05cc680b | ||
|
|
50e42bc197 | ||
|
|
c64afda7fc | ||
|
|
1ad9acd22e | ||
|
|
2f856971ed | ||
|
|
8546d5db06 | ||
|
|
59f221986c | ||
|
|
99c6f9ced8 | ||
|
|
7bff0196b1 | ||
|
|
f055eeabe0 | ||
|
|
7f9333723f | ||
|
|
cce0277e1a | ||
|
|
51a2dc3efb | ||
|
|
0dcdea3325 | ||
|
|
1cd2588241 | ||
|
|
087b15bdb3 | ||
|
|
5e4151e3e0 | ||
|
|
b23b53227d | ||
|
|
8cbaf505ee | ||
|
|
0b85b80ece | ||
|
|
821478070d | ||
|
|
69f42ea480 | ||
|
|
f6d56c6440 | ||
|
|
538c6a6b76 | ||
|
|
1c9a9e0cce | ||
|
|
6fc18710bd | ||
|
|
67cbc36ef0 | ||
|
|
2cd0efbc0f | ||
|
|
3c027baa44 | ||
|
|
33ad9d530e | ||
|
|
d0321b8502 | ||
|
|
ee8e2c0e12 | ||
|
|
8fc78e31d7 | ||
|
|
477b48fb3d | ||
|
|
df0d801790 | ||
|
|
d2760be0d4 | ||
|
|
d18bcc2a69 | ||
|
|
8a45a2d46e | ||
|
|
63a59fce22 | ||
|
|
c4b84b20f7 | ||
|
|
d5f7878eb4 | ||
|
|
459711ba71 | ||
|
|
0820506d74 | ||
|
|
063b938f41 | ||
|
|
cb618d6f9c | ||
|
|
4aceba1106 | ||
|
|
d359a9ce3c | ||
|
|
3dbfa7708f | ||
|
|
cda258cbed | ||
|
|
cccf7ef1b3 | ||
|
|
8844593abb | ||
|
|
9badc5485d | ||
|
|
fd319beb8a | ||
|
|
c9e270a9fa | ||
|
|
d1067002da | ||
|
|
a7ecba4f4e | ||
|
|
99c26d3994 | ||
|
|
e656b11cd1 | ||
|
|
f7c087468d | ||
|
|
227e432695 | ||
|
|
38ae4e617d | ||
|
|
e717c513e9 | ||
|
|
f6f6f84047 | ||
|
|
cacadd6c7d | ||
|
|
65094514d6 | ||
|
|
f88b59f39f | ||
|
|
700858c112 | ||
|
|
2ba4bd9211 | ||
|
|
7523a0cd8e | ||
|
|
8c2b7410f0 | ||
|
|
70b5ce802b | ||
|
|
88e87b8abb | ||
|
|
7628a540af | ||
|
|
06ee8b7261 | ||
|
|
f5ae58ddab | ||
|
|
86b7cfb05c | ||
|
|
8a29360ade | ||
|
|
b032379de2 | ||
|
|
ba893abb2c | ||
|
|
94a63dd407 | ||
|
|
d9fb606869 | ||
|
|
8baff6ae33 | ||
|
|
7c6b225b7e | ||
|
|
2a9757bf38 | ||
|
|
74d12c5b5e | ||
|
|
d4a18d4dfb | ||
|
|
96ea587df0 | ||
|
|
2d8d8add42 | ||
|
|
7dcd4e0b65 | ||
|
|
ace84ef2b3 | ||
|
|
a3553e7bee | ||
|
|
e2f7755611 | ||
|
|
d2f62fda93 | ||
|
|
93401038c0 | ||
|
|
3334576b1d | ||
|
|
6d6d15e003 | ||
|
|
d6b206cc7d | ||
|
|
edd4cd4faf | ||
|
|
91216bdac1 | ||
|
|
92300ab675 | ||
|
|
0b50390359 | ||
|
|
621a67e819 | ||
|
|
75a776c516 | ||
|
|
743e0f4a82 | ||
|
|
649598c74c | ||
|
|
0a3657517d | ||
|
|
2957939d4b | ||
|
|
abf199cf0e | ||
|
|
7da43d8302 | ||
|
|
c8853d1507 | ||
|
|
edf43ae76f | ||
|
|
63478f2946 | ||
|
|
95721f5040 | ||
|
|
1397d5d01e | ||
|
|
098f375e9d | ||
|
|
44fe96b2da | ||
|
|
0ebb92da26 | ||
|
|
73bcf5e0db | ||
|
|
e962efca12 | ||
|
|
0b51926fff | ||
|
|
08cd5fe30b | ||
|
|
da0c31926e | ||
|
|
e96cfc5e56 | ||
|
|
2ef131f7ac | ||
|
|
945ea27fec | ||
|
|
490bd38962 | ||
|
|
121fc62aff | ||
|
|
5d888d2ac0 | ||
|
|
38259d11b6 | ||
|
|
86fa00416d | ||
|
|
645b8cdf24 | ||
|
|
dde6aa34ce | ||
|
|
7601011bba | ||
|
|
18844537bd | ||
|
|
7e80ea6577 | ||
|
|
82dab79096 | ||
|
|
1a4bcac666 | ||
|
|
e413e67ab3 | ||
|
|
b1ec180a3b | ||
|
|
1f164d7ee0 | ||
|
|
3fa88ae7f3 | ||
|
|
351d67c81e | ||
|
|
472a131df7 | ||
|
|
e6c613f28d | ||
|
|
e4b6bac50a | ||
|
|
3c4ea47e02 | ||
|
|
5bd8c49448 | ||
|
|
ba8e4f9dec | ||
|
|
15b75c7c68 | ||
|
|
925a6640d1 | ||
|
|
b42d7c2d9e | ||
|
|
d58f512a85 | ||
|
|
1356c15e44 | ||
|
|
3c47e5735a | ||
|
|
0750e18ad0 | ||
|
|
a24dd8b2dc | ||
|
|
2022bacd38 | ||
|
|
29fd2cf90d | ||
|
|
daf5826610 | ||
|
|
66808eec27 | ||
|
|
a3e7f5103d | ||
|
|
7e116ca601 | ||
|
|
56accae45e | ||
|
|
967b1bae57 | ||
|
|
6b3aebfa32 | ||
|
|
704c5a1c96 | ||
|
|
781f6d90bf | ||
|
|
40323cc02c | ||
|
|
9cddf46932 | ||
|
|
589c9b9433 | ||
|
|
99b71a37b7 | ||
|
|
b70696fc0a | ||
|
|
4b770cffb4 | ||
|
|
42e043cb01 | ||
|
|
c5c3c45102 | ||
|
|
c3be3c8050 | ||
|
|
cac2c3df13 | ||
|
|
2e4dbb846c | ||
|
|
b5fa4212b0 | ||
|
|
c23b0ee972 | ||
|
|
48e575b885 | ||
|
|
5fd1c93d43 | ||
|
|
a6f4dd031f | ||
|
|
73b3ca7d2c | ||
|
|
de4de1c3f8 | ||
|
|
f8fb1d6646 | ||
|
|
d2de6b42cf | ||
|
|
370a40348b | ||
|
|
ee152be17b | ||
|
|
a799fedea6 | ||
|
|
8f8b5ade55 | ||
|
|
bb0300f605 | ||
|
|
7d3021c34d | ||
|
|
e0e0fadacc | ||
|
|
f511f6c34d | ||
|
|
0a855fce47 | ||
|
|
f149dd304c | ||
|
|
8113f1df7a | ||
|
|
b34f685f70 | ||
|
|
f22676eea9 | ||
|
|
cdfff86c2d | ||
|
|
176e333774 | ||
|
|
3924fe9c18 | ||
|
|
3813249826 | ||
|
|
185a49e628 | ||
|
|
f7ce38dbc1 | ||
|
|
22050b97a7 | ||
|
|
7e1efe6d76 | ||
|
|
f39c1799d2 | ||
|
|
cf27c63b2d | ||
|
|
796d5174f3 | ||
|
|
cc1b36bafd | ||
|
|
255bcff3ac | ||
|
|
877fc1e812 | ||
|
|
affe4cde57 | ||
|
|
232b2eb04c | ||
|
|
baf012cf6a | ||
|
|
25377b20af | ||
|
|
d9fdc20492 | ||
|
|
5ead758f28 | ||
|
|
34603ebd60 | ||
|
|
3b43b304a3 | ||
|
|
3afb7e1a21 | ||
|
|
e24efae5a0 | ||
|
|
4262aea87c | ||
|
|
dde8de0a0d | ||
|
|
7ca78a75e7 | ||
|
|
ec124a0905 | ||
|
|
ca83bc50bf | ||
|
|
52287c025f | ||
|
|
3c7727a8f4 | ||
|
|
3191d98391 | ||
|
|
3bd04a70bd | ||
|
|
feb2fe0d94 | ||
|
|
b211af9d24 | ||
|
|
9740203485 | ||
|
|
a21bd8a417 | ||
|
|
1d42c45a34 | ||
|
|
38895f8f29 | ||
|
|
f316ec0d66 | ||
|
|
671ff628ff | ||
|
|
c7e8075972 | ||
|
|
646dcd3838 | ||
|
|
93d0443998 | ||
|
|
6c20fdb7bd | ||
|
|
e743838d09 | ||
|
|
f1a8192a36 | ||
|
|
87d77f0133 | ||
|
|
aca58ca90f | ||
|
|
a5f4d40901 | ||
|
|
8ed0acf1ec | ||
|
|
39b646349f | ||
|
|
56b0b679c6 | ||
|
|
6baea8d846 | ||
|
|
fcc7acbfc6 | ||
|
|
bf4c72f93b | ||
|
|
c78a342bdc | ||
|
|
c2ed0aab00 | ||
|
|
5563b3b6f8 | ||
|
|
5750c888a6 | ||
|
|
8b564f509b | ||
|
|
c7c778f109 | ||
|
|
cbd78e689c | ||
|
|
8d0c75171c | ||
|
|
6e06cd18da | ||
|
|
198cce29ba | ||
|
|
a518efbedb | ||
|
|
13236822d3 | ||
|
|
2285e5b6da | ||
|
|
1bab793c75 | ||
|
|
566f4e6efc | ||
|
|
83eb54ec7e | ||
|
|
f82f59a024 | ||
|
|
12fab5d5d7 | ||
|
|
7ece66d06e | ||
|
|
a64234e897 | ||
|
|
58bd13b623 | ||
|
|
b97df914dd | ||
|
|
0e371d6e5c | ||
|
|
1106e89c84 | ||
|
|
506f703da5 | ||
|
|
853ee5df17 | ||
|
|
964474837f | ||
|
|
ce4be92a67 | ||
|
|
3d2421f3bf | ||
|
|
28c006ee8c | ||
|
|
193b1d2c9f | ||
|
|
147fe4ee9f | ||
|
|
c233a39052 | ||
|
|
1264184fbb | ||
|
|
5d1992ef05 | ||
|
|
f4369ed694 | ||
|
|
7e7b6d7fb8 | ||
|
|
d915745073 | ||
|
|
0a9d9db5ae | ||
|
|
318b1c1a34 | ||
|
|
0dc93c1d5c | ||
|
|
ac3f30ed6c | ||
|
|
447ab12cec | ||
|
|
2cb1a8de73 | ||
|
|
1d79f5c59b | ||
|
|
065f36ab56 | ||
|
|
c561fe8baf | ||
|
|
2b4852048d | ||
|
|
0144e3ebec | ||
|
|
d410e551f8 | ||
|
|
344f87abe3 | ||
|
|
57c816f906 | ||
|
|
82e1dbe162 | ||
|
|
e6b0bdd4df | ||
|
|
4f13c4d1bb | ||
|
|
c37ec19751 | ||
|
|
aaaf8e75af | ||
|
|
8cc31c6cf1 | ||
|
|
8468af1a20 | ||
|
|
db39d70c75 | ||
|
|
04fff7ebd8 | ||
|
|
b154af87a1 | ||
|
|
8b7480806a | ||
|
|
e29f5ad559 | ||
|
|
4618fd5a43 | ||
|
|
ed979c6aa1 | ||
|
|
141c5da3f9 | ||
|
|
0185ee5df3 | ||
|
|
27c08a65e1 | ||
|
|
de408a5eb7 | ||
|
|
fa3c072dd1 | ||
|
|
aac4b7090a | ||
|
|
f0d664530d | ||
|
|
702140bf93 | ||
|
|
2075db3db0 | ||
|
|
b7fec513cf | ||
|
|
78e7758022 | ||
|
|
818318ddd5 | ||
|
|
39eea781bc | ||
|
|
c4f7bd6a04 | ||
|
|
dd03d0f3c9 | ||
|
|
369ce6af2c | ||
|
|
f83817510d | ||
|
|
bfcd779cfb | ||
|
|
e69c380205 | ||
|
|
61e14aa827 | ||
|
|
0c6c83e278 | ||
|
|
6f52208b68 |
@@ -0,0 +1,165 @@
|
||||
---
|
||||
# DESCRIPTION: Github actions composite action
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: Artifact cache
|
||||
description: >-
|
||||
Cache a directory across workflow runs using a GitHub Actions artifact instead
|
||||
of actions/cache (useful where artifacts are preferable, e.g. no 10 GB limit
|
||||
on public repos). The base 'key' is scoped automatically: per pull request
|
||||
(<key>-pr-<N>) or per branch (<key>-branch-<name>), and on a PR restore falls
|
||||
back to the target branch's cache. Call once with mode=restore before the work
|
||||
and once with mode=save after, passing the same 'key' and 'path'.
|
||||
|
||||
Branch-scoped restores are provenance-checked (the artifact must come from a
|
||||
same-repo push to that branch) - artifact names are a global, unauthenticated
|
||||
namespace, so otherwise a fork PR could forge one.
|
||||
|
||||
mode=restore calls the GitHub API via 'gh', so the caller's job must grant
|
||||
'actions: read'.
|
||||
|
||||
inputs:
|
||||
mode:
|
||||
description: "'restore' or 'save'"
|
||||
required: true
|
||||
path:
|
||||
description: "Directory to cache (archived on save, extracted into on restore)"
|
||||
required: true
|
||||
key:
|
||||
description: "Base artifact name; scoped per PR/branch automatically"
|
||||
required: true
|
||||
retention-days:
|
||||
description: "Artifact retention in days (mode=save)"
|
||||
required: false
|
||||
default: '3'
|
||||
token:
|
||||
description: "Token for the GitHub API (mode=restore); defaults to the job's GITHUB_TOKEN"
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
outputs:
|
||||
key:
|
||||
description: "Echoes the input 'key', so a later save can reuse it without repeating the expression"
|
||||
value: ${{ inputs.key }}
|
||||
cache-hit:
|
||||
description: "'true' if an artifact was restored (mode=restore)"
|
||||
value: ${{ steps.restore.outputs.cache-hit }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check mode
|
||||
shell: bash
|
||||
env:
|
||||
MODE: ${{ inputs.mode }}
|
||||
run: |
|
||||
case "$MODE" in
|
||||
restore|save) ;;
|
||||
*) echo "::error::artifact-cache: 'mode' must be 'restore' or 'save' (got '$MODE')"; exit 1 ;;
|
||||
esac
|
||||
|
||||
# Scope the base key: <key>-pr-<N> for pull requests (with the target branch
|
||||
# as fallback), else <key>-branch-<name>. Refs come via env, not ${{ }}
|
||||
# interpolation, so a branch name with shell metacharacters can't inject.
|
||||
- name: Compute artifact name
|
||||
id: name
|
||||
shell: bash
|
||||
env:
|
||||
BASE_KEY: ${{ inputs.key }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sanitize() { printf '%s' "$1" | tr '/' '-'; } # artifact names cannot contain '/'
|
||||
if [ "$EVENT_NAME" = pull_request ]; then
|
||||
{
|
||||
echo "primary=${BASE_KEY}-pr-${PR_NUMBER}"
|
||||
echo "primary-branch="
|
||||
echo "fallback=${BASE_KEY}-branch-$(sanitize "$BASE_REF")"
|
||||
echo "fallback-branch=$BASE_REF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
{
|
||||
echo "primary=${BASE_KEY}-branch-$(sanitize "$REF_NAME")"
|
||||
echo "primary-branch=$REF_NAME"
|
||||
echo "fallback="
|
||||
echo "fallback-branch="
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Restore artifact cache
|
||||
id: restore
|
||||
if: ${{ inputs.mode == 'restore' }}
|
||||
continue-on-error: true # a cold start is fine; a restore failure must not fail the caller
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ inputs.token || github.token }}
|
||||
CACHE_PATH: ${{ inputs.path }}
|
||||
PRIMARY: ${{ steps.name.outputs.primary }}
|
||||
PRIMARY_BRANCH: ${{ steps.name.outputs.primary-branch }}
|
||||
FALLBACK: ${{ steps.name.outputs.fallback }}
|
||||
FALLBACK_BRANCH: ${{ steps.name.outputs.fallback-branch }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p "$CACHE_PATH"
|
||||
# Newest non-expired artifact named $1 (empty if none). With a non-empty
|
||||
# $2 (branch), require provenance: a same-repo run (head_repository_id ==
|
||||
# repository_id) whose head branch is $2, so a fork cannot forge it.
|
||||
newest_run_id() {
|
||||
want="$1" br="$2" gh api \
|
||||
"/repos/$GITHUB_REPOSITORY/actions/artifacts?name=$1&per_page=100" \
|
||||
--jq '.artifacts[]
|
||||
| select(.expired == false and .name == env.want)
|
||||
| select(env.br == ""
|
||||
or (.workflow_run.head_branch == env.br
|
||||
and .workflow_run.head_repository_id == .workflow_run.repository_id))
|
||||
| [.created_at, (.workflow_run.id | tostring)] | @tsv' \
|
||||
| sort | tail -n1 | cut -f2
|
||||
}
|
||||
restore() { # $1=name $2=branch(empty ok); returns 0 on a successful restore
|
||||
local name="$1" br="$2" rid tarball
|
||||
[ -n "$name" ] || return 1
|
||||
rid="$(newest_run_id "$name" "$br")" || return 1
|
||||
[ -n "$rid" ] || return 1
|
||||
echo "Restoring '$CACHE_PATH' from artifact '$name' (run $rid)"
|
||||
gh run download "$rid" -R "$GITHUB_REPOSITORY" --name "$name" --dir "$RUNNER_TEMP/artifact-cache-dl" || return 1
|
||||
# Each artifact holds a single tarball; match by extension so the inner
|
||||
# filename is not part of the contract.
|
||||
tarball="$(find "$RUNNER_TEMP/artifact-cache-dl" -name '*.tar.zst' -print -quit)"
|
||||
[ -n "$tarball" ] || return 1
|
||||
tar -I zstd -x -f "$tarball" -C "$CACHE_PATH"
|
||||
}
|
||||
if restore "$PRIMARY" "$PRIMARY_BRANCH"; then
|
||||
echo "Restored from primary key"
|
||||
echo "cache-hit=true" >> "$GITHUB_OUTPUT"
|
||||
elif restore "$FALLBACK" "$FALLBACK_BRANCH"; then
|
||||
echo "Restored from fallback key"
|
||||
echo "cache-hit=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "No matching artifact found; starting cold"
|
||||
echo "cache-hit=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Pack artifact cache
|
||||
id: pack
|
||||
if: ${{ inputs.mode == 'save' }}
|
||||
shell: bash
|
||||
env:
|
||||
CACHE_PATH: ${{ inputs.path }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
packdir="$(mktemp -d)" # unique dir so concurrent saves cannot collide
|
||||
tar -I 'zstd -T0' -cf "$packdir/cache.tar.zst" -C "$CACHE_PATH" .
|
||||
echo "tarball=$packdir/cache.tar.zst" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload artifact cache
|
||||
if: ${{ inputs.mode == 'save' }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: ${{ steps.name.outputs.primary }}
|
||||
path: ${{ steps.pack.outputs.tarball }}
|
||||
retention-days: ${{ inputs.retention-days }}
|
||||
overwrite: true
|
||||
compression-level: 0 # tarball is already zstd-compressed
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
# DESCRIPTION: Github actions composite action
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: Install dependencies
|
||||
description: >-
|
||||
Install the dependencies of a CI stage with 'ci/ci-install.bash', caching the
|
||||
'.deb' files apt downloads for that stage, so a package is fetched from the
|
||||
network at most once per runner image. On hosts that do not use apt this is
|
||||
just the install, there is nothing to cache.
|
||||
|
||||
inputs:
|
||||
repo:
|
||||
description: "Verilator checkout holding the ci/ scripts"
|
||||
required: false
|
||||
default: repo
|
||||
stage:
|
||||
description: "CI stage to install the dependencies of, e.g. 'build'"
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Compute apt cache key
|
||||
id: cachekey
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.repo }}
|
||||
env:
|
||||
STAGE: ${{ inputs.stage }}
|
||||
run: |
|
||||
source ci/ci-common.bash
|
||||
host="${DISTRO_ID:-$HOST_OS}${DISTRO_VERSION:+-$DISTRO_VERSION}"
|
||||
echo "key=apt-${host}-${STAGE}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore apt downloads
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
with:
|
||||
path: ${{ runner.temp }}/apt-cache
|
||||
key: ${{ steps.cachekey.outputs.key }}
|
||||
restore-keys: |
|
||||
${{ steps.cachekey.outputs.key }}-
|
||||
|
||||
- name: Seed apt archive from cache
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
shell: bash
|
||||
env:
|
||||
APT_CACHE_DIR: ${{ runner.temp }}/apt-cache
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Ask apt where it keeps its downloads, rather than assume
|
||||
eval "$(apt-config shell ARCHIVES Dir::Cache::archives/d)"
|
||||
ARCHIVES=${ARCHIVES%/} # '/d' yields a trailing slash
|
||||
# Wipe the archive first, so that whatever the runner image left behind is not stored
|
||||
sudo apt-get -o DPkg::Lock::Timeout=60 clean
|
||||
# The directory is absent when nothing was restored, e.g. on a cold start
|
||||
if [ -d "$APT_CACHE_DIR" ] &&
|
||||
[ -n "$(find "$APT_CACHE_DIR" -maxdepth 1 -name '*.deb' -print -quit)" ]; then
|
||||
sudo cp "$APT_CACHE_DIR"/*.deb "$ARCHIVES"
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.repo }}
|
||||
env:
|
||||
STAGE: ${{ inputs.stage }}
|
||||
run: ./ci/ci-install.bash "$STAGE"
|
||||
|
||||
- name: Collect apt downloads
|
||||
id: collect
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
shell: bash
|
||||
env:
|
||||
APT_CACHE_DIR: ${{ runner.temp }}/apt-cache
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Ask apt where it keeps its downloads, rather than assume
|
||||
eval "$(apt-config shell ARCHIVES Dir::Cache::archives/d)"
|
||||
ARCHIVES=${ARCHIVES%/} # '/d' yields a trailing slash
|
||||
# Sorted names of the '.deb' files in a directory, empty if it has none
|
||||
list-debs() {
|
||||
[ -d "$1" ] || return 0
|
||||
find "$1" -maxdepth 1 -name '*.deb' -printf '%f\n' | sort
|
||||
}
|
||||
# Superseded downloads can no longer be fetched, so drop them
|
||||
sudo apt-get -o DPkg::Lock::Timeout=60 autoclean
|
||||
downloaded=$(list-debs "$ARCHIVES")
|
||||
if [ -z "$downloaded" ]; then
|
||||
echo "Apt kept no '.deb' files, nothing to cache"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$downloaded" = "$(list-debs "$APT_CACHE_DIR")" ]; then
|
||||
echo "Downloads unchanged, keeping the restored cache entry"
|
||||
exit 0
|
||||
fi
|
||||
# Mirror the archive, rather than add to it, so that the cache cannot
|
||||
# grow without bound as package versions churn
|
||||
mkdir -p "$APT_CACHE_DIR"
|
||||
rm -f "$APT_CACHE_DIR"/*.deb
|
||||
sudo cp "$ARCHIVES"/*.deb "$APT_CACHE_DIR"
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Save apt downloads
|
||||
if: ${{ steps.collect.outputs.changed == 'true' }}
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
with:
|
||||
path: ${{ runner.temp }}/apt-cache
|
||||
key: ${{ steps.cachekey.outputs.key }}-${{ github.run_id }}
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
# DESCRIPTION: Github actions composite action
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: Set up Python venv
|
||||
description: >-
|
||||
Restore, create, and optionally save the make-managed Python venv in a GitHub
|
||||
Actions cache. The cache key combines the venv path (venvs are not
|
||||
relocatable), the host OS and version, the Python major.minor version, and
|
||||
the python-dev-requirements.txt hash, so any of those changing invalidates
|
||||
it. Requires the already-configured Verilator tree to be present in 'repo'.
|
||||
|
||||
inputs:
|
||||
repo:
|
||||
description: "Verilator checkout holding the Makefile and .venv"
|
||||
required: false
|
||||
default: repo
|
||||
save:
|
||||
description: "Save the cache on a miss (set true only on trusted branches)"
|
||||
required: false
|
||||
default: 'false'
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Compute Python venv cache key
|
||||
id: cachekey
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.repo }}
|
||||
run: |
|
||||
source ci/ci-common.bash
|
||||
host="${DISTRO_ID:-$HOST_OS}${DISTRO_VERSION:+-$DISTRO_VERSION}"
|
||||
pyver="$(python3 -c 'import sys; print("%d.%d" % sys.version_info[:2])')"
|
||||
reqs="$(sha256sum python-dev-requirements.txt | cut -d' ' -f1)"
|
||||
venv="${PWD#/}/.venv" # absolute venv path (leading '/' trimmed)
|
||||
echo "key=venv-${host}-py${pyver}-${reqs}-${venv//\//-}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore Python venv
|
||||
id: venv
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
with:
|
||||
path: ${{ inputs.repo }}/.venv
|
||||
key: ${{ steps.cachekey.outputs.key }}
|
||||
|
||||
- name: Create Python venv
|
||||
if: ${{ steps.venv.outputs.cache-hit != 'true' }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.repo }}
|
||||
run: make venv
|
||||
|
||||
- name: Save Python venv
|
||||
if: ${{ inputs.save == 'true' && steps.venv.outputs.cache-hit != 'true' }}
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
with:
|
||||
path: ${{ inputs.repo }}/.venv
|
||||
key: ${{ steps.venv.outputs.cache-primary-key }}
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
# DESCRIPTION: Github actions composite action
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: Upload PR notification
|
||||
description: >-
|
||||
Upload the 'pr-notification' artifact, holding a comment for 'pages.yml' to
|
||||
post on a pull request. The given directory must hold the content of the
|
||||
comment in 'body.txt', and its single line summary in 'hist.txt'.
|
||||
|
||||
inputs:
|
||||
path:
|
||||
description: "Directory holding the notification contents"
|
||||
required: true
|
||||
key:
|
||||
description: "Unique key for combining comments"
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Validate
|
||||
shell: bash
|
||||
env:
|
||||
NOTIFICATION_DIR: ${{ inputs.path }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
run: |
|
||||
# The notification is posted on the pull request of the triggering event
|
||||
if [ "${EVENT_NAME}" != "pull_request" ]; then
|
||||
echo "This action can only be used on a 'pull_request' run, but event is '${EVENT_NAME}'" >&2
|
||||
exit 1
|
||||
fi
|
||||
# The content of the comment, and its single line summary
|
||||
for f in body.txt hist.txt; do
|
||||
if [ ! -f "${NOTIFICATION_DIR}/${f}" ]; then
|
||||
echo "No '${f}' in '${NOTIFICATION_DIR}'" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
# Nothing else, so the artifact holds only what this action puts in it
|
||||
EXTRA=$(find "${NOTIFICATION_DIR}" -mindepth 1 -not -name body.txt -not -name hist.txt)
|
||||
if [ -n "${EXTRA}" ]; then
|
||||
echo "Unexpected content in '${NOTIFICATION_DIR}':" >&2
|
||||
echo "${EXTRA}" >&2
|
||||
exit 1
|
||||
fi
|
||||
# 'ci-pages-notify.bash' stashes 'hist.txt' in an HTML comment, and later
|
||||
# shows it in the history of the notifications superseding this one, so
|
||||
# it must be a single line, and must neither open nor close an HTML comment
|
||||
if [ $(wc -l < "${NOTIFICATION_DIR}/hist.txt") -ne 1 ]; then
|
||||
echo "'hist.txt' must hold exactly one line" >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -q -e '<!--' -e '-->' "${NOTIFICATION_DIR}/hist.txt"; then
|
||||
echo "'hist.txt' must not contain '<!--' or '-->'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Add metadata
|
||||
shell: bash
|
||||
env:
|
||||
NOTIFICATION_DIR: ${{ inputs.path }}
|
||||
NOTIFICATION_KEY: ${{ inputs.key }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
echo "${NOTIFICATION_KEY}" > "${NOTIFICATION_DIR}/key.txt"
|
||||
echo "${PR_NUMBER}" > "${NOTIFICATION_DIR}/pr-number.txt"
|
||||
ls -lsha "${NOTIFICATION_DIR}"
|
||||
|
||||
- name: Upload notification
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: ${{ inputs.path }}
|
||||
name: pr-notification
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
# DESCRIPTION: Github actions config
|
||||
# This name is key to badges in README.rst, so we use the name build
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: build-test
|
||||
name: Regressions
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -16,6 +15,7 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -29,272 +29,141 @@ concurrency:
|
||||
jobs:
|
||||
|
||||
build-2604-gcc:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
name: Build | 26.04 | gcc
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: gcc
|
||||
runs-on: ubuntu-26.04
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: linux
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: ubuntu-26.04, cc: gcc, asan: 0}
|
||||
|
||||
build-2604-clang:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
name: Build | 26.04 | clang
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: clang
|
||||
dev-asan: true # Build (and run) with address sanitizer
|
||||
runs-on: ubuntu-26.04
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: linux
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: ubuntu-26.04, cc: clang, asan: 1}
|
||||
|
||||
build-2404-gcc:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
name: Build | 24.04 | gcc
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: gcc
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: linux
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: ubuntu-24.04, cc: gcc, asan: 0}
|
||||
|
||||
build-2404-clang:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
name: Build | 24.04 | clang
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: clang
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: linux
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: ubuntu-24.04, cc: clang, asan: 0}
|
||||
|
||||
build-2204-gcc:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
name: Build | 22.04 | gcc
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: gcc
|
||||
runs-on: ubuntu-22.04
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: linux
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: ubuntu-22.04, cc: gcc, asan: 0}
|
||||
|
||||
build-osx-gcc:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
build-macos-15-clang:
|
||||
name: Build | macos-15 | clang
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: clang
|
||||
runs-on: macos-15
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: osx
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: macos-15, cc: gcc, asan: 0}
|
||||
|
||||
build-osx-clang:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: osx
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: macos-15, cc: clang, asan: 0}
|
||||
|
||||
build-windows:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: windows-2025-vs2026, cc: msvc}
|
||||
env:
|
||||
CI_OS_NAME: win
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
name: Build | windows-2025-vs2026 | msvc
|
||||
runs-on: windows-2025-vs2026
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
path: repo
|
||||
- name: Cache $CCACHE_DIR
|
||||
- name: Cache win_flex_bison
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: msbuild-msvc-cmake
|
||||
path: ${{ github.workspace }}/win_flex_bison
|
||||
key: win_flex_bison
|
||||
- name: compile
|
||||
env:
|
||||
WIN_FLEX_BISON: ${{ github.workspace }}/.ccache
|
||||
run: ./ci/ci-win-compile.ps1
|
||||
- name: test build
|
||||
run: ./ci/ci-win-test.ps1
|
||||
- name: Zip up repository
|
||||
run: Compress-Archive -LiteralPath install -DestinationPath verilator.zip
|
||||
- name: Upload zip archive
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: ${{ github.workspace }}/repo/verilator.zip
|
||||
name: verilator-win.zip
|
||||
|
||||
test-2604-gcc:
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
name: Test | 26.04 | gcc | ${{ matrix.suite }}
|
||||
needs: build-2604-gcc
|
||||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build-2604-gcc.outputs.archive }}
|
||||
os: ${{ matrix.os }}
|
||||
cc: ${{ matrix.cc }}
|
||||
reloc: ${{ matrix.reloc }}
|
||||
cc: gcc
|
||||
runs-on: ubuntu-26.04
|
||||
suite: ${{ matrix.suite }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Ubuntu 26.04 gcc
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-1}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-2}
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||
|
||||
test-2604-clang:
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
name: Test | 26.04 | clang | ${{ matrix.suite }}
|
||||
needs: build-2604-clang
|
||||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build-2604-clang.outputs.archive }}
|
||||
os: ${{ matrix.os }}
|
||||
cc: ${{ matrix.cc }}
|
||||
reloc: ${{ matrix.reloc }}
|
||||
cc: clang
|
||||
runs-on: ubuntu-26.04
|
||||
suite: ${{ matrix.suite }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Ubuntu 26.04 clang
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-0}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-3}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-1}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-2}
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||
|
||||
test-2404-gcc:
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
name: Test | 24.04 | gcc | ${{ matrix.suite }}
|
||||
needs: build-2404-gcc
|
||||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build-2404-gcc.outputs.archive }}
|
||||
os: ${{ matrix.os }}
|
||||
cc: ${{ matrix.cc }}
|
||||
reloc: ${{ matrix.reloc }}
|
||||
cc: gcc
|
||||
runs-on: ubuntu-24.04
|
||||
suite: ${{ matrix.suite }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Ubuntu 24.04 gcc
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-1}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-2}
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||
|
||||
test-2404-clang:
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
name: Test | 24.04 | clang | ${{ matrix.suite }}
|
||||
needs: build-2404-clang
|
||||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build-2404-clang.outputs.archive }}
|
||||
os: ${{ matrix.os }}
|
||||
cc: ${{ matrix.cc }}
|
||||
reloc: ${{ matrix.reloc }}
|
||||
cc: clang
|
||||
reloc: true # Test with relocated installation
|
||||
runs-on: ubuntu-24.04
|
||||
suite: ${{ matrix.suite }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Ubuntu 24.04 clang
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-0}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-3}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-1}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-2}
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||
|
||||
test-2204-gcc:
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
name: Test | 22.04 | gcc | ${{ matrix.suite }}
|
||||
needs: build-2204-gcc
|
||||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build-2204-gcc.outputs.archive }}
|
||||
os: ${{ matrix.os }}
|
||||
cc: ${{ matrix.cc }}
|
||||
reloc: ${{ matrix.reloc }}
|
||||
cc: gcc
|
||||
runs-on: ubuntu-22.04
|
||||
suite: ${{ matrix.suite }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Ubuntu 22.04 gcc
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-1}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-2}
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||
|
||||
lint-py:
|
||||
name: Lint Python
|
||||
|
||||
@@ -16,5 +16,5 @@ jobs:
|
||||
name: "'docs/CONTRIBUTORS' was signed"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- run: test_regress/t/t_dist_contributors.py
|
||||
|
||||
@@ -13,6 +13,7 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -38,15 +39,13 @@ jobs:
|
||||
(github.event_name == 'workflow_dispatch')
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: gcc
|
||||
dev-gcov: true
|
||||
runs-on: ubuntu-24.04
|
||||
# For pull requests, build the head of the pull request branch, not the
|
||||
# merge commit, otherwise patch coverage would include the changes
|
||||
# between the root of the pull request and the target branch
|
||||
sha: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
os: ubuntu-24.04
|
||||
os-name: linux
|
||||
cc: gcc
|
||||
dev-asan: 0
|
||||
dev-gcov: 1
|
||||
|
||||
test:
|
||||
name: Test | ${{ matrix.test }}${{ matrix.num }}
|
||||
@@ -54,11 +53,10 @@ jobs:
|
||||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build.outputs.archive }}
|
||||
os: ubuntu-24.04
|
||||
cc: gcc
|
||||
reloc: 0
|
||||
dev-gcov: true
|
||||
runs-on: ubuntu-24.04
|
||||
suite: ${{ matrix.test }}${{ matrix.num }}
|
||||
dev-gcov: 1
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -74,7 +72,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
|
||||
- name: Download code coverage data
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
@@ -106,13 +104,6 @@ jobs:
|
||||
if: ${{ contains(needs.*.result, 'success') && !cancelled() }}
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
echo 'set man-db/auto-update false' | sudo debconf-communicate >/dev/null
|
||||
sudo dpkg-reconfigure man-db
|
||||
sudo apt install lcov || \
|
||||
sudo apt install lcov
|
||||
|
||||
- name: Download repository archive
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
@@ -121,9 +112,14 @@ jobs:
|
||||
|
||||
- name: Unpack repository archive
|
||||
run: |
|
||||
tar -x -z -f ${{ needs.build.outputs.archive }}
|
||||
tar --zstd -x -f ${{ needs.build.outputs.archive }}
|
||||
ls -lsha
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./repo/.github/actions/install-deps
|
||||
with:
|
||||
stage: coverage
|
||||
|
||||
- name: Download code coverage data
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
@@ -136,53 +132,30 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
ls -lsha obj_coverage
|
||||
# Combine reports from test jobs
|
||||
nodist/fastcov.py -C obj_coverage/verilator-*.info --lcov -o obj_coverage/verilator.info
|
||||
# For a PR, report patch coverage against the merge-base between the head of the PR and the target branch
|
||||
ci/ci-coverage-report.bash \
|
||||
${{ github.run_id }} \
|
||||
"${{ github.event_name == 'pull_request' && github.event.number || '' }}" \
|
||||
"${{ github.event.pull_request.base.sha }}" \
|
||||
"${{ github.event.pull_request.head.sha }}"
|
||||
# Create the report artifact. Note there is no report if the patch is empty.
|
||||
mkdir ../report-artifact
|
||||
[ ! -d obj_coverage/report ] || mv obj_coverage/report ../report-artifact/
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
COVERAGE_BASE=$(git rev-parse --short $(git merge-base ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}))
|
||||
make coverage-report COVERAGE_BASE=${COVERAGE_BASE} |& tee ${{ github.workspace }}/make-coverage-report.log
|
||||
else
|
||||
make coverage-report
|
||||
fi
|
||||
# Remove data files
|
||||
rm -f obj_coverage/verilator*.info
|
||||
# Some extra work for PRs only
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
# Save PR number in report
|
||||
echo ${{ github.event.number }} > obj_coverage/pr-number.txt
|
||||
# Generate notification comment content
|
||||
mkdir -p notification
|
||||
echo ${{ github.event.number }} > notification/pr-number.txt
|
||||
NUM=$(gh run view ${{ github.run_id }} --json number --jq ".number")
|
||||
URL=$(gh run view ${{ github.run_id }} --json url --jq ".url")
|
||||
echo "Patch coverage from PR workflow [#$NUM]($URL) (code coverage of lines changed relative to ${COVERAGE_BASE}):" > notification/body.txt
|
||||
if [[ ! -f obj_coverage/empty-patch ]]; then
|
||||
echo "<pre>" >> notification/body.txt
|
||||
grep -E "(lines|branches)\.*:" ${{ github.workspace }}/make-coverage-report.log | sed "s/\.*:/:/" >> notification/body.txt || true
|
||||
echo "</pre>" >> notification/body.txt
|
||||
echo "Report: [${{ github.run_id }}](https://${{ github.repository_owner }}.github.io/verilator/coverage-reports/${{ github.run_id }}/index.html)" >> notification/body.txt
|
||||
echo "" >> notification/body.txt
|
||||
echo "Please get to 100% line coverage, and understand all branches; see https://github.com/verilator/verilator/blob/master/docs/internals.rst#code-coverage-results" >> notification/body.txt
|
||||
else
|
||||
echo "Patch contains no code changes" >> notification/body.txt
|
||||
fi
|
||||
cat notification/body.txt
|
||||
echo ${{ github.event.number }} > ../report-artifact/pr-number.txt
|
||||
fi
|
||||
|
||||
- name: Upload report
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: repo/obj_coverage
|
||||
path: report-artifact
|
||||
name: coverage-report
|
||||
|
||||
- name: Upload notification
|
||||
- name: Upload PR notification
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
uses: ./repo/.github/actions/upload-pr-notification
|
||||
with:
|
||||
path: repo/notification
|
||||
name: pr-notification
|
||||
key: coverage
|
||||
|
||||
# Create GitHub issue for failed scheduled jobs
|
||||
# This should always be the last job (we want an issue if anything breaks)
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
|
||||
- name: Extract context variables
|
||||
run: |
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
|
||||
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
|
||||
with:
|
||||
images: |
|
||||
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
||||
@@ -64,21 +64,21 @@ jobs:
|
||||
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4
|
||||
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4
|
||||
with:
|
||||
buildkitd-flags: --debug
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Build and Push to Docker
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
|
||||
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
context: ${{ env.build_context }}
|
||||
|
||||
@@ -11,35 +11,47 @@ on:
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: repo
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-24.04
|
||||
name: Ubuntu 24.04 | format
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CI_RUNS_ON: ubuntu-24.04
|
||||
CI_COMMIT: ${{ github.sha }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
path: repo
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install packages for build
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
uses: ./repo/.github/actions/install-deps
|
||||
with:
|
||||
stage: format
|
||||
|
||||
- name: Configure git identity
|
||||
run: |
|
||||
sudo apt install clang-format-18 || \
|
||||
sudo apt install clang-format-18
|
||||
git config --global user.email "[email protected]"
|
||||
git config --global user.name "github action"
|
||||
- name: Format code
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
autoconf
|
||||
./configure
|
||||
make venv
|
||||
|
||||
- name: Set up Python venv
|
||||
uses: ./repo/.github/actions/setup-venv
|
||||
with:
|
||||
save: ${{ github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: Format code
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
make -j 4 format CLANGFORMAT=clang-format-18
|
||||
git status
|
||||
|
||||
- name: Push
|
||||
run: |-
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
|
||||
@@ -7,7 +7,7 @@ name: Pages
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths: ["ci/**", ".github/workflows"]
|
||||
paths: ["ci/**", ".github/workflows/**"]
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: ["Code coverage", "RTLMeter"]
|
||||
@@ -22,8 +22,11 @@ permissions:
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run
|
||||
# in-progress and latest queued. However, do NOT cancel in-progress runs as we
|
||||
# want to allow these deployments to complete.
|
||||
# A skipped-upstream run does no work (see the build job's if:), so put it in its
|
||||
# own throwaway group: otherwise, as the newest queued run, it would cancel a
|
||||
# pending real run and then deploy nothing.
|
||||
concurrency:
|
||||
group: "pages"
|
||||
group: ${{ (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'skipped') && format('pages-skip-{0}', github.run_id) || 'pages' }}
|
||||
cancel-in-progress: false
|
||||
|
||||
defaults:
|
||||
@@ -34,19 +37,21 @@ jobs:
|
||||
build:
|
||||
name: Build content
|
||||
runs-on: ubuntu-24.04
|
||||
# A skipped upstream run (e.g. Code coverage / RTLMeter on a branch push)
|
||||
# still fires workflow_run; don't rebuild and redeploy for it. deploy and
|
||||
# notify need this job, so they cascade-skip too.
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion != 'skipped' }}
|
||||
outputs:
|
||||
pr-run-ids: ${{ steps.build.outputs.pr-run-ids }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- name: Build pages
|
||||
id: build
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
./ci/ci-pages.bash
|
||||
ls -lsha
|
||||
tree -L 3 pages
|
||||
- name: Upload pages artifact
|
||||
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
|
||||
with:
|
||||
@@ -58,9 +63,16 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
url: ${{ steps.deploy-2.outputs.page_url || steps.deploy-1.outputs.page_url }}
|
||||
steps:
|
||||
# GitHub's Pages backend intermittently fails a deployment mid-sync, retry
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deploy-1
|
||||
continue-on-error: true
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
|
||||
- name: Deploy to GitHub Pages (retry)
|
||||
id: deploy-2
|
||||
if: steps.deploy-1.outcome == 'failure'
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
|
||||
|
||||
notify:
|
||||
@@ -70,7 +82,7 @@ jobs:
|
||||
if: ${{ github.repository == 'verilator/verilator' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
# Use the Verilator CI app to post the comment
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
@@ -83,5 +95,7 @@ jobs:
|
||||
- name: Comment on PR
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
# Slug of the app the above token belongs to, see 'ci-pages-notify.bash'
|
||||
GH_AUTHOR: ${{ steps.generate-token.outputs.app-slug }}
|
||||
PR_RUN_IDS: ${{ needs.build.outputs.pr-run-ids }}
|
||||
run: ./ci/ci-pages-notify.bash
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
# Build and push verilator docker image when tags are pushed to the repository.
|
||||
# The following variable(s) must be configured in the github repository:
|
||||
# DOCKER_HUB_NAMESPACE: docker hub namespace.
|
||||
# The following secrets must be configured in the github repository:
|
||||
# DOCKER_HUB_USER: user name for logging into docker hub
|
||||
# DOCKER_HUB_ACCESS_TOKEN: docker hub access token.
|
||||
name: Maintainance - PR conflict check
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
pull_request_target:
|
||||
types: [synchronize]
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-26.04
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: check if prs are dirty
|
||||
uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # 3.1.0
|
||||
with:
|
||||
dirtyLabel: "pr-blocked: needs-rebase"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
@@ -7,37 +7,47 @@ name: reusable-build
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cc:
|
||||
description: "Compiler to use: 'gcc' or 'clang'"
|
||||
required: true
|
||||
type: string
|
||||
ccwarn:
|
||||
description: "Build Verilator with warnings treated as errors (--enable-ccwarn)"
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
dev-asan:
|
||||
description: "Build Verilator with the address sanitizer (--enable-dev-asan)"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
dev-gcov:
|
||||
description: "Build Verilator with gcov instrumentation (--enable-dev-gcov)"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
install:
|
||||
description: "Archive the Verilator installation, not the repo tree"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
runs-on:
|
||||
description: "Runner to build on, e.g. ubuntu-24.04"
|
||||
required: true
|
||||
type: string
|
||||
sha:
|
||||
description: "Commit SHA to build"
|
||||
required: true
|
||||
type: string
|
||||
os: # e.g. ubuntu-24.04
|
||||
required: true
|
||||
type: string
|
||||
cc: # 'clang' or 'gcc'
|
||||
required: true
|
||||
type: string
|
||||
os-name: # 'linux' or 'osx'
|
||||
required: true
|
||||
type: string
|
||||
dev-asan:
|
||||
required: true
|
||||
type: number
|
||||
dev-gcov:
|
||||
required: true
|
||||
type: number
|
||||
outputs:
|
||||
archive:
|
||||
description: "Name of the built repository archive artifact"
|
||||
description: "Name of the built archive artifact"
|
||||
value: ${{ jobs.build.outputs.archive }}
|
||||
|
||||
env:
|
||||
CI_OS_NAME: ${{ inputs.os-name }}
|
||||
CCACHE_COMPRESS: 1
|
||||
CACHE_BASE_KEY: build-${{ inputs.runs-on }}-${{ inputs.cc }}${{ inputs.ccwarn && '-ccwarn' || '' }}${{ inputs.dev-asan && '-asan' || '' }}${{ inputs.dev-gcov && '-gcov' || '' }}
|
||||
CCACHE_COMPILERCHECK: content
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
INSTALL_DIR: ${{ github.workspace }}/install
|
||||
RELOC_DIR: ${{ github.workspace }}/relloc
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -48,26 +58,28 @@ jobs:
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ${{ inputs.os }}
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
outputs:
|
||||
archive: ${{ steps.create-archive.outputs.archive }}
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
CI_DEV_ASAN: ${{ inputs.dev-asan }}
|
||||
CI_DEV_GCOV: ${{ inputs.dev-gcov }}
|
||||
CI_RUNS_ON: ${{ inputs.os }}
|
||||
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
|
||||
CACHE_BASE_KEY: build-${{ inputs.os }}-${{ inputs.cc }}
|
||||
CCACHE_MAXSIZE: 1000M # Per build matrix entry (* 5 = 5000M in total)
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
path: repo
|
||||
ref: ${{ inputs.sha }}
|
||||
fetch-depth: ${{ inputs.dev-gcov && '0' || '1' }} # Coverage flow needs full history
|
||||
# Coverage needs full history; install needs it for the 'git describe'
|
||||
# behind 'verilator --version'
|
||||
fetch-depth: ${{ (inputs.install || inputs.dev-gcov) && '0' || '1' }}
|
||||
|
||||
- name: Configure ccache
|
||||
run: |
|
||||
# ccache is unreliable on macOS, and does nothing with 'gcc --coverage'
|
||||
if [ "${{ startsWith(inputs.runs-on, 'macos') }}" = true ] || [ "${{ inputs.dev-gcov }}" = true ]; then
|
||||
echo "CCACHE_DISABLE=1" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Cache $CCACHE_DIR
|
||||
if: ${{ env.CCACHE_DISABLE != '1' }}
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||
@@ -75,25 +87,46 @@ jobs:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ env.CACHE_KEY }}-${{ inputs.sha }}
|
||||
restore-keys: |
|
||||
${{ env.CACHE_KEY }}-
|
||||
${{ env.CACHE_KEY }}
|
||||
|
||||
- name: Install packages for build
|
||||
run: ./ci/ci-install.bash
|
||||
uses: ./repo/.github/actions/install-deps
|
||||
with:
|
||||
stage: build
|
||||
|
||||
- name: Build
|
||||
run: ./ci/ci-script.bash
|
||||
run: |
|
||||
./ci/ci-build.bash \
|
||||
--prefix ${{ github.workspace }}/install \
|
||||
--compiler ${{ inputs.cc }} \
|
||||
--light-debug \
|
||||
${{ inputs.ccwarn && '--ccwarn' || '' }} \
|
||||
${{ inputs.dev-asan && '--asan' || '' }} \
|
||||
${{ inputs.dev-gcov && '--gcov' || '' }}
|
||||
|
||||
- name: Create repository archive
|
||||
- name: Install
|
||||
if: ${{ inputs.install }}
|
||||
run: make install
|
||||
|
||||
- name: Create archive
|
||||
id: create-archive
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
# Name of the archive must be unique based on the build parameters
|
||||
ARCHIVE=verilator-${{ inputs.sha }}-${{ inputs.os }}-${{ inputs.cc }}-${{ inputs.dev-asan }}-${{ inputs.dev-gcov }}.tar.gz
|
||||
tar --posix -c -z -f $ARCHIVE repo
|
||||
# Archive name, unique per build; flavour tags mark non-default builds
|
||||
ARCHIVE="verilator-${{ inputs.sha }}-${{ inputs.runs-on }}-${{ inputs.cc }}"
|
||||
ARCHIVE="$ARCHIVE${{ inputs.ccwarn && '-ccwarn' || '' }}"
|
||||
ARCHIVE="$ARCHIVE${{ inputs.dev-asan && '-asan' || '' }}"
|
||||
ARCHIVE="$ARCHIVE${{ inputs.dev-gcov && '-gcov' || '' }}"
|
||||
ARCHIVE="$ARCHIVE.tar.zst"
|
||||
# zstd compresses faster than gzip and decompresses far faster in the many downstream jobs
|
||||
ZSTD_NBTHREADS=0 tar --posix --zstd -c -f "$ARCHIVE" ${{ inputs.install && 'install' || 'repo' }}
|
||||
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload repository archive
|
||||
- name: Upload archive
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
|
||||
name: ${{ steps.create-archive.outputs.archive }}
|
||||
overwrite: true
|
||||
# Archive is already zstd-compressed; skip upload-artifact's zip pass
|
||||
compression-level: 0
|
||||
|
||||
@@ -7,14 +7,6 @@ name: reusable-lint-py
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
CI_RUNS_ON: ubuntu-22.04
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@@ -27,23 +19,24 @@ jobs:
|
||||
name: Sub-lint | Python
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
path: repo
|
||||
|
||||
- name: Install packages for build
|
||||
run: ./ci/ci-install.bash
|
||||
- name: Install dependencies
|
||||
uses: ./repo/.github/actions/install-deps
|
||||
with:
|
||||
stage: lint-py
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
autoconf
|
||||
./configure --enable-longtests --enable-ccwarn
|
||||
|
||||
- name: Install python dependencies
|
||||
run: |
|
||||
sudo apt install python3-clang || \
|
||||
sudo apt install python3-clang
|
||||
make venv
|
||||
- name: Set up Python venv
|
||||
uses: ./repo/.github/actions/setup-venv
|
||||
with:
|
||||
save: ${{ github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: Lint
|
||||
run: |-
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
---
|
||||
# DESCRIPTION: Github actions config
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: reusable-rtlmeter-build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
runs-on:
|
||||
description: "Runner to use, e.g.: ubuntu-24.04"
|
||||
type: string
|
||||
required: true
|
||||
cc:
|
||||
description: "Compiler to use: 'gcc' or 'clang'"
|
||||
type: string
|
||||
required: true
|
||||
sha:
|
||||
description: "Git SHA to build"
|
||||
type: string
|
||||
required: true
|
||||
outputs:
|
||||
archive:
|
||||
description: "Name of the built installation archive artifact"
|
||||
value: ${{ jobs.build.outputs.archive }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/ccache
|
||||
CCACHE_MAXSIZE: 512M
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
outputs:
|
||||
archive: ${{ steps.create-archive.outputs.archive }}
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
echo "path-exclude /usr/share/doc/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
echo "path-exclude /usr/share/man/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
sudo apt update || \
|
||||
sudo apt update
|
||||
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev || \
|
||||
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev
|
||||
|
||||
- name: Use saved ccache
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
with:
|
||||
path: ccache
|
||||
key: rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
restore-keys: |
|
||||
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}-${{ github.run_id }}
|
||||
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}
|
||||
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
path: repo
|
||||
ref: ${{ inputs.sha }}
|
||||
fetch-depth: 0 # Required for 'git describe' used for 'verilator --version'
|
||||
|
||||
- name: Configure
|
||||
working-directory: repo
|
||||
run: |
|
||||
autoconf
|
||||
./configure --prefix=${{ github.workspace }}/install CXX=${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
|
||||
|
||||
- name: Make
|
||||
working-directory: repo
|
||||
run: make -j $(nproc)
|
||||
|
||||
- name: Install
|
||||
working-directory: repo
|
||||
run: make install
|
||||
|
||||
- name: Tar up installation
|
||||
id: create-archive
|
||||
run: |
|
||||
SHA=$(git -C repo rev-parse HEAD)
|
||||
ARCHIVE=verilator-$SHA-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}.tar.gz
|
||||
tar --posix -c -z -f $ARCHIVE install
|
||||
echo "archive=$ARCHIVE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Verilator installation archive
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: ${{ steps.create-archive.outputs.archive }}
|
||||
name: ${{ steps.create-archive.outputs.archive }}
|
||||
overwrite: true
|
||||
@@ -20,11 +20,11 @@ on:
|
||||
type: string
|
||||
required: true
|
||||
verilator-archive-new:
|
||||
description: "Name of the installation archive artifact from reusable-rtlmeter-build, new version"
|
||||
description: "Name of the installation archive artifact from reusable-build, new version"
|
||||
type: string
|
||||
required: true
|
||||
verilator-archive-old:
|
||||
description: "Name of the installation archive artifact from reusable-rtlmeter-build, old version"
|
||||
description: "Name of the installation archive artifact from reusable-build, old version"
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
@@ -53,8 +53,8 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/ccache
|
||||
CCACHE_MAXSIZE: 512M
|
||||
# RTLMeter measures compile/execute times, so caching must stay off to keep
|
||||
# timings representative; ccache is still on PATH but acts as a pass-through
|
||||
CCACHE_DISABLE: 1
|
||||
|
||||
jobs:
|
||||
@@ -62,18 +62,21 @@ jobs:
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
name: Run
|
||||
steps:
|
||||
- name: Checkout Verilator CI scripts
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
path: repo
|
||||
sparse-checkout: |
|
||||
.github/actions
|
||||
ci
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
echo "path-exclude /usr/share/doc/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
echo "path-exclude /usr/share/man/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
sudo apt update || \
|
||||
sudo apt update
|
||||
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev || \
|
||||
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev
|
||||
uses: ./repo/.github/actions/install-deps
|
||||
with:
|
||||
stage: rtlmeter-run
|
||||
|
||||
- name: Checkout RTLMeter
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
repository: "verilator/rtlmeter"
|
||||
path: rtlmeter
|
||||
@@ -82,14 +85,6 @@ jobs:
|
||||
working-directory: rtlmeter
|
||||
run: make venv
|
||||
|
||||
- name: Use saved ccache
|
||||
if: ${{ env.CCACHE_DISABLE == 0 }}
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: rtlmeter-run-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.cases }}-${{ inputs.compileArgs }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
restore-keys: rtlmeter-run-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.cases }}-${{ inputs.compileArgs }}
|
||||
|
||||
########################################################################
|
||||
# Run with new Verilator
|
||||
########################################################################
|
||||
@@ -101,7 +96,7 @@ jobs:
|
||||
|
||||
- name: Unpack Verilator installation archive - new
|
||||
run: |
|
||||
tar -x -z -f ${{ inputs.verilator-archive-new }}
|
||||
tar --zstd -x -f ${{ inputs.verilator-archive-new }}
|
||||
mv install verilator-new
|
||||
|
||||
- name: Compile cases - new
|
||||
@@ -164,7 +159,7 @@ jobs:
|
||||
- name: Unpack Verilator installation archive - old
|
||||
if: ${{ inputs.verilator-archive-old != '' }}
|
||||
run: |
|
||||
tar -x -z -f ${{ inputs.verilator-archive-old }}
|
||||
tar --zstd -x -f ${{ inputs.verilator-archive-old }}
|
||||
mv install verilator-old
|
||||
|
||||
- name: Compile cases - old
|
||||
|
||||
@@ -11,29 +11,37 @@ on:
|
||||
description: "Name of the repository archive artifact from reusable-build"
|
||||
required: true
|
||||
type: string
|
||||
os: # e.g. ubuntu-24.04
|
||||
required: true
|
||||
type: string
|
||||
cc: # gcc or clang
|
||||
required: true
|
||||
type: string
|
||||
reloc: # 0 or 1
|
||||
required: true
|
||||
type: number
|
||||
suite: # e.g. dist-vlt-0
|
||||
cc:
|
||||
description: "Compiler to use: 'gcc' or 'clang'"
|
||||
required: true
|
||||
type: string
|
||||
dev-gcov:
|
||||
description: "Collect gcov coverage data from the test run"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
reloc:
|
||||
description: "Relocate the installation before testing"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
runs-on:
|
||||
description: "Runner to test on, e.g. ubuntu-24.04"
|
||||
required: true
|
||||
type: number
|
||||
type: string
|
||||
suite:
|
||||
description: "Test suite to run, e.g. dist-vlt-0"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_COMPILERCHECK: content
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
INSTALL_DIR: ${{ github.workspace }}/install
|
||||
RELOC_DIR: ${{ github.workspace }}/relloc
|
||||
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -43,15 +51,8 @@ defaults:
|
||||
jobs:
|
||||
|
||||
test:
|
||||
runs-on: ${{ inputs.os }}
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
name: Test
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: test
|
||||
CI_RUNS_ON: ${{ inputs.os }}
|
||||
CI_RELOC: ${{inputs.reloc }}
|
||||
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
|
||||
CACHE_BASE_KEY: test-${{ inputs.os }}-${{ inputs.cc }}-${{inputs.reloc }}-${{ inputs.suite }}
|
||||
CCACHE_MAXSIZE: 100M # Per build per suite (* 5 * 5 = 2500M in total)
|
||||
steps:
|
||||
|
||||
- name: Download repository archive
|
||||
@@ -63,32 +64,44 @@ jobs:
|
||||
- name: Unpack repository archive
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
tar -x -z -f ${{ inputs.archive }}
|
||||
tar --zstd -x -f ${{ inputs.archive }}
|
||||
ls -lsha
|
||||
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2
|
||||
- name: Configure ccache
|
||||
run: |
|
||||
# ccache is unreliable on macOS, and does nothing with 'gcc --coverage'
|
||||
if [ "${{ startsWith(inputs.runs-on, 'macos') }}" = true ] || [ "${{ inputs.dev-gcov }}" = true ]; then
|
||||
echo "CCACHE_DISABLE=1" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
# Test-job ccache is stored as an artifact, not actions/cache
|
||||
- name: Restore ccache
|
||||
id: ccache
|
||||
if: ${{ env.CCACHE_DISABLE != '1' }}
|
||||
uses: ./repo/.github/actions/artifact-cache
|
||||
with:
|
||||
mode: restore
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ env.CACHE_KEY }}-
|
||||
key: ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.suite }}
|
||||
|
||||
- name: Install test dependencies
|
||||
run: |
|
||||
./ci/ci-install.bash
|
||||
make venv
|
||||
uses: ./repo/.github/actions/install-deps
|
||||
with:
|
||||
stage: test
|
||||
|
||||
- name: Set up Python venv
|
||||
uses: ./repo/.github/actions/setup-venv
|
||||
with:
|
||||
save: ${{ github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: Test
|
||||
id: run-test
|
||||
continue-on-error: true
|
||||
env:
|
||||
TESTS: ${{ inputs.suite }}
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
./ci/ci-script.bash
|
||||
./ci/ci-test.bash \
|
||||
--suite ${{ inputs.suite }} \
|
||||
${{ inputs.reloc && format('--reloc {0}/reloc', github.workspace) || '' }}
|
||||
|
||||
- name: Combine code coverage data
|
||||
if: ${{ inputs.dev-gcov }}
|
||||
@@ -104,6 +117,16 @@ jobs:
|
||||
path: ${{ github.workspace }}/repo/obj_coverage/verilator-${{ inputs.suite }}.info
|
||||
name: code-coverage-${{ inputs.suite }}
|
||||
|
||||
- name: Save ccache
|
||||
if: ${{ env.CCACHE_DISABLE != '1' && !cancelled() }}
|
||||
uses: ./repo/.github/actions/artifact-cache
|
||||
with:
|
||||
mode: save
|
||||
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 }}
|
||||
|
||||
- name: Fail job if a test failed
|
||||
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
|
||||
run: |-
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
cases: ${{ steps.cases.outputs.cases }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
|
||||
- name: Startup
|
||||
id: start
|
||||
@@ -65,39 +65,47 @@ jobs:
|
||||
build-gcc-new:
|
||||
name: Build New Verilator - GCC
|
||||
needs: start
|
||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
runs-on: ubuntu-24.04
|
||||
cc: gcc
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ github.sha }}
|
||||
|
||||
build-clang-new:
|
||||
name: Build New Verilator - Clang
|
||||
needs: start
|
||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
runs-on: ubuntu-24.04
|
||||
cc: clang
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ github.sha }}
|
||||
|
||||
build-gcc-old:
|
||||
name: Build Old Verilator - GCC
|
||||
needs: start
|
||||
if: ${{ needs.start.outputs.old-sha != '' }}
|
||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
runs-on: ubuntu-24.04
|
||||
cc: gcc
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ needs.start.outputs.old-sha }}
|
||||
|
||||
build-clang-old:
|
||||
name: Build Old Verilator - Clang
|
||||
needs: start
|
||||
if: ${{ needs.start.outputs.old-sha != '' }}
|
||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
runs-on: ubuntu-24.04
|
||||
cc: clang
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ needs.start.outputs.old-sha }}
|
||||
|
||||
run-gcc:
|
||||
@@ -208,7 +216,7 @@ jobs:
|
||||
run: echo "tags=$(jq -r 'keys | map(sub("^run-"; "")) | join(" ")' <<< '${{ toJSON(needs) }}')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout RTLMeter
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
repository: "verilator/rtlmeter"
|
||||
path: rtlmeter
|
||||
@@ -298,7 +306,7 @@ jobs:
|
||||
repositories: verilator-rtlmeter-results
|
||||
permission-contents: write
|
||||
- name: Checkout verilator-rtlmeter-results
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
repository: "verilator/verilator-rtlmeter-results"
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
@@ -331,7 +339,7 @@ jobs:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Checkout RTLMeter
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
repository: "verilator/rtlmeter"
|
||||
path: rtlmeter
|
||||
@@ -341,7 +349,7 @@ jobs:
|
||||
run: make venv
|
||||
|
||||
- name: Checkout Verilator
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
path: verilator
|
||||
|
||||
@@ -361,10 +369,6 @@ jobs:
|
||||
mkdir ../report-artifact
|
||||
mv rtlmeter-report/report ../report-artifact/
|
||||
echo ${{ github.event.number }} > ../report-artifact/pr-number.txt
|
||||
# Create the notification artifact
|
||||
mkdir ../notification-artifact
|
||||
mv rtlmeter-report/notification.txt ../notification-artifact/body.txt
|
||||
echo ${{ github.event.number }} > ../notification-artifact/pr-number.txt
|
||||
|
||||
- name: Upload report
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
@@ -372,11 +376,11 @@ jobs:
|
||||
path: report-artifact
|
||||
name: rtlmeter-report
|
||||
|
||||
- name: Upload notification
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
- name: Upload PR notification
|
||||
uses: ./verilator/.github/actions/upload-pr-notification
|
||||
with:
|
||||
path: notification-artifact
|
||||
name: pr-notification
|
||||
path: verilator/rtlmeter-report/notification
|
||||
key: rtlmeter
|
||||
|
||||
- name: Report status
|
||||
run: exit ${{ steps.report.outputs.status }}
|
||||
|
||||
@@ -96,9 +96,15 @@ ______________________________________________________________________
|
||||
- [ ] Ran the full regression on at least one OS before submitting. Partial runs
|
||||
are fine during development, but the submitted PR is expected to pass every
|
||||
test.
|
||||
- [ ] Validated the code changes against recommendations in the AGENTS.md files.
|
||||
- [ ] Did not edit `docs/CONTRIBUTORS` (humans only) or `Changes` (maintainer
|
||||
updates it near release).
|
||||
|
||||
## Drafting a PR
|
||||
|
||||
- [ ] When creating a pull request message, omit itemized lists of changed
|
||||
files and changes, since reviewers already have access to the diff.
|
||||
|
||||
## Pick the right diagnostic (and its required test)
|
||||
|
||||
The API you choose determines which test must accompany the change.
|
||||
|
||||
+4
-3
@@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15)
|
||||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||
project(
|
||||
Verilator
|
||||
VERSION 5.050
|
||||
VERSION 5.052
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
@@ -155,11 +155,12 @@ install(
|
||||
foreach(
|
||||
program
|
||||
verilator
|
||||
verilator_gantt
|
||||
verilator_ccache_report
|
||||
verilator_coverage
|
||||
verilator_difftree
|
||||
verilator_profcfunc
|
||||
verilator_gantt
|
||||
verilator_includer
|
||||
verilator_profcfunc
|
||||
)
|
||||
install(PROGRAMS bin/${program} TYPE BIN)
|
||||
endforeach()
|
||||
|
||||
@@ -10,6 +10,182 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested or implemented a given issue are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.052 2026-09-05
|
||||
==========================
|
||||
|
||||
**Important:**
|
||||
|
||||
* Verilator now supports UVM 2020-3.2; see (#1538) for limitations.
|
||||
|
||||
**Other:**
|
||||
|
||||
* Add comments as a branch description in coverage .info files (#7843). [Eryk Szpotanski]
|
||||
* Add --enable-light-debug configure option (#7886). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Add user-provided DPI-C function declarations (#7626) (#7893). [Jakub Michalski]
|
||||
* Add error on static virtual functions (#7932). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Add error when scalar is passed to array task argument (#7948). [Pawel Klopotek]
|
||||
* Add MULTIDRIVENPROC warning for signals driven by multiple plain always blocks (#7968). [Aisha]
|
||||
* Add FSM arc and state coverage to verilator_coverage .info output (#7972) (#7973). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Add SIMILARNAME warning when variables have names that differ in case (#7992) (#8020) (#8186) (#8226) (#8233). [Paul Campbell]
|
||||
* Add Unigen2 constrained randomization algorithm (#8042). [Jakub Wasilewski, Antmicro Ltd.]
|
||||
* Add `+verilator+assert+lock` to ignore RTL assert control statements (#8086). [Sumanth Kadiyala]
|
||||
* Add NEVERMATCH warning when an SVA sequence can never match (#8176) (#8183). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Remove and deprecate `--assert-unroll-limit`. (#8096). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support embedded covergroup member references (#7749) (#8015). [Marco Bartoli]
|
||||
* Support a sequence used as an event control (#7797) (#7846). [Yilou Wang]
|
||||
* Support `[\*N:$]` consecutive repetition (#7940). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support dynamic containers in unique constraints (#7947). [Adam Kostrzewski, Antmicro Ltd.]
|
||||
* Support delayed tristate gates (#7960) (#7961). [Patrick Creighton]
|
||||
* Support `default clocking` (#7984). [Kamil Danecki, Antmicro Ltd.]
|
||||
* Support embedded covergroup clocking events (#8028). [Marco Bartoli]
|
||||
* Support `weak`/`strong` keywords in property expressions (#8054). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support class handle covergroup arguments (#8071). [Marco Bartoli]
|
||||
* Support `soft foreach` constraints (#8174). [Kamil Danecki, Antmicro Ltd.]
|
||||
* Support overlong `within` sequences as never-matching (#8177). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support covergroup/function/task (ref/virtual/array) interface arguments (#8180). [Marco Bartoli]
|
||||
* Support covergroup events with reference members or ref arguments (#8201). [Marco Bartoli]
|
||||
* Support s_until/s_until_with in property expression (#8220). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support solve before with associative array (#8232). [Kamil Danecki, Antmicro Ltd.]
|
||||
* Optimize random initialization. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize more always blocks in DFG (#7775). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize assertion NFAs using bit-vector ring buffers (#7885). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize circular logic in DFG (#7902). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize VPI symbol registration and scope construction (#7936). [Nick Brereton]
|
||||
* Optimize bounded always properties using ring buffers (#8061) (#8092). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize VL_WORDS_I/VL_BYTES_I (#8095). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize consecutive and goto repetitions using ring buffers (#8096). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize temporary insertion for single bit replicates in DFG (#8110). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize mtask coarsening in multi-threaded scheduling (#8120). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize NFA delay-ring edge traversal, drop complexity to linear (#8145). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Optimize associative constant patterns to fixed point (#8147) (#8209). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix procedural tristate assignment overrides (#6785) (#8130). [Eddie Rydell]
|
||||
* Fix $finish continuing event loop (#7267) (#7950). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix continuing on wait(0) (#7372) (#8231). [Szymon Gizler, Antmicro Ltd.]
|
||||
* Fix $display accepting streaming concat arguments (#7663) (#7890). [Jaeuk Lee]
|
||||
* Fix typedef clocking input sampling (#7688 partial) (#8204). [Marco Bartoli]
|
||||
* Fix DFG misoptimizing bound checks (#7755). [Jakub Michalski]
|
||||
* Fix unique0 case side effects (#7787). [Pawel Klopotek]
|
||||
* Fix mid-window disable iff (#7792) (#7869). [Yilou Wang]
|
||||
* Fix cleaning purity cache after assertions. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix uncaught type error leading to invalid C++ output (#7814). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix internal error for coverpoints that reference a covergroup formal parameter (#7853 partial) (#7889). [Matthew Ballance]
|
||||
* Fix immediate disable of fork join branches (#7856) (#7931). [Marco Bartoli]
|
||||
* Fix self-disable of named blocks with forks (Part of #7857) (#7996) (#8006). [Marco Bartoli]
|
||||
* Fix clang++ ambiguous overload of '==' operator (#7863). [Pawel Kojma, Antmicro Ltd.]
|
||||
* Fix heap-use-after-free in `VlRNG::VlRNG()` (#7865). [Dragon-Git]
|
||||
* Fix mixed-width inside and dist range bounds failing randomization (#7875). [Yilou Wang]
|
||||
* Fix solve-before over array variables failing randomization (#7876). [Yilou Wang]
|
||||
* Fix scoped randomize with array members under rand_mode (#7877). [Yilou Wang]
|
||||
* Fix dist constraint on a frozen variable failing randomization (#7878). [Yilou Wang]
|
||||
* Fix range delays with parameter bounds (#7882). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix --coverage on labeled inline assert/cover property (#7898) (#7904). [Patrick Creighton]
|
||||
* Fix memory leak in VerilatedFst::close() (#7899). [Jakub Michalski]
|
||||
* Fix spurious FSM COVERIGN on comparisons (#7900) (#7908). [Yogish Sekhar]
|
||||
* Fix release of forced port nets (#7901) (#7907). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix independent force of multiply instantiated signals (#7905). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix scheduling of variables written in non-inlined functions in suspendable processes (#7909). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Fix randomization of unreferenced rand members of unpacked structs (#7910) (#7911). [Philip Axer]
|
||||
* Fix VL_TO_STRING function for array of structs (#7912). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix queues falling into wrong template spec (#7914). [Adam Kostrzewski, Antmicro Ltd.]
|
||||
* Fix type resolution when a local name shadows a type name (#7915). [Pawel Klopotek]
|
||||
* Fix crash streaming an unpacked array of unpacked structs (#7917). [Nick Brereton]
|
||||
* Fix streaming concat as output-port lvalue into unpacked struct (#7918). [Nick Brereton]
|
||||
* Fix dropping variable writes across opaque calls (#7921) (#7933). [Philip Axer]
|
||||
* Fix dynamic array handling in solve...before (#7922). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix unlinked error with function call in derived class parameter (#7923). [Mateusz Gancarz, Antmicro Ltd.]
|
||||
* Fix scheduling of variables written in non-inlined functions in suspendable processes (#7924). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Fix class parameter resolution (#7935).
|
||||
* Fix FSM detection of coverage-only branches (#7941) (#7942). [Patrick Creighton]
|
||||
* Fix use-after-free in V3LinkDotIfaceCapture (#7943). [Nick Brereton]
|
||||
* Fix sampled-value clocks in repeated assertions (#7944). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix queued $finish/$stop request thread ordering (#7946 prep) (#7952). [Yilou Wang]
|
||||
* Fix NFA assertion scheduling to fix outcome counts (#7946). [Yilou Wang]
|
||||
* Fix DFG nested shifts with overflowing shift amount (#7955) (#7977).
|
||||
* Fix timing controls in interface tasks called via virtual interface (#7959). [Yilou Wang]
|
||||
* Fix lambda parameter types in queue min and max (#7962). [Bartosz Skorowski]
|
||||
* Fix solve-before dropping all soft constraints in randomize() (#7963). [Yilou Wang]
|
||||
* Fix generic interface param resolution in module/cell parameterization (#7970) (#7971). [David Garau]
|
||||
* Fix lost writes when select width exceeds variable width (#7975). [Bartosz Skorowski]
|
||||
* Fix $fgets being mis-optimized away (#7976). [G-A. Kamendje]
|
||||
* Fix SYNCASYNCNET false positive with changed non-edge logic (#7980). [Oron Port]
|
||||
* Fix variable scope in unique on dynamic array (#7981). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix table optimization causing not contextually convertible to bool error (#7983). [Jakub Michalski]
|
||||
* Fix wait fork trigger temporary split across generated functions (#7985) (#7986). [Marco Brambilla]
|
||||
* Fix randc cycling in a class that also uses solve...before (#7991 partial) (#8055). [Yilou Wang]
|
||||
* Fix solver session stability to avoid breaking randomize (#7991 partial). [Yilou Wang]
|
||||
* Fix simulation hang when the solver stops answering (#7991 partial) (#8129) (#8136). [Yilou Wang]
|
||||
* Fix randomize() with a user variable named 'item' (#7993) (#7994).
|
||||
* Fix UNSATCONSTR reporting constraint indices above 9 (#7995). [Yilou Wang]
|
||||
* Fix MULTIDRIVEN/MULTIDRIVENPROC warning suppression via lint_off (#8000). [Gilberto Abram]
|
||||
* Fix force not substituted into an unpacked array index (#8002).
|
||||
* Fix dist weights ignored when an item is not a literal (#8003).
|
||||
* Fix error when forcing an array element read at a run-time index (#8004).
|
||||
* Fix FSM coverage on empty reset branches (#8005) (#8064). [Yogish Sekhar]
|
||||
* Fix missing verilator_coverage in cmake install (#8008) (#8011).
|
||||
* Fix dist inside a foreach nested in a constraint if (#8016).
|
||||
* Fix gcov dump on warnings discarding later coverage counts (#8017). [Yilou Wang]
|
||||
* Fix unique constraint crash, guards, and size (#8018).
|
||||
* Fix Linux peak memory stat to use VmHWM (#8022) (#8070). [Tyrone Marhguy]
|
||||
* Fix assignment pattern key constant expressions (#8029) (#8030). [Josep Sans]
|
||||
* Fix skipping optimization for unpacked data types (#8031). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix gate deduplication with function arguments (#8038). [JOTEGO]
|
||||
* Fix conditional expression with parameterized classes (#8039). [Pawel Klopotek, Antmicro Ltd.]
|
||||
* Fix array size references in constraints (#8040). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix mid-range consecutive repetition rejects (#8041). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix write access to dynamic arrays (#8043). [Bartosz Skorowski, Antmicro Ltd.]
|
||||
* Fix nondeterminism in trace stage (#8046). [Sumanth Kadiyala]
|
||||
* Fix force helpers in multiply instantiated modules (#8047). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix pin to non-existent port with PINNOTFOUND suppressed (#8051) (#8052). [Nikolai Kumar]
|
||||
* Fix wildcard equality against a 4-state constant in an assertion (#8056) (#8057). [Nikolai Kumar]
|
||||
* Fix untyped datatype error on sampling functions with property arguments (#8060). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix VPI cbValueChange for 1-bit select (#8063). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix NFA assertion crash and mis-counted property if/case (#8074). [Yilou Wang]
|
||||
* Fix skipping non-constrained enum constraining when inside object of other class (#8075). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix use-after-free of captured interface typedef reference during parameter cloning (#8076). [em2machine]
|
||||
* Fix unintended side-effect insertion on associative array read (#8077). [Adam Kostrzewski, Antmicro Ltd.]
|
||||
* Fix undefined symbol solver error (#8080). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Fix unpacked array element force with procedural assign (#8084) (#8085). [Nikolai Kumar]
|
||||
* Fix reading another parameter resolves to 0 in cloned modules (#8086) (#8069). [em2machine]
|
||||
* Fix hierarchical class scope resolution (#8094). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix segmentation fault on parameters (#8102) (#8149). [Sumanth Kadiyala]
|
||||
* Fix the clang compilation using precompiled headers (#8105). [Demin Han]
|
||||
* Fix invalid typedef (#8106). [Adam Kostrzewski, Antmicro Ltd.]
|
||||
* Fix R/W references to random builtin function seeds (#8112). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix invalid C++ on cast (#8113). [Pawel Klopotek, Antmicro Ltd.]
|
||||
* Fix COVERIGN on SVA goto repetition (#8118). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix unordered data hazards in multi-threaded scheduling (#8133). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix public table offsets larger than 32 bits (#8135). [Sunimali Rathnayake]
|
||||
* Fix 'get-value' for empty constrained arrays (#8138). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix streaming concatenation uninitialized read-modify-write access (#8143). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix unpacked array passing to logic task argument error (#8148). [Pawel Klopotek, Antmicro Ltd.]
|
||||
* Fix $past in final blocks on a sampling-tick finish (#8153). [Yilou Wang]
|
||||
* Fix array reference in reduction constraints (#8158). [Kornel Uriasz, Antmicro Ltd.]
|
||||
* Fix VPI force data corruption (#8160). [Jonathan Drolet]
|
||||
* Fix sampled-value functions output on the first cycle (#8162). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix null pointer dereference in process destruction (#8171). [Andrii, Antmicro Ltd.]
|
||||
* Fix invalid conversion of queue (#8172). [Andrii, Antmicro Ltd.]
|
||||
* Fix crash on forward-referencing parameters (#8175). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix crash on empty `#()` parameter list on class instance (#8182). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix crash on multiple missing type parameters (#8184). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix SVA bound limit guard, leave headroom for alignment, respect --max-num-width (#8192). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix internal error on an enum literal inside a `with` lambda body (#8190) (#8193). [Aditya Shevade]
|
||||
* Fix internal error on backslash-quoted strings (#8198). [Henry Hsieh]
|
||||
* Fix backslash behavior in string literals (#8211). [Pawel Klopotek, Antmicro Ltd.]
|
||||
* Fix task inout pin resizing error (#8212). [Pawel Klopotek, Antmicro Ltd.]
|
||||
* Fix code generation for postponed region (#8218). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix method calls to be a reorder barrier (#8222). [Matthew Ballance]
|
||||
* Fix errors on stream connected to instance port (#8234). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix folding of short circuiting operators guarding member access (#8238). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix MULTIDRIVENPROC on static loop induction variables (#8242). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix multiply undefined behavior on signed int overflow (#8247) (#8248). [Paul Rigge]
|
||||
* Fix initial statements with gate optimization (#8250). [Prithayan Barua]
|
||||
* Fix segmentation fault on context destruction (#8265).
|
||||
* Fix mis-randomizing an unpacked struct member when not rand/randc (#8277) (#8278). [Aditya Shevade]
|
||||
* Fix std::randomize() with-clause array index arguments (#8279) (#8280). [Aditya Shevade]
|
||||
* Fix incorrect reference to sync/async in SYNCASYNCNET warning (#8281). [Andrii, Antmicro Ltd.]
|
||||
* Fix use-after-free of captured interface typedef in deleted generate branch (#8287). [Nick Brereton]
|
||||
|
||||
|
||||
Verilator 5.050 2026-07-01
|
||||
==========================
|
||||
|
||||
@@ -5738,7 +5914,7 @@ Verilator 3.104 2003-04-30
|
||||
**Major:**
|
||||
|
||||
* Indicate direction of ports with VL_IN and VL_OUT.
|
||||
* Allow $c32, etc, to specify width of the $c statement for VCS.
|
||||
* Allow $c32, etc, to specify width of the $c statement.
|
||||
* Numerous performance improvements, worth about 25%
|
||||
|
||||
**Minor:**
|
||||
|
||||
+17
-4
@@ -94,6 +94,7 @@ CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
|
||||
CFG_WITH_DEV_GCOV = @CFG_WITH_DEV_GCOV@
|
||||
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
|
||||
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
|
||||
GCOV = @GCOV@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
@@ -466,9 +467,11 @@ analyzer-include:
|
||||
|
||||
# Bash/sh files
|
||||
BASH_FILES = \
|
||||
ci/ci-coverage-report.bash \
|
||||
ci/ci-install.bash \
|
||||
ci/ci-pages-notify.bash \
|
||||
ci/ci-pages.bash \
|
||||
ci/ci-rtlmeter-report.bash \
|
||||
ci/ci-script.bash \
|
||||
ci/docker/run/hooks/post_push \
|
||||
ci/docker/run/verilator-docker \
|
||||
@@ -536,6 +539,7 @@ PY_PROGRAMS = \
|
||||
src/vlcovgen \
|
||||
test_regress/*.py \
|
||||
test_regress/t/*.pf \
|
||||
test_regress/t/randomize_solver_tamper.py \
|
||||
|
||||
# Python files, subject to format but not lint
|
||||
PY_FILES = \
|
||||
@@ -695,20 +699,28 @@ COVERAGE_DIR := obj_coverage
|
||||
ifeq ($(CFG_WITH_DEV_GCOV),yes)
|
||||
|
||||
# Figure out base and head refs for coverage report
|
||||
COVERAGE_REF_BASE := $(if $(COVERAGE_BASE),$(shell git rev-parse --short $(COVERAGE_BASE)))
|
||||
COVERAGE_REF_BASE := $(if $(COVERAGE_BASE),$(shell git rev-parse --short $(COVERAGE_BASE) 2>/dev/null))
|
||||
ifneq ($(COVERAGE_BASE),)
|
||||
ifeq ($(COVERAGE_REF_BASE),)
|
||||
$(error COVERAGE_BASE='$(COVERAGE_BASE)' is not a valid git revision)
|
||||
endif
|
||||
endif
|
||||
COVERAGE_REF_HEAD := $(shell git rev-parse --short HEAD)
|
||||
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_OPT += --lcov
|
||||
FASTCOV_OPT += --process-gcno
|
||||
FASTCOV_OPT += --branch-coverage
|
||||
FASTCOV_OPT += --dump-statistic
|
||||
# Files matching the following glob patterns will be excluded from coverage
|
||||
FASTCOV_OPT += --exclude-glob
|
||||
FASTCOV_OPT += '/usr/*'
|
||||
FASTCOV_OPT += '/usr/include/*'
|
||||
FASTCOV_OPT += '/usr/lib*/*'
|
||||
FASTCOV_OPT += '/usr/share/*'
|
||||
FASTCOV_OPT += '*examples/*'
|
||||
FASTCOV_OPT += '*include/fstcpp/*'
|
||||
FASTCOV_OPT += '*src/obj_dbg/*'
|
||||
@@ -749,6 +761,7 @@ GENHTML_OPT += --missed
|
||||
GENHTML_OPT += --rc branch_coverage=1
|
||||
GENHTML_OPT += --rc genhtml_hi_limit=100
|
||||
GENHTML_OPT += --ignore-errors negative
|
||||
GENHTML_OPT += --ignore-errors inconsistent
|
||||
ifeq ($(COVERAGE_REF_BASE),)
|
||||
GENHTML_OPT += --header-title "Code coverage for Verilator $(shell git describe --dirty)"
|
||||
else
|
||||
@@ -780,7 +793,7 @@ $(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
|
||||
[ -s $(COVERAGE_DIR)/filter.patch ] || touch $(COVERAGE_DIR)/empty-patch
|
||||
$(FASTCOV) -C $^ --lcov -o $@ --diff-filter $(COVERAGE_DIR)/filter.patch
|
||||
|
||||
# Build coverage report
|
||||
@@ -789,7 +802,7 @@ $(COVERAGE_DIR)/report/index.html: $(COVERAGE_DIR)/verilator$(if $(COVERAGE_REF_
|
||||
@echo "# genhtml: Generating coverage report"
|
||||
@echo "####################################################################"
|
||||
@rm -rf $(COVERAGE_DIR)/report
|
||||
[ -f $(COVERAGE_DIR)/empty-patch ]] || /usr/bin/time -f "That took %E" \
|
||||
[ -f $(COVERAGE_DIR)/empty-patch ] || /usr/bin/time -f "That took %E" \
|
||||
$(GENHTML) $(GENHTML_OPT) --output-directory $(COVERAGE_DIR)/report $^ || true
|
||||
@# Uncommitted changes not tracked, force rebuild on next run if patch coverage
|
||||
@$(if $(COVERAGE_REF_BASE),mv $(COVERAGE_DIR)/verilator-patch.info $(COVERAGE_DIR)/verilator-patch-last.info)
|
||||
|
||||
+5
-3
@@ -4,7 +4,9 @@
|
||||
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5| |badge7| |badge8|
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
|badge7| |badge8|
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|
||||
:target: https://verilator.org
|
||||
@@ -21,8 +23,8 @@
|
||||
.. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator
|
||||
:target: https://hub.docker.com/r/verilator/verilator
|
||||
|
||||
.. |badge7| image:: https://github.com/verilator/verilator/workflows/build/badge.svg
|
||||
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
|
||||
.. |badge7| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/build-test.yml?branch=master&label=regressions
|
||||
:target: https://github.com/verilator/verilator/actions/workflows/build-test.yml
|
||||
|
||||
.. |badge8| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/rtlmeter.yml?branch=master&event=schedule&label=benchmarks
|
||||
:target: https://verilator.github.io/verilator-rtlmeter-results
|
||||
|
||||
@@ -636,6 +636,7 @@ description of these arguments.
|
||||
|
||||
=for VL_SPHINX_EXTRACT "_build/gen/args_verilated.rst"
|
||||
|
||||
+verilator+assert+lock Lock assertion status changes at startup
|
||||
+verilator+coverage+file+<filename> Set coverage output filename
|
||||
+verilator+debug Enable debugging
|
||||
+verilator+debugi+<value> Enable debugging at a level
|
||||
|
||||
@@ -51,12 +51,17 @@ def profcfunc(filename: str) -> None:
|
||||
# Find modules
|
||||
verilated_mods = {}
|
||||
for func in funcs:
|
||||
match = re.search(r'(.*)::eval(_step)?\(', func)
|
||||
# Match only the model entry points: 'eval', 'eval_step', 'eval_end_step',
|
||||
# and the 'evalXxx' region entry points, but not e.g. a user's '::evaluate'
|
||||
match = re.match(r'([A-Za-z_][A-Za-z_0-9]*)::eval(_step|_end_step|[A-Z][A-Za-z_0-9]*)?\(',
|
||||
func)
|
||||
if match:
|
||||
prefix = match.group(1)
|
||||
if prefix.startswith('Verilated'): # Run-time library, not a model
|
||||
continue
|
||||
if Args.debug:
|
||||
print("-got _eval %s prefix=%s" % (func, prefix))
|
||||
verilated_mods[prefix] = re.compile(r'^' + prefix)
|
||||
verilated_mods[prefix] = re.compile(r'^' + re.escape(prefix))
|
||||
# pprint(verilated_mods)
|
||||
|
||||
# Sort by Verilog name
|
||||
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
# DESCRIPTION: Verilator: CI build job script
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
################################################################################
|
||||
# Executed in the 'build' stage.
|
||||
################################################################################
|
||||
|
||||
# Destructive to the checkout (reconfigures and rebuilds); never run locally
|
||||
if [ "$GITHUB_ACTIONS" != "true" ]; then
|
||||
echo "ERROR: $(basename "$0") must only be run in GitHub Actions CI" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
source "$(dirname "$0")/ci-common.bash"
|
||||
|
||||
################################################################################
|
||||
# Parse arguments
|
||||
|
||||
OPT_ASAN=0
|
||||
OPT_CCWARN=0
|
||||
OPT_GCOV=0
|
||||
OPT_LIGHT_DEBUG=0
|
||||
OPT_COMPILER=
|
||||
OPT_PREFIX=
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--asan) OPT_ASAN=1 ;;
|
||||
--ccwarn) OPT_CCWARN=1 ;;
|
||||
--compiler)
|
||||
[ $# -ge 2 ] || fatal "--compiler requires an argument"
|
||||
OPT_COMPILER="$2"
|
||||
shift
|
||||
;;
|
||||
--gcov) OPT_GCOV=1 ;;
|
||||
--light-debug) OPT_LIGHT_DEBUG=1 ;;
|
||||
--prefix)
|
||||
[ $# -ge 2 ] || fatal "--prefix requires an argument"
|
||||
OPT_PREFIX="$2"
|
||||
shift
|
||||
;;
|
||||
*) fatal "Unknown option: '$1'" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ -n "$OPT_COMPILER" ] || fatal "--compiler is required"
|
||||
[ -n "$OPT_PREFIX" ] || fatal "--prefix is required"
|
||||
|
||||
# Map the compiler name to the executable name configure CXX expects
|
||||
case "$OPT_COMPILER" in
|
||||
clang) CXX=clang++ ;;
|
||||
gcc) CXX=g++ ;;
|
||||
*) fatal "Unknown compiler: '$OPT_COMPILER'" ;;
|
||||
esac
|
||||
|
||||
################################################################################
|
||||
# Configure
|
||||
|
||||
CONFIGURE_ARGS="--prefix=$OPT_PREFIX --enable-longtests"
|
||||
if [ "$OPT_CCWARN" = 1 ]; then
|
||||
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-ccwarn"
|
||||
fi
|
||||
if [ "$OPT_ASAN" = 1 ]; then
|
||||
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-asan"
|
||||
CXX="$CXX -DVL_LEAK_CHECKS"
|
||||
# Replaces VNUser's union with a std::variant to validate type usage.
|
||||
# Only takes effect when the build is C++17 or newer.
|
||||
CXX="$CXX -DVL_USER_TYPE_CHECKS"
|
||||
fi
|
||||
if [ "$OPT_GCOV" = 1 ]; then
|
||||
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-gcov"
|
||||
fi
|
||||
if [ "$OPT_LIGHT_DEBUG" = 1 ]; then
|
||||
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-light-debug"
|
||||
fi
|
||||
autoconf
|
||||
./configure $CONFIGURE_ARGS CXX="$CXX"
|
||||
|
||||
################################################################################
|
||||
# Build
|
||||
|
||||
ccache -z
|
||||
BUILD_START=$SECONDS
|
||||
"$MAKE" -j "$NPROC" -k
|
||||
ccache -svv
|
||||
ccache --evict-older-than "$((SECONDS - BUILD_START + 60))s"
|
||||
ccache -svv
|
||||
@@ -0,0 +1,35 @@
|
||||
# DESCRIPTION: Verilator: CI common definitions, sourced by the stage scripts
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
################################################################################
|
||||
# Common definitions sourced by the CI stage scripts; not executed directly.
|
||||
################################################################################
|
||||
|
||||
fatal() {
|
||||
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
|
||||
}
|
||||
|
||||
case "$(uname -s)" in
|
||||
Linux)
|
||||
HOST_OS=linux
|
||||
MAKE=make
|
||||
NPROC=$(nproc)
|
||||
# Distro id/version; subshell keeps os-release out of our namespace
|
||||
if [ -r /etc/os-release ]; then
|
||||
DISTRO_ID=$(. /etc/os-release; echo "$ID")
|
||||
DISTRO_VERSION=$(. /etc/os-release; echo "$VERSION_ID")
|
||||
fi
|
||||
;;
|
||||
Darwin)
|
||||
HOST_OS=macOS
|
||||
MAKE=make
|
||||
NPROC=$(sysctl -n hw.logicalcpu)
|
||||
;;
|
||||
*)
|
||||
fatal "Unknown host OS: '$(uname -s)'"
|
||||
;;
|
||||
esac
|
||||
|
||||
export MAKE
|
||||
Executable
+100
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env bash
|
||||
# DESCRIPTION: Verilator: CI script for 'coverage.yml' results
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
# This script combines the code coverage data gathered by the test jobs into
|
||||
# the HTML report published on GitHub Pages, and for a pull request, builds the
|
||||
# content of the response comment posted on it.
|
||||
|
||||
# Developer note: You should be able to run this script in your local checkout
|
||||
# if you have GitHub CLI (command 'gh') setup, authenticated ('gh auth login'),
|
||||
# and have set a default repository ('gh repo set-default').
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# Trace when running in the CI
|
||||
[ "$GITHUB_ACTIONS" != "true" ] || set -x
|
||||
|
||||
# Arguments:
|
||||
# 1. run ID
|
||||
# 2. number of the pull request, or empty for a non-pull-request run
|
||||
# 3. base SHA of the pull request (pull requests only)
|
||||
# 4. head SHA of the pull request (pull requests only)
|
||||
RUN_ID=$1
|
||||
PR_NUMBER=$2
|
||||
PR_BASE_SHA=$3
|
||||
PR_HEAD_SHA=$4
|
||||
|
||||
# Coverage data and report directory, uploaded as the 'coverage-report' artifact
|
||||
COVERAGE_DIR=obj_coverage
|
||||
ls -lsha ${COVERAGE_DIR}
|
||||
|
||||
# Combine the reports from the test jobs
|
||||
nodist/fastcov.py -C ${COVERAGE_DIR}/verilator-*.info --lcov -o ${COVERAGE_DIR}/verilator.info
|
||||
|
||||
# Create the report. For a pull request, report patch coverage against the
|
||||
# merge-base between the head of the pull request and the target branch. The
|
||||
# summary quoted in the notification below is only in the log of 'make'.
|
||||
MAKE_LOG=make-coverage-report.log
|
||||
if [ -n "${PR_NUMBER}" ]; then
|
||||
COVERAGE_BASE=$(git rev-parse --short $(git merge-base ${PR_BASE_SHA} ${PR_HEAD_SHA}))
|
||||
make coverage-report COVERAGE_BASE=${COVERAGE_BASE} |& tee ${MAKE_LOG}
|
||||
else
|
||||
make coverage-report
|
||||
fi
|
||||
|
||||
# Remove the data files, only the HTML report is published
|
||||
rm -f ${COVERAGE_DIR}/verilator*.info
|
||||
|
||||
# The rest is for pull requests only
|
||||
if [ -z "${PR_NUMBER}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get some metadata about the run
|
||||
RUN_NUM=$(gh run view ${RUN_ID} --json number --jq ".number")
|
||||
RUN_URL=$(gh run view ${RUN_ID} --json url --jq ".url")
|
||||
|
||||
# Repository owner and name of the default repository, used to build the
|
||||
# GitHub Pages URL of the report. The owner is lowercased, as required for the
|
||||
# '<owner>.github.io' Pages domain.
|
||||
PAGES_OWNER=$(gh repo view --json owner --jq '.owner.login' | tr '[:upper:]' '[:lower:]')
|
||||
PAGES_NAME=$(gh repo view --json name --jq '.name')
|
||||
|
||||
REPORT_URL=https://${PAGES_OWNER}.github.io/${PAGES_NAME}/coverage-reports/${RUN_ID}/index.html
|
||||
|
||||
###############################################################################
|
||||
# Create the PR notification
|
||||
###############################################################################
|
||||
|
||||
NOTIFICATION_DIR=notification
|
||||
mkdir -p ${NOTIFICATION_DIR}
|
||||
|
||||
cat > ${NOTIFICATION_DIR}/body.txt <<NOTIFICATION_TEMPLATE
|
||||
Patch coverage from PR workflow [#${RUN_NUM}](${RUN_URL}) (code coverage of lines changed relative to ${COVERAGE_BASE}):
|
||||
NOTIFICATION_TEMPLATE
|
||||
|
||||
if [ -f ${COVERAGE_DIR}/empty-patch ]; then
|
||||
# Patch is empty
|
||||
cat >> ${NOTIFICATION_DIR}/body.txt <<SUMMARY_TEMPLATE
|
||||
Patch contains no code changes
|
||||
SUMMARY_TEMPLATE
|
||||
|
||||
echo "Workflow [#${RUN_NUM}](${RUN_URL}): patch contains no code changes" > ${NOTIFICATION_DIR}/hist.txt
|
||||
|
||||
else
|
||||
# Patch contains code changes
|
||||
|
||||
cat >> ${NOTIFICATION_DIR}/body.txt <<SUMMARY_TEMPLATE
|
||||
<pre>
|
||||
$(grep -E "(lines|branches)\.*:" ${MAKE_LOG} | sed "s/\.*:/:/" || true)
|
||||
</pre>
|
||||
Report: [${RUN_ID}](${REPORT_URL})
|
||||
|
||||
Please get to 100% line coverage, and understand all branches; see the [developer docs](https://github.com/verilator/verilator/blob/master/docs/internals.rst#code-coverage-results)
|
||||
SUMMARY_TEMPLATE
|
||||
|
||||
echo "Workflow [#${RUN_NUM}](${RUN_URL}) report: [${RUN_ID}](${REPORT_URL})" > ${NOTIFICATION_DIR}/hist.txt
|
||||
fi
|
||||
+161
-91
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# DESCRIPTION: Verilator: CI dependency install script
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2020 Geza Lore
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
################################################################################
|
||||
@@ -10,141 +10,211 @@
|
||||
# required by the particular build stage.
|
||||
################################################################################
|
||||
|
||||
# Installs system packages with sudo; never run locally
|
||||
if [ "$GITHUB_ACTIONS" != "true" ]; then
|
||||
echo "ERROR: $(basename "$0") must only be run in GitHub Actions CI" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
cd $(dirname "$0")/..
|
||||
|
||||
# Avoid occasional cpan failures "Issued certificate has expired."
|
||||
export PERL_LWP_SSL_VERIFY_HOSTNAME=0
|
||||
echo "check_certificate = off" >> ~/.wgetrc
|
||||
source "$(dirname "$0")/ci-common.bash"
|
||||
|
||||
fatal() {
|
||||
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
|
||||
################################################################################
|
||||
# Parse arguments
|
||||
|
||||
# Which stage to install dependencies for
|
||||
STAGE="$1"
|
||||
|
||||
################################################################################
|
||||
# Install dependencies
|
||||
|
||||
if [ "$HOST_OS" = "macOS" ]; then
|
||||
# The macos runner image ships an untrusted third-party tap we don't use;
|
||||
# untap it so brew stops emitting a tap-trust warning. Force + '|| true' since
|
||||
# untap fails if a formula was installed from it, which is harmless here.
|
||||
brew untap --force aws/tap || true
|
||||
fi
|
||||
|
||||
# Run 'sudo apt-get', with timeout, retrying if failed
|
||||
apt-get-retry() {
|
||||
local _attempt
|
||||
for _attempt in 1 2 3; do
|
||||
[ "$_attempt" -eq 1 ] || sleep $((15 * _attempt))
|
||||
sudo timeout --kill-after=30s 5m env DEBIAN_FRONTEND=noninteractive \
|
||||
apt-get -o DPkg::Lock::Timeout=60 "$@" && return 0
|
||||
done
|
||||
fatal "'apt-get $*' failed on all attempts"
|
||||
}
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
MAKE=make
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
MAKE=make
|
||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
MAKE=gmake
|
||||
else
|
||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
||||
fi
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
# Avoid slow "processing triggers for man db"
|
||||
echo "path-exclude /usr/share/doc/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
echo "path-exclude /usr/share/man/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
fi
|
||||
# Run 'brew', retrying if failed
|
||||
brew-retry() {
|
||||
local _attempt
|
||||
for _attempt in 1 2 3; do
|
||||
[ "$_attempt" -eq 1 ] || sleep $((15 * _attempt))
|
||||
brew "$@" && return 0
|
||||
done
|
||||
fatal "'brew $*' failed on all attempts"
|
||||
}
|
||||
|
||||
install-wavediff() {
|
||||
source ci/docker/buildenv/wavetools.conf
|
||||
local _base_url="https://github.com/hudson-trading/wavetools/releases/download/${WAVETOOLS_VERSION}"
|
||||
local _platform
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
if [ "$HOST_OS" = "linux" ]; then
|
||||
_platform="linux-x86_64"
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
elif [ "$HOST_OS" = "macOS" ]; then
|
||||
_platform="macos-arm64"
|
||||
elif [ "$CI_OS_NAME" = "windows" ]; then
|
||||
_platform="windows-x86_64"
|
||||
else
|
||||
echo "WARNING: No wavetools binary available for CI_OS_NAME=$CI_OS_NAME, skipping"
|
||||
echo "WARNING: No wavetools binary available for HOST_OS=$HOST_OS, skipping"
|
||||
return 0
|
||||
fi
|
||||
local _tmpdir
|
||||
_tmpdir=$(mktemp -d)
|
||||
local _archive="wavetools-${WAVETOOLS_VERSION}-${_platform}"
|
||||
if [ "$CI_OS_NAME" = "windows" ]; then
|
||||
wget -q -O "${_tmpdir}/${_archive}.zip" "${_base_url}/${_archive}.zip"
|
||||
unzip -o "${_tmpdir}/${_archive}.zip" -d "${_tmpdir}"
|
||||
else
|
||||
wget -q -O "${_tmpdir}/${_archive}.tar.gz" "${_base_url}/${_archive}.tar.gz"
|
||||
tar -xzf "${_tmpdir}/${_archive}.tar.gz" -C "${_tmpdir}"
|
||||
fi
|
||||
wget -q -O "${_tmpdir}/${_archive}.tar.gz" "${_base_url}/${_archive}.tar.gz"
|
||||
tar -xzf "${_tmpdir}/${_archive}.tar.gz" -C "${_tmpdir}"
|
||||
sudo cp "${_tmpdir}/${_archive}/wavediff" /usr/local/bin/wavediff
|
||||
rm -rf "${_tmpdir}"
|
||||
}
|
||||
|
||||
if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
if [ "$STAGE" = "build" ]; then
|
||||
##############################################################################
|
||||
# Dependencies of jobs in the 'build' stage, i.e.: packages required to
|
||||
# build Verilator
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
sudo apt-get update ||
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes ccache help2man libfl-dev ||
|
||||
sudo apt-get install --yes ccache help2man libfl-dev
|
||||
if [[ ! "$CI_RUNS_ON" =~ "ubuntu-22.04" ]]; then
|
||||
# Some conflict of libunwind verison on 22.04, can live without it for now
|
||||
sudo apt-get install --yes libjemalloc-dev ||
|
||||
sudo apt-get install --yes libjemalloc-dev
|
||||
fi
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev ||
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev
|
||||
if [ "$HOST_OS" = "linux" ]; then
|
||||
if [ "$DISTRO_ID" = "ubuntu" ]; then
|
||||
PACKAGES=(
|
||||
bear
|
||||
ccache
|
||||
help2man
|
||||
libfl-dev
|
||||
libsystemc-dev
|
||||
mold
|
||||
)
|
||||
# libunwind conflict on 22.04, can live without libjemalloc there
|
||||
if [ "$DISTRO_VERSION" != "22.04" ]; then
|
||||
PACKAGES+=(libjemalloc-dev)
|
||||
fi
|
||||
apt-get-retry update
|
||||
apt-get-retry install --yes "${PACKAGES[@]}"
|
||||
fi
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
sudo apt-get install --yes bear mold ||
|
||||
sudo apt-get install --yes bear mold
|
||||
fi
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
brew update ||
|
||||
brew update
|
||||
brew install ccache perl gperftools autoconf bison flex help2man ||
|
||||
brew install ccache perl gperftools autoconf bison flex help2man
|
||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
sudo pkg install -y autoconf bison ccache gmake perl5
|
||||
elif [ "$HOST_OS" = "macOS" ]; then
|
||||
PACKAGES=(
|
||||
autoconf
|
||||
bison
|
||||
ccache
|
||||
flex
|
||||
gperftools
|
||||
help2man
|
||||
perl
|
||||
)
|
||||
brew-retry update
|
||||
brew-retry install "${PACKAGES[@]}"
|
||||
else
|
||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
||||
fatal "Unknown HOST_OS: '$HOST_OS'"
|
||||
fi
|
||||
|
||||
if [ -n "$CCACHE_DIR" ]; then
|
||||
mkdir -p "$CCACHE_DIR"
|
||||
fi
|
||||
elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
elif [ "$STAGE" = "test" ]; then
|
||||
##############################################################################
|
||||
# Dependencies of jobs in the 'test' stage, i.e.: packages required to
|
||||
# run the tests
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
sudo apt-get update ||
|
||||
sudo apt-get update
|
||||
# libfl-dev needed for internal coverage's test runs
|
||||
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3 ||
|
||||
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3
|
||||
# Required for test_regress/t/t_dist_attributes.py
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
sudo apt-get install --yes python3-clang mold ||
|
||||
sudo apt-get install --yes python3-clang mold
|
||||
if [ "$HOST_OS" = "linux" ]; then
|
||||
if [ "$DISTRO_ID" = "ubuntu" ]; then
|
||||
PACKAGES=(
|
||||
ccache
|
||||
gdb
|
||||
jq
|
||||
lcov
|
||||
libfl-dev
|
||||
libsystemc-dev
|
||||
mold
|
||||
python3-clang
|
||||
z3
|
||||
)
|
||||
apt-get-retry update
|
||||
apt-get-retry install --yes "${PACKAGES[@]}"
|
||||
fi
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev ||
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev
|
||||
fi
|
||||
fi
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
brew update
|
||||
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
|
||||
brew install ccache perl jq z3
|
||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
# fst2vcd fails with "Could not open '<input file>', exiting."
|
||||
sudo pkg install -y ccache gmake perl5 python3 jq z3
|
||||
elif [ "$HOST_OS" = "macOS" ]; then
|
||||
PACKAGES=(
|
||||
ccache
|
||||
jq
|
||||
perl
|
||||
z3
|
||||
)
|
||||
brew-retry update
|
||||
brew-retry install "${PACKAGES[@]}"
|
||||
else
|
||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
||||
fatal "Unknown HOST_OS: '$HOST_OS'"
|
||||
fi
|
||||
# Common installs
|
||||
install-wavediff
|
||||
# Workaround -fsanitize=address crash
|
||||
sudo sysctl -w vm.mmap_rnd_bits=28
|
||||
elif [ "$STAGE" = "lint-py" ]; then
|
||||
##############################################################################
|
||||
# Dependencies for nodist/clang_check_attributes
|
||||
|
||||
if [ "$HOST_OS" = "linux" ] && [ "$DISTRO_ID" = "ubuntu" ]; then
|
||||
PACKAGES=(
|
||||
python3-clang # Not run, but importers are linted
|
||||
)
|
||||
apt-get-retry update
|
||||
apt-get-retry install --yes "${PACKAGES[@]}"
|
||||
fi
|
||||
elif [ "$STAGE" = "format" ]; then
|
||||
##############################################################################
|
||||
# Dependencies of 'make format'
|
||||
|
||||
if [ "$HOST_OS" = "linux" ] && [ "$DISTRO_ID" = "ubuntu" ]; then
|
||||
PACKAGES=(
|
||||
clang-format-18 # Version pinned, so all of CI formats alike
|
||||
)
|
||||
apt-get-retry update
|
||||
apt-get-retry install --yes "${PACKAGES[@]}"
|
||||
fi
|
||||
elif [ "$STAGE" = "coverage" ]; then
|
||||
##############################################################################
|
||||
# Dependencies of the coverage report job, which only collates what the
|
||||
# 'test' stage jobs measured
|
||||
|
||||
if [ "$HOST_OS" = "linux" ] && [ "$DISTRO_ID" = "ubuntu" ]; then
|
||||
PACKAGES=(
|
||||
lcov
|
||||
)
|
||||
apt-get-retry update
|
||||
apt-get-retry install --yes "${PACKAGES[@]}"
|
||||
fi
|
||||
elif [ "$STAGE" = "rtlmeter-run" ]; then
|
||||
##############################################################################
|
||||
# Dependencies of the RTLMeter jobs, which compile and run designs with an
|
||||
# already built Verilator, so they need its runtime prerequisites only
|
||||
|
||||
if [ "$HOST_OS" = "linux" ] && [ "$DISTRO_ID" = "ubuntu" ]; then
|
||||
PACKAGES=(
|
||||
ccache
|
||||
libfl-dev
|
||||
libjemalloc-dev
|
||||
libsystemc-dev
|
||||
mold
|
||||
)
|
||||
apt-get-retry update
|
||||
apt-get-retry install --yes "${PACKAGES[@]}"
|
||||
fi
|
||||
else
|
||||
##############################################################################
|
||||
# Unknown build stage
|
||||
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
|
||||
fatal "Unknown stage '$STAGE'"
|
||||
fi
|
||||
|
||||
# Report where the tools we may have installed live (ok if some are missing)
|
||||
set +x
|
||||
echo "Tools:"
|
||||
for bin in autoconf bear bison ccache flex gdb help2man jq lcov mold perl wavediff z3; do
|
||||
echo -n " $bin: "
|
||||
which "$bin" || echo "Not found"
|
||||
done
|
||||
|
||||
+139
-14
@@ -6,37 +6,162 @@
|
||||
|
||||
# Notify PRs via comment that their workflow reports are available
|
||||
|
||||
# Get the current repo URL - might differ on a fork
|
||||
readonly REPO_URL=$(gh repo view --json url --jq .url)
|
||||
# Note this deliberately does not 'set -e'. A run that cannot be notified, say
|
||||
# because its pull request was locked, must not hold up the notification of all
|
||||
# the others, so each failure below moves on to the next run instead. Its
|
||||
# artifact is then left in place, so it is retried on the next run of 'pages.yml'.
|
||||
|
||||
# The account we post as, which is how we tell our own earlier notifications
|
||||
# from anybody else's comments. 'pages.yml' sets this to the slug of the app the
|
||||
# token belongs to. Set it to your own login when running this by hand. The 'jq'
|
||||
# filter below picks it up from the environment.
|
||||
if [ -z "${GH_AUTHOR:-}" ]; then
|
||||
echo "GH_AUTHOR must hold the account we post as" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Posting as '${GH_AUTHOR}'"
|
||||
|
||||
# Marker line separating the report from the history of the older reports
|
||||
readonly HISTORY_MARKER='<!-- Verilator CI notification history -->'
|
||||
# Opening line of the HTML comment stashing the history entry of the report
|
||||
# itself. It is not shown in its own history, only inherited by the ones
|
||||
# superseding it, hence it is kept out of sight until then.
|
||||
readonly NEXTHIST_MARKER='<!-- Verilator CI notification entry'
|
||||
|
||||
# Create artifacts root directory
|
||||
ARTIFACTS_ROOT=artifacts
|
||||
mkdir -p ${ARTIFACTS_ROOT}
|
||||
|
||||
for RUN_ID in ${PR_RUN_IDS//,/ }; do
|
||||
echo "@@@ Processing run ${RUN_ID}"
|
||||
|
||||
# Create workflow artifacts directory
|
||||
ARTIFACTS_DIR=${ARTIFACTS_ROOT}/${RUN_ID}
|
||||
mkdir -p ${ARTIFACTS_DIR}
|
||||
|
||||
# Download artifact of this run, if exists
|
||||
gh run download ${RUN_ID} --name pr-notification --dir ${ARTIFACTS_DIR} || true
|
||||
ls -lsha ${ARTIFACTS_DIR}
|
||||
gh run download ${RUN_ID} --name pr-notification --dir ${ARTIFACTS_DIR} &> /dev/null || true
|
||||
|
||||
# Move on if no notification is required
|
||||
if [ ! -f ${ARTIFACTS_DIR}/pr-number.txt ]; then
|
||||
echo "No notification found"
|
||||
continue
|
||||
fi
|
||||
echo "Posting notification found"
|
||||
|
||||
cat ${ARTIFACTS_DIR}/body.txt
|
||||
gh pr comment $(cat ${ARTIFACTS_DIR}/pr-number.txt) --body-file ${ARTIFACTS_DIR}/body.txt
|
||||
PR_NUMBER=$(cat ${ARTIFACTS_DIR}/pr-number.txt)
|
||||
echo "@@@ Run ${RUN_ID}: notifying PR #${PR_NUMBER}"
|
||||
|
||||
# Get the artifact ID
|
||||
ARTIFACT_ID=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
|
||||
# Everything below is created and validated by the 'upload-pr-notification'
|
||||
# action. Move on if this artifact was not made by it, e.g. it is left over
|
||||
# from an earlier version of the CI, so it does not hold up the other runs.
|
||||
for f in body.txt key.txt hist.txt; do
|
||||
if [ ! -f ${ARTIFACTS_DIR}/${f} ]; then
|
||||
echo "Notification has no '${f}'" >&2
|
||||
continue 2 # Note: continues the enclosing 'RUN_ID' loop
|
||||
fi
|
||||
done
|
||||
|
||||
# Delete it, so we only notify once
|
||||
gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}"
|
||||
# 'key.txt' holds the key naming the source of the notification. It goes on the
|
||||
# first line of the comment as an HTML comment, so it is invisible when
|
||||
# rendered, and is used below to find the stale notifications with the same
|
||||
# key, which are then superseded by the new one and deleted.
|
||||
COMMENT_MARKER="<!-- Verilator CI notification: $(cat ${ARTIFACTS_DIR}/key.txt) -->"
|
||||
|
||||
# Find the stale notifications from the same source on this PR. That is, our
|
||||
# own comments with the same marker on their first line. Oldest first, as
|
||||
# returned by the API. An app posts as '<slug>[bot]', so drop that suffix to
|
||||
# compare against the slug. Move on if they cannot be listed, as without them
|
||||
# we would post a duplicate instead of superseding them.
|
||||
if ! COMMENT_MARKER="${COMMENT_MARKER}" \
|
||||
gh api --paginate "repos/{owner}/{repo}/issues/${PR_NUMBER}/comments" \
|
||||
--jq '.[]
|
||||
| select((.user.login | rtrimstr("[bot]")) == env.GH_AUTHOR)
|
||||
| select((.body | split("\n") | .[0] | rtrimstr("\r")) == env.COMMENT_MARKER)
|
||||
| {id, body}' > ${ARTIFACTS_DIR}/stale.json; then
|
||||
echo "Failed to list the comments of PR #${PR_NUMBER}" >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
# Inherit the history of the newest stale notification. There should only ever
|
||||
# be one, and it holds the whole history anyway, as each notification inherits
|
||||
# the history of the one it superseded. Any older ones are only deleted below.
|
||||
STALE_COUNT=$(wc -l < ${ARTIFACTS_DIR}/stale.json)
|
||||
if [ ${STALE_COUNT} -gt 1 ]; then
|
||||
echo "Found ${STALE_COUNT} stale notifications, only the newest is inherited from"
|
||||
fi
|
||||
|
||||
HISTORY=${ARTIFACTS_DIR}/history.txt
|
||||
touch ${HISTORY}
|
||||
if [ ${STALE_COUNT} -gt 0 ]; then
|
||||
tail -n 1 ${ARTIFACTS_DIR}/stale.json | jq -r '.body' | tr -d '\r' > ${ARTIFACTS_DIR}/stale-body.txt
|
||||
# Its own entry, stashed in an HTML comment
|
||||
awk -v marker="${NEXTHIST_MARKER}" '
|
||||
$0 == marker { inside = 1; next }
|
||||
!inside { next }
|
||||
$0 == "-->" { exit }
|
||||
NF { print }
|
||||
' ${ARTIFACTS_DIR}/stale-body.txt >> ${HISTORY}
|
||||
# Followed by its history, without the enclosing '<details>' markup
|
||||
awk -v marker="${HISTORY_MARKER}" '
|
||||
$0 == marker { inside = 1; next }
|
||||
!inside { next }
|
||||
$0 == "<details>" || /^<summary>/ { next }
|
||||
$0 == "</details>" { exit }
|
||||
NF { print }
|
||||
' ${ARTIFACTS_DIR}/stale-body.txt >> ${HISTORY}
|
||||
fi
|
||||
|
||||
# Assemble the comment. Starts with the marker and the report.
|
||||
COMMENT=${ARTIFACTS_DIR}/comment.txt
|
||||
cat > ${COMMENT} <<COMMENT_TEMPLATE
|
||||
${COMMENT_MARKER}
|
||||
$(cat ${ARTIFACTS_DIR}/body.txt)
|
||||
COMMENT_TEMPLATE
|
||||
|
||||
# Then the history inherited from the notifications this one supersedes, so
|
||||
# the older reports remain reachable from the only remaining comment.
|
||||
if [ -s ${HISTORY} ]; then
|
||||
echo "History holds $(wc -l < ${HISTORY}) entries"
|
||||
cat >> ${COMMENT} <<HISTORY_TEMPLATE
|
||||
${HISTORY_MARKER}
|
||||
<details>
|
||||
<summary>PR history</summary>
|
||||
|
||||
$(cat ${HISTORY})
|
||||
</details>
|
||||
HISTORY_TEMPLATE
|
||||
fi
|
||||
|
||||
# Finally this report's own history entry, stashed out of sight until a later
|
||||
# notification inherits it.
|
||||
cat >> ${COMMENT} <<NEXTHIST_TEMPLATE
|
||||
${NEXTHIST_MARKER}
|
||||
$(cat ${ARTIFACTS_DIR}/hist.txt)
|
||||
-->
|
||||
NEXTHIST_TEMPLATE
|
||||
|
||||
# Post it. Move on if this failed, without deleting anything below, otherwise
|
||||
# the history held by the stale notifications would be lost.
|
||||
if ! jq -Rs '{body: .}' ${COMMENT} \
|
||||
| gh api --method POST "repos/{owner}/{repo}/issues/${PR_NUMBER}/comments" \
|
||||
--input - > ${ARTIFACTS_DIR}/posted.json; then
|
||||
echo "Failed to post this notification on PR #${PR_NUMBER}:" >&2
|
||||
cat ${COMMENT} >&2
|
||||
continue
|
||||
fi
|
||||
echo "Posted $(jq -r '.html_url' ${ARTIFACTS_DIR}/posted.json)"
|
||||
|
||||
# Delete the stale notifications, so only the new one remains
|
||||
for COMMENT_ID in $(jq -r '.id' ${ARTIFACTS_DIR}/stale.json); do
|
||||
echo "Deleting stale comment ${COMMENT_ID}"
|
||||
gh api --method DELETE "repos/{owner}/{repo}/issues/comments/${COMMENT_ID}"
|
||||
done
|
||||
|
||||
# Get the artifact IDs. Note there can be more than one artifact named
|
||||
# 'pr-notification' for a single run, as the artifacts endpoint lists
|
||||
# artifacts across all run attempts, and a re-run uploads a new one while
|
||||
# keeping the previous attempt's artifact.
|
||||
ARTIFACT_IDS=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
|
||||
|
||||
# Delete them all, so we only notify once
|
||||
for ARTIFACT_ID in ${ARTIFACT_IDS}; do
|
||||
gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}"
|
||||
done
|
||||
done
|
||||
|
||||
+6
-22
@@ -35,13 +35,10 @@ compile_coverage_reports() {
|
||||
|
||||
# Gather all coverage workflow runs within the time window
|
||||
gh run list -w coverage.yml --limit 1000 --created ">=${OLDEST}" --json "databaseId,event,status,conclusion,createdAt,number" > recentRuns.json
|
||||
echo @@@ Recent runs:
|
||||
jq "." recentRuns.json
|
||||
|
||||
# Select completd runs that were not cancelled or skipped, sort by descending run number
|
||||
jq 'sort_by(-.number) | map(select(.status == "completed" and (.conclusion == "success" or .conclusion == "failure")))' recentRuns.json > completedRuns.json
|
||||
echo @@@ Completed with success or failure:
|
||||
jq "." completedRuns.json
|
||||
echo "@@@ $(jq length completedRuns.json) of $(jq length recentRuns.json) runs since ${OLDEST} completed with success or failure"
|
||||
|
||||
# Create artifacts root directory
|
||||
local ARTIFACTS_ROOT=artifacts-coverage
|
||||
@@ -64,11 +61,8 @@ compile_coverage_reports() {
|
||||
|
||||
# For each worfklow run that was triggered by this event type
|
||||
for RUN_ID in $(jq ".[] | select(.event == \"${EVENT}\") |.databaseId" completedRuns.json); do
|
||||
echo "@@@ Processing run ${RUN_ID}"
|
||||
|
||||
# Extract the info of this run
|
||||
jq ".[] | select(.databaseId == $RUN_ID)" completedRuns.json > workflow.json
|
||||
jq "." workflow.json
|
||||
|
||||
# Record run ID of PR job
|
||||
[[ $EVENT != "pull_request" ]] || PR_RUN_IDS="$PR_RUN_IDS $RUN_ID"
|
||||
@@ -78,15 +72,13 @@ compile_coverage_reports() {
|
||||
mkdir -p ${ARTIFACTS_DIR}
|
||||
|
||||
# Download artifacts of this run, if exists
|
||||
gh run download ${RUN_ID} --name coverage-report --dir ${ARTIFACTS_DIR} || true
|
||||
ls -lsha ${ARTIFACTS_DIR}
|
||||
gh run download ${RUN_ID} --name coverage-report --dir ${ARTIFACTS_DIR} &> /dev/null || true
|
||||
|
||||
# Move on if no coverage report is available
|
||||
if [ ! -d ${ARTIFACTS_DIR}/report ]; then
|
||||
echo "No coverage report found"
|
||||
continue
|
||||
fi
|
||||
echo "Coverage report found"
|
||||
echo "@@@ Run ${RUN_ID}: coverage report found"
|
||||
|
||||
# Emit section header
|
||||
if [[ -n $EMIT_SECTION_HEADER ]]; then
|
||||
@@ -159,13 +151,10 @@ compile_rtlmeter_reports() {
|
||||
|
||||
# Gather all RTLMeter workflow runs within the time window
|
||||
gh run list -w rtlmeter.yml --limit 1000 --created ">=${OLDEST}" --json "databaseId,event,status,conclusion,createdAt,number" > recentRuns.json
|
||||
echo @@@ Recent runs:
|
||||
jq "." recentRuns.json
|
||||
|
||||
# Select completd runs that were not cancelled or skipped, sort by descending run number
|
||||
jq 'sort_by(-.number) | map(select(.status == "completed" and (.conclusion == "success" or .conclusion == "failure")))' recentRuns.json > completedRuns.json
|
||||
echo @@@ Completed with success or failure:
|
||||
jq "." completedRuns.json
|
||||
echo "@@@ $(jq length completedRuns.json) of $(jq length recentRuns.json) runs since ${OLDEST} completed with success or failure"
|
||||
|
||||
# Create artifacts root directory
|
||||
local ARTIFACTS_ROOT=artifacts-rtlmeter
|
||||
@@ -188,11 +177,8 @@ compile_rtlmeter_reports() {
|
||||
|
||||
# For each worfklow run that was triggered by this event type
|
||||
for RUN_ID in $(jq ".[] | select(.event == \"${EVENT}\") |.databaseId" completedRuns.json); do
|
||||
echo "@@@ Processing run ${RUN_ID}"
|
||||
|
||||
# Extract the info of this run
|
||||
jq ".[] | select(.databaseId == $RUN_ID)" completedRuns.json > workflow.json
|
||||
jq "." workflow.json
|
||||
|
||||
# Record run ID of PR job
|
||||
[[ $EVENT != "pull_request" ]] || PR_RUN_IDS="$PR_RUN_IDS $RUN_ID"
|
||||
@@ -202,15 +188,13 @@ compile_rtlmeter_reports() {
|
||||
mkdir -p ${ARTIFACTS_DIR}
|
||||
|
||||
# Download artifacts of this run, if exists
|
||||
gh run download ${RUN_ID} --name rtlmeter-report --dir ${ARTIFACTS_DIR} || true
|
||||
ls -lsha ${ARTIFACTS_DIR}
|
||||
gh run download ${RUN_ID} --name rtlmeter-report --dir ${ARTIFACTS_DIR} &> /dev/null || true
|
||||
|
||||
# Move on if no RTLMeter report is available
|
||||
if [ ! -d ${ARTIFACTS_DIR}/report ]; then
|
||||
echo "No RTLMeter report found"
|
||||
continue
|
||||
fi
|
||||
echo "RTLMeter report found"
|
||||
echo "@@@ Run ${RUN_ID}: RTLMeter report found"
|
||||
|
||||
# Emit section header
|
||||
if [[ -n $EMIT_SECTION_HEADER ]]; then
|
||||
|
||||
+12
-4
@@ -25,6 +25,8 @@ CASES = {
|
||||
("BlackParrot:2x2:*", 14),
|
||||
("BlackParrot:4x4:*", 25),
|
||||
("Caliptra:default:*", 19),
|
||||
("HummingbirdV2-E203:default:*", 3),
|
||||
("HummingbirdV2-E203:nodelay:*", 3),
|
||||
("NVDLA:*", 29),
|
||||
("OpenPiton:1x1:*", 4),
|
||||
("OpenPiton:2x2:*", 7),
|
||||
@@ -57,13 +59,15 @@ CASES = {
|
||||
("BlackParrot:2x2:*", 9),
|
||||
("BlackParrot:4x4:*", 12),
|
||||
("Caliptra:default:*", 16),
|
||||
("HummingbirdV2-E203:default:*", 10),
|
||||
("HummingbirdV2-E203:nodelay:*", 10),
|
||||
("NVDLA:*", 28),
|
||||
("OpenPiton:1x1:*", 5),
|
||||
("OpenPiton:2x2:*", 6),
|
||||
("OpenPiton:4x4:*", 8),
|
||||
("OpenTitan:*", 10),
|
||||
("Servant:qerv:*", 17),
|
||||
("Servant:serv:*", 15),
|
||||
("Servant:qerv:phil", 17),
|
||||
("Servant:serv:phil", 15),
|
||||
("VeeR-EH1:asic:*", 6),
|
||||
("VeeR-EH1:default:*", 8),
|
||||
("VeeR-EH1:hiperf:*", 5),
|
||||
@@ -107,6 +111,8 @@ CASES_PR = {
|
||||
("BlackParrot:4x4:*", 25),
|
||||
("Caliptra:default:*", 19),
|
||||
("NVDLA:*", 29),
|
||||
("HummingbirdV2-E203:default:*", 3),
|
||||
("HummingbirdV2-E203:nodelay:*", 3),
|
||||
("OpenPiton:1x1:*", 4),
|
||||
("OpenPiton:4x4:*", 10),
|
||||
("OpenTitan:*", 16),
|
||||
@@ -137,11 +143,13 @@ CASES_PR = {
|
||||
("BlackParrot:4x4:*", 12),
|
||||
("Caliptra:default:*", 16),
|
||||
("NVDLA:*", 28),
|
||||
("HummingbirdV2-E203:default:*", 10),
|
||||
("HummingbirdV2-E203:nodelay:*", 10),
|
||||
("OpenPiton:1x1:*", 5),
|
||||
("OpenPiton:4x4:*", 8),
|
||||
("OpenTitan:*", 10),
|
||||
("Servant:qerv:*", 17),
|
||||
("Servant:serv:*", 15),
|
||||
("Servant:qerv:phil", 17),
|
||||
("Servant:serv:phil", 15),
|
||||
("VeeR-EH1:asic:*", 6),
|
||||
("VeeR-EH1:default:*", 8),
|
||||
("VeeR-EH1:hiperf:*", 5),
|
||||
|
||||
@@ -88,9 +88,16 @@ venv/bin/python3 $SCRIPT_DIR/ci-rtlmeter-report.py ${SUMMARY_ARGS[@]} > $TMP_DIR
|
||||
# Print it
|
||||
cat $TMP_DIR/summary.txt
|
||||
|
||||
# Create notification comment content
|
||||
NOTIFICATION=$TMP_DIR/notification.txt
|
||||
cat > $NOTIFICATION <<NOTIFICATION_TEMPLATE
|
||||
REPORT_URL=https://${PAGES_OWNER}.github.io/${PAGES_NAME}/rtlmeter-reports/${RUN_ID}/index.html
|
||||
|
||||
###############################################################################
|
||||
# Create the PR notification
|
||||
###############################################################################
|
||||
|
||||
NOTIFICATION_DIR=$TMP_DIR/notification
|
||||
mkdir -p $NOTIFICATION_DIR
|
||||
|
||||
cat > ${NOTIFICATION_DIR}/body.txt <<NOTIFICATION_TEMPLATE
|
||||
Performance metrics for PR workflow [#$RUN_NUM]($RUN_URL), comparing:
|
||||
- target branch commit [${REF_SHA:0:9}](https://github.com/${PAGES_OWNER}/${PAGES_NAME}/commit/${REF_SHA}) (A)
|
||||
- with PR merge commit [${RUN_SHA:0:9}](https://github.com/${PAGES_OWNER}/${PAGES_NAME}/commit/${RUN_SHA}) (B)
|
||||
@@ -106,10 +113,15 @@ The reported numbers are the geometric means of the ratios of the metrics over a
|
||||
less than 1 is a regression, greater than 1 is an improvement.
|
||||
The jobs run on variable and noisy runners, so some variance is expected between runs.
|
||||
|
||||
Detailed report: [${RUN_ID}](https://${PAGES_OWNER}.github.io/${PAGES_NAME}/rtlmeter-reports/${RUN_ID}/index.html)
|
||||
Detailed report: [${RUN_ID}](${REPORT_URL})
|
||||
NOTIFICATION_TEMPLATE
|
||||
|
||||
echo "Workflow [#$RUN_NUM]($RUN_URL) report: [${RUN_ID}](${REPORT_URL})" > ${NOTIFICATION_DIR}/hist.txt
|
||||
|
||||
###############################################################################
|
||||
# Create detailed report
|
||||
###############################################################################
|
||||
|
||||
REPORT=$TMP_DIR/body.html
|
||||
cat > $REPORT <<SUMMARY_TEMPLATE
|
||||
<h3>Summary of all runs</h3>
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# DESCRIPTION: Verilator: CI main job script
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2020 Geza Lore
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
################################################################################
|
||||
# This is the main script executed in the 'script' phase by all jobs. We use a
|
||||
# single script to keep the CI setting simple. We pass job parameters via
|
||||
# environment variables using 'env' keys.
|
||||
################################################################################
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
fatal() {
|
||||
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
|
||||
}
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
export MAKE=make
|
||||
NPROC=$(nproc)
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
export MAKE=make
|
||||
NPROC=$(sysctl -n hw.logicalcpu)
|
||||
# Disable ccache, doesn't always work in GitHub Actions
|
||||
export OBJCACHE=
|
||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
export MAKE=gmake
|
||||
NPROC=$(sysctl -n hw.ncpu)
|
||||
else
|
||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
||||
fi
|
||||
NPROC=$(expr $NPROC '+' 1)
|
||||
|
||||
if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
##############################################################################
|
||||
# Build verilator
|
||||
|
||||
autoconf
|
||||
CONFIGURE_ARGS="--enable-longtests --enable-ccwarn"
|
||||
if [ "$CI_DEV_ASAN" = 1 ]; then
|
||||
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-asan"
|
||||
CXX="$CXX -DVL_LEAK_CHECKS"
|
||||
fi
|
||||
if [ "$CI_DEV_GCOV" = 1 ]; then
|
||||
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-gcov"
|
||||
fi
|
||||
./configure $CONFIGURE_ARGS --prefix="$INSTALL_DIR"
|
||||
ccache -z
|
||||
"$MAKE" -j "$NPROC" -k
|
||||
# 22.04: ccache -s -v
|
||||
ccache -s
|
||||
if [ "$CI_OS_NAME" = "osx" ]; then
|
||||
file bin/verilator_bin
|
||||
file bin/verilator_bin_dbg
|
||||
md5 bin/verilator_bin
|
||||
md5 bin/verilator_bin_dbg
|
||||
stat bin/verilator_bin
|
||||
stat bin/verilator_bin_dbg
|
||||
fi
|
||||
elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
##############################################################################
|
||||
# Run tests
|
||||
|
||||
export VERILATOR_TEST_NO_CONTRIBUTORS=1 # Separate workflow check
|
||||
export VERILATOR_TEST_NO_LINT_PY=1 # Separate workflow check
|
||||
|
||||
if [ "$CI_OS_NAME" = "osx" ]; then
|
||||
export VERILATOR_TEST_NO_GDB=1 # Pain to get GDB to work on OS X
|
||||
# TODO below may no longer be required as configure checks for -pg
|
||||
export VERILATOR_TEST_NO_GPROF=1 # Apple Clang has no -pg
|
||||
# export PATH="/Applications/gtkwave.app/Contents/Resources/bin:$PATH" # fst2vcd
|
||||
file bin/verilator_bin
|
||||
file bin/verilator_bin_dbg
|
||||
md5 bin/verilator_bin
|
||||
md5 bin/verilator_bin_dbg
|
||||
stat bin/verilator_bin
|
||||
stat bin/verilator_bin_dbg
|
||||
# For some reason, the dbg exe is corrupted by this point ('file' reports
|
||||
# it as data rather than a Mach-O). Unclear if this is an OS X issue or
|
||||
# CI's. Remove the file and re-link...
|
||||
rm bin/verilator_bin_dbg
|
||||
"$MAKE" -j "$NPROC" -k
|
||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
export VERILATOR_TEST_NO_GDB=1 # Disable for now, ideally should run
|
||||
# TODO below may no longer be required as configure checks for -pg
|
||||
export VERILATOR_TEST_NO_GPROF=1 # gprof is a bit different on FreeBSD, disable
|
||||
fi
|
||||
|
||||
TEST_REGRESS=test_regress
|
||||
if [ "$CI_RELOC" == 1 ]; then
|
||||
# Testing that the installation is relocatable.
|
||||
"$MAKE" install
|
||||
mkdir -p "$RELOC_DIR"
|
||||
mv "$INSTALL_DIR" "$RELOC_DIR/relocated-install"
|
||||
export VERILATOR_ROOT="$RELOC_DIR/relocated-install/share/verilator"
|
||||
TEST_REGRESS="$RELOC_DIR/test_regress"
|
||||
mv test_regress "$TEST_REGRESS"
|
||||
NODIST="$RELOC_DIR/nodist"
|
||||
mv nodist "$NODIST"
|
||||
# Feeling brave?
|
||||
find . -delete
|
||||
ls -la .
|
||||
fi
|
||||
|
||||
# Run the specified test
|
||||
ccache -z
|
||||
case $TESTS in
|
||||
dist-vlt-0)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=0/4
|
||||
;;
|
||||
dist-vlt-1)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=1/4
|
||||
;;
|
||||
dist-vlt-2)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=2/4
|
||||
;;
|
||||
dist-vlt-3)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=3/4
|
||||
;;
|
||||
vltmt-0)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=0/3
|
||||
;;
|
||||
vltmt-1)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=1/3
|
||||
;;
|
||||
vltmt-2)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=2/3
|
||||
;;
|
||||
coverage-dist)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist"
|
||||
;;
|
||||
coverage-vlt-0)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=0/10
|
||||
;;
|
||||
coverage-vlt-1)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=1/10
|
||||
;;
|
||||
coverage-vlt-2)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=2/10
|
||||
;;
|
||||
coverage-vlt-3)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=3/10
|
||||
;;
|
||||
coverage-vlt-4)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=4/10
|
||||
;;
|
||||
coverage-vlt-5)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=5/10
|
||||
;;
|
||||
coverage-vlt-6)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=6/10
|
||||
;;
|
||||
coverage-vlt-7)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=7/10
|
||||
;;
|
||||
coverage-vlt-8)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=8/10
|
||||
;;
|
||||
coverage-vlt-9)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=9/10
|
||||
;;
|
||||
coverage-vltmt-0)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=0/10
|
||||
;;
|
||||
coverage-vltmt-1)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=1/10
|
||||
;;
|
||||
coverage-vltmt-2)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=2/10
|
||||
;;
|
||||
coverage-vltmt-3)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=3/10
|
||||
;;
|
||||
coverage-vltmt-4)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=4/10
|
||||
;;
|
||||
coverage-vltmt-5)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=5/10
|
||||
;;
|
||||
coverage-vltmt-6)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=6/10
|
||||
;;
|
||||
coverage-vltmt-7)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=7/10
|
||||
;;
|
||||
coverage-vltmt-8)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=8/10
|
||||
;;
|
||||
coverage-vltmt-9)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=9/10
|
||||
;;
|
||||
*)
|
||||
fatal "Unknown TESTS: $TESTS"
|
||||
;;
|
||||
esac
|
||||
|
||||
# To see load average (1 minute, 5 minute, 15 minute)
|
||||
uptime
|
||||
# 22.04: ccache -s -v
|
||||
ccache -s
|
||||
|
||||
else
|
||||
##############################################################################
|
||||
# Unknown build stage
|
||||
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
|
||||
fi
|
||||
Executable
+169
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bash
|
||||
# DESCRIPTION: Verilator: CI test job script
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
################################################################################
|
||||
# Executed in the 'test' stage.
|
||||
################################################################################
|
||||
|
||||
# Destructive to the checkout (wipes most of it); never run locally
|
||||
if [ "$GITHUB_ACTIONS" != "true" ]; then
|
||||
echo "ERROR: $(basename "$0") must only be run in GitHub Actions CI" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
source "$(dirname "$0")/ci-common.bash"
|
||||
|
||||
################################################################################
|
||||
# Parse arguments
|
||||
|
||||
OPT_RELOC=
|
||||
OPT_SUITE=
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--reloc)
|
||||
[ $# -ge 2 ] || fatal "--reloc requires an argument"
|
||||
OPT_RELOC="$2"
|
||||
shift
|
||||
;;
|
||||
--suite)
|
||||
[ $# -ge 2 ] || fatal "--suite requires an argument"
|
||||
OPT_SUITE="$2"
|
||||
shift
|
||||
;;
|
||||
*) fatal "Unknown option: '$1'" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ -n "$OPT_SUITE" ] || fatal "--suite is required"
|
||||
|
||||
################################################################################
|
||||
# Run tests
|
||||
|
||||
export VERILATOR_TEST_NO_CONTRIBUTORS=1 # Separate workflow check
|
||||
export VERILATOR_TEST_NO_LINT_PY=1 # Separate workflow check
|
||||
|
||||
if [ "$HOST_OS" = "macOS" ]; then
|
||||
export VERILATOR_TEST_NO_GDB=1 # No working GDB on macOS
|
||||
fi
|
||||
|
||||
TEST_REGRESS=test_regress
|
||||
if [ -n "$OPT_RELOC" ]; then
|
||||
# Testing that the installation is relocatable.
|
||||
"$MAKE" install
|
||||
# Install prefix, as configured into the Makefile at build time
|
||||
INSTALL_DIR=$(sed -n 's/^prefix = //p' Makefile)
|
||||
mkdir -p "$OPT_RELOC"
|
||||
mv "$INSTALL_DIR" "$OPT_RELOC/relocated-install"
|
||||
export VERILATOR_ROOT="$OPT_RELOC/relocated-install/share/verilator"
|
||||
TEST_REGRESS="$OPT_RELOC/test_regress"
|
||||
mv test_regress "$TEST_REGRESS"
|
||||
NODIST="$OPT_RELOC/nodist"
|
||||
mv nodist "$NODIST"
|
||||
# Delete everything else, but keep the CI infrastructure
|
||||
find . -mindepth 1 -maxdepth 1 ! -name .github ! -name ci -exec rm -rf {} +
|
||||
ls -la .
|
||||
fi
|
||||
|
||||
# Run the specified suite
|
||||
ccache -z
|
||||
TEST_START=$SECONDS
|
||||
case $OPT_SUITE in
|
||||
dist-vlt-0)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=0/4
|
||||
;;
|
||||
dist-vlt-1)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=1/4
|
||||
;;
|
||||
dist-vlt-2)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=2/4
|
||||
;;
|
||||
dist-vlt-3)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=3/4
|
||||
;;
|
||||
vltmt-0)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=0/3
|
||||
;;
|
||||
vltmt-1)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=1/3
|
||||
;;
|
||||
vltmt-2)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=2/3
|
||||
;;
|
||||
coverage-dist)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist"
|
||||
;;
|
||||
coverage-vlt-0)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=0/10
|
||||
;;
|
||||
coverage-vlt-1)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=1/10
|
||||
;;
|
||||
coverage-vlt-2)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=2/10
|
||||
;;
|
||||
coverage-vlt-3)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=3/10
|
||||
;;
|
||||
coverage-vlt-4)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=4/10
|
||||
;;
|
||||
coverage-vlt-5)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=5/10
|
||||
;;
|
||||
coverage-vlt-6)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=6/10
|
||||
;;
|
||||
coverage-vlt-7)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=7/10
|
||||
;;
|
||||
coverage-vlt-8)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=8/10
|
||||
;;
|
||||
coverage-vlt-9)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=9/10
|
||||
;;
|
||||
coverage-vltmt-0)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=0/10
|
||||
;;
|
||||
coverage-vltmt-1)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=1/10
|
||||
;;
|
||||
coverage-vltmt-2)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=2/10
|
||||
;;
|
||||
coverage-vltmt-3)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=3/10
|
||||
;;
|
||||
coverage-vltmt-4)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=4/10
|
||||
;;
|
||||
coverage-vltmt-5)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=5/10
|
||||
;;
|
||||
coverage-vltmt-6)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=6/10
|
||||
;;
|
||||
coverage-vltmt-7)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=7/10
|
||||
;;
|
||||
coverage-vltmt-8)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=8/10
|
||||
;;
|
||||
coverage-vltmt-9)
|
||||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=9/10
|
||||
;;
|
||||
*)
|
||||
fatal "Unknown suite: $OPT_SUITE"
|
||||
;;
|
||||
esac
|
||||
ccache -svv
|
||||
ccache --evict-older-than "$((SECONDS - TEST_START + 60))s"
|
||||
ccache -svv
|
||||
uptime # To see load average
|
||||
+20
-6
@@ -6,19 +6,33 @@
|
||||
|
||||
Set-PSDebug -Trace 1
|
||||
|
||||
if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
|
||||
$NPROC = $env:NUMBER_OF_PROCESSORS
|
||||
|
||||
# Absolute path for the win_flex_bison install; CMake reads this from the environment
|
||||
$env:WIN_FLEX_BISON = "$(Resolve-Path ..)/win_flex_bison"
|
||||
|
||||
if (-Not (Test-Path $env:WIN_FLEX_BISON/win_bison.exe)) {
|
||||
git clone --depth 1 https://github.com/lexxmark/winflexbison
|
||||
cd winflexbison
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. --install-prefix $PWD/../../../.ccache
|
||||
cmake --build . --config Release -j 3
|
||||
cmake --install . --prefix $PWD/../../../.ccache
|
||||
cmake .. --install-prefix $env:WIN_FLEX_BISON
|
||||
cmake --build . --config Release -j $NPROC
|
||||
cmake --install . --prefix $env:WIN_FLEX_BISON
|
||||
cd ../..
|
||||
}
|
||||
|
||||
# Enter the MSVC developer shell so cl/link are on PATH for the Ninja generator
|
||||
$VsPath = & "${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe" -latest -products * -property installationPath
|
||||
Import-Module "$VsPath/Common7/Tools/Microsoft.VisualStudio.DevShell.dll"
|
||||
Enter-VsDevShell -VsInstallPath $VsPath -SkipAutomaticLocation -DevCmdArguments "-arch=x64 -host_arch=x64"
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. --install-prefix $PWD/../install
|
||||
cmake --build . --config Release -j 3
|
||||
# Ninja saturates all cores; the MSBuild generator only parallelizes across
|
||||
# projects and Verilator is a single target, so it compiles serially. /Od skips
|
||||
# optimization: this job only checks that Verilator builds and can verilate an
|
||||
# example, so an optimized binary is not needed and the codegen time dominates.
|
||||
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release --install-prefix $PWD/../install "-DCMAKE_CXX_FLAGS_RELEASE=/Od /DNDEBUG"
|
||||
cmake --build .
|
||||
cmake --install . --prefix $PWD/../install
|
||||
|
||||
+6
-2
@@ -7,13 +7,17 @@
|
||||
|
||||
Set-PSDebug -Trace 1
|
||||
|
||||
$NPROC = $env:NUMBER_OF_PROCESSORS
|
||||
|
||||
cd install
|
||||
$Env:VERILATOR_ROOT=$PWD
|
||||
cd examples/cmake_tracing_c
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --config Release -j 3
|
||||
# /Od skips optimization; this only checks the example verilates and builds, so
|
||||
# an optimized binary is not needed (see ci-win-compile.ps1).
|
||||
cmake .. "-DCMAKE_CXX_FLAGS_RELEASE=/Od /DNDEBUG"
|
||||
cmake --build . --config Release -j $NPROC
|
||||
|
||||
# TODO put this back in, see issue# 5163
|
||||
# Release/example.exe
|
||||
|
||||
+64
-7
@@ -12,7 +12,7 @@
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.050 2026-07-01],
|
||||
AC_INIT([Verilator],[5.052 2026-09-05],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
@@ -129,6 +129,21 @@ AC_ARG_ENABLE([dev-gcov],
|
||||
AC_SUBST(CFG_WITH_DEV_GCOV)
|
||||
AC_MSG_RESULT($CFG_WITH_DEV_GCOV)
|
||||
|
||||
# Flag to reduce the debug info in the debug executable to minimize its size
|
||||
AC_MSG_CHECKING(whether to reduce debug info in the debug executable)
|
||||
AC_ARG_ENABLE([light-debug],
|
||||
[AS_HELP_STRING([--enable-light-debug],
|
||||
[Reduce the amount of debug information in the debug
|
||||
Verilator executable to minimize its size. Enable
|
||||
slight optimization. Enough for backtraces only.])],
|
||||
[case "${enableval}" in
|
||||
yes) CFG_WITH_LIGHT_DEBUG=yes ;;
|
||||
no) CFG_WITH_LIGHT_DEBUG=no ;;
|
||||
*) AC_MSG_ERROR([bad value '${enableval}' for --enable-light-debug]) ;;
|
||||
esac],
|
||||
CFG_WITH_LIGHT_DEBUG=no)
|
||||
AC_MSG_RESULT($CFG_WITH_LIGHT_DEBUG)
|
||||
|
||||
# Special Substitutions - CFG_WITH_DEFENV
|
||||
AC_MSG_CHECKING(whether to use hardcoded paths)
|
||||
AC_ARG_ENABLE([defenv],
|
||||
@@ -432,6 +447,7 @@ AC_DEFUN([_MY_LDLIBS_CHECK_SET],
|
||||
|
||||
# Add the coverage flags early as they influence later checks.
|
||||
if test "$CFG_WITH_DEV_GCOV" = "yes"; then
|
||||
CFG_GCOV_CXX="$CXX"
|
||||
_MY_CXX_CHECK_OPT(CXX,--coverage)
|
||||
# Otherwise inline may not show as uncovered
|
||||
# If we use this then e.g. verilated.h functions properly show up
|
||||
@@ -453,8 +469,32 @@ if test "$CFG_WITH_DEV_GCOV" = "yes"; then
|
||||
_MY_CXX_CHECK_OPT(CXX,-fprofile-abs-path)
|
||||
# Define so compiled code can know
|
||||
_MY_CXX_CHECK_OPT(CXX,-DVL_GCOV)
|
||||
AC_DEFINE([HAVE_DEV_GCOV],[1],[Defined if compiled with code coverage collection for gcov])]
|
||||
AC_DEFINE([HAVE_DEV_GCOV],[1],[Defined if compiled with code coverage collection for gcov])
|
||||
if test "x$GCOV" = "x"; then GCOV=gcov; fi
|
||||
AC_MSG_CHECKING([whether $GCOV reads coverage data from $CFG_GCOV_CXX])
|
||||
CFG_GCOV_DIR=conftest.gcov.dir
|
||||
rm -rf $CFG_GCOV_DIR
|
||||
AS_MKDIR_P([$CFG_GCOV_DIR])
|
||||
echo 'int main() { return 0; }' > $CFG_GCOV_DIR/conftest_gcov.cpp
|
||||
CFG_GCOV_RES=no
|
||||
if (cd $CFG_GCOV_DIR \
|
||||
&& $CFG_GCOV_CXX --coverage -o conftest_gcov conftest_gcov.cpp \
|
||||
&& ./conftest_gcov) >/dev/null 2>&1; then
|
||||
(cd $CFG_GCOV_DIR && $GCOV conftest_gcov.gcda) >/dev/null 2>&1
|
||||
if test -s $CFG_GCOV_DIR/conftest_gcov.cpp.gcov; then CFG_GCOV_RES=yes; fi
|
||||
else
|
||||
CFG_GCOV_RES=untested
|
||||
fi
|
||||
rm -rf $CFG_GCOV_DIR
|
||||
AC_MSG_RESULT($CFG_GCOV_RES)
|
||||
if test "$CFG_GCOV_RES" = "no"; then
|
||||
AC_MSG_ERROR(['$GCOV' cannot read coverage data written by '$CFG_GCOV_CXX'.
|
||||
Coverage collection would silently produce no data.
|
||||
Pass a matching tool, e.g. GCOV=gcov-<version>
|
||||
or configure without --enable-dev-gcov.])
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(GCOV)
|
||||
AC_SUBST(HAVE_DEV_GCOV)
|
||||
|
||||
# Compiler flags to enable profiling
|
||||
@@ -492,6 +532,11 @@ _MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-std=gnu++20)
|
||||
AC_SUBST(CFG_CXXFLAGS_COROUTINES)
|
||||
AC_SUBST(HAVE_COROUTINES)
|
||||
|
||||
# Check if the C++ compiler supports ThreadSanitizer
|
||||
_MY_CXX_CHECK_IFELSE(-fsanitize=thread,
|
||||
[AC_DEFINE([HAVE_TSAN],[1],[Defined if ThreadSanitizer is supported by $CXX])])
|
||||
AC_SUBST(HAVE_TSAN)
|
||||
|
||||
# Flags for compiling Verilator internals including parser always
|
||||
if test "$CFG_WITH_DEV_ASAN" = "yes"; then
|
||||
_MY_CXX_CHECK_IFELSE(-fsanitize=address -DVL_ASAN,
|
||||
@@ -518,15 +563,27 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PARSER,-Wno-unused)
|
||||
AC_SUBST(CFG_CXXFLAGS_PARSER)
|
||||
|
||||
# Flags for compiling the debug version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
|
||||
if test "$CFG_WITH_DEV_GCOV" = "no"; then # Do not optimize for the coverage build
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-Og)
|
||||
if test "$CFG_WITH_LIGHT_DEBUG" = "yes"; then
|
||||
# Slight optimization and minimal compressed debug info. This is enough for
|
||||
# --gdb/--gdbbt backtraces, but omits the bulk of the debug info, which
|
||||
# significantly reduces object file sizes. For CI or release builds.
|
||||
if test "$CFG_WITH_DEV_GCOV" = "no"; then # Do not optimize for the coverage build
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-Og)
|
||||
fi
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-ggdb1)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-fdebug-info-for-profiling) # For fully-qualified names with clang
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-gz)
|
||||
else
|
||||
# Full debug: no optimization, full debug info, for development.
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-O0)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-ggdb)
|
||||
fi
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-ggdb)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-gz)
|
||||
AC_SUBST(CFG_CXXFLAGS_DBG)
|
||||
|
||||
# Flags for linking the debug version of Verilator (in addition to above CFG_LDFLAGS_SRC)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_DBG,-gz)
|
||||
if test "$CFG_WITH_LIGHT_DEBUG" = "yes"; then
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_DBG,-gz)
|
||||
fi
|
||||
AC_SUBST(CFG_LDFLAGS_DBG)
|
||||
|
||||
# Flags for compiling the optimized version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
|
||||
|
||||
+36
-13
@@ -7,13 +7,16 @@ contribution terms including the AI policy in ``docs/CONTRIBUTING.rst``.
|
||||
|
||||
Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
||||
|
||||
24bit-xjkp
|
||||
404allen404
|
||||
Adam Bagley
|
||||
Adam Kostrzewski
|
||||
Aditya Shevade
|
||||
Adrian Sampson
|
||||
Adrien Le Masle
|
||||
أحمد المحمودي (Ahmed El-Mahmoudy)
|
||||
Aidan McNay
|
||||
Aisha Salimgereyeva
|
||||
Aleksander Kiryk
|
||||
Alex Chadwick
|
||||
Alex Solomatnikov
|
||||
@@ -26,16 +29,20 @@ Andrei Kostovski
|
||||
Andrew Miloradovsky
|
||||
Andrew Nolte
|
||||
Andrew Voznytsa
|
||||
Andrii Andrieiev
|
||||
anonkey
|
||||
Anthony Donlon
|
||||
Anthony Moore
|
||||
apocelipes
|
||||
Arkadiusz Kozdra
|
||||
Arthur Rosa
|
||||
Artur Bieniek
|
||||
AUDIY
|
||||
Aylon Chaim Porat
|
||||
Bartosz Skorowski
|
||||
Bartłomiej Chmiel
|
||||
Benjamin Collier
|
||||
BRDR LIFE
|
||||
Brian Li
|
||||
Cameron Kirk
|
||||
Cameron Waite
|
||||
@@ -53,27 +60,36 @@ Dan Ruelas-Petrisko
|
||||
Daniel Bates
|
||||
Danny Oler
|
||||
Dave Sargeant
|
||||
David Garau
|
||||
David Horton
|
||||
David Ledger
|
||||
David Metz
|
||||
David Stanford
|
||||
David Turner
|
||||
dependabot[bot]
|
||||
Dercury
|
||||
Demin Han
|
||||
Diego Roux
|
||||
Dominick Grochowina
|
||||
Don Williamson
|
||||
Dragon-Git
|
||||
Drew Ranck
|
||||
Drew Taussig
|
||||
Driss Hafdi
|
||||
Edgar E. Iglesias
|
||||
Eddie Rydell
|
||||
em2machine
|
||||
emmettifelts
|
||||
Eric Mejdrich
|
||||
Eric Müller
|
||||
Eric Rippey
|
||||
Eunseo Song
|
||||
Eryk Szpotański
|
||||
Ethan Sifferman
|
||||
Eunseo Song
|
||||
Eyck Jentzsch
|
||||
Fabian Keßler-Schulz
|
||||
Fan Shupei
|
||||
february cozzocrea
|
||||
Felix Neumärker
|
||||
Felix Yan
|
||||
Frans Skarman
|
||||
@@ -108,6 +124,8 @@ Ilya Barkov
|
||||
Iru Cai
|
||||
Ivan Vnučec
|
||||
Iztok Jeras
|
||||
JAEUK LEE
|
||||
Jaeuk Lee (이재욱)
|
||||
Jake Merdich
|
||||
Jakub Michalski
|
||||
Jakub Wasilewski
|
||||
@@ -122,6 +140,8 @@ Jamie Iles
|
||||
Jan Van Winkel
|
||||
Jean Berniolles
|
||||
Jean-Nicolas Strauss
|
||||
Jeffrey Song
|
||||
jeffrey.song
|
||||
Jens Yuechao Liu
|
||||
Jeremy Bennett
|
||||
Jesse Taube
|
||||
@@ -147,6 +167,7 @@ Josep Sans
|
||||
Joseph Nwabueze
|
||||
Josh Redford
|
||||
Joshua Leahy
|
||||
JOTEGO (Jose Tejada)
|
||||
Julian Carrier
|
||||
Julian Daube
|
||||
Julie Schwartz
|
||||
@@ -183,6 +204,7 @@ Maarten De Braekeleer
|
||||
Maciej Sobkowski
|
||||
Marcel Chang
|
||||
Marco Bartoli
|
||||
Marco Brambilla
|
||||
Marco Widmer
|
||||
Mariusz Glebocki
|
||||
Markus Krause
|
||||
@@ -208,6 +230,7 @@ Mladen Slijepcevic
|
||||
Morten Borup Petersen
|
||||
Mostafa Gamal
|
||||
Moubarak Jeje
|
||||
Muzaffer Kal
|
||||
Nandu Raj
|
||||
Natan Kreimer
|
||||
Nathan Graybeal
|
||||
@@ -218,8 +241,11 @@ Nikolai Kumar
|
||||
Nikolay Puzanov
|
||||
Nolan Poe
|
||||
Oleh Maksymenko
|
||||
Patrick Creighton
|
||||
Patrick Stewart
|
||||
Paul Bowen-Huggett
|
||||
Paul Campbell
|
||||
Paul Rigge
|
||||
Paul Swirhun
|
||||
Paul Wright
|
||||
Pawel Jewstafjew
|
||||
@@ -262,15 +288,19 @@ Sergey Chusov
|
||||
Sergey Fedorov
|
||||
Sergi Granell
|
||||
Seth Pellegrino
|
||||
Shashvat Prabhu
|
||||
Shogo Yamazaki
|
||||
Shou-Li Hsu
|
||||
spomatasmd
|
||||
Srinivasan Venkataramanan
|
||||
Stefan Wallentowitz
|
||||
Stephen Henry
|
||||
Steven Hugg
|
||||
Stuart Morris
|
||||
sumpster
|
||||
Szymon Gizler
|
||||
Sören Tempel
|
||||
Sumanth Kadiyala
|
||||
Teng Huang
|
||||
Thomas Aldrian
|
||||
Thomas Brown
|
||||
@@ -291,6 +321,7 @@ Tracy Narine
|
||||
Trung Nguyen
|
||||
Tudor Timi
|
||||
Tymoteusz Blazejczyk
|
||||
Tyrone Marhguy
|
||||
Udaya Raj Subedi
|
||||
Udi Finkelstein
|
||||
Unai Martinez-Corral
|
||||
@@ -308,9 +339,11 @@ Wolfgang Mayerwieser
|
||||
Xi Zhang
|
||||
Yan Xu
|
||||
Yangyu Chen
|
||||
Yilin Li
|
||||
Yilou Wang
|
||||
Yinan Xu
|
||||
Yoda Lee
|
||||
Yogish Sekhar
|
||||
Yossi Nivin
|
||||
Yu-Sheng Lin
|
||||
Yuri Victorovich
|
||||
@@ -321,17 +354,7 @@ Zhen Yan
|
||||
Zhou Shen
|
||||
Zhouyi Shen
|
||||
Zixi Li
|
||||
apocelipes
|
||||
dependabot[bot]
|
||||
february cozzocrea
|
||||
sumpster
|
||||
em2machine
|
||||
emmettifelts
|
||||
Zubin Jain
|
||||
Àlex Torregrosa
|
||||
Ícaro Lima
|
||||
Yogish Sekhar
|
||||
24bit-xjkp
|
||||
Zubin Jain
|
||||
Muzaffer Kal
|
||||
Yilin Li
|
||||
Shashvat Prabhu
|
||||
Sunimali Rathnayake
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
.. code-block::
|
||||
|
||||
-V{t#,#} 'stl' region trigger index 0 is active: @([hybrid] a)
|
||||
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 10000 tries
|
||||
%Error-DIDNOTCONVERGE: Settle region did not converge after '--converge-limit' of 10000 tries
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.. comment: generated by t_lint_didnotconverge_nodbg_bad
|
||||
.. code-block::
|
||||
|
||||
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 10000 tries
|
||||
%Error-DIDNOTCONVERGE: Settle region did not converge after '--converge-limit' of 10000 tries
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
.. comment: generated by t_lint_multidriven_proc_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
|
||||
always @(posedge clk) q <= d;
|
||||
always @(posedge clk) q <= ~d;
|
||||
@@ -0,0 +1,13 @@
|
||||
.. comment: generated by t_lint_multidriven_proc_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
|
||||
integer i;
|
||||
|
||||
always @(posedge clk) begin
|
||||
for (i = 0; i < 4; i = i + 1) q1[i] <= d[i];
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
for (i = 0; i < 4; i = i + 1) q2[i] <= ~d[i];
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
.. comment: generated by t_lint_multidriven_proc_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-MULTIDRIVENPROC: example.v:1:25 Variable written to in always block also written by another always block: 'q'
|
||||
: ... note: In instance 't.u1'
|
||||
example.v:1:25
|
||||
54 | always @(posedge clk) q <= ~d;
|
||||
| ^
|
||||
@@ -0,0 +1,6 @@
|
||||
.. comment: generated by t_lint_similarname_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
|
||||
reg i;
|
||||
wire I;
|
||||
@@ -0,0 +1,9 @@
|
||||
.. comment: generated by t_lint_similarname_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-SIMILARNAME: example.v:1:8 Declaration overlaps another with different case: 'I'
|
||||
13 | wire I;
|
||||
| ^
|
||||
example.v:1:7 ... Location of original declaration
|
||||
12 | reg i;
|
||||
| ^
|
||||
@@ -92,13 +92,17 @@ model.
|
||||
Refer to ``examples/make_tracing_c`` in the distribution for a detailed
|
||||
commented example.
|
||||
|
||||
Top level IO signals are read and written as members of the model. You call
|
||||
the model's ``eval()`` method to evaluate the model. When the simulation is
|
||||
complete call the model's ``final()`` method to execute any SystemVerilog
|
||||
final blocks, and complete any assertions. If using :vlopt:`--timing`,
|
||||
there are two additional functions for checking if there are any events
|
||||
pending in the simulation due to delays, and for retrieving the simulation
|
||||
time of the next delayed event. See :ref:`Evaluation Loop`.
|
||||
Top level IO signals are read and written as members of the model. All
|
||||
inputs must be sanitized, that is have no bits set above those
|
||||
corresponding to the width of the Verilog construct;
|
||||
:vlopt:`--runtime-debug` will assert this is correct.
|
||||
|
||||
Call the model's ``eval()`` method to evaluate the model. When the
|
||||
simulation is complete call the model's ``final()`` method to execute any
|
||||
SystemVerilog final blocks, and complete any assertions. If using
|
||||
:vlopt:`--timing`, there are two additional functions for checking if there
|
||||
are any events pending in the simulation due to delays, and for retrieving
|
||||
the simulation time of the next delayed event. See :ref:`Evaluation Loop`.
|
||||
|
||||
|
||||
Connecting to SystemC
|
||||
@@ -179,7 +183,7 @@ DPI Example
|
||||
In the SYSTEMC example above, if you wanted to import C++ functions into
|
||||
Verilog, put in our.v:
|
||||
|
||||
.. code-block:: sv
|
||||
.. code-block::
|
||||
|
||||
import "DPI-C" function int add (input int a, input int b);
|
||||
|
||||
@@ -211,7 +215,7 @@ Verilator extends the DPI format to allow using the same scheme to
|
||||
efficiently add system functions. Use a dollar-sign prefixed system
|
||||
function name for the import, but note it must be escaped.
|
||||
|
||||
.. code-block:: sv
|
||||
.. code-block::
|
||||
|
||||
import "DPI-C" function integer \$myRand;
|
||||
|
||||
|
||||
@@ -21,6 +21,13 @@ Summary:
|
||||
|
||||
Options:
|
||||
|
||||
.. option:: +verilator+assert+lock
|
||||
|
||||
Only allow command line options to disable / enable assertions.
|
||||
Disables RTL from changing assertion handling via ``$asserton``,
|
||||
``$assertoff``, and ``$assertcontrol``. Also prevents ``VerilatedContext*``
|
||||
assertion control functions from updating assertion handling.
|
||||
|
||||
.. option:: +verilator+coverage+file+<filename>
|
||||
|
||||
When a model was Verilated using :vlopt:`--coverage`, sets the filename
|
||||
@@ -114,7 +121,7 @@ Options:
|
||||
When a model was Verilated using :vlopt:`--x-initial unique
|
||||
<--x-initial>`, sets the simulation runtime initialization technique. 0
|
||||
= Reset to zeros. 1 = Reset to all-ones. 2 = Randomize. See
|
||||
:ref:`Unknown States`.
|
||||
:ref:`Unknown States`. Default is 0.
|
||||
|
||||
.. option:: +verilator+seed+<value>
|
||||
|
||||
|
||||
@@ -116,6 +116,10 @@ Summary:
|
||||
|
||||
.. option:: --assert-unroll-limit <iterations>
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions before 5.052:
|
||||
|
||||
Rarely needed. Specifies the maximum repetition or range count Verilator
|
||||
will unroll inside an SVA concurrent assertion (e.g. ``[*N]``, ``[->M:N]``,
|
||||
``always[lo:hi]``). Beyond this, the assertion is rejected with an error
|
||||
@@ -715,6 +719,10 @@ Summary:
|
||||
|
||||
.. option:: -fno-dfg-break-cycles
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions before 5.052:
|
||||
|
||||
Rarely needed. Disable breaking combinational cycles during DFG.
|
||||
|
||||
.. option:: -fno-dfg-peephole
|
||||
@@ -936,7 +944,7 @@ Summary:
|
||||
:file:`*.mk` files.
|
||||
|
||||
Feature may be one of the following: COROUTINES, DEV_ASAN, DEV_GCOV,
|
||||
SYSTEMC.
|
||||
SYSTEMC, TSAN.
|
||||
|
||||
.. option:: --getenv <variable>
|
||||
|
||||
@@ -1219,6 +1227,8 @@ Summary:
|
||||
.. option:: --max-num-width <value>
|
||||
|
||||
Set the maximum number literal width (e.g., in 1024'd22 the 1024).
|
||||
This also limits SVA cycle delay and temporal bound values.
|
||||
|
||||
Defaults to 64K.
|
||||
|
||||
.. option:: --Mdir <directory>
|
||||
|
||||
@@ -133,7 +133,7 @@ verilator_coverage Arguments
|
||||
|
||||
.. option:: --filter-type <regex>
|
||||
|
||||
Skips records of coverage types that matches with <regex>
|
||||
Keeps records of coverage types that matches with <regex>
|
||||
Possible values are `toggle`, `line`, `branch`, `expr`, `covergroup`,
|
||||
`user`, `fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The
|
||||
default value is `\*`.
|
||||
|
||||
@@ -294,6 +294,25 @@ or "`ifdef`"'s may break other tools.
|
||||
(if appropriate :vlopt:`--coverage` flags are passed) after being
|
||||
disabled earlier with :option:`/*verilator&32;coverage_off*/`.
|
||||
|
||||
.. option:: /*verilator&32;dpi_c_decl "<C function declaration>"*/
|
||||
|
||||
Specifies C function declaration that will be emitted for given DPI-C
|
||||
function into the Verilator-generated __Dpi.h header, replacing the declaration
|
||||
that Verilator would build by default using the Verilog function signature.
|
||||
|
||||
This enables use of C functions with types not specified in the
|
||||
standard. For example, it enables use of functions that return ``char*``:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
module t;
|
||||
import "DPI-C" function string getenv(input string arg) /*verilator dpi_c_decl "char* getenv(const char*)"*/;
|
||||
|
||||
initial begin
|
||||
$display("%s", getenv("HOME"));
|
||||
end
|
||||
endmodule
|
||||
|
||||
.. option:: /*verilator&32;fargs <arguments>*/
|
||||
|
||||
For Verilator developers only. When a source file containing these `fargs`
|
||||
|
||||
@@ -66,7 +66,7 @@ The information in this report is:
|
||||
|
||||
.. describe:: "allocated 123 MB"
|
||||
|
||||
Total memory used during simulation in megabytes.
|
||||
Peak resident memory used during simulation in megabytes.
|
||||
|
||||
|
||||
.. _benchmarking & optimization:
|
||||
|
||||
@@ -623,5 +623,5 @@ The information in this report is:
|
||||
|
||||
.. describe:: "allocated 123 MB"
|
||||
|
||||
Total memory used during build by Verilator executable (excludes
|
||||
:vlopt:`--build` compiler's usage) in megabytes.
|
||||
Peak resident memory used by the Verilator executable during build
|
||||
(excludes :vlopt:`--build` compiler's usage) in megabytes.
|
||||
|
||||
@@ -1469,11 +1469,54 @@ List Of Warnings
|
||||
q <= d;
|
||||
end
|
||||
|
||||
A further case is when a signal named as a clocking block ``output`` is
|
||||
also driven by a continuous assignment, or is named as an ``output`` of a
|
||||
second clocking block. The clocking block drives the signal, so the design
|
||||
and the testbench contend for it and the synchronous drive may be silently
|
||||
lost. Declare the clocking block ``input`` if the intent is only to
|
||||
observe the signal.
|
||||
|
||||
Ignoring this warning may hide clock domain crossing, timing, or
|
||||
portability bugs. It may also cause longer simulation runtimes due to
|
||||
reduced optimizations.
|
||||
|
||||
|
||||
.. option:: MULTIDRIVENPROC
|
||||
|
||||
Warns that the whole of a variable is driven by more than one plain
|
||||
``always`` block. Unlike the :option:`MULTIDRIVEN` cases, plain
|
||||
``always`` blocks carry no ``always_comb``/``always_ff`` intent, so this
|
||||
is legal SystemVerilog rather than an IEEE 1800 violation. It is,
|
||||
however, typically a synthesis error: hardware cannot have a signal
|
||||
driven by two separate sequential blocks, so the design usually will not
|
||||
behave as the RTL simulation suggests.
|
||||
|
||||
Disabled by default as this is a code-style warning; it will simulate
|
||||
correctly.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. include:: ../../docs/gen/ex_MULTIDRIVENPROC_faulty.rst
|
||||
|
||||
Results in:
|
||||
|
||||
.. include:: ../../docs/gen/ex_MULTIDRIVENPROC_msg.rst
|
||||
|
||||
Also warns when a signal named as a clocking block ``output`` is driven
|
||||
by a plain ``always`` block. Driving a signal from both a clocking block
|
||||
and a plain ``always`` block is a deliberate idiom in some testbenches,
|
||||
so it is reported as MULTIDRIVENPROC rather than under the on-by-default
|
||||
:option:`MULTIDRIVEN`.
|
||||
|
||||
To fix, drive the signal from a single ``always`` block, or use
|
||||
``always_ff``/``always_comb`` if the intent is a single specialized
|
||||
process.
|
||||
|
||||
Does not warn for static variables used as loop induction variables:
|
||||
|
||||
.. include:: ../../docs/gen/ex_MULTIDRIVENPROC_loopidx.rst
|
||||
|
||||
|
||||
.. option:: MULTITOP
|
||||
|
||||
.. TODO better example
|
||||
@@ -1517,6 +1560,19 @@ List Of Warnings
|
||||
(neither :vlopt:`--timing` nor :vlopt:`--no-timing` option was provided).
|
||||
|
||||
|
||||
.. option:: NEVERMATCH
|
||||
|
||||
Warns that an SVA sequence is statically known to never match. Such a
|
||||
sequence is legal SystemVerilog and is fully supported, the warning
|
||||
only highlights a potential problem. For example, using the sequence as
|
||||
the antecedent of an implication causes the implication to pass
|
||||
vacuously. For an intersect sequence, ensure its operands can match
|
||||
over a common length (IEEE 1800-2023 16.9.6).
|
||||
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
|
||||
|
||||
.. option:: NEWERSTD
|
||||
|
||||
Warns that a feature requires a newer standard of Verilog or SystemVerilog
|
||||
@@ -2089,6 +2145,32 @@ List Of Warnings
|
||||
simulators.
|
||||
|
||||
|
||||
.. option:: SIMILARNAME
|
||||
|
||||
Warns that an entity name only differs from another in lexical case.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. include:: ../../docs/gen/ex_SIMILARNAME_faulty.rst
|
||||
|
||||
Results in:
|
||||
|
||||
.. include:: ../../docs/gen/ex_SIMILARNAME_msg.rst
|
||||
|
||||
Only declarations that can reach a downstream VLSI tool as a name are
|
||||
checked, that is nets, variables, instances, named blocks (``begin``,
|
||||
``fork``, and generate blocks), functions and tasks. All of these can form
|
||||
part of a flattened signal or scope name. Other declarations, such as
|
||||
parameters, localparams, genvars and typedefs, are elaborated away, and so
|
||||
are not checked.
|
||||
|
||||
Disabled by default as this is a code-style warning; it will simulate
|
||||
correctly.
|
||||
|
||||
This is a warning as some downstream VLSI tools do
|
||||
not distinguish net and gate names with the same case.
|
||||
|
||||
|
||||
.. option:: SPECIFYIGN
|
||||
|
||||
Warns that Verilator does not support certain constructs in
|
||||
|
||||
+87
-71
@@ -425,8 +425,9 @@ logic.
|
||||
|
||||
To achieve this, we invoke ``V3Order::order`` on all of the combinational
|
||||
and hybrid logic, and iterate the resulting evaluation function until no
|
||||
more hybrid logic is triggered. This yields the `_eval_settle` function,
|
||||
which is invoked at the beginning of simulation after the `_eval_initial`.
|
||||
more hybrid logic is triggered. This yields the `_eval_stl` function, which
|
||||
the runtime event loop iterates at the beginning of simulation, after the
|
||||
`_eval_initial`.
|
||||
|
||||
|
||||
Partitioning logic for correct NBA updates
|
||||
@@ -500,51 +501,66 @@ and clock signals on separate evaluations, as was necessary with earlier
|
||||
versions of Verilator).
|
||||
|
||||
|
||||
Constructing the top level `_eval` function
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Constructing the region evaluation functions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To construct the top level `_eval` function, which updates the state of the
|
||||
circuit to the end of the current time step, we invoke ``V3Order::order``
|
||||
separately on the 'ico', 'act' and 'nba' logic, which yields the
|
||||
`_eval_ico`, `_eval_act`, and `_eval_nba` functions. We then put these all
|
||||
together with the corresponding functions that compute the respective
|
||||
trigger expressions into the top level `_eval` function, which on the high
|
||||
level has the form:
|
||||
To update the state of the circuit to the end of the current time step, we
|
||||
invoke ``V3Order::order`` separately on the 'ico', 'act' and 'nba' logic,
|
||||
which yields the `_eval_body__ico`, `_eval_body__act`, and
|
||||
`_eval_body__nba` functions. Each of these is then combined with the
|
||||
function computing the respective trigger expressions into a single entry
|
||||
point per region, `_eval_ico`, `_eval_act` and `_eval_nba`. A region entry
|
||||
point evaluates one iteration of its region, and returns whether it did any
|
||||
work, meaning the region has not converged yet and must be evaluated again.
|
||||
On the high level, `_eval_act` has the form:
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
void _eval() {
|
||||
// Update combinational logic dependent on top level inputs ('ico' region)
|
||||
while (true) {
|
||||
_eval__triggers__ico();
|
||||
// If no 'ico' region trigger is active
|
||||
if (!ico_triggers.any()) break;
|
||||
_eval_ico();
|
||||
}
|
||||
|
||||
// Iterate 'act' and 'nba' regions together
|
||||
while (true) {
|
||||
|
||||
// Iterate 'act' region, this computes all derived clocks updaed in the
|
||||
// Active scheduling region, but does not commit any NBAs that executed
|
||||
// in 'act' region logic.
|
||||
while (true) {
|
||||
_eval__triggers__act();
|
||||
// If no 'act' region trigger is active
|
||||
if (!act_triggers.any()) break;
|
||||
// Remember what 'act' triggers were active, 'nba' uses the same
|
||||
latch_act_triggers_for_nba();
|
||||
_eval_act();
|
||||
}
|
||||
|
||||
// If no 'nba' region trigger is active
|
||||
if (!nba_triggers.any()) break;
|
||||
|
||||
// Evaluate all other Active region logic, and commit NBAs
|
||||
_eval_nba();
|
||||
}
|
||||
bool _eval_act() {
|
||||
_eval_triggers_vec__act();
|
||||
// Remember what 'act' triggers were active, 'nba' uses the same
|
||||
latch_act_triggers_for_nba();
|
||||
const bool execute = act_triggers.any();
|
||||
// Compute all derived clocks updated in the Active scheduling region,
|
||||
// but do not commit any NBAs that executed in 'act' region logic.
|
||||
if (execute) _eval_body__act();
|
||||
return execute;
|
||||
}
|
||||
|
||||
The loops iterating these regions are not generated. They live in the
|
||||
runtime library, in ``VerilatedEvalLoop``, and the model exposes its region
|
||||
entry points to it as virtual methods of ``VerilatedModel``. The generated
|
||||
model holds a ``VerilatedEvalLoop`` as a member, which its ``eval_step``
|
||||
runs, and which on the high level has the form:
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
void VerilatedEvalLoop::evalImpl() {
|
||||
// Update combinational logic dependent on top level inputs ('ico' region)
|
||||
uint32_t icoIterCount = 0;
|
||||
do {
|
||||
if (++icoIterCount > m_convergeLimit) didNotConverge("Input combinational");
|
||||
} while (m_model.evalIco(icoIterCount == 1));
|
||||
|
||||
// Iterate 'act' and 'nba' regions together
|
||||
uint32_t nbaIterCount = 0;
|
||||
do {
|
||||
if (++nbaIterCount > m_convergeLimit) didNotConverge("NBA");
|
||||
// Iterate the 'act' region to convergence
|
||||
uint32_t actIterCount = 0;
|
||||
do {
|
||||
if (++actIterCount > m_convergeLimit) didNotConverge("Active");
|
||||
} while (m_model.evalAct());
|
||||
// Evaluate all other Active region logic, and commit NBAs
|
||||
} while (m_model.evalNba());
|
||||
}
|
||||
|
||||
The remaining regions ('inact', 'obs' and 'react') nest around these in the
|
||||
same manner, each iterating the loops of all regions that precede it in the
|
||||
SystemVerilog scheduling order. The runtime event loop calls every region
|
||||
unconditionally, so scheduling emits an entry point for each one even when
|
||||
the design has no logic in it. Such an entry point immediately returns
|
||||
false, ending its loop after a single iteration.
|
||||
|
||||
Timing
|
||||
------
|
||||
@@ -730,7 +746,8 @@ they wouldn't be evaluated and next coroutine after resumption would fire
|
||||
the event `a` then it is impossible to get to know whether await or fire on
|
||||
event `a` was called first - which is necessary to know.
|
||||
|
||||
There are two functions for managing timing logic called by ``_eval()``:
|
||||
There are two functions for managing timing logic called by the 'act'
|
||||
region:
|
||||
|
||||
- ``_timing_ready()``, which commits all coroutines whose triggers were not
|
||||
set in the current iteration,
|
||||
@@ -751,36 +768,27 @@ Thanks to this separation a coroutine:
|
||||
(``test_regress/t/t_event_control_double_lost.v``) - which is possible
|
||||
when triggers are not evaluated right before awaiting.
|
||||
|
||||
All coroutines are committed and resumed in the 'act' eval loop. With
|
||||
timing features enabled, the ``_eval()`` function takes this form:
|
||||
All coroutines are committed and resumed in the 'act' region. With timing
|
||||
features enabled, the 'act' region entry point takes this form:
|
||||
|
||||
::
|
||||
|
||||
void _eval() {
|
||||
while (true) {
|
||||
_eval__triggers__ico();
|
||||
if (!ico_triggers.any()) break;
|
||||
_eval_ico();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
while (true) {
|
||||
_eval__triggers__act();
|
||||
|
||||
// Commit all non-triggered coroutines
|
||||
_timing_commit();
|
||||
|
||||
if (!act_triggers.any()) break;
|
||||
latch_act_triggers_for_nba();
|
||||
|
||||
// Resume all triggered coroutines
|
||||
_timing_resume();
|
||||
|
||||
_eval_act();
|
||||
}
|
||||
if (!nba_triggers.any()) break;
|
||||
_eval_nba();
|
||||
bool _eval_act() {
|
||||
_eval_triggers_vec__act();
|
||||
|
||||
// Commit all non-triggered coroutines
|
||||
_timing_commit();
|
||||
|
||||
const bool execute = act_triggers.any();
|
||||
if (execute) {
|
||||
latch_act_triggers_for_nba();
|
||||
|
||||
// Resume all triggered coroutines
|
||||
_timing_resume();
|
||||
|
||||
_eval_body__act();
|
||||
}
|
||||
return execute;
|
||||
}
|
||||
|
||||
Forks
|
||||
@@ -1082,9 +1090,17 @@ solver gets a setup query, then the definition of variables, then all the
|
||||
constraints (SMT assertions) about the variables. Since the solver has no
|
||||
information about the class' PRNG state, if the problem is satisfiable, the
|
||||
solution space is further constrained by adding extra random constraints,
|
||||
and querying the values satisfying the problem statement. The constraint is
|
||||
currently constructed as fixing a simple xor of randomly chosen bits of the
|
||||
variables being randomized.
|
||||
and querying the values satisfying the problem statement. The constraints
|
||||
are currently built by the UniGen2 algorithm ("On Parallel Scalable Uniform
|
||||
SAT Witness Generation", Chakraborty et al.). It first works out how many
|
||||
random xor equations are needed to split the solution space into cells of a
|
||||
size worth enumerating, then draws one cell and enumerates it, which gives
|
||||
a near-uniform sample of the whole space. Both the estimate and the
|
||||
leftover solutions are cached, so the following calls are answered without
|
||||
asking the solver again. Cases outside its assumptions, such as ``randc``
|
||||
variables or soft constraints, fall back to pinning every free bit to a
|
||||
random value, or, when arrays are involved, to asserting four random xor
|
||||
equations in a row.
|
||||
|
||||
The runtime classes used for handling the randomization are defined in
|
||||
``verilated_random.h`` and ``verilated_random.cpp``.
|
||||
|
||||
@@ -32,6 +32,7 @@ Bagri
|
||||
Balboni
|
||||
Baltazar
|
||||
Bardsley
|
||||
Barua
|
||||
Benchmarking
|
||||
Berman
|
||||
Bhagavatula
|
||||
@@ -198,6 +199,7 @@ Holme
|
||||
Homebrew
|
||||
Hornung
|
||||
Hossell
|
||||
Hsieh
|
||||
Hsu
|
||||
Huggett
|
||||
Hutt
|
||||
@@ -389,6 +391,7 @@ Preprocess
|
||||
Pretet
|
||||
Pretl
|
||||
Pringlemeir
|
||||
Prithayan
|
||||
Priyadharshini
|
||||
Pullup
|
||||
Pulver
|
||||
@@ -477,6 +480,7 @@ Syms
|
||||
Synopsys
|
||||
SystemC
|
||||
SystemVerilog
|
||||
Szpotanski
|
||||
Takatsukasa
|
||||
Tambe
|
||||
Tarik
|
||||
@@ -512,6 +516,7 @@ Ueno
|
||||
Uints
|
||||
Undefine
|
||||
Undefines
|
||||
Unigen
|
||||
Unsized
|
||||
Uppal
|
||||
Urbach
|
||||
@@ -537,6 +542,7 @@ Verilog
|
||||
Vighnesh
|
||||
Viktor
|
||||
Vilp
|
||||
VlRNG
|
||||
VlWide
|
||||
Vlip
|
||||
Vm
|
||||
@@ -644,6 +650,7 @@ casez
|
||||
casted
|
||||
castro
|
||||
cb
|
||||
cbValueChange
|
||||
ccache
|
||||
ccall
|
||||
cdc
|
||||
@@ -707,6 +714,7 @@ de
|
||||
dearray
|
||||
deassign
|
||||
debugi
|
||||
deduplication
|
||||
defenv
|
||||
defname
|
||||
defparam
|
||||
@@ -947,6 +955,7 @@ misconnected
|
||||
misconversion
|
||||
misdetecting
|
||||
misoptimized
|
||||
misoptimizing
|
||||
missized
|
||||
mk
|
||||
mno
|
||||
@@ -995,6 +1004,7 @@ noprivate
|
||||
noreturn
|
||||
notif
|
||||
nullptr
|
||||
num
|
||||
numactl
|
||||
onehot
|
||||
ooo
|
||||
@@ -1174,6 +1184,7 @@ tcmalloc
|
||||
tcmalloc
|
||||
tenghtt
|
||||
testbench
|
||||
testbenches
|
||||
threadsafe
|
||||
threashold
|
||||
timeInc
|
||||
|
||||
@@ -90,7 +90,7 @@ static inline constexpr unsigned bitPerEncodedBit(EncodingType type) {
|
||||
[[maybe_unused]]
|
||||
static const char* kEncodedBitToCharTable = (
|
||||
"01" // Binary
|
||||
"xzhu" // Verilog
|
||||
"zxhu" // Verilog
|
||||
"wl-? " // Vhdl (padded with ' ')
|
||||
);
|
||||
|
||||
|
||||
@@ -423,18 +423,26 @@ public:
|
||||
|
||||
void emitValueChange(uint64_t current_time_index, const uint32_t *val, EncodingType encoding) {
|
||||
auto wh = emitValueChangeCommonPart(current_time_index, encoding);
|
||||
for (unsigned i = 0; i < bitPerEncodedBit(encoding); ++i) {
|
||||
// C++17: replace this with if constexpr
|
||||
if (sizeof(T) == 8) {
|
||||
// C++17: replace this with if constexpr
|
||||
if (sizeof(T) == 8) {
|
||||
if (encoding == EncodingType::VERILOG) {
|
||||
uint64_t v = val[2]; // high bits
|
||||
v <<= 32;
|
||||
v |= val[0]; // low bits
|
||||
wh.template write<uint64_t>(v);
|
||||
v = val[3]; // high bits
|
||||
v <<= 32;
|
||||
v |= val[1]; // low bits
|
||||
wh.template write<uint64_t>(v);
|
||||
} else {
|
||||
uint64_t v = val[1]; // high bits
|
||||
v <<= 32;
|
||||
v |= val[0]; // low bits
|
||||
wh.template write<uint64_t>(v);
|
||||
val += 2;
|
||||
} else {
|
||||
wh.template write<T>(val[0]);
|
||||
val += 1;
|
||||
}
|
||||
} else {
|
||||
wh.template write<T>(val[0]);
|
||||
if (encoding == EncodingType::VERILOG) wh.template write<T>(val[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,7 +525,7 @@ public:
|
||||
} else {
|
||||
unsigned val = 0;
|
||||
for (unsigned i = 0; i < num_element; ++i) {
|
||||
val |= rh.peek<T>(i);
|
||||
val |= rh.peek<T>(i) << i;
|
||||
}
|
||||
uint64_t delta_time_index = time_index - prev_time_index;
|
||||
prev_time_index = time_index;
|
||||
@@ -525,8 +533,8 @@ public:
|
||||
// clang-format off
|
||||
case 0: delta_time_index = (delta_time_index<<2) | (0<<1) | 0; break; // '0'
|
||||
case 1: delta_time_index = (delta_time_index<<2) | (1<<1) | 0; break; // '1'
|
||||
case 2: delta_time_index = (delta_time_index<<4) | (0<<1) | 1; break; // 'X'
|
||||
case 3: delta_time_index = (delta_time_index<<4) | (1<<1) | 1; break; // 'Z'
|
||||
case 2: delta_time_index = (delta_time_index<<4) | (1<<1) | 1; break; // 'Z'
|
||||
case 3: delta_time_index = (delta_time_index<<4) | (0<<1) | 1; break; // 'X'
|
||||
// Not supporting VHDL now
|
||||
// LCOV_EXCL_START
|
||||
case 4: delta_time_index = (delta_time_index<<4) | (2<<1) | 1; break; // 'H'
|
||||
@@ -556,13 +564,29 @@ public:
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
FST_CHECK(enc == EncodingType::BINARY); // TODO
|
||||
const bool has_non_binary = enc != EncodingType::BINARY;
|
||||
const uint64_t delta_time_index = time_index - prev_time_index;
|
||||
prev_time_index = time_index;
|
||||
h //
|
||||
.writeLEB128((delta_time_index << 1) | has_non_binary)
|
||||
.writeUIntPartialForValueChange(rh.peek<T>(), bitwidth);
|
||||
switch (enc) {
|
||||
case EncodingType::BINARY: {
|
||||
h //
|
||||
.writeLEB128(delta_time_index << 1)
|
||||
.writeUIntPartialForValueChange(rh.peek<T>(), bitwidth);
|
||||
} break;
|
||||
case EncodingType::VERILOG: {
|
||||
h.writeLEB128((delta_time_index << 1) | 1);
|
||||
const T val = rh.peek<T>();
|
||||
const T xz = rh.peek<T>(1);
|
||||
for (int j = bitwidth; j > 0;) {
|
||||
--j;
|
||||
h.writeUIntBE(
|
||||
kEncodedBitToCharTable[(((xz >> j) << 1) & 2) | ((val >> j) & 1)]
|
||||
);
|
||||
}
|
||||
} break;
|
||||
[[unlikely]] case EncodingType::VHDL: {
|
||||
FST_FAIL_STRING("VHDL format is unsupported with wide values");
|
||||
} break;
|
||||
}
|
||||
}
|
||||
rh.skip(num_byte);
|
||||
}
|
||||
@@ -573,16 +597,26 @@ public:
|
||||
class VariableInfoLongInt {
|
||||
VariableInfo &info;
|
||||
unsigned num_words() const { return (info.bitwidth() + 63) / 64; }
|
||||
unsigned num_words32() const { return (info.bitwidth() + 31) / 32; }
|
||||
|
||||
public:
|
||||
VariableInfoLongInt(VariableInfo &info_) : info(info_) {}
|
||||
|
||||
public:
|
||||
size_t computeBytesNeededNoHeader(EncodingType encoding) const {
|
||||
switch (encoding) {
|
||||
case EncodingType::BINARY:
|
||||
return num_words() * sizeof(uint64_t);
|
||||
case EncodingType::VERILOG:
|
||||
return num_words32() * sizeof(uint32_t) * 2;
|
||||
[[unlikely]] case EncodingType::VHDL:
|
||||
FST_FAIL_STRING("VHDL format is unsupported with wide values");
|
||||
}
|
||||
FST_UNREACHABLE;
|
||||
}
|
||||
|
||||
size_t computeBytesNeeded(EncodingType encoding) const {
|
||||
return (
|
||||
kEmitTimeIndexAndEncodingSize +
|
||||
num_words() * sizeof(uint64_t) * bitPerEncodedBit(encoding)
|
||||
);
|
||||
return kEmitTimeIndexAndEncodingSize + computeBytesNeededNoHeader(encoding);
|
||||
}
|
||||
|
||||
EmitWriterHelper emitValueChangeCommonPart(uint64_t current_time_index, EncodingType encoding) {
|
||||
@@ -600,13 +634,12 @@ public:
|
||||
|
||||
public:
|
||||
void construct() {
|
||||
const size_t nw = num_words();
|
||||
const size_t nw = num_words32();
|
||||
info.resize(computeBytesNeeded(EncodingType::VERILOG));
|
||||
EmitWriterHelper wh(info.data_ptr());
|
||||
wh //
|
||||
.writeTimeIndexAndEncoding(0, EncodingType::VERILOG)
|
||||
.fill(uint64_t(0), nw)
|
||||
.fill(uint64_t(-1), nw);
|
||||
.fill(static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) << 32, nw);
|
||||
}
|
||||
|
||||
void emitValueChange(uint64_t current_time_index, const uint64_t val) {
|
||||
@@ -616,12 +649,12 @@ public:
|
||||
}
|
||||
|
||||
void emitValueChange(uint64_t current_time_index, const uint32_t *val, EncodingType encoding) {
|
||||
const unsigned nw32 = (info.bitwidth() + 31) / 32;
|
||||
const unsigned bpb = bitPerEncodedBit(encoding);
|
||||
const unsigned nw32 = num_words32();
|
||||
|
||||
auto wh = emitValueChangeCommonPart(current_time_index, encoding);
|
||||
|
||||
for (unsigned i = 0; i < bpb; ++i) {
|
||||
switch (encoding) {
|
||||
case EncodingType::BINARY: {
|
||||
for (unsigned j = 0; j < nw32 / 2; ++j) {
|
||||
uint64_t v = val[1]; // high bits
|
||||
v <<= 32;
|
||||
@@ -634,13 +667,25 @@ public:
|
||||
wh.write(v);
|
||||
val += 1;
|
||||
}
|
||||
} break;
|
||||
case EncodingType::VERILOG: {
|
||||
for (unsigned j = 0; j < nw32; ++j) {
|
||||
uint64_t v = val[1]; // high bits
|
||||
v <<= 32;
|
||||
v |= val[0]; // low bits
|
||||
wh.write(v);
|
||||
val += 2;
|
||||
}
|
||||
} break;
|
||||
[[unlikely]] case EncodingType::VHDL:
|
||||
FST_FAIL_STRING("VHDL format is unsupported with wide values");
|
||||
}
|
||||
}
|
||||
|
||||
void emitValueChange(uint64_t current_time_index, const uint64_t *val, EncodingType encoding) {
|
||||
const unsigned nw_encoded = num_words() * bitPerEncodedBit(encoding);
|
||||
auto wh = emitValueChangeCommonPart(current_time_index, encoding);
|
||||
wh.write(val, nw_encoded);
|
||||
FST_CHECK(encoding == EncodingType::BINARY);
|
||||
wh.write(val, num_words());
|
||||
}
|
||||
|
||||
void dumpInitialBits(std::vector<uint8_t> &buf) const {
|
||||
@@ -663,15 +708,16 @@ public:
|
||||
break;
|
||||
}
|
||||
case EncodingType::VERILOG: {
|
||||
for (unsigned word_index = nw; word_index-- > 0;) {
|
||||
const uint64_t v0 = rh.peek<uint64_t>(nw * 0 + word_index);
|
||||
const uint64_t v1 = rh.peek<uint64_t>(nw * 1 + word_index);
|
||||
for (unsigned word_index = num_words32(); word_index-- > 0;) {
|
||||
const uint64_t val = rh.peek<uint64_t>(word_index);
|
||||
const uint32_t aval = static_cast<uint32_t>(val);
|
||||
const uint32_t bval = static_cast<uint32_t>(val >> 32);
|
||||
const unsigned num_bit =
|
||||
(word_index * 64 + 64 > info.bitwidth()) ? (info.bitwidth() % 64) : 64;
|
||||
(word_index * 32 + 32 > info.bitwidth()) ? (info.bitwidth() % 32) : 32;
|
||||
for (unsigned bit_index = num_bit; bit_index-- > 0;) {
|
||||
const bool b0 = ((v0 >> bit_index) & uint64_t(1));
|
||||
const bool b1 = ((v1 >> bit_index) & uint64_t(1));
|
||||
const char c = kEncodedBitToCharTable[(b1 << 1) | b0];
|
||||
const bool a = ((aval >> bit_index) & 1);
|
||||
const bool b = ((bval >> bit_index) & 1);
|
||||
const char c = kEncodedBitToCharTable[(b << 1) | a];
|
||||
buf.push_back(c);
|
||||
}
|
||||
}
|
||||
@@ -717,33 +763,58 @@ public:
|
||||
FST_DCHECK_GT(tail, rh.ptr);
|
||||
const auto time_index = rh.read<uint64_t>();
|
||||
const auto enc = rh.read<EncodingType>();
|
||||
const auto num_element = bitPerEncodedBit(enc);
|
||||
const auto num_byte = num_element * nw * sizeof(uint64_t);
|
||||
const auto num_byte = computeBytesNeededNoHeader(enc);
|
||||
if (first) {
|
||||
// Note: [0] is initial value, which is already dumped in dumpInitialBits()
|
||||
first = false;
|
||||
} else {
|
||||
FST_CHECK(enc == EncodingType::BINARY); // TODO
|
||||
const bool has_non_binary = enc != EncodingType::BINARY;
|
||||
const uint64_t delta_time_index = time_index - prev_time_index;
|
||||
prev_time_index = time_index;
|
||||
h.writeLEB128((delta_time_index << 1) | has_non_binary);
|
||||
if (bitwidth % 64 != 0) {
|
||||
const unsigned remaining = bitwidth % 64;
|
||||
uint64_t hi64 = rh.peek<uint64_t>(nw - 1);
|
||||
// write from nw-1 to 1
|
||||
for (unsigned j = nw - 1; j > 0; --j) {
|
||||
uint64_t lo64 = rh.peek<uint64_t>(j - 1);
|
||||
h.writeUIntBE((hi64 << (64 - remaining)) | (lo64 >> remaining));
|
||||
hi64 = lo64;
|
||||
switch (enc) {
|
||||
case EncodingType::BINARY: {
|
||||
h.writeLEB128((delta_time_index << 1));
|
||||
if (bitwidth % 64 != 0) {
|
||||
const unsigned remaining = bitwidth % 64;
|
||||
uint64_t hi64 = rh.peek<uint64_t>(nw - 1);
|
||||
// write from nw-1 to 1
|
||||
for (unsigned j = nw - 1; j > 0; --j) {
|
||||
uint64_t lo64 = rh.peek<uint64_t>(j - 1);
|
||||
h.writeUIntBE((hi64 << (64 - remaining)) | (lo64 >> remaining));
|
||||
hi64 = lo64;
|
||||
}
|
||||
// write 0
|
||||
h.writeUIntPartialForValueChange(hi64, remaining);
|
||||
} else {
|
||||
// write from nw-1 to 0
|
||||
for (unsigned j = nw; j-- > 0;) {
|
||||
h.writeUIntBE(rh.peek<uint64_t>(j));
|
||||
}
|
||||
}
|
||||
// write 0
|
||||
h.writeUIntPartialForValueChange(hi64, remaining);
|
||||
} else {
|
||||
// write from nw-1 to 0
|
||||
for (unsigned j = nw; j-- > 0;) {
|
||||
h.writeUIntBE(rh.peek<uint64_t>(j));
|
||||
} break;
|
||||
case EncodingType::VERILOG: {
|
||||
h.writeLEB128((delta_time_index << 1) | 1);
|
||||
const int fullWords = (bitwidth / 32);
|
||||
if (int j = bitwidth % 32) {
|
||||
const uint64_t val = rh.peek<uint64_t>(fullWords);
|
||||
while (j > 0) {
|
||||
--j;
|
||||
const uint64_t v = val >> j;
|
||||
h.writeUIntBE(kEncodedBitToCharTable[((v >> 31) & 2) | (v & 1)]);
|
||||
}
|
||||
}
|
||||
for (size_t i = fullWords; i > 0;) {
|
||||
--i;
|
||||
const uint64_t val = rh.peek<uint64_t>(i);
|
||||
for (int j = 32; j > 0;) {
|
||||
--j;
|
||||
const uint64_t v = val >> j;
|
||||
h.writeUIntBE(kEncodedBitToCharTable[((v >> 31) & 2) | (v & 1)]);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
[[unlikely]] case EncodingType::VHDL: {
|
||||
FST_FAIL_STRING("VHDL format is unsupported with wide values");
|
||||
} break;
|
||||
}
|
||||
}
|
||||
rh.skip(num_byte);
|
||||
|
||||
@@ -268,26 +268,55 @@ void Writer::emitValueChange(Handle handle, const char *val) {
|
||||
|
||||
// For normal integer handles, const char* is "01xz..." (1B per bit)
|
||||
const uint32_t bitwidth{var_info.bitwidth()};
|
||||
const bool hasXZ = // Detects A-Z and a-z but not 0-9 and NOT `-` `?`
|
||||
(std::accumulate(val, val + bitwidth, 0, [](int a, char b) { return a | b; }) & (1 << 6)) !=
|
||||
0;
|
||||
FST_DCHECK_NE(bitwidth, 0);
|
||||
|
||||
val += bitwidth;
|
||||
const unsigned num_words{(bitwidth + 63) / 64};
|
||||
m_packed_value_buffer_.assign(num_words, 0);
|
||||
m_packed_value_buffer_.assign(num_words << (hasXZ ? 1 : 0), 0);
|
||||
for (unsigned i = 0; i < num_words; ++i) {
|
||||
const char *start{val - std::min((i + 1) * 64, bitwidth)};
|
||||
const char *end{val - 64 * i};
|
||||
m_packed_value_buffer_[i] = 0;
|
||||
for (const char *p = start; p < end; ++p) {
|
||||
// No checking for invalid characters, follow original C implementation
|
||||
m_packed_value_buffer_[i] <<= 1;
|
||||
m_packed_value_buffer_[i] |= static_cast<uint64_t>(*p - '0');
|
||||
if (hasXZ) {
|
||||
const size_t j = i << 1;
|
||||
m_packed_value_buffer_[j] <<= 1;
|
||||
m_packed_value_buffer_[j | 1] <<= 1;
|
||||
switch (*p) {
|
||||
case '0':
|
||||
break;
|
||||
case '1': {
|
||||
m_packed_value_buffer_[i] |= 1;
|
||||
} break;
|
||||
case 'X':
|
||||
case 'x': {
|
||||
m_packed_value_buffer_[i] |= 1;
|
||||
} // FALLTHROUGH
|
||||
case 'Z':
|
||||
case 'z': {
|
||||
m_packed_value_buffer_[j | 1] |= 1;
|
||||
} break;
|
||||
[[unlikely]] default: { FST_FAIL_STRING("Unexpected char"); } break;
|
||||
}
|
||||
} else {
|
||||
m_packed_value_buffer_[i] <<= 1;
|
||||
m_packed_value_buffer_[i] |= static_cast<uint64_t>(*p - '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bitwidth <= 64) {
|
||||
if (bitwidth <= 64 && !hasXZ) {
|
||||
emitValueChange(handle, m_packed_value_buffer_.front());
|
||||
} else {
|
||||
emitValueChange(handle, m_packed_value_buffer_.data(), EncodingType::BINARY);
|
||||
emitValueChange(
|
||||
handle,
|
||||
m_packed_value_buffer_.data(),
|
||||
hasXZ ? EncodingType::VERILOG : EncodingType::BINARY
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,7 +634,7 @@ void detail::ValueChangeData::writeEncodedPositions(
|
||||
}
|
||||
|
||||
// encode as signed (value << 1) | 1 and write as signed LEB128
|
||||
h.writeLEB128Signed((value_to_encode << 1) | 1);
|
||||
h.writeLEB128Signed((static_cast<uint64_t>(value_to_encode) << 1) | 1);
|
||||
|
||||
++i;
|
||||
}
|
||||
@@ -695,7 +724,7 @@ void Writer::flushValueChangeDataConstPart_(
|
||||
(void)count;
|
||||
return std::make_pair(positions, memory_usage);
|
||||
}();
|
||||
const std::vector<int64_t> positions{p_tmp2.first};
|
||||
const std::vector<int64_t> positions{std::move(p_tmp2.first)};
|
||||
const size_t memory_usage{p_tmp2.second};
|
||||
|
||||
// 4. Position Section
|
||||
|
||||
+202
-32
@@ -243,20 +243,32 @@ void vl_warn(const char* filename, int linenum, const char* hier, const char* ms
|
||||
// Wrapper to call certain functions via messages when multithreaded
|
||||
|
||||
void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE {
|
||||
VerilatedContext* const contextp = Verilated::threadContextp();
|
||||
contextp->finishPendingInc();
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||
vl_finish(filename, linenum, hier);
|
||||
contextp->finishPendingDec();
|
||||
}});
|
||||
}
|
||||
|
||||
void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
|
||||
// Classify now, so a queued request is pending from the moment it is posted
|
||||
VerilatedContext* const contextp = Verilated::threadContextp();
|
||||
const bool stop = contextp->stopRequestReserve(maybe);
|
||||
if (stop) contextp->finishPendingInc();
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||
vl_stop_maybe(filename, linenum, hier, maybe);
|
||||
contextp->stopRequestRelease();
|
||||
if (stop) contextp->finishPendingDec();
|
||||
}});
|
||||
}
|
||||
|
||||
void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
|
||||
VerilatedContext* const contextp = Verilated::threadContextp();
|
||||
contextp->finishPendingInc();
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||
vl_fatal(filename, linenum, hier, msg);
|
||||
contextp->finishPendingDec();
|
||||
}});
|
||||
}
|
||||
|
||||
@@ -563,9 +575,10 @@ IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE {
|
||||
}
|
||||
|
||||
IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
|
||||
if (Verilated::threadContextp()->randReset() == 0) return 0;
|
||||
const int randReset = Verilated::threadContextp()->randReset();
|
||||
if (randReset == 0) return 0;
|
||||
IData data = ~0;
|
||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
||||
if (randReset != 1) { // if 2, randomize
|
||||
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
||||
data = rng.rand64();
|
||||
}
|
||||
@@ -574,9 +587,10 @@ IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT
|
||||
}
|
||||
|
||||
QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
|
||||
if (Verilated::threadContextp()->randReset() == 0) return 0;
|
||||
const int randReset = Verilated::threadContextp()->randReset();
|
||||
if (randReset == 0) return 0;
|
||||
QData data = ~0ULL;
|
||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
||||
if (randReset != 1) { // if 2, randomize
|
||||
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
||||
data = rng.rand64();
|
||||
}
|
||||
@@ -586,10 +600,17 @@ QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT
|
||||
|
||||
WDataOutP VL_SCOPED_RAND_RESET_W(int obits, WDataOutP outwp, uint64_t scopeHash,
|
||||
uint64_t salt) VL_MT_UNSAFE {
|
||||
if (Verilated::threadContextp()->randReset() != 2) { return VL_RAND_RESET_W(obits, outwp); }
|
||||
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
||||
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = rng.rand64();
|
||||
outwp[VL_WORDS_I(obits) - 1] = rng.rand64() & VL_MASK_E(obits);
|
||||
const int words = VL_WORDS_I(obits);
|
||||
const int randReset = Verilated::threadContextp()->randReset();
|
||||
if (randReset == 0) {
|
||||
VL_MEMSET_ZERO_W(outwp, words);
|
||||
} else if (randReset == 1) {
|
||||
VL_MEMSET_ONES_W(outwp, words);
|
||||
} else {
|
||||
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
||||
for (int i = 0; i < words; ++i) outwp[i] = rng.rand64();
|
||||
}
|
||||
outwp[words - 1] &= VL_MASK_E(obits);
|
||||
return outwp;
|
||||
}
|
||||
|
||||
@@ -614,30 +635,14 @@ WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint64_t sco
|
||||
}
|
||||
|
||||
IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
|
||||
if (Verilated::threadContextp()->randReset() == 0) return 0;
|
||||
const int randReset = Verilated::threadContextp()->randReset();
|
||||
if (randReset == 0) return 0;
|
||||
IData data = ~0;
|
||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
||||
data = VL_RANDOM_I();
|
||||
}
|
||||
if (randReset != 1) data = VL_RANDOM_I(); // if 2, randomize
|
||||
data &= VL_MASK_I(obits);
|
||||
return data;
|
||||
}
|
||||
|
||||
QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
|
||||
if (Verilated::threadContextp()->randReset() == 0) return 0;
|
||||
QData data = ~0ULL;
|
||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
||||
data = VL_RANDOM_Q();
|
||||
}
|
||||
data &= VL_MASK_Q(obits);
|
||||
return data;
|
||||
}
|
||||
|
||||
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_I(32);
|
||||
outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_I(32) & VL_MASK_E(obits);
|
||||
return outwp;
|
||||
}
|
||||
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
// Not inlined to speed up compilation of slowpath code
|
||||
return VL_ZERO_W(obits, outwp);
|
||||
@@ -3067,9 +3072,11 @@ VerilatedContext::VerilatedContext()
|
||||
|
||||
// Must declare here not in interface, as otherwise forward declarations not known
|
||||
VerilatedContext::~VerilatedContext() {
|
||||
Verilated::threadContextp(this); // In unlikely case some other destructor needs it
|
||||
checkMagic(this);
|
||||
m_magic = 0x1; // Arbitrary but 0x1 is what Verilator src uses for a deleted pointer
|
||||
logRestoreOutput();
|
||||
Verilated::threadContextp(nullptr);
|
||||
}
|
||||
|
||||
void VerilatedContext::checkMagic(const VerilatedContext* contextp) {
|
||||
@@ -3087,6 +3094,7 @@ 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;
|
||||
// Set all assert and directive types when true, clear otherwise.
|
||||
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
|
||||
}
|
||||
@@ -3105,16 +3113,22 @@ uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
|
||||
}
|
||||
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;
|
||||
const uint32_t mask = assertOnMask(types, directives);
|
||||
const uint32_t lockedMask = mask & ~m_s.m_assertLock;
|
||||
switch (controlType) {
|
||||
@@ -3286,6 +3300,15 @@ void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_gotFinish = flag;
|
||||
}
|
||||
bool VerilatedContext::stopRequestReserve(bool maybe) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
const int reserved = ++m_ns.m_stopReserved;
|
||||
return !maybe || m_s.m_errorCount + reserved >= m_s.m_errorLimit;
|
||||
}
|
||||
void VerilatedContext::stopRequestRelease() VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
--m_ns.m_stopReserved;
|
||||
}
|
||||
bool VerilatedContext::executingFinal() const VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_executingFinal;
|
||||
@@ -3556,7 +3579,9 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||
if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
|
||||
std::string str;
|
||||
uint64_t u64;
|
||||
if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
|
||||
if (arg == "+verilator+assert+lock") {
|
||||
assertCtlsLocked(true);
|
||||
} else if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
|
||||
coverageFilename(str);
|
||||
} else if (arg == "+verilator+debug") {
|
||||
Verilated::debug(4);
|
||||
@@ -3575,7 +3600,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||
logFilename(str);
|
||||
logOutputToFile(false /* append */);
|
||||
} else if (arg == "+verilator+noassert") {
|
||||
assertOn(false);
|
||||
// Set directly on to avoid conflicts with +verilator+assert+lock
|
||||
m_s.m_assertOn = 0;
|
||||
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+start+", u64)) {
|
||||
profExecStart(u64);
|
||||
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)) {
|
||||
@@ -3891,7 +3917,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
|
||||
// When running internal code coverage (gcc --coverage, as opposed to
|
||||
// verilator --coverage), dump coverage data to properly cover failing
|
||||
// tests.
|
||||
VL_GCOV_DUMP();
|
||||
VL_GCOV_DUMP_RESET();
|
||||
}
|
||||
|
||||
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { addCbExit(cb, datap); }
|
||||
@@ -4012,6 +4038,106 @@ void VerilatedImp::versionDump() VL_MT_SAFE {
|
||||
VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// VerilatedEvalLoop:: Methods
|
||||
|
||||
void VerilatedEvalLoop::didNotConverge(const char* namep,
|
||||
void (VerilatedModel::*dumpTriggersp)()) {
|
||||
if (dumpTriggersp) (m_model.*dumpTriggersp)();
|
||||
const std::string msg = "DIDNOTCONVERGE: "s + namep
|
||||
+ " region did not converge after '--converge-limit' of "
|
||||
+ std::to_string(m_convergeLimit) + " tries";
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
VL_UNREACHABLE; // VL_FATAL_MT does not return
|
||||
}
|
||||
|
||||
template <bool Profiling>
|
||||
void VerilatedEvalLoop::evalImpl() {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+ Eval\n"););
|
||||
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) {
|
||||
// Advance the profiling window
|
||||
if (VL_UNLIKELY(m_profTopLevel)) m_profilerp->configure();
|
||||
m_profilerp->sectionPush("eval");
|
||||
}
|
||||
|
||||
m_model.evalBegin();
|
||||
|
||||
// Initialization on first time step only
|
||||
if (VL_UNLIKELY(!m_model.m_didInit)) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+ Initial\n"););
|
||||
// Static initializers
|
||||
m_model.evalStatic();
|
||||
// Initial blocks
|
||||
m_model.evalInitial();
|
||||
// The 'Settle' region, iterated until it converges
|
||||
uint32_t stlIterCount = 0;
|
||||
do {
|
||||
checkConvergence(++stlIterCount, "Settle", &VerilatedModel::dumpTriggersStl);
|
||||
} while (m_model.evalStl(stlIterCount == 1));
|
||||
m_model.m_didInit = true;
|
||||
}
|
||||
|
||||
// Sampled values are collected before anything can read them
|
||||
m_model.evalSample();
|
||||
|
||||
// The 'Input combinational' region updates combinational logic driven from primary inputs
|
||||
{
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop ico");
|
||||
uint32_t icoIterCount = 0;
|
||||
do {
|
||||
checkConvergence(++icoIterCount, "Input combinational",
|
||||
&VerilatedModel::dumpTriggersIco);
|
||||
} while (m_model.evalIco(icoIterCount == 1));
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop ico
|
||||
}
|
||||
|
||||
// The remaining regions are nested: each iteration of a region's loop
|
||||
// re-runs the loops of all regions that precede it in the scheduling order.
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop react");
|
||||
uint32_t reactIterCount = 0;
|
||||
do {
|
||||
checkConvergence(++reactIterCount, "Reactive", &VerilatedModel::dumpTriggersReact);
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop obs");
|
||||
uint32_t obsIterCount = 0;
|
||||
do {
|
||||
checkConvergence(++obsIterCount, "Observed", &VerilatedModel::dumpTriggersObs);
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop nba");
|
||||
uint32_t nbaIterCount = 0;
|
||||
do {
|
||||
checkConvergence(++nbaIterCount, "NBA", &VerilatedModel::dumpTriggersNba);
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop inact");
|
||||
uint32_t inactIterCount = 0;
|
||||
do {
|
||||
checkConvergence(++inactIterCount, "Inactive");
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop act");
|
||||
uint32_t actIterCount = 0;
|
||||
do {
|
||||
checkConvergence(++actIterCount, "Active",
|
||||
&VerilatedModel::dumpTriggersAct);
|
||||
} while (m_model.evalAct());
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop act
|
||||
} while (m_model.evalInact());
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop inact
|
||||
} while (m_model.evalNba());
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop nba
|
||||
} while (m_model.evalObs());
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop obs
|
||||
} while (m_model.evalReact());
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop react
|
||||
|
||||
// The 'Postponed' region runs once, at the end of the time step
|
||||
m_model.evalPostponed();
|
||||
|
||||
m_model.evalEnd();
|
||||
|
||||
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // eval
|
||||
}
|
||||
|
||||
// Template instantiations
|
||||
template void VerilatedEvalLoop::evalImpl<false>();
|
||||
template void VerilatedEvalLoop::evalImpl<true>();
|
||||
|
||||
//===========================================================================
|
||||
// VerilatedModel:: Methods
|
||||
|
||||
@@ -4078,12 +4204,12 @@ VerilatedScope::VerilatedScope(VerilatedSyms* symsp, const char* suffixp, const
|
||||
, m_defnamep{defnamep}
|
||||
, m_timeunit{timeunit}
|
||||
, m_type{type} {
|
||||
Verilated::threadContextp()->impp()->scopeInsert(this);
|
||||
contextp()->impp()->scopeInsert(this);
|
||||
}
|
||||
|
||||
VerilatedScope::~VerilatedScope() {
|
||||
// Memory cleanup - not called during normal operation
|
||||
Verilated::threadContextp()->impp()->scopeErase(this);
|
||||
contextp()->impp()->scopeErase(this);
|
||||
VL_DO_DANGLING(delete[] m_namep, m_namep);
|
||||
VL_DO_DANGLING(delete[] m_callbacksp, m_callbacksp);
|
||||
VL_DO_DANGLING(delete m_varsp, m_varsp);
|
||||
@@ -4142,6 +4268,50 @@ VerilatedVar* VerilatedScope::varInsert(const char* namep, void* datap, bool isP
|
||||
return &(m_varsp->find(namep)->second);
|
||||
}
|
||||
|
||||
void VerilatedScope::varsInsertFromTable(const VlVarTableEntry* entp, size_t n,
|
||||
void* basep) VL_MT_UNSAFE {
|
||||
// Table-driven equivalent of a run of varInsert()/varInsertSized() calls; see VlVarTableEntry.
|
||||
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
|
||||
uint8_t* const base = static_cast<uint8_t*>(basep);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const VlVarTableEntry& e = entp[i];
|
||||
void* const datap = base + e.byteOffset;
|
||||
const VerilatedVarFlags vlflags = static_cast<VerilatedVarFlags>(e.vlflags);
|
||||
VerilatedVar var{e.namep, datap, e.vltype, vlflags, e.udims, e.pdims, /*isParam=*/false};
|
||||
for (int d = 0; d < e.udims; ++d) {
|
||||
var.m_unpacked[d].m_left = e.dims[2 * d];
|
||||
var.m_unpacked[d].m_right = e.dims[2 * d + 1];
|
||||
}
|
||||
for (int d = 0; d < e.pdims; ++d) {
|
||||
var.m_packed[d].m_left = e.dims[2 * (e.udims + d)];
|
||||
var.m_packed[d].m_right = e.dims[2 * (e.udims + d) + 1];
|
||||
}
|
||||
// Recompute the flattened DPI packed range now dims are known (see
|
||||
// VerilatedVarProps::initPacked)
|
||||
if (e.pdims == 1) {
|
||||
var.m_packedDpi = var.m_packed.front();
|
||||
} else if (e.pdims > 1) {
|
||||
int packedSize = 1;
|
||||
for (int d = 0; d < e.pdims; ++d) packedSize *= var.m_packed[d].elements();
|
||||
var.m_packedDpi = VerilatedRange{packedSize - 1, 0};
|
||||
}
|
||||
m_varsp->emplace(e.namep, std::move(var));
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedScope::scopesConstructFromTable(const VlScopeTableEntry* entp, size_t n,
|
||||
VerilatedSyms* symsp) VL_MT_UNSAFE {
|
||||
// Table-driven equivalent of a run of 'new VerilatedScope{...}' statements; see
|
||||
// VlScopeTableEntry. The generated Syms class derives VerilatedSyms as its sole primary
|
||||
// base at offset 0, so symsp doubles as the base for the offsetof-baked member addresses.
|
||||
uint8_t* const base = reinterpret_cast<uint8_t*>(symsp);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const VlScopeTableEntry& e = entp[i];
|
||||
VerilatedScope** const slotp = reinterpret_cast<VerilatedScope**>(base + e.ptrOffset);
|
||||
*slotp = new VerilatedScope{symsp, e.namep, e.identp, e.defnamep, e.timeunit, e.type};
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
|
||||
VerilatedVarType vltype, int vlflags, int udims,
|
||||
uint32_t entSize...) VL_MT_UNSAFE {
|
||||
|
||||
@@ -160,6 +160,21 @@ enum VerilatedVarFlags : uint32_t {
|
||||
VLVF_NET = (1 << 15) // Net object
|
||||
};
|
||||
|
||||
// One VPI-visible variable, consumed by VerilatedScope::varsInsertFromTable();
|
||||
// replaces per-variable varInsert() calls, which compiles faster at scale.
|
||||
struct VlVarTableEntry final {
|
||||
static constexpr int kMaxDims = 3; // Max packed+unpacked dims a table row holds
|
||||
const char* namep; // VPI-facing (protected) variable name, string literal
|
||||
size_t byteOffset; // offsetof of storage member from module instance base
|
||||
VerilatedVarType vltype;
|
||||
uint32_t vlflags; // Direction + flags (VLVD_*/VLVF_*)
|
||||
uint8_t udims; // udims + pdims <= kMaxDims
|
||||
uint8_t pdims;
|
||||
// (left,right) pairs: unpacked dims first, then packed; int32_t since large
|
||||
// unpacked memories exceed int16 range
|
||||
int32_t dims[kMaxDims * 2];
|
||||
};
|
||||
|
||||
// IEEE 1800-2023 Table 20-6
|
||||
enum class VerilatedAssertType : uint8_t {
|
||||
ASSERT_TYPE_CONCURRENT = (1 << 0),
|
||||
@@ -286,6 +301,8 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
class VlExecutionProfilerBase;
|
||||
|
||||
//=========================================================================
|
||||
/// Base class of a Verilator generated (Verilated) model.
|
||||
///
|
||||
@@ -298,6 +315,8 @@ class VerilatedModel VL_NOT_FINAL {
|
||||
VerilatedContext& m_context; // The VerilatedContext this model is instantiated under
|
||||
|
||||
protected:
|
||||
bool m_didInit = false; // Time 0 initialization has run
|
||||
|
||||
explicit VerilatedModel(VerilatedContext& context);
|
||||
virtual ~VerilatedModel() = default;
|
||||
|
||||
@@ -316,8 +335,82 @@ private:
|
||||
// The following are for use by Verilator internals only
|
||||
template <typename, typename>
|
||||
friend class VerilatedTrace;
|
||||
friend class VerilatedEvalLoop;
|
||||
|
||||
// Run-time trace configuration requested by this model
|
||||
virtual std::unique_ptr<VerilatedTraceConfig> traceConfig() const;
|
||||
|
||||
// Entry points called by VerilatedEvalLoop
|
||||
virtual void evalBegin() = 0;
|
||||
virtual void evalEnd() = 0;
|
||||
virtual void evalStatic() = 0;
|
||||
virtual void evalInitial() = 0;
|
||||
virtual void evalSample() = 0;
|
||||
virtual bool evalStl(bool firstIteration) = 0;
|
||||
virtual bool evalIco(bool firstIteration) = 0;
|
||||
virtual bool evalAct() = 0;
|
||||
virtual bool evalInact() = 0;
|
||||
virtual bool evalNba() = 0;
|
||||
virtual bool evalObs() = 0;
|
||||
virtual bool evalReact() = 0;
|
||||
virtual void evalPostponed() = 0;
|
||||
virtual void dumpTriggersStl() = 0;
|
||||
virtual void dumpTriggersIco() = 0;
|
||||
virtual void dumpTriggersAct() = 0;
|
||||
virtual void dumpTriggersNba() = 0;
|
||||
virtual void dumpTriggersObs() = 0;
|
||||
virtual void dumpTriggersReact() = 0;
|
||||
|
||||
// Runs 'final' blocks at the end of the simulation
|
||||
virtual void evalFinal() = 0;
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
/// Evaluation loop calling a VerilatedModel's entry points
|
||||
|
||||
class VerilatedEvalLoop final {
|
||||
VL_UNCOPYABLE(VerilatedEvalLoop);
|
||||
|
||||
// MEMBERS
|
||||
VerilatedModel& m_model; // The model this loop evaluates
|
||||
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;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedEvalLoop(VerilatedModel& model, uint32_t convergeLimit)
|
||||
: m_model{model}
|
||||
, m_convergeLimit{convergeLimit} {}
|
||||
|
||||
// METHODS
|
||||
// Evaluate a single time step of the SV scheduling model
|
||||
void eval() {
|
||||
if (VL_UNLIKELY(m_profilerp)) {
|
||||
evalImpl<true>();
|
||||
} else {
|
||||
evalImpl<false>();
|
||||
}
|
||||
}
|
||||
// Set --prof-exec profiler
|
||||
void profiler(VlExecutionProfilerBase* profilerp, bool topLevel) {
|
||||
m_profilerp = profilerp;
|
||||
m_profTopLevel = topLevel;
|
||||
}
|
||||
|
||||
private:
|
||||
// Evaluate a time step, recording --prof-exec sections iff 'Profiling'.
|
||||
template <bool Profiling>
|
||||
void evalImpl();
|
||||
// Check the iteration convergence
|
||||
void checkConvergence(uint32_t iterCount, const char* namep,
|
||||
void (VerilatedModel::*dumpTriggersp)() = nullptr) {
|
||||
if (VL_UNLIKELY(iterCount > m_convergeLimit)) didNotConverge(namep, dumpTriggersp);
|
||||
}
|
||||
// Dump the region's triggers, if it has any, then report non-convergence and abort
|
||||
void didNotConverge(const char* namep, void (VerilatedModel::*dumpTriggersp)());
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
@@ -342,6 +435,23 @@ public:
|
||||
virtual ~VerilatedVirtualBase() = default;
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
// Internal: Base of the '--prof-exec' execution profiler
|
||||
//
|
||||
// Implemented by VlExecutionProfiler, see verilated_profiler.h. Declared here
|
||||
// so the evaluation loop can drive the profiler without naming it, as it is
|
||||
// only linked when Verilated with --prof-exec.
|
||||
|
||||
class VlExecutionProfilerBase VL_NOT_FINAL : public VerilatedVirtualBase {
|
||||
public:
|
||||
// Mark the beginning of a section of execution
|
||||
virtual void sectionPush(const char* namep) = 0;
|
||||
// Mark the end of the innermost open section
|
||||
virtual void sectionPop() = 0;
|
||||
// Advance the profiling window at the start of a time step
|
||||
virtual void configure() = 0;
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
/// Verilator simulation context
|
||||
///
|
||||
@@ -372,6 +482,8 @@ private:
|
||||
static uint32_t assertOnMask(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_PURE;
|
||||
static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1;
|
||||
// No termination request has stamped m_finishPendingTime yet
|
||||
static constexpr uint64_t TIME_UNSET = ~0ULL;
|
||||
|
||||
protected:
|
||||
// TYPES
|
||||
@@ -430,6 +542,12 @@ protected:
|
||||
struct NonSerialized final { // Non-serialized information
|
||||
// These are reloaded from on command-line settings, so do not need to persist
|
||||
// Fast path
|
||||
// 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
|
||||
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
|
||||
uint32_t m_profExecWindow = 2; // +prof+exec+window size
|
||||
@@ -510,6 +628,12 @@ public:
|
||||
/// 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;
|
||||
@@ -669,6 +793,27 @@ public:
|
||||
|
||||
// METHODS - public but for internal use only
|
||||
|
||||
// Internal: Track $finish/$stop callbacks queued by worker threads
|
||||
bool finishPending() const VL_MT_SAFE { return m_ns.m_finishPending.load() != 0; }
|
||||
void finishPendingInc() VL_MT_SAFE {
|
||||
++m_ns.m_finishPending;
|
||||
uint64_t unset = TIME_UNSET;
|
||||
m_ns.m_finishPendingTime.compare_exchange_strong(unset, time());
|
||||
}
|
||||
void finishPendingDec() VL_MT_SAFE {
|
||||
const uint32_t previous = m_ns.m_finishPending.fetch_sub(1);
|
||||
assert(previous > 0);
|
||||
if (previous == 1 && !gotFinish()) m_ns.m_finishPendingTime = TIME_UNSET;
|
||||
}
|
||||
// Internal: Time of the first termination request, else the current time
|
||||
uint64_t finishPendingTime() const VL_MT_SAFE {
|
||||
const uint64_t stamped = m_ns.m_finishPendingTime.load();
|
||||
return stamped == TIME_UNSET ? time() : stamped;
|
||||
}
|
||||
// Internal: Reserve a posted $stop, returning true if it reaches the termination limit
|
||||
bool stopRequestReserve(bool maybe) VL_MT_SAFE;
|
||||
void stopRequestRelease() 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 {
|
||||
@@ -756,6 +901,8 @@ public: // But for internal use only
|
||||
// Verilator scope information class
|
||||
// Used for internal VPI implementation, and introspection into scopes
|
||||
|
||||
struct VlScopeTableEntry; // Defined below VerilatedScope; used by scopesConstructFromTable()
|
||||
|
||||
class VerilatedScope final {
|
||||
public:
|
||||
enum Type : uint8_t {
|
||||
@@ -792,6 +939,9 @@ public: // But internals only - called from verilated modules, VerilatedSyms
|
||||
void* forceReadSignalData, const char* forceReadSignalName,
|
||||
std::pair<VerilatedVar*, VerilatedVar*> forceControlSignals,
|
||||
int udims, int pdims...) VL_MT_UNSAFE;
|
||||
void varsInsertFromTable(const VlVarTableEntry* entp, size_t n, void* basep) VL_MT_UNSAFE;
|
||||
static void scopesConstructFromTable(const VlScopeTableEntry* entp, size_t n,
|
||||
VerilatedSyms* symsp) VL_MT_UNSAFE;
|
||||
// ACCESSORS
|
||||
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
|
||||
const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }
|
||||
@@ -805,6 +955,18 @@ public: // But internals only - called from verilated modules, VerilatedSyms
|
||||
static void* exportFindNullError(int funcnum) VL_MT_SAFE;
|
||||
static void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE;
|
||||
Type type() const { return m_type; }
|
||||
VerilatedContext* contextp() const { return m_symsp->_vm_contextp__; }
|
||||
};
|
||||
|
||||
// One scope, consumed by VerilatedScope::scopesConstructFromTable(); replaces
|
||||
// per-scope 'new VerilatedScope{...}' statements, which compiles faster at scale.
|
||||
struct VlScopeTableEntry final {
|
||||
size_t ptrOffset; // offsetof of the target __Vscopep_* member within the Syms object
|
||||
const char* namep; // Scope suffix name (protected), string literal
|
||||
const char* identp; // Identifier with escapes removed (protected)
|
||||
const char* defnamep; // Definition name (SCOPE_MODULE only), else "<null>"
|
||||
int8_t timeunit; // Timeunit in negative power-of-10
|
||||
VerilatedScope::Type type;
|
||||
};
|
||||
|
||||
class VerilatedHierarchy final {
|
||||
|
||||
@@ -43,7 +43,7 @@ CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
|
||||
# Compiler flags that enable coroutine support
|
||||
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
|
||||
# Compiler flags when creating a precompiled header
|
||||
CFG_CXXFLAGS_PCH = -x c++-header
|
||||
CFG_CXXFLAGS_PCH = -c -x c++-header
|
||||
# Compiler option to put in front of filename to read precompiled header
|
||||
CFG_CXXFLAGS_PCH_I = @CFG_CXXFLAGS_PCH_I@
|
||||
# Compiler's filename prefix for precompiled headers, .gch if clang, empty if GCC
|
||||
|
||||
+12
-12
@@ -75,24 +75,24 @@ class VerilatedCovImp;
|
||||
ccontextp->_insertp("hier", name, __VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData,
|
||||
const IData oldData) {
|
||||
inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData,
|
||||
const IData oldData) {
|
||||
const IData chgData = newData ^ oldData;
|
||||
for (int i = 0; i < width; ++i) {
|
||||
*(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const QData newData,
|
||||
const QData oldData) {
|
||||
inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const QData newData,
|
||||
const QData oldData) {
|
||||
const QData chgData = newData ^ oldData;
|
||||
for (int i = 0; i < width; ++i) {
|
||||
*(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData,
|
||||
WDataInP oldData) {
|
||||
inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData,
|
||||
WDataInP oldData) {
|
||||
for (int i = 0; i < VL_WORDS_I(width); ++i) {
|
||||
const EData chgData = newData[i] ^ oldData[i];
|
||||
if (chgData) {
|
||||
@@ -104,8 +104,8 @@ static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WData
|
||||
}
|
||||
}
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic<uint32_t>* covp,
|
||||
const IData newData, const IData oldData) VL_MT_SAFE {
|
||||
inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic<uint32_t>* covp,
|
||||
const IData newData, const IData oldData) VL_MT_SAFE {
|
||||
const IData chgData = newData ^ oldData;
|
||||
for (int i = 0; i < width; ++i) {
|
||||
if (VL_BITISSET_I(chgData, i)) {
|
||||
@@ -114,8 +114,8 @@ static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic<uint32_t>
|
||||
}
|
||||
}
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic<uint32_t>* covp,
|
||||
const QData newData, const QData oldData) VL_MT_SAFE {
|
||||
inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic<uint32_t>* covp,
|
||||
const QData newData, const QData oldData) VL_MT_SAFE {
|
||||
const QData chgData = newData ^ oldData;
|
||||
for (int i = 0; i < width; ++i) {
|
||||
if (VL_BITISSET_Q(chgData, i)) {
|
||||
@@ -124,8 +124,8 @@ static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic<uint32_t>
|
||||
}
|
||||
}
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic<uint32_t>* covp,
|
||||
WDataInP newData, WDataInP oldData) VL_MT_SAFE {
|
||||
inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic<uint32_t>* covp, WDataInP newData,
|
||||
WDataInP oldData) VL_MT_SAFE {
|
||||
for (int i = 0; i < VL_WORDS_I(width); ++i) {
|
||||
const EData chgData = newData[i] ^ oldData[i];
|
||||
if (chgData) {
|
||||
|
||||
@@ -41,9 +41,13 @@ enum class VlCovBinKind : uint8_t {
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverpointIf
|
||||
/// Read-side view of a coverpoint. The writer queries bins by index; the
|
||||
/// implementor computes names/kinds on demand. Bounded bin count, so random
|
||||
/// access by index is the primary usage.
|
||||
/// Read-side view of a coverpoint -- a named, index-addressable set of bins
|
||||
/// with a coverage fraction. A cross is also a coverpoint from this view: its
|
||||
/// auto cross bins (one per element of the Cartesian product of the feeding
|
||||
/// coverpoints' Normal bins) are all Normal, and their names are built on
|
||||
/// demand by concatenating the feeding coverpoints' bin names. The writer
|
||||
/// queries bins by index; the implementor computes names/kinds on demand.
|
||||
/// Bounded bin count, so random access by index is the primary usage.
|
||||
|
||||
class VlCoverpointIf VL_NOT_FINAL {
|
||||
public:
|
||||
@@ -51,11 +55,11 @@ public:
|
||||
virtual ~VlCoverpointIf() = default;
|
||||
|
||||
// METHODS
|
||||
// All bins, across every set; index range [0, binCount())
|
||||
virtual int binCount() const = 0;
|
||||
// Bin name in declaration order (e.g. "myBin" or "b[3]")
|
||||
virtual std::string binName(int i) const = 0;
|
||||
virtual VlCovBinKind binKind(int i) const = 0;
|
||||
// All bins, across every set; index range [0, binCount()).
|
||||
virtual uint32_t binCount() const = 0;
|
||||
// Bin name in declaration order (e.g. "myBin" or "b[3]"); for a cross,
|
||||
// the concatenated cross bin name (e.g. "b1_x_b2_x_b3")
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -23,34 +23,57 @@
|
||||
|
||||
#include "verilated_covergroup.h"
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
// This file is compiled whenever covergroups are used, with or without
|
||||
// "verilator --coverage" (see V3Global::verilatedCppFiles). Bin counts are
|
||||
// members of the covergroup objects themselves, so sampling, bin naming, and
|
||||
// coverage queries such as get_inst_coverage() all work with no coverage
|
||||
// database present. VL_COVER_INSERT does not copy a count; it hands the
|
||||
// database the address of a counter to read at write time. Only that
|
||||
// publication step needs verilated_cov.cpp, which is compiled solely under
|
||||
// --coverage, so only the registerBins() bodies are gated on VM_COVERAGE.
|
||||
#if VM_COVERAGE
|
||||
#include "verilated_cov.h"
|
||||
#endif
|
||||
|
||||
void VlCoverpoint::init(const char* hier, uint32_t atLeast, int nBins) {
|
||||
void VlCoverpoint::init(const char* hier, uint32_t atLeast, uint32_t nBins) {
|
||||
m_hier = hier;
|
||||
m_atLeast = atLeast;
|
||||
m_total = nBins;
|
||||
m_counts.assign(nBins, 0);
|
||||
m_crossIdx.assign(nBins, -1);
|
||||
m_crossToBin.clear();
|
||||
}
|
||||
|
||||
void VlCoverpoint::addNamer(VlCovBinKind set, int count, VlCovBinNaming naming, const char* name,
|
||||
const char* file, int line, int col) {
|
||||
void VlCoverpoint::addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming,
|
||||
const char* name, const char* file, int line, int col) {
|
||||
m_namers.emplace_back(set, count, m_nextBase, naming, name, file, line, col);
|
||||
if (set == VlCovBinKind::KIND_NORMAL) {
|
||||
// Assign each Normal bin a cross index, and record the inverse map.
|
||||
for (uint32_t b = m_nextBase; b < m_nextBase + count; ++b) {
|
||||
m_crossIdx[b] = static_cast<int>(m_crossToBin.size());
|
||||
m_crossToBin.push_back(b);
|
||||
}
|
||||
m_normal += count;
|
||||
}
|
||||
m_nextBase += count;
|
||||
if (set == VlCovBinKind::KIND_NORMAL) m_normal += count;
|
||||
}
|
||||
|
||||
const VlCovNamer& VlCoverpoint::namerFor(int i) const {
|
||||
// Namers are appended in ascending, contiguous index order covering [0, m_total),
|
||||
// and i is always a valid bin index, so the matching namer always exists.
|
||||
std::string VlCoverpoint::normalBinName(uint32_t crossIdx) const {
|
||||
// Build the bin name based on the bin index
|
||||
return binName(m_crossToBin[crossIdx]);
|
||||
}
|
||||
|
||||
const VlCovNamer& VlCoverpoint::namerFor(uint32_t i) const {
|
||||
// Namers are appended in ascending order covering [0, m_total),
|
||||
for (const VlCovNamer& nm : m_namers) {
|
||||
if (i < nm.base() + nm.count()) return nm;
|
||||
}
|
||||
VL_UNREACHABLE;
|
||||
VL_UNREACHABLE; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
std::string VlCoverpoint::binName(int i) const {
|
||||
std::string VlCoverpoint::binName(uint32_t i) const {
|
||||
const VlCovNamer& nm = namerFor(i);
|
||||
std::string name = nm.name();
|
||||
if (nm.naming() == VlCovBinNaming::Array) name += '[' + std::to_string(i - nm.base()) + ']';
|
||||
@@ -59,7 +82,7 @@ std::string VlCoverpoint::binName(int i) const {
|
||||
|
||||
#if VM_COVERAGE
|
||||
void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* page) {
|
||||
for (int i = 0; i < binCount(); ++i) {
|
||||
for (uint32_t i = 0; i < binCount(); ++i) {
|
||||
const VlCovNamer& nm = namerFor(i);
|
||||
const VlCovBinKind kind = binKind(i);
|
||||
const std::string binp = binName(i);
|
||||
@@ -81,3 +104,89 @@ void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* pa
|
||||
}
|
||||
}
|
||||
#endif // VM_COVERAGE
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverCross
|
||||
|
||||
void VlCoverCross::init(const char* hier, uint32_t dims, VlCoverpoint* const* cps,
|
||||
const char* file, int line, int col) {
|
||||
m_hier = hier;
|
||||
m_file = file;
|
||||
m_line = line;
|
||||
m_col = col;
|
||||
m_dims = dims;
|
||||
m_cps.assign(cps, cps + dims);
|
||||
m_cpBinCounts.resize(dims);
|
||||
// Accumulate in 64 bits so the overflow check itself cannot overflow.
|
||||
uint64_t product = 1;
|
||||
for (uint32_t d = 0; d < dims; ++d) {
|
||||
m_cpBinCounts[d] = cps[d]->normalBinCount();
|
||||
product *= m_cpBinCounts[d];
|
||||
if (VL_UNLIKELY(product > UINT32_MAX)) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(file, line, "", "Cross has too many auto bins to represent");
|
||||
} // LCOV_EXCL_STOP
|
||||
}
|
||||
m_numAutoBins = static_cast<uint32_t>(product);
|
||||
// stride[d] = product of the Normal bin counts of all dimensions after d.
|
||||
// Counts down with an offset so the unsigned index never wraps below zero.
|
||||
m_stride.assign(dims, 1);
|
||||
for (uint32_t d = dims; d > 1; --d) m_stride[d - 2] = m_stride[d - 1] * m_cpBinCounts[d - 1];
|
||||
m_flatCounts.assign(m_numAutoBins, 0);
|
||||
}
|
||||
|
||||
void VlCoverCross::iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx) {
|
||||
const uint32_t hits = cps[dim]->hitCount();
|
||||
const uint32_t* const list = cps[dim]->hitList();
|
||||
const bool last = (dim == m_dims - 1);
|
||||
const uint32_t stride = m_stride[dim];
|
||||
for (uint32_t hit = 0; hit < hits; ++hit) {
|
||||
const uint32_t idx = baseIdx + list[hit] * stride;
|
||||
if (last) {
|
||||
incrementTuple(idx);
|
||||
} else {
|
||||
iterateProduct(cps, dim + 1, idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VlCoverCross::sample(VlCoverpoint* const* cps) {
|
||||
// Fast path: if any dimension had no Normal-bin hit, the cross cannot hit.
|
||||
for (uint32_t d = 0; d < m_dims; ++d) {
|
||||
if (cps[d]->hitCount() == 0) return;
|
||||
}
|
||||
iterateProduct(cps, 0, 0);
|
||||
}
|
||||
|
||||
std::string VlCoverCross::binName(uint32_t flat) const {
|
||||
// Built on demand by concatenating each coverpoint's own bin name.
|
||||
std::string name;
|
||||
for (uint32_t d = 0; d < m_dims; ++d) {
|
||||
const uint32_t crossIdx = (flat / m_stride[d]) % m_cpBinCounts[d];
|
||||
if (d > 0) name += "_x_";
|
||||
name += m_cps[d]->normalBinName(crossIdx);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
#if VM_COVERAGE
|
||||
void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* page) {
|
||||
// Register every auto cross bin (zero-count bins included), so the report
|
||||
// shows the full Cartesian product of cross bins. Names are built on the fly.
|
||||
const std::string lineStr = std::to_string(m_line);
|
||||
const std::string colStr = std::to_string(m_col);
|
||||
for (uint32_t flat = 0; flat < binCount(); ++flat) {
|
||||
const std::string bin = binName(flat); // "b1_x_b2_x_..."
|
||||
// cross_bins metadata: the same components joined by ',' (not read by the report)
|
||||
std::string crossBins;
|
||||
for (uint32_t d = 0; d < m_dims; ++d) {
|
||||
const uint32_t crossIdx = (flat / m_stride[d]) % m_cpBinCounts[d];
|
||||
if (d > 0) crossBins += ",";
|
||||
crossBins += m_cps[d]->normalBinName(crossIdx);
|
||||
}
|
||||
const std::string full = m_hier + "." + bin;
|
||||
VL_COVER_INSERT(covcontextp, full.c_str(), &m_flatCounts[flat], "page", page, "filename",
|
||||
m_file, "lineno", lineStr.c_str(), "column", colStr.c_str(), "bin",
|
||||
bin.c_str(), "cross", "1", "cross_bins", crossBins.c_str());
|
||||
}
|
||||
}
|
||||
#endif // VM_COVERAGE
|
||||
|
||||
+136
-23
@@ -22,6 +22,9 @@
|
||||
/// it in the constructor (init + add*Namer), increments bins from sample(),
|
||||
/// and registers via registerBins().
|
||||
///
|
||||
/// Collection and coverage queries are always available; only registerBins(),
|
||||
/// which publishes bin counters to the coverage database, requires VM_COVERAGE.
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#ifndef VERILATOR_VERILATED_COVERGROUP_H_
|
||||
@@ -49,8 +52,8 @@ enum class VlCovBinNaming : uint8_t {
|
||||
class VlCovNamer final {
|
||||
// MEMBERS
|
||||
VlCovBinKind m_set; // which set the bins belong to
|
||||
int m_count; // bins this namer covers (1 for Single)
|
||||
int m_base; // first bin index (declaration order), assigned on append
|
||||
uint32_t m_count; // bins this namer covers (1 for Single)
|
||||
uint32_t m_base; // first bin index (declaration order), assigned on append
|
||||
VlCovBinNaming m_naming; // how bin names are built
|
||||
const char* m_name; // bin name (Single) or array base name (Array)
|
||||
const char* m_file; // declaration file
|
||||
@@ -59,8 +62,8 @@ class VlCovNamer final {
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCovNamer(VlCovBinKind set, int count, int base, VlCovBinNaming naming, const char* name,
|
||||
const char* file, int line, int col)
|
||||
VlCovNamer(VlCovBinKind set, uint32_t count, uint32_t base, VlCovBinNaming naming,
|
||||
const char* name, const char* file, int line, int col)
|
||||
: m_set{set}
|
||||
, m_count{count}
|
||||
, m_base{base}
|
||||
@@ -72,8 +75,8 @@ public:
|
||||
|
||||
// METHODS
|
||||
VlCovBinKind set() const { return m_set; }
|
||||
int count() const { return m_count; }
|
||||
int base() const { return m_base; }
|
||||
uint32_t count() const { return m_count; }
|
||||
uint32_t base() const { return m_base; }
|
||||
VlCovBinNaming naming() const { return m_naming; }
|
||||
const char* name() const { return m_name; }
|
||||
const char* file() const { return m_file; }
|
||||
@@ -87,19 +90,30 @@ public:
|
||||
/// bin's set/name come from the owning namer. coverage() is computed on demand
|
||||
/// by scanning bin counts, keeping the sample() hot path a plain counter bump.
|
||||
|
||||
class VlCoverpoint final : public VlCoverpointIf {
|
||||
// MEMBERS
|
||||
// Base coverpoint runtime (read side + collection logic, no hit-list storage).
|
||||
// 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 {
|
||||
protected:
|
||||
// MEMBERS (protected so VlCoverpointT::incrementBin can update them)
|
||||
std::string m_hier; // "covergroup.coverpoint"
|
||||
uint32_t m_atLeast = 1; // option.at_least (coverpoint-wide)
|
||||
int m_total = 0; // bins across all sets
|
||||
int m_normal = 0; // Normal bins (coverage denominator)
|
||||
int m_nextBase = 0; // running append cursor
|
||||
uint32_t m_total = 0; // bins across all sets
|
||||
uint32_t m_normal = 0; // Normal bins (coverage denominator)
|
||||
uint32_t m_nextBase = 0; // running append cursor
|
||||
std::vector<uint32_t> m_counts; // [m_total], one per bin
|
||||
std::vector<VlCovNamer> m_namers; // appended in declaration order
|
||||
// [m_total] full bin idx -> cross idx (Normal-only), -1 otherwise. The only
|
||||
// signed index here: -1 marks a non-Normal bin, which incrementBin filters on.
|
||||
std::vector<int> m_crossIdx;
|
||||
// [m_normal] inverse of m_crossIdx: cross idx -> full bin idx, appended in cross-index order
|
||||
std::vector<uint32_t> m_crossToBin;
|
||||
uint32_t m_hitCount = 0; // entries valid in the hit list this sample
|
||||
|
||||
private:
|
||||
// PRIVATE METHODS
|
||||
const VlCovNamer& namerFor(int i) const; // obtain the bin-specific name producer
|
||||
void addNamer(VlCovBinKind set, int count, VlCovBinNaming naming, const char* name,
|
||||
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);
|
||||
|
||||
public:
|
||||
@@ -108,31 +122,44 @@ public:
|
||||
|
||||
// METHODS
|
||||
// ---- configuration (from generated constructor) ----
|
||||
void init(const char* hier, uint32_t atLeast, int nBins);
|
||||
void init(const char* hier, uint32_t atLeast, uint32_t nBins);
|
||||
void addSingleNamer(VlCovBinKind set, const char* name, const char* file, int line, int col) {
|
||||
addNamer(set, 1, VlCovBinNaming::Single, name, file, line, col);
|
||||
}
|
||||
void addArrayNamer(VlCovBinKind set, int count, const char* name, const char* file, int line,
|
||||
int col) {
|
||||
void addArrayNamer(VlCovBinKind set, uint32_t count, const char* name, const char* file,
|
||||
int line, int col) {
|
||||
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
|
||||
}
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
|
||||
// ---- hot path (from generated sample()) ----
|
||||
void incrementBin(int i) { ++m_counts[i]; } // Normal bin: count only
|
||||
void recordHit(int i) { ++m_counts[i]; } // Ignore/Illegal/Default: count only
|
||||
// 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]; }
|
||||
// incrementBin (Normal bin: count + hit-list append) lives in VlCoverpointT<MaxHits>,
|
||||
// where MaxHits is the gen-time max per-sample bin overlap.
|
||||
|
||||
// ---- cross support (read by VlCoverCross) ----
|
||||
uint32_t hitCount() const { return m_hitCount; }
|
||||
virtual const uint32_t* hitList() const = 0; // provided by VlCoverpointT
|
||||
uint32_t normalBinCount() const { return m_normal; } // cross dimension size (Normal bins)
|
||||
std::string normalBinName(uint32_t crossIdx) const; // name of the crossIdx-th Normal bin
|
||||
|
||||
// ---- VlCoverpointIf ----
|
||||
int binCount() const override { return m_total; }
|
||||
std::string binName(int i) const override;
|
||||
VlCovBinKind binKind(int i) const override { return namerFor(i).set(); }
|
||||
uint32_t binCount() const override { return m_total; }
|
||||
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(); }
|
||||
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.
|
||||
int numCovered = 0;
|
||||
uint32_t numCovered = 0;
|
||||
for (const VlCovNamer& nm : m_namers) {
|
||||
if (nm.set() != VlCovBinKind::KIND_NORMAL) continue;
|
||||
for (int i = nm.base(); i < nm.base() + nm.count(); ++i) {
|
||||
for (uint32_t i = nm.base(); i < nm.base() + nm.count(); ++i) {
|
||||
if (m_counts[i] >= m_atLeast) ++numCovered;
|
||||
}
|
||||
}
|
||||
@@ -141,4 +168,90 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverpointT
|
||||
/// Concrete coverpoint with an inline hit-list array sized to MaxHits -- the
|
||||
/// gen-time maximum number of Normal bins one sample value can match (1 for the
|
||||
/// common non-overlapping case). The bound is a compile-time constant, so for
|
||||
/// MaxHits == 1 incrementBin collapses to a single store. Generated code holds
|
||||
/// the coverpoint as VlCoverpointT<K> and calls incrementBin via the concrete
|
||||
/// type; the cross reads it polymorphically through VlCoverpoint*.
|
||||
|
||||
template <uint32_t MaxHits>
|
||||
class VlCoverpointT final : public VlCoverpoint {
|
||||
// MEMBERS
|
||||
uint32_t m_hits[MaxHits]; // cross indices of Normal bins hit this sample
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCoverpointT() = default;
|
||||
|
||||
// METHODS
|
||||
// Normal bin: bump count and append the bin's cross index to the hit list.
|
||||
// m_hitCount can never exceed MaxHits (the gen-time overlap bound), so no hit
|
||||
// is ever dropped; the bound check is a compile-time-folded safety net.
|
||||
void incrementBin(uint32_t i) {
|
||||
++m_counts[i];
|
||||
// m_crossIdx is signed only to carry the -1 "not a Normal bin" marker;
|
||||
// the >= 0 test below is what makes every stored hit index unsigned-safe.
|
||||
const int cx = m_crossIdx[i];
|
||||
if (cx >= 0 && m_hitCount < MaxHits) m_hits[m_hitCount++] = static_cast<uint32_t>(cx);
|
||||
}
|
||||
const uint32_t* hitList() const override { return m_hits; }
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverCross
|
||||
/// Per-instance auto cross runtime. Holds flat uint32_t[] storage over the
|
||||
/// Cartesian product of the feeding coverpoints' Normal bins. Each sample()
|
||||
/// walks the coverpoint hit lists (O(hits), not O(product)). Bin names are
|
||||
/// built on demand from the coverpoints, so no per-bin name is stored.
|
||||
|
||||
class VlCoverCross final : public VlCoverpointIf {
|
||||
// MEMBERS
|
||||
std::string m_hier; // "covergroup.cross"
|
||||
const char* m_file = nullptr; // Cross declaration file (registration metadata)
|
||||
int m_line = 0; // Cross declaration line
|
||||
int m_col = 0; // Cross declaration column
|
||||
uint32_t m_dims = 0; // Number of feeding coverpoints
|
||||
// Cross bin indexes are unsigned, like the coverpoint bin indexes they are
|
||||
// built from. init() fatals if the product would exceed UINT32_MAX, so every
|
||||
// index computed here provably fits. That bound is far beyond anything
|
||||
// storable anyway: m_flatCounts alone would need 16GB.
|
||||
uint32_t m_numAutoBins = 0; // Product of per-dim Normal bin counts
|
||||
uint32_t m_numCovered = 0; // Distinct bins hit >= 1 (maintained incrementally)
|
||||
std::vector<uint32_t> m_cpBinCounts; // [m_dims] Normal bin count per dimension
|
||||
std::vector<uint32_t> m_stride; // [m_dims] Flat-index stride per dimension
|
||||
std::vector<uint32_t> m_flatCounts; // [m_numAutoBins] Per-bin hit counts
|
||||
std::vector<VlCoverpoint*> m_cps; // Feeding coverpoints (the only name source)
|
||||
|
||||
// PRIVATE METHODS
|
||||
void iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx);
|
||||
void incrementTuple(uint32_t idx) {
|
||||
if (m_flatCounts[idx]++ == 0) ++m_numCovered;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCoverCross() = default;
|
||||
|
||||
// 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);
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
|
||||
// ---- hot path (from generated sample(), after all coverpoints sampled) ----
|
||||
void sample(VlCoverpoint* const* cps);
|
||||
|
||||
// ---- VlCoverpointIf ----
|
||||
// A cross is a coverpoint whose bins are the auto cross bins (all Normal).
|
||||
uint32_t binCount() const override { return m_numAutoBins; }
|
||||
std::string binName(uint32_t flat) const override;
|
||||
void coverageParts(double& covered, double& total) const override {
|
||||
covered = m_numCovered;
|
||||
total = m_numAutoBins;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+16
-16
@@ -37,68 +37,68 @@
|
||||
// SETTING OPERATORS
|
||||
|
||||
// Convert svBitVecVal to Verilator internal data
|
||||
static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
||||
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_Q_SVBV(int obits, QData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_Q_SVBV(int obits, QData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_Q(obits) & VL_SET_QII(lwp[1], lwp[0]);
|
||||
}
|
||||
static inline void VL_SET_I_SVBV(int obits, IData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_I_SVBV(int obits, IData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0];
|
||||
}
|
||||
static inline void VL_SET_S_SVBV(int obits, SData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_S_SVBV(int obits, SData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0];
|
||||
}
|
||||
static inline void VL_SET_C_SVBV(int obits, CData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_C_SVBV(int obits, CData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0];
|
||||
}
|
||||
|
||||
// Convert Verilator internal data to svBitVecVal
|
||||
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
||||
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; }
|
||||
static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE {
|
||||
inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; }
|
||||
inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE {
|
||||
VL_SET_WQ(WDataOutP::external(owp), ld);
|
||||
}
|
||||
|
||||
// Convert svLogicVecVal to Verilator internal data
|
||||
// Note these functions ignore X/Z in svLogicVecVal
|
||||
static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval;
|
||||
owp[words - 1] = lwp[words - 1].aval & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_Q_SVLV(int obits, QData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_Q_SVLV(int obits, QData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_Q(obits) & VL_SET_QII(lwp[1].aval, lwp[0].aval);
|
||||
}
|
||||
static inline void VL_SET_I_SVLV(int obits, IData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_I_SVLV(int obits, IData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0].aval;
|
||||
}
|
||||
static inline void VL_SET_S_SVLV(int obits, SData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_S_SVLV(int obits, SData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0].aval;
|
||||
}
|
||||
static inline void VL_SET_C_SVLV(int obits, CData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_C_SVLV(int obits, CData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0].aval;
|
||||
}
|
||||
|
||||
// Convert Verilator internal data to svLogicVecVal
|
||||
// Note these functions never create X/Z in svLogicVecVal
|
||||
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words; ++i) owp[i].bval = 0;
|
||||
for (int i = 0; i < words - 1; ++i) owp[i].aval = lwp[i];
|
||||
owp[words - 1].aval = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, const IData ld) VL_MT_SAFE {
|
||||
inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, const IData ld) VL_MT_SAFE {
|
||||
owp[0].aval = ld;
|
||||
owp[0].bval = 0;
|
||||
}
|
||||
static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, const QData ld) VL_MT_SAFE {
|
||||
inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, const QData ld) VL_MT_SAFE {
|
||||
VlWide<2> lwp;
|
||||
VL_SET_WQ(lwp, ld);
|
||||
owp[0].aval = lwp[0];
|
||||
|
||||
@@ -98,8 +98,10 @@ void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
Super::closeBase();
|
||||
emitTimeChangeMaybe();
|
||||
if (m_fst) m_fst->close(); // LCOV_EXCL_BR_LINE
|
||||
m_fst = nullptr;
|
||||
if (m_fst) {
|
||||
m_fst->close();
|
||||
VL_DO_CLEAR(delete m_fst, m_fst = nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
|
||||
+406
-465
File diff suppressed because it is too large
Load Diff
@@ -90,23 +90,26 @@ static double _vl_dbase_chi_square(IData& seedr, int32_t deg_of_free) VL_MT_SAFE
|
||||
return x;
|
||||
}
|
||||
|
||||
static IData _vl_round_to_int32(double r) VL_PURE {
|
||||
int32_t i;
|
||||
if (r >= 0) {
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
} else {
|
||||
r = -r;
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
i = -i;
|
||||
}
|
||||
return static_cast<IData>(i);
|
||||
}
|
||||
|
||||
IData VL_DIST_CHI_SQUARE(IData& seedr, IData udf) VL_MT_SAFE {
|
||||
const int32_t df = static_cast<int32_t>(udf);
|
||||
if (VL_UNLIKELY(df <= 0)) {
|
||||
// Chi_square distribution must have positive degree of freedom
|
||||
return 0;
|
||||
}
|
||||
double r = _vl_dbase_chi_square(seedr, df);
|
||||
int32_t i;
|
||||
if (r >= 0) {
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
} else {
|
||||
r = -r; // LCOV_EXCL_LINE
|
||||
i = static_cast<int32_t>(
|
||||
r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding // LCOV_EXCL_LINE
|
||||
i = -i; // LCOV_EXCL_LINE
|
||||
}
|
||||
return static_cast<IData>(i);
|
||||
const double r = _vl_dbase_chi_square(seedr, df);
|
||||
return _vl_round_to_int32(r);
|
||||
}
|
||||
|
||||
IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE {
|
||||
@@ -120,16 +123,8 @@ IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE {
|
||||
for (int32_t i = 1; i <= k; ++i) x = x * _vl_dbase_uniform(seedr, 0, 1);
|
||||
const double a = static_cast<double>(mean);
|
||||
const double b = static_cast<double>(k);
|
||||
double r = -a * log(x) / b;
|
||||
int32_t i;
|
||||
if (r >= 0) {
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
} else {
|
||||
r = -r;
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
i = -i;
|
||||
}
|
||||
return static_cast<IData>(i);
|
||||
const double r = -a * log(x) / b;
|
||||
return _vl_round_to_int32(r);
|
||||
}
|
||||
|
||||
IData VL_DIST_EXPONENTIAL(IData& seedr, IData umean) VL_MT_SAFE {
|
||||
@@ -139,31 +134,15 @@ IData VL_DIST_EXPONENTIAL(IData& seedr, IData umean) VL_MT_SAFE {
|
||||
return 0;
|
||||
}
|
||||
int32_t i;
|
||||
double r = _vl_dbase_exponential(seedr, mean);
|
||||
if (r >= 0) {
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
} else {
|
||||
r = -r; // LCOV_EXCL_LINE
|
||||
i = static_cast<int32_t>(
|
||||
r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding // LCOV_EXCL_LINE
|
||||
i = -i; // LCOV_EXCL_LINE
|
||||
}
|
||||
return static_cast<IData>(i);
|
||||
const double r = _vl_dbase_exponential(seedr, mean);
|
||||
return _vl_round_to_int32(r);
|
||||
}
|
||||
|
||||
IData VL_DIST_NORMAL(IData& seedr, IData umean, IData usd) VL_MT_SAFE {
|
||||
const int32_t mean = static_cast<int32_t>(umean);
|
||||
const int32_t sd = static_cast<int32_t>(usd);
|
||||
double r = _vl_dbase_normal(seedr, mean, sd);
|
||||
int32_t i;
|
||||
if (r >= 0) {
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
} else {
|
||||
r = -r;
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
i = -i;
|
||||
}
|
||||
return static_cast<IData>(i);
|
||||
const double r = _vl_dbase_normal(seedr, mean, sd);
|
||||
return _vl_round_to_int32(r);
|
||||
}
|
||||
|
||||
IData VL_DIST_POISSON(IData& seedr, IData umean) VL_MT_SAFE {
|
||||
@@ -192,16 +171,8 @@ IData VL_DIST_T(IData& seedr, IData udf) VL_MT_SAFE {
|
||||
const double chi2 = _vl_dbase_chi_square(seedr, df);
|
||||
const double div = chi2 / static_cast<double>(df);
|
||||
const double root = std::sqrt(div);
|
||||
double r = _vl_dbase_normal(seedr, 0, 1) / root;
|
||||
int32_t i;
|
||||
if (r >= 0) {
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
} else {
|
||||
r = -r;
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
i = -i;
|
||||
}
|
||||
return static_cast<IData>(i);
|
||||
const double r = _vl_dbase_normal(seedr, 0, 1) / root;
|
||||
return _vl_round_to_int32(r);
|
||||
}
|
||||
|
||||
IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE {
|
||||
|
||||
@@ -149,7 +149,7 @@ static_assert(std::is_trivially_destructible<VlExecutionRecord>::value,
|
||||
//=============================================================================
|
||||
// VlExecutionProfiler is for collecting profiling data about model execution
|
||||
|
||||
class VlExecutionProfiler final : public VerilatedVirtualBase {
|
||||
class VlExecutionProfiler final : public VlExecutionProfilerBase {
|
||||
// CONSTANTS
|
||||
|
||||
// In order to try to avoid dynamic memory allocations during the actual profiling phase,
|
||||
@@ -193,8 +193,15 @@ public:
|
||||
t_trace.emplace_back();
|
||||
return t_trace.back();
|
||||
}
|
||||
// Configure profiler (called in beginning of 'eval')
|
||||
void configure();
|
||||
// Record the beginning/end of a section, for the run-time library
|
||||
void sectionPush(const char* namep) override {
|
||||
if (VL_UNLIKELY(m_enabled)) addRecord().sectionPush(namep);
|
||||
}
|
||||
void sectionPop() override {
|
||||
if (VL_UNLIKELY(m_enabled)) addRecord().sectionPop();
|
||||
}
|
||||
// Advance the profiling window, called by the run-time evaluation loop
|
||||
void configure() override;
|
||||
// Setup profiling on a particular thread;
|
||||
void setupThread(uint32_t threadId);
|
||||
// Clear all profiling data
|
||||
|
||||
+1132
-466
File diff suppressed because it is too large
Load Diff
+158
-32
@@ -79,10 +79,14 @@ public:
|
||||
virtual void* datap(int /*idx*/) const { return m_datap; }
|
||||
std::uint32_t randModeIdx() const { return m_randModeIdx; }
|
||||
bool randModeIdxNone() const { return randModeIdx() == std::numeric_limits<unsigned>::max(); }
|
||||
bool set(const std::string& idx, const std::string& val) const;
|
||||
void set(const std::string& idx, const std::string& val) const;
|
||||
virtual void emitGetValue(std::ostream& s) const;
|
||||
virtual void emitExtract(std::ostream& s, int i) const;
|
||||
virtual void emitType(std::ostream& s) const;
|
||||
// Emit the expression referring to element j as a whole (the full var
|
||||
// for scalars, "(select arr idx...)" for array element j). Used by BSAT
|
||||
// to query/re-assert one whole element's value, not a single bit.
|
||||
virtual void emitElement(std::ostream& s, int j) const;
|
||||
// Emit the current runtime value as an SMT bit-vector literal (#b...).
|
||||
// Used by randomize(null) to pin a var to its existing value.
|
||||
virtual void emitConcreteValue(std::ostream& s) const;
|
||||
@@ -104,13 +108,32 @@ public:
|
||||
count_cache[base_name] = count;
|
||||
return count;
|
||||
}
|
||||
bool hasMatchingElements(const ArrayInfoMap& arr_vars, const std::string& base_name) const {
|
||||
return arr_vars.find(base_name + "0") != arr_vars.end();
|
||||
}
|
||||
};
|
||||
// SMT key width per associative-array level (string = 128, integral = 8 * sizeof).
|
||||
template <typename T>
|
||||
struct VlRandomAssocKeyWidths final {
|
||||
static void push(std::vector<size_t>&) {}
|
||||
};
|
||||
template <typename T_Key, typename T_Value>
|
||||
struct VlRandomAssocKeyWidths<VlAssocArray<T_Key, T_Value>> final {
|
||||
static void push(std::vector<size_t>& widths) {
|
||||
widths.push_back(std::is_same<T_Key, std::string>::value ? 128 : sizeof(T_Key) * 8);
|
||||
VlRandomAssocKeyWidths<T_Value>::push(widths);
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
class VlRandomArrayVarTemplate final : public VlRandomVar {
|
||||
// Static key widths per level for the empty-array declaration fallback
|
||||
const std::vector<size_t> m_fallbackIdxWidths;
|
||||
|
||||
public:
|
||||
VlRandomArrayVarTemplate(const std::string& name, int width, void* datap, int dimension,
|
||||
std::uint32_t randModeIdx)
|
||||
: VlRandomVar{name, width, datap, dimension, randModeIdx} {}
|
||||
std::uint32_t randModeIdx, const std::vector<size_t>& idxWidths = {})
|
||||
: VlRandomVar{name, width, datap, dimension, randModeIdx}
|
||||
, m_fallbackIdxWidths{idxWidths} {}
|
||||
void* datap(int idx) const override {
|
||||
const std::string indexed_name = name() + std::to_string(idx);
|
||||
const auto it = m_arrVarsRefp->find(indexed_name);
|
||||
@@ -175,7 +198,13 @@ public:
|
||||
}
|
||||
} else {
|
||||
if (dimension() > 0) {
|
||||
for (int i = 0; i < dimension(); ++i) s << "(Array (_ BitVec 32) ";
|
||||
// Empty array: declare from the static key widths, not a 32-bit default.
|
||||
for (int i = 0; i < dimension(); ++i) {
|
||||
const size_t idxWidth = i < static_cast<int>(m_fallbackIdxWidths.size())
|
||||
? m_fallbackIdxWidths[i]
|
||||
: 32;
|
||||
s << "(Array (_ BitVec " << idxWidth << ") ";
|
||||
}
|
||||
s << "(_ BitVec " << width() << ")";
|
||||
for (int i = 0; i < dimension(); ++i) s << ")";
|
||||
} else {
|
||||
@@ -188,6 +217,20 @@ public:
|
||||
const int elementCounts = countMatchingElements(*m_arrVarsRefp, name());
|
||||
return width() * elementCounts;
|
||||
}
|
||||
void emitElement(std::ostream& s, int j) const override {
|
||||
const std::string indexed_name = name() + std::to_string(j);
|
||||
const auto it = m_arrVarsRefp->find(indexed_name);
|
||||
// Callers take j from totalWidth(), which counts these same keys, so the element
|
||||
// should always be there
|
||||
if (VL_UNCOVERABLE(it == m_arrVarsRefp->end())) {
|
||||
// LCOV_EXCL_START
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
|
||||
return;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
s << ' ';
|
||||
emitSelect(s, it->second->m_indices, it->second->m_idxWidths);
|
||||
} // LCOV_EXCL_BR_LINE
|
||||
void emitExtract(std::ostream& s, int i) const override {
|
||||
const int j = i / width();
|
||||
i = i % width();
|
||||
@@ -205,6 +248,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class VlSolverSession;
|
||||
|
||||
//=============================================================================
|
||||
// Object holding constraints and variable references.
|
||||
class VlRandomizer VL_NOT_FINAL {
|
||||
@@ -217,8 +262,7 @@ class VlRandomizer VL_NOT_FINAL {
|
||||
std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back)
|
||||
// variables
|
||||
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
|
||||
std::vector<std::string> m_unique_arrays;
|
||||
std::map<std::string, uint32_t> m_unique_array_sizes;
|
||||
std::vector<std::string> m_unique_arrays; // Arrays whose elements must be distinct
|
||||
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
|
||||
const VlQueue<CData>* m_static_randmodep = nullptr; // Static rand_mode state (shared)
|
||||
std::unordered_set<std::string> m_staticVars; // Names of static rand vars
|
||||
@@ -230,17 +274,88 @@ class VlRandomizer VL_NOT_FINAL {
|
||||
std::vector<std::pair<std::string, std::string>>
|
||||
m_solveBefore; // Solve-before ordering pairs (beforeVar, afterVar)
|
||||
bool m_checkOnly = false; // Set for randomize(null)
|
||||
bool isFrozenVar(const std::string& name,
|
||||
const VlRandomVar& var) const; // true if this var is currently frozen
|
||||
bool hasFrozenVar() const; // true if any var is currently rand_mode(0)-frozen
|
||||
|
||||
// PRIVATE METHODS
|
||||
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
|
||||
bool parseSolution(std::iostream& os, bool log = false);
|
||||
bool checkSat(std::iostream& os);
|
||||
// Fetch the model and write it into the registered variables.
|
||||
bool applyModel(VlSolverSession& sess);
|
||||
bool parseModel(std::istream& is, size_t requested);
|
||||
// Assert the maximal compatible soft-constraint set onto the open session.
|
||||
void relaxSoftConstraints(VlSolverSession& sess);
|
||||
// Indices of the "a<N>" literals named by (get-unsat-assumptions).
|
||||
std::vector<int> readUnsatAssumptions(std::iostream& os);
|
||||
std::vector<int> readUnsatAssumptions(VlSolverSession& sess);
|
||||
void reportUnsatSetup(VlSolverSession& sess, const std::vector<std::string>& uniqueExprs);
|
||||
void reportUnsatCore(VlSolverSession& sess);
|
||||
void emitRandcExclusions(std::ostream& os) const; // Emit randc exclusion constraints
|
||||
void recordRandcValues(); // Record solved randc values for future exclusion
|
||||
size_t hashConstraints() const;
|
||||
bool nextPhased(VlRNG& rngr); // Phased solving for solve...before
|
||||
size_t hashConstraints(const std::vector<std::string>& extras) const;
|
||||
bool nextRandomize(VlRNGReseeds& rngr, bool checkOnly);
|
||||
// "(distinct ...)" expression per unique-constrained array
|
||||
std::vector<std::string> buildUniqueExprs() const;
|
||||
void emitDefines(std::ostream& os) const;
|
||||
void emitDeclares(std::ostream& os, bool pinCurrent) const;
|
||||
void emitAsserts(std::ostream& os, const std::vector<std::string>& extras, bool named) const;
|
||||
bool nextFlat(VlRNG& rngr, VlSolverSession& sess, const std::vector<std::string>& uniqueExprs);
|
||||
|
||||
// --- UniGen2 (a near-uniform constrained randomization sampler) fields ---
|
||||
// Implementation based on the following paper:
|
||||
// "On Parallel Scalable Uniform SAT Witness Generation", Chakraborty et al.
|
||||
|
||||
using Witness = std::map<std::string, std::map<int, std::string>>; // One sampled solution
|
||||
|
||||
// Sample one random solution. False if a sample couldn't be produced
|
||||
bool unigen2(VlRNG& rngr, VlSolverSession& sess, const std::vector<std::string>& uniqueExprs);
|
||||
// Find out how finely to cut the solution space, and what cell size to accept
|
||||
bool estimateParameters(VlSolverSession& sess, VlRNG& rngr);
|
||||
// Collect up to `bound` different solutions of whatever is asserted right now.
|
||||
// diversifyRngp adds a randomization step that spreads the solutions apart
|
||||
int bsat(VlSolverSession& sess, size_t bound, std::vector<Witness>& witnesses,
|
||||
VlRNG* diversifyRngp = nullptr);
|
||||
// Add `bits` random XOR equations, which cut the solution space into cells holding
|
||||
// roughly 1/2**bits of all the solutions
|
||||
void unigenXors(std::iostream& os, VlRNG& rngr, int bits);
|
||||
// Draw a cell and refill the batch of solutions from it
|
||||
bool generateSamples(VlSolverSession& sess, VlRNG& rngr);
|
||||
// Copy one solution into the SV variables
|
||||
void writeBackWitness(const Witness& witness);
|
||||
|
||||
// UniGen2: sampling state
|
||||
struct Unigen2State final {
|
||||
std::vector<Witness> loThreshWitnesses; // Consumable batch of solutions: loThresh random
|
||||
// picks out of the cell BSAT enumerated
|
||||
std::vector<std::pair<std::string, int>> bsatOrder; // (var, element) query order
|
||||
bool isLargeSpace = false; // Large solution space (more than 61 * 2^10 solutions)
|
||||
// Below properties are what estimateParameters worked out, kept until the constraints
|
||||
// change so the expensive search is not repeated on every call.
|
||||
bool paramsValid = false; // Whether the values below are set
|
||||
size_t paramHash = 0; // Constraint set they were computed for
|
||||
int hashBits = 0; // How many XOR equations to cut the space with
|
||||
int loThresh = 0; // Smallest usable cell, and how many samples to take
|
||||
int hiThresh = 0; // First cell size counted as too big
|
||||
uint64_t rngReseeds = 0; // rngr.reseeds() as of the last call
|
||||
int lastSuccessI = -1; // Hash-bit count that worked last time, -1 if none
|
||||
};
|
||||
Unigen2State m_ug2;
|
||||
|
||||
void solveDiversity(VlRNG& rngr, VlSolverSession& sess);
|
||||
void solveDiversityPins(VlRNG& rngr, VlSolverSession& sess);
|
||||
void solveDiversityXor(VlRNG& rngr, VlSolverSession& sess);
|
||||
// Layers of solve...before variables in dependency order
|
||||
bool buildSolveLayers(std::vector<std::vector<std::string>>& layersr);
|
||||
const char* phasedLogic() const;
|
||||
bool nextPhased(VlRNG& rngr, VlSolverSession& sess,
|
||||
const std::vector<std::string>& uniqueExprs);
|
||||
bool solvePhases(VlRNG& rngr, VlSolverSession& sess,
|
||||
const std::vector<std::vector<std::string>>& layers,
|
||||
const std::vector<std::string>& uniqueExprs, bool& exhaustedr);
|
||||
bool solvePhaseValues(VlSolverSession& sess, VlRNG& rngr,
|
||||
const std::vector<std::string>& layerVars,
|
||||
std::map<std::string, std::string>& solvedValuesr);
|
||||
bool readPhaseValues(VlSolverSession& sess, std::map<std::string, std::string>& solvedValuesr);
|
||||
bool parsePhaseValues(std::istream& is, std::map<std::string, std::string>& solvedValuesr);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -249,10 +364,10 @@ public:
|
||||
|
||||
// METHODS
|
||||
// Finds the next solution satisfying the constraints
|
||||
bool next(VlRNG& rngr);
|
||||
bool next(VlRNGReseeds& rngr);
|
||||
// Validate the constraints against the current runtime values of every
|
||||
// registered rand variable without picking new ones.
|
||||
bool next_check_only(VlRNG& rngr);
|
||||
bool next_check_only(VlRNGReseeds& rngr);
|
||||
|
||||
// --- Process the key for associative array ---
|
||||
|
||||
@@ -377,12 +492,12 @@ public:
|
||||
}
|
||||
|
||||
// Register queue of non-struct types
|
||||
template <typename T>
|
||||
template <typename T, size_t N_MaxSize>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlQueue<T>& var, int width, const char* name, int dimension,
|
||||
write_var(VlQueue<T, N_MaxSize>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (m_vars.find(name) == m_vars.end()) {
|
||||
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T>>>(
|
||||
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T, N_MaxSize>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
}
|
||||
if (dimension > 0) {
|
||||
@@ -394,9 +509,9 @@ public:
|
||||
}
|
||||
|
||||
// Register queue of structs
|
||||
template <typename T>
|
||||
template <typename T, size_t N_MaxSize>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlQueue<T>& var, int width, const char* name, int dimension,
|
||||
write_var(VlQueue<T, N_MaxSize>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
}
|
||||
@@ -433,9 +548,11 @@ public:
|
||||
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 (m_vars.find(name) == m_vars.end()) {
|
||||
std::vector<size_t> keyWidths;
|
||||
VlRandomAssocKeyWidths<VlAssocArray<T_Key, T_Value>>::push(keyWidths);
|
||||
m_vars[name]
|
||||
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
name, width, &var, dimension, randmodeIdx, keyWidths);
|
||||
}
|
||||
if (dimension > 0) {
|
||||
m_index = 0;
|
||||
@@ -465,11 +582,10 @@ public:
|
||||
++m_index;
|
||||
}
|
||||
|
||||
// This is the "Sender" API for the generated code
|
||||
void rand_unique(const std::string& name, uint32_t size) {
|
||||
m_unique_arrays.push_back(name);
|
||||
m_unique_array_sizes[name] = size;
|
||||
}
|
||||
// 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); }
|
||||
|
||||
// Recursively record all elements in an unpacked array
|
||||
template <typename T, std::size_t N_Depth>
|
||||
@@ -488,8 +604,8 @@ public:
|
||||
}
|
||||
|
||||
// Recursively record all elements in a queue
|
||||
template <typename T>
|
||||
void record_arr_table(VlQueue<T>& var, const std::string& name, int dimension,
|
||||
template <typename T, size_t N_MaxSize>
|
||||
void record_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
if ((dimension > 0) && (var.size() != 0)) {
|
||||
idxWidths.push_back(32);
|
||||
@@ -522,7 +638,8 @@ public:
|
||||
idxWidths.push_back(idx_width);
|
||||
indices.insert(indices.end(), integral_index.begin(), integral_index.end());
|
||||
|
||||
record_arr_table(var.at(key), indexed_name, dimension - 1, indices, idxWidths);
|
||||
record_arr_table(var.atWrite(key), indexed_name, dimension - 1, indices,
|
||||
idxWidths);
|
||||
|
||||
// Cleanup indices and widths
|
||||
idxWidths.pop_back();
|
||||
@@ -562,8 +679,8 @@ public:
|
||||
}
|
||||
|
||||
// Recursively process VlQueue of structs
|
||||
template <typename T>
|
||||
void record_struct_arr(VlQueue<T>& var, const std::string& name, int dimension,
|
||||
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) {
|
||||
if ((dimension > 0) && (var.size() != 0)) {
|
||||
idxWidths.push_back(32);
|
||||
@@ -596,7 +713,7 @@ public:
|
||||
|
||||
std::string result = oss.str();
|
||||
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
|
||||
record_struct_arr(var.at(key), name + "." + result, dimension - 1, indices,
|
||||
record_struct_arr(var.atWrite(key), name + "." + result, dimension - 1, indices,
|
||||
idxWidths);
|
||||
}
|
||||
}
|
||||
@@ -666,7 +783,7 @@ public:
|
||||
// Light wrapper for RNG used by std::randomize() to support scope-level randomization.
|
||||
class VlStdRandomizer final : public VlRandomizer {
|
||||
// MEMBERS
|
||||
VlRNG m_rng; // Random number generator
|
||||
VlRNGReseeds m_rng; // Random number generator
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -724,11 +841,20 @@ public:
|
||||
bool basicStdRandomization(VlAssocArray<T_Key, T_Value>& value, size_t width) {
|
||||
T_Key key;
|
||||
for (int exists = value.first(key); exists; exists = value.next(key)) {
|
||||
basicStdRandomization(value.at(key), width);
|
||||
basicStdRandomization(value.atWrite(key), width);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool next() { return VlRandomizer::next(m_rng); }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
//Helper method for dynamic array handling in SMT expressions
|
||||
|
||||
inline std::string vlToSolverHex(const IData& value) {
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << std::setfill('0') << std::setw(8) << value;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
#endif // Guard
|
||||
|
||||
@@ -327,7 +327,7 @@ VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VlAssocArray<T_Key, T
|
||||
T_Value value;
|
||||
os >> index;
|
||||
os >> value;
|
||||
rhs.at(index) = value;
|
||||
rhs.atWrite(index) = value;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -196,7 +196,8 @@ package std;
|
||||
static task killQueue(ref process processQueue[$]);
|
||||
`ifdef VERILATOR_TIMING
|
||||
repeat (processQueue.size()) begin
|
||||
processQueue.pop_front().kill();
|
||||
process p = processQueue.pop_front();
|
||||
if (p) p.kill();
|
||||
end
|
||||
`endif
|
||||
endtask
|
||||
|
||||
@@ -284,13 +284,13 @@ void VlDynamicTriggerScheduler::dump() const {
|
||||
//======================================================================
|
||||
// VlForkSync:: Methods
|
||||
|
||||
void VlProcess::forkSyncOnKill(VlForkSyncState* forkSyncp) {
|
||||
void VlProcess::forkSyncOnKill(std::shared_ptr<VlForkSyncState> forkSyncp) {
|
||||
m_forkSyncOnKillp = forkSyncp;
|
||||
m_forkSyncOnKillDone = false;
|
||||
}
|
||||
|
||||
void VlProcess::forkSyncOnKillClear(VlForkSyncState* forkSyncp) {
|
||||
if (m_forkSyncOnKillp != forkSyncp) return;
|
||||
if (m_forkSyncOnKillp.get() != forkSyncp) return;
|
||||
m_forkSyncOnKillp = nullptr;
|
||||
m_forkSyncOnKillDone = false;
|
||||
}
|
||||
@@ -301,19 +301,15 @@ void VlProcess::state(int s) {
|
||||
m_forkSyncOnKillDone = true;
|
||||
m_state = s;
|
||||
m_forkSyncOnKillp->done();
|
||||
m_forkSyncOnKillp = nullptr;
|
||||
return;
|
||||
}
|
||||
m_state = s;
|
||||
}
|
||||
|
||||
VlForkSyncState::~VlForkSyncState() {
|
||||
for (const VlProcessRef& processp : m_onKillProcessps) processp->forkSyncOnKillClear(this);
|
||||
}
|
||||
|
||||
void VlForkSync::onKill(VlProcessRef process) {
|
||||
if (!process) return;
|
||||
m_state->m_onKillProcessps.emplace_back(process);
|
||||
process->forkSyncOnKill(m_state.get());
|
||||
process->forkSyncOnKill(m_state);
|
||||
}
|
||||
|
||||
void VlForkSyncState::done(const char* filename, int lineno) {
|
||||
@@ -337,16 +333,18 @@ void VlForkSyncState::done(const char* filename, int lineno) {
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VlCoroutine:: Methods
|
||||
// VlPromise:: Methods
|
||||
|
||||
VlCoroutine::VlPromise::~VlPromise() {
|
||||
VlCoroutine VlPromise::get_return_object() { return {this}; }
|
||||
|
||||
VlPromise::~VlPromise() {
|
||||
// Indicate to the return object that the coroutine has finished or been destroyed
|
||||
if (m_corop) m_corop->m_promisep = nullptr;
|
||||
// If there is a continuation, destroy it
|
||||
if (m_continuation) m_continuation.destroy();
|
||||
}
|
||||
|
||||
std::suspend_never VlCoroutine::VlPromise::final_suspend() noexcept {
|
||||
std::suspend_never VlPromise::final_suspend() noexcept {
|
||||
// Indicate to the return object that the coroutine has finished
|
||||
if (m_corop) {
|
||||
m_corop->m_promisep = nullptr;
|
||||
|
||||
+69
-44
@@ -99,11 +99,34 @@ public:
|
||||
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
|
||||
// suspended, another VlCoroutineHandle was created to manage it.
|
||||
|
||||
class VlCoroutine;
|
||||
|
||||
struct VlPromise final {
|
||||
std::coroutine_handle<VlPromise>
|
||||
m_continuation; // Coroutine to resume after this one finishes
|
||||
VlCoroutine* m_corop = nullptr; // Pointer to the coroutine return object
|
||||
bool m_forever = false; // Coroutine suspended forever
|
||||
|
||||
~VlPromise();
|
||||
|
||||
VlCoroutine get_return_object();
|
||||
|
||||
// Never suspend at the start of the coroutine
|
||||
std::suspend_never initial_suspend() const { return {}; }
|
||||
|
||||
// Never suspend at the end of the coroutine (thanks to this, the coroutine will clean up
|
||||
// after itself)
|
||||
std::suspend_never final_suspend() noexcept;
|
||||
|
||||
void unhandled_exception() const { std::abort(); } // LCOV_EXCL_LINE
|
||||
void return_void() const {}
|
||||
};
|
||||
|
||||
class VlCoroutineHandle final {
|
||||
VL_UNCOPYABLE(VlCoroutineHandle);
|
||||
|
||||
// MEMBERS
|
||||
std::coroutine_handle<> m_coro; // The wrapped coroutine handle
|
||||
std::coroutine_handle<VlPromise> m_coro; // The wrapped coroutine handle
|
||||
VlProcessRef m_process; // Data of the suspended process, null if not needed
|
||||
VlFileLineDebug m_fileline;
|
||||
|
||||
@@ -117,7 +140,8 @@ public:
|
||||
, m_process{process} {
|
||||
if (m_process) m_process->state(VlProcess::WAITING);
|
||||
}
|
||||
VlCoroutineHandle(std::coroutine_handle<> coro, VlProcessRef process, VlFileLineDebug fileline)
|
||||
VlCoroutineHandle(std::coroutine_handle<VlPromise> coro, VlProcessRef process,
|
||||
VlFileLineDebug fileline)
|
||||
: m_coro{coro}
|
||||
, m_process{process}
|
||||
, m_fileline{fileline} {
|
||||
@@ -135,10 +159,11 @@ public:
|
||||
// Usually these coroutines should get resumed; we only need to clean up if we destroy a
|
||||
// model with some coroutines suspended
|
||||
if (VL_UNLIKELY(m_coro)) {
|
||||
m_coro.destroy();
|
||||
if (m_process && m_process->state() != VlProcess::KILLED) {
|
||||
if (m_process && m_process->state() != VlProcess::KILLED
|
||||
&& !m_coro.promise().m_forever) {
|
||||
m_process->state(VlProcess::FINISHED);
|
||||
}
|
||||
m_coro.destroy();
|
||||
}
|
||||
}
|
||||
// METHODS
|
||||
@@ -173,7 +198,13 @@ class VlDelayScheduler final {
|
||||
std::vector<VlCoroutineHandle> m_zeroDelayed; // Coroutines waiting for #0
|
||||
// Coroutines that waited for #0 and are being resumed now. As member to avoid reallocations
|
||||
std::vector<VlCoroutineHandle> m_zeroDelayesSwap;
|
||||
|
||||
std::vector<VlCoroutineHandle>
|
||||
m_forevered; // Coroutines, that called wait(0), and are to be freed.
|
||||
// Their callers are freed recursively through m_continuation.
|
||||
//
|
||||
// NOTE: it would be possible to clean up forevered coroutines immedietely without storing
|
||||
// them, but it involves a lot of nasty edge cases. Defering cleanup to end of eval step
|
||||
// simplifies logic a lot by ensuring that whole "coroutine stack" was already suspended.
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit VlDelayScheduler(VerilatedContext& context)
|
||||
@@ -195,6 +226,7 @@ public:
|
||||
}
|
||||
// Are there coroutines to resume in the inactive region after a #0 delay?
|
||||
bool awaitingZeroDelay() const { return !m_context.gotFinish() && !m_zeroDelayed.empty(); }
|
||||
void cleanupForevered() { m_forevered.clear(); };
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
@@ -210,7 +242,7 @@ public:
|
||||
const VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
void await_suspend(std::coroutine_handle<VlPromise> coro) {
|
||||
// Both active delays and fork..join_none #0 are resumed out of the time queue.
|
||||
if (phase != VlDelayPhase::INACTIVE) {
|
||||
queue.emplace(delay, VlCoroutineHandle{coro, process, fileline});
|
||||
@@ -233,6 +265,26 @@ public:
|
||||
m_zeroDelayed, m_context.time() + delay,
|
||||
phase, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
|
||||
// Helper awaitable func for suspending coroutines forever.
|
||||
// Used for constant wait statements.
|
||||
auto waitForever(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) {
|
||||
VL_DEBUG_IF(
|
||||
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
|
||||
struct Awaitable final {
|
||||
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||
std::vector<VlCoroutineHandle>& forevered;
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() { return false; }
|
||||
void await_suspend(std::coroutine_handle<VlPromise> coro) {
|
||||
coro.promise().m_forever = true;
|
||||
forevered.emplace_back(VlCoroutineHandle{coro, process, fileline});
|
||||
}
|
||||
void await_resume() const {} // LCOV_EXCL_LINE
|
||||
};
|
||||
return Awaitable{process, m_forevered, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -280,7 +332,7 @@ public:
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
void await_suspend(std::coroutine_handle<VlPromise> coro) {
|
||||
suspended.emplace_back(coro, process, fileline);
|
||||
}
|
||||
void await_resume() const {}
|
||||
@@ -327,7 +379,7 @@ class VlDynamicTriggerScheduler final {
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
void await_suspend(std::coroutine_handle<VlPromise> coro) {
|
||||
suspended.emplace_back(coro, process, fileline);
|
||||
}
|
||||
void await_resume() const {}
|
||||
@@ -371,16 +423,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlForever is a helper awaitable type for suspending coroutines forever. Used for constant
|
||||
// wait statements.
|
||||
|
||||
struct VlForever final {
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) const { coro.destroy(); }
|
||||
void await_resume() const {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlForkSync is used to manage fork..join and fork..join_any constructs.
|
||||
|
||||
@@ -393,11 +435,9 @@ public:
|
||||
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()
|
||||
std::vector<VlProcessRef> m_onKillProcessps; // Branches registered for kill hooks
|
||||
|
||||
VlForkSyncState() // Construct with a null coroutine handle
|
||||
: m_susp{VlProcessRef{}} {}
|
||||
~VlForkSyncState();
|
||||
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
|
||||
};
|
||||
|
||||
@@ -431,7 +471,7 @@ public:
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() { return state->m_counter == 0; } // Suspend if join still exists
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
void await_suspend(std::coroutine_handle<VlPromise> coro) {
|
||||
state->m_susp = {coro, process, fileline};
|
||||
}
|
||||
void await_resume() const {}
|
||||
@@ -445,31 +485,10 @@ public:
|
||||
// Return value of a coroutine. Used for chaining coroutine suspension/resumption.
|
||||
|
||||
class VlCoroutine final {
|
||||
private:
|
||||
// TYPES
|
||||
struct VlPromise final {
|
||||
std::coroutine_handle<> m_continuation; // Coroutine to resume after this one finishes
|
||||
VlCoroutine* m_corop = nullptr; // Pointer to the coroutine return object
|
||||
|
||||
~VlPromise();
|
||||
|
||||
VlCoroutine get_return_object() { return {this}; }
|
||||
|
||||
// Never suspend at the start of the coroutine
|
||||
std::suspend_never initial_suspend() const { return {}; }
|
||||
|
||||
// Never suspend at the end of the coroutine (thanks to this, the coroutine will clean up
|
||||
// after itself)
|
||||
std::suspend_never final_suspend() noexcept;
|
||||
|
||||
void unhandled_exception() const { std::abort(); }
|
||||
void return_void() const {}
|
||||
};
|
||||
|
||||
public:
|
||||
// MEMBERS
|
||||
VlPromise* m_promisep; // The promise created for this coroutine
|
||||
|
||||
public:
|
||||
// TYPES
|
||||
using promise_type = VlPromise; // promise_type has to be public
|
||||
|
||||
@@ -495,7 +514,13 @@ public:
|
||||
// Suspend the awaiter if the coroutine is suspended (the promise exists)
|
||||
bool await_ready() const noexcept { return !m_promisep; }
|
||||
// Set the awaiting coroutine as the continuation of the current coroutine
|
||||
void await_suspend(std::coroutine_handle<> coro) { m_promisep->m_continuation = coro; }
|
||||
// If the current coroutine is suspended forever, propagate that to the awaiting coroutine
|
||||
// In that case, m_continuation will be used for cleaning up the whole suspended coroutine
|
||||
// stack
|
||||
void await_suspend(std::coroutine_handle<VlPromise> awaiting_coro) {
|
||||
if (m_promisep->m_forever) awaiting_coro.promise().m_forever = true;
|
||||
m_promisep->m_continuation = awaiting_coro;
|
||||
}
|
||||
void await_resume() const noexcept {}
|
||||
};
|
||||
|
||||
|
||||
@@ -449,7 +449,7 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::initLib(const std::string& name) VL_MT_
|
||||
// All of these take a destination pointer where the string will be emitted,
|
||||
// and a value to convert. There are a couple of variants for efficiency.
|
||||
|
||||
static inline void cvtCDataToStr(char* dstp, CData value) {
|
||||
inline void cvtCDataToStr(char* dstp, CData value) {
|
||||
#ifdef VL_HAVE_SSE2
|
||||
// Similar to cvtSDataToStr but only the bottom 8 byte lanes are used
|
||||
const __m128i a = _mm_cvtsi32_si128(value);
|
||||
@@ -471,7 +471,7 @@ static inline void cvtCDataToStr(char* dstp, CData value) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtSDataToStr(char* dstp, SData value) {
|
||||
inline void cvtSDataToStr(char* dstp, SData value) {
|
||||
#ifdef VL_HAVE_SSE2
|
||||
// We want each bit in the 16-bit input value to end up in a byte lane
|
||||
// within the 128-bit XMM register. Note that x86 is little-endian and we
|
||||
@@ -507,7 +507,7 @@ static inline void cvtSDataToStr(char* dstp, SData value) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtIDataToStr(char* dstp, IData value) {
|
||||
inline void cvtIDataToStr(char* dstp, IData value) {
|
||||
#ifdef VL_HAVE_AVX2
|
||||
// Similar to cvtSDataToStr but the bottom 16-bits are processed in the
|
||||
// top half of the YMM registers
|
||||
@@ -526,7 +526,7 @@ static inline void cvtIDataToStr(char* dstp, IData value) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtQDataToStr(char* dstp, QData value) {
|
||||
inline void cvtQDataToStr(char* dstp, QData value) {
|
||||
cvtIDataToStr(dstp, value >> 32);
|
||||
cvtIDataToStr(dstp + 32, value);
|
||||
}
|
||||
|
||||
+46
-14
@@ -214,7 +214,7 @@ public:
|
||||
};
|
||||
static_assert(sizeof(WDataInP) == sizeof(EData*), "WDataInP should be a single pointer");
|
||||
|
||||
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
|
||||
inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
|
||||
|
||||
template <std::size_t N_Words>
|
||||
bool VlWide<N_Words>::operator<(const VlWide<N_Words>& rhs) const VL_PURE {
|
||||
@@ -276,7 +276,7 @@ constexpr IData VL_CLOG2_CE_Q(QData lhs) VL_PURE {
|
||||
// Random
|
||||
|
||||
// Random Number Generator with internal state
|
||||
class VlRNG final {
|
||||
class VlRNG VL_NOT_FINAL {
|
||||
std::array<uint64_t, 2> m_state;
|
||||
|
||||
public:
|
||||
@@ -295,6 +295,22 @@ public:
|
||||
static VlRNG& vl_thread_rng() VL_MT_SAFE;
|
||||
};
|
||||
|
||||
// VlRNG that also counts how often it was reseeded, for randomize() to notice.
|
||||
class VlRNGReseeds final : public VlRNG {
|
||||
uint64_t m_reseeds = 0; // Times the state was set from outside
|
||||
|
||||
public:
|
||||
void srandom(uint64_t n) VL_MT_UNSAFE {
|
||||
VlRNG::srandom(n);
|
||||
++m_reseeds;
|
||||
}
|
||||
void set_randstate(const std::string& state) VL_MT_UNSAFE {
|
||||
VlRNG::set_randstate(state);
|
||||
++m_reseeds;
|
||||
}
|
||||
uint64_t reseeds() const VL_MT_UNSAFE { return m_reseeds; }
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Metadata of processes
|
||||
using VlProcessRef = std::shared_ptr<VlProcess>;
|
||||
@@ -306,7 +322,7 @@ class VlProcess final {
|
||||
int m_state; // Current state of the process
|
||||
VlProcessRef m_parentp = nullptr; // Parent process, if exists
|
||||
std::set<VlProcess*> m_children; // Active child processes
|
||||
VlForkSyncState* m_forkSyncOnKillp
|
||||
std::shared_ptr<VlForkSyncState> m_forkSyncOnKillp
|
||||
= nullptr; // Optional fork..join counter to decrement on kill
|
||||
bool m_forkSyncOnKillDone = false; // Ensure on-kill callback fires only once
|
||||
VlRNG m_rng; // Per-process RNG (IEEE 1800-2023 18.14)
|
||||
@@ -337,6 +353,7 @@ public:
|
||||
|
||||
~VlProcess() {
|
||||
if (m_parentp) m_parentp->detach(this);
|
||||
if (t_currentp == this) t_currentp = m_parentp.get();
|
||||
}
|
||||
|
||||
void attach(VlProcess* childp) { m_children.insert(childp); }
|
||||
@@ -347,13 +364,14 @@ public:
|
||||
void disable() {
|
||||
state(KILLED);
|
||||
disableFork();
|
||||
m_forkSyncOnKillp = nullptr;
|
||||
}
|
||||
void disableFork() {
|
||||
// childp->disable() may resume coroutines and mutate m_children
|
||||
const std::set<VlProcess*> children = m_children;
|
||||
for (VlProcess* childp : children) childp->disable();
|
||||
}
|
||||
void forkSyncOnKill(VlForkSyncState* forkSyncp);
|
||||
void forkSyncOnKill(std::shared_ptr<VlForkSyncState> forkSyncp);
|
||||
void forkSyncOnKillClear(VlForkSyncState* forkSyncp);
|
||||
bool completed() const { return state() == FINISHED || state() == KILLED; }
|
||||
bool completedFork() const {
|
||||
@@ -931,7 +949,7 @@ public:
|
||||
VlQueue min(T_Func with_func) const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend(),
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
[&with_func](const T_Value& a, const T_Value& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
return VlQueue::consV(*it);
|
||||
@@ -945,7 +963,7 @@ public:
|
||||
VlQueue max(T_Func with_func) const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend(),
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
[&with_func](const T_Value& a, const T_Value& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
return VlQueue::consV(*it);
|
||||
@@ -1119,9 +1137,7 @@ public:
|
||||
return 1;
|
||||
}
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(const T_Key& index) {
|
||||
T_Value& atWrite(const T_Key& index) {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
|
||||
@@ -1137,7 +1153,7 @@ public:
|
||||
}
|
||||
// Setting as a chained operation
|
||||
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
||||
at(index) = value;
|
||||
atWrite(index) = value;
|
||||
return *this;
|
||||
}
|
||||
VlAssocArray& setDefault(const T_Value& value) {
|
||||
@@ -1394,7 +1410,7 @@ void VL_READMEM_N(bool hex, int bits, const std::string& filename,
|
||||
QData addr;
|
||||
std::string data;
|
||||
if (rmem.get(addr /*ref*/, data /*ref*/)) {
|
||||
rmem.setData(&(obj.at(addr)), data);
|
||||
rmem.setData(&(obj.atWrite(addr)), data);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -2069,8 +2085,24 @@ struct VlNull final {
|
||||
operator T*() const {
|
||||
return nullptr;
|
||||
}
|
||||
template <class T>
|
||||
bool operator==(T* rhs) const {
|
||||
return !rhs;
|
||||
}
|
||||
template <class T>
|
||||
bool operator==(const T* rhs) const {
|
||||
return !rhs;
|
||||
}
|
||||
};
|
||||
inline bool operator==(const void* ptr, VlNull) { return !ptr; }
|
||||
|
||||
template <class T>
|
||||
inline bool operator==(T* lhs, VlNull) {
|
||||
return !lhs;
|
||||
}
|
||||
template <class T>
|
||||
inline bool operator==(const T* lhs, VlNull) {
|
||||
return !lhs;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog class reference container
|
||||
@@ -2223,7 +2255,7 @@ public:
|
||||
};
|
||||
|
||||
template <typename T_Lhs, typename T_Out>
|
||||
static inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr) {
|
||||
inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr) {
|
||||
if (!in) {
|
||||
outr = VlNull{};
|
||||
return true;
|
||||
@@ -2237,7 +2269,7 @@ static inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr
|
||||
}
|
||||
|
||||
template <typename T_Lhs>
|
||||
static inline bool VL_CAST_DYNAMIC(VlNull, VlClassRef<T_Lhs>& outr) {
|
||||
inline bool VL_CAST_DYNAMIC(VlNull, VlClassRef<T_Lhs>& outr) {
|
||||
outr = VlNull{};
|
||||
return true;
|
||||
}
|
||||
|
||||
+119
-41
@@ -539,7 +539,7 @@ public:
|
||||
for (auto idx : index()) m_fullname += "[" + std::to_string(idx) + "]";
|
||||
return m_fullname.c_str();
|
||||
}
|
||||
void* prevDatap() const { return m_prevDatap; }
|
||||
uint8_t* prevDatap() const { return m_prevDatap; }
|
||||
void* varDatap() const override { return m_varDatap; }
|
||||
void createPrevDatap() {
|
||||
if (VL_UNLIKELY(!m_prevDatap)) {
|
||||
@@ -1027,6 +1027,26 @@ struct VerilatedVpiTimedCbsCmp final {
|
||||
class VerilatedVpiError;
|
||||
void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset);
|
||||
|
||||
// Information about how to access packed array data.
|
||||
// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word
|
||||
// boundaries, in which case m_maskHi != 0.
|
||||
template <typename T>
|
||||
struct VarAccessInfo final {
|
||||
T* m_datap; // Typed pointer to packed array base address
|
||||
size_t m_bitOffset; // Data start location (bit offset)
|
||||
size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only)
|
||||
T m_maskLo; // Access mask for m_datap[m_wordOffset]
|
||||
T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only)
|
||||
};
|
||||
template <typename T>
|
||||
VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount,
|
||||
size_t addOffset);
|
||||
template <typename T>
|
||||
T vl_vpi_get_word_gen(VarAccessInfo<T> info);
|
||||
|
||||
template <typename T>
|
||||
void vl_vpi_put_word_gen(VarAccessInfo<T> info, T word);
|
||||
|
||||
class VerilatedVpiImp final {
|
||||
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maximum callback reason
|
||||
using VpioCbList = std::list<VerilatedVpiCbHolder>;
|
||||
@@ -1169,6 +1189,72 @@ public:
|
||||
s().m_cbCallList.clear();
|
||||
return called;
|
||||
}
|
||||
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->entSize()););
|
||||
if (varop->bitSize() == 1) {
|
||||
T* const prevDatap = reinterpret_cast<T*>(
|
||||
varop->prevDatap()); // Was malloced when we added the callback
|
||||
const VarAccessInfo<T> currInfo
|
||||
= vl_vpi_var_access_info<T>(varop, varop->bitSize(), 0);
|
||||
VarAccessInfo<T> prevInfo = currInfo;
|
||||
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;
|
||||
}
|
||||
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) {
|
||||
switch (varop->varp()->vltype()) {
|
||||
case VLVT_UINT8: return valueDiffersFromPrev<CData>(varop);
|
||||
case VLVT_UINT16: return valueDiffersFromPrev<SData>(varop);
|
||||
case VLVT_UINT32: return valueDiffersFromPrev<IData>(varop);
|
||||
case VLVT_UINT64: return valueDiffersFromPrev<QData>(varop);
|
||||
case VLVT_WDATA:
|
||||
return valueDiffersFromPrev<EData>(varop);
|
||||
// LCOV_EXCL_START - Would require earlier type check to not catch that
|
||||
default:
|
||||
const std::string msg
|
||||
= "Unsupported type (" + std::to_string(varop->varp()->vltype()) + ")";
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
return true;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static void updatePrev(const VerilatedVpioVar* const varop) {
|
||||
if (varop->bitSize() == 1) {
|
||||
const VarAccessInfo<T> currInfo
|
||||
= vl_vpi_var_access_info<T>(varop, varop->bitSize(), 0);
|
||||
VarAccessInfo<T> prevInfo = currInfo;
|
||||
T* const prevDatap = reinterpret_cast<T*>(varop->prevDatap());
|
||||
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);
|
||||
} else {
|
||||
std::memcpy(varop->prevDatap(), varop->varDatap(), varop->entSize());
|
||||
}
|
||||
}
|
||||
static void updatePrev(const VerilatedVpioVar* const varop) {
|
||||
switch (varop->varp()->vltype()) {
|
||||
case VLVT_UINT8: updatePrev<CData>(varop); break;
|
||||
case VLVT_UINT16: updatePrev<SData>(varop); break;
|
||||
case VLVT_UINT32: updatePrev<IData>(varop); break;
|
||||
case VLVT_UINT64: updatePrev<QData>(varop); break;
|
||||
case VLVT_WDATA:
|
||||
updatePrev<EData>(varop);
|
||||
break;
|
||||
// LCOV_EXCL_START - Would require earlier type check to not catch that
|
||||
default:
|
||||
const std::string msg
|
||||
= "Unsupported type (" + std::to_string(varop->varp()->vltype()) + ")";
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
static bool callValueCbs() VL_MT_UNSAFE_ONE {
|
||||
assertOneCheck();
|
||||
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
|
||||
@@ -1187,15 +1273,10 @@ public:
|
||||
VerilatedVpiCbHolder& ho = *it++;
|
||||
VerilatedVpioVar* const varop
|
||||
= reinterpret_cast<VerilatedVpioVar*>(ho.cb_datap()->obj);
|
||||
void* const newDatap = varop->varDatap();
|
||||
void* const prevDatap = varop->prevDatap(); // Was malloced when we added the callback
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
|
||||
varop->fullname(), *(static_cast<CData*>(newDatap)),
|
||||
*(static_cast<CData*>(prevDatap)), newDatap, prevDatap););
|
||||
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
|
||||
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*>(newDatap))););
|
||||
*(static_cast<CData*>(varop->varDatap()))););
|
||||
update.insert(varop);
|
||||
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
||||
(ho.cb_rtnp())(ho.cb_datap());
|
||||
@@ -1203,9 +1284,7 @@ public:
|
||||
}
|
||||
if (was_last) break;
|
||||
}
|
||||
for (const VerilatedVpioVar* const ip : update) {
|
||||
std::memcpy(ip->prevDatap(), ip->varDatap(), ip->entSize());
|
||||
}
|
||||
for (const VerilatedVpioVar* const varop : update) updatePrev(varop);
|
||||
return called;
|
||||
}
|
||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
||||
@@ -2410,7 +2489,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
return vop->castVpiHandle();
|
||||
}
|
||||
default:
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported callback type %s", __func__,
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported callback type '%s'", __func__,
|
||||
VerilatedVpiError::strFromVpiCallbackReason(reason));
|
||||
return nullptr;
|
||||
}
|
||||
@@ -2856,7 +2935,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
|
||||
}
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
|
||||
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
|
||||
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
|
||||
return nullptr;
|
||||
}
|
||||
@@ -2870,7 +2949,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
|
||||
}
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
|
||||
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
|
||||
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
|
||||
return nullptr;
|
||||
}
|
||||
@@ -3031,8 +3110,9 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
|
||||
[[fallthrough]];
|
||||
}
|
||||
default:
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
|
||||
__func__, VerilatedVpiError::strFromVpiProp(property));
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__,
|
||||
"%s: Unsupported property '%s', nothing will be returned", __func__,
|
||||
VerilatedVpiError::strFromVpiProp(property));
|
||||
return vpiUndefined;
|
||||
}
|
||||
}
|
||||
@@ -3195,18 +3275,6 @@ static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Information about how to access packed array data.
|
||||
// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word
|
||||
// boundaries, in which case m_maskHi != 0.
|
||||
template <typename T>
|
||||
struct VarAccessInfo final {
|
||||
T* m_datap; // Typed pointer to packed array base address
|
||||
size_t m_bitOffset; // Data start location (bit offset)
|
||||
size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only)
|
||||
T m_maskLo; // Access mask for m_datap[m_wordOffset]
|
||||
T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only)
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount,
|
||||
size_t addOffset) {
|
||||
@@ -3262,21 +3330,25 @@ VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
|
||||
T vl_vpi_get_word_gen(VarAccessInfo<T> info) {
|
||||
const size_t wordBits = sizeof(T) * 8;
|
||||
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
||||
if (info.m_maskHi)
|
||||
if (info.m_maskHi) {
|
||||
return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset)
|
||||
| ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi)
|
||||
<< (wordBits - info.m_bitOffset));
|
||||
}
|
||||
return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) {
|
||||
const size_t wordBits = sizeof(T) * 8;
|
||||
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);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void vl_vpi_put_word_gen(VarAccessInfo<T> info, T word) {
|
||||
const size_t wordBits = sizeof(T) * 8;
|
||||
if (info.m_maskHi) {
|
||||
info.m_datap[info.m_wordOffset + 1]
|
||||
= (info.m_datap[info.m_wordOffset + 1] & ~info.m_maskHi)
|
||||
@@ -3287,6 +3359,12 @@ void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, s
|
||||
| ((word << info.m_bitOffset) & info.m_maskLo);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds
|
||||
// addOffset: additional read bitoffset
|
||||
QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
|
||||
@@ -3500,7 +3578,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
|
||||
VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
|
||||
return;
|
||||
}
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
|
||||
}
|
||||
|
||||
vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/,
|
||||
@@ -3841,7 +3919,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
__func__, vop->fullname());
|
||||
return nullptr;
|
||||
}
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -4236,13 +4314,13 @@ void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
|
||||
|
||||
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vop->type() != vpiRegArray) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
|
||||
VerilatedVpiError::strFromVpiObjType(vop->type()));
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type '%s' for '%s'", __func__,
|
||||
VerilatedVpiError::strFromVpiObjType(vop->type()), vop->name());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4418,13 +4496,13 @@ void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
|
||||
|
||||
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vop->type() != vpiRegArray) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
|
||||
VerilatedVpiError::strFromVpiObjType(vop->type()));
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle type '%s' for '%s'", __func__,
|
||||
VerilatedVpiError::strFromVpiObjType(vop->type()), vop->name());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+17
-4
@@ -332,10 +332,19 @@
|
||||
|
||||
#ifdef VL_GCOV
|
||||
extern "C" void __gcov_dump();
|
||||
extern "C" void __gcov_reset();
|
||||
// Dump internal code coverage data before e.g. std::abort()
|
||||
# define VL_GCOV_DUMP() __gcov_dump()
|
||||
// Dump, then re-arm dumping; dumping is one-shot, so without the reset a dump
|
||||
// on a nonfatal path would silently discard everything counted after it
|
||||
# define VL_GCOV_DUMP_RESET() \
|
||||
do { \
|
||||
__gcov_dump(); \
|
||||
__gcov_reset(); \
|
||||
} while (false)
|
||||
#else
|
||||
# define VL_GCOV_DUMP()
|
||||
# define VL_GCOV_DUMP_RESET()
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
@@ -368,6 +377,7 @@ extern "C" void __gcov_dump();
|
||||
#define __STDC_FORMAT_MACROS
|
||||
|
||||
// Now that C++ requires these standard types the vl types are deprecated
|
||||
#include <cstddef> // offsetof (used by generated VlVarTableEntry tables)
|
||||
#include <cstdint>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
@@ -460,6 +470,7 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
// Integer size macros
|
||||
|
||||
#define VL_BYTESIZE 8 ///< Bits in a CData / byte
|
||||
#define VL_BYTESIZE_LOG2 3 ///< log2(VL_BYTESIZE)
|
||||
#define VL_SHORTSIZE 16 ///< Bits in a SData / short
|
||||
#define VL_IDATASIZE 32 ///< Bits in an IData / word
|
||||
#define VL_QUADSIZE 64 ///< Bits in a QData / quadword
|
||||
@@ -472,9 +483,9 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
#endif
|
||||
|
||||
/// Return number of bytes argument-number of bits needs (1 bit=1 byte)
|
||||
#define VL_BYTES_I(nbits) (((nbits) + (VL_BYTESIZE - 1)) / VL_BYTESIZE)
|
||||
#define VL_BYTES_I(nbits) (((nbits) + (VL_BYTESIZE - 1)) >> VL_BYTESIZE_LOG2)
|
||||
/// Return Words/EDatas in argument-number of bits needs (1 bit=1 word)
|
||||
#define VL_WORDS_I(nbits) (((nbits) + (VL_EDATASIZE - 1)) / VL_EDATASIZE)
|
||||
#define VL_WORDS_I(nbits) (((nbits) + (VL_EDATASIZE - 1)) >> VL_EDATASIZE_LOG2)
|
||||
// Number of Words/EDatas a quad requires
|
||||
#define VL_WQ_WORDS_E VL_WORDS_I(VL_QUADSIZE)
|
||||
|
||||
@@ -536,10 +547,10 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
// #defines, to avoid requiring math.h on all compile runs
|
||||
|
||||
#ifdef _MSC_VER
|
||||
static inline double VL_TRUNC(double n) {
|
||||
inline double VL_TRUNC(double n) {
|
||||
return (n < 0) ? std::ceil(n) : std::floor(n);
|
||||
}
|
||||
static inline double VL_ROUND(double n) {
|
||||
inline double VL_ROUND(double n) {
|
||||
return (n < 0) ? std::ceil(n-0.5) : std::floor(n + 0.5);
|
||||
}
|
||||
#else
|
||||
@@ -608,6 +619,8 @@ static inline double VL_ROUND(double n) {
|
||||
# define VL_CPU_RELAX() asm volatile("lr 0,0" ::: "memory")
|
||||
#elif defined(__sparc__)
|
||||
# define VL_CPU_RELAX() asm volatile("rd %%ccr, %%g0" ::: "memory")
|
||||
#elif defined(__wasm__) // Wasm has no spin-wait hint, and no inline asm
|
||||
# define VL_CPU_RELAX()
|
||||
#elif defined(VL_IGNORE_UNKNOWN_ARCH)
|
||||
# define VL_CPU_RELAX()
|
||||
#else
|
||||
|
||||
@@ -65,7 +65,7 @@ double DeltaCpuTime::gettime() VL_MT_SAFE {
|
||||
| static_cast<uint64_t>(lpUserTime.dwHighDateTime) << 32ULL)
|
||||
* 1e-7;
|
||||
}
|
||||
#else
|
||||
#elif !defined(__wasi__) // WASI has no CLOCK_PROCESS_CPUTIME_ID
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
||||
timespec ts;
|
||||
if (0 == clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) // MT-Safe // LCOV_EXCL_BR_LINE
|
||||
@@ -169,17 +169,19 @@ void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
|
||||
}
|
||||
#else
|
||||
// Highly unportable. Sorry
|
||||
// Use VmHWM (peak resident), matching Windows PeakWorkingSetSize and macOS resident_size_max.
|
||||
// VmHWM excludes pages swapped out before the peak; /proc has no peak-(RSS+Swap) counter.
|
||||
std::ifstream is{"/proc/self/status"};
|
||||
if (!is) return;
|
||||
std::string line;
|
||||
uint64_t vmPeak = 0;
|
||||
uint64_t vmHwm = 0;
|
||||
uint64_t vmRss = 0;
|
||||
uint64_t vmSwap = 0;
|
||||
std::string field;
|
||||
while (std::getline(is, line)) {
|
||||
if (line.rfind("VmPeak:", 0) == 0) {
|
||||
if (line.rfind("VmHWM:", 0) == 0) {
|
||||
std::stringstream ss{line};
|
||||
ss >> field >> vmPeak;
|
||||
ss >> field >> vmHwm;
|
||||
} else if (line.rfind("VmRSS:", 0) == 0) {
|
||||
std::stringstream ss{line};
|
||||
ss >> field >> vmRss;
|
||||
@@ -188,7 +190,7 @@ void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
|
||||
ss >> field >> vmSwap;
|
||||
}
|
||||
}
|
||||
peakr = vmPeak * 1024;
|
||||
peakr = vmHwm * 1024;
|
||||
currentr = (vmRss + vmSwap) * 1024;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
# These flags tested against verible-v0.0-4080-ga0a8d8eb
|
||||
|
||||
verible-verilog-format \
|
||||
--inplace \
|
||||
--wrap_end_else_clauses \
|
||||
|
||||
@@ -33,4 +33,4 @@ sphinxcontrib-spelling==8.0.2
|
||||
yamlfix==1.19.1
|
||||
yapf==0.43.0
|
||||
|
||||
git+https://github.com/antmicro/astsee.git
|
||||
git+https://github.com/antmicro/astsee.git@222480a8ec13b312809ea4acc08c81b4c0f4da2f
|
||||
|
||||
+7
-2
@@ -62,6 +62,7 @@ set(HEADERS
|
||||
V3Cast.h
|
||||
V3Cfg.h
|
||||
V3Class.h
|
||||
V3ClassGraph.h
|
||||
V3Clean.h
|
||||
V3Clock.h
|
||||
V3Combine.h
|
||||
@@ -149,6 +150,7 @@ set(HEADERS
|
||||
V3OrderGraph.h
|
||||
V3OrderInternal.h
|
||||
V3OrderMoveGraph.h
|
||||
V3OrderMTaskGraph.h
|
||||
V3Os.h
|
||||
V3PairingHeap.h
|
||||
V3Param.h
|
||||
@@ -156,6 +158,7 @@ set(HEADERS
|
||||
V3ParseImp.h
|
||||
V3PchAstMT.h
|
||||
V3PchAstNoMT.h
|
||||
V3PoolAllocator.h
|
||||
V3PreExpr.h
|
||||
V3PreLex.h
|
||||
V3PreProc.h
|
||||
@@ -170,7 +173,6 @@ set(HEADERS
|
||||
V3Sampled.h
|
||||
V3Sched.h
|
||||
V3Scope.h
|
||||
V3Scoreboard.h
|
||||
V3SenExprBuilder.h
|
||||
V3SenTree.h
|
||||
V3Simulate.h
|
||||
@@ -229,6 +231,7 @@ set(COMMON_SOURCES
|
||||
V3CfgBuilder.cpp
|
||||
V3CfgLiveVariables.cpp
|
||||
V3Class.cpp
|
||||
V3ClassGraph.cpp
|
||||
V3Clean.cpp
|
||||
V3Clock.cpp
|
||||
V3Combine.cpp
|
||||
@@ -319,6 +322,9 @@ set(COMMON_SOURCES
|
||||
V3Order.cpp
|
||||
V3OrderGraphBuilder.cpp
|
||||
V3OrderMoveGraph.cpp
|
||||
V3OrderMTaskContraction.cpp
|
||||
V3OrderMTaskFixHazards.cpp
|
||||
V3OrderMTaskGraph.cpp
|
||||
V3OrderParallel.cpp
|
||||
V3OrderProcessDomains.cpp
|
||||
V3OrderSerial.cpp
|
||||
@@ -346,7 +352,6 @@ set(COMMON_SOURCES
|
||||
V3SchedUtil.cpp
|
||||
V3SchedVirtIface.cpp
|
||||
V3Scope.cpp
|
||||
V3Scoreboard.cpp
|
||||
V3Slice.cpp
|
||||
V3Split.cpp
|
||||
V3SplitVar.cpp
|
||||
|
||||
+7
-1
@@ -105,6 +105,9 @@ CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
|
||||
#CPPFLAGS += -DVL_ALLOC_RANDOM_CHECKS # To allow --debug-new-random
|
||||
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
|
||||
CPPFLAGS += -MP # Only works on recent GCC versions
|
||||
# Replaces VNUser's union with a std::variant to validate int vs void* usage
|
||||
# Ignored unless the build is C++17 or newer
|
||||
#CPPFLAGS += -DVL_USER_TYPE_CHECKS
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
CPPFLAGS += -W -Wall $(CFG_CXXFLAGS_WEXTRA) $(CFG_CXXFLAGS_SRC) -Werror
|
||||
#CPPFLAGS += -pedantic-errors
|
||||
@@ -244,6 +247,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3CfgBuilder.o \
|
||||
V3CfgLiveVariables.o \
|
||||
V3Class.o \
|
||||
V3ClassGraph.o \
|
||||
V3Clean.o \
|
||||
V3Clock.o \
|
||||
V3Combine.o \
|
||||
@@ -309,6 +313,9 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3Order.o \
|
||||
V3OrderGraphBuilder.o \
|
||||
V3OrderMoveGraph.o \
|
||||
V3OrderMTaskContraction.o \
|
||||
V3OrderMTaskFixHazards.o \
|
||||
V3OrderMTaskGraph.o \
|
||||
V3OrderParallel.o \
|
||||
V3OrderProcessDomains.o \
|
||||
V3OrderSerial.o \
|
||||
@@ -330,7 +337,6 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3SchedUtil.o \
|
||||
V3SchedVirtIface.o \
|
||||
V3Scope.o \
|
||||
V3Scoreboard.o \
|
||||
V3Slice.o \
|
||||
V3Split.o \
|
||||
V3SplitVar.o \
|
||||
|
||||
+31
-3
@@ -34,6 +34,7 @@
|
||||
#include "V3Graph.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
@@ -670,6 +671,32 @@ public:
|
||||
//######################################################################
|
||||
// Pass 2: inject automatic sample() calls for covergroup instances
|
||||
|
||||
class CovergroupEventBindVisitor final : public VNVisitor {
|
||||
AstVarScope* const m_instancep; // Variable scope for the covergroup instance being sampled
|
||||
std::unordered_set<const AstVar*> m_memberps; // Non-static variables in the covergroup class
|
||||
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (!m_memberps.count(nodep->varp())) return;
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstMemberSel* const selp
|
||||
= new AstMemberSel{fl, new AstVarRef{fl, m_instancep, VAccess::READ}, nodep->varp()};
|
||||
selp->access(nodep->access());
|
||||
nodep->replaceWith(selp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
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);
|
||||
});
|
||||
iterate(eventp);
|
||||
}
|
||||
~CovergroupEventBindVisitor() override = default;
|
||||
};
|
||||
|
||||
class CovergroupInjectVisitor final : public VNVisitor {
|
||||
// NODE STATE (set by CovergroupCollectVisitor, consumed here)
|
||||
// AstClass::user1p() -> AstCFunc*. The sample() CFunc for this covergroup class
|
||||
@@ -719,10 +746,11 @@ class CovergroupInjectVisitor final : public VNVisitor {
|
||||
cmethodCallp->dtypeSetVoid();
|
||||
cmethodCallp->argTypes("vlSymsp");
|
||||
|
||||
// Clone the sensitivity for this active block.
|
||||
// V3Scope has already resolved all VarRefs in eventp, so the clone
|
||||
// inherits correct varScopep values with no fixup needed.
|
||||
// Clone the sensitivity for this active block. References to covergroup members need
|
||||
// to select through this particular instance; all other VarRefs retain the VarScopes
|
||||
// resolved by V3Scope.
|
||||
AstSenTree* senTreep = eventp->cloneTree(false);
|
||||
CovergroupEventBindVisitor{senTreep, classp, nodep};
|
||||
|
||||
// Get or create the AstActive node for this sensitivity
|
||||
// senTreep is a template used by getActive() which clones it into the AstActive;
|
||||
|
||||
+3
-1
@@ -36,11 +36,13 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
// Active class functions
|
||||
|
||||
class ActiveTopVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// AstVarScope::user1() // bool. Processed
|
||||
|
||||
// STATE
|
||||
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
|
||||
|
||||
// METHODS
|
||||
|
||||
static bool isInitial(AstNode* nodep) {
|
||||
const VNUser1InUse user1InUse;
|
||||
// Return true if no variables that read.
|
||||
|
||||
+254
-36
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "V3AstUserAllocator.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
@@ -89,6 +90,58 @@ public:
|
||||
explicit DefaultDisablePropagateVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
||||
// Lower a sequence used as an event control ('@seq', IEEE 1800-2023 9.4.2.4) into a
|
||||
// synthesized event fired by an internal 'cover sequence' on each end-of-match
|
||||
class SeqEventLowerVisitor final : public VNVisitor {
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
V3UniqueNames m_names{"__VseqEvent"}; // Synthesized event names
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstSenItem* nodep) override {
|
||||
AstFuncRef* const funcrefp = VN_CAST(nodep->sensp(), FuncRef);
|
||||
if (funcrefp && VN_IS(funcrefp->taskp(), Sequence)) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstVar* const eventp = new AstVar{flp, VVarType::MODULETEMP, m_names.get(nodep),
|
||||
m_modp->findBasicDType(VBasicDTypeKwd::EVENT)};
|
||||
eventp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
m_modp->addStmtsp(eventp);
|
||||
v3Global.setHasEvents();
|
||||
funcrefp->unlinkFrBack();
|
||||
nodep->sensp(new AstVarRef{flp, eventp, VAccess::READ});
|
||||
const bool automaticActual = funcrefp->exists([](const AstNodeVarRef* refp) {
|
||||
return refp->varp() && refp->varp()->lifetime().isAutomatic();
|
||||
});
|
||||
if (automaticActual) {
|
||||
nodep->v3error("Arguments to a sequence used as an event control must be"
|
||||
" static (IEEE 1800-2023 9.4.2.4)");
|
||||
VN_AS(funcrefp->taskp(), Sequence)->isReferenced(false);
|
||||
VL_DO_DANGLING(pushDeletep(funcrefp), funcrefp);
|
||||
return;
|
||||
}
|
||||
AstFireEvent* const firep
|
||||
= new AstFireEvent{flp, new AstVarRef{flp, eventp, VAccess::WRITE}, false};
|
||||
AstCover* const coverp
|
||||
= new AstCover{flp, new AstPropSpec{flp, nullptr, nullptr, funcrefp}, firep,
|
||||
VAssertType::CONCURRENT};
|
||||
coverp->isCoverSeq(true);
|
||||
coverp->isSeqEvent(true);
|
||||
m_modp->addStmtsp(coverp);
|
||||
return;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
explicit SeqEventLowerVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) {
|
||||
@@ -96,6 +149,11 @@ void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) {
|
||||
{ DefaultDisablePropagateVisitor{nodep}; }
|
||||
}
|
||||
|
||||
void V3AssertCommon::lowerSequenceEvents(AstNetlist* nodep) {
|
||||
{ SeqEventLowerVisitor{nodep}; }
|
||||
V3Global::dumpCheckGlobalTree("assertseqevent", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// AssertDeFutureVisitor
|
||||
// If any AstFuture, then move all non-future varrefs to be one cycle behind,
|
||||
@@ -188,6 +246,41 @@ public:
|
||||
~AssertDeFutureVisitor() = default;
|
||||
};
|
||||
|
||||
// Prepare one default sampled expression.
|
||||
class DefaultSampledValueVisitor final : public VNVisitor {
|
||||
// METHODS
|
||||
static AstConst* newTypeDefault(AstNodeExpr* nodep) {
|
||||
AstConst* const constp
|
||||
= new AstConst{nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()};
|
||||
if (nodep->dtypep()->isFourstate()) constp->num().setAllBitsX();
|
||||
constp->dtypeFrom(nodep);
|
||||
return constp;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstCMethodHard* nodep) override {
|
||||
if (nodep->method() == VCMethod::EVENT_IS_TRIGGERED) {
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
AstVar* const varp = nodep->varp();
|
||||
if (varp->isParam()
|
||||
|| (!varp->isNet() && varp->lifetime().isStatic() && varp->hasUserInit())) {
|
||||
return;
|
||||
}
|
||||
nodep->replaceWith(newTypeDefault(nodep));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
explicit DefaultSampledValueVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// AssertVisitor
|
||||
|
||||
@@ -228,9 +321,17 @@ class AssertVisitor final : public VNVisitor {
|
||||
AstNode* m_failsp = nullptr; // Current fail statement
|
||||
AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion
|
||||
AstFinal* m_finalp = nullptr; // Current final block
|
||||
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
|
||||
VDouble0 m_statLiftedCaseExprs; // Count of purified case expressions
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
V3UniqueNames m_caseTempNames{"__VCase"};
|
||||
// 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;
|
||||
// Source sampled-value history is initialized, unlike internal property-lowering history.
|
||||
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
|
||||
m_modExpr2Sen2DefaultDelayedAlwaysp;
|
||||
// Map from delayed-value AstAlways to its last-sampling-tick time variable
|
||||
std::unordered_map<const AstAlways*, AstVar*> m_delayedAlways2TickTimep;
|
||||
|
||||
// METHODS
|
||||
static string assertCtlGetCall(const char* query, VAssertType type,
|
||||
@@ -326,8 +427,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
AstSampled* newSampledExpr(AstNodeExpr* nodep) {
|
||||
AstSampled* const sampledp = new AstSampled{nodep->fileline(), nodep, nodep->dtypep()};
|
||||
return sampledp;
|
||||
return new AstSampled{nodep->fileline(), nodep, nodep->dtypep(), true};
|
||||
}
|
||||
AstVarRef* newMonitorNumVarRefp(const AstNode* nodep, VAccess access) {
|
||||
if (!m_monitorNumVarp) {
|
||||
@@ -437,12 +537,18 @@ class AssertVisitor final : public VNVisitor {
|
||||
return bodysp;
|
||||
}
|
||||
|
||||
AstVar* createDelayedVar(const std::string& name, AstAlways* alwaysp, AstNodeExpr* exprp) {
|
||||
AstVar* createDelayedVar(const std::string& name, AstAlways* alwaysp, AstNodeExpr* exprp,
|
||||
AstNodeExpr* initp) {
|
||||
FileLine* const flp = exprp->fileline();
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, exprp->dtypep()};
|
||||
// TODO: this lifetime seems nonsene (can't have NBAs to automatics), but is as before
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
m_modp->addStmtsp(varp);
|
||||
// Before enough clock ticks, $past returns the expression's default sampled value.
|
||||
if (initp) {
|
||||
m_modp->addStmtsp(new AstInitialStatic{
|
||||
flp, new AstAssign{flp, new AstVarRef{flp, varp, VAccess::WRITE}, initp}});
|
||||
}
|
||||
++m_statPastVars;
|
||||
// Actually set the delayed value
|
||||
AstNodeExpr* const lhsp = new AstVarRef{flp, varp, VAccess::WRITE};
|
||||
@@ -455,8 +561,10 @@ class AssertVisitor final : public VNVisitor {
|
||||
return varp;
|
||||
}
|
||||
|
||||
AstAlways* getDelayedAlways(AstNodeExpr* exprp, AstSenTree* senTreep) {
|
||||
AstAlways*& alwayspr = m_modExpr2Sen2DelayedAlwaysp[*exprp][*senTreep];
|
||||
AstAlways* getDelayedAlways(AstNodeExpr* exprp, AstNodeExpr* initp, AstSenTree* senTreep) {
|
||||
auto& expr2Sen2Alwaysr
|
||||
= initp ? m_modExpr2Sen2DefaultDelayedAlwaysp : m_modExpr2Sen2DelayedAlwaysp;
|
||||
AstAlways*& alwayspr = expr2Sen2Alwaysr[*exprp][*senTreep];
|
||||
if (!alwayspr) {
|
||||
FileLine* const flp = exprp->fileline();
|
||||
// Create the always block that computes the delayed values
|
||||
@@ -464,36 +572,64 @@ class AssertVisitor final : public VNVisitor {
|
||||
m_modp->addStmtsp(alwayspr);
|
||||
// Create the once-delayed variable
|
||||
const std::string name = "_Vpast_" + cvtToStr(m_modPastNum++) + "_1";
|
||||
AstVar* const varp = createDelayedVar(name, alwayspr, exprp);
|
||||
AstVar* const varp = createDelayedVar(name, alwayspr, exprp, initp);
|
||||
// Add it to delayed variable vector
|
||||
m_delayed(alwayspr).emplace_back(varp);
|
||||
} else {
|
||||
// Reusing exiting, not needed
|
||||
VL_DO_DANGLING(pushDeletep(exprp), exprp);
|
||||
if (initp) VL_DO_DANGLING(pushDeletep(initp), initp);
|
||||
VL_DO_DANGLING(pushDeletep(senTreep), senTreep);
|
||||
}
|
||||
return alwayspr;
|
||||
}
|
||||
|
||||
AstNodeExpr* getPastValue(AstNodeExpr* exprp, AstSenTree* senTreep, uint32_t ticks) {
|
||||
AstNodeExpr* getPastValue(AstNodeExpr* exprp, AstNodeExpr* initp, AstSenTree* senTreep,
|
||||
uint32_t ticks) {
|
||||
UASSERT_OBJ(ticks > 0, exprp, "Delay must be > 0");
|
||||
AstAlways* const alwaysp = getDelayedAlways(exprp, senTreep);
|
||||
FileLine* const flp = exprp->fileline();
|
||||
AstAlways* const alwaysp
|
||||
= getDelayedAlways(exprp, initp ? initp->cloneTreePure(false) : nullptr, senTreep);
|
||||
AstNodeExpr* const resultp = pastValueRef(alwaysp, flp, ticks, initp);
|
||||
if (initp) VL_DO_DANGLING(pushDeletep(initp), initp);
|
||||
return resultp;
|
||||
}
|
||||
|
||||
AstNodeExpr* pastValueRef(AstAlways* alwaysp, FileLine* flp, uint32_t ticks,
|
||||
AstNodeExpr* initp) {
|
||||
std::vector<AstVar*>& delayedr = m_delayed(alwaysp);
|
||||
// Ensure the required delay exists
|
||||
while (delayedr.size() < ticks) {
|
||||
AstVar* const firstp = delayedr.front();
|
||||
FileLine* const flp = firstp->fileline();
|
||||
FileLine* const varFlp = firstp->fileline();
|
||||
// Create once more delayed value
|
||||
std::string name = firstp->name();
|
||||
name.resize(name.size() - 1);
|
||||
name += std::to_string(delayedr.size() + 1);
|
||||
AstNodeExpr* const prevp = new AstVarRef{flp, delayedr.back(), VAccess::READ};
|
||||
AstVar* const varp = createDelayedVar(name, alwaysp, prevp);
|
||||
AstNodeExpr* const prevp = new AstVarRef{varFlp, delayedr.back(), VAccess::READ};
|
||||
AstVar* const varp = createDelayedVar(name, alwaysp, prevp,
|
||||
initp ? initp->cloneTreePure(false) : nullptr);
|
||||
// Add it to delayed variable vector
|
||||
delayedr.emplace_back(varp);
|
||||
}
|
||||
// Return a reference to the appropriately delayed variable
|
||||
return new AstVarRef{exprp->fileline(), delayedr.at(ticks - 1), VAccess::READ};
|
||||
return new AstVarRef{flp, delayedr.at(ticks - 1), VAccess::READ};
|
||||
}
|
||||
|
||||
// Time of the pipeline's last sampling tick, for end-of-simulation readers
|
||||
AstVar* getPastTickTimeVar(AstAlways* alwaysp) {
|
||||
AstVar*& varpr = m_delayedAlways2TickTimep[alwaysp];
|
||||
if (!varpr) {
|
||||
FileLine* const flp = alwaysp->fileline();
|
||||
varpr = new AstVar{flp, VVarType::MODULETEMP,
|
||||
"_Vpast_" + cvtToStr(m_modPastNum++) + "_t",
|
||||
m_modp->findUInt64DType()};
|
||||
m_modp->addStmtsp(varpr);
|
||||
AstNodeExpr* const timep = new AstCExpr{flp, "vlSymsp->_vm_contextp__->time()", 64};
|
||||
alwaysp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, varpr, VAccess::WRITE}, timep});
|
||||
}
|
||||
return varpr;
|
||||
}
|
||||
|
||||
void visitAssertionIterate(AstNodeCoverOrAssert* nodep, AstNode* failsp) {
|
||||
@@ -542,14 +678,19 @@ class AssertVisitor final : public VNVisitor {
|
||||
|
||||
bool selfDestruct = false;
|
||||
bool passspGated = false;
|
||||
if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
|
||||
const AstCover* const coverp = VN_CAST(nodep, Cover);
|
||||
// A sequence event control is not an assertion directive; no assertion control
|
||||
const bool seqEvent = coverp && coverp->isSeqEvent();
|
||||
if (coverp) {
|
||||
++m_statCover;
|
||||
if (!v3Global.opt.coverageUser()) {
|
||||
if (seqEvent) {
|
||||
// Keep the event-fire action, with no coverage bucket
|
||||
} else if (!v3Global.opt.coverageUser()) {
|
||||
selfDestruct = true;
|
||||
} else {
|
||||
// V3Coverage assigned us a bucket to increment.
|
||||
AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc);
|
||||
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
|
||||
AstCoverInc* const covincp = VN_AS(coverp->coverincsp(), CoverInc);
|
||||
UASSERT_OBJ(covincp, coverp, "Missing AstCoverInc under assertion");
|
||||
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
||||
if (message != "") covincp->declp()->comment(message);
|
||||
if (passsp) {
|
||||
@@ -584,26 +725,51 @@ class AssertVisitor final : public VNVisitor {
|
||||
|
||||
AstNode* propExprp;
|
||||
AstNodeExpr* disablep = nullptr;
|
||||
AstNodeExpr* matchCountp = nullptr;
|
||||
if (AstPropSpec* const specp = VN_CAST(nodep->propp(), PropSpec)) {
|
||||
propExprp = specp->propp()->unlinkFrBack();
|
||||
if (specp->disablep()) disablep = specp->disablep()->unlinkFrBack();
|
||||
matchCountp = specp->matchCountp();
|
||||
} else {
|
||||
propExprp = nodep->propp()->unlinkFrBack();
|
||||
}
|
||||
FileLine* const flp = nodep->fileline();
|
||||
bool passspAlreadyGated = false;
|
||||
if (passsp && VN_IS(passsp, If)) passspAlreadyGated = VN_AS(passsp, If)->user1();
|
||||
if (passsp && !passspGated && !passspAlreadyGated && !VN_IS(propExprp, PExpr)) {
|
||||
if (passsp && !passspGated && !passspAlreadyGated && !VN_IS(propExprp, PExpr)
|
||||
&& !seqEvent) {
|
||||
passsp = newIfAssertPassOn(passsp, nodep->directive(), nodep->userType(),
|
||||
/*vacuous=*/false);
|
||||
}
|
||||
if (failsp && !VN_IS(propExprp, PExpr)) {
|
||||
failsp = newIfAssertFailOn(failsp, nodep->directive(), nodep->userType());
|
||||
}
|
||||
if (coverp && matchCountp && passsp) {
|
||||
// Convert the match count into a loop that decrements a temporary variable until it
|
||||
// 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});
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(
|
||||
new AstLoopTest{flp, loopp, new AstVarRef{flp, remainingp, VAccess::READ}});
|
||||
loopp->addStmtsp(passsp);
|
||||
loopp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, remainingp, VAccess::WRITE},
|
||||
new AstSub{flp, new AstVarRef{flp, remainingp, VAccess::READ},
|
||||
new AstConst{flp, AstConst::WidthedValue{},
|
||||
remainingp->dtypep()->width(), 1}}});
|
||||
replayp->addStmtsp(loopp);
|
||||
passsp = replayp;
|
||||
}
|
||||
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
|
||||
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
|
||||
// Add assertOn check last, for better combining
|
||||
bodysp = newIfAssertOn(bodysp, nodep->directive(), nodep->userType());
|
||||
if (!seqEvent) bodysp = newIfAssertOn(bodysp, nodep->directive(), nodep->userType());
|
||||
if (sentreep) bodysp = new AstAlways{flp, VAlwaysKwd::ALWAYS, sentreep, bodysp};
|
||||
|
||||
if (passsp && !passsp->backp()) VL_DO_DANGLING(pushDeletep(passsp), passsp);
|
||||
@@ -755,6 +921,28 @@ class AssertVisitor final : public VNVisitor {
|
||||
|
||||
//========== Case assertions
|
||||
void visit(AstCase* nodep) override {
|
||||
// Introduce temporary variable for AstCase if needed - it is done here and not in V3Case
|
||||
// because this phase is before V3Scope and V3Case is not. Doing it before V3Scope ensures
|
||||
// that V3Scope will take care of a scope creation
|
||||
// We also need to do it before V3Begin, co that pragmas like `unique0` also work correctly
|
||||
if (!nodep->exprp()->isPure()) {
|
||||
++m_statLiftedCaseExprs;
|
||||
FileLine* const fl = nodep->exprp()->fileline();
|
||||
AstVar* const varp = new AstVar{fl, VVarType::BLOCKTEMP, m_caseTempNames.get(nodep),
|
||||
nodep->exprp()->dtypep()};
|
||||
AstNodeExpr* const origp = nodep->exprp()->unlinkFrBack();
|
||||
nodep->addHereThisAsNext(
|
||||
new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, origp});
|
||||
nodep->exprp(new AstVarRef{fl, varp, VAccess::READ});
|
||||
if (m_ftaskp) {
|
||||
varp->funcLocal(true);
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
m_ftaskp->stmtsp()->addHereThisAsNext(varp);
|
||||
} else {
|
||||
m_modp->stmtsp()->addHereThisAsNext(varp);
|
||||
}
|
||||
VIsCached::clearCacheTree();
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
bool has_default = false;
|
||||
@@ -878,6 +1066,11 @@ class AssertVisitor final : public VNVisitor {
|
||||
|
||||
//========== Past
|
||||
void visit(AstPast* nodep) override {
|
||||
if (!nodep->propertyTiming()) {
|
||||
nodep->initp(nodep->exprp()->cloneTreePure(false));
|
||||
{ DefaultSampledValueVisitor{nodep->initp()}; }
|
||||
}
|
||||
AstNodeExpr* const initp = nodep->initp() ? nodep->initp()->unlinkFrBack() : nullptr;
|
||||
iterateChildren(nodep);
|
||||
uint32_t ticks = 1;
|
||||
if (nodep->ticksp()) {
|
||||
@@ -887,7 +1080,27 @@ class AssertVisitor final : public VNVisitor {
|
||||
}
|
||||
UASSERT_OBJ(ticks >= 1, nodep, "0 tick should have been checked in V3Width");
|
||||
AstNodeExpr* const exprp = newSampledExpr(nodep->exprp()->unlinkFrBack());
|
||||
AstNodeExpr* inp = getPastValue(exprp, nodep->sentreep()->unlinkFrBack(), ticks);
|
||||
AstSenTree* const senTreep = nodep->sentreep()->unlinkFrBack();
|
||||
AstNodeExpr* inp = nullptr;
|
||||
// Deliberately emitted for every ftask $past; calls outside final take the normal stage
|
||||
if (VN_IS(m_procedurep, Final) || m_ftaskp) {
|
||||
// A sampling-tick finish reads one stage deeper (IEEE 1800-2023 16.9.3).
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstAlways* const alwaysp
|
||||
= getDelayedAlways(exprp, initp ? initp->cloneTreePure(false) : nullptr, senTreep);
|
||||
AstNodeExpr* const betweenTicksp = pastValueRef(alwaysp, flp, ticks, initp);
|
||||
AstNodeExpr* const onTickp = pastValueRef(alwaysp, flp, ticks + 1, initp);
|
||||
if (initp) VL_DO_DANGLING(pushDeletep(initp), initp);
|
||||
AstNodeExpr* const onTickCondp = new AstLogAnd{
|
||||
flp, new AstCExpr{flp, "vlSymsp->_vm_contextp__->executingFinal()", 1},
|
||||
new AstEq{flp, new AstVarRef{flp, getPastTickTimeVar(alwaysp), VAccess::READ},
|
||||
new AstCExpr{flp, "vlSymsp->_vm_contextp__->finishPendingTime()", 64}}};
|
||||
AstCond* const condp = new AstCond{flp, onTickCondp, onTickp, betweenTicksp};
|
||||
condp->dtypeFrom(onTickp);
|
||||
inp = condp;
|
||||
} else {
|
||||
inp = getPastValue(exprp, initp, senTreep, ticks);
|
||||
}
|
||||
nodep->replaceWith(inp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
@@ -983,11 +1196,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
= new AstSenItem{fl, VEdgeType::ET_CHANGED,
|
||||
// Clone so get VarRef or VarXRef as needed
|
||||
varrefp->cloneTree(false)};
|
||||
if (!monSenItemsp) {
|
||||
monSenItemsp = senItemp;
|
||||
} else {
|
||||
monSenItemsp->addNext(senItemp);
|
||||
}
|
||||
monSenItemsp = AstNode::addNextNull(monSenItemsp, senItemp);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1047,9 +1256,9 @@ class AssertVisitor final : public VNVisitor {
|
||||
|
||||
bool assertTypeConst = true;
|
||||
if (!nodep->assertTypesp()) {
|
||||
nodep->ctlAssertTypes(VAssertType{ALL_ASSERT_TYPES});
|
||||
nodep->assertTypes(VAssertType{ALL_ASSERT_TYPES});
|
||||
} else if (const AstConst* const assertTypesp = VN_CAST(nodep->assertTypesp(), Const)) {
|
||||
nodep->ctlAssertTypes(VAssertType{assertTypesp->toSInt()});
|
||||
nodep->assertTypes(VAssertType{assertTypesp->toSInt()});
|
||||
} else {
|
||||
assertTypeConst = false;
|
||||
}
|
||||
@@ -1069,9 +1278,9 @@ class AssertVisitor final : public VNVisitor {
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
if (assertTypeConst && nodep->ctlAssertTypes() != ALL_ASSERT_TYPES
|
||||
&& nodep->ctlAssertTypes().containsAny(VAssertType::UNIQUE | VAssertType::UNIQUE0
|
||||
| VAssertType::PRIORITY)) {
|
||||
if (assertTypeConst && nodep->assertTypes() != ALL_ASSERT_TYPES
|
||||
&& nodep->assertTypes().containsAny(VAssertType::UNIQUE | VAssertType::UNIQUE0
|
||||
| VAssertType::PRIORITY)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assert control assertion_type");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
@@ -1079,11 +1288,11 @@ class AssertVisitor final : public VNVisitor {
|
||||
|
||||
bool directiveTypeConst = true;
|
||||
if (!nodep->directiveTypesp()) {
|
||||
nodep->ctlDirectiveTypes(VAssertDirectiveType::ASSERT | VAssertDirectiveType::ASSUME
|
||||
| VAssertDirectiveType::COVER);
|
||||
nodep->directiveTypes(VAssertDirectiveType::ASSERT | VAssertDirectiveType::ASSUME
|
||||
| VAssertDirectiveType::COVER);
|
||||
} else if (const AstConst* const directiveTypesp
|
||||
= VN_CAST(nodep->directiveTypesp(), Const)) {
|
||||
nodep->ctlDirectiveTypes(VAssertDirectiveType{directiveTypesp->toSInt()});
|
||||
nodep->directiveTypes(VAssertDirectiveType{directiveTypesp->toSInt()});
|
||||
} else {
|
||||
directiveTypeConst = false;
|
||||
}
|
||||
@@ -1105,7 +1314,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
} else {
|
||||
newp->add(std::to_string(ALL_ASSERT_TYPES));
|
||||
}
|
||||
newp->add(", " + std::to_string(nodep->ctlDirectiveTypes()) + ");\n");
|
||||
newp->add(", " + std::to_string(nodep->directiveTypes()) + ");\n");
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
@@ -1127,12 +1336,13 @@ class AssertVisitor final : public VNVisitor {
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_modPastNum);
|
||||
VL_RESTORER(m_modStrobeNum);
|
||||
VL_RESTORER(m_modExpr2Sen2DelayedAlwaysp);
|
||||
VL_RESTORER(m_finalp);
|
||||
VL_RESTORER_CLEAR(m_modExpr2Sen2DelayedAlwaysp);
|
||||
VL_RESTORER_CLEAR(m_modExpr2Sen2DefaultDelayedAlwaysp);
|
||||
VL_RESTORER_CLEAR(m_delayedAlways2TickTimep);
|
||||
m_modp = nodep;
|
||||
m_modPastNum = 0;
|
||||
m_modStrobeNum = 0;
|
||||
m_modExpr2Sen2DelayedAlwaysp.clear();
|
||||
m_finalp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -1141,6 +1351,11 @@ class AssertVisitor final : public VNVisitor {
|
||||
m_procedurep = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
VL_RESTORER(m_ftaskp);
|
||||
m_ftaskp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstGenBlock* nodep) override {
|
||||
// This code is needed rather than a visitor in V3Begin,
|
||||
// because V3Assert is called before V3Begin
|
||||
@@ -1180,6 +1395,9 @@ public:
|
||||
V3Stats::addStat("Assertions, $past variables", m_statPastVars);
|
||||
V3Stats::addStat("Assertions, assertOn checks combined", m_statAssertOnCombined);
|
||||
V3Stats::addStat("Assertions, assertOn checks hoisted", m_statAssertOnHoisted);
|
||||
V3Stats::addStat("Assertions, lifted impure case expressions", m_statLiftedCaseExprs);
|
||||
// Rewrites can change purity, e.g. by compiling out assertion statements with --no-assert
|
||||
VIsCached::clearCacheTree();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
class V3AssertCommon final {
|
||||
public:
|
||||
static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
static void lowerSequenceEvents(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
class V3Assert final {
|
||||
|
||||
+957
-572
File diff suppressed because it is too large
Load Diff
+44
-20
@@ -79,6 +79,14 @@ private:
|
||||
|
||||
// METHODS
|
||||
|
||||
static void checkSamplingFuncDType(AstNodeExpr* nodep, const AstNode* exprp) {
|
||||
const AstNodeDType* const dtypep = exprp->dtypep()->skipRefp();
|
||||
if (!dtypep->isIntegralOrPacked()) {
|
||||
nodep->v3error("Expected numeric type, but got a " << dtypep->prettyDTypeNameQ()
|
||||
<< " data type");
|
||||
}
|
||||
}
|
||||
|
||||
AstSenTree* newSenTree(AstNode* nodep, AstSenTree* useTreep = nullptr,
|
||||
AstNodeCoverOrAssert* cassertp = nullptr) {
|
||||
// Create sentree based on clocked or default clock
|
||||
@@ -372,7 +380,7 @@ private:
|
||||
// #1step means the value that is sampled is always the signal's last value
|
||||
// before the clock edge (IEEE 1800-2023 14.4)
|
||||
AstSampled* const sampledp
|
||||
= new AstSampled{flp, exprp->cloneTreePure(false), exprp->dtypep()};
|
||||
= new AstSampled{flp, exprp->cloneTreePure(false), exprp->dtypep(), true};
|
||||
AstAssign* const assignp = new AstAssign{flp, refp, sampledp};
|
||||
m_clockingp->addNextHere(new AstAlways{
|
||||
flp, VAlwaysKwd::ALWAYS,
|
||||
@@ -683,6 +691,7 @@ private:
|
||||
void visit(AstFalling* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
@@ -696,6 +705,7 @@ private:
|
||||
void visit(AstFell* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
@@ -712,11 +722,13 @@ private:
|
||||
void visit(AstFuture* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
AstSenTree* const sentreep = nodep->sentreep();
|
||||
if (sentreep) VL_DO_DANGLING(pushDeletep(sentreep->unlinkFrBack()), sentreep);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
}
|
||||
void visit(AstPast* nodep) override {
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
@@ -774,6 +786,7 @@ private:
|
||||
void visit(AstRising* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
@@ -787,6 +800,7 @@ private:
|
||||
void visit(AstRose* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
@@ -849,7 +863,8 @@ private:
|
||||
|
||||
// Assertion condition check
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
AstNodeExpr* const condp = new AstSampled{flp, nodep->exprp()->unlinkFrBack(), nullptr};
|
||||
AstSampled* const condp
|
||||
= new AstSampled{flp, nodep->exprp()->unlinkFrBack(), nullptr, true};
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, new AstLogNot{flp, condp}});
|
||||
loopp->addStmtsp(new AstEventControl{flp, sentreep, nullptr});
|
||||
|
||||
@@ -916,6 +931,7 @@ private:
|
||||
void visit(AstStable* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
AstSenTree* sentreep = nodep->sentreep();
|
||||
@@ -931,6 +947,7 @@ private:
|
||||
void visit(AstSteady* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
@@ -941,6 +958,10 @@ private:
|
||||
nodep->replaceWith(exprp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstSampled* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->internal()) checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
}
|
||||
|
||||
// Validate repetition count: must be a non-negative elaboration-time constant.
|
||||
// Shared by goto [->N] and nonconsecutive [=N] repetition.
|
||||
@@ -952,7 +973,7 @@ private:
|
||||
nodep->v3error("Repetition count is not an elaboration-time constant"
|
||||
" (IEEE 1800-2023 16.9.2)");
|
||||
VL_DO_DANGLING(pushDeletep(countp), countp);
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalseErroring{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return nullptr;
|
||||
}
|
||||
@@ -960,7 +981,7 @@ private:
|
||||
nodep->v3error("Repetition count must be non-negative"
|
||||
" (IEEE 1800-2023 16.9.2)");
|
||||
VL_DO_DANGLING(pushDeletep(countp), countp);
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalseErroring{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1203,11 +1224,7 @@ private:
|
||||
AstNode* const nextp = stmtp->nextp();
|
||||
if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) {
|
||||
assignp->unlinkFrBack();
|
||||
if (!matchAssignsp) {
|
||||
matchAssignsp = assignp;
|
||||
} else {
|
||||
matchAssignsp->addNext(assignp);
|
||||
}
|
||||
matchAssignsp = AstNode::addNextNull(matchAssignsp, assignp);
|
||||
}
|
||||
stmtp = nextp;
|
||||
}
|
||||
@@ -1233,11 +1250,7 @@ private:
|
||||
AstNodeExpr* const assignRhsp = assignp->rhsp()->unlinkFrBack();
|
||||
AstAssignDly* const dlyp = new AstAssignDly{flp, assignLhsp, assignRhsp};
|
||||
VL_DO_DANGLING(pushDeletep(assignp), assignp);
|
||||
if (!matchAssignsp) {
|
||||
matchAssignsp = dlyp;
|
||||
} else {
|
||||
matchAssignsp->addNext(dlyp);
|
||||
}
|
||||
matchAssignsp = AstNode::addNextNull(matchAssignsp, dlyp);
|
||||
}
|
||||
stmtp = nextp;
|
||||
}
|
||||
@@ -1271,7 +1284,8 @@ private:
|
||||
lhsp
|
||||
= new AstAnd{flp, new AstNot{flp, m_disablep->cloneTreePure(false)}, lhsp};
|
||||
}
|
||||
AstPast* const pastp = new AstPast{flp, lhsp};
|
||||
AstPast* const pastp
|
||||
= new AstPast{flp, lhsp, nullptr, nullptr, /* propertyTiming */ true};
|
||||
pastp->dtypeFrom(lhsp);
|
||||
pastp->sentreep(newSenTree(nodep));
|
||||
condp = pastp;
|
||||
@@ -1295,7 +1309,8 @@ private:
|
||||
lhsp = new AstAnd{flp, new AstNot{flp, m_disablep->cloneTreePure(false)}, lhsp};
|
||||
}
|
||||
|
||||
AstPast* const pastp = new AstPast{flp, lhsp};
|
||||
AstPast* const pastp
|
||||
= new AstPast{flp, lhsp, nullptr, nullptr, /* propertyTiming */ true};
|
||||
pastp->dtypeFrom(lhsp);
|
||||
pastp->sentreep(newSenTree(nodep));
|
||||
AstNodeExpr* const exprp
|
||||
@@ -1312,14 +1327,21 @@ private:
|
||||
!m_pexprp, nodep,
|
||||
"'" << nodep->verilogKwd()
|
||||
<< "' in complex property expression should have been rejected by V3AssertNfa");
|
||||
if (nodep->isStrong()
|
||||
&& (v3Global.opt.timing().isSetFalse() || !v3Global.opt.timing().isSetTrue())) {
|
||||
nodep->v3warn(E_NOTIMING, nodep->verilogKwd() << " requires --timing");
|
||||
nodep->replaceWith(new AstConst{flp, AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
if (nodep->isStrong()) {
|
||||
// p s_until q / p s_until_with q: q must eventually be true. Until then, p must
|
||||
// be true on every sampled tick. For s_until, check q first: when q is true on
|
||||
// this tick, p is not required. For s_until_with, p must be true on the q tick too.
|
||||
AstNodeExpr* const rawLhsp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const rawRhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstSampled* const lhsp = new AstSampled{flp, rawLhsp, rawLhsp->dtypep()};
|
||||
AstSampled* const rhsp = new AstSampled{flp, rawRhsp, rawRhsp->dtypep()};
|
||||
AstSampled* const lhsp = new AstSampled{flp, rawLhsp, rawLhsp->dtypep(), true};
|
||||
AstSampled* const rhsp = new AstSampled{flp, rawRhsp, rawRhsp->dtypep(), true};
|
||||
AstNodeExpr* finalCondp = rhsp->cloneTreePure(false);
|
||||
if (nodep->isOverlapping()) {
|
||||
finalCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), finalCondp};
|
||||
@@ -1466,7 +1488,9 @@ private:
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
if (m_senip && m_senip != nodep->sensesp())
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Only one PSL clock allowed per assertion");
|
||||
if (!nodep->disablep() && m_defaultDisablep) {
|
||||
const AstCover* const coverp = VN_CAST(nodep->backp(), Cover);
|
||||
const bool seqEvent = coverp && coverp->isSeqEvent();
|
||||
if (!nodep->disablep() && m_defaultDisablep && !seqEvent) {
|
||||
nodep->disablep(m_defaultDisablep->condp()->cloneTreePure(true));
|
||||
}
|
||||
m_disablep = nodep->disablep();
|
||||
@@ -1476,7 +1500,7 @@ private:
|
||||
if (!VN_AS(nodep->backp(), NodeCoverOrAssert)->immediate()) {
|
||||
const AstNodeDType* const propDtp = nodep->propp()->dtypep();
|
||||
nodep->propp(new AstSampled{nodep->fileline(), nodep->propp()->unlinkFrBack(),
|
||||
propDtp->dtypep()});
|
||||
propDtp->dtypep(), true});
|
||||
}
|
||||
// cover counts non-vacuous matches only (IEEE 1800-2023 16.15.2), so an
|
||||
// implication antecedent must hold; assert passes vacuously instead.
|
||||
|
||||
+77
-34
@@ -49,26 +49,31 @@ bool VNUser4InUse::s_userBusy = false;
|
||||
|
||||
int AstNodeDType::s_uniqueNum = 0;
|
||||
|
||||
V3AST_VCMETHOD_ITEMDATA_DECL;
|
||||
|
||||
//======================================================================
|
||||
// VCMethod information
|
||||
|
||||
VCMethod VCMethod::arrayMethod(const string& name) {
|
||||
for (const auto& it : s_itemData)
|
||||
if (it.m_name == name) return it.m_e;
|
||||
v3fatalSrc("Not a method name known to VCMethod::s_itemData: '" << name << '\'');
|
||||
for (int i = 0; i < _ENUM_MAX; ++i) {
|
||||
const VCMethod method{i};
|
||||
if (name == method.ascii()) return method;
|
||||
}
|
||||
v3fatalSrc("Not a method name known to VCMethod: '" << name << '\'');
|
||||
return VCMethod{};
|
||||
}
|
||||
void VCMethod::selfTest() {
|
||||
int i = 0;
|
||||
for (const auto& it : s_itemData) {
|
||||
const VCMethod exp{i};
|
||||
UASSERT_STATIC(it.m_e == exp,
|
||||
"VCMethod::s_itemData table rows are out-of-order, starting at row "s
|
||||
+ cvtToStr(i) + " '" + +it.m_name + '\'');
|
||||
++i;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// VNUser
|
||||
|
||||
std::string VNUser::dumpStr(std::string (*fmtAddrp)(const void*)) const {
|
||||
#ifdef VL_USER_TYPE_CHECKS
|
||||
if (const int* const uip = std::get_if<int>(&m_u)) return "#"s + cvtToStr(*uip);
|
||||
if (void* const* const upp = std::get_if<void*>(&m_u)) return fmtAddrp(*upp);
|
||||
return "";
|
||||
#else
|
||||
// Dumps void* representation
|
||||
if (!m_u.up) return "";
|
||||
return fmtAddrp(m_u.up);
|
||||
#endif
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
@@ -1384,10 +1389,15 @@ void AstNode::dumpPtrs(std::ostream& os) const {
|
||||
if (op2p()) os << " op2p=" << cvtToHex(op2p());
|
||||
if (op3p()) os << " op3p=" << cvtToHex(op3p());
|
||||
if (op4p()) os << " op4p=" << cvtToHex(op4p());
|
||||
if (user1p()) os << " user1p=" << cvtToHex(user1p());
|
||||
if (user2p()) os << " user2p=" << cvtToHex(user2p());
|
||||
if (user3p()) os << " user3p=" << cvtToHex(user3p());
|
||||
if (user4p()) os << " user4p=" << cvtToHex(user4p());
|
||||
const auto dumpUser = [&os](const char* prefix, const VNUser& user) {
|
||||
const std::string s
|
||||
= user.dumpStr([](const void* p) -> std::string { return cvtToHex(p); });
|
||||
if (!s.empty()) os << prefix << s;
|
||||
};
|
||||
dumpUser(" user1p=", user1u());
|
||||
dumpUser(" user2p=", user2u());
|
||||
dumpUser(" user3p=", user3u());
|
||||
dumpUser(" user4p=", user4u());
|
||||
if (m_iterpp) {
|
||||
os << " iterpp=" << cvtToHex(m_iterpp);
|
||||
// This may cause address sanitizer failures as iterpp can be stale
|
||||
@@ -1542,14 +1552,14 @@ string AstNode::instanceStr() const {
|
||||
return "";
|
||||
}
|
||||
void AstNode::v3errorEnd(const std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
// Don't look for instance name when warning is disabled.
|
||||
// In case of large number of warnings, this can
|
||||
// take significant amount of time
|
||||
const string instanceStrExtra
|
||||
= m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr();
|
||||
if (!m_fileline) {
|
||||
V3Error::v3errorEnd(str, instanceStrExtra, nullptr);
|
||||
V3Error::v3errorEnd(str, "", nullptr);
|
||||
} else {
|
||||
// Don't look for instance name when warning is disabled.
|
||||
// In case of large number of warnings, this can
|
||||
// take significant amount of time
|
||||
const string instanceStrExtra
|
||||
= m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr();
|
||||
std::ostringstream nsstr;
|
||||
nsstr << str.str();
|
||||
if (debug()) {
|
||||
@@ -1660,7 +1670,7 @@ static const AstNodeDType* computeCastableBase(const AstNodeDType* nodep) {
|
||||
}
|
||||
|
||||
static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDType* fromDtp,
|
||||
const AstNode* fromConstp) {
|
||||
const AstNode* fromConstp, const bool checkIfaceArgCompat) {
|
||||
const VCastable castable = VCastable::UNSUPPORTED;
|
||||
toDtp = toDtp->skipRefToEnump();
|
||||
fromDtp = fromDtp->skipRefToEnump();
|
||||
@@ -1698,22 +1708,52 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp
|
||||
if (upcast) return VCastable::COMPATIBLE;
|
||||
if (downcast) return VCastable::DYNAMIC_CLASS;
|
||||
return VCastable::INCOMPATIBLE;
|
||||
} else if (const AstIfaceRefDType* const toIfp = VN_CAST(toDtp, IfaceRefDType)) {
|
||||
} else if (const AstIfaceRefDType* const toIfp
|
||||
= VN_CAST(checkIfaceArgCompat ? toDtp->elemDTypep(true) : toDtp, IfaceRefDType)) {
|
||||
// Two interface refs are compatible if they point at the same interface
|
||||
// module (and modport, if any). Pointer-equality on the dtype isn't
|
||||
// enough since every cell binding clones the dtype.
|
||||
const AstIfaceRefDType* const fromIfp = VN_CAST(fromDtp, IfaceRefDType);
|
||||
if (fromIfp && toIfp->ifaceViaCellp() == fromIfp->ifaceViaCellp()
|
||||
&& (!toIfp->modportp() || toIfp->modportp() == fromIfp->modportp())) {
|
||||
// Argument compatibility also requires matching virtualness for ref arguments, while
|
||||
// input arguments may bind an unqualified or same-modport source to a virtual target.
|
||||
// Argument compatibility also supports fixed-size arrays of interface references.
|
||||
const AstIfaceRefDType* const fromIfp
|
||||
= VN_CAST(checkIfaceArgCompat ? fromDtp->elemDTypep(true) : fromDtp, IfaceRefDType);
|
||||
if (checkIfaceArgCompat && fromIfp && (toDtp != toIfp || fromDtp != fromIfp)) {
|
||||
const AstUnpackArrayDType* const toArrayp = VN_CAST(toDtp, UnpackArrayDType);
|
||||
const AstUnpackArrayDType* const fromArrayp = VN_CAST(fromDtp, UnpackArrayDType);
|
||||
// IEEE 1800-2023 6.22.2: Equal-sized fixed arrays have equivalent types.
|
||||
if (!toArrayp || !fromArrayp
|
||||
|| toArrayp->elementsConst() != fromArrayp->elementsConst()) {
|
||||
return VCastable::INCOMPATIBLE;
|
||||
}
|
||||
return computeCastableImp(toArrayp->subDTypep(), fromArrayp->subDTypep(), nullptr,
|
||||
checkIfaceArgCompat);
|
||||
}
|
||||
if (!fromIfp || toIfp->ifaceViaCellp() != fromIfp->ifaceViaCellp()) {
|
||||
if (!checkIfaceArgCompat) return castable;
|
||||
return VCastable::INCOMPATIBLE;
|
||||
}
|
||||
const bool sameModport = toIfp->modportp() == fromIfp->modportp();
|
||||
if (!checkIfaceArgCompat) {
|
||||
if (!toIfp->modportp() || sameModport) return VCastable::COMPATIBLE;
|
||||
return castable;
|
||||
}
|
||||
if (toIfp->isVirtual() == fromIfp->isVirtual() && sameModport) {
|
||||
return VCastable::SAMEISH;
|
||||
}
|
||||
// An unqualified interface or virtual interface may bind to a modport-qualified
|
||||
// virtual interface.
|
||||
if (toIfp->isVirtual() && (!fromIfp->modportp() || sameModport)) {
|
||||
return VCastable::COMPATIBLE;
|
||||
}
|
||||
return VCastable::INCOMPATIBLE;
|
||||
}
|
||||
return castable;
|
||||
}
|
||||
|
||||
VCastable AstNode::computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp,
|
||||
const AstNode* fromConstp) {
|
||||
const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp);
|
||||
const AstNode* fromConstp, const bool checkIfaceArgCompat) {
|
||||
const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp, checkIfaceArgCompat);
|
||||
UINFO(9, " castable=" << castable << " for " << toDtp);
|
||||
UINFO(9, " =?= " << fromDtp);
|
||||
if (fromConstp) UINFO(9, " const= " << fromConstp);
|
||||
@@ -1735,12 +1775,15 @@ AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) {
|
||||
if (castable == VCastable::DYNAMIC_CLASS) return node2p->dtypep();
|
||||
}
|
||||
|
||||
AstClassRefDType* classDtypep1 = VN_CAST(node1p->dtypep(), ClassRefDType);
|
||||
AstClassRefDType* classDtypep1 = VN_CAST(node1p->dtypep()->skipRefp(), ClassRefDType);
|
||||
while (classDtypep1) {
|
||||
const VCastable castable = computeCastable(classDtypep1, node2p->dtypep(), node2p);
|
||||
if (castable == VCastable::COMPATIBLE) return classDtypep1;
|
||||
const AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
|
||||
classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr;
|
||||
AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
|
||||
if (!extendsp) break;
|
||||
AstNodeDType* const edtp
|
||||
= extendsp->dtypep() ? extendsp->dtypep() : extendsp->childDTypep();
|
||||
classDtypep1 = VN_AS(edtp->skipRefp(), ClassRefDType);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+62
-2
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "V3Ast__gen_forward_class_decls.h" // From ./astgen
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
@@ -40,6 +41,14 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// VL_USER_TYPE_CHECKS requires C++17 for std::variant. Ignore on older standards
|
||||
#if defined(VL_USER_TYPE_CHECKS) && __cplusplus < 201703L
|
||||
#undef VL_USER_TYPE_CHECKS
|
||||
#endif
|
||||
#ifdef VL_USER_TYPE_CHECKS
|
||||
#include <variant>
|
||||
#endif
|
||||
|
||||
// clang-format off
|
||||
#include "V3AstAttr.h"
|
||||
// clang-format on
|
||||
@@ -137,12 +146,48 @@ class V3GraphVertex;
|
||||
class VSymEnt;
|
||||
|
||||
class VNUser final {
|
||||
// Defining VL_USER_TYPE_CHECKS replaces the VNUser union with a std::variant
|
||||
// which remembers which form of VNUser is being stored to catch mismatched reads.
|
||||
#ifdef VL_USER_TYPE_CHECKS
|
||||
// monostate is an unwritten / cleared slot. It can be read as either form
|
||||
// and yields nullptr/0.
|
||||
std::variant<std::monostate, int, void*> m_u;
|
||||
#else
|
||||
union {
|
||||
void* up;
|
||||
int ui;
|
||||
} m_u;
|
||||
#endif
|
||||
|
||||
public:
|
||||
#ifdef VL_USER_TYPE_CHECKS
|
||||
VNUser() = default;
|
||||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VNUser(int i) {
|
||||
// VNUser{0} represents the monostate
|
||||
if (i) m_u = i;
|
||||
}
|
||||
explicit VNUser(void* p) {
|
||||
// VNUser{nullptr} represents the monostate
|
||||
if (p) m_u = p;
|
||||
}
|
||||
~VNUser() = default;
|
||||
// Casters
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE {
|
||||
if (std::holds_alternative<std::monostate>(m_u)) return nullptr;
|
||||
void* const* const upp = std::get_if<void*>(&m_u);
|
||||
UASSERT_STATIC(upp, "AstNode user() slot written as int, read as pointer");
|
||||
return reinterpret_cast<T>(*upp);
|
||||
}
|
||||
int toInt() const {
|
||||
if (std::holds_alternative<std::monostate>(m_u)) return 0;
|
||||
const int* const uip = std::get_if<int>(&m_u);
|
||||
UASSERT_STATIC(uip, "AstNode user() slot written as pointer, read as int");
|
||||
return *uip;
|
||||
}
|
||||
#else
|
||||
VNUser() = default;
|
||||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
@@ -157,10 +202,14 @@ public:
|
||||
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE {
|
||||
return reinterpret_cast<T>(m_u.up);
|
||||
}
|
||||
int toInt() const { return m_u.ui; }
|
||||
#endif
|
||||
VSymEnt* toSymEnt() const { return to<VSymEnt*>(); }
|
||||
AstNode* toNodep() const VL_MT_SAFE { return to<AstNode*>(); }
|
||||
V3GraphVertex* toGraphVertex() const { return to<V3GraphVertex*>(); }
|
||||
int toInt() const { return m_u.ui; }
|
||||
// Render for dumps without asserting on the form held: "" if unset, "#<int>"
|
||||
// if an int, else the pointer via fmtAddrp
|
||||
std::string dumpStr(std::string (*fmtAddrp)(const void*)) const;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -441,6 +490,8 @@ class AstNode VL_NOT_FINAL {
|
||||
static int s_cloneCntGbl; // Count of which userp is set
|
||||
|
||||
// This member ordering both allows 64 bit alignment and puts associated data together
|
||||
// (under VL_USER_TYPE_CHECKS a VNUser is larger than 64 bits, so this packing no
|
||||
// longer holds; that build trades node size for catching int/pointer confusion)
|
||||
VNUser m_user1u{0}; // Contains any information the user iteration routine wants
|
||||
uint32_t m_user1Cnt = 0; // Mark of when userp was set
|
||||
uint32_t m_user2Cnt = 0; // Mark of when userp was set
|
||||
@@ -730,6 +781,7 @@ public:
|
||||
|
||||
// ACCESSORS for specific types
|
||||
// Alas these can't be virtual or they break when passed a nullptr
|
||||
bool isDisableQueuePushSelfStmt();
|
||||
inline bool isClassHandleValue() const;
|
||||
inline bool isNull() const;
|
||||
inline bool isZero() const;
|
||||
@@ -808,7 +860,7 @@ public:
|
||||
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
|
||||
static VCastable computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp,
|
||||
const AstNode* fromConstp);
|
||||
const AstNode* fromConstp, bool checkIfaceArgCompat = false);
|
||||
static AstNodeDType* getCommonClassTypep(AstNode* nodep1, AstNode* nodep2);
|
||||
|
||||
// METHODS - dump and error
|
||||
@@ -1260,6 +1312,14 @@ public:
|
||||
this->foreach([&count](const AstNode*) { ++count; });
|
||||
return count;
|
||||
}
|
||||
|
||||
// Return true if and only if the tree rooted at this node has more than 'limit' nodes.
|
||||
// Traversal terminates as soon as the result is known, so unlike comparing 'nodeCount',
|
||||
// this is cheap on a large tree.
|
||||
bool isLargerThan(int limit) const {
|
||||
int count = 0;
|
||||
return this->exists([&count, limit](const AstNode*) { return ++count > limit; });
|
||||
}
|
||||
};
|
||||
|
||||
// Forward declarations of specializations defined in V3Ast.cpp
|
||||
|
||||
+277
-251
@@ -299,6 +299,34 @@ constexpr VAssertType::en operator|(VAssertType::en lhs, VAssertType::en rhs) {
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VPropStrength final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
DEFAULT = 0, // Resolve from assertion/coverage context
|
||||
WEAK,
|
||||
STRONG,
|
||||
};
|
||||
enum en m_e;
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VPropStrength(en _e)
|
||||
: m_e{_e} {}
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"default", "weak", "strong"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VPropStrength& lhs, const VPropStrength& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
constexpr bool operator==(const VPropStrength& lhs, VPropStrength::en rhs) {
|
||||
return lhs.m_e == rhs;
|
||||
}
|
||||
constexpr bool operator!=(const VPropStrength& lhs, VPropStrength::en rhs) {
|
||||
return lhs.m_e != rhs;
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VAttrType final {
|
||||
public:
|
||||
// clang-format off
|
||||
@@ -779,137 +807,136 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
|
||||
|
||||
// ######################################################################
|
||||
|
||||
// C++ methods invoked on runtime library data types via AstCMethodHard
|
||||
// clang-format off
|
||||
#define FOR_EACH_CMETHOD(macro) \
|
||||
/* id, method, pure */ \
|
||||
macro(_NONE, "_none", false) \
|
||||
macro(ARRAY_AND, "and", true) \
|
||||
macro(ARRAY_AT, "at", true) \
|
||||
macro(ARRAY_AT_BACK, "atBack", true) \
|
||||
macro(ARRAY_AT_WRITE, "atWrite", true) \
|
||||
macro(ARRAY_FIND, "find", true) \
|
||||
macro(ARRAY_FIND_FIRST, "find_first", true) \
|
||||
macro(ARRAY_FIND_FIRST_INDEX, "find_first_index", true) \
|
||||
macro(ARRAY_FIND_INDEX, "find_index", true) \
|
||||
macro(ARRAY_FIND_LAST, "find_last", true) \
|
||||
macro(ARRAY_FIND_LAST_INDEX, "find_last_index", true) \
|
||||
macro(ARRAY_FIRST, "first", false) \
|
||||
macro(ARRAY_INSIDE, "inside", true) \
|
||||
macro(ARRAY_LAST, "last", false) \
|
||||
macro(ARRAY_MAP, "map", true) \
|
||||
macro(ARRAY_MAX, "max", true) \
|
||||
macro(ARRAY_MIN, "min", true) \
|
||||
macro(ARRAY_NEXT, "next", false) \
|
||||
macro(ARRAY_OR, "or", true) \
|
||||
macro(ARRAY_POP_BACK, "pop_back", false) \
|
||||
macro(ARRAY_POP_FRONT, "pop_front", false) \
|
||||
macro(ARRAY_PREV, "prev", false) \
|
||||
macro(ARRAY_PRODUCT, "product", true) \
|
||||
macro(ARRAY_PUSH_BACK, "push_back", false) \
|
||||
macro(ARRAY_PUSH_FRONT, "push_front", false) \
|
||||
macro(ARRAY_REVERSE, "reverse", false) \
|
||||
macro(ARRAY_RSORT, "rsort", false) \
|
||||
macro(ARRAY_R_AND, "r_and", true) \
|
||||
macro(ARRAY_R_OR, "r_or", true) \
|
||||
macro(ARRAY_R_PRODUCT, "r_product", true) \
|
||||
macro(ARRAY_R_SUM, "r_sum", true) \
|
||||
macro(ARRAY_R_XOR, "r_xor", true) \
|
||||
macro(ARRAY_SHUFFLE, "shuffle", false) \
|
||||
macro(ARRAY_SORT, "sort", false) \
|
||||
macro(ARRAY_SUM, "sum", true) \
|
||||
macro(ARRAY_UNIQUE, "unique", true) \
|
||||
macro(ARRAY_UNIQUE_INDEX, "unique_index", true) \
|
||||
macro(ARRAY_XOR, "xor", true) \
|
||||
macro(ASSOC_CLEAR, "clear", false) \
|
||||
macro(ASSOC_ERASE, "erase", false) \
|
||||
macro(ASSOC_EXISTS, "exists", true) \
|
||||
macro(ASSOC_FIRST, "first", false) \
|
||||
macro(ASSOC_NEXT, "next", false) \
|
||||
macro(ASSOC_SIZE, "size", true) \
|
||||
macro(CLASS_SET_RANDMODE, "set_randmode", false) \
|
||||
macro(DYN_AT_WRITE_APPEND, "atWriteAppend", false) \
|
||||
macro(DYN_AT_WRITE_APPEND_BACK, "atWriteAppendBack", false) \
|
||||
macro(DYN_CLEAR, "clear", false) \
|
||||
macro(DYN_ERASE, "erase", false) \
|
||||
macro(DYN_INSERT, "insert", false) \
|
||||
macro(DYN_POP, "pop", false) \
|
||||
macro(DYN_POP_FRONT, "pop_front", false) \
|
||||
macro(DYN_PUSH, "push", false) \
|
||||
macro(DYN_PUSH_FRONT, "push_front", false) \
|
||||
macro(DYN_RENEW, "renew", false) \
|
||||
macro(DYN_RENEW_COPY, "renew_copy", false) \
|
||||
macro(DYN_RESIZE, "resize", false) \
|
||||
macro(DYN_SIZE, "size", true) \
|
||||
macro(DYN_SLICE, "slice", true) \
|
||||
macro(DYN_SLICE_ASSIGN, "sliceAssign", false) \
|
||||
macro(DYN_SLICE_ASSIGN_BACK_BACK, "sliceAssignBackBack", false) \
|
||||
macro(DYN_SLICE_ASSIGN_FRONT_BACK, "sliceAssignFrontBack", false) \
|
||||
macro(DYN_SLICE_BACK_BACK, "sliceBackBack", true) \
|
||||
macro(DYN_SLICE_FRONT_BACK, "sliceFrontBack", true) \
|
||||
macro(EVENT_CLEAR_FIRED, "clearFired", false) \
|
||||
macro(EVENT_CLEAR_TRIGGERED, "clearTriggered", false) \
|
||||
macro(EVENT_FIRE, "fire", false) \
|
||||
macro(EVENT_IS_FIRED, "isFired", true) \
|
||||
macro(EVENT_IS_TRIGGERED, "isTriggered", true) \
|
||||
macro(FORCE_ADD, "addForce", false) \
|
||||
macro(FORCE_READ, "read", true) \
|
||||
macro(FORCE_READ_INDEX, "readIndex", true) \
|
||||
macro(FORCE_READ_SEL, "readSel", true) \
|
||||
macro(FORCE_RELEASE, "release", false) \
|
||||
macro(FORCE_TOUCH, "touch", false) \
|
||||
macro(FORK_DONE, "done", false) \
|
||||
macro(FORK_INIT, "init", false) \
|
||||
macro(FORK_JOIN, "join", false) \
|
||||
macro(FORK_ON_KILL, "onKill", false) \
|
||||
macro(RANDOMIZER_BASIC_STD_RANDOMIZATION, "basicStdRandomization", false) \
|
||||
macro(RANDOMIZER_CLEARCONSTRAINTS, "clearConstraints", false) \
|
||||
macro(RANDOMIZER_CLEARALL, "clearAll", false) \
|
||||
macro(RANDOMIZER_DISABLE_SOFT, "disable_soft", false) \
|
||||
macro(RANDOMIZER_HARD, "hard", false) \
|
||||
macro(RANDOMIZER_SOFT, "soft", false) \
|
||||
macro(RANDOMIZER_UNIQUE, "rand_unique", false) \
|
||||
macro(RANDOMIZER_MARK_RANDC, "markRandc", false) \
|
||||
macro(RANDOMIZER_SOLVE_BEFORE, "solveBefore", false) \
|
||||
macro(RANDOMIZER_PIN_VAR, "pin_var", false) \
|
||||
macro(RANDOMIZER_WRITE_VAR, "write_var", false) \
|
||||
macro(RANDOMIZER_SET_VAR_DISABLED, "set_var_disabled", false) \
|
||||
macro(RANDOMIZER_CLEAR_VAR_DISABLED, "clear_var_disabled", false) \
|
||||
macro(RANDOMIZER_MARK_VAR_STATIC, "mark_var_static", false) \
|
||||
macro(RANDOMIZER_SET_STATIC_RANDMODE, "set_static_randmode", false) \
|
||||
macro(RNG_GET_RANDSTATE, "__Vm_rng.get_randstate", true) \
|
||||
macro(RNG_SET_RANDSTATE, "__Vm_rng.set_randstate", false) \
|
||||
macro(SCHED_ANY_TRIGGERED, "anyTriggered", false) \
|
||||
macro(SCHED_AWAITING_CURRENT_TIME, "awaitingCurrentTime", true) \
|
||||
macro(SCHED_AWAITING_ZERO_DELAY, "awaitingZeroDelay", true) \
|
||||
macro(SCHED_READY, "ready", false) \
|
||||
macro(SCHED_COMMIT, "commit", false) \
|
||||
macro(SCHED_MOVE_TO_RESUME_QUEUE, "moveToResumeQueue", false) \
|
||||
macro(SCHED_DELAY, "delay", false) \
|
||||
macro(SCHED_DO_POST_UPDATES, "doPostUpdates", false) \
|
||||
macro(SCHED_ENQUEUE, "enqueue", false) \
|
||||
macro(SCHED_EVALUATE, "evaluate", false) \
|
||||
macro(SCHED_EVALUATION, "evaluation", false) \
|
||||
macro(SCHED_POST_UPDATE, "postUpdate", false) \
|
||||
macro(SCHED_RESUME, "resume", false) \
|
||||
macro(SCHED_RESUME_ZERO_DELAY, "resumeZeroDelay", false) \
|
||||
macro(SCHED_RESUMPTION, "resumption", false) \
|
||||
macro(SCHED_TRIGGER, "trigger", false) \
|
||||
macro(SCHED_WAIT_FOREVER, "waitForever", false) \
|
||||
macro(UNPACKED_ASSIGN, "assign", false) \
|
||||
macro(UNPACKED_FILL, "fill", false) \
|
||||
macro(UNPACKED_NEQ, "neq", true)
|
||||
// clang-format on
|
||||
|
||||
class VCMethod final {
|
||||
public:
|
||||
// Entries in this table need to match below VCMethod::s_itemData[] table
|
||||
enum en : uint8_t {
|
||||
_NONE, // Unknown
|
||||
ARRAY_AND,
|
||||
ARRAY_AT,
|
||||
ARRAY_AT_BACK,
|
||||
ARRAY_AT_WRITE,
|
||||
ARRAY_FIND,
|
||||
ARRAY_FIND_FIRST,
|
||||
ARRAY_FIND_FIRST_INDEX,
|
||||
ARRAY_FIND_INDEX,
|
||||
ARRAY_FIND_LAST,
|
||||
ARRAY_FIND_LAST_INDEX,
|
||||
ARRAY_FIRST,
|
||||
ARRAY_INSIDE,
|
||||
ARRAY_LAST,
|
||||
ARRAY_MAP,
|
||||
ARRAY_MAX,
|
||||
ARRAY_MIN,
|
||||
ARRAY_NEXT,
|
||||
ARRAY_OR,
|
||||
ARRAY_POP_BACK,
|
||||
ARRAY_POP_FRONT,
|
||||
ARRAY_PREV,
|
||||
ARRAY_PRODUCT,
|
||||
ARRAY_PUSH_BACK,
|
||||
ARRAY_PUSH_FRONT,
|
||||
ARRAY_REVERSE,
|
||||
ARRAY_RSORT,
|
||||
ARRAY_R_AND,
|
||||
ARRAY_R_OR,
|
||||
ARRAY_R_PRODUCT,
|
||||
ARRAY_R_SUM,
|
||||
ARRAY_R_XOR,
|
||||
ARRAY_SHUFFLE,
|
||||
ARRAY_SORT,
|
||||
ARRAY_SUM,
|
||||
ARRAY_UNIQUE,
|
||||
ARRAY_UNIQUE_INDEX,
|
||||
ARRAY_XOR,
|
||||
ASSOC_CLEAR,
|
||||
ASSOC_ERASE,
|
||||
ASSOC_EXISTS,
|
||||
ASSOC_FIRST,
|
||||
ASSOC_NEXT,
|
||||
ASSOC_SIZE,
|
||||
CLASS_SET_RANDMODE,
|
||||
DYN_AT_WRITE_APPEND,
|
||||
DYN_AT_WRITE_APPEND_BACK,
|
||||
DYN_CLEAR,
|
||||
DYN_ERASE,
|
||||
DYN_INSERT,
|
||||
DYN_POP,
|
||||
DYN_POP_FRONT,
|
||||
DYN_PUSH,
|
||||
DYN_PUSH_FRONT,
|
||||
DYN_RENEW,
|
||||
DYN_RENEW_COPY,
|
||||
DYN_RESIZE,
|
||||
DYN_SIZE,
|
||||
DYN_SLICE,
|
||||
DYN_SLICE_ASSIGN,
|
||||
DYN_SLICE_ASSIGN_BACK_BACK,
|
||||
DYN_SLICE_ASSIGN_FRONT_BACK,
|
||||
DYN_SLICE_BACK_BACK,
|
||||
DYN_SLICE_FRONT_BACK,
|
||||
EVENT_CLEAR_FIRED,
|
||||
EVENT_CLEAR_TRIGGERED,
|
||||
EVENT_FIRE,
|
||||
EVENT_IS_FIRED,
|
||||
EVENT_IS_TRIGGERED,
|
||||
FORCE_ADD,
|
||||
FORCE_READ,
|
||||
FORCE_READ_INDEX,
|
||||
FORCE_READ_SEL,
|
||||
FORCE_RELEASE,
|
||||
FORCE_TOUCH,
|
||||
FORK_DONE,
|
||||
FORK_INIT,
|
||||
FORK_JOIN,
|
||||
FORK_ON_KILL,
|
||||
RANDOMIZER_BASIC_STD_RANDOMIZATION,
|
||||
RANDOMIZER_CLEARCONSTRAINTS,
|
||||
RANDOMIZER_CLEARALL,
|
||||
RANDOMIZER_DISABLE_SOFT,
|
||||
RANDOMIZER_HARD,
|
||||
RANDOMIZER_SOFT,
|
||||
RANDOMIZER_UNIQUE,
|
||||
RANDOMIZER_MARK_RANDC,
|
||||
RANDOMIZER_SOLVE_BEFORE,
|
||||
RANDOMIZER_PIN_VAR,
|
||||
RANDOMIZER_WRITE_VAR,
|
||||
RANDOMIZER_SET_VAR_DISABLED,
|
||||
RANDOMIZER_CLEAR_VAR_DISABLED,
|
||||
RANDOMIZER_MARK_VAR_STATIC,
|
||||
RANDOMIZER_SET_STATIC_RANDMODE,
|
||||
RNG_GET_RANDSTATE,
|
||||
RNG_SET_RANDSTATE,
|
||||
SCHED_ANY_TRIGGERED,
|
||||
SCHED_AWAITING_CURRENT_TIME,
|
||||
SCHED_AWAITING_ZERO_DELAY,
|
||||
SCHED_READY,
|
||||
SCHED_COMMIT,
|
||||
SCHED_MOVE_TO_RESUME_QUEUE,
|
||||
SCHED_DELAY,
|
||||
SCHED_DO_POST_UPDATES,
|
||||
SCHED_ENQUEUE,
|
||||
SCHED_EVALUATE,
|
||||
SCHED_EVALUATION,
|
||||
SCHED_POST_UPDATE,
|
||||
SCHED_RESUME,
|
||||
SCHED_RESUME_ZERO_DELAY,
|
||||
SCHED_RESUMPTION,
|
||||
SCHED_TRIGGER,
|
||||
UNPACKED_ASSIGN,
|
||||
UNPACKED_FILL,
|
||||
UNPACKED_NEQ,
|
||||
_ENUM_MAX // Leave last
|
||||
#define VL_CMETHOD_ID(id, method, pure) id,
|
||||
FOR_EACH_CMETHOD(VL_CMETHOD_ID)
|
||||
#undef VL_CMETHOD_ID
|
||||
_ENUM_MAX // Leave last
|
||||
};
|
||||
|
||||
private:
|
||||
struct Item final {
|
||||
enum en m_e; // Method's enum mnemonic, for checking
|
||||
const char* m_name; // Method name, printed into C++
|
||||
bool m_pure; // Method being called is pure
|
||||
};
|
||||
static Item s_itemData[];
|
||||
|
||||
public:
|
||||
enum en m_e;
|
||||
VCMethod()
|
||||
: m_e{_NONE} {}
|
||||
@@ -919,11 +946,26 @@ public:
|
||||
explicit VCMethod(int _e)
|
||||
: 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 { return s_itemData[m_e].m_name; }
|
||||
bool isPure() const VL_PURE { return s_itemData[m_e].m_pure; }
|
||||
const char* ascii() const VL_PURE {
|
||||
static const char* const values[] = {
|
||||
#define VL_CMETHOD_NAME(id, method, pure) method,
|
||||
FOR_EACH_CMETHOD(VL_CMETHOD_NAME)
|
||||
#undef VL_CMETHOD_NAME
|
||||
"_ENUM_MAX" //
|
||||
};
|
||||
return values[m_e];
|
||||
}
|
||||
bool isPure() const VL_PURE {
|
||||
static const bool values[] = {
|
||||
#define VL_CMETHOD_PURE(id, method, pure) pure,
|
||||
FOR_EACH_CMETHOD(VL_CMETHOD_PURE)
|
||||
#undef VL_CMETHOD_PURE
|
||||
false //
|
||||
};
|
||||
return values[m_e];
|
||||
}
|
||||
// Return array method for given name
|
||||
static VCMethod arrayMethod(const string& name);
|
||||
static void selfTest();
|
||||
};
|
||||
constexpr bool operator==(const VCMethod& lhs, const VCMethod& rhs) { return lhs.m_e == rhs.m_e; }
|
||||
constexpr bool operator==(const VCMethod& lhs, VCMethod::en rhs) { return lhs.m_e == rhs; }
|
||||
@@ -932,127 +974,7 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
|
||||
return os << rhs.ascii();
|
||||
}
|
||||
|
||||
// Entries in this table need to match above VCMethod enum table
|
||||
//
|
||||
// {Mnemonic, C++ method, pure}
|
||||
#define V3AST_VCMETHOD_ITEMDATA_DECL \
|
||||
VCMethod::Item VCMethod::s_itemData[] \
|
||||
= {{_NONE, "_none", false}, \
|
||||
{ARRAY_AND, "and", true}, \
|
||||
{ARRAY_AT, "at", true}, \
|
||||
{ARRAY_AT_BACK, "atBack", true}, \
|
||||
{ARRAY_AT_WRITE, "atWrite", true}, \
|
||||
{ARRAY_FIND, "find", true}, \
|
||||
{ARRAY_FIND_FIRST, "find_first", true}, \
|
||||
{ARRAY_FIND_FIRST_INDEX, "find_first_index", true}, \
|
||||
{ARRAY_FIND_INDEX, "find_index", true}, \
|
||||
{ARRAY_FIND_LAST, "find_last", true}, \
|
||||
{ARRAY_FIND_LAST_INDEX, "find_last_index", true}, \
|
||||
{ARRAY_FIRST, "first", false}, \
|
||||
{ARRAY_INSIDE, "inside", true}, \
|
||||
{ARRAY_LAST, "last", false}, \
|
||||
{ARRAY_MAP, "map", true}, \
|
||||
{ARRAY_MAX, "max", true}, \
|
||||
{ARRAY_MIN, "min", true}, \
|
||||
{ARRAY_NEXT, "next", false}, \
|
||||
{ARRAY_OR, "or", true}, \
|
||||
{ARRAY_POP_BACK, "pop_back", false}, \
|
||||
{ARRAY_POP_FRONT, "pop_front", false}, \
|
||||
{ARRAY_PREV, "prev", false}, \
|
||||
{ARRAY_PRODUCT, "product", true}, \
|
||||
{ARRAY_PUSH_BACK, "push_back", false}, \
|
||||
{ARRAY_PUSH_FRONT, "push_front", false}, \
|
||||
{ARRAY_REVERSE, "reverse", false}, \
|
||||
{ARRAY_RSORT, "rsort", false}, \
|
||||
{ARRAY_R_AND, "r_and", true}, \
|
||||
{ARRAY_R_OR, "r_or", true}, \
|
||||
{ARRAY_R_PRODUCT, "r_product", true}, \
|
||||
{ARRAY_R_SUM, "r_sum", true}, \
|
||||
{ARRAY_R_XOR, "r_xor", true}, \
|
||||
{ARRAY_SHUFFLE, "shuffle", false}, \
|
||||
{ARRAY_SORT, "sort", false}, \
|
||||
{ARRAY_SUM, "sum", true}, \
|
||||
{ARRAY_UNIQUE, "unique", true}, \
|
||||
{ARRAY_UNIQUE_INDEX, "unique_index", true}, \
|
||||
{ARRAY_XOR, "xor", true}, \
|
||||
{ASSOC_CLEAR, "clear", false}, \
|
||||
{ASSOC_ERASE, "erase", false}, \
|
||||
{ASSOC_EXISTS, "exists", true}, \
|
||||
{ASSOC_FIRST, "first", false}, \
|
||||
{ASSOC_NEXT, "next", false}, \
|
||||
{ASSOC_SIZE, "size", true}, \
|
||||
{CLASS_SET_RANDMODE, "set_randmode", false}, \
|
||||
{DYN_AT_WRITE_APPEND, "atWriteAppend", false}, \
|
||||
{DYN_AT_WRITE_APPEND_BACK, "atWriteAppendBack", false}, \
|
||||
{DYN_CLEAR, "clear", false}, \
|
||||
{DYN_ERASE, "erase", false}, \
|
||||
{DYN_INSERT, "insert", false}, \
|
||||
{DYN_POP, "pop", false}, \
|
||||
{DYN_POP_FRONT, "pop_front", false}, \
|
||||
{DYN_PUSH, "push", false}, \
|
||||
{DYN_PUSH_FRONT, "push_front", false}, \
|
||||
{DYN_RENEW, "renew", false}, \
|
||||
{DYN_RENEW_COPY, "renew_copy", false}, \
|
||||
{DYN_RESIZE, "resize", false}, \
|
||||
{DYN_SIZE, "size", true}, \
|
||||
{DYN_SLICE, "slice", true}, \
|
||||
{DYN_SLICE_ASSIGN, "sliceAssign", false}, \
|
||||
{DYN_SLICE_ASSIGN_BACK_BACK, "sliceAssignBackBack", false}, \
|
||||
{DYN_SLICE_ASSIGN_FRONT_BACK, "sliceAssignFrontBack", false}, \
|
||||
{DYN_SLICE_BACK_BACK, "sliceBackBack", true}, \
|
||||
{DYN_SLICE_FRONT_BACK, "sliceFrontBack", true}, \
|
||||
{EVENT_CLEAR_FIRED, "clearFired", false}, \
|
||||
{EVENT_CLEAR_TRIGGERED, "clearTriggered", false}, \
|
||||
{EVENT_FIRE, "fire", false}, \
|
||||
{EVENT_IS_FIRED, "isFired", true}, \
|
||||
{EVENT_IS_TRIGGERED, "isTriggered", true}, \
|
||||
{FORCE_ADD, "addForce", false}, \
|
||||
{FORCE_READ, "read", true}, \
|
||||
{FORCE_READ_INDEX, "readIndex", true}, \
|
||||
{FORCE_READ_SEL, "readSel", true}, \
|
||||
{FORCE_RELEASE, "release", false}, \
|
||||
{FORCE_TOUCH, "touch", false}, \
|
||||
{FORK_DONE, "done", false}, \
|
||||
{FORK_INIT, "init", false}, \
|
||||
{FORK_JOIN, "join", false}, \
|
||||
{FORK_ON_KILL, "onKill", false}, \
|
||||
{RANDOMIZER_BASIC_STD_RANDOMIZATION, "basicStdRandomization", false}, \
|
||||
{RANDOMIZER_CLEARCONSTRAINTS, "clearConstraints", false}, \
|
||||
{RANDOMIZER_CLEARALL, "clearAll", false}, \
|
||||
{RANDOMIZER_DISABLE_SOFT, "disable_soft", false}, \
|
||||
{RANDOMIZER_HARD, "hard", false}, \
|
||||
{RANDOMIZER_SOFT, "soft", false}, \
|
||||
{RANDOMIZER_UNIQUE, "rand_unique", false}, \
|
||||
{RANDOMIZER_MARK_RANDC, "markRandc", false}, \
|
||||
{RANDOMIZER_SOLVE_BEFORE, "solveBefore", false}, \
|
||||
{RANDOMIZER_PIN_VAR, "pin_var", false}, \
|
||||
{RANDOMIZER_WRITE_VAR, "write_var", false}, \
|
||||
{RANDOMIZER_SET_VAR_DISABLED, "set_var_disabled", false}, \
|
||||
{RANDOMIZER_CLEAR_VAR_DISABLED, "clear_var_disabled", false}, \
|
||||
{RANDOMIZER_MARK_VAR_STATIC, "mark_var_static", false}, \
|
||||
{RANDOMIZER_SET_STATIC_RANDMODE, "set_static_randmode", false}, \
|
||||
{RNG_GET_RANDSTATE, "__Vm_rng.get_randstate", true}, \
|
||||
{RNG_SET_RANDSTATE, "__Vm_rng.set_randstate", false}, \
|
||||
{SCHED_ANY_TRIGGERED, "anyTriggered", false}, \
|
||||
{SCHED_AWAITING_CURRENT_TIME, "awaitingCurrentTime", true}, \
|
||||
{SCHED_AWAITING_ZERO_DELAY, "awaitingZeroDelay", true}, \
|
||||
{SCHED_READY, "ready", false}, \
|
||||
{SCHED_COMMIT, "commit", false}, \
|
||||
{SCHED_MOVE_TO_RESUME_QUEUE, "moveToResumeQueue", false}, \
|
||||
{SCHED_DELAY, "delay", false}, \
|
||||
{SCHED_DO_POST_UPDATES, "doPostUpdates", false}, \
|
||||
{SCHED_ENQUEUE, "enqueue", false}, \
|
||||
{SCHED_EVALUATE, "evaluate", false}, \
|
||||
{SCHED_EVALUATION, "evaluation", false}, \
|
||||
{SCHED_POST_UPDATE, "postUpdate", false}, \
|
||||
{SCHED_RESUME, "resume", false}, \
|
||||
{SCHED_RESUME_ZERO_DELAY, "resumeZeroDelay", false}, \
|
||||
{SCHED_RESUMPTION, "resumption", false}, \
|
||||
{SCHED_TRIGGER, "trigger", false}, \
|
||||
{UNPACKED_ASSIGN, "assign", false}, \
|
||||
{UNPACKED_FILL, "fill", false}, \
|
||||
{UNPACKED_NEQ, "neq", true}, \
|
||||
{_ENUM_MAX, "_ENUM_MAX", false}};
|
||||
#undef FOR_EACH_CMETHOD
|
||||
|
||||
// ######################################################################
|
||||
|
||||
@@ -1509,6 +1431,110 @@ constexpr bool operator==(VEdgeType::en lhs, const VEdgeType& rhs) { return lhs
|
||||
|
||||
// ######################################################################
|
||||
|
||||
// Enumeration of the model's evaluation entry points. Fields:
|
||||
// is iterated, has triggers, takes 'firstIteration' flag, is slow
|
||||
// clang-format off
|
||||
#define FOR_EACH_EVAL(macro) \
|
||||
/* id, iterated, hasTrigs, firstIt, slow */ \
|
||||
macro(STATIC, false, false, false, true) \
|
||||
macro(INITIAL, false, false, false, true) \
|
||||
macro(STL, true, true, true, true) \
|
||||
macro(SAMPLE, false, false, false, false) \
|
||||
macro(ICO, true, true, true, false) \
|
||||
macro(ACT, true, true, false, false) \
|
||||
macro(INACT, true, false, false, false) \
|
||||
macro(NBA, true, true, false, false) \
|
||||
macro(OBS, true, true, false, false) \
|
||||
macro(REACT, true, true, false, false) \
|
||||
macro(POSTPONED, false, false, false, false) \
|
||||
macro(FINAL, false, false, false, true)
|
||||
// clang-format on
|
||||
|
||||
class VEval final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
#define VL_EVAL_ID(id, iterated, triggers, first, slow) id,
|
||||
FOR_EACH_EVAL(VL_EVAL_ID)
|
||||
#undef VL_EVAL_ID
|
||||
_ENUM_END
|
||||
};
|
||||
enum en m_e;
|
||||
|
||||
const char* ascii() const {
|
||||
static const char* const values[] = {
|
||||
#define VL_EVAL_NAME(id, iterated, triggers, first, slow) #id,
|
||||
FOR_EACH_EVAL(VL_EVAL_NAME)
|
||||
#undef VL_EVAL_NAME
|
||||
"_ENUM_END" //
|
||||
};
|
||||
return values[m_e];
|
||||
}
|
||||
bool isIterated() const {
|
||||
static const bool values[] = {
|
||||
#define VL_EVAL_IS_ITERATED(id, iterated, triggers, first, slow) iterated,
|
||||
FOR_EACH_EVAL(VL_EVAL_IS_ITERATED)
|
||||
#undef VL_EVAL_IS_ITERATED
|
||||
false //
|
||||
};
|
||||
return values[m_e];
|
||||
}
|
||||
bool hasTriggers() const {
|
||||
static const bool values[] = {
|
||||
#define VL_EVAL_HAS_TRIGGERS(id, iterated, triggers, first, slow) triggers,
|
||||
FOR_EACH_EVAL(VL_EVAL_HAS_TRIGGERS)
|
||||
#undef VL_EVAL_HAS_TRIGGERS
|
||||
false //
|
||||
};
|
||||
return values[m_e];
|
||||
}
|
||||
bool firstIteration() const {
|
||||
static const bool values[] = {
|
||||
#define VL_EVAL_FIRST(id, iterated, triggers, first, slow) first,
|
||||
FOR_EACH_EVAL(VL_EVAL_FIRST)
|
||||
#undef VL_EVAL_FIRST
|
||||
false //
|
||||
};
|
||||
return values[m_e];
|
||||
}
|
||||
bool slow() const {
|
||||
static const bool values[] = {
|
||||
#define VL_EVAL_SLOW(id, iterated, triggers, first, slow) slow,
|
||||
FOR_EACH_EVAL(VL_EVAL_SLOW)
|
||||
#undef VL_EVAL_SLOW
|
||||
false //
|
||||
};
|
||||
return values[m_e];
|
||||
}
|
||||
|
||||
// Short name
|
||||
std::string tag() const { return VString::downcase(ascii()); }
|
||||
// Name of the generated entry point function
|
||||
std::string funcName() const { return "_eval_" + tag(); }
|
||||
// Name of the VerilatedModel virtual method invoking the entry point
|
||||
std::string evalMethod() const { return "eval" + capitalizedTag(); }
|
||||
// Name of the function dumping the triggers of this region
|
||||
std::string dumpTriggersFuncName() const { return "_eval_dump_triggers__" + tag(); }
|
||||
// Name of the VerilatedModel virtual method dumping this region's triggers
|
||||
std::string dumpTriggersMethod() const { return "dumpTriggers" + capitalizedTag(); }
|
||||
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VEval(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VEval(int _e)
|
||||
: m_e(static_cast<en>(_e)) {}
|
||||
operator en() const { return m_e; }
|
||||
|
||||
private:
|
||||
std::string capitalizedTag() const {
|
||||
const std::string name = ascii();
|
||||
return name.substr(0, 1) + VString::downcase(name.substr(1));
|
||||
}
|
||||
};
|
||||
|
||||
#undef FOR_EACH_EVAL
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VFwdType final {
|
||||
public:
|
||||
enum en : uint8_t { NONE, ENUM, STRUCT, UNION, CLASS, INTERFACE_CLASS, GENERIC_INTERFACE };
|
||||
|
||||
+3
-1
@@ -146,6 +146,7 @@ bool AstBasicDType::ascending() const {
|
||||
|
||||
bool AstActive::hasClocked() const { return m_sentreep->hasClocked(); }
|
||||
bool AstActive::hasCombo() const { return m_sentreep->hasCombo(); }
|
||||
bool AstActive::hasInitial() const { return m_sentreep->hasInitial(); }
|
||||
bool AstActive::hasStatic() const { return m_sentreep->hasStatic(); }
|
||||
|
||||
AstAlways::AstAlways(AstAssignW* assignp)
|
||||
@@ -160,7 +161,8 @@ AstElabDisplay::AstElabDisplay(FileLine* fl, VDisplayType dispType, AstNodeExpr*
|
||||
|
||||
bool AstVar::sameNode(const AstNode* samep) const {
|
||||
const AstVar* const asamep = VN_DBG_AS(samep, Var);
|
||||
return m_name == asamep->m_name && varType() == asamep->varType();
|
||||
return m_name == asamep->m_name && varType() == asamep->varType()
|
||||
&& covergroupRefMember() == asamep->covergroupRefMember();
|
||||
}
|
||||
|
||||
AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp)
|
||||
|
||||
+19
-5
@@ -38,7 +38,6 @@ class AstNodeDType VL_NOT_FINAL : public AstNode {
|
||||
int m_widthMin
|
||||
= 0; // (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation
|
||||
VSigning m_numeric; // (also in AstTypeTable::Key) Node is signed
|
||||
// Other members
|
||||
bool m_generic = false; // Simple globally referenced type, don't garbage collect
|
||||
// Unique number assigned to each dtype during creation for IEEE matching
|
||||
static int s_uniqueNum;
|
||||
@@ -584,6 +583,7 @@ class AstClassRefDType final : public AstNodeDType {
|
||||
//
|
||||
// @astgen ptr := m_classp : Optional[AstClass] // data type pointed to, BELOW the AstTypedef
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Package hierarchy
|
||||
bool m_rawPointer = false; // Emit as a non-owning C++ pointer rather than VlClassRef
|
||||
public:
|
||||
AstClassRefDType(FileLine* fl, AstClass* classp, AstPin* paramsp)
|
||||
: ASTGEN_SUPER_ClassRefDType(fl)
|
||||
@@ -595,7 +595,8 @@ public:
|
||||
// METHODS
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
|
||||
return (m_classp == asamep->m_classp && m_classOrPackagep == asamep->m_classOrPackagep);
|
||||
return (m_classp == asamep->m_classp && m_classOrPackagep == asamep->m_classOrPackagep
|
||||
&& m_rawPointer == asamep->m_rawPointer);
|
||||
}
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
@@ -613,6 +614,9 @@ public:
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
AstClass* classp() const VL_MT_STABLE { return m_classp; }
|
||||
void classp(AstClass* nodep) { m_classp = nodep; }
|
||||
bool rawPointer() const { return m_rawPointer; }
|
||||
void rawPointer(bool flag) { m_rawPointer = flag; }
|
||||
static void selfTest();
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
class AstConstDType final : public AstNodeDType {
|
||||
@@ -868,6 +872,8 @@ public:
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstIfaceGenericDType;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
bool hasDType() const override VL_MT_SAFE { return true; }
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
@@ -918,7 +924,8 @@ public:
|
||||
: ASTGEN_SUPER_IfaceRefDType(fl)
|
||||
, m_modportFileline{modportFl}
|
||||
, m_cellName{cellName}
|
||||
, m_ifaceName{ifaceName} {
|
||||
, m_ifaceName{ifaceName}
|
||||
, m_modportName{modport} {
|
||||
addParamsp(paramsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstIfaceRefDType;
|
||||
@@ -968,7 +975,7 @@ class AstMemberDType final : public AstNodeDType {
|
||||
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;
|
||||
// UNSUP: int m_randType; // Randomization type (IEEE)
|
||||
VRandAttr m_rand; // Randomizability of this member (rand, randc, etc)
|
||||
public:
|
||||
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
|
||||
AstNode* valuep)
|
||||
@@ -1025,6 +1032,8 @@ public:
|
||||
}
|
||||
bool isConstrainedRand() const { return m_constrainedRand; }
|
||||
void markConstrainedRand(bool flag) { m_constrainedRand = flag; }
|
||||
VRandAttr rand() const { return m_rand; }
|
||||
void rand(const VRandAttr flag) { m_rand = flag; }
|
||||
};
|
||||
class AstNBACommitQueueDType final : public AstNodeDType {
|
||||
// @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable
|
||||
@@ -1038,7 +1047,8 @@ public:
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstNBACommitQueueDType;
|
||||
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE { return m_subDTypep; }
|
||||
bool partial() const { return m_partial; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
@@ -1108,6 +1118,8 @@ public:
|
||||
ASTGEN_MEMBERS_AstParseTypeDType;
|
||||
AstNodeDType* dtypep() const VL_MT_STABLE { return nullptr; }
|
||||
// METHODS
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
int widthAlignBytes() const override { return 0; }
|
||||
@@ -1452,6 +1464,8 @@ public:
|
||||
widthFromSub(subDTypep());
|
||||
}
|
||||
ASTGEN_MEMBERS_AstUnpackArrayDType;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string prettyDTypeName(bool full) const override;
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType);
|
||||
|
||||
+83
-45
@@ -237,8 +237,9 @@ private:
|
||||
string m_name; // Name of variable
|
||||
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
|
||||
string m_inlinedDots; // Dotted hierarchy flattened out
|
||||
bool m_pli = false; // Pli system call ($name)
|
||||
bool m_containsGenBlock = false; // Contains gen block reference
|
||||
bool m_pli = false; // Pli system call ($name)
|
||||
bool m_superReference = false; // Called with super reference
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
protected:
|
||||
@@ -263,10 +264,12 @@ public:
|
||||
void dotted(const string& name) { m_dotted = name; }
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
bool pli() const { return m_pli; }
|
||||
void pli(bool flag) { m_pli = flag; }
|
||||
bool containsGenBlock() const { return m_containsGenBlock; }
|
||||
void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
|
||||
bool pli() const { return m_pli; }
|
||||
void pli(bool flag) { m_pli = flag; }
|
||||
bool superReference() const { return m_superReference; }
|
||||
void superReference(bool flag) { m_superReference = flag; }
|
||||
bool isPure() override;
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstNodeFTaskRef* const asamep = VN_DBG_AS(samep, NodeFTaskRef);
|
||||
@@ -276,7 +279,8 @@ public:
|
||||
&& dotted() == asamep->dotted() //
|
||||
&& inlinedDots() == asamep->inlinedDots() //
|
||||
&& pli() == asamep->pli() //
|
||||
&& containsGenBlock() == asamep->containsGenBlock();
|
||||
&& containsGenBlock() == asamep->containsGenBlock()
|
||||
&& superReference() == asamep->superReference();
|
||||
}
|
||||
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() final override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -681,9 +685,9 @@ public:
|
||||
init(text, setwidth);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCExpr;
|
||||
// METHODS
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
// METHODS
|
||||
bool cleanOut() const override { return true; }
|
||||
std::string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
std::string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -714,6 +718,8 @@ public:
|
||||
, m_pure{true} {}
|
||||
ASTGEN_MEMBERS_AstCExprUser;
|
||||
// METHODS
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
bool cleanOut() const override { return false; }
|
||||
std::string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
std::string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -905,7 +911,7 @@ public:
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
// There's no classOrPackagep(); use classOrPackageNodep() to get Node,
|
||||
// or iterating to package with classOrPackageSkipp()
|
||||
AstNodeModule* classOrPackageSkipp() const;
|
||||
AstNodeModule* classOrPackageSkipp(const bool doRefs = true) const;
|
||||
AstNode* classOrPackageNodep() const { return m_classOrPackageNodep; }
|
||||
void classOrPackageNodep(AstNode* nodep) { m_classOrPackageNodep = nodep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) {
|
||||
@@ -1159,6 +1165,16 @@ public:
|
||||
, m_num(this, 1, 0) { // Need () constructor
|
||||
dtypeSetBit();
|
||||
}
|
||||
// False but created due to reporting earlier error; suppress some later errors
|
||||
class BitFalseErroring {};
|
||||
AstConst(FileLine* fl, BitFalseErroring)
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num(this, 1, 0) { // Need () constructor
|
||||
dtypeSetBit();
|
||||
FileLine* const newfl = new FileLine{fileline()};
|
||||
newfl->erroringOn(true);
|
||||
fileline(newfl);
|
||||
}
|
||||
// Shorthand const 1 (or with argument 0/1), dtype should be a bit of size 1
|
||||
class BitTrue {};
|
||||
AstConst(FileLine* fl, BitTrue, bool on = true)
|
||||
@@ -1358,6 +1374,8 @@ public:
|
||||
this->weightp(weightp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstDistItem;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string emitVerilog() override { return "%l "s + (m_isWhole ? ":/" : ":=") + " %r"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return false; } // NA
|
||||
@@ -2022,21 +2040,33 @@ class AstPast final : public AstNodeExpr {
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := ticksp : Optional[AstNodeExpr]
|
||||
// @astgen op3 := sentreep : Optional[AstSenTree]
|
||||
// @astgen op4 := initp : Optional[AstNodeExpr] // Default sampled expression
|
||||
// True only for history synthesized as property-evaluation bookkeeping (for example, |=> or
|
||||
// NFA local-variable capture). Source $past and AstPast nodes lowering IEEE sampled-value
|
||||
// functions such as $rose/$fell must remain false so they receive default initialization.
|
||||
bool m_propertyTiming : 1;
|
||||
|
||||
public:
|
||||
AstPast(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* ticksp = nullptr,
|
||||
AstSenTree* sentreep = nullptr)
|
||||
: ASTGEN_SUPER_Past(fl) {
|
||||
AstSenTree* sentreep = nullptr, bool propertyTiming = false)
|
||||
: ASTGEN_SUPER_Past(fl)
|
||||
, m_propertyTiming{propertyTiming} {
|
||||
this->exprp(exprp);
|
||||
this->ticksp(ticksp);
|
||||
this->sentreep(sentreep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstPast;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
return m_propertyTiming == VN_DBG_AS(samep, Past)->m_propertyTiming;
|
||||
}
|
||||
bool propertyTiming() const { return m_propertyTiming; }
|
||||
bool isSystemFunc() const override { return true; }
|
||||
};
|
||||
class AstPatMember final : public AstNodeExpr {
|
||||
@@ -2163,6 +2193,8 @@ public:
|
||||
dtypeSetUInt32();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRand;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string emitVerilog() override {
|
||||
return seedp() ? (m_urandom ? "%f$urandom(%l)" : "%f$random(%l)")
|
||||
: (m_urandom ? "%f$urandom()" : "%f$random()");
|
||||
@@ -2262,24 +2294,25 @@ public:
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
||||
};
|
||||
class AstSConsRep final : public AstNodeExpr {
|
||||
// Consecutive repetition [*N], [*N:M], [+], [*] (IEEE 1800-2023 16.9.2)
|
||||
// Consecutive repetition [*N], [*N:M], [*N:$], [+], [*] (IEEE 1800-2023 16.9.2)
|
||||
// op1 := exprp -- the repeated expression
|
||||
// op2 := countp -- min repetition count (N); always a positive constant after V3Width
|
||||
// op2 := countp -- min repetition count (N); always a non-negative constant after V3Width
|
||||
// op3 := maxCountp -- max repetition count (M); nullptr when exact or unbounded
|
||||
//
|
||||
// Encoding:
|
||||
// [*N]: countp=N, maxCountp=nullptr, unbounded=false
|
||||
// [*N:M]: countp=N, maxCountp=M, unbounded=false
|
||||
// [*N:$]: countp=N, maxCountp=nullptr, unbounded=true
|
||||
// [+]: countp=1, maxCountp=nullptr, unbounded=true (= [*1:$])
|
||||
// [*]: countp=0, maxCountp=nullptr, unbounded=true (= [*0:$])
|
||||
//
|
||||
// Lowering:
|
||||
// Exact [*N] standalone: V3AssertPre saturating counter
|
||||
// All other forms and all SExpr-contained forms: V3AssertProp PExpr loop
|
||||
// All other forms and all SExpr-contained forms: V3AssertNfa
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := countp : AstNodeExpr
|
||||
// @astgen op3 := maxCountp : Optional[AstNodeExpr]
|
||||
const bool m_unbounded = false; // True for [+] and [*] (upper bound is $)
|
||||
const bool m_unbounded = false; // True when the upper bound is $
|
||||
public:
|
||||
// Exact [*N]
|
||||
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
|
||||
@@ -2287,7 +2320,7 @@ public:
|
||||
this->exprp(exprp);
|
||||
this->countp(countp);
|
||||
}
|
||||
// Range [*N:M] or unbounded [+]/[*]
|
||||
// Range [*N:M] or unbounded [*N:$]/[+]/[*]
|
||||
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp, AstNodeExpr* maxCountp,
|
||||
bool unbounded)
|
||||
: ASTGEN_SUPER_SConsRep(fl)
|
||||
@@ -2554,19 +2587,26 @@ public:
|
||||
class AstSampled final : public AstNodeExpr {
|
||||
// Verilog $sampled
|
||||
// @astgen op1 := exprp : AstNode<AstNodeExpr|AstPropSpec>
|
||||
bool m_internal : 1; // Internally created, not from a source $sampled
|
||||
public:
|
||||
AstSampled(FileLine* fl, AstNode* exprp, AstNodeDType* dtypep)
|
||||
: ASTGEN_SUPER_Sampled(fl) {
|
||||
AstSampled(FileLine* fl, AstNode* exprp, AstNodeDType* dtypep, bool internal = false)
|
||||
: ASTGEN_SUPER_Sampled(fl)
|
||||
, m_internal{internal} {
|
||||
this->exprp(exprp);
|
||||
this->dtypep(dtypep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSampled;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string emitVerilog() override { return "$sampled(%l)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
int instrCount() const override { return 0; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
return m_internal == VN_DBG_AS(samep, Sampled)->m_internal;
|
||||
}
|
||||
bool internal() const { return m_internal; }
|
||||
bool isSystemFunc() const override { return true; }
|
||||
};
|
||||
class AstScopeName final : public AstNodeExpr {
|
||||
@@ -3063,6 +3103,8 @@ public:
|
||||
dtypeSetInt();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCompareNN;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opCompareNN(lhs, rhs, m_ignoreCase);
|
||||
}
|
||||
@@ -3079,6 +3121,7 @@ public:
|
||||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
bool ignoreCase() const { return m_ignoreCase; }
|
||||
};
|
||||
class AstConcat final : public AstNodeBiop {
|
||||
// If you're looking for {#{}}, see AstReplicate
|
||||
@@ -3258,6 +3301,7 @@ public:
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
bool isSystemFunc() const override { return true; }
|
||||
int instrCount() const override { return widthInstrs() * 64; }
|
||||
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
};
|
||||
class AstFUngetC final : public AstNodeBiop {
|
||||
public:
|
||||
@@ -3978,12 +4022,16 @@ public:
|
||||
class AstSAnd final : public AstNodeBiop {
|
||||
// Sequence 'and' (IEEE 1800-2023 16.9.5): both operand sequences must match.
|
||||
// Operates on match sets, not values. For boolean operands, lowered to AstLogAnd.
|
||||
const bool m_propertyControl; // Parser-generated property if/case branch conjunction
|
||||
public:
|
||||
AstSAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_SAnd(fl, lhsp, rhsp) {
|
||||
AstSAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, bool propertyControl = false)
|
||||
: ASTGEN_SUPER_SAnd(fl, lhsp, rhsp)
|
||||
, m_propertyControl{propertyControl} {
|
||||
dtypeSetBit();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSAnd;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opLogAnd(lhs, rhs);
|
||||
}
|
||||
@@ -3997,6 +4045,10 @@ public:
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_LINE
|
||||
return m_propertyControl == VN_DBG_AS(samep, SAnd)->m_propertyControl; // LCOV_EXCL_LINE
|
||||
}
|
||||
bool propertyControl() const { return m_propertyControl; }
|
||||
};
|
||||
class AstSIntersect final : public AstNodeBiop {
|
||||
// Sequence 'intersect' (IEEE 1800-2023 16.9.6): both operands match with equal length.
|
||||
@@ -4886,9 +4938,11 @@ public:
|
||||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
bool isGateOptimizable() const override { return false; } // AssocSel creates on miss
|
||||
bool isGateOptimizable() const override {
|
||||
return !isLValue(); // AssocSel creates on miss
|
||||
}
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() override { return false; } // AssocSel creates on miss
|
||||
bool isPure() override { return !isLValue(); } // AssocSel creates on miss
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
};
|
||||
@@ -5044,19 +5098,11 @@ public:
|
||||
// === AstNodeFTaskRef ===
|
||||
class AstFuncRef final : public AstNodeFTaskRef {
|
||||
// A reference to a function
|
||||
bool m_superReference = false; // Called with super reference
|
||||
public:
|
||||
inline AstFuncRef(FileLine* fl, AstFunc* taskp, AstArg* argsp = nullptr);
|
||||
AstFuncRef(FileLine* fl, const string& name, AstArg* argsp = nullptr)
|
||||
: ASTGEN_SUPER_FuncRef(fl, name, argsp) {}
|
||||
ASTGEN_MEMBERS_AstFuncRef;
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
if (!this->AstNodeFTaskRef::sameNode(samep)) return false;
|
||||
const AstFuncRef* const asamep = VN_DBG_AS(samep, FuncRef);
|
||||
return superReference() == asamep->superReference();
|
||||
}
|
||||
bool superReference() const { return m_superReference; }
|
||||
void superReference(bool flag) { m_superReference = flag; }
|
||||
};
|
||||
class AstMethodCall final : public AstNodeFTaskRef {
|
||||
// A reference to a member task (or function)
|
||||
@@ -5097,7 +5143,6 @@ public:
|
||||
};
|
||||
class AstTaskRef final : public AstNodeFTaskRef {
|
||||
// A reference to a task
|
||||
bool m_superReference = false; // Called with super reference
|
||||
public:
|
||||
inline AstTaskRef(FileLine* fl, AstTask* taskp, AstArg* argsp = nullptr);
|
||||
AstTaskRef(FileLine* fl, const string& name, AstArg* argsp = nullptr)
|
||||
@@ -5105,13 +5150,6 @@ public:
|
||||
dtypeSetVoid();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstTaskRef;
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
if (!this->AstNodeFTaskRef::sameNode(samep)) return false;
|
||||
const AstTaskRef* const asamep = VN_DBG_AS(samep, TaskRef);
|
||||
return superReference() == asamep->superReference();
|
||||
}
|
||||
bool superReference() const { return m_superReference; }
|
||||
void superReference(bool flag) { m_superReference = flag; }
|
||||
};
|
||||
|
||||
// === AstNodePreSel ===
|
||||
@@ -5392,17 +5430,17 @@ public:
|
||||
enum FmtType : int { ATOI = 10, ATOHEX = 16, ATOOCT = 8, ATOBIN = 2, ATOREAL = -1 };
|
||||
|
||||
private:
|
||||
const FmtType m_fmt; // Operation type
|
||||
const FmtType m_fmtType; // Operation type
|
||||
public:
|
||||
AstAtoN(FileLine* fl, AstNodeExpr* lhsp, FmtType fmt)
|
||||
AstAtoN(FileLine* fl, AstNodeExpr* lhsp, FmtType fmtType)
|
||||
: ASTGEN_SUPER_AtoN(fl, lhsp)
|
||||
, m_fmt{fmt} {
|
||||
fmt == ATOREAL ? dtypeSetDouble() : dtypeSetInteger();
|
||||
, m_fmtType{fmtType} {
|
||||
fmtType == ATOREAL ? dtypeSetDouble() : dtypeSetInteger();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstAtoN;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAtoN(lhs, m_fmt); }
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAtoN(lhs, m_fmtType); }
|
||||
string name() const override VL_MT_STABLE {
|
||||
switch (m_fmt) {
|
||||
switch (m_fmtType) {
|
||||
case ATOI: return "atoi";
|
||||
case ATOHEX: return "atohex";
|
||||
case ATOOCT: return "atooct";
|
||||
@@ -5413,7 +5451,7 @@ public:
|
||||
}
|
||||
string emitVerilog() override { return "%l." + name() + "()"; }
|
||||
string emitC() override {
|
||||
switch (m_fmt) {
|
||||
switch (m_fmtType) {
|
||||
case ATOI: return "VL_ATOI_N(%li, 10)";
|
||||
case ATOHEX: return "VL_ATOI_N(%li, 16)";
|
||||
case ATOOCT: return "VL_ATOI_N(%li, 8)";
|
||||
@@ -5425,7 +5463,7 @@ public:
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
FmtType format() const { return m_fmt; }
|
||||
FmtType fmtType() const { return m_fmtType; }
|
||||
};
|
||||
class AstBitsToRealD final : public AstNodeUniop {
|
||||
public:
|
||||
|
||||
+88
-60
@@ -94,6 +94,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
||||
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
||||
string m_name; // Name of task
|
||||
string m_cname; // Name of task if DPI import
|
||||
string m_dpiCDecl; // Custom DPI-C function declaration
|
||||
string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8)
|
||||
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
|
||||
bool m_taskPublic : 1; // Public task
|
||||
@@ -199,6 +200,9 @@ public:
|
||||
void dpiOpenChild(bool flag) { m_dpiOpenChild = flag; }
|
||||
bool dpiTask() const { return m_dpiTask; }
|
||||
void dpiTask(bool flag) { m_dpiTask = flag; }
|
||||
bool dpiCDeclOverride() const { return !m_dpiCDecl.empty(); }
|
||||
const string& dpiCDecl() const { return m_dpiCDecl; }
|
||||
void dpiCDecl(const string& cDecl) { m_dpiCDecl = cDecl; }
|
||||
bool isConstructor() const { return m_isConstructor; }
|
||||
void isConstructor(bool flag) { m_isConstructor = flag; }
|
||||
bool isHideLocal() const { return m_isHideLocal; }
|
||||
@@ -456,6 +460,7 @@ public:
|
||||
// METHODS
|
||||
inline bool hasClocked() const;
|
||||
inline bool hasCombo() const;
|
||||
inline bool hasInitial() const;
|
||||
inline bool hasStatic() const;
|
||||
};
|
||||
class AstAlias final : public AstNode {
|
||||
@@ -504,6 +509,7 @@ class AstCFunc final : public AstNode {
|
||||
// @astgen op1 := argsp : List[AstVar] // Argument (and return value) variables
|
||||
// @astgen op2 := varsp : List[AstVar] // Local variables
|
||||
// @astgen op3 := stmtsp : List[AstNode]
|
||||
// @astgen op4 := scopeNamep : Optional[AstScopeName] // Scoping context for DPI export
|
||||
//
|
||||
// @astgen ptr := m_scopep : Optional[AstScope] // Scope that function is under
|
||||
string m_name;
|
||||
@@ -511,6 +517,7 @@ class AstCFunc final : public AstNode {
|
||||
string m_rtnType; // void, bool, or other return type
|
||||
string m_argTypes; // Argument types
|
||||
string m_ifdef; // #ifdef symbol around this function
|
||||
string m_dpiCDecl; // Custom DPI-C function declaration
|
||||
VBoolOrUnknown m_isConst; // Function is declared const (*this not changed)
|
||||
bool m_isStatic : 1; // Function is static (no need for a 'this' pointer)
|
||||
bool m_isTrace : 1; // Function is related to tracing
|
||||
@@ -640,6 +647,9 @@ public:
|
||||
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
|
||||
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
|
||||
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
|
||||
bool dpiCDeclOverride() const { return !m_dpiCDecl.empty(); }
|
||||
const string& dpiCDecl() const { return m_dpiCDecl; }
|
||||
void dpiCDecl(const string& cDecl) { m_dpiCDecl = cDecl; }
|
||||
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
@@ -650,20 +660,10 @@ public:
|
||||
void cost(int cost) { m_cost = cost; }
|
||||
// Special methods
|
||||
bool emptyBody() const {
|
||||
return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp() && !isVirtual()
|
||||
return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp() && !scopeNamep() && !isVirtual()
|
||||
&& !dpiImportPrototype();
|
||||
}
|
||||
};
|
||||
class AstCLocalScope final : public AstNode {
|
||||
// Pack statements into an unnamed scope when generating C++
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
public:
|
||||
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_CLocalScope(fl) {
|
||||
addStmtsp(stmtsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCLocalScope;
|
||||
};
|
||||
class AstCUse final : public AstNode {
|
||||
// C++ use of a class or #include; indicates need of forward declaration
|
||||
// Parents: NODEMODULE
|
||||
@@ -806,7 +806,7 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
VCoverOptionType optionType() const { return m_optType; }
|
||||
VCoverOptionType optType() const { return m_optType; }
|
||||
bool typeOption() const { return m_typeOption; }
|
||||
};
|
||||
class AstClassExtends final : public AstNode {
|
||||
@@ -844,7 +844,7 @@ class AstClocking final : public AstNode {
|
||||
// @astgen op2 := itemsp : List[AstNode]
|
||||
// @astgen op3 := eventp : Optional[AstVar]
|
||||
std::string m_name; // Clocking block name
|
||||
const bool m_isDefault; // True if default clocking
|
||||
bool m_isDefault; // True if default clocking
|
||||
const bool m_isGlobal; // True if global clocking
|
||||
|
||||
public:
|
||||
@@ -865,6 +865,7 @@ public:
|
||||
bool isDefault() const { return m_isDefault; }
|
||||
bool isGlobal() const { return m_isGlobal; }
|
||||
AstVar* ensureEventp(bool childDType = false);
|
||||
void makeDefault() { m_isDefault = true; }
|
||||
};
|
||||
class AstClockingItem final : public AstNode {
|
||||
// Parents: CLOCKING
|
||||
@@ -1042,22 +1043,6 @@ public:
|
||||
void isStatic(bool flag) { m_isStatic = flag; }
|
||||
bool isStatic() const { return m_isStatic; }
|
||||
};
|
||||
class AstConstraintBefore final : public AstNode {
|
||||
// Constraint solve before item
|
||||
// @astgen op1 := lhssp : List[AstNodeExpr]
|
||||
// @astgen op2 := rhssp : List[AstNodeExpr]
|
||||
public:
|
||||
AstConstraintBefore(FileLine* fl, AstNodeExpr* lhssp, AstNodeExpr* rhssp)
|
||||
: ASTGEN_SUPER_ConstraintBefore(fl) {
|
||||
addLhssp(lhssp);
|
||||
addRhssp(rhssp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConstraintBefore;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstCoverBin final : public AstNode {
|
||||
// Captures data for a coverpoint 'bins' declaration
|
||||
// @astgen op1 := rangesp : List[AstNode]
|
||||
@@ -1065,7 +1050,7 @@ class AstCoverBin final : public AstNode {
|
||||
// @astgen op3 := arraySizep : Optional[AstNodeExpr]
|
||||
// @astgen op4 := transp : List[AstCoverTransSet]
|
||||
const string m_name; // Base name of the bin
|
||||
const VCoverBinsType m_type; // Bin type (eg AUTO, IGNORE, ILLEGAL)
|
||||
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_isWildcard = false; // Bin uses wildcard matching (independent of ignore/illegal)
|
||||
|
||||
@@ -1074,10 +1059,10 @@ public:
|
||||
bool isWildcard = false)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{isIllegal ? VCoverBinsType::BINS_ILLEGAL
|
||||
: (isIgnore ? VCoverBinsType::BINS_IGNORE
|
||||
: (isWildcard ? VCoverBinsType::BINS_WILDCARD
|
||||
: VCoverBinsType::BINS_USER))}
|
||||
, m_binsType{isIllegal ? VCoverBinsType::BINS_ILLEGAL
|
||||
: (isIgnore ? VCoverBinsType::BINS_IGNORE
|
||||
: (isWildcard ? VCoverBinsType::BINS_WILDCARD
|
||||
: VCoverBinsType::BINS_USER))}
|
||||
, m_isWildcard{isWildcard} {
|
||||
addRangesp(rangesp);
|
||||
}
|
||||
@@ -1085,7 +1070,7 @@ public:
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{VCoverBinsType::BINS_AUTO}
|
||||
, m_binsType{VCoverBinsType::BINS_AUTO}
|
||||
, m_isArray{true} {
|
||||
this->arraySizep(arraySizep);
|
||||
}
|
||||
@@ -1093,13 +1078,13 @@ public:
|
||||
AstCoverBin(FileLine* fl, const string& name, VCoverBinsType type)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{type} {}
|
||||
, m_binsType{type} {}
|
||||
// Constructor for transition bins
|
||||
AstCoverBin(FileLine* fl, const string& name, AstCoverTransSet* transp,
|
||||
VCoverBinsType type = VCoverBinsType::BINS_TRANSITION, bool isArrayBin = false)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_type{type}
|
||||
, m_binsType{type}
|
||||
, m_isArray{isArrayBin} {
|
||||
UASSERT(transp, "AstCoverBin transition constructor requires non-null transp");
|
||||
addTransp(transp);
|
||||
@@ -1108,7 +1093,7 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
VCoverBinsType binsType() const { return m_type; }
|
||||
VCoverBinsType binsType() const { return m_binsType; }
|
||||
bool isWildcard() const { return m_isWildcard; }
|
||||
bool isArray() const { return m_isArray; }
|
||||
void isArray(bool flag) { m_isArray = flag; }
|
||||
@@ -1116,18 +1101,18 @@ public:
|
||||
class AstCoverOption final : public AstNode {
|
||||
// Coverage-option assignment
|
||||
// @astgen op1 := valuep : AstNodeExpr
|
||||
const VCoverOptionType m_type; // Option being assigned
|
||||
const VCoverOptionType m_optType; // Option being assigned
|
||||
|
||||
public:
|
||||
AstCoverOption(FileLine* fl, VCoverOptionType type, AstNodeExpr* valuep)
|
||||
AstCoverOption(FileLine* fl, VCoverOptionType optType, AstNodeExpr* valuep)
|
||||
: ASTGEN_SUPER_CoverOption(fl)
|
||||
, m_type{type} {
|
||||
, m_optType{optType} {
|
||||
this->valuep(valuep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverOption;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
VCoverOptionType optionType() const { return m_type; }
|
||||
VCoverOptionType optType() const { return m_optType; }
|
||||
};
|
||||
class AstCoverTransItem final : public AstNode {
|
||||
// Represents a single transition item: value or value[*N] or value[->N] or value[=N]
|
||||
@@ -1222,6 +1207,16 @@ public:
|
||||
ASTGEN_MEMBERS_AstDefParam;
|
||||
bool sameNode(const AstNode*) const override { return true; }
|
||||
};
|
||||
class AstDefaultClocking final : public AstNode {
|
||||
std::string m_name; // Clocking block name
|
||||
|
||||
public:
|
||||
AstDefaultClocking(FileLine* fl, const std::string& name)
|
||||
: ASTGEN_SUPER_DefaultClocking(fl)
|
||||
, m_name{name} {}
|
||||
ASTGEN_MEMBERS_AstDefaultClocking;
|
||||
std::string name() const override VL_MT_STABLE { return m_name; }
|
||||
};
|
||||
class AstDefaultDisable final : public AstNode {
|
||||
// @astgen op1 := condp : AstNodeExpr
|
||||
|
||||
@@ -1244,6 +1239,8 @@ public:
|
||||
, m_name{vname}
|
||||
, m_cname{cname} {}
|
||||
ASTGEN_MEMBERS_AstDpiExport;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string cname() const { return m_cname; }
|
||||
@@ -1447,8 +1444,6 @@ class AstNetlist final : public AstNode {
|
||||
// @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
|
||||
// @astgen ptr := m_evalp : Optional[AstCFunc] // The '_eval' function
|
||||
// @astgen ptr := m_evalNbap : Optional[AstCFunc] // The '_eval__nba' function
|
||||
// @astgen ptr := m_dpiExportTriggerp : Optional[AstVarScope] // DPI export trigger variable
|
||||
// @astgen ptr := m_delaySchedulerp : Optional[AstVar] // Delay scheduler variable
|
||||
// @astgen ptr := m_nbaEventp : Optional[AstVarScope] // NBA event variable
|
||||
@@ -1466,6 +1461,10 @@ 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;
|
||||
// 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
|
||||
std::array<AstCFunc*, VEval::_ENUM_END> m_dumpTriggersFuncps{};
|
||||
|
||||
public:
|
||||
AstNetlist();
|
||||
@@ -1489,10 +1488,10 @@ public:
|
||||
void astConstOrigParamNameErase(const AstConst* nodep);
|
||||
AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; }
|
||||
AstPackage* dollarUnitPkgAddp();
|
||||
AstCFunc* evalp() const { return m_evalp; }
|
||||
void evalp(AstCFunc* funcp) { m_evalp = funcp; }
|
||||
AstCFunc* evalNbap() const { return m_evalNbap; }
|
||||
void evalNbap(AstCFunc* funcp) { m_evalNbap = funcp; }
|
||||
AstCFunc* evalFuncp(VEval eval) const { return m_evalFuncps[eval]; }
|
||||
void evalFuncp(VEval eval, AstCFunc* funcp) { m_evalFuncps[eval] = funcp; }
|
||||
AstCFunc* dumpTriggersFuncp(VEval eval) const { return m_dumpTriggersFuncps[eval]; }
|
||||
void dumpTriggersFuncp(VEval eval, AstCFunc* funcp) { m_dumpTriggersFuncps[eval] = funcp; }
|
||||
AstVarScope* dpiExportTriggerp() const { return m_dpiExportTriggerp; }
|
||||
void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; }
|
||||
AstVar* delaySchedulerp() const { return m_delaySchedulerp; }
|
||||
@@ -1528,6 +1527,9 @@ public:
|
||||
const std::string& name = resolvedTopModuleName();
|
||||
return prettyName(name.empty() ? v3Global.rootp()->topModulep()->name() : name);
|
||||
}
|
||||
|
||||
// Record statistics for eval functions
|
||||
void addEvalStats(const std::string& phase);
|
||||
};
|
||||
class AstPackageExport final : public AstNode {
|
||||
// A package export declaration
|
||||
@@ -1679,17 +1681,25 @@ class AstPropSpec final : public AstNode {
|
||||
// @astgen op1 := sensesp : Optional[AstSenItem]
|
||||
// @astgen op2 := disablep : Optional[AstNodeExpr]
|
||||
// @astgen op3 := propp : AstNode
|
||||
// @astgen op4 := matchCountp : Optional[AstNodeExpr] // Cover sequence matches this tick
|
||||
VPropStrength m_propStrength = VPropStrength::DEFAULT;
|
||||
|
||||
public:
|
||||
AstPropSpec(FileLine* fl, AstSenItem* sensesp, AstNodeExpr* disablep, AstNode* propp)
|
||||
: ASTGEN_SUPER_PropSpec(fl) {
|
||||
AstPropSpec(FileLine* fl, AstSenItem* sensesp, AstNodeExpr* disablep, AstNode* propp,
|
||||
VPropStrength propStrength = VPropStrength::DEFAULT)
|
||||
: ASTGEN_SUPER_PropSpec(fl)
|
||||
, m_propStrength{propStrength} {
|
||||
this->sensesp(sensesp);
|
||||
this->disablep(disablep);
|
||||
this->propp(propp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstPropSpec;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool hasDType() const override VL_MT_SAFE {
|
||||
return true;
|
||||
} // Used under Cover, which expects a bool child
|
||||
VPropStrength propStrength() const { return m_propStrength; }
|
||||
};
|
||||
class AstPull final : public AstNode {
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
@@ -1706,7 +1716,9 @@ public:
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
return direction() == VN_DBG_AS(samep, Pull)->direction();
|
||||
}
|
||||
uint32_t direction() const { return static_cast<uint32_t>(m_direction); }
|
||||
bool direction() const { return m_direction; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
class AstScope final : public AstNode {
|
||||
// A particular usage of a cell
|
||||
@@ -2091,6 +2103,8 @@ public:
|
||||
addOFieldsp(oFieldsp2);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstUdpTableLine;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
int udpIsCombo() const { return m_udpIsCombo; }
|
||||
};
|
||||
class AstUdpTableLineVal final : public AstNode {
|
||||
@@ -2152,7 +2166,8 @@ class AstVar final : public AstNode {
|
||||
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_fileDescr : 1; // File descriptor
|
||||
bool m_covergroupRefMember : 1; // Persistent covergroup ref/const ref argument
|
||||
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
|
||||
bool m_isConst : 1; // Table contains constant data
|
||||
@@ -2173,7 +2188,7 @@ class AstVar final : public AstNode {
|
||||
bool m_noSubst : 1; // Do not substitute out references
|
||||
bool m_sampled : 1; // Sampled timing region
|
||||
bool m_substConstOnly : 1; // Only substitute if constant
|
||||
bool m_overridenParam : 1; // Overridden parameter by #(...) or defparam
|
||||
bool m_overriddenParam : 1; // Overridden parameter by #(...) or defparam
|
||||
bool m_trace : 1; // Trace this variable
|
||||
bool m_isLatched : 1; // Not assigned in all control paths of combo always
|
||||
bool m_isForceable : 1; // May be forced/released externally from user C code
|
||||
@@ -2216,7 +2231,8 @@ class AstVar final : public AstNode {
|
||||
m_attrFsmResetArc = false;
|
||||
m_attrFsmArcInclCond = false;
|
||||
m_constPoolEntry = false;
|
||||
m_fileDescr = false;
|
||||
m_covergroupRefMember = false;
|
||||
m_attrFileDescr = false;
|
||||
m_gotNansiType = false;
|
||||
m_icoMaybeWritten = false;
|
||||
m_isConst = false;
|
||||
@@ -2237,7 +2253,7 @@ class AstVar final : public AstNode {
|
||||
m_noSubst = false;
|
||||
m_sampled = false;
|
||||
m_substConstOnly = false;
|
||||
m_overridenParam = false;
|
||||
m_overriddenParam = false;
|
||||
m_trace = false;
|
||||
m_isLatched = false;
|
||||
m_isForceable = false;
|
||||
@@ -2356,7 +2372,7 @@ public:
|
||||
void ansi(bool flag) { m_ansi = flag; }
|
||||
void declTyped(bool flag) { m_declTyped = flag; }
|
||||
void sensIfacep(AstIface* nodep) { m_sensIfacep = nodep; }
|
||||
void attrFileDescr(bool flag) { m_fileDescr = flag; }
|
||||
void attrFileDescr(bool flag) { m_attrFileDescr = flag; }
|
||||
void attrScBv(bool flag) { m_attrScBv = flag; }
|
||||
void attrScBigUint(bool flag) { m_attrScBigUint = flag; }
|
||||
void attrSFormat(bool flag) { m_attrSFormat = flag; }
|
||||
@@ -2367,6 +2383,8 @@ public:
|
||||
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; }
|
||||
void rand(const VRandAttr flag) { m_rand = flag; }
|
||||
void usedParam(bool flag) { m_usedParam = flag; }
|
||||
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
|
||||
@@ -2420,8 +2438,8 @@ public:
|
||||
void sampled(bool flag) { m_sampled = flag; }
|
||||
bool substConstOnly() const { return m_substConstOnly; }
|
||||
void substConstOnly(bool flag) { m_substConstOnly = flag; }
|
||||
bool overriddenParam() const { return m_overridenParam; }
|
||||
void overriddenParam(bool flag) { m_overridenParam = flag; }
|
||||
bool overriddenParam() const { return m_overriddenParam; }
|
||||
void overriddenParam(bool flag) { m_overriddenParam = flag; }
|
||||
void trace(bool flag) { m_trace = flag; }
|
||||
void isLatched(bool flag) { m_isLatched = flag; }
|
||||
bool isForceable() const { return m_isForceable; }
|
||||
@@ -2525,7 +2543,7 @@ public:
|
||||
bool isPulldown() const { return m_isPulldown; }
|
||||
bool attrScBv() const { return m_attrScBv; }
|
||||
bool attrScBigUint() const { return m_attrScBigUint; }
|
||||
bool attrFileDescr() const { return m_fileDescr; }
|
||||
bool attrFileDescr() const { return m_attrFileDescr; }
|
||||
bool attrSFormat() const { return m_attrSFormat; }
|
||||
bool attrSplitVar() const { return m_attrSplitVar; }
|
||||
bool attrFsmState() const { return m_attrFsmState; }
|
||||
@@ -2723,6 +2741,8 @@ public:
|
||||
AstSequence(FileLine* fl, const string& name, AstNode* stmtp)
|
||||
: ASTGEN_SUPER_Sequence(fl, name, stmtp) {}
|
||||
ASTGEN_MEMBERS_AstSequence;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool hasDType() const override VL_MT_SAFE { return true; }
|
||||
AstNodeFTask* cloneType(const string& name) override {
|
||||
return new AstSequence{fileline(), name, nullptr};
|
||||
@@ -2784,11 +2804,14 @@ class AstCoverCross final : public AstNodeFuncCovItem {
|
||||
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
|
||||
// @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items;
|
||||
// // post-LinkParse: empty
|
||||
// @astgen op4 := iffp : Optional[AstNodeExpr] // Conditional sampling guard
|
||||
public:
|
||||
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp)
|
||||
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp,
|
||||
AstNodeExpr* iffp = nullptr)
|
||||
: ASTGEN_SUPER_CoverCross(fl, name) {
|
||||
UASSERT(itemsp, "AstCoverCross requires at least one coverpoint reference");
|
||||
addItemsp(itemsp);
|
||||
this->iffp(iffp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverCross;
|
||||
void dump(std::ostream& str) const override;
|
||||
@@ -2889,6 +2912,7 @@ class AstClass final : public AstNodeModule {
|
||||
// @astgen op4 := extendsp : List[AstClassExtends]
|
||||
// MEMBERS
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
|
||||
// @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class
|
||||
uint32_t m_declTokenNum; // Declaration token number
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?)
|
||||
@@ -2913,6 +2937,10 @@ public:
|
||||
bool timescaleMatters() const override { return false; }
|
||||
AstClassPackage* classOrPackagep() const VL_MT_STABLE { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
|
||||
AstClass* covergroupEnclosingClassp() const VL_MT_STABLE {
|
||||
return m_covergroupEnclosingClassp;
|
||||
}
|
||||
void covergroupEnclosingClassp(AstClass* classp) { m_covergroupEnclosingClassp = classp; }
|
||||
AstNode* membersp() const VL_MT_STABLE { return stmtsp(); }
|
||||
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
|
||||
bool isCovergroup() const { return m_covergroup; }
|
||||
|
||||
+79
-14
@@ -163,6 +163,8 @@ protected:
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstNodeIf;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isGateDedupable() const override { return true; }
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
@@ -190,6 +192,8 @@ public:
|
||||
this->msbp(msbp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstNodeReadWriteMem;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() override { return false; }
|
||||
@@ -262,10 +266,10 @@ public:
|
||||
bool isOutputter() override { return true; }
|
||||
VAssertCtlType ctlType() const { return m_ctlType; }
|
||||
void ctlType(int32_t type) { m_ctlType = VAssertCtlType{type}; }
|
||||
VAssertType ctlAssertTypes() const { return m_assertTypes; }
|
||||
void ctlAssertTypes(VAssertType types) { m_assertTypes = types; }
|
||||
VAssertDirectiveType ctlDirectiveTypes() const { return m_directiveTypes; }
|
||||
void ctlDirectiveTypes(VAssertDirectiveType types) { m_directiveTypes = types; }
|
||||
VAssertType assertTypes() const { return m_assertTypes; }
|
||||
void assertTypes(VAssertType types) { m_assertTypes = types; }
|
||||
VAssertDirectiveType directiveTypes() const { return m_directiveTypes; }
|
||||
void directiveTypes(VAssertDirectiveType types) { m_directiveTypes = types; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
};
|
||||
@@ -297,6 +301,16 @@ public:
|
||||
AstSenTree* sentreep() const { return m_sentreep; }
|
||||
void clearSentreep() { m_sentreep = nullptr; }
|
||||
};
|
||||
class AstCLocalScope final : public AstNodeStmt {
|
||||
// Pack statements into an unnamed scope when generating C++
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
public:
|
||||
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_CLocalScope(fl) {
|
||||
addStmtsp(stmtsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCLocalScope;
|
||||
};
|
||||
class AstCReturn final : public AstNodeStmt {
|
||||
// C++ return from a function
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
@@ -382,6 +396,8 @@ public:
|
||||
, m_fromDollarC{fromDollarC} {}
|
||||
ASTGEN_MEMBERS_AstCStmtUser;
|
||||
// METHODS
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
@@ -447,10 +463,27 @@ public:
|
||||
, m_name{name}
|
||||
, m_showAt{showAt} {}
|
||||
ASTGEN_MEMBERS_AstComment;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Text
|
||||
bool sameNode(const AstNode* samep) const override { return true; } // Ignore name in comments
|
||||
virtual bool showAt() const { return m_showAt; }
|
||||
};
|
||||
class AstConstraintBefore final : public AstNodeStmt {
|
||||
// Constraint solve before item
|
||||
// @astgen op1 := lhssp : List[AstNodeExpr]
|
||||
// @astgen op2 := rhssp : List[AstNodeExpr]
|
||||
public:
|
||||
AstConstraintBefore(FileLine* fl, AstNodeExpr* lhssp, AstNodeExpr* rhssp)
|
||||
: ASTGEN_SUPER_ConstraintBefore(fl) {
|
||||
addLhssp(lhssp);
|
||||
addRhssp(rhssp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConstraintBefore;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstConstraintExpr final : public AstNodeStmt {
|
||||
// Constraint expression
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
@@ -557,11 +590,11 @@ class AstDelay final : public AstNodeStmt {
|
||||
// @astgen op3 := rhsp : Optional[AstNodeExpr] // Max bound for cycle range or fall delay
|
||||
// @astgen op4 := throughoutp : Optional[AstNodeExpr] // Throughout condition (IEEE 16.9.9)
|
||||
VTimescale m_timeunit; // Delay's time unit
|
||||
const bool m_isCycle; // True if it is a cycle delay
|
||||
const bool m_isCycleDelay; // True if it is a cycle delay
|
||||
public:
|
||||
AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycle)
|
||||
AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycleDelay)
|
||||
: ASTGEN_SUPER_Delay(fl)
|
||||
, m_isCycle{isCycle} {
|
||||
, m_isCycleDelay{isCycleDelay} {
|
||||
this->lhsp(lhsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstDelay;
|
||||
@@ -571,11 +604,11 @@ public:
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
bool isCycleDelay() const { return m_isCycle; }
|
||||
bool isRangeDelay() const { return m_isCycle && rhsp() != nullptr; }
|
||||
bool isCycleDelay() const { return m_isCycleDelay; }
|
||||
bool isRangeDelay() const { return m_isCycleDelay && rhsp(); }
|
||||
bool isUnbounded() const { return isRangeDelay() && VN_IS(rhsp(), Unbounded); }
|
||||
void fallDelay(AstNodeExpr* const fallDelayp) { rhsp(fallDelayp); }
|
||||
AstNodeExpr* fallDelay() const { return m_isCycle ? nullptr : rhsp(); }
|
||||
AstNodeExpr* fallDelay() const { return m_isCycleDelay ? nullptr : rhsp(); }
|
||||
};
|
||||
class AstDisable final : public AstNodeStmt {
|
||||
// @astgen op1 := targetRefp : Optional[AstNodeExpr] // Reference to link in V3LinkDot
|
||||
@@ -598,6 +631,7 @@ public:
|
||||
explicit AstDisableFork(FileLine* fl)
|
||||
: ASTGEN_SUPER_DisableFork(fl) {}
|
||||
ASTGEN_MEMBERS_AstDisableFork;
|
||||
string verilogKwd() const override { return "disable fork"; }
|
||||
};
|
||||
class AstDisplay final : public AstNodeStmt {
|
||||
// Parents: stmtlist
|
||||
@@ -667,6 +701,8 @@ public:
|
||||
bool isPure() override { return false; }
|
||||
virtual bool cleanOut() const { return true; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
VDumpCtlType ctlType() const { return m_ctlType; }
|
||||
};
|
||||
class AstEventControl final : public AstNodeStmt {
|
||||
@@ -785,6 +821,8 @@ public:
|
||||
this->operandp(operandp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstFireEvent;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool isDelayed() const { return m_delayed; }
|
||||
};
|
||||
class AstInitialAutomaticStmt final : public AstNodeStmt {
|
||||
@@ -928,6 +966,8 @@ public:
|
||||
: ASTGEN_SUPER_MonitorOff(fl)
|
||||
, m_off{off} {}
|
||||
ASTGEN_MEMBERS_AstMonitorOff;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string verilogKwd() const override { return m_off ? "$monitoroff" : "$monitoron"; }
|
||||
bool isGateOptimizable() const override { return false; } // Though deleted before opt
|
||||
bool isPredictOptimizable() const override { return false; } // Though deleted before opt
|
||||
@@ -940,9 +980,9 @@ public:
|
||||
bool off() const { return m_off; }
|
||||
};
|
||||
class AstPExprClause final : public AstNodeStmt {
|
||||
const bool m_pass; // True if will be replaced by passing assertion clause, false for
|
||||
const bool m_pass; // Will be replaced by passing assertion clause, false for
|
||||
// assertion failure clause
|
||||
const bool m_vacuous; // True if pass is vacuous
|
||||
const bool m_vacuous; // Pass is vacuous
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstPExprClause;
|
||||
@@ -950,6 +990,8 @@ public:
|
||||
: ASTGEN_SUPER_PExprClause(fl)
|
||||
, m_pass{pass}
|
||||
, m_vacuous{vacuous} {}
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool pass() const { return m_pass; }
|
||||
bool vacuous() const { return m_vacuous; }
|
||||
};
|
||||
@@ -1406,6 +1448,8 @@ public:
|
||||
, m_quotedPrefix{quotedPrefix} {}
|
||||
ASTGEN_MEMBERS_AstTracePushPrefix;
|
||||
bool sameNode(const AstNode* samep) const override { return false; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string prefix() const { return m_prefix; }
|
||||
VTracePrefixType prefixType() const { return m_prefixType; }
|
||||
int left() const { return m_left; }
|
||||
@@ -1431,6 +1475,7 @@ public:
|
||||
: ASTGEN_SUPER_WaitFork(fl) {}
|
||||
ASTGEN_MEMBERS_AstWaitFork;
|
||||
bool isTimingControl() const override { return true; }
|
||||
string verilogKwd() const override { return "wait fork"; }
|
||||
};
|
||||
|
||||
// === AstNodeAssign ===
|
||||
@@ -1568,15 +1613,23 @@ class AstFork final : public AstNodeBlock {
|
||||
//
|
||||
// @astgen op3 := forksp : List[AstBegin]
|
||||
const VJoinType m_joinType; // Join keyword type
|
||||
bool m_immediateStart = false; // Fork starts before its parent blocks or exits
|
||||
|
||||
public:
|
||||
AstFork(FileLine* fl, VJoinType joinType, const string& name = "")
|
||||
: ASTGEN_SUPER_Fork(fl, name)
|
||||
, m_joinType{joinType} {}
|
||||
ASTGEN_MEMBERS_AstFork;
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstFork* const asamep = VN_DBG_AS(samep, Fork);
|
||||
return joinType() == asamep->joinType() && immediateStart() == asamep->immediateStart();
|
||||
}
|
||||
bool isTimingControl() const override { return !joinType().joinNone(); }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
VJoinType joinType() const { return m_joinType; }
|
||||
bool immediateStart() const { return m_immediateStart; }
|
||||
void immediateStart(bool flag) { m_immediateStart = flag; }
|
||||
};
|
||||
|
||||
// === AstNodeCoverOrAssert ===
|
||||
@@ -1612,6 +1665,8 @@ class AstCover final : public AstNodeCoverOrAssert {
|
||||
// @astgen op3 := coverincsp: List[AstNode] // Coverage node
|
||||
bool m_isCoverSeq = false; // 'cover sequence' (IEEE 1800-2023 16.14.3): fires per
|
||||
// end-of-match, not per property success
|
||||
bool m_isSeqEvent = false; // Synthesized for a sequence used as an event control
|
||||
// (IEEE 1800-2023 9.4.2.4)
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstCover;
|
||||
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type,
|
||||
@@ -1622,6 +1677,8 @@ public:
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool isCoverSeq() const { return m_isCoverSeq; }
|
||||
void isCoverSeq(bool flag) { m_isCoverSeq = flag; }
|
||||
bool isSeqEvent() const { return m_isSeqEvent; }
|
||||
void isSeqEvent(bool flag) { m_isSeqEvent = flag; }
|
||||
};
|
||||
class AstRestrict final : public AstNodeCoverOrAssert {
|
||||
public:
|
||||
@@ -1636,10 +1693,16 @@ public:
|
||||
// === AstNodeForeach ===
|
||||
class AstConstraintForeach final : public AstNodeForeach {
|
||||
// Constraint foreach statement
|
||||
bool m_soft; // is soft foreach, non-standard extension
|
||||
public:
|
||||
AstConstraintForeach(FileLine* fl, AstForeachHeader* headerp, AstNode* bodyp)
|
||||
: ASTGEN_SUPER_ConstraintForeach(fl, headerp, bodyp) {}
|
||||
AstConstraintForeach(FileLine* fl, AstForeachHeader* headerp, AstNode* bodyp,
|
||||
bool soft = false)
|
||||
: ASTGEN_SUPER_ConstraintForeach(fl, headerp, bodyp)
|
||||
, m_soft{soft} {}
|
||||
ASTGEN_MEMBERS_AstConstraintForeach;
|
||||
bool isSoft() const { return m_soft; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
class AstForeach final : public AstNodeForeach {
|
||||
public:
|
||||
@@ -1670,6 +1733,8 @@ public:
|
||||
void unique0Pragma(bool flag) { m_unique0Pragma = flag; }
|
||||
bool priorityPragma() const { return m_priorityPragma; }
|
||||
void priorityPragma(bool flag) { m_priorityPragma = flag; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
};
|
||||
|
||||
// === AstNodeReadWriteMem ===
|
||||
|
||||
+399
-38
@@ -22,6 +22,8 @@
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3Hasher.h"
|
||||
#include "V3InstrCount.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3String.h"
|
||||
|
||||
#include "V3Ast__gen_impl.h" // Generated by 'astgen'
|
||||
@@ -108,11 +110,27 @@ int AstNodeSel::bitConst() const {
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
bool AstNode::isDisableQueuePushSelfStmt() {
|
||||
// Detect LinkJump-generated registration:
|
||||
// __VprocessQueue_*.push_back(std::process::self())
|
||||
AstStmtExpr* const stmtExprp = VN_CAST(this, StmtExpr);
|
||||
if (!stmtExprp) return false;
|
||||
AstCMethodHard* const methodp = VN_CAST(stmtExprp->exprp(), CMethodHard);
|
||||
if (!methodp || methodp->name() != "push_back") return false;
|
||||
AstNode* const basep = AstArraySel::baseFromp(methodp->fromp(), false);
|
||||
if (AstVarRef* const refp = VN_CAST(basep, VarRef)) return refp->varp()->processQueue();
|
||||
if (AstMemberSel* const selp = VN_CAST(basep, MemberSel)) {
|
||||
return selp->varp() && selp->varp()->processQueue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AstNodeStmt::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
void AstNodeStmt::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
|
||||
void AstNodeCCall::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (superReference()) str << " [SUPERREF]";
|
||||
if (funcp()) {
|
||||
str << " " << funcp()->name() << " => ";
|
||||
funcp()->dump(str);
|
||||
@@ -121,6 +139,7 @@ void AstNodeCCall::dump(std::ostream& str) const {
|
||||
}
|
||||
}
|
||||
void AstNodeCCall::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, superReference);
|
||||
if (funcp()) dumpJsonStr(str, "funcName", funcp()->name());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
@@ -420,14 +439,19 @@ void AstCReset::dumpJson(std::ostream& str) const {
|
||||
void AstCase::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " " << verilogKwd();
|
||||
if (fullPragma()) str << " [FULL]";
|
||||
if (parallelPragma()) str << " [PAR]";
|
||||
if (priorityPragma()) str << " [PRI]";
|
||||
if (uniquePragma()) str << " [UNQ]";
|
||||
if (unique0Pragma()) str << " [UNQ0]";
|
||||
}
|
||||
void AstCase::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "kwd", verilogKwd());
|
||||
dumpJsonBoolIf(str, "full", fullPragma());
|
||||
dumpJsonBoolIf(str, "parallel", parallelPragma());
|
||||
dumpJsonBoolIf(str, "priority", priorityPragma());
|
||||
dumpJsonBoolIf(str, "unique", uniquePragma());
|
||||
dumpJsonBoolIf(str, "unique0", unique0Pragma());
|
||||
dumpJsonBoolIf(str, "priority", priorityPragma());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
@@ -468,6 +492,14 @@ void AstSConsRep::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, unbounded);
|
||||
dumpJsonGen(str);
|
||||
} // LCOV_EXCL_STOP
|
||||
void AstSAnd::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (propertyControl()) str << " [PROPERTY_CONTROL]";
|
||||
}
|
||||
void AstSAnd::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, propertyControl);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstPropAlways::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (isStrong()) str << " [strong]";
|
||||
@@ -495,6 +527,7 @@ void AstConstraint::dump(std::ostream& str) const {
|
||||
str << " [PROTO]";
|
||||
if (isKwdPure()) str << " [KWDPURE]";
|
||||
if (isStatic()) str << " [STATIC]";
|
||||
if (baseOverride().isAny()) str << " [" << baseOverride().ascii() << "]";
|
||||
}
|
||||
void AstConstraint::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isExternDef);
|
||||
@@ -579,6 +612,15 @@ const char* AstNetlist::broken() const {
|
||||
for (const AstVar* const varp : m_deferredParamVarps) {
|
||||
BROKEN_RTN(!varp || !varp->brokeExists());
|
||||
}
|
||||
for (int i = 0; i < VEval::_ENUM_END; ++i) {
|
||||
const AstCFunc* const funcp = m_evalFuncps[i];
|
||||
const AstCFunc* const dumpp = m_dumpTriggersFuncps[i];
|
||||
BROKEN_RTN(funcp && !funcp->brokeExists());
|
||||
BROKEN_RTN(dumpp && !dumpp->brokeExists());
|
||||
// Once created, an entry point has a trigger dump iff it has triggers
|
||||
BROKEN_RTN(funcp && VEval{i}.hasTriggers() && !dumpp);
|
||||
BROKEN_RTN(dumpp && !VEval{i}.hasTriggers());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -694,13 +736,17 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
||||
|
||||
asRef = asRef || isDpiOpenArray() || (forFunc && (isWritable() || isRef() || isConstRef()));
|
||||
|
||||
if (forFunc && (isReadOnly() || constRef) && asRef) ostatic = ostatic + "const ";
|
||||
|
||||
string oname;
|
||||
if (named) {
|
||||
if (!namespc.empty()) oname += namespc + "::";
|
||||
oname += VIdProtect::protectIf(name(), protect());
|
||||
}
|
||||
if (forFunc && (isReadOnly() || constRef) && asRef) {
|
||||
if (VN_IS(dtypep()->skipRefp(), IfaceRefDType)) {
|
||||
return ostatic + dtypep()->cType("", forFunc, false) + " const &" + oname;
|
||||
}
|
||||
ostatic += "const ";
|
||||
}
|
||||
return ostatic + dtypep()->cType(oname, forFunc, asRef);
|
||||
}
|
||||
|
||||
@@ -1179,7 +1225,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
||||
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = "VlClassRef<" + EmitCUtil::prefixNameProtect(adtypep) + ">";
|
||||
const string className = EmitCUtil::prefixNameProtect(adtypep);
|
||||
info.m_type = adtypep->rawPointer() ? className + "*" : "VlClassRef<" + className + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = EmitCUtil::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
|
||||
@@ -1401,6 +1448,9 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
|
||||
} else if (VN_IS(nodep, WildcardSel)) {
|
||||
nodep = VN_AS(nodep, WildcardSel)->fromp();
|
||||
continue;
|
||||
} else if (VN_IS(nodep, CMethodHard)) {
|
||||
nodep = VN_AS(nodep, CMethodHard)->fromp();
|
||||
continue;
|
||||
} else if (overMembers && VN_IS(nodep, MemberSel)) {
|
||||
nodep = VN_AS(nodep, MemberSel)->fromp();
|
||||
continue;
|
||||
@@ -1815,10 +1865,16 @@ void AstNode::dump(std::ostream& str) const {
|
||||
#endif
|
||||
<< " {" << fileline()->filenameLetters() << std::dec << fileline()->lastLineno()
|
||||
<< fileline()->firstColumnLetters() << "}";
|
||||
if (user1p()) str << " u1=" << nodeAddr(user1p());
|
||||
if (user2p()) str << " u2=" << nodeAddr(user2p());
|
||||
if (user3p()) str << " u3=" << nodeAddr(user3p());
|
||||
if (user4p()) str << " u4=" << nodeAddr(user4p());
|
||||
const auto dumpUser = [&str](const char* prefix, const VNUser& user) {
|
||||
const std::string s = user.dumpStr([](const void* p) -> std::string {
|
||||
return nodeAddr(reinterpret_cast<const AstNode*>(p));
|
||||
});
|
||||
if (!s.empty()) str << prefix << s;
|
||||
};
|
||||
dumpUser(" u1=", user1u());
|
||||
dumpUser(" u2=", user2u());
|
||||
dumpUser(" u3=", user3u());
|
||||
dumpUser(" u4=", user4u());
|
||||
if (hasDType()) {
|
||||
// Final @ so less likely to by accident read it as a nodep
|
||||
if (dtypep() == this) {
|
||||
@@ -1830,6 +1886,7 @@ void AstNode::dump(std::ostream& str) const {
|
||||
} else { // V3Broken will throw an error
|
||||
if (dtypep()) str << " %Error-dtype-exp=null,got=" << nodeAddr(dtypep());
|
||||
}
|
||||
if (fileline()->erroringOn()) str << " [ERRORING]";
|
||||
if (name() != "") {
|
||||
if (VN_IS(this, Const)) {
|
||||
str << " " << name(); // Already quoted
|
||||
@@ -1933,15 +1990,15 @@ void AstAssertCtl::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (ctlType() != VAssertCtlType::_TO_BE_EVALUATED) {
|
||||
str << " [" << ctlType().ascii() << "]";
|
||||
str << " [" << ctlAssertTypes().ascii() << "]";
|
||||
str << " [" << ctlDirectiveTypes().ascii() << "]";
|
||||
str << " [" << assertTypes().ascii() << "]";
|
||||
str << " [" << directiveTypes().ascii() << "]";
|
||||
}
|
||||
}
|
||||
void AstAssertCtl::dumpJson(std::ostream& str) const {
|
||||
if (ctlType() != VAssertCtlType::_TO_BE_EVALUATED) {
|
||||
dumpJsonStr(str, "ctlType", ctlType().ascii());
|
||||
dumpJsonStr(str, "ctlAssertTypes", ctlAssertTypes().ascii());
|
||||
dumpJsonStr(str, "ctlDirectiveTypes", ctlDirectiveTypes().ascii());
|
||||
dumpJsonStr(str, "assertTypes", assertTypes().ascii());
|
||||
dumpJsonStr(str, "directiveTypes", directiveTypes().ascii());
|
||||
}
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
@@ -1977,6 +2034,18 @@ string AstBasicDType::prettyDTypeName(bool) const {
|
||||
void AstNodeExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
void AstNodeExpr::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
|
||||
void AstPropSpec::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (propStrength() != VPropStrength::DEFAULT) {
|
||||
str << " [" << VString::upcase(propStrength().ascii()) << "]";
|
||||
}
|
||||
}
|
||||
void AstPropSpec::dumpJson(std::ostream& str) const {
|
||||
if (propStrength() != VPropStrength::DEFAULT)
|
||||
dumpJsonStr(str, "strength", propStrength().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
AstConst::~AstConst() {
|
||||
// Only rare constants carry originating parameter-name metadata. For all other AstConst nodes,
|
||||
// the V3Number bit keeps this destructor from touching AstNetlist's side table. When the bit
|
||||
@@ -2022,6 +2091,8 @@ bool AstNodeExpr::isLValue() const {
|
||||
return varrefp->access().isWriteOrRW();
|
||||
} else if (const AstMemberSel* const memberselp = VN_CAST(this, MemberSel)) {
|
||||
return memberselp->access().isWriteOrRW();
|
||||
} else if (const AstStructSel* const structselp = VN_CAST(this, StructSel)) {
|
||||
return structselp->fromp()->isLValue();
|
||||
} else if (const AstSel* const selp = VN_CAST(this, Sel)) {
|
||||
return selp->fromp()->isLValue();
|
||||
} else if (const AstNodeSel* const nodeSelp = VN_CAST(this, NodeSel)) {
|
||||
@@ -2080,6 +2151,7 @@ void AstCell::dump(std::ostream& str) const {
|
||||
}
|
||||
}
|
||||
void AstCell::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, modName);
|
||||
dumpJsonStrFunc(str, origName);
|
||||
dumpJsonStrFunc(str, verilogName);
|
||||
dumpJsonBoolFuncIf(str, recursive);
|
||||
@@ -2110,6 +2182,14 @@ void AstCExpr::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolIf(str, "pure", m_pure);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstCExprUser::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (m_pure) str << " [PURE]";
|
||||
}
|
||||
void AstCExprUser::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolIf(str, "pure", m_pure);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
bool AstClass::isCacheableChild(const AstNode* nodep) {
|
||||
return VN_IS(nodep, Var) || VN_IS(nodep, Typedef)
|
||||
|| (VN_IS(nodep, Constraint) && !VN_AS(nodep, Constraint)->isExternProto())
|
||||
@@ -2136,26 +2216,32 @@ void AstClass::dump(std::ostream& str) const {
|
||||
if (isCovergroup()) str << " [CG]";
|
||||
if (isExtended()) str << " [EXT]";
|
||||
if (isInterfaceClass()) str << " [IFCCLS]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
if (useVirtualPublic()) str << " [VIRPUB]";
|
||||
if (isPrintedFrom()) str << " [PRINTED]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
if (needRNG()) str << " [NRNG]";
|
||||
if (useVirtualPublic()) str << " [VIRPUB]";
|
||||
if (baseOverride().isAny()) str << " [" << baseOverride().ascii() << "]";
|
||||
}
|
||||
void AstClass::dumpJson(std::ostream& str) const {
|
||||
// dumpJsonNumFunc(str, declTokenNum); // Not dumped as adding token changes whole file
|
||||
dumpJsonBoolFuncIf(str, isCovergroup);
|
||||
dumpJsonBoolFuncIf(str, isExtended);
|
||||
dumpJsonBoolFuncIf(str, isInterfaceClass);
|
||||
dumpJsonBoolFuncIf(str, isVirtual);
|
||||
dumpJsonBoolFuncIf(str, isPrintedFrom);
|
||||
dumpJsonBoolFuncIf(str, isVirtual);
|
||||
dumpJsonBoolFuncIf(str, needRNG);
|
||||
dumpJsonBoolFuncIf(str, useVirtualPublic);
|
||||
if (baseOverride().isAny()) dumpJsonStr(str, "baseOverride", baseOverride().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstClassExtends::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isImplements()) str << " [IMPLEMENTS]";
|
||||
if (isImplements()) str << " [IMPL]";
|
||||
if (parameterized()) str << " [PAR]";
|
||||
}
|
||||
void AstClassExtends::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isImplements);
|
||||
dumpJsonBoolFuncIf(str, parameterized);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
AstClass* AstClassExtends::classOrNullp() const {
|
||||
@@ -2181,8 +2267,22 @@ void AstClassRefDType::dump(std::ostream& str) const {
|
||||
} else {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
if (rawPointer()) str << " [RAWPTR]";
|
||||
}
|
||||
void AstClassRefDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, rawPointer);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstClassRefDType::selfTest() {
|
||||
FileLine* const fl = new FileLine{FileLine::commandLineFilename()};
|
||||
AstClassRefDType* const owningp = new AstClassRefDType{fl, nullptr, nullptr};
|
||||
AstClassRefDType* const rawp = new AstClassRefDType{fl, nullptr, nullptr};
|
||||
rawp->rawPointer(true);
|
||||
UASSERT_OBJ(!owningp->sameNode(rawp) && !rawp->sameNode(owningp) && rawp->sameNode(rawp), rawp,
|
||||
"Raw class pointer must have distinct type identity");
|
||||
VL_DO_DANGLING(owningp->deleteTree(), owningp);
|
||||
VL_DO_DANGLING(rawp->deleteTree(), rawp);
|
||||
}
|
||||
void AstClassRefDType::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstClassRefDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "class:" << name();
|
||||
@@ -2241,11 +2341,21 @@ void AstNodeCoverOrAssert::dumpJson(std::ostream& str) const {
|
||||
void AstCover::dump(std::ostream& str) const {
|
||||
this->AstNodeCoverOrAssert::dump(str);
|
||||
if (isCoverSeq()) str << " [COVERSEQ]";
|
||||
if (isSeqEvent()) str << " [SEQEVENT]";
|
||||
}
|
||||
void AstCover::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isCoverSeq);
|
||||
dumpJsonBoolFuncIf(str, isSeqEvent);
|
||||
this->AstNodeCoverOrAssert::dumpJson(str);
|
||||
}
|
||||
void AstConstraintForeach::dump(std::ostream& str) const {
|
||||
this->AstNodeForeach::dump(str);
|
||||
if (isSoft()) str << "[SOFT]";
|
||||
}
|
||||
void AstConstraintForeach::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isSoft);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstClocking::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isDefault()) str << " [DEFAULT]";
|
||||
@@ -2286,7 +2396,26 @@ void AstDisplay::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " [" << displayType().ascii() << "]";
|
||||
}
|
||||
void AstDisplay::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstDisplay::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "displayType", displayType().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstDistItem::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (isWhole()) str << " [WHOLE]";
|
||||
}
|
||||
void AstDistItem::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isWhole);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstDumpCtl::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " [" << ctlType().ascii() << "]";
|
||||
}
|
||||
void AstDumpCtl::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "ctlType", ctlType().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstEnumDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
str << " enum";
|
||||
@@ -2348,6 +2477,29 @@ void AstGenBlock::dumpJson(std::ostream& str) const {
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
void AstNodeIf::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isBoundsCheck()) str << " [BOUNDS]";
|
||||
}
|
||||
void AstNodeIf::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolIf(str, "isBoundsCheck", isBoundsCheck());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstIf::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " " << verilogKwd();
|
||||
if (priorityPragma()) str << " [PRI]";
|
||||
if (uniquePragma()) str << " [UNQ]";
|
||||
if (unique0Pragma()) str << " [UNQ0]";
|
||||
}
|
||||
void AstIf::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "kwd", verilogKwd());
|
||||
dumpJsonBoolIf(str, "priority", priorityPragma());
|
||||
dumpJsonBoolIf(str, "unique", uniquePragma());
|
||||
dumpJsonBoolIf(str, "unique0", unique0Pragma());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
void AstIfaceRefDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (isPortDecl()) str << " [PORTDECL]";
|
||||
@@ -2485,13 +2637,17 @@ const char* AstLoopTest::broken() const {
|
||||
|
||||
void AstMemberDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (isConstrainedRand()) str << " [CONSTRAINEDRAND]";
|
||||
if (isConstrainedRand()) str << " [CSRAND]";
|
||||
if (rand().isRandomizable()) str << " [" << rand() << "]";
|
||||
if (lsb()) str << " lsb=" << lsb();
|
||||
if (name() != "") str << " name=" << name();
|
||||
if (tag() != "") str << " tag=" << tag();
|
||||
}
|
||||
|
||||
void AstMemberDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isConstrainedRand);
|
||||
if (rand().isRandomizable()) dumpJsonStr(str, "rand", rand().ascii());
|
||||
if (lsb() != 0) dumpJsonStr(str, "lsb", std::to_string(lsb()));
|
||||
dumpJsonStrFunc(str, name);
|
||||
dumpJsonStrFunc(str, tag);
|
||||
dumpJsonGen(str);
|
||||
@@ -2584,6 +2740,14 @@ void AstModule::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, hasGenericIface);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstMonitorOff::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (off()) str << " [OFF]";
|
||||
}
|
||||
void AstMonitorOff::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, off);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstPin::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (modVarp()) {
|
||||
@@ -2592,11 +2756,13 @@ void AstPin::dump(std::ostream& str) const {
|
||||
} else {
|
||||
str << " ->UNLINKED";
|
||||
}
|
||||
if (param()) str << " [PAR]";
|
||||
if (!paramPath().empty()) str << " paramPath=" << paramPath();
|
||||
if (svDotName()) str << " [.n]";
|
||||
if (svImplicit()) str << " [.SV]";
|
||||
}
|
||||
void AstPin::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, param);
|
||||
dumpJsonBoolFuncIf(str, svDotName);
|
||||
dumpJsonBoolFuncIf(str, svImplicit);
|
||||
if (!paramPath().empty()) dumpJsonStr(str, "paramPath", paramPath());
|
||||
@@ -2630,7 +2796,14 @@ void AstPrintTimeScale::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "timeunit", timeunit().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
void AstPull::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (direction()) str << " [UP]";
|
||||
}
|
||||
void AstPull::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, direction);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstTime::dump(std::ostream& str) const {
|
||||
this->AstNodeTermop::dump(str);
|
||||
str << " " << timeunit();
|
||||
@@ -2655,6 +2828,16 @@ void AstTimeImport::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "timeunit", timeunit().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstTracePushPrefix::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (m_quotedPrefix) str << " [QUOTE]";
|
||||
str << " [" << prefixType().ascii() << "]";
|
||||
}
|
||||
void AstTracePushPrefix::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, quotedPrefix);
|
||||
dumpJsonStr(str, "prefixType", prefixType().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstTypedef::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (attrPublic()) str << " [PUBLIC]";
|
||||
@@ -2680,6 +2863,14 @@ void AstTypedefFwd::dumpJson(std::ostream& str) const {
|
||||
}
|
||||
void AstNodeRange::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
void AstNodeRange::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstRand::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (urandom()) str << " [U]";
|
||||
}
|
||||
void AstRand::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolIf(str, "urandom", urandom());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstRandSequence::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " start=" << start();
|
||||
@@ -2706,8 +2897,18 @@ void AstRange::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, fromBracket);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstNodeReadWriteMem::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (isHex()) str << " [HEX]";
|
||||
}
|
||||
void AstNodeReadWriteMem::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isHex);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstParamTypeDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (fwdType() != VFwdType::NONE) str << " [" << fwdType().ascii() << "]";
|
||||
str << " [" << varType().ascii() << "]";
|
||||
if (subDTypep()) {
|
||||
str << " -> ";
|
||||
subDTypep()->dump(str);
|
||||
@@ -2715,7 +2916,19 @@ void AstParamTypeDType::dump(std::ostream& str) const {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstParamTypeDType::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstParamTypeDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "fwdType", fwdType().ascii());
|
||||
dumpJsonStr(str, "varType", varType().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstParseTypeDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (fwdType() != VFwdType::NONE) str << " [" << fwdType().ascii() << "]";
|
||||
}
|
||||
void AstParseTypeDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "fwdType", fwdType().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstRefDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (typedefp() || subDTypep()) {
|
||||
@@ -2752,15 +2965,25 @@ void AstNodeUOrStructDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (packed()) str << " [PACKED]";
|
||||
if (isFourstate()) str << " [4STATE]";
|
||||
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
|
||||
if (isConstrainedRand()) str << " [CSRAND]";
|
||||
if (emitToString()) str << " [EMITSTR]";
|
||||
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
|
||||
}
|
||||
void AstNodeUOrStructDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, packed);
|
||||
dumpJsonBoolFuncIf(str, isConstrainedRand);
|
||||
dumpJsonBoolFuncIf(str, isFourstate);
|
||||
dumpJsonBoolFuncIf(str, emitToString);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstUdpTableLine::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (udpIsCombo()) str << " [COMBO]";
|
||||
}
|
||||
void AstUdpTableLine::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, udpIsCombo);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstUnionDType::dump(std::ostream& str) const {
|
||||
this->AstNodeUOrStructDType::dump(str);
|
||||
if (isSoft()) str << " [soft]";
|
||||
@@ -2872,11 +3095,21 @@ std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
|
||||
}
|
||||
return dims;
|
||||
}
|
||||
void AstNBACommitQueueDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (partial()) str << " [PARTIAL]";
|
||||
}
|
||||
void AstNBACommitQueueDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, partial);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstNetlist::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (timescaleSpecified()) str << " [TIMESCALES]";
|
||||
str << " [" << timeunit() << "/" << timeprecision() << "]";
|
||||
}
|
||||
void AstNetlist::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolIf(str, "timescaleSpecified", timescaleSpecified());
|
||||
dumpJsonStr(str, "timeunit", timeunit().ascii());
|
||||
dumpJsonStr(str, "timeprecision", timeprecision().ascii());
|
||||
dumpJsonGen(str);
|
||||
@@ -2887,13 +3120,13 @@ void AstNetlist::deleteContents() {
|
||||
m_constPoolp = nullptr;
|
||||
m_dollarUnitPkgp = nullptr;
|
||||
m_stdPackagep = nullptr;
|
||||
m_evalp = nullptr;
|
||||
m_evalNbap = nullptr;
|
||||
m_dpiExportTriggerp = nullptr;
|
||||
m_delaySchedulerp = nullptr;
|
||||
m_nbaEventp = nullptr;
|
||||
m_nbaEventTriggerp = nullptr;
|
||||
m_topScopep = nullptr;
|
||||
m_evalFuncps.fill(nullptr);
|
||||
m_dumpTriggersFuncps.fill(nullptr);
|
||||
if (op1p()) op1p()->unlinkFrBackWithNext()->deleteTree();
|
||||
if (op2p()) op2p()->unlinkFrBackWithNext()->deleteTree();
|
||||
if (op3p()) op3p()->unlinkFrBackWithNext()->deleteTree();
|
||||
@@ -2937,6 +3170,18 @@ AstFuncRef* AstNetlist::stdPackageProcessSelfp(FileLine* flp) const {
|
||||
processSelfp->classOrPackagep(v3Global.rootp()->stdPackageProcessp());
|
||||
return processSelfp;
|
||||
}
|
||||
void AstNetlist::addEvalStats(const std::string& phase) {
|
||||
if (!v3Global.opt.stats()) return;
|
||||
for (int i = 0; i < VEval::_ENUM_END; ++i) {
|
||||
VEval eval{i};
|
||||
AstCFunc* const funcp = m_evalFuncps[i];
|
||||
if (!funcp) continue;
|
||||
const uint32_t nodes = funcp->nodeCount();
|
||||
const uint32_t instr = V3InstrCount::count(funcp, false);
|
||||
V3Stats::addStat("Size of eval, nodes - '" + eval.tag() + "', " + phase, nodes);
|
||||
V3Stats::addStat("Size of eval, instr - '" + eval.tag() + "', " + phase, instr);
|
||||
}
|
||||
}
|
||||
void AstNodeModule::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " L" << level();
|
||||
@@ -2959,6 +3204,7 @@ void AstNodeModule::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, origName);
|
||||
dumpJsonStrFunc(str, verilogName);
|
||||
dumpJsonNumFunc(str, level);
|
||||
dumpJsonNumFunc(str, depth);
|
||||
dumpJsonBoolFuncIf(str, modPublic);
|
||||
dumpJsonBoolFuncIf(str, inLibrary);
|
||||
dumpJsonBoolFuncIf(str, ctorVarReset);
|
||||
@@ -2970,6 +3216,16 @@ void AstNodeModule::dumpJson(std::ostream& str) const {
|
||||
if (libname() != "work") dumpJsonStr(str, "libname=", libname());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstPExprClause::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (pass()) str << " [PASS]";
|
||||
if (vacuous()) str << " [VACUOUS]";
|
||||
}
|
||||
void AstPExprClause::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, pass);
|
||||
dumpJsonBoolFuncIf(str, vacuous);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstPackageExport::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (packagep()) {
|
||||
@@ -2978,7 +3234,10 @@ void AstPackageExport::dump(std::ostream& str) const {
|
||||
str << " ->UNLINKED:" << pkgName();
|
||||
}
|
||||
}
|
||||
void AstPackageExport::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstPackageExport::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, pkgName);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstPackageImport::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (packagep()) {
|
||||
@@ -2991,6 +3250,14 @@ void AstPackageImport::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstPackageImport::pkgNameFrom() {
|
||||
if (packagep()) m_pkgName = packagep()->name();
|
||||
}
|
||||
void AstPast::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (propertyTiming()) str << " [PROPERTY_TIMING]";
|
||||
}
|
||||
void AstPast::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, propertyTiming);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstPatMember::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (isConcat()) str << " [CONCAT]";
|
||||
@@ -3025,13 +3292,23 @@ VFormatAttr AstSFormatArg::formatAttrDefauled(const AstSFormatArg* nodep,
|
||||
void AstSFormatF::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (exprFormat()) str << " [EXPRFMT]";
|
||||
if (hidden()) str << " [HID]";
|
||||
if (optionalFormat()) str << " [OPTFMT]";
|
||||
}
|
||||
void AstSFormatF::dumpJson(std::ostream& str) const {
|
||||
dumpJsonGen(str);
|
||||
dumpJsonBoolFuncIf(str, exprFormat);
|
||||
dumpJsonBoolFuncIf(str, hidden);
|
||||
dumpJsonBoolFuncIf(str, optionalFormat);
|
||||
}
|
||||
void AstSampled::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (internal()) str << " [INTERNAL]";
|
||||
}
|
||||
void AstSampled::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, internal);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstSel::dump(std::ostream& str) const {
|
||||
this->AstNodeBiop::dump(str);
|
||||
str << " widthConst=" << this->widthConst();
|
||||
@@ -3122,6 +3399,15 @@ bool AstWildcardArrayDType::similarDTypeNode(const AstNodeDType* samep) const {
|
||||
const AstWildcardArrayDType* const asamep = VN_DBG_AS(samep, WildcardArrayDType);
|
||||
return asamep->subDTypep() && subDTypep()->similarDType(asamep->subDTypep());
|
||||
}
|
||||
void AstUnpackArrayDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (isCompound()) str << " [COMPOUND]";
|
||||
str << " " << declRange();
|
||||
}
|
||||
void AstUnpackArrayDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isCompound);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
bool AstUnpackArrayDType::similarDTypeNode(const AstNodeDType* samep) const {
|
||||
const AstUnpackArrayDType* const asamep = VN_DBG_AS(samep, UnpackArrayDType);
|
||||
return hi() == asamep->hi() && rangep()->sameTree(asamep->rangep())
|
||||
@@ -3152,6 +3438,14 @@ void AstVoidDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "void";
|
||||
}
|
||||
void AstIfaceGenericDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (modportName() != "") str << " mp=" << modportName();
|
||||
}
|
||||
void AstIfaceGenericDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, modportName);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstIfaceGenericDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "generic_interface";
|
||||
@@ -3256,6 +3550,7 @@ int AstVarRef::instrCount() const {
|
||||
void AstVar::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (constPoolEntry()) str << " [CONSTPOOL]";
|
||||
if (covergroupRefMember()) str << " [CGREF]";
|
||||
if (isSc()) str << " [SC]";
|
||||
if (isPrimaryIO()) str << (isInout() ? " [PIO]" : (isWritable() ? " [PO]" : " [PI]"));
|
||||
if (isPrimaryClock()) str << " [PCLK]";
|
||||
@@ -3271,7 +3566,7 @@ void AstVar::dump(std::ostream& str) const {
|
||||
if (isInternal()) str << " [INTERNAL]";
|
||||
if (isLatched()) str << " [LATCHED]";
|
||||
if (isUsedLoopIdx()) str << " [LOOPIDX]";
|
||||
if (rand().isRandomizable()) str << " [" << rand() << "]";
|
||||
if (rand().isRandomizable()) str << " [" << rand().ascii() << "]";
|
||||
if (noCReset()) str << " [!CRST]";
|
||||
if (noReset()) str << " [!RST]";
|
||||
if (processQueue()) str << " [PROCQ]";
|
||||
@@ -3298,6 +3593,7 @@ void AstVar::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, origName);
|
||||
dumpJsonStrFunc(str, verilogName);
|
||||
dumpJsonBoolFuncIf(str, constPoolEntry);
|
||||
dumpJsonBoolFuncIf(str, covergroupRefMember);
|
||||
dumpJsonBoolFuncIf(str, isSc);
|
||||
dumpJsonBoolFuncIf(str, isPrimaryIO);
|
||||
dumpJsonBoolFuncIf(str, isPrimaryClock);
|
||||
@@ -3335,6 +3631,7 @@ void AstVar::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, hasUserInit);
|
||||
dumpJsonBoolFuncIf(str, ignorePostWrite);
|
||||
dumpJsonBoolFuncIf(str, ignoreSchedWrite);
|
||||
if (rand().isRandomizable()) dumpJsonStr(str, "rand", rand().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstScope::dump(std::ostream& str) const {
|
||||
@@ -3401,7 +3698,7 @@ void AstClassOrPackageRef::dump(std::ostream& str) const {
|
||||
}
|
||||
}
|
||||
void AstClassOrPackageRef::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp() const {
|
||||
AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp(const bool doRefs) const {
|
||||
AstNode* foundp = m_classOrPackageNodep;
|
||||
AstNode* lastp = nullptr;
|
||||
while (foundp != lastp) {
|
||||
@@ -3409,11 +3706,12 @@ AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp() const {
|
||||
if (AstNodeDType* const anodep = VN_CAST(foundp, NodeDType)) {
|
||||
foundp = anodep->skipRefOrNullp();
|
||||
}
|
||||
if (const AstTypedef* const anodep = VN_CAST(foundp, Typedef)) {
|
||||
foundp = anodep->subDTypep();
|
||||
}
|
||||
if (const AstClassRefDType* const anodep = VN_CAST(foundp, ClassRefDType)) {
|
||||
foundp = anodep->classp();
|
||||
if (doRefs) {
|
||||
if (const AstTypedef* const anodep = VN_CAST(foundp, Typedef)) {
|
||||
foundp = anodep->subDTypep();
|
||||
} else if (const AstClassRefDType* const anodep = VN_CAST(foundp, ClassRefDType)) {
|
||||
foundp = anodep->classp();
|
||||
}
|
||||
}
|
||||
}
|
||||
return VN_CAST(foundp, NodeModule);
|
||||
@@ -3441,6 +3739,8 @@ void AstNodeFTaskRef::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
|
||||
if (containsGenBlock()) str << " [GENBLK]";
|
||||
if (pli()) str << " [PLI]";
|
||||
if (superReference()) str << " [SUPERREF]";
|
||||
str << " -> ";
|
||||
if (dotted() != "") str << ".=" << dotted() << " ";
|
||||
if (taskp()) {
|
||||
@@ -3452,6 +3752,8 @@ void AstNodeFTaskRef::dump(std::ostream& str) const {
|
||||
void AstNodeFTaskRef::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, dotted);
|
||||
dumpJsonBoolFuncIf(str, containsGenBlock);
|
||||
dumpJsonBoolFuncIf(str, pli);
|
||||
dumpJsonBoolFuncIf(str, superReference);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstNodeFTask::dump(std::ostream& str) const {
|
||||
@@ -3473,6 +3775,7 @@ void AstNodeFTask::dump(std::ostream& str) const {
|
||||
if (verilogFunction()) str << " [VFUNC]";
|
||||
if (needProcess()) str << " [NPRC]";
|
||||
if ((dpiImport() || dpiExport()) && cname() != name()) str << " [c=" << cname() << "]";
|
||||
if (baseOverride().isAny()) str << " [" << baseOverride().ascii() << "]";
|
||||
}
|
||||
bool AstNodeFTask::isPure() {
|
||||
if (!m_purity.isCached()) m_purity.set(getPurityRecurse());
|
||||
@@ -3538,6 +3841,22 @@ void AstBegin::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, needProcess);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstComment::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (showAt()) str << " [SHOWAT]";
|
||||
}
|
||||
void AstComment::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, showAt);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstCompareNN::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (ignoreCase()) str << " [IGNORECASE]";
|
||||
}
|
||||
void AstCompareNN::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, ignoreCase);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstNodeCoverDecl::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (!page().empty()) str << " page=" << page();
|
||||
@@ -3597,12 +3916,38 @@ void AstCoverInc::dump(std::ostream& str) const {
|
||||
}
|
||||
}
|
||||
void AstCoverInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstDpiExport::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (cname() != "" && cname() != name()) str << " [c=" << cname() << "]";
|
||||
}
|
||||
void AstDpiExport::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "cname", cname());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstFireEvent::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (isDelayed()) str << " [DLY]";
|
||||
}
|
||||
void AstFireEvent::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isDelayed);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstFork::dump(std::ostream& str) const {
|
||||
this->AstNodeBlock::dump(str);
|
||||
str << " [" << joinType() << "]";
|
||||
if (immediateStart()) str << " [IMMEDIATE]";
|
||||
}
|
||||
void AstFork::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "joinType", joinType().ascii());
|
||||
dumpJsonBoolFuncIf(str, immediateStart);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstSequence::dump(std::ostream& str) const {
|
||||
this->AstNodeFTask::dump(str);
|
||||
if (isReferenced()) str << " [REFED]";
|
||||
}
|
||||
void AstSequence::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isReferenced);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstStop::dump(std::ostream& str) const {
|
||||
@@ -3615,10 +3960,12 @@ void AstStop::dumpJson(std::ostream& str) const {
|
||||
}
|
||||
void AstTraceDecl::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (inDtypeFunc()) str << " [DT]";
|
||||
if (codeAssigned()) str << " [code=" << code() << "]";
|
||||
if (dtypeCallp()) str << " [dtypeCallp=" << dtypeCallp() << "]";
|
||||
}
|
||||
void AstTraceDecl::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, inDtypeFunc);
|
||||
dumpJsonNumFunc(str, code);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
@@ -3685,6 +4032,7 @@ void AstCFunc::dump(std::ostream& str) const {
|
||||
if (needProcess()) str << " [NPRC]";
|
||||
if (entryPoint()) str << " [ENTRY]";
|
||||
if (noLife()) str << " [NOLIFE]";
|
||||
if (!cname().empty() && cname() != name()) str << " [c=" << cname() << "]";
|
||||
}
|
||||
void AstCFunc::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, slow);
|
||||
@@ -3700,6 +4048,7 @@ void AstCFunc::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isCoroutine);
|
||||
dumpJsonBoolFuncIf(str, needProcess);
|
||||
dumpJsonBoolFuncIf(str, noLife);
|
||||
dumpJsonStrFunc(str, cname);
|
||||
dumpJsonGen(str);
|
||||
// TODO: maybe try to shorten these flags somehow
|
||||
}
|
||||
@@ -3746,6 +4095,14 @@ void AstCStmt::dumpJson(std::ostream& str) const {
|
||||
dumpJsonGen(str);
|
||||
if (!stmtType().isNone()) dumpJsonStr(str, "stmtType", stmtType().ascii());
|
||||
}
|
||||
void AstCStmtUser::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (!fromDollarC()) str << " [$C]";
|
||||
}
|
||||
void AstCStmtUser::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, fromDollarC);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstCUse::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << useType() << "]";
|
||||
@@ -3776,10 +4133,12 @@ string AstCase::pragmaString() const {
|
||||
}
|
||||
|
||||
void AstCgOptionAssign::dump(std::ostream& str) const {
|
||||
str << " [" << optType().ascii() << "]";
|
||||
if (typeOption()) str << " [TYPEOPT]";
|
||||
this->AstNode::dump(str);
|
||||
}
|
||||
void AstCgOptionAssign::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "optType", optType().ascii());
|
||||
dumpJsonBoolFuncIf(str, typeOption);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
@@ -3793,7 +4152,7 @@ void AstDelay::dumpJson(std::ostream& str) const {
|
||||
}
|
||||
|
||||
const char* AstDisable::broken() const {
|
||||
BROKEN_RTN((m_targetp && targetRefp()) || ((!m_targetp && !targetRefp())));
|
||||
BROKEN_RTN(!m_targetp && !targetRefp());
|
||||
return nullptr;
|
||||
}
|
||||
void AstDisable::dump(std::ostream& str) const {
|
||||
@@ -3861,14 +4220,16 @@ void AstCoverpoint::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem
|
||||
|
||||
void AstCoverBin::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " " << m_type.ascii();
|
||||
str << " " << m_binsType.ascii();
|
||||
if (m_isArray) str << "[]";
|
||||
if (m_isWildcard) str << "[*]";
|
||||
}
|
||||
|
||||
void AstCoverBin::dumpJson(std::ostream& str) const {
|
||||
this->AstNode::dumpJson(str);
|
||||
str << ", \"binsType\": \"" << m_type.ascii() << "\"";
|
||||
if (m_isArray) str << ", \"isArray\": true";
|
||||
dumpJsonBoolIf(str, "isArray", isArray());
|
||||
dumpJsonBoolIf(str, "isWildcard", isWildcard());
|
||||
str << ", \"binsType\": \"" << binsType().ascii() << "\"";
|
||||
}
|
||||
|
||||
void AstCoverTransItem::dump(std::ostream& str) const {
|
||||
@@ -3891,12 +4252,12 @@ void AstCoverCross::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem
|
||||
|
||||
void AstCoverOption::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " " << m_type.ascii();
|
||||
str << " " << m_optType.ascii();
|
||||
}
|
||||
|
||||
void AstCoverOption::dumpJson(std::ostream& str) const {
|
||||
this->AstNode::dumpJson(str);
|
||||
str << ", \"optionType\": \"" << m_type.ascii() << "\"";
|
||||
str << ", \"optType\": \"" << m_optType.ascii() << "\"";
|
||||
}
|
||||
|
||||
void AstCoverpointRef::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user