Compare commits
209 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
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 |
|
|
@ -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,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 }}
|
||||||
|
|
@ -16,6 +16,7 @@ on:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
actions: read
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|
@ -29,272 +30,141 @@ concurrency:
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build-2604-gcc:
|
build-2604-gcc:
|
||||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
name: Build | 26.04 | gcc
|
||||||
uses: ./.github/workflows/reusable-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
with:
|
with:
|
||||||
|
cc: gcc
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
sha: ${{ github.sha }}
|
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:
|
build-2604-clang:
|
||||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
name: Build | 26.04 | clang
|
||||||
uses: ./.github/workflows/reusable-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
with:
|
with:
|
||||||
|
cc: clang
|
||||||
|
dev-asan: true # Build (and run) with address sanitizer
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
sha: ${{ github.sha }}
|
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:
|
build-2404-gcc:
|
||||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
name: Build | 24.04 | gcc
|
||||||
uses: ./.github/workflows/reusable-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
with:
|
with:
|
||||||
|
cc: gcc
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
sha: ${{ github.sha }}
|
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:
|
build-2404-clang:
|
||||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
name: Build | 24.04 | clang
|
||||||
uses: ./.github/workflows/reusable-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
with:
|
with:
|
||||||
|
cc: clang
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
sha: ${{ github.sha }}
|
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:
|
build-2204-gcc:
|
||||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
name: Build | 22.04 | gcc
|
||||||
uses: ./.github/workflows/reusable-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
with:
|
with:
|
||||||
|
cc: gcc
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
sha: ${{ github.sha }}
|
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:
|
build-macos-15-clang:
|
||||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
name: Build | macos-15 | clang
|
||||||
uses: ./.github/workflows/reusable-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
with:
|
with:
|
||||||
|
cc: clang
|
||||||
|
runs-on: macos-15
|
||||||
sha: ${{ github.sha }}
|
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:
|
build-windows:
|
||||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}
|
name: Build | windows-2025-vs2026 | msvc
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: windows-2025-vs2026
|
||||||
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
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
path: repo
|
path: repo
|
||||||
- name: Cache $CCACHE_DIR
|
- name: Cache win_flex_bison
|
||||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||||
with:
|
with:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ github.workspace }}/win_flex_bison
|
||||||
key: msbuild-msvc-cmake
|
key: win_flex_bison
|
||||||
- name: compile
|
- name: compile
|
||||||
env:
|
|
||||||
WIN_FLEX_BISON: ${{ github.workspace }}/.ccache
|
|
||||||
run: ./ci/ci-win-compile.ps1
|
run: ./ci/ci-win-compile.ps1
|
||||||
- name: test build
|
- name: test build
|
||||||
run: ./ci/ci-win-test.ps1
|
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:
|
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
|
needs: build-2604-gcc
|
||||||
uses: ./.github/workflows/reusable-test.yml
|
uses: ./.github/workflows/reusable-test.yml
|
||||||
with:
|
with:
|
||||||
archive: ${{ needs.build-2604-gcc.outputs.archive }}
|
archive: ${{ needs.build-2604-gcc.outputs.archive }}
|
||||||
os: ${{ matrix.os }}
|
cc: gcc
|
||||||
cc: ${{ matrix.cc }}
|
runs-on: ubuntu-26.04
|
||||||
reloc: ${{ matrix.reloc }}
|
|
||||||
suite: ${{ matrix.suite }}
|
suite: ${{ matrix.suite }}
|
||||||
dev-gcov: 0
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||||
# 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}
|
|
||||||
|
|
||||||
test-2604-clang:
|
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
|
needs: build-2604-clang
|
||||||
uses: ./.github/workflows/reusable-test.yml
|
uses: ./.github/workflows/reusable-test.yml
|
||||||
with:
|
with:
|
||||||
archive: ${{ needs.build-2604-clang.outputs.archive }}
|
archive: ${{ needs.build-2604-clang.outputs.archive }}
|
||||||
os: ${{ matrix.os }}
|
cc: clang
|
||||||
cc: ${{ matrix.cc }}
|
runs-on: ubuntu-26.04
|
||||||
reloc: ${{ matrix.reloc }}
|
|
||||||
suite: ${{ matrix.suite }}
|
suite: ${{ matrix.suite }}
|
||||||
dev-gcov: 0
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||||
# 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}
|
|
||||||
|
|
||||||
test-2404-gcc:
|
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
|
needs: build-2404-gcc
|
||||||
uses: ./.github/workflows/reusable-test.yml
|
uses: ./.github/workflows/reusable-test.yml
|
||||||
with:
|
with:
|
||||||
archive: ${{ needs.build-2404-gcc.outputs.archive }}
|
archive: ${{ needs.build-2404-gcc.outputs.archive }}
|
||||||
os: ${{ matrix.os }}
|
cc: gcc
|
||||||
cc: ${{ matrix.cc }}
|
runs-on: ubuntu-24.04
|
||||||
reloc: ${{ matrix.reloc }}
|
|
||||||
suite: ${{ matrix.suite }}
|
suite: ${{ matrix.suite }}
|
||||||
dev-gcov: 0
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||||
# 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}
|
|
||||||
|
|
||||||
test-2404-clang:
|
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
|
needs: build-2404-clang
|
||||||
uses: ./.github/workflows/reusable-test.yml
|
uses: ./.github/workflows/reusable-test.yml
|
||||||
with:
|
with:
|
||||||
archive: ${{ needs.build-2404-clang.outputs.archive }}
|
archive: ${{ needs.build-2404-clang.outputs.archive }}
|
||||||
os: ${{ matrix.os }}
|
cc: clang
|
||||||
cc: ${{ matrix.cc }}
|
reloc: true # Test with relocated installation
|
||||||
reloc: ${{ matrix.reloc }}
|
runs-on: ubuntu-24.04
|
||||||
suite: ${{ matrix.suite }}
|
suite: ${{ matrix.suite }}
|
||||||
dev-gcov: 0
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||||
# 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}
|
|
||||||
|
|
||||||
test-2204-gcc:
|
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
|
needs: build-2204-gcc
|
||||||
uses: ./.github/workflows/reusable-test.yml
|
uses: ./.github/workflows/reusable-test.yml
|
||||||
with:
|
with:
|
||||||
archive: ${{ needs.build-2204-gcc.outputs.archive }}
|
archive: ${{ needs.build-2204-gcc.outputs.archive }}
|
||||||
os: ${{ matrix.os }}
|
cc: gcc
|
||||||
cc: ${{ matrix.cc }}
|
runs-on: ubuntu-22.04
|
||||||
reloc: ${{ matrix.reloc }}
|
|
||||||
suite: ${{ matrix.suite }}
|
suite: ${{ matrix.suite }}
|
||||||
dev-gcov: 0
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||||
# 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}
|
|
||||||
|
|
||||||
lint-py:
|
lint-py:
|
||||||
name: Lint Python
|
name: Lint Python
|
||||||
|
|
|
||||||
|
|
@ -16,5 +16,5 @@ jobs:
|
||||||
name: "'docs/CONTRIBUTORS' was signed"
|
name: "'docs/CONTRIBUTORS' was signed"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
- run: test_regress/t/t_dist_contributors.py
|
- run: test_regress/t/t_dist_contributors.py
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ on:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
actions: read
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|
@ -38,15 +39,13 @@ jobs:
|
||||||
(github.event_name == 'workflow_dispatch')
|
(github.event_name == 'workflow_dispatch')
|
||||||
uses: ./.github/workflows/reusable-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
with:
|
with:
|
||||||
|
cc: gcc
|
||||||
|
dev-gcov: true
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
# For pull requests, build the head of the pull request branch, not the
|
# For pull requests, build the head of the pull request branch, not the
|
||||||
# merge commit, otherwise patch coverage would include the changes
|
# merge commit, otherwise patch coverage would include the changes
|
||||||
# between the root of the pull request and the target branch
|
# 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 }}
|
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:
|
test:
|
||||||
name: Test | ${{ matrix.test }}${{ matrix.num }}
|
name: Test | ${{ matrix.test }}${{ matrix.num }}
|
||||||
|
|
@ -54,11 +53,10 @@ jobs:
|
||||||
uses: ./.github/workflows/reusable-test.yml
|
uses: ./.github/workflows/reusable-test.yml
|
||||||
with:
|
with:
|
||||||
archive: ${{ needs.build.outputs.archive }}
|
archive: ${{ needs.build.outputs.archive }}
|
||||||
os: ubuntu-24.04
|
|
||||||
cc: gcc
|
cc: gcc
|
||||||
reloc: 0
|
dev-gcov: true
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
suite: ${{ matrix.test }}${{ matrix.num }}
|
suite: ${{ matrix.test }}${{ matrix.num }}
|
||||||
dev-gcov: 1
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|
@ -74,7 +72,7 @@ jobs:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
|
|
||||||
- name: Download code coverage data
|
- name: Download code coverage data
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||||
|
|
@ -121,7 +119,7 @@ jobs:
|
||||||
|
|
||||||
- name: Unpack repository archive
|
- name: Unpack repository archive
|
||||||
run: |
|
run: |
|
||||||
tar -x -z -f ${{ needs.build.outputs.archive }}
|
tar --zstd -x -f ${{ needs.build.outputs.archive }}
|
||||||
ls -lsha
|
ls -lsha
|
||||||
|
|
||||||
- name: Download code coverage data
|
- name: Download code coverage data
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
|
|
||||||
- name: Extract context variables
|
- name: Extract context variables
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -54,7 +54,7 @@ jobs:
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: docker_meta
|
id: docker_meta
|
||||||
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
|
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
||||||
|
|
@ -64,21 +64,21 @@ jobs:
|
||||||
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
|
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4
|
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
|
||||||
with:
|
with:
|
||||||
buildkitd-flags: --debug
|
buildkitd-flags: --debug
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
|
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Build and Push to Docker
|
- 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'
|
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
|
||||||
with:
|
with:
|
||||||
context: ${{ env.build_context }}
|
context: ${{ env.build_context }}
|
||||||
|
|
|
||||||
|
|
@ -11,35 +11,44 @@ on:
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: repo
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
format:
|
format:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
name: Ubuntu 24.04 | format
|
name: Ubuntu 24.04 | format
|
||||||
env:
|
|
||||||
CI_OS_NAME: linux
|
|
||||||
CI_RUNS_ON: ubuntu-24.04
|
|
||||||
CI_COMMIT: ${{ github.sha }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
|
path: repo
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Install packages for build
|
- name: Install packages for build
|
||||||
env:
|
|
||||||
CI_BUILD_STAGE_NAME: build
|
|
||||||
run: |
|
run: |
|
||||||
sudo apt install clang-format-18 || \
|
sudo apt install clang-format-18 || \
|
||||||
sudo apt install clang-format-18
|
sudo apt install clang-format-18
|
||||||
git config --global user.email "action@example.com"
|
git config --global user.email "action@example.com"
|
||||||
git config --global user.name "github action"
|
git config --global user.name "github action"
|
||||||
- name: Format code
|
|
||||||
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
autoconf
|
autoconf
|
||||||
./configure
|
./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
|
source .venv/bin/activate
|
||||||
make -j 4 format CLANGFORMAT=clang-format-18
|
make -j 4 format CLANGFORMAT=clang-format-18
|
||||||
git status
|
git status
|
||||||
|
|
||||||
- name: Push
|
- name: Push
|
||||||
run: |-
|
run: |-
|
||||||
if [ -n "$(git status --porcelain)" ]; then
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ name: Pages
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [master]
|
branches: [master]
|
||||||
paths: ["ci/**", ".github/workflows"]
|
paths: ["ci/**", ".github/workflows/**"]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
workflow_run:
|
workflow_run:
|
||||||
workflows: ["Code coverage", "RTLMeter"]
|
workflows: ["Code coverage", "RTLMeter"]
|
||||||
|
|
@ -22,8 +22,11 @@ permissions:
|
||||||
# Allow only one concurrent deployment, skipping runs queued between the run
|
# 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
|
# in-progress and latest queued. However, do NOT cancel in-progress runs as we
|
||||||
# want to allow these deployments to complete.
|
# 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:
|
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
|
cancel-in-progress: false
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
|
|
@ -34,11 +37,15 @@ jobs:
|
||||||
build:
|
build:
|
||||||
name: Build content
|
name: Build content
|
||||||
runs-on: ubuntu-24.04
|
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:
|
outputs:
|
||||||
pr-run-ids: ${{ steps.build.outputs.pr-run-ids }}
|
pr-run-ids: ${{ steps.build.outputs.pr-run-ids }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
- name: Build pages
|
- name: Build pages
|
||||||
id: build
|
id: build
|
||||||
env:
|
env:
|
||||||
|
|
@ -58,9 +65,16 @@ jobs:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
environment:
|
environment:
|
||||||
name: github-pages
|
name: github-pages
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deploy-2.outputs.page_url || steps.deploy-1.outputs.page_url }}
|
||||||
steps:
|
steps:
|
||||||
|
# GitHub's Pages backend intermittently fails a deployment mid-sync, retry
|
||||||
- name: Deploy to GitHub Pages
|
- 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
|
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
|
||||||
|
|
||||||
notify:
|
notify:
|
||||||
|
|
@ -70,7 +84,7 @@ jobs:
|
||||||
if: ${{ github.repository == 'verilator/verilator' }}
|
if: ${{ github.repository == 'verilator/verilator' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
# Use the Verilator CI app to post the comment
|
# Use the Verilator CI app to post the comment
|
||||||
- name: Generate access token
|
- name: Generate access token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
|
|
|
||||||
|
|
@ -7,37 +7,47 @@ name: reusable-build
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
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:
|
sha:
|
||||||
description: "Commit SHA to build"
|
description: "Commit SHA to build"
|
||||||
required: true
|
required: true
|
||||||
type: string
|
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:
|
outputs:
|
||||||
archive:
|
archive:
|
||||||
description: "Name of the built repository archive artifact"
|
description: "Name of the built archive artifact"
|
||||||
value: ${{ jobs.build.outputs.archive }}
|
value: ${{ jobs.build.outputs.archive }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CI_OS_NAME: ${{ inputs.os-name }}
|
CACHE_BASE_KEY: build-${{ inputs.runs-on }}-${{ inputs.cc }}${{ inputs.ccwarn && '-ccwarn' || '' }}${{ inputs.dev-asan && '-asan' || '' }}${{ inputs.dev-gcov && '-gcov' || '' }}
|
||||||
CCACHE_COMPRESS: 1
|
CCACHE_COMPILERCHECK: content
|
||||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
|
||||||
INSTALL_DIR: ${{ github.workspace }}/install
|
|
||||||
RELOC_DIR: ${{ github.workspace }}/relloc
|
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|
@ -48,26 +58,28 @@ jobs:
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: ${{ inputs.os }}
|
runs-on: ${{ inputs.runs-on }}
|
||||||
outputs:
|
outputs:
|
||||||
archive: ${{ steps.create-archive.outputs.archive }}
|
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:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
path: repo
|
path: repo
|
||||||
ref: ${{ inputs.sha }}
|
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
|
- name: Cache $CCACHE_DIR
|
||||||
|
if: ${{ env.CCACHE_DISABLE != '1' }}
|
||||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||||
env:
|
env:
|
||||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||||
|
|
@ -75,25 +87,44 @@ jobs:
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: ${{ env.CACHE_KEY }}-${{ inputs.sha }}
|
key: ${{ env.CACHE_KEY }}-${{ inputs.sha }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ env.CACHE_KEY }}-
|
${{ env.CACHE_KEY }}
|
||||||
|
|
||||||
- name: Install packages for build
|
- name: Install packages for build
|
||||||
run: ./ci/ci-install.bash
|
run: ./ci/ci-install.bash build
|
||||||
|
|
||||||
- name: 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
|
id: create-archive
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
run: |
|
run: |
|
||||||
# Name of the archive must be unique based on the build parameters
|
# Archive name, unique per build; flavour tags mark non-default builds
|
||||||
ARCHIVE=verilator-${{ inputs.sha }}-${{ inputs.os }}-${{ inputs.cc }}-${{ inputs.dev-asan }}-${{ inputs.dev-gcov }}.tar.gz
|
ARCHIVE="verilator-${{ inputs.sha }}-${{ inputs.runs-on }}-${{ inputs.cc }}"
|
||||||
tar --posix -c -z -f $ARCHIVE repo
|
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"
|
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Upload repository archive
|
- name: Upload archive
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
|
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
|
||||||
name: ${{ 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:
|
on:
|
||||||
workflow_call:
|
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:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
@ -27,23 +19,22 @@ jobs:
|
||||||
name: Sub-lint | Python
|
name: Sub-lint | Python
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
path: repo
|
path: repo
|
||||||
|
|
||||||
- name: Install packages for build
|
- name: Install dependencies
|
||||||
run: ./ci/ci-install.bash
|
run: ./ci/ci-install.bash lint-py
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
autoconf
|
autoconf
|
||||||
./configure --enable-longtests --enable-ccwarn
|
./configure --enable-longtests --enable-ccwarn
|
||||||
|
|
||||||
- name: Install python dependencies
|
- name: Set up Python venv
|
||||||
run: |
|
uses: ./repo/.github/actions/setup-venv
|
||||||
sudo apt install python3-clang || \
|
with:
|
||||||
sudo apt install python3-clang
|
save: ${{ github.ref == 'refs/heads/master' }}
|
||||||
make venv
|
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: |-
|
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
|
type: string
|
||||||
required: true
|
required: true
|
||||||
verilator-archive-new:
|
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
|
type: string
|
||||||
required: true
|
required: true
|
||||||
verilator-archive-old:
|
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
|
type: string
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
|
|
@ -53,8 +53,8 @@ defaults:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CCACHE_DIR: ${{ github.workspace }}/ccache
|
# RTLMeter measures compile/execute times, so caching must stay off to keep
|
||||||
CCACHE_MAXSIZE: 512M
|
# timings representative; ccache is still on PATH but acts as a pass-through
|
||||||
CCACHE_DISABLE: 1
|
CCACHE_DISABLE: 1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
@ -73,7 +73,7 @@ jobs:
|
||||||
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev
|
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev
|
||||||
|
|
||||||
- name: Checkout RTLMeter
|
- name: Checkout RTLMeter
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
repository: "verilator/rtlmeter"
|
repository: "verilator/rtlmeter"
|
||||||
path: rtlmeter
|
path: rtlmeter
|
||||||
|
|
@ -82,14 +82,6 @@ jobs:
|
||||||
working-directory: rtlmeter
|
working-directory: rtlmeter
|
||||||
run: make venv
|
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
|
# Run with new Verilator
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
@ -101,7 +93,7 @@ jobs:
|
||||||
|
|
||||||
- name: Unpack Verilator installation archive - new
|
- name: Unpack Verilator installation archive - new
|
||||||
run: |
|
run: |
|
||||||
tar -x -z -f ${{ inputs.verilator-archive-new }}
|
tar --zstd -x -f ${{ inputs.verilator-archive-new }}
|
||||||
mv install verilator-new
|
mv install verilator-new
|
||||||
|
|
||||||
- name: Compile cases - new
|
- name: Compile cases - new
|
||||||
|
|
@ -164,7 +156,7 @@ jobs:
|
||||||
- name: Unpack Verilator installation archive - old
|
- name: Unpack Verilator installation archive - old
|
||||||
if: ${{ inputs.verilator-archive-old != '' }}
|
if: ${{ inputs.verilator-archive-old != '' }}
|
||||||
run: |
|
run: |
|
||||||
tar -x -z -f ${{ inputs.verilator-archive-old }}
|
tar --zstd -x -f ${{ inputs.verilator-archive-old }}
|
||||||
mv install verilator-old
|
mv install verilator-old
|
||||||
|
|
||||||
- name: Compile cases - old
|
- name: Compile cases - old
|
||||||
|
|
|
||||||
|
|
@ -11,29 +11,37 @@ on:
|
||||||
description: "Name of the repository archive artifact from reusable-build"
|
description: "Name of the repository archive artifact from reusable-build"
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
os: # e.g. ubuntu-24.04
|
cc:
|
||||||
required: true
|
description: "Compiler to use: 'gcc' or 'clang'"
|
||||||
type: string
|
|
||||||
cc: # gcc or clang
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
reloc: # 0 or 1
|
|
||||||
required: true
|
|
||||||
type: number
|
|
||||||
suite: # e.g. dist-vlt-0
|
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
dev-gcov:
|
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
|
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:
|
env:
|
||||||
CI_OS_NAME: linux
|
CCACHE_COMPILERCHECK: content
|
||||||
CCACHE_COMPRESS: 1
|
|
||||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
|
||||||
INSTALL_DIR: ${{ github.workspace }}/install
|
|
||||||
RELOC_DIR: ${{ github.workspace }}/relloc
|
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|
@ -43,15 +51,8 @@ defaults:
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ${{ inputs.os }}
|
runs-on: ${{ inputs.runs-on }}
|
||||||
name: Test
|
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:
|
steps:
|
||||||
|
|
||||||
- name: Download repository archive
|
- name: Download repository archive
|
||||||
|
|
@ -63,32 +64,43 @@ jobs:
|
||||||
- name: Unpack repository archive
|
- name: Unpack repository archive
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
run: |
|
run: |
|
||||||
tar -x -z -f ${{ inputs.archive }}
|
tar --zstd -x -f ${{ inputs.archive }}
|
||||||
ls -lsha
|
ls -lsha
|
||||||
|
|
||||||
- name: Cache $CCACHE_DIR
|
- name: Configure ccache
|
||||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
run: |
|
||||||
env:
|
# ccache is unreliable on macOS, and does nothing with 'gcc --coverage'
|
||||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2
|
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:
|
with:
|
||||||
|
mode: restore
|
||||||
path: ${{ env.CCACHE_DIR }}
|
path: ${{ env.CCACHE_DIR }}
|
||||||
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
|
key: ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.suite }}
|
||||||
restore-keys: |
|
|
||||||
${{ env.CACHE_KEY }}-
|
|
||||||
|
|
||||||
- name: Install test dependencies
|
- name: Install test dependencies
|
||||||
run: |
|
run: |
|
||||||
./ci/ci-install.bash
|
./ci/ci-install.bash test
|
||||||
make venv
|
|
||||||
|
- name: Set up Python venv
|
||||||
|
uses: ./repo/.github/actions/setup-venv
|
||||||
|
with:
|
||||||
|
save: ${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
id: run-test
|
id: run-test
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
env:
|
|
||||||
TESTS: ${{ inputs.suite }}
|
|
||||||
run: |
|
run: |
|
||||||
source .venv/bin/activate
|
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
|
- name: Combine code coverage data
|
||||||
if: ${{ inputs.dev-gcov }}
|
if: ${{ inputs.dev-gcov }}
|
||||||
|
|
@ -104,6 +116,16 @@ jobs:
|
||||||
path: ${{ github.workspace }}/repo/obj_coverage/verilator-${{ inputs.suite }}.info
|
path: ${{ github.workspace }}/repo/obj_coverage/verilator-${{ inputs.suite }}.info
|
||||||
name: code-coverage-${{ inputs.suite }}
|
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
|
- name: Fail job if a test failed
|
||||||
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
|
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
|
||||||
run: |-
|
run: |-
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ jobs:
|
||||||
cases: ${{ steps.cases.outputs.cases }}
|
cases: ${{ steps.cases.outputs.cases }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
|
|
||||||
- name: Startup
|
- name: Startup
|
||||||
id: start
|
id: start
|
||||||
|
|
@ -65,39 +65,47 @@ jobs:
|
||||||
build-gcc-new:
|
build-gcc-new:
|
||||||
name: Build New Verilator - GCC
|
name: Build New Verilator - GCC
|
||||||
needs: start
|
needs: start
|
||||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
with:
|
with:
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
cc: gcc
|
cc: gcc
|
||||||
|
ccwarn: false
|
||||||
|
install: true
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
sha: ${{ github.sha }}
|
sha: ${{ github.sha }}
|
||||||
|
|
||||||
build-clang-new:
|
build-clang-new:
|
||||||
name: Build New Verilator - Clang
|
name: Build New Verilator - Clang
|
||||||
needs: start
|
needs: start
|
||||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
with:
|
with:
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
cc: clang
|
cc: clang
|
||||||
|
ccwarn: false
|
||||||
|
install: true
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
sha: ${{ github.sha }}
|
sha: ${{ github.sha }}
|
||||||
|
|
||||||
build-gcc-old:
|
build-gcc-old:
|
||||||
name: Build Old Verilator - GCC
|
name: Build Old Verilator - GCC
|
||||||
needs: start
|
needs: start
|
||||||
if: ${{ needs.start.outputs.old-sha != '' }}
|
if: ${{ needs.start.outputs.old-sha != '' }}
|
||||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
with:
|
with:
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
cc: gcc
|
cc: gcc
|
||||||
|
ccwarn: false
|
||||||
|
install: true
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
sha: ${{ needs.start.outputs.old-sha }}
|
sha: ${{ needs.start.outputs.old-sha }}
|
||||||
|
|
||||||
build-clang-old:
|
build-clang-old:
|
||||||
name: Build Old Verilator - Clang
|
name: Build Old Verilator - Clang
|
||||||
needs: start
|
needs: start
|
||||||
if: ${{ needs.start.outputs.old-sha != '' }}
|
if: ${{ needs.start.outputs.old-sha != '' }}
|
||||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
with:
|
with:
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
cc: clang
|
cc: clang
|
||||||
|
ccwarn: false
|
||||||
|
install: true
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
sha: ${{ needs.start.outputs.old-sha }}
|
sha: ${{ needs.start.outputs.old-sha }}
|
||||||
|
|
||||||
run-gcc:
|
run-gcc:
|
||||||
|
|
@ -208,7 +216,7 @@ jobs:
|
||||||
run: echo "tags=$(jq -r 'keys | map(sub("^run-"; "")) | join(" ")' <<< '${{ toJSON(needs) }}')" >> "$GITHUB_OUTPUT"
|
run: echo "tags=$(jq -r 'keys | map(sub("^run-"; "")) | join(" ")' <<< '${{ toJSON(needs) }}')" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Checkout RTLMeter
|
- name: Checkout RTLMeter
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
repository: "verilator/rtlmeter"
|
repository: "verilator/rtlmeter"
|
||||||
path: rtlmeter
|
path: rtlmeter
|
||||||
|
|
@ -298,7 +306,7 @@ jobs:
|
||||||
repositories: verilator-rtlmeter-results
|
repositories: verilator-rtlmeter-results
|
||||||
permission-contents: write
|
permission-contents: write
|
||||||
- name: Checkout verilator-rtlmeter-results
|
- name: Checkout verilator-rtlmeter-results
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
repository: "verilator/verilator-rtlmeter-results"
|
repository: "verilator/verilator-rtlmeter-results"
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
@ -331,7 +339,7 @@ jobs:
|
||||||
actions: read
|
actions: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout RTLMeter
|
- name: Checkout RTLMeter
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
repository: "verilator/rtlmeter"
|
repository: "verilator/rtlmeter"
|
||||||
path: rtlmeter
|
path: rtlmeter
|
||||||
|
|
@ -341,7 +349,7 @@ jobs:
|
||||||
run: make venv
|
run: make venv
|
||||||
|
|
||||||
- name: Checkout Verilator
|
- name: Checkout Verilator
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||||
with:
|
with:
|
||||||
path: verilator
|
path: verilator
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15)
|
||||||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||||
project(
|
project(
|
||||||
Verilator
|
Verilator
|
||||||
VERSION 5.050
|
VERSION 5.051
|
||||||
HOMEPAGE_URL https://verilator.org
|
HOMEPAGE_URL https://verilator.org
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
|
|
@ -155,11 +155,12 @@ install(
|
||||||
foreach(
|
foreach(
|
||||||
program
|
program
|
||||||
verilator
|
verilator
|
||||||
verilator_gantt
|
|
||||||
verilator_ccache_report
|
verilator_ccache_report
|
||||||
|
verilator_coverage
|
||||||
verilator_difftree
|
verilator_difftree
|
||||||
verilator_profcfunc
|
verilator_gantt
|
||||||
verilator_includer
|
verilator_includer
|
||||||
|
verilator_profcfunc
|
||||||
)
|
)
|
||||||
install(PROGRAMS bin/${program} TYPE BIN)
|
install(PROGRAMS bin/${program} TYPE BIN)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
|
||||||
121
Changes
121
Changes
|
|
@ -10,6 +10,125 @@ The changes in each Verilator version are described below. The
|
||||||
contributors that suggested or implemented a given issue are shown in []. Thanks!
|
contributors that suggested or implemented a given issue are shown in []. Thanks!
|
||||||
|
|
||||||
|
|
||||||
|
Verilator 5.051 devel
|
||||||
|
==========================
|
||||||
|
|
||||||
|
**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 only differ in lexical case (#7992) (#8020). [Paul Campbell]
|
||||||
|
* Add `+verilator+assert+lock` to ignore RTL assert control statements (#8086). [Sumanth Kadiyala]
|
||||||
|
* 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]
|
||||||
|
* 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 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 temporary insertion for single bit replicates in DFG (#8110). [Geza Lore, Testorrent USA, Inc.]
|
||||||
|
* Fix $finish continuing event loop (#7267) (#7950). [Artur Bieniek, Antmicro Ltd.]
|
||||||
|
* Fix $display accepting streaming concat arguments (#7663) (#7890). [Jaeuk Lee]
|
||||||
|
* 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 independent force of multiply instantiated signals (#7905). [Artur Bieniek, Antmicro Ltd.]
|
||||||
|
* Fix release of forced port nets (#7907) (#7901). [Artur Bieniek, 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 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 randomize() with a user variable named 'item' (#7993) (#7994).
|
||||||
|
* Fix UNSATCONSTR reporting constraint indices above 9 (#7995). [Yilou Wang]
|
||||||
|
* Fix MULTIDRIVEN/PROC 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 skipping non-constrained enum constraining when inside object of other class (#8075). [Kornel Uriasz, Antmicro Ltd.]
|
||||||
|
* 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 hierarchical class scope resolution (#8094). [Artur Bieniek, Antmicro Ltd.]
|
||||||
|
* 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 COVERIGN on SVA goto repetition (#8118). [Artur Bieniek, Antmicro Ltd.]
|
||||||
|
|
||||||
|
|
||||||
Verilator 5.050 2026-07-01
|
Verilator 5.050 2026-07-01
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
|
@ -5738,7 +5857,7 @@ Verilator 3.104 2003-04-30
|
||||||
**Major:**
|
**Major:**
|
||||||
|
|
||||||
* Indicate direction of ports with VL_IN and VL_OUT.
|
* 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%
|
* Numerous performance improvements, worth about 25%
|
||||||
|
|
||||||
**Minor:**
|
**Minor:**
|
||||||
|
|
|
||||||
|
|
@ -536,6 +536,7 @@ PY_PROGRAMS = \
|
||||||
src/vlcovgen \
|
src/vlcovgen \
|
||||||
test_regress/*.py \
|
test_regress/*.py \
|
||||||
test_regress/t/*.pf \
|
test_regress/t/*.pf \
|
||||||
|
test_regress/t/randomize_solver_tamper.py \
|
||||||
|
|
||||||
# Python files, subject to format but not lint
|
# Python files, subject to format but not lint
|
||||||
PY_FILES = \
|
PY_FILES = \
|
||||||
|
|
|
||||||
|
|
@ -636,6 +636,7 @@ description of these arguments.
|
||||||
|
|
||||||
=for VL_SPHINX_EXTRACT "_build/gen/args_verilated.rst"
|
=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+coverage+file+<filename> Set coverage output filename
|
||||||
+verilator+debug Enable debugging
|
+verilator+debug Enable debugging
|
||||||
+verilator+debugi+<value> Enable debugging at a level
|
+verilator+debugi+<value> Enable debugging at a level
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
#!/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"
|
||||||
|
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
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# DESCRIPTION: Verilator: CI dependency install script
|
# 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
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
@ -10,141 +10,168 @@
|
||||||
# required by the particular build stage.
|
# 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 -e
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
cd $(dirname "$0")/..
|
cd $(dirname "$0")/..
|
||||||
|
|
||||||
|
source "$(dirname "$0")/ci-common.bash"
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Parse arguments
|
||||||
|
|
||||||
|
# Which stage to install dependencies for: 'build' or 'test'
|
||||||
|
STAGE="$1"
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Install dependencies
|
||||||
|
|
||||||
# Avoid occasional cpan failures "Issued certificate has expired."
|
# Avoid occasional cpan failures "Issued certificate has expired."
|
||||||
export PERL_LWP_SSL_VERIFY_HOSTNAME=0
|
export PERL_LWP_SSL_VERIFY_HOSTNAME=0
|
||||||
echo "check_certificate = off" >> ~/.wgetrc
|
echo "check_certificate = off" >> ~/.wgetrc
|
||||||
|
|
||||||
fatal() {
|
if [ "$HOST_OS" = "linux" ]; then
|
||||||
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
# 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/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/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
|
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||||
|
elif [ "$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
|
fi
|
||||||
|
|
||||||
install-wavediff() {
|
install-wavediff() {
|
||||||
source ci/docker/buildenv/wavetools.conf
|
source ci/docker/buildenv/wavetools.conf
|
||||||
local _base_url="https://github.com/hudson-trading/wavetools/releases/download/${WAVETOOLS_VERSION}"
|
local _base_url="https://github.com/hudson-trading/wavetools/releases/download/${WAVETOOLS_VERSION}"
|
||||||
local _platform
|
local _platform
|
||||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
if [ "$HOST_OS" = "linux" ]; then
|
||||||
_platform="linux-x86_64"
|
_platform="linux-x86_64"
|
||||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
elif [ "$HOST_OS" = "macOS" ]; then
|
||||||
_platform="macos-arm64"
|
_platform="macos-arm64"
|
||||||
elif [ "$CI_OS_NAME" = "windows" ]; then
|
|
||||||
_platform="windows-x86_64"
|
|
||||||
else
|
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
|
return 0
|
||||||
fi
|
fi
|
||||||
local _tmpdir
|
local _tmpdir
|
||||||
_tmpdir=$(mktemp -d)
|
_tmpdir=$(mktemp -d)
|
||||||
local _archive="wavetools-${WAVETOOLS_VERSION}-${_platform}"
|
local _archive="wavetools-${WAVETOOLS_VERSION}-${_platform}"
|
||||||
if [ "$CI_OS_NAME" = "windows" ]; then
|
wget -q -O "${_tmpdir}/${_archive}.tar.gz" "${_base_url}/${_archive}.tar.gz"
|
||||||
wget -q -O "${_tmpdir}/${_archive}.zip" "${_base_url}/${_archive}.zip"
|
tar -xzf "${_tmpdir}/${_archive}.tar.gz" -C "${_tmpdir}"
|
||||||
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
|
|
||||||
sudo cp "${_tmpdir}/${_archive}/wavediff" /usr/local/bin/wavediff
|
sudo cp "${_tmpdir}/${_archive}/wavediff" /usr/local/bin/wavediff
|
||||||
rm -rf "${_tmpdir}"
|
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
|
# Dependencies of jobs in the 'build' stage, i.e.: packages required to
|
||||||
# build Verilator
|
# build Verilator
|
||||||
|
|
||||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
if [ "$HOST_OS" = "linux" ]; then
|
||||||
sudo apt-get update ||
|
if [ "$DISTRO_ID" = "ubuntu" ]; then
|
||||||
sudo apt-get update
|
PACKAGES=(
|
||||||
sudo apt-get install --yes ccache help2man libfl-dev ||
|
bear
|
||||||
sudo apt-get install --yes ccache help2man libfl-dev
|
ccache
|
||||||
if [[ ! "$CI_RUNS_ON" =~ "ubuntu-22.04" ]]; then
|
help2man
|
||||||
# Some conflict of libunwind verison on 22.04, can live without it for now
|
libfl-dev
|
||||||
sudo apt-get install --yes libjemalloc-dev ||
|
libsystemc-dev
|
||||||
sudo apt-get install --yes libjemalloc-dev
|
mold
|
||||||
fi
|
)
|
||||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
# libunwind conflict on 22.04, can live without libjemalloc there
|
||||||
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
|
if [ "$DISTRO_VERSION" != "22.04" ]; then
|
||||||
sudo apt-get install --yes libsystemc libsystemc-dev ||
|
PACKAGES+=(libjemalloc-dev)
|
||||||
sudo apt-get install --yes libsystemc libsystemc-dev
|
|
||||||
fi
|
fi
|
||||||
|
sudo apt-get update ||
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --yes "${PACKAGES[@]}" ||
|
||||||
|
sudo apt-get install --yes "${PACKAGES[@]}"
|
||||||
fi
|
fi
|
||||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
elif [ "$HOST_OS" = "macOS" ]; then
|
||||||
sudo apt-get install --yes bear mold ||
|
PACKAGES=(
|
||||||
sudo apt-get install --yes bear mold
|
autoconf
|
||||||
fi
|
bison
|
||||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
ccache
|
||||||
|
flex
|
||||||
|
gperftools
|
||||||
|
help2man
|
||||||
|
perl
|
||||||
|
)
|
||||||
brew update ||
|
brew update ||
|
||||||
brew update
|
brew update
|
||||||
brew install ccache perl gperftools autoconf bison flex help2man ||
|
brew install "${PACKAGES[@]}" ||
|
||||||
brew install ccache perl gperftools autoconf bison flex help2man
|
brew install "${PACKAGES[@]}"
|
||||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
|
||||||
sudo pkg install -y autoconf bison ccache gmake perl5
|
|
||||||
else
|
else
|
||||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
fatal "Unknown HOST_OS: '$HOST_OS'"
|
||||||
fi
|
fi
|
||||||
|
elif [ "$STAGE" = "test" ]; then
|
||||||
if [ -n "$CCACHE_DIR" ]; then
|
|
||||||
mkdir -p "$CCACHE_DIR"
|
|
||||||
fi
|
|
||||||
elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# Dependencies of jobs in the 'test' stage, i.e.: packages required to
|
# Dependencies of jobs in the 'test' stage, i.e.: packages required to
|
||||||
# run the tests
|
# run the tests
|
||||||
|
|
||||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
if [ "$HOST_OS" = "linux" ]; then
|
||||||
sudo apt-get update ||
|
if [ "$DISTRO_ID" = "ubuntu" ]; then
|
||||||
sudo apt-get update
|
PACKAGES=(
|
||||||
# libfl-dev needed for internal coverage's test runs
|
ccache
|
||||||
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3 ||
|
gdb
|
||||||
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3
|
jq
|
||||||
# Required for test_regress/t/t_dist_attributes.py
|
lcov
|
||||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
libfl-dev
|
||||||
sudo apt-get install --yes python3-clang mold ||
|
libsystemc-dev
|
||||||
sudo apt-get install --yes python3-clang mold
|
mold
|
||||||
|
python3-clang
|
||||||
|
z3
|
||||||
|
)
|
||||||
|
sudo apt-get update ||
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --yes "${PACKAGES[@]}" ||
|
||||||
|
sudo apt-get install --yes "${PACKAGES[@]}"
|
||||||
fi
|
fi
|
||||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
elif [ "$HOST_OS" = "macOS" ]; then
|
||||||
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
|
PACKAGES=(
|
||||||
sudo apt-get install --yes libsystemc libsystemc-dev ||
|
ccache
|
||||||
sudo apt-get install --yes libsystemc libsystemc-dev
|
jq
|
||||||
fi
|
perl
|
||||||
fi
|
z3
|
||||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
)
|
||||||
|
brew update ||
|
||||||
brew update
|
brew update
|
||||||
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
|
brew install "${PACKAGES[@]}" ||
|
||||||
brew install ccache perl jq z3
|
brew install "${PACKAGES[@]}"
|
||||||
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
|
|
||||||
else
|
else
|
||||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
fatal "Unknown HOST_OS: '$HOST_OS'"
|
||||||
fi
|
fi
|
||||||
# Common installs
|
# Common installs
|
||||||
install-wavediff
|
install-wavediff
|
||||||
# Workaround -fsanitize=address crash
|
# Workaround -fsanitize=address crash
|
||||||
sudo sysctl -w vm.mmap_rnd_bits=28
|
sudo sysctl -w vm.mmap_rnd_bits=28
|
||||||
|
elif [ "$STAGE" = "lint-py" ]; then
|
||||||
|
# nodist/clang_check_attributes.
|
||||||
|
if [ "$HOST_OS" = "linux" ] && [ "$DISTRO_ID" = "ubuntu" ]; then
|
||||||
|
PACKAGES=(
|
||||||
|
python3-clang # Not run, but importers are linted
|
||||||
|
)
|
||||||
|
sudo apt-get update ||
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --yes "${PACKAGES[@]}" ||
|
||||||
|
sudo apt-get install --yes "${PACKAGES[@]}"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# Unknown build stage
|
# Unknown build stage
|
||||||
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
|
fatal "Unknown stage '$STAGE' (expected 'build', 'test' or 'lint-py')"
|
||||||
fi
|
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
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,14 @@ for RUN_ID in ${PR_RUN_IDS//,/ }; do
|
||||||
cat ${ARTIFACTS_DIR}/body.txt
|
cat ${ARTIFACTS_DIR}/body.txt
|
||||||
gh pr comment $(cat ${ARTIFACTS_DIR}/pr-number.txt) --body-file ${ARTIFACTS_DIR}/body.txt
|
gh pr comment $(cat ${ARTIFACTS_DIR}/pr-number.txt) --body-file ${ARTIFACTS_DIR}/body.txt
|
||||||
|
|
||||||
# Get the artifact ID
|
# Get the artifact IDs. Note there can be more than one artifact named
|
||||||
ARTIFACT_ID=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
|
# '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 it, so we only notify once
|
# Delete them all, so we only notify once
|
||||||
gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}"
|
for ARTIFACT_ID in ${ARTIFACT_IDS}; do
|
||||||
|
gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}"
|
||||||
|
done
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -6,19 +6,33 @@
|
||||||
|
|
||||||
Set-PSDebug -Trace 1
|
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
|
git clone --depth 1 https://github.com/lexxmark/winflexbison
|
||||||
cd winflexbison
|
cd winflexbison
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. --install-prefix $PWD/../../../.ccache
|
cmake .. --install-prefix $env:WIN_FLEX_BISON
|
||||||
cmake --build . --config Release -j 3
|
cmake --build . --config Release -j $NPROC
|
||||||
cmake --install . --prefix $PWD/../../../.ccache
|
cmake --install . --prefix $env:WIN_FLEX_BISON
|
||||||
cd ../..
|
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
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. --install-prefix $PWD/../install
|
# Ninja saturates all cores; the MSBuild generator only parallelizes across
|
||||||
cmake --build . --config Release -j 3
|
# 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
|
cmake --install . --prefix $PWD/../install
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,17 @@
|
||||||
|
|
||||||
Set-PSDebug -Trace 1
|
Set-PSDebug -Trace 1
|
||||||
|
|
||||||
|
$NPROC = $env:NUMBER_OF_PROCESSORS
|
||||||
|
|
||||||
cd install
|
cd install
|
||||||
$Env:VERILATOR_ROOT=$PWD
|
$Env:VERILATOR_ROOT=$PWD
|
||||||
cd examples/cmake_tracing_c
|
cd examples/cmake_tracing_c
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
# /Od skips optimization; this only checks the example verilates and builds, so
|
||||||
cmake --build . --config Release -j 3
|
# 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
|
# TODO put this back in, see issue# 5163
|
||||||
# Release/example.exe
|
# Release/example.exe
|
||||||
|
|
|
||||||
44
configure.ac
44
configure.ac
|
|
@ -12,7 +12,7 @@
|
||||||
# Then 'make maintainer-dist'
|
# Then 'make maintainer-dist'
|
||||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||||
#AC_INIT([Verilator],[#.### devel])
|
#AC_INIT([Verilator],[#.### devel])
|
||||||
AC_INIT([Verilator],[5.050 2026-07-01],
|
AC_INIT([Verilator],[5.051 devel],
|
||||||
[https://verilator.org],
|
[https://verilator.org],
|
||||||
[verilator],[https://verilator.org])
|
[verilator],[https://verilator.org])
|
||||||
|
|
||||||
|
|
@ -129,6 +129,21 @@ AC_ARG_ENABLE([dev-gcov],
|
||||||
AC_SUBST(CFG_WITH_DEV_GCOV)
|
AC_SUBST(CFG_WITH_DEV_GCOV)
|
||||||
AC_MSG_RESULT($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
|
# Special Substitutions - CFG_WITH_DEFENV
|
||||||
AC_MSG_CHECKING(whether to use hardcoded paths)
|
AC_MSG_CHECKING(whether to use hardcoded paths)
|
||||||
AC_ARG_ENABLE([defenv],
|
AC_ARG_ENABLE([defenv],
|
||||||
|
|
@ -492,6 +507,11 @@ _MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-std=gnu++20)
|
||||||
AC_SUBST(CFG_CXXFLAGS_COROUTINES)
|
AC_SUBST(CFG_CXXFLAGS_COROUTINES)
|
||||||
AC_SUBST(HAVE_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
|
# Flags for compiling Verilator internals including parser always
|
||||||
if test "$CFG_WITH_DEV_ASAN" = "yes"; then
|
if test "$CFG_WITH_DEV_ASAN" = "yes"; then
|
||||||
_MY_CXX_CHECK_IFELSE(-fsanitize=address -DVL_ASAN,
|
_MY_CXX_CHECK_IFELSE(-fsanitize=address -DVL_ASAN,
|
||||||
|
|
@ -518,15 +538,27 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PARSER,-Wno-unused)
|
||||||
AC_SUBST(CFG_CXXFLAGS_PARSER)
|
AC_SUBST(CFG_CXXFLAGS_PARSER)
|
||||||
|
|
||||||
# Flags for compiling the debug version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
|
# 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
|
if test "$CFG_WITH_LIGHT_DEBUG" = "yes"; then
|
||||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-Og)
|
# 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
|
fi
|
||||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-ggdb)
|
|
||||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-gz)
|
|
||||||
AC_SUBST(CFG_CXXFLAGS_DBG)
|
AC_SUBST(CFG_CXXFLAGS_DBG)
|
||||||
|
|
||||||
# Flags for linking the debug version of Verilator (in addition to above CFG_LDFLAGS_SRC)
|
# 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)
|
AC_SUBST(CFG_LDFLAGS_DBG)
|
||||||
|
|
||||||
# Flags for compiling the optimized version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
|
# Flags for compiling the optimized version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ contribution terms including the AI policy in ``docs/CONTRIBUTING.rst``.
|
||||||
|
|
||||||
Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
||||||
|
|
||||||
|
24bit-xjkp
|
||||||
404allen404
|
404allen404
|
||||||
Adam Bagley
|
Adam Bagley
|
||||||
Adam Kostrzewski
|
Adam Kostrzewski
|
||||||
|
|
@ -14,6 +15,7 @@ Adrian Sampson
|
||||||
Adrien Le Masle
|
Adrien Le Masle
|
||||||
أحمد المحمودي (Ahmed El-Mahmoudy)
|
أحمد المحمودي (Ahmed El-Mahmoudy)
|
||||||
Aidan McNay
|
Aidan McNay
|
||||||
|
Aisha Salimgereyeva
|
||||||
Aleksander Kiryk
|
Aleksander Kiryk
|
||||||
Alex Chadwick
|
Alex Chadwick
|
||||||
Alex Solomatnikov
|
Alex Solomatnikov
|
||||||
|
|
@ -26,16 +28,20 @@ Andrei Kostovski
|
||||||
Andrew Miloradovsky
|
Andrew Miloradovsky
|
||||||
Andrew Nolte
|
Andrew Nolte
|
||||||
Andrew Voznytsa
|
Andrew Voznytsa
|
||||||
|
Andrii Andrieiev
|
||||||
anonkey
|
anonkey
|
||||||
Anthony Donlon
|
Anthony Donlon
|
||||||
Anthony Moore
|
Anthony Moore
|
||||||
|
apocelipes
|
||||||
Arkadiusz Kozdra
|
Arkadiusz Kozdra
|
||||||
Arthur Rosa
|
Arthur Rosa
|
||||||
Artur Bieniek
|
Artur Bieniek
|
||||||
AUDIY
|
AUDIY
|
||||||
Aylon Chaim Porat
|
Aylon Chaim Porat
|
||||||
|
Bartosz Skorowski
|
||||||
Bartłomiej Chmiel
|
Bartłomiej Chmiel
|
||||||
Benjamin Collier
|
Benjamin Collier
|
||||||
|
BRDR LIFE
|
||||||
Brian Li
|
Brian Li
|
||||||
Cameron Kirk
|
Cameron Kirk
|
||||||
Cameron Waite
|
Cameron Waite
|
||||||
|
|
@ -53,27 +59,35 @@ Dan Ruelas-Petrisko
|
||||||
Daniel Bates
|
Daniel Bates
|
||||||
Danny Oler
|
Danny Oler
|
||||||
Dave Sargeant
|
Dave Sargeant
|
||||||
|
David Garau
|
||||||
David Horton
|
David Horton
|
||||||
David Ledger
|
David Ledger
|
||||||
David Metz
|
David Metz
|
||||||
David Stanford
|
David Stanford
|
||||||
David Turner
|
David Turner
|
||||||
|
dependabot[bot]
|
||||||
Dercury
|
Dercury
|
||||||
|
Demin Han
|
||||||
Diego Roux
|
Diego Roux
|
||||||
Dominick Grochowina
|
Dominick Grochowina
|
||||||
Don Williamson
|
Don Williamson
|
||||||
|
Dragon-Git
|
||||||
Drew Ranck
|
Drew Ranck
|
||||||
Drew Taussig
|
Drew Taussig
|
||||||
Driss Hafdi
|
Driss Hafdi
|
||||||
Edgar E. Iglesias
|
Edgar E. Iglesias
|
||||||
|
em2machine
|
||||||
|
emmettifelts
|
||||||
Eric Mejdrich
|
Eric Mejdrich
|
||||||
Eric Müller
|
Eric Müller
|
||||||
Eric Rippey
|
Eric Rippey
|
||||||
Eunseo Song
|
Eryk Szpotański
|
||||||
Ethan Sifferman
|
Ethan Sifferman
|
||||||
|
Eunseo Song
|
||||||
Eyck Jentzsch
|
Eyck Jentzsch
|
||||||
Fabian Keßler-Schulz
|
Fabian Keßler-Schulz
|
||||||
Fan Shupei
|
Fan Shupei
|
||||||
|
february cozzocrea
|
||||||
Felix Neumärker
|
Felix Neumärker
|
||||||
Felix Yan
|
Felix Yan
|
||||||
Frans Skarman
|
Frans Skarman
|
||||||
|
|
@ -108,6 +122,8 @@ Ilya Barkov
|
||||||
Iru Cai
|
Iru Cai
|
||||||
Ivan Vnučec
|
Ivan Vnučec
|
||||||
Iztok Jeras
|
Iztok Jeras
|
||||||
|
JAEUK LEE
|
||||||
|
Jaeuk Lee (이재욱)
|
||||||
Jake Merdich
|
Jake Merdich
|
||||||
Jakub Michalski
|
Jakub Michalski
|
||||||
Jakub Wasilewski
|
Jakub Wasilewski
|
||||||
|
|
@ -122,6 +138,8 @@ Jamie Iles
|
||||||
Jan Van Winkel
|
Jan Van Winkel
|
||||||
Jean Berniolles
|
Jean Berniolles
|
||||||
Jean-Nicolas Strauss
|
Jean-Nicolas Strauss
|
||||||
|
Jeffrey Song
|
||||||
|
jeffrey.song
|
||||||
Jens Yuechao Liu
|
Jens Yuechao Liu
|
||||||
Jeremy Bennett
|
Jeremy Bennett
|
||||||
Jesse Taube
|
Jesse Taube
|
||||||
|
|
@ -147,6 +165,7 @@ Josep Sans
|
||||||
Joseph Nwabueze
|
Joseph Nwabueze
|
||||||
Josh Redford
|
Josh Redford
|
||||||
Joshua Leahy
|
Joshua Leahy
|
||||||
|
JOTEGO (Jose Tejada)
|
||||||
Julian Carrier
|
Julian Carrier
|
||||||
Julian Daube
|
Julian Daube
|
||||||
Julie Schwartz
|
Julie Schwartz
|
||||||
|
|
@ -183,6 +202,7 @@ Maarten De Braekeleer
|
||||||
Maciej Sobkowski
|
Maciej Sobkowski
|
||||||
Marcel Chang
|
Marcel Chang
|
||||||
Marco Bartoli
|
Marco Bartoli
|
||||||
|
Marco Brambilla
|
||||||
Marco Widmer
|
Marco Widmer
|
||||||
Mariusz Glebocki
|
Mariusz Glebocki
|
||||||
Markus Krause
|
Markus Krause
|
||||||
|
|
@ -208,6 +228,7 @@ Mladen Slijepcevic
|
||||||
Morten Borup Petersen
|
Morten Borup Petersen
|
||||||
Mostafa Gamal
|
Mostafa Gamal
|
||||||
Moubarak Jeje
|
Moubarak Jeje
|
||||||
|
Muzaffer Kal
|
||||||
Nandu Raj
|
Nandu Raj
|
||||||
Natan Kreimer
|
Natan Kreimer
|
||||||
Nathan Graybeal
|
Nathan Graybeal
|
||||||
|
|
@ -218,8 +239,10 @@ Nikolai Kumar
|
||||||
Nikolay Puzanov
|
Nikolay Puzanov
|
||||||
Nolan Poe
|
Nolan Poe
|
||||||
Oleh Maksymenko
|
Oleh Maksymenko
|
||||||
|
Patrick Creighton
|
||||||
Patrick Stewart
|
Patrick Stewart
|
||||||
Paul Bowen-Huggett
|
Paul Bowen-Huggett
|
||||||
|
Paul Campbell
|
||||||
Paul Swirhun
|
Paul Swirhun
|
||||||
Paul Wright
|
Paul Wright
|
||||||
Pawel Jewstafjew
|
Pawel Jewstafjew
|
||||||
|
|
@ -262,15 +285,19 @@ Sergey Chusov
|
||||||
Sergey Fedorov
|
Sergey Fedorov
|
||||||
Sergi Granell
|
Sergi Granell
|
||||||
Seth Pellegrino
|
Seth Pellegrino
|
||||||
|
Shashvat Prabhu
|
||||||
Shogo Yamazaki
|
Shogo Yamazaki
|
||||||
Shou-Li Hsu
|
Shou-Li Hsu
|
||||||
|
spomatasmd
|
||||||
Srinivasan Venkataramanan
|
Srinivasan Venkataramanan
|
||||||
Stefan Wallentowitz
|
Stefan Wallentowitz
|
||||||
Stephen Henry
|
Stephen Henry
|
||||||
Steven Hugg
|
Steven Hugg
|
||||||
Stuart Morris
|
Stuart Morris
|
||||||
|
sumpster
|
||||||
Szymon Gizler
|
Szymon Gizler
|
||||||
Sören Tempel
|
Sören Tempel
|
||||||
|
Sumanth Kadiyala
|
||||||
Teng Huang
|
Teng Huang
|
||||||
Thomas Aldrian
|
Thomas Aldrian
|
||||||
Thomas Brown
|
Thomas Brown
|
||||||
|
|
@ -291,6 +318,7 @@ Tracy Narine
|
||||||
Trung Nguyen
|
Trung Nguyen
|
||||||
Tudor Timi
|
Tudor Timi
|
||||||
Tymoteusz Blazejczyk
|
Tymoteusz Blazejczyk
|
||||||
|
Tyrone Marhguy
|
||||||
Udaya Raj Subedi
|
Udaya Raj Subedi
|
||||||
Udi Finkelstein
|
Udi Finkelstein
|
||||||
Unai Martinez-Corral
|
Unai Martinez-Corral
|
||||||
|
|
@ -308,9 +336,11 @@ Wolfgang Mayerwieser
|
||||||
Xi Zhang
|
Xi Zhang
|
||||||
Yan Xu
|
Yan Xu
|
||||||
Yangyu Chen
|
Yangyu Chen
|
||||||
|
Yilin Li
|
||||||
Yilou Wang
|
Yilou Wang
|
||||||
Yinan Xu
|
Yinan Xu
|
||||||
Yoda Lee
|
Yoda Lee
|
||||||
|
Yogish Sekhar
|
||||||
Yossi Nivin
|
Yossi Nivin
|
||||||
Yu-Sheng Lin
|
Yu-Sheng Lin
|
||||||
Yuri Victorovich
|
Yuri Victorovich
|
||||||
|
|
@ -321,17 +351,7 @@ Zhen Yan
|
||||||
Zhou Shen
|
Zhou Shen
|
||||||
Zhouyi Shen
|
Zhouyi Shen
|
||||||
Zixi Li
|
Zixi Li
|
||||||
apocelipes
|
Zubin Jain
|
||||||
dependabot[bot]
|
|
||||||
february cozzocrea
|
|
||||||
sumpster
|
|
||||||
em2machine
|
|
||||||
emmettifelts
|
|
||||||
Àlex Torregrosa
|
Àlex Torregrosa
|
||||||
Ícaro Lima
|
Ícaro Lima
|
||||||
Yogish Sekhar
|
Sunimali Rathnayake
|
||||||
24bit-xjkp
|
|
||||||
Zubin Jain
|
|
||||||
Muzaffer Kal
|
|
||||||
Yilin Li
|
|
||||||
Shashvat Prabhu
|
|
||||||
|
|
|
||||||
|
|
@ -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,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'
|
||||||
|
example.v:1:25
|
||||||
|
16 | 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
|
Refer to ``examples/make_tracing_c`` in the distribution for a detailed
|
||||||
commented example.
|
commented example.
|
||||||
|
|
||||||
Top level IO signals are read and written as members of the model. You call
|
Top level IO signals are read and written as members of the model. All
|
||||||
the model's ``eval()`` method to evaluate the model. When the simulation is
|
inputs must be sanitized, that is have no bits set above those
|
||||||
complete call the model's ``final()`` method to execute any SystemVerilog
|
corresponding to the width of the Verilog construct;
|
||||||
final blocks, and complete any assertions. If using :vlopt:`--timing`,
|
:vlopt:`--runtime-debug` will assert this is correct.
|
||||||
there are two additional functions for checking if there are any events
|
|
||||||
pending in the simulation due to delays, and for retrieving the simulation
|
Call the model's ``eval()`` method to evaluate the model. When the
|
||||||
time of the next delayed event. See :ref:`Evaluation Loop`.
|
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
|
Connecting to SystemC
|
||||||
|
|
@ -179,7 +183,7 @@ DPI Example
|
||||||
In the SYSTEMC example above, if you wanted to import C++ functions into
|
In the SYSTEMC example above, if you wanted to import C++ functions into
|
||||||
Verilog, put in our.v:
|
Verilog, put in our.v:
|
||||||
|
|
||||||
.. code-block:: sv
|
.. code-block::
|
||||||
|
|
||||||
import "DPI-C" function int add (input int a, input int b);
|
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
|
efficiently add system functions. Use a dollar-sign prefixed system
|
||||||
function name for the import, but note it must be escaped.
|
function name for the import, but note it must be escaped.
|
||||||
|
|
||||||
.. code-block:: sv
|
.. code-block::
|
||||||
|
|
||||||
import "DPI-C" function integer \$myRand;
|
import "DPI-C" function integer \$myRand;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,13 @@ Summary:
|
||||||
|
|
||||||
Options:
|
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>
|
.. option:: +verilator+coverage+file+<filename>
|
||||||
|
|
||||||
When a model was Verilated using :vlopt:`--coverage`, sets the 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
|
When a model was Verilated using :vlopt:`--x-initial unique
|
||||||
<--x-initial>`, sets the simulation runtime initialization technique. 0
|
<--x-initial>`, sets the simulation runtime initialization technique. 0
|
||||||
= Reset to zeros. 1 = Reset to all-ones. 2 = Randomize. See
|
= Reset to zeros. 1 = Reset to all-ones. 2 = Randomize. See
|
||||||
:ref:`Unknown States`.
|
:ref:`Unknown States`. Default is 0.
|
||||||
|
|
||||||
.. option:: +verilator+seed+<value>
|
.. option:: +verilator+seed+<value>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -715,6 +715,10 @@ Summary:
|
||||||
|
|
||||||
.. option:: -fno-dfg-break-cycles
|
.. option:: -fno-dfg-break-cycles
|
||||||
|
|
||||||
|
Deprecated and has no effect (ignored).
|
||||||
|
|
||||||
|
In versions before 5.052:
|
||||||
|
|
||||||
Rarely needed. Disable breaking combinational cycles during DFG.
|
Rarely needed. Disable breaking combinational cycles during DFG.
|
||||||
|
|
||||||
.. option:: -fno-dfg-peephole
|
.. option:: -fno-dfg-peephole
|
||||||
|
|
@ -936,7 +940,7 @@ Summary:
|
||||||
:file:`*.mk` files.
|
:file:`*.mk` files.
|
||||||
|
|
||||||
Feature may be one of the following: COROUTINES, DEV_ASAN, DEV_GCOV,
|
Feature may be one of the following: COROUTINES, DEV_ASAN, DEV_GCOV,
|
||||||
SYSTEMC.
|
SYSTEMC, TSAN.
|
||||||
|
|
||||||
.. option:: --getenv <variable>
|
.. option:: --getenv <variable>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ verilator_coverage Arguments
|
||||||
|
|
||||||
.. option:: --filter-type <regex>
|
.. 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`,
|
Possible values are `toggle`, `line`, `branch`, `expr`, `covergroup`,
|
||||||
`user`, `fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The
|
`user`, `fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The
|
||||||
default value is `\*`.
|
default value is `\*`.
|
||||||
|
|
|
||||||
|
|
@ -294,6 +294,25 @@ or "`ifdef`"'s may break other tools.
|
||||||
(if appropriate :vlopt:`--coverage` flags are passed) after being
|
(if appropriate :vlopt:`--coverage` flags are passed) after being
|
||||||
disabled earlier with :option:`/*verilator&32;coverage_off*/`.
|
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>*/
|
.. option:: /*verilator&32;fargs <arguments>*/
|
||||||
|
|
||||||
For Verilator developers only. When a source file containing these `fargs`
|
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"
|
.. describe:: "allocated 123 MB"
|
||||||
|
|
||||||
Total memory used during simulation in megabytes.
|
Peak resident memory used during simulation in megabytes.
|
||||||
|
|
||||||
|
|
||||||
.. _benchmarking & optimization:
|
.. _benchmarking & optimization:
|
||||||
|
|
|
||||||
|
|
@ -623,5 +623,5 @@ The information in this report is:
|
||||||
|
|
||||||
.. describe:: "allocated 123 MB"
|
.. describe:: "allocated 123 MB"
|
||||||
|
|
||||||
Total memory used during build by Verilator executable (excludes
|
Peak resident memory used by the Verilator executable during build
|
||||||
:vlopt:`--build` compiler's usage) in megabytes.
|
(excludes :vlopt:`--build` compiler's usage) in megabytes.
|
||||||
|
|
|
||||||
|
|
@ -1469,11 +1469,50 @@ List Of Warnings
|
||||||
q <= d;
|
q <= d;
|
||||||
end
|
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
|
Ignoring this warning may hide clock domain crossing, timing, or
|
||||||
portability bugs. It may also cause longer simulation runtimes due to
|
portability bugs. It may also cause longer simulation runtimes due to
|
||||||
reduced optimizations.
|
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.
|
||||||
|
|
||||||
|
|
||||||
.. option:: MULTITOP
|
.. option:: MULTITOP
|
||||||
|
|
||||||
.. TODO better example
|
.. TODO better example
|
||||||
|
|
@ -2089,6 +2128,25 @@ List Of Warnings
|
||||||
simulators.
|
simulators.
|
||||||
|
|
||||||
|
|
||||||
|
.. option:: SIMILARNAME
|
||||||
|
|
||||||
|
Warns that a variable 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
|
||||||
|
|
||||||
|
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
|
.. option:: SPECIFYIGN
|
||||||
|
|
||||||
Warns that Verilator does not support certain constructs in
|
Warns that Verilator does not support certain constructs in
|
||||||
|
|
|
||||||
|
|
@ -477,6 +477,7 @@ Syms
|
||||||
Synopsys
|
Synopsys
|
||||||
SystemC
|
SystemC
|
||||||
SystemVerilog
|
SystemVerilog
|
||||||
|
Szpotanski
|
||||||
Takatsukasa
|
Takatsukasa
|
||||||
Tambe
|
Tambe
|
||||||
Tarik
|
Tarik
|
||||||
|
|
@ -537,6 +538,7 @@ Verilog
|
||||||
Vighnesh
|
Vighnesh
|
||||||
Viktor
|
Viktor
|
||||||
Vilp
|
Vilp
|
||||||
|
VlRNG
|
||||||
VlWide
|
VlWide
|
||||||
Vlip
|
Vlip
|
||||||
Vm
|
Vm
|
||||||
|
|
@ -644,6 +646,7 @@ casez
|
||||||
casted
|
casted
|
||||||
castro
|
castro
|
||||||
cb
|
cb
|
||||||
|
cbValueChange
|
||||||
ccache
|
ccache
|
||||||
ccall
|
ccall
|
||||||
cdc
|
cdc
|
||||||
|
|
@ -707,6 +710,7 @@ de
|
||||||
dearray
|
dearray
|
||||||
deassign
|
deassign
|
||||||
debugi
|
debugi
|
||||||
|
deduplication
|
||||||
defenv
|
defenv
|
||||||
defname
|
defname
|
||||||
defparam
|
defparam
|
||||||
|
|
@ -947,6 +951,7 @@ misconnected
|
||||||
misconversion
|
misconversion
|
||||||
misdetecting
|
misdetecting
|
||||||
misoptimized
|
misoptimized
|
||||||
|
misoptimizing
|
||||||
missized
|
missized
|
||||||
mk
|
mk
|
||||||
mno
|
mno
|
||||||
|
|
@ -1174,6 +1179,7 @@ tcmalloc
|
||||||
tcmalloc
|
tcmalloc
|
||||||
tenghtt
|
tenghtt
|
||||||
testbench
|
testbench
|
||||||
|
testbenches
|
||||||
threadsafe
|
threadsafe
|
||||||
threashold
|
threashold
|
||||||
timeInc
|
timeInc
|
||||||
|
|
|
||||||
|
|
@ -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
|
// Wrapper to call certain functions via messages when multithreaded
|
||||||
|
|
||||||
void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE {
|
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{[=]() { //
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||||
vl_finish(filename, linenum, hier);
|
vl_finish(filename, linenum, hier);
|
||||||
|
contextp->finishPendingDec();
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
|
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{[=]() { //
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||||
vl_stop_maybe(filename, linenum, hier, maybe);
|
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 {
|
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{[=]() { //
|
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||||
vl_fatal(filename, linenum, hier, msg);
|
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 {
|
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;
|
IData data = ~0;
|
||||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
if (randReset != 1) { // if 2, randomize
|
||||||
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
||||||
data = rng.rand64();
|
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 {
|
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;
|
QData data = ~0ULL;
|
||||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
if (randReset != 1) { // if 2, randomize
|
||||||
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
||||||
data = rng.rand64();
|
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,
|
WDataOutP VL_SCOPED_RAND_RESET_W(int obits, WDataOutP outwp, uint64_t scopeHash,
|
||||||
uint64_t salt) VL_MT_UNSAFE {
|
uint64_t salt) VL_MT_UNSAFE {
|
||||||
if (Verilated::threadContextp()->randReset() != 2) { return VL_RAND_RESET_W(obits, outwp); }
|
const int words = VL_WORDS_I(obits);
|
||||||
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
const int randReset = Verilated::threadContextp()->randReset();
|
||||||
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = rng.rand64();
|
if (randReset == 0) {
|
||||||
outwp[VL_WORDS_I(obits) - 1] = rng.rand64() & VL_MASK_E(obits);
|
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;
|
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 {
|
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;
|
IData data = ~0;
|
||||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
if (randReset != 1) data = VL_RANDOM_I(); // if 2, randomize
|
||||||
data = VL_RANDOM_I();
|
|
||||||
}
|
|
||||||
data &= VL_MASK_I(obits);
|
data &= VL_MASK_I(obits);
|
||||||
return data;
|
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 {
|
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||||
// Not inlined to speed up compilation of slowpath code
|
// Not inlined to speed up compilation of slowpath code
|
||||||
return VL_ZERO_W(obits, outwp);
|
return VL_ZERO_W(obits, outwp);
|
||||||
|
|
@ -3087,6 +3092,7 @@ VerilatedContext::Serialized::Serialized() {
|
||||||
|
|
||||||
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
|
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
|
||||||
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
||||||
|
if (assertCtlsLocked()) return;
|
||||||
// Set all assert and directive types when true, clear otherwise.
|
// Set all assert and directive types when true, clear otherwise.
|
||||||
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
|
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
|
||||||
}
|
}
|
||||||
|
|
@ -3105,16 +3111,22 @@ uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
|
||||||
}
|
}
|
||||||
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||||
|
if (assertCtlsLocked()) return;
|
||||||
m_s.m_assertOn |= assertOnMask(types, directives);
|
m_s.m_assertOn |= assertOnMask(types, directives);
|
||||||
}
|
}
|
||||||
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||||
|
if (assertCtlsLocked()) return;
|
||||||
m_s.m_assertOn &= ~assertOnMask(types, directives);
|
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,
|
void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||||
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the
|
// 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.
|
// 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 mask = assertOnMask(types, directives);
|
||||||
const uint32_t lockedMask = mask & ~m_s.m_assertLock;
|
const uint32_t lockedMask = mask & ~m_s.m_assertLock;
|
||||||
switch (controlType) {
|
switch (controlType) {
|
||||||
|
|
@ -3286,6 +3298,15 @@ void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
|
||||||
const VerilatedLockGuard lock{m_mutex};
|
const VerilatedLockGuard lock{m_mutex};
|
||||||
m_s.m_gotFinish = flag;
|
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 {
|
bool VerilatedContext::executingFinal() const VL_MT_SAFE {
|
||||||
const VerilatedLockGuard lock{m_mutex};
|
const VerilatedLockGuard lock{m_mutex};
|
||||||
return m_ns.m_executingFinal;
|
return m_ns.m_executingFinal;
|
||||||
|
|
@ -3556,7 +3577,9 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||||
if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
|
if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
|
||||||
std::string str;
|
std::string str;
|
||||||
uint64_t u64;
|
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);
|
coverageFilename(str);
|
||||||
} else if (arg == "+verilator+debug") {
|
} else if (arg == "+verilator+debug") {
|
||||||
Verilated::debug(4);
|
Verilated::debug(4);
|
||||||
|
|
@ -3575,7 +3598,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||||
logFilename(str);
|
logFilename(str);
|
||||||
logOutputToFile(false /* append */);
|
logOutputToFile(false /* append */);
|
||||||
} else if (arg == "+verilator+noassert") {
|
} 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)) {
|
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+start+", u64)) {
|
||||||
profExecStart(u64);
|
profExecStart(u64);
|
||||||
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)) {
|
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)) {
|
||||||
|
|
@ -3891,7 +3915,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
|
||||||
// When running internal code coverage (gcc --coverage, as opposed to
|
// When running internal code coverage (gcc --coverage, as opposed to
|
||||||
// verilator --coverage), dump coverage data to properly cover failing
|
// verilator --coverage), dump coverage data to properly cover failing
|
||||||
// tests.
|
// tests.
|
||||||
VL_GCOV_DUMP();
|
VL_GCOV_DUMP_RESET();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { addCbExit(cb, datap); }
|
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { addCbExit(cb, datap); }
|
||||||
|
|
@ -4142,6 +4166,50 @@ VerilatedVar* VerilatedScope::varInsert(const char* namep, void* datap, bool isP
|
||||||
return &(m_varsp->find(namep)->second);
|
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,
|
VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
|
||||||
VerilatedVarType vltype, int vlflags, int udims,
|
VerilatedVarType vltype, int vlflags, int udims,
|
||||||
uint32_t entSize...) VL_MT_UNSAFE {
|
uint32_t entSize...) VL_MT_UNSAFE {
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,21 @@ enum VerilatedVarFlags : uint32_t {
|
||||||
VLVF_NET = (1 << 15) // Net object
|
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
|
// IEEE 1800-2023 Table 20-6
|
||||||
enum class VerilatedAssertType : uint8_t {
|
enum class VerilatedAssertType : uint8_t {
|
||||||
ASSERT_TYPE_CONCURRENT = (1 << 0),
|
ASSERT_TYPE_CONCURRENT = (1 << 0),
|
||||||
|
|
@ -372,6 +387,8 @@ private:
|
||||||
static uint32_t assertOnMask(VerilatedAssertType_t types,
|
static uint32_t assertOnMask(VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_PURE;
|
VerilatedAssertDirectiveType_t directives) VL_PURE;
|
||||||
static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1;
|
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:
|
protected:
|
||||||
// TYPES
|
// TYPES
|
||||||
|
|
@ -430,6 +447,12 @@ protected:
|
||||||
struct NonSerialized final { // Non-serialized information
|
struct NonSerialized final { // Non-serialized information
|
||||||
// These are reloaded from on command-line settings, so do not need to persist
|
// These are reloaded from on command-line settings, so do not need to persist
|
||||||
// Fast path
|
// 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
|
bool m_executingFinal = false; // Running generated final() code
|
||||||
uint64_t m_profExecStart = 1; // +prof+exec+start time
|
uint64_t m_profExecStart = 1; // +prof+exec+start time
|
||||||
uint32_t m_profExecWindow = 2; // +prof+exec+window size
|
uint32_t m_profExecWindow = 2; // +prof+exec+window size
|
||||||
|
|
@ -510,6 +533,12 @@ public:
|
||||||
/// Clear enabled status for given assertion types
|
/// Clear enabled status for given assertion types
|
||||||
void assertOnClear(VerilatedAssertType_t types,
|
void assertOnClear(VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
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
|
/// Apply assertion control for given control, assertion, and directive types
|
||||||
void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||||
|
|
@ -669,6 +698,27 @@ public:
|
||||||
|
|
||||||
// METHODS - public but for internal use only
|
// 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
|
// Internal: access to implementation class
|
||||||
VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); }
|
VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); }
|
||||||
const VerilatedContextImp* impp() const VL_MT_SAFE {
|
const VerilatedContextImp* impp() const VL_MT_SAFE {
|
||||||
|
|
@ -756,6 +806,8 @@ public: // But for internal use only
|
||||||
// Verilator scope information class
|
// Verilator scope information class
|
||||||
// Used for internal VPI implementation, and introspection into scopes
|
// Used for internal VPI implementation, and introspection into scopes
|
||||||
|
|
||||||
|
struct VlScopeTableEntry; // Defined below VerilatedScope; used by scopesConstructFromTable()
|
||||||
|
|
||||||
class VerilatedScope final {
|
class VerilatedScope final {
|
||||||
public:
|
public:
|
||||||
enum Type : uint8_t {
|
enum Type : uint8_t {
|
||||||
|
|
@ -792,6 +844,9 @@ public: // But internals only - called from verilated modules, VerilatedSyms
|
||||||
void* forceReadSignalData, const char* forceReadSignalName,
|
void* forceReadSignalData, const char* forceReadSignalName,
|
||||||
std::pair<VerilatedVar*, VerilatedVar*> forceControlSignals,
|
std::pair<VerilatedVar*, VerilatedVar*> forceControlSignals,
|
||||||
int udims, int pdims...) VL_MT_UNSAFE;
|
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
|
// ACCESSORS
|
||||||
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
|
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
|
||||||
const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }
|
const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }
|
||||||
|
|
@ -807,6 +862,17 @@ public: // But internals only - called from verilated modules, VerilatedSyms
|
||||||
Type type() const { return m_type; }
|
Type type() const { return m_type; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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 {
|
class VerilatedHierarchy final {
|
||||||
public:
|
public:
|
||||||
static void add(const VerilatedScope* fromp, const VerilatedScope* top);
|
static void add(const VerilatedScope* fromp, const VerilatedScope* top);
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
|
||||||
# Compiler flags that enable coroutine support
|
# Compiler flags that enable coroutine support
|
||||||
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
|
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
|
||||||
# Compiler flags when creating a precompiled header
|
# 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
|
# Compiler option to put in front of filename to read precompiled header
|
||||||
CFG_CXXFLAGS_PCH_I = @CFG_CXXFLAGS_PCH_I@
|
CFG_CXXFLAGS_PCH_I = @CFG_CXXFLAGS_PCH_I@
|
||||||
# Compiler's filename prefix for precompiled headers, .gch if clang, empty if GCC
|
# Compiler's filename prefix for precompiled headers, .gch if clang, empty if GCC
|
||||||
|
|
|
||||||
|
|
@ -75,24 +75,24 @@ class VerilatedCovImp;
|
||||||
ccontextp->_insertp("hier", name, __VA_ARGS__); \
|
ccontextp->_insertp("hier", name, __VA_ARGS__); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
static inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData,
|
inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData,
|
||||||
const IData oldData) {
|
const IData oldData) {
|
||||||
const IData chgData = newData ^ oldData;
|
const IData chgData = newData ^ oldData;
|
||||||
for (int i = 0; i < width; ++i) {
|
for (int i = 0; i < width; ++i) {
|
||||||
*(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1;
|
*(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,
|
inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const QData newData,
|
||||||
const QData oldData) {
|
const QData oldData) {
|
||||||
const QData chgData = newData ^ oldData;
|
const QData chgData = newData ^ oldData;
|
||||||
for (int i = 0; i < width; ++i) {
|
for (int i = 0; i < width; ++i) {
|
||||||
*(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1;
|
*(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,
|
inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData,
|
||||||
WDataInP oldData) {
|
WDataInP oldData) {
|
||||||
for (int i = 0; i < VL_WORDS_I(width); ++i) {
|
for (int i = 0; i < VL_WORDS_I(width); ++i) {
|
||||||
const EData chgData = newData[i] ^ oldData[i];
|
const EData chgData = newData[i] ^ oldData[i];
|
||||||
if (chgData) {
|
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,
|
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 newData, const IData oldData) VL_MT_SAFE {
|
||||||
const IData chgData = newData ^ oldData;
|
const IData chgData = newData ^ oldData;
|
||||||
for (int i = 0; i < width; ++i) {
|
for (int i = 0; i < width; ++i) {
|
||||||
if (VL_BITISSET_I(chgData, 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,
|
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 newData, const QData oldData) VL_MT_SAFE {
|
||||||
const QData chgData = newData ^ oldData;
|
const QData chgData = newData ^ oldData;
|
||||||
for (int i = 0; i < width; ++i) {
|
for (int i = 0; i < width; ++i) {
|
||||||
if (VL_BITISSET_Q(chgData, 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,
|
inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic<uint32_t>* covp, WDataInP newData,
|
||||||
WDataInP newData, WDataInP oldData) VL_MT_SAFE {
|
WDataInP oldData) VL_MT_SAFE {
|
||||||
for (int i = 0; i < VL_WORDS_I(width); ++i) {
|
for (int i = 0; i < VL_WORDS_I(width); ++i) {
|
||||||
const EData chgData = newData[i] ^ oldData[i];
|
const EData chgData = newData[i] ^ oldData[i];
|
||||||
if (chgData) {
|
if (chgData) {
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,13 @@ enum class VlCovBinKind : uint8_t {
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// VlCoverpointIf
|
// VlCoverpointIf
|
||||||
/// Read-side view of a coverpoint. The writer queries bins by index; the
|
/// Read-side view of a coverpoint -- a named, index-addressable set of bins
|
||||||
/// implementor computes names/kinds on demand. Bounded bin count, so random
|
/// with a coverage fraction. A cross is also a coverpoint from this view: its
|
||||||
/// access by index is the primary usage.
|
/// 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 {
|
class VlCoverpointIf VL_NOT_FINAL {
|
||||||
public:
|
public:
|
||||||
|
|
@ -51,11 +55,11 @@ public:
|
||||||
virtual ~VlCoverpointIf() = default;
|
virtual ~VlCoverpointIf() = default;
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
// All bins, across every set; index range [0, binCount())
|
// All bins, across every set; index range [0, binCount()).
|
||||||
virtual int binCount() const = 0;
|
virtual uint32_t binCount() const = 0;
|
||||||
// Bin name in declaration order (e.g. "myBin" or "b[3]")
|
// Bin name in declaration order (e.g. "myBin" or "b[3]"); for a cross,
|
||||||
virtual std::string binName(int i) const = 0;
|
// the concatenated cross bin name (e.g. "b1_x_b2_x_b3")
|
||||||
virtual VlCovBinKind binKind(int i) const = 0;
|
virtual std::string binName(uint32_t i) const = 0;
|
||||||
// Bins covered / effective total (Normal set only) for the coverage calc
|
// Bins covered / effective total (Normal set only) for the coverage calc
|
||||||
virtual void coverageParts(double& covered, double& total) const = 0;
|
virtual void coverageParts(double& covered, double& total) const = 0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -23,34 +23,57 @@
|
||||||
|
|
||||||
#include "verilated_covergroup.h"
|
#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
|
#if VM_COVERAGE
|
||||||
#include "verilated_cov.h"
|
#include "verilated_cov.h"
|
||||||
#endif
|
#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_hier = hier;
|
||||||
m_atLeast = atLeast;
|
m_atLeast = atLeast;
|
||||||
m_total = nBins;
|
m_total = nBins;
|
||||||
m_counts.assign(nBins, 0);
|
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,
|
void VlCoverpoint::addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming,
|
||||||
const char* file, int line, int col) {
|
const char* name, const char* file, int line, int col) {
|
||||||
m_namers.emplace_back(set, count, m_nextBase, naming, name, file, line, 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;
|
m_nextBase += count;
|
||||||
if (set == VlCovBinKind::KIND_NORMAL) m_normal += count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const VlCovNamer& VlCoverpoint::namerFor(int i) const {
|
std::string VlCoverpoint::normalBinName(uint32_t crossIdx) const {
|
||||||
// Namers are appended in ascending, contiguous index order covering [0, m_total),
|
// Build the bin name based on the bin index
|
||||||
// and i is always a valid bin index, so the matching namer always exists.
|
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) {
|
for (const VlCovNamer& nm : m_namers) {
|
||||||
if (i < nm.base() + nm.count()) return nm;
|
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);
|
const VlCovNamer& nm = namerFor(i);
|
||||||
std::string name = nm.name();
|
std::string name = nm.name();
|
||||||
if (nm.naming() == VlCovBinNaming::Array) name += '[' + std::to_string(i - nm.base()) + ']';
|
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
|
#if VM_COVERAGE
|
||||||
void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* page) {
|
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 VlCovNamer& nm = namerFor(i);
|
||||||
const VlCovBinKind kind = binKind(i);
|
const VlCovBinKind kind = binKind(i);
|
||||||
const std::string binp = binName(i);
|
const std::string binp = binName(i);
|
||||||
|
|
@ -81,3 +104,89 @@ void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* pa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // VM_COVERAGE
|
#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
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@
|
||||||
/// it in the constructor (init + add*Namer), increments bins from sample(),
|
/// it in the constructor (init + add*Namer), increments bins from sample(),
|
||||||
/// and registers via registerBins().
|
/// 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_
|
#ifndef VERILATOR_VERILATED_COVERGROUP_H_
|
||||||
|
|
@ -49,8 +52,8 @@ enum class VlCovBinNaming : uint8_t {
|
||||||
class VlCovNamer final {
|
class VlCovNamer final {
|
||||||
// MEMBERS
|
// MEMBERS
|
||||||
VlCovBinKind m_set; // which set the bins belong to
|
VlCovBinKind m_set; // which set the bins belong to
|
||||||
int m_count; // bins this namer covers (1 for Single)
|
uint32_t m_count; // bins this namer covers (1 for Single)
|
||||||
int m_base; // first bin index (declaration order), assigned on append
|
uint32_t m_base; // first bin index (declaration order), assigned on append
|
||||||
VlCovBinNaming m_naming; // how bin names are built
|
VlCovBinNaming m_naming; // how bin names are built
|
||||||
const char* m_name; // bin name (Single) or array base name (Array)
|
const char* m_name; // bin name (Single) or array base name (Array)
|
||||||
const char* m_file; // declaration file
|
const char* m_file; // declaration file
|
||||||
|
|
@ -59,8 +62,8 @@ class VlCovNamer final {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
VlCovNamer(VlCovBinKind set, int count, int base, VlCovBinNaming naming, const char* name,
|
VlCovNamer(VlCovBinKind set, uint32_t count, uint32_t base, VlCovBinNaming naming,
|
||||||
const char* file, int line, int col)
|
const char* name, const char* file, int line, int col)
|
||||||
: m_set{set}
|
: m_set{set}
|
||||||
, m_count{count}
|
, m_count{count}
|
||||||
, m_base{base}
|
, m_base{base}
|
||||||
|
|
@ -72,8 +75,8 @@ public:
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
VlCovBinKind set() const { return m_set; }
|
VlCovBinKind set() const { return m_set; }
|
||||||
int count() const { return m_count; }
|
uint32_t count() const { return m_count; }
|
||||||
int base() const { return m_base; }
|
uint32_t base() const { return m_base; }
|
||||||
VlCovBinNaming naming() const { return m_naming; }
|
VlCovBinNaming naming() const { return m_naming; }
|
||||||
const char* name() const { return m_name; }
|
const char* name() const { return m_name; }
|
||||||
const char* file() const { return m_file; }
|
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
|
/// 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.
|
/// by scanning bin counts, keeping the sample() hot path a plain counter bump.
|
||||||
|
|
||||||
class VlCoverpoint final : public VlCoverpointIf {
|
// Base coverpoint runtime (read side + collection logic, no hit-list storage).
|
||||||
// MEMBERS
|
// 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"
|
std::string m_hier; // "covergroup.coverpoint"
|
||||||
uint32_t m_atLeast = 1; // option.at_least (coverpoint-wide)
|
uint32_t m_atLeast = 1; // option.at_least (coverpoint-wide)
|
||||||
int m_total = 0; // bins across all sets
|
uint32_t m_total = 0; // bins across all sets
|
||||||
int m_normal = 0; // Normal bins (coverage denominator)
|
uint32_t m_normal = 0; // Normal bins (coverage denominator)
|
||||||
int m_nextBase = 0; // running append cursor
|
uint32_t m_nextBase = 0; // running append cursor
|
||||||
std::vector<uint32_t> m_counts; // [m_total], one per bin
|
std::vector<uint32_t> m_counts; // [m_total], one per bin
|
||||||
std::vector<VlCovNamer> m_namers; // appended in declaration order
|
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
|
// PRIVATE METHODS
|
||||||
const VlCovNamer& namerFor(int i) const; // obtain the bin-specific name producer
|
const VlCovNamer& namerFor(uint32_t i) const; // obtain the bin-specific name producer
|
||||||
void addNamer(VlCovBinKind set, int count, VlCovBinNaming naming, const char* name,
|
void addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming, const char* name,
|
||||||
const char* file, int line, int col);
|
const char* file, int line, int col);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -108,31 +122,44 @@ public:
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
// ---- configuration (from generated constructor) ----
|
// ---- 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) {
|
void addSingleNamer(VlCovBinKind set, const char* name, const char* file, int line, int col) {
|
||||||
addNamer(set, 1, VlCovBinNaming::Single, name, file, line, col);
|
addNamer(set, 1, VlCovBinNaming::Single, name, file, line, col);
|
||||||
}
|
}
|
||||||
void addArrayNamer(VlCovBinKind set, int count, const char* name, const char* file, int line,
|
void addArrayNamer(VlCovBinKind set, uint32_t count, const char* name, const char* file,
|
||||||
int col) {
|
int line, int col) {
|
||||||
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
|
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
|
||||||
}
|
}
|
||||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||||
|
|
||||||
// ---- hot path (from generated sample()) ----
|
// ---- hot path (from generated sample()) ----
|
||||||
void incrementBin(int i) { ++m_counts[i]; } // Normal bin: count only
|
// Clear the hit list at the start of each sample() for a cross-fed coverpoint.
|
||||||
void recordHit(int i) { ++m_counts[i]; } // Ignore/Illegal/Default: count only
|
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 ----
|
// ---- VlCoverpointIf ----
|
||||||
int binCount() const override { return m_total; }
|
uint32_t binCount() const override { return m_total; }
|
||||||
std::string binName(int i) const override;
|
std::string binName(uint32_t i) const override;
|
||||||
VlCovBinKind binKind(int i) const override { return namerFor(i).set(); }
|
// Deliberately not on VlCoverpointIf: only registerBins() needs it, via the
|
||||||
|
// concrete coverpoint. A cross has all-Normal bins and exposes no kind, so the
|
||||||
|
// interface omits it; add it back only if a writer needs it polymorphically.
|
||||||
|
VlCovBinKind binKind(uint32_t i) const { return namerFor(i).set(); }
|
||||||
void coverageParts(double& covered, double& total) const override {
|
void coverageParts(double& covered, double& total) const override {
|
||||||
// Count Normal bins that reached option.at_least on demand, so the hot
|
// Count Normal bins that reached option.at_least on demand, so the hot
|
||||||
// path (incrementBin) stays a plain counter bump.
|
// path (incrementBin) stays a plain counter bump.
|
||||||
int numCovered = 0;
|
uint32_t numCovered = 0;
|
||||||
for (const VlCovNamer& nm : m_namers) {
|
for (const VlCovNamer& nm : m_namers) {
|
||||||
if (nm.set() != VlCovBinKind::KIND_NORMAL) continue;
|
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;
|
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
|
#endif // Guard
|
||||||
|
|
|
||||||
|
|
@ -37,68 +37,68 @@
|
||||||
// SETTING OPERATORS
|
// SETTING OPERATORS
|
||||||
|
|
||||||
// Convert svBitVecVal to Verilator internal data
|
// 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);
|
const int words = VL_WORDS_I(obits);
|
||||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
||||||
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
|
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]);
|
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];
|
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];
|
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];
|
out = VL_MASK_I(obits) & lwp[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert Verilator internal data to svBitVecVal
|
// 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);
|
const int words = VL_WORDS_I(obits);
|
||||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
||||||
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
|
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; }
|
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_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE {
|
||||||
VL_SET_WQ(WDataOutP::external(owp), ld);
|
VL_SET_WQ(WDataOutP::external(owp), ld);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert svLogicVecVal to Verilator internal data
|
// Convert svLogicVecVal to Verilator internal data
|
||||||
// Note these functions ignore X/Z in svLogicVecVal
|
// 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);
|
const int words = VL_WORDS_I(obits);
|
||||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval;
|
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval;
|
||||||
owp[words - 1] = lwp[words - 1].aval & VL_MASK_I(obits);
|
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);
|
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;
|
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;
|
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;
|
out = VL_MASK_I(obits) & lwp[0].aval;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert Verilator internal data to svLogicVecVal
|
// Convert Verilator internal data to svLogicVecVal
|
||||||
// Note these functions never create X/Z in 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);
|
const int words = VL_WORDS_I(obits);
|
||||||
for (int i = 0; i < words; ++i) owp[i].bval = 0;
|
for (int i = 0; i < words; ++i) owp[i].bval = 0;
|
||||||
for (int i = 0; i < words - 1; ++i) owp[i].aval = lwp[i];
|
for (int i = 0; i < words - 1; ++i) owp[i].aval = lwp[i];
|
||||||
owp[words - 1].aval = lwp[words - 1] & VL_MASK_I(obits);
|
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].aval = ld;
|
||||||
owp[0].bval = 0;
|
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;
|
VlWide<2> lwp;
|
||||||
VL_SET_WQ(lwp, ld);
|
VL_SET_WQ(lwp, ld);
|
||||||
owp[0].aval = lwp[0];
|
owp[0].aval = lwp[0];
|
||||||
|
|
|
||||||
|
|
@ -98,8 +98,10 @@ void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||||
const VerilatedLockGuard lock{m_mutex};
|
const VerilatedLockGuard lock{m_mutex};
|
||||||
Super::closeBase();
|
Super::closeBase();
|
||||||
emitTimeChangeMaybe();
|
emitTimeChangeMaybe();
|
||||||
if (m_fst) m_fst->close(); // LCOV_EXCL_BR_LINE
|
if (m_fst) {
|
||||||
m_fst = nullptr;
|
m_fst->close();
|
||||||
|
VL_DO_CLEAR(delete m_fst, m_fst = nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -129,7 +129,7 @@ public:
|
||||||
void wait_report() {
|
void wait_report() {
|
||||||
if (m_pidExited) return;
|
if (m_pidExited) return;
|
||||||
#ifdef _VL_SOLVER_PIPE
|
#ifdef _VL_SOLVER_PIPE
|
||||||
if (waitpid(m_pid, &m_pidStatus, 0) != m_pid) return;
|
if (waitpid(m_pid, &m_pidStatus, WNOHANG) != m_pid) m_pidStatus = 0;
|
||||||
if (m_pidStatus) {
|
if (m_pidStatus) {
|
||||||
std::stringstream msg;
|
std::stringstream msg;
|
||||||
msg << "Subprocess command `" << m_cmd[0];
|
msg << "Subprocess command `" << m_cmd[0];
|
||||||
|
|
@ -208,8 +208,10 @@ public:
|
||||||
// Child
|
// Child
|
||||||
close(fd_stdin[P_WR]);
|
close(fd_stdin[P_WR]);
|
||||||
dup2(fd_stdin[P_RD], STDIN_FILENO);
|
dup2(fd_stdin[P_RD], STDIN_FILENO);
|
||||||
|
close(fd_stdin[P_RD]);
|
||||||
close(fd_stdout[P_RD]);
|
close(fd_stdout[P_RD]);
|
||||||
dup2(fd_stdout[P_WR], STDOUT_FILENO);
|
dup2(fd_stdout[P_WR], STDOUT_FILENO);
|
||||||
|
close(fd_stdout[P_WR]);
|
||||||
execvp(cmd[0], const_cast<char* const*>(cmd));
|
execvp(cmd[0], const_cast<char* const*>(cmd));
|
||||||
std::stringstream msg;
|
std::stringstream msg;
|
||||||
msg << "VlRProcess::open: execvp(" << cmd[0] << ")";
|
msg << "VlRProcess::open: execvp(" << cmd[0] << ")";
|
||||||
|
|
@ -441,11 +443,14 @@ void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits) {
|
||||||
os << ')';
|
os << ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t VlRandomizer::hashConstraints() const {
|
size_t VlRandomizer::hashConstraints(const std::vector<std::string>& extras) const {
|
||||||
size_t h = 0;
|
size_t h = 0;
|
||||||
for (const auto& c : m_constraints) {
|
for (const auto& c : m_constraints) {
|
||||||
h ^= std::hash<std::string>{}(c) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
h ^= std::hash<std::string>{}(c) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||||
}
|
}
|
||||||
|
for (const auto& c : extras) {
|
||||||
|
h ^= std::hash<std::string>{}(c) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||||
|
}
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -478,36 +483,20 @@ void VlRandomizer::recordRandcValues() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VlRandomizer::next_check_only(VlRNG& rngr) {
|
bool VlRandomizer::next_check_only(VlRNG& rngr) { return nextRandomize(rngr, true); }
|
||||||
m_checkOnly = true;
|
|
||||||
const bool result = next(rngr);
|
|
||||||
m_checkOnly = false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VlRandomizer::next(VlRNG& rngr) {
|
bool VlRandomizer::next(VlRNG& rngr) { return nextRandomize(rngr, false); }
|
||||||
if (!m_checkOnly && m_vars.empty() && m_unique_arrays.empty()) return true;
|
|
||||||
if (m_checkOnly && m_vars.empty()) return true; // No rand members: trivially SAT
|
|
||||||
for (const std::string& baseName : m_unique_arrays) {
|
|
||||||
const auto it = m_vars.find(baseName);
|
|
||||||
const uint32_t size = m_unique_array_sizes.at(baseName);
|
|
||||||
|
|
||||||
if (it != m_vars.end()) {
|
bool VlRandomizer::nextRandomize(VlRNG& rngr, bool checkOnly) {
|
||||||
std::string distinctExpr = "(__Vbv (distinct";
|
if (!checkOnly && m_vars.empty() && m_unique_arrays.empty()) return true;
|
||||||
for (uint32_t i = 0; i < size; ++i) {
|
if (checkOnly && m_vars.empty()) return true; // No rand members: trivially SAT
|
||||||
char hexIdx[12];
|
m_checkOnly = checkOnly;
|
||||||
sprintf(hexIdx, "#x%08x", i);
|
const std::vector<std::string> uniqueExprs = buildUniqueExprs();
|
||||||
distinctExpr += " (select " + it->first + " " + hexIdx + ")";
|
|
||||||
}
|
|
||||||
distinctExpr += "))";
|
|
||||||
m_constraints.push_back(distinctExpr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Randc exclusion-based cycling: exclude previously used values per randc var.
|
// Randc exclusion-based cycling: exclude previously used values per randc var.
|
||||||
// When solver returns unsat (all values exhausted), clear history for new cycle.
|
// When solver returns unsat (all values exhausted), clear history for new cycle.
|
||||||
if (!m_randcVarNames.empty()) {
|
if (!m_randcVarNames.empty()) {
|
||||||
const size_t currentHash = hashConstraints();
|
const size_t currentHash = hashConstraints(uniqueExprs);
|
||||||
// Invalidate history if constraints changed (e.g., constraint_mode toggled)
|
// Invalidate history if constraints changed (e.g., constraint_mode toggled)
|
||||||
if (currentHash != m_randcConstraintHash) {
|
if (currentHash != m_randcConstraintHash) {
|
||||||
m_randcUsedValues.clear();
|
m_randcUsedValues.clear();
|
||||||
|
|
@ -516,83 +505,103 @@ bool VlRandomizer::next(VlRNG& rngr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pinned vars make phase ordering moot; skip phased path in check-only.
|
// Pinned vars make phase ordering moot; skip phased path in check-only.
|
||||||
if (!m_checkOnly && !m_solveBefore.empty()) return nextPhased(rngr);
|
bool result;
|
||||||
|
if (!m_checkOnly && !m_solveBefore.empty()) {
|
||||||
|
result = nextPhased(rngr, uniqueExprs);
|
||||||
|
} else {
|
||||||
|
result = nextFlat(rngr, uniqueExprs);
|
||||||
|
}
|
||||||
|
m_checkOnly = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> VlRandomizer::buildUniqueExprs() const {
|
||||||
|
std::vector<std::string> exprs;
|
||||||
|
if (m_unique_arrays.empty()) return exprs;
|
||||||
|
const auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
||||||
|
for (const std::string& baseName : m_unique_arrays) {
|
||||||
|
const auto it = m_vars.find(baseName);
|
||||||
|
if (it == m_vars.end()) continue;
|
||||||
|
const VlRandomVar& var = *it->second;
|
||||||
|
// Select the elements the array actually holds now, by their own index
|
||||||
|
// or key, rather than by ordinal position
|
||||||
|
var.setArrayInfo(arrVarsp);
|
||||||
|
// 'distinct' needs at least two operands; fewer elements are trivially unique
|
||||||
|
if (var.countMatchingElements(*arrVarsp, baseName) < 2) continue;
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "(__Vbv (distinct ";
|
||||||
|
var.emitGetValue(os);
|
||||||
|
os << "))";
|
||||||
|
exprs.push_back(os.str());
|
||||||
|
}
|
||||||
|
return exprs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VlRandomizer::emitDefines(std::ostream& os) const {
|
||||||
|
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
|
||||||
|
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void VlRandomizer::emitDeclares(std::ostream& os, bool pinCurrent) const {
|
||||||
|
for (const auto& var : m_vars) {
|
||||||
|
if (var.second->dimension() > 0) {
|
||||||
|
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
||||||
|
var.second->setArrayInfo(arrVarsp);
|
||||||
|
}
|
||||||
|
os << "(declare-fun " << var.first << " () ";
|
||||||
|
var.second->emitType(os);
|
||||||
|
os << ")\n";
|
||||||
|
// Pin each var to its current value
|
||||||
|
if (pinCurrent) {
|
||||||
|
assert(var.second->dimension() == 0);
|
||||||
|
os << "(assert (= " << var.first << ' ';
|
||||||
|
var.second->emitConcreteValue(os);
|
||||||
|
os << "))\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VlRandomizer::emitAsserts(std::ostream& os, const std::vector<std::string>& extras,
|
||||||
|
bool named) const {
|
||||||
|
int j = 0;
|
||||||
|
for (const std::string& constraint : m_constraints) {
|
||||||
|
if (named) {
|
||||||
|
os << "(assert (! (= #b1 " << constraint << ") :named cons" << j++ << "))\n";
|
||||||
|
} else {
|
||||||
|
os << "(assert (= #b1 " << constraint << "))\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const std::string& extra : extras) {
|
||||||
|
if (named) {
|
||||||
|
os << "(assert (! (= #b1 " << extra << ") :named cons" << j++ << "))\n";
|
||||||
|
} else {
|
||||||
|
os << "(assert (= #b1 " << extra << "))\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VlRandomizer::nextFlat(VlRNG& rngr, const std::vector<std::string>& uniqueExprs) {
|
||||||
// Randc retry: if unsat due to randc exhaustion, clear history and retry once
|
// Randc retry: if unsat due to randc exhaustion, clear history and retry once
|
||||||
const bool hasRandc = !m_randcVarNames.empty();
|
const bool hasRandc = !m_randcVarNames.empty();
|
||||||
for (int attempt = 0; attempt < (hasRandc ? 2 : 1); ++attempt) {
|
for (int attempt = 0; attempt < (hasRandc ? 2 : 1); ++attempt) {
|
||||||
std::iostream& os = getSolver();
|
std::iostream& os = getSolver();
|
||||||
if (!os) return false;
|
if (!os) return false;
|
||||||
|
|
||||||
// Soft constraint relaxation (IEEE 1800-2023 18.5.13, last-wins priority):
|
|
||||||
// Try hard + soft[0..N-1], then hard + soft[1..N-1], ..., then hard only.
|
|
||||||
// First SAT phase wins. If hard-only is UNSAT, report via unsat-core.
|
|
||||||
os << "(set-option :produce-models true)\n";
|
os << "(set-option :produce-models true)\n";
|
||||||
// Lets the scalar pin path learn which free-bit assumptions conflict.
|
// Lets the scalar pin path learn which free-bit assumptions conflict.
|
||||||
os << "(set-option :produce-unsat-assumptions true)\n";
|
os << "(set-option :produce-unsat-assumptions true)\n";
|
||||||
os << "(set-logic QF_ABV)\n";
|
os << "(set-logic QF_ABV)\n";
|
||||||
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
|
emitDefines(os);
|
||||||
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
|
emitDeclares(os, m_checkOnly);
|
||||||
for (const auto& var : m_vars) {
|
emitAsserts(os, uniqueExprs, false);
|
||||||
if (var.second->dimension() > 0) {
|
|
||||||
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
|
||||||
var.second->setArrayInfo(arrVarsp);
|
|
||||||
}
|
|
||||||
os << "(declare-fun " << var.first << " () ";
|
|
||||||
var.second->emitType(os);
|
|
||||||
os << ")\n";
|
|
||||||
// Pin each var to its current value: SAT iff the current values
|
|
||||||
// satisfy the constraints. V3Randomize rejects non-scalar rand
|
|
||||||
// members upstream, hence the assert.
|
|
||||||
if (m_checkOnly) {
|
|
||||||
assert(var.second->dimension() == 0);
|
|
||||||
os << "(assert (= " << var.first << ' ';
|
|
||||||
var.second->emitConcreteValue(os);
|
|
||||||
os << "))\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::string& constraint : m_constraints) {
|
|
||||||
os << "(assert (= #b1 " << constraint << "))\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// randc exclusions vs. a pinned current value would make every check
|
// randc exclusions vs. a pinned current value would make every check
|
||||||
// trivially UNSAT after the first cycle.
|
// trivially UNSAT after the first cycle.
|
||||||
if (!m_checkOnly) emitRandcExclusions(os);
|
if (!m_checkOnly) emitRandcExclusions(os);
|
||||||
|
|
||||||
const size_t nSoft = m_softConstraints.size();
|
relaxSoftConstraints(os);
|
||||||
bool sat = false;
|
os << "(check-sat)\n";
|
||||||
if (nSoft > 0) {
|
const bool sat = parseSolution(os);
|
||||||
// Fast path: try all soft constraints at once
|
|
||||||
os << "(push 1)\n";
|
|
||||||
for (const auto& s : m_softConstraints) os << "(assert (= #b1 " << s << "))\n";
|
|
||||||
os << "(check-sat)\n";
|
|
||||||
sat = parseSolution(os, false);
|
|
||||||
if (!sat) {
|
|
||||||
// Some soft constraints conflict. Incrementally add from back
|
|
||||||
// (highest priority first), keeping only compatible ones.
|
|
||||||
// This preserves the maximum set of compatible soft constraints.
|
|
||||||
os << "(pop 1)\n";
|
|
||||||
for (int i = static_cast<int>(nSoft) - 1; i >= 0; --i) {
|
|
||||||
os << "(push 1)\n";
|
|
||||||
os << "(assert (= #b1 " << m_softConstraints[i] << "))\n";
|
|
||||||
os << "(check-sat)\n";
|
|
||||||
if (checkSat(os)) {
|
|
||||||
// Compatible -- keep this push level
|
|
||||||
} else {
|
|
||||||
// Incompatible -- remove this soft constraint
|
|
||||||
os << "(pop 1)\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Read solution with remaining compatible soft constraints
|
|
||||||
os << "(check-sat)\n";
|
|
||||||
sat = parseSolution(os, false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No soft constraints -- hard-only
|
|
||||||
os << "(check-sat)\n";
|
|
||||||
sat = parseSolution(os, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sat) {
|
if (!sat) {
|
||||||
os << "(reset)\n";
|
os << "(reset)\n";
|
||||||
|
|
@ -606,116 +615,120 @@ bool VlRandomizer::next(VlRNG& rngr) {
|
||||||
// the solver's free assignment.
|
// the solver's free assignment.
|
||||||
if (m_checkOnly) return false;
|
if (m_checkOnly) return false;
|
||||||
// Genuine unsat: report via unsat-core
|
// Genuine unsat: report via unsat-core
|
||||||
os << "(set-option :produce-unsat-cores true)\n";
|
reportUnsatSetup(os, uniqueExprs);
|
||||||
os << "(set-logic QF_ABV)\n";
|
|
||||||
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
|
|
||||||
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
|
|
||||||
for (const auto& var : m_vars) {
|
|
||||||
if (var.second->dimension() > 0) {
|
|
||||||
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
|
||||||
var.second->setArrayInfo(arrVarsp);
|
|
||||||
}
|
|
||||||
os << "(declare-fun " << var.first << " () ";
|
|
||||||
var.second->emitType(os);
|
|
||||||
os << ")\n";
|
|
||||||
}
|
|
||||||
int j = 0;
|
|
||||||
for (const std::string& constraint : m_constraints) {
|
|
||||||
os << "(assert (! (= #b1 " << constraint << ") :named cons" << j++ << "))\n";
|
|
||||||
}
|
|
||||||
os << "(check-sat)\n";
|
|
||||||
sat = parseSolution(os, true);
|
|
||||||
(void)sat;
|
|
||||||
os << "(reset)\n";
|
os << "(reset)\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_checkOnly) {
|
if (!m_checkOnly) {
|
||||||
bool hasArray = false;
|
solveDiversity(rngr, os);
|
||||||
for (const auto& var : m_vars) {
|
// Check-only must not advance randc cycle state.
|
||||||
if (var.second->dimension() > 0) {
|
recordRandcValues();
|
||||||
hasArray = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasArray) {
|
|
||||||
// Tie each free bit to a fresh random target via a boolean
|
|
||||||
// assumption literal a_k <=> (bit_k == target_k), then force the
|
|
||||||
// bits with (check-sat-assuming ...). If UNSAT,
|
|
||||||
// (get-unsat-assumptions) names the literals clashing with the
|
|
||||||
// feasible base; drop ONE per round so the maximal compatible
|
|
||||||
// set survives -- dropping a whole conflicting group at once
|
|
||||||
// would collapse the diversity of tightly coupled bits (one-hot,
|
|
||||||
// 2-value sets) onto the solver's fixed default. Assumptions are
|
|
||||||
// ephemeral, so rounds need no push/pop or re-asserting and the
|
|
||||||
// solver keeps its learned clauses. Each round drops >= 1 -> ends
|
|
||||||
// in <= npins rounds.
|
|
||||||
std::vector<bool> targets;
|
|
||||||
int npins = 0;
|
|
||||||
for (const auto& var : m_vars) {
|
|
||||||
const int w = var.second->totalWidth();
|
|
||||||
for (int b = 0; b < w; b++) {
|
|
||||||
const bool target = (VL_RANDOM_RNG_I(rngr) & 1);
|
|
||||||
targets.push_back(target);
|
|
||||||
os << "(declare-fun a" << npins << " () Bool)\n";
|
|
||||||
os << "(assert (= a" << npins << " (=";
|
|
||||||
var.second->emitExtract(os, b);
|
|
||||||
os << " #b" << (target ? '1' : '0') << ")))\n";
|
|
||||||
++npins;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<bool> dropped(npins, false);
|
|
||||||
for (int round = 0; round <= npins; ++round) {
|
|
||||||
os << "(check-sat-assuming (";
|
|
||||||
for (int k = 0; k < npins; k++)
|
|
||||||
if (!dropped[k]) os << " a" << k;
|
|
||||||
os << "))\n";
|
|
||||||
if (parseSolution(os, false)) break;
|
|
||||||
// get-unsat-assumptions only echoes still-active literals,
|
|
||||||
// so the first in-range index is a live conflicting bit.
|
|
||||||
const std::vector<int> core = readUnsatAssumptions(os);
|
|
||||||
for (const int idx : core)
|
|
||||||
if (idx < npins) {
|
|
||||||
dropped[idx] = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Array present: original XOR-rounds path.
|
|
||||||
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
|
|
||||||
os << "(assert ";
|
|
||||||
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
|
|
||||||
os << ")\n";
|
|
||||||
os << "\n(check-sat)\n";
|
|
||||||
sat = parseSolution(os, false);
|
|
||||||
(void)sat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check-only must not advance randc cycle state.
|
|
||||||
if (!m_checkOnly) recordRandcValues();
|
|
||||||
|
|
||||||
os << "(reset)\n";
|
os << "(reset)\n";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false; // Should not reach here
|
return false; // Should not reach here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VlRandomizer::solveDiversity(VlRNG& rngr, std::iostream& os) {
|
||||||
|
bool hasArray = false;
|
||||||
|
for (const auto& var : m_vars) {
|
||||||
|
if (var.second->dimension() > 0) {
|
||||||
|
hasArray = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasArray) {
|
||||||
|
solveDiversityXor(rngr, os);
|
||||||
|
} else {
|
||||||
|
solveDiversityPins(rngr, os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VlRandomizer::solveDiversityPins(VlRNG& rngr, std::iostream& os) {
|
||||||
|
// Tie each free bit to a random target via an assumption literal;
|
||||||
|
// drop one conflicting literal per round until compatible
|
||||||
|
int npins = 0;
|
||||||
|
for (const auto& var : m_vars) {
|
||||||
|
const int w = var.second->totalWidth();
|
||||||
|
for (int b = 0; b < w; ++b) {
|
||||||
|
const bool target = (VL_RANDOM_RNG_I(rngr) & 1);
|
||||||
|
os << "(declare-fun a" << npins << " () Bool)\n";
|
||||||
|
os << "(assert (= a" << npins << " (=";
|
||||||
|
var.second->emitExtract(os, b);
|
||||||
|
os << " #b" << (target ? '1' : '0') << ")))\n";
|
||||||
|
++npins;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<bool> dropped(npins, false);
|
||||||
|
for (int round = 0; round <= npins; ++round) {
|
||||||
|
os << "(check-sat-assuming (";
|
||||||
|
for (int k = 0; k < npins; ++k) {
|
||||||
|
if (!dropped[k]) os << " a" << k;
|
||||||
|
}
|
||||||
|
os << "))\n";
|
||||||
|
if (parseSolution(os)) return;
|
||||||
|
// get-unsat-assumptions only echoes still-active literals,
|
||||||
|
// so the first in-range index is a live conflicting bit.
|
||||||
|
const std::vector<int> core = readUnsatAssumptions(os);
|
||||||
|
for (const int idx : core) {
|
||||||
|
if (idx < npins) {
|
||||||
|
dropped[idx] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VlRandomizer::solveDiversityXor(VlRNG& rngr, std::iostream& os) {
|
||||||
|
bool sat = true;
|
||||||
|
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
|
||||||
|
os << "(assert ";
|
||||||
|
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
|
||||||
|
os << ")\n";
|
||||||
|
os << "\n(check-sat)\n";
|
||||||
|
sat = parseSolution(os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// False once the solver is gone, so no reply loop can spin forever
|
||||||
|
static bool readNonBlankLine(std::istream& is, std::string& liner) {
|
||||||
|
do {
|
||||||
|
if (!std::getline(is, liner)) return false;
|
||||||
|
} while (liner.empty());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool VlRandomizer::checkSat(std::iostream& os) {
|
bool VlRandomizer::checkSat(std::iostream& os) {
|
||||||
std::string result;
|
std::string result;
|
||||||
do { std::getline(os, result); } while (result.empty());
|
if (!readNonBlankLine(os, result)) return false;
|
||||||
return result == "sat";
|
return result == "sat";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> VlRandomizer::readUnsatAssumptions(std::iostream& os) {
|
void VlRandomizer::relaxSoftConstraints(std::iostream& os) {
|
||||||
os << "(get-unsat-assumptions)\n";
|
// Re-add softs highest-priority first, dropping incompatible ones.
|
||||||
std::string line;
|
const size_t nSoft = m_softConstraints.size();
|
||||||
do { std::getline(os, line); } while (line.empty());
|
if (nSoft == 0) return;
|
||||||
// The response lists only "a<N>" literals; collect each full integer run.
|
os << "(push 1)\n";
|
||||||
|
for (const auto& s : m_softConstraints) os << "(assert (= #b1 " << s << "))\n";
|
||||||
|
os << "(check-sat)\n";
|
||||||
|
if (checkSat(os)) return;
|
||||||
|
os << "(pop 1)\n";
|
||||||
|
for (auto it = m_softConstraints.rbegin(); it != m_softConstraints.rend(); ++it) {
|
||||||
|
os << "(push 1)\n";
|
||||||
|
os << "(assert (= #b1 " << *it << "))\n";
|
||||||
|
os << "(check-sat)\n";
|
||||||
|
if (!checkSat(os)) os << "(pop 1)\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every complete run of digits in the reply, in order
|
||||||
|
static std::vector<int> scanIntRuns(const std::string& reply) {
|
||||||
std::vector<int> idxs;
|
std::vector<int> idxs;
|
||||||
std::string num;
|
std::string num;
|
||||||
for (const char c : line) {
|
for (const char c : reply) {
|
||||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||||
num += c;
|
num += c;
|
||||||
} else if (!num.empty()) {
|
} else if (!num.empty()) {
|
||||||
|
|
@ -727,60 +740,66 @@ std::vector<int> VlRandomizer::readUnsatAssumptions(std::iostream& os) {
|
||||||
return idxs;
|
return idxs;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
|
std::vector<int> VlRandomizer::readUnsatAssumptions(std::iostream& os) {
|
||||||
std::string sat;
|
os << "(get-unsat-assumptions)\n";
|
||||||
do { std::getline(os, sat); } while (sat == "");
|
std::string line;
|
||||||
if (sat == "unsat") {
|
if (!readNonBlankLine(os, line)) return {};
|
||||||
if (!log) return false;
|
// The response lists only "a<N>" literals; collect each full integer run.
|
||||||
os << "(get-unsat-core) \n";
|
return scanIntRuns(line);
|
||||||
sat.clear();
|
}
|
||||||
std::getline(os, sat);
|
|
||||||
std::vector<int> numbers;
|
// Re-solve with named asserts so an unsat core can name the failing constraints
|
||||||
std::string currentNum;
|
void VlRandomizer::reportUnsatSetup(std::iostream& os,
|
||||||
for (const char c : sat) {
|
const std::vector<std::string>& uniqueExprs) {
|
||||||
if (std::isdigit(c)) {
|
os << "(set-option :produce-unsat-cores true)\n";
|
||||||
currentNum += c;
|
os << "(set-logic QF_ABV)\n";
|
||||||
numbers.push_back(std::stoi(currentNum));
|
emitDefines(os);
|
||||||
currentNum.clear();
|
emitDeclares(os, false);
|
||||||
}
|
emitAsserts(os, uniqueExprs, true);
|
||||||
}
|
os << "(check-sat)\n";
|
||||||
if (Verilated::threadContextp()->warnUnsatConstr()) {
|
std::string status;
|
||||||
for (const int n : numbers) {
|
if (!readNonBlankLine(os, status)) return;
|
||||||
if (n < m_constraints_line.size()) {
|
if (status == "unsat") reportUnsatCore(os);
|
||||||
const std::string& constraint_info = m_constraints_line[n];
|
}
|
||||||
// Parse "filename:linenum source" format
|
|
||||||
const size_t colon_pos = constraint_info.find(':');
|
void VlRandomizer::reportUnsatCore(std::iostream& os) {
|
||||||
if (colon_pos != std::string::npos) {
|
os << "(get-unsat-core)\n";
|
||||||
const std::string filename = constraint_info.substr(0, colon_pos);
|
std::string reply;
|
||||||
const size_t space_pos = constraint_info.find(" ", colon_pos);
|
std::getline(os, reply);
|
||||||
std::string linenum_str;
|
const std::vector<int> numbers = scanIntRuns(reply);
|
||||||
std::string source;
|
if (Verilated::threadContextp()->warnUnsatConstr()) {
|
||||||
if (space_pos != std::string::npos) {
|
for (const int n : numbers) {
|
||||||
linenum_str
|
if (static_cast<size_t>(n) < m_constraints_line.size()) {
|
||||||
= constraint_info.substr(colon_pos + 1, space_pos - colon_pos - 1);
|
const std::string& constraint_info = m_constraints_line[n];
|
||||||
source = constraint_info.substr(space_pos + 3);
|
// Parse "filename:linenum source" format, parts optional
|
||||||
} else {
|
std::string filename;
|
||||||
linenum_str = constraint_info.substr(colon_pos + 1);
|
int linenum = 0;
|
||||||
}
|
std::string source = constraint_info;
|
||||||
const int linenum = std::stoi(linenum_str);
|
const size_t colon_pos = constraint_info.find(':');
|
||||||
std::string msg = "UNSATCONSTR: Unsatisfied constraint";
|
if (colon_pos != std::string::npos) {
|
||||||
if (!source.empty()) {
|
filename = constraint_info.substr(0, colon_pos);
|
||||||
// Trim leading whitespace and add quotes
|
const size_t space_pos = constraint_info.find(" ", colon_pos);
|
||||||
const size_t start = source.find_first_not_of(" \t");
|
const size_t num_end
|
||||||
if (start != std::string::npos) {
|
= space_pos == std::string::npos ? constraint_info.size() : space_pos;
|
||||||
msg += ": '" + source.substr(start) + "'";
|
linenum = std::atoi(
|
||||||
}
|
constraint_info.substr(colon_pos + 1, num_end - colon_pos - 1).c_str());
|
||||||
}
|
source = space_pos == std::string::npos
|
||||||
VL_WARN_MT(filename.c_str(), linenum, "", msg.c_str());
|
? ""
|
||||||
} else {
|
: constraint_info.substr(space_pos + 3);
|
||||||
VL_PRINTF("%%Warning-UNSATCONSTR: Unsatisfied constraint: %s\n",
|
|
||||||
constraint_info.c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
std::string msg = "UNSATCONSTR: Unsatisfied constraint";
|
||||||
|
const size_t start = source.find_first_not_of(" \t");
|
||||||
|
if (start != std::string::npos) msg += ": '" + source.substr(start) + "'";
|
||||||
|
VL_WARN_MT(filename.c_str(), linenum, "", msg.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VlRandomizer::parseSolution(std::iostream& os) {
|
||||||
|
std::string sat;
|
||||||
|
if (!readNonBlankLine(os, sat)) return false;
|
||||||
|
if (sat == "unsat") return false;
|
||||||
if (sat != "sat") {
|
if (sat != "sat") {
|
||||||
std::stringstream msg;
|
std::stringstream msg;
|
||||||
msg << "Internal: Solver error: " << sat;
|
msg << "Internal: Solver error: " << sat;
|
||||||
|
|
@ -789,25 +808,29 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
os << "(get-value (";
|
std::stringstream getValueStr;
|
||||||
for (const auto& var : m_vars) {
|
for (const auto& var : m_vars) {
|
||||||
if (var.second->dimension() > 0) {
|
if (var.second->dimension() > 0) {
|
||||||
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
||||||
var.second->setArrayInfo(arrVarsp);
|
var.second->setArrayInfo(arrVarsp);
|
||||||
}
|
}
|
||||||
var.second->emitGetValue(os);
|
var.second->emitGetValue(getValueStr);
|
||||||
}
|
}
|
||||||
os << "))\n";
|
if (getValueStr.str() == "") {
|
||||||
|
// Mark as m_checkOnly to skip generation of any subsequent solver calls
|
||||||
|
m_checkOnly = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
os << "(get-value (" << getValueStr.str() << "))\n";
|
||||||
// Quasi-parse S-expression of the form ((x #xVALUE) (y #bVALUE) (z #xVALUE))
|
// Quasi-parse S-expression of the form ((x #xVALUE) (y #bVALUE) (z #xVALUE))
|
||||||
char c;
|
char c;
|
||||||
os >> c;
|
if (!(os >> c) || c != '(') {
|
||||||
if (c != '(') {
|
|
||||||
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
||||||
"Internal: Unable to parse solver's response: invalid S-expression");
|
"Internal: Unable to parse solver's response: invalid S-expression");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
os >> c;
|
if (!(os >> c)) return false;
|
||||||
if (c == ')') break;
|
if (c == ')') break;
|
||||||
if (c != '(') {
|
if (c != '(') {
|
||||||
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
||||||
|
|
@ -912,6 +935,7 @@ void VlRandomizer::clearConstraints() {
|
||||||
m_constraints_line.clear();
|
m_constraints_line.clear();
|
||||||
m_solveBefore.clear();
|
m_solveBefore.clear();
|
||||||
m_softConstraints.clear();
|
m_softConstraints.clear();
|
||||||
|
m_unique_arrays.clear(); // Re-registered by constraint setup
|
||||||
// Keep m_vars for class member randomization
|
// Keep m_vars for class member randomization
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -930,13 +954,7 @@ void VlRandomizer::solveBefore(const std::string& beforeName, const std::string&
|
||||||
m_solveBefore.emplace_back(beforeName, afterName);
|
m_solveBefore.emplace_back(beforeName, afterName);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
bool VlRandomizer::buildSolveLayers(std::vector<std::vector<std::string>>& layersr) {
|
||||||
// Phased solving for solve...before constraints.
|
|
||||||
// Variables are solved in layers determined by topological sort of the
|
|
||||||
// solve-before dependency graph. Each layer is solved with ALL constraints
|
|
||||||
// (preserving the solution space) but earlier layers' values are pinned.
|
|
||||||
|
|
||||||
// Step 1: Build dependency graph (before -> {after vars})
|
|
||||||
std::map<std::string, std::set<std::string>> graph;
|
std::map<std::string, std::set<std::string>> graph;
|
||||||
std::map<std::string, int> inDegree;
|
std::map<std::string, int> inDegree;
|
||||||
std::set<std::string> solveBeforeVars;
|
std::set<std::string> solveBeforeVars;
|
||||||
|
|
@ -953,18 +971,12 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
||||||
if (inDegree.find(after) == inDegree.end()) inDegree[after] = 0;
|
if (inDegree.find(after) == inDegree.end()) inDegree[after] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute in-degrees (after depends on before, so edge is before->after,
|
// "solve x before y": edge x -> y, in-degree of y increases
|
||||||
// but for solving order: before has no incoming edge from after)
|
|
||||||
// Actually: "solve x before y" means x should be solved first.
|
|
||||||
// Dependency: y depends on x. Edge: x -> y. in-degree of y increases.
|
|
||||||
for (const auto& entry : graph) {
|
for (const auto& entry : graph) {
|
||||||
for (const auto& to : entry.second) { inDegree[to]++; }
|
for (const auto& to : entry.second) { inDegree[to]++; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Topological sort into layers (Kahn's algorithm)
|
|
||||||
std::vector<std::vector<std::string>> layers;
|
|
||||||
std::set<std::string> remaining = solveBeforeVars;
|
std::set<std::string> remaining = solveBeforeVars;
|
||||||
|
|
||||||
while (!remaining.empty()) {
|
while (!remaining.empty()) {
|
||||||
std::vector<std::string> currentLayer;
|
std::vector<std::string> currentLayer;
|
||||||
for (const auto& var : remaining) {
|
for (const auto& var : remaining) {
|
||||||
|
|
@ -981,22 +993,38 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
||||||
for (const auto& to : graph[var]) { inDegree[to]--; }
|
for (const auto& to : graph[var]) { inDegree[to]--; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
layers.push_back(std::move(currentLayer));
|
layersr.push_back(std::move(currentLayer));
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// If only one layer, no phased solving needed -- fall through to normal path
|
const char* VlRandomizer::phasedLogic() const {
|
||||||
// (all solve_before vars are independent, no actual ordering required)
|
for (const auto& var : m_vars) {
|
||||||
if (layers.size() <= 1) {
|
if (var.second->dimension() == 0) continue;
|
||||||
// Clear solve_before temporarily and call normal next()
|
if (!var.second->hasMatchingElements(m_arr_vars, var.second->name())) return "ALL";
|
||||||
const auto saved = std::move(m_solveBefore);
|
|
||||||
m_solveBefore.clear();
|
|
||||||
const bool result = next(rngr);
|
|
||||||
m_solveBefore = std::move(saved);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
return "QF_ABV";
|
||||||
|
}
|
||||||
|
|
||||||
// Step 3: Solve phase by phase
|
bool VlRandomizer::nextPhased(VlRNG& rngr, const std::vector<std::string>& uniqueExprs) {
|
||||||
|
// Solve layer by layer with ALL constraints, pinning earlier layers
|
||||||
|
std::vector<std::vector<std::string>> layers;
|
||||||
|
if (!buildSolveLayers(layers)) return false;
|
||||||
|
|
||||||
|
// One layer: all solve_before vars are independent, no ordering required
|
||||||
|
if (layers.size() <= 1) return nextFlat(rngr, uniqueExprs);
|
||||||
|
|
||||||
|
if (solvePhases(rngr, layers, uniqueExprs)) return true;
|
||||||
|
// Retry once with the randc cycle cleared, as nextFlat does
|
||||||
|
if (m_randcUsedValues.empty()) return false;
|
||||||
|
m_randcUsedValues.clear();
|
||||||
|
return solvePhases(rngr, layers, uniqueExprs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VlRandomizer::solvePhases(VlRNG& rngr, const std::vector<std::vector<std::string>>& layers,
|
||||||
|
const std::vector<std::string>& uniqueExprs) {
|
||||||
std::map<std::string, std::string> solvedValues; // varName -> SMT value literal
|
std::map<std::string, std::string> solvedValues; // varName -> SMT value literal
|
||||||
|
const char* const logicp = phasedLogic();
|
||||||
|
|
||||||
for (size_t phase = 0; phase < layers.size(); phase++) {
|
for (size_t phase = 0; phase < layers.size(); phase++) {
|
||||||
const bool isFinalPhase = (phase == layers.size() - 1);
|
const bool isFinalPhase = (phase == layers.size() - 1);
|
||||||
|
|
@ -1004,143 +1032,45 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
||||||
std::iostream& os = getSolver();
|
std::iostream& os = getSolver();
|
||||||
if (!os) return false;
|
if (!os) return false;
|
||||||
|
|
||||||
// Solver session setup
|
|
||||||
os << "(set-option :produce-models true)\n";
|
os << "(set-option :produce-models true)\n";
|
||||||
os << "(set-logic QF_ABV)\n";
|
os << "(set-logic " << logicp << ")\n";
|
||||||
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
|
emitDefines(os);
|
||||||
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
|
emitDeclares(os, false);
|
||||||
|
|
||||||
// Declare ALL variables
|
|
||||||
for (const auto& var : m_vars) {
|
|
||||||
if (var.second->dimension() > 0) {
|
|
||||||
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
|
||||||
var.second->setArrayInfo(arrVarsp);
|
|
||||||
}
|
|
||||||
os << "(declare-fun " << var.first << " () ";
|
|
||||||
var.second->emitType(os);
|
|
||||||
os << ")\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pin all previously solved variables
|
|
||||||
for (const auto& entry : solvedValues) {
|
for (const auto& entry : solvedValues) {
|
||||||
os << "(assert (= " << entry.first << " " << entry.second << "))\n";
|
os << "(assert (= " << entry.first << " " << entry.second << "))\n";
|
||||||
}
|
}
|
||||||
|
emitAsserts(os, uniqueExprs, false);
|
||||||
// Assert ALL constraints
|
|
||||||
for (const std::string& constraint : m_constraints) {
|
|
||||||
os << "(assert (= #b1 " << constraint << "))\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Randc: exclude previously used values
|
// Randc: exclude previously used values
|
||||||
emitRandcExclusions(os);
|
emitRandcExclusions(os);
|
||||||
|
|
||||||
|
// Soft constraints participate in every phase, priority-ordered.
|
||||||
|
relaxSoftConstraints(os);
|
||||||
|
|
||||||
// Initial check-sat WITHOUT diversity (guaranteed sat if constraints are consistent)
|
// Initial check-sat WITHOUT diversity (guaranteed sat if constraints are consistent)
|
||||||
os << "(check-sat)\n";
|
os << "(check-sat)\n";
|
||||||
|
|
||||||
if (isFinalPhase) {
|
if (isFinalPhase) {
|
||||||
// Final phase: use parseSolution to write ALL values to memory
|
// Final phase: use parseSolution to write ALL values to memory
|
||||||
bool sat = parseSolution(os, true);
|
const bool sat = parseSolution(os);
|
||||||
if (!sat) {
|
if (!sat) {
|
||||||
if (!m_randcVarNames.empty()) m_randcUsedValues.clear();
|
|
||||||
os << "(reset)\n";
|
os << "(reset)\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
solveDiversityXor(rngr, os);
|
||||||
// Record solved randc values for future exclusion
|
// Record solved randc values for future exclusion
|
||||||
recordRandcValues();
|
recordRandcValues();
|
||||||
// Diversity loop (same as normal next())
|
|
||||||
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
|
|
||||||
os << "(assert ";
|
|
||||||
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
|
|
||||||
os << ")\n";
|
|
||||||
os << "\n(check-sat)\n";
|
|
||||||
sat = parseSolution(os, false);
|
|
||||||
(void)sat;
|
|
||||||
}
|
|
||||||
os << "(reset)\n";
|
os << "(reset)\n";
|
||||||
} else {
|
} else {
|
||||||
// Intermediate phase: extract values for current layer variables only
|
if (!checkSat(os)) {
|
||||||
std::string satResponse;
|
|
||||||
do { std::getline(os, satResponse); } while (satResponse.empty());
|
|
||||||
|
|
||||||
if (satResponse != "sat") {
|
|
||||||
os << "(reset)\n";
|
os << "(reset)\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!solvePhaseValues(os, rngr, layers[phase], solvedValues)) {
|
||||||
// Build get-value variable list for this layer
|
|
||||||
const auto& layerVars = layers[phase];
|
|
||||||
auto getValueCmd = [&]() {
|
|
||||||
os << "(get-value (";
|
|
||||||
for (const auto& varName : layerVars) {
|
|
||||||
if (m_vars.count(varName)) os << varName << " ";
|
|
||||||
}
|
|
||||||
os << "))\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper to parse ((name1 value1) (name2 value2) ...) response
|
|
||||||
auto parseGetValue = [&]() -> bool {
|
|
||||||
char c;
|
|
||||||
os >> c; // outer '('
|
|
||||||
while (true) {
|
|
||||||
os >> c;
|
|
||||||
if (c == ')') break; // outer closing
|
|
||||||
if (c != '(') return false;
|
|
||||||
std::string name;
|
|
||||||
os >> name;
|
|
||||||
|
|
||||||
// Read value handling nested parens for (_ bvN W) format
|
|
||||||
os >> std::ws;
|
|
||||||
std::string value;
|
|
||||||
char firstChar;
|
|
||||||
os.get(firstChar);
|
|
||||||
if (firstChar == '(') {
|
|
||||||
// Compound value like (_ bv5 32)
|
|
||||||
value = "(";
|
|
||||||
int depth = 1;
|
|
||||||
while (depth > 0) {
|
|
||||||
os.get(c);
|
|
||||||
value += c;
|
|
||||||
if (c == '(')
|
|
||||||
depth++;
|
|
||||||
else if (c == ')')
|
|
||||||
depth--;
|
|
||||||
}
|
|
||||||
// Read closing ')' of the pair
|
|
||||||
os >> c;
|
|
||||||
} else {
|
|
||||||
// Atom value like #x00000005 or #b101
|
|
||||||
value += firstChar;
|
|
||||||
while (os.get(c) && c != ')') { value += c; }
|
|
||||||
// Trim trailing whitespace
|
|
||||||
const size_t end = value.find_last_not_of(" \t\n\r");
|
|
||||||
if (end != std::string::npos) value = value.substr(0, end + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
solvedValues[name] = value;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get baseline values (deterministic, always valid)
|
|
||||||
getValueCmd();
|
|
||||||
if (!parseGetValue()) {
|
|
||||||
os << "(reset)\n";
|
os << "(reset)\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try diversity: add random constraint, re-check. If sat, get
|
|
||||||
// updated (more diverse) values. If unsat, keep baseline values.
|
|
||||||
os << "(assert ";
|
|
||||||
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
|
|
||||||
os << ")\n";
|
|
||||||
os << "(check-sat)\n";
|
|
||||||
satResponse.clear();
|
|
||||||
do { std::getline(os, satResponse); } while (satResponse.empty());
|
|
||||||
if (satResponse == "sat") {
|
|
||||||
getValueCmd();
|
|
||||||
parseGetValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
os << "(reset)\n";
|
os << "(reset)\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1148,6 +1078,86 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intermediate phase: extract this layer's values, then try one diversity round
|
||||||
|
bool VlRandomizer::solvePhaseValues(std::iostream& os, VlRNG& rngr,
|
||||||
|
const std::vector<std::string>& layerVars,
|
||||||
|
std::map<std::string, std::string>& solvedValuesr) {
|
||||||
|
const auto emitGetValueCmd = [&]() {
|
||||||
|
os << "(get-value (";
|
||||||
|
for (const auto& varName : layerVars) {
|
||||||
|
const auto it = m_vars.find(varName);
|
||||||
|
if (it->second->dimension() > 0) {
|
||||||
|
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
||||||
|
it->second->setArrayInfo(arrVarsp);
|
||||||
|
// Enumerable arrays: query each element for a QF_ABV-safe pin.
|
||||||
|
if (it->second->hasMatchingElements(m_arr_vars, it->second->name())) {
|
||||||
|
it->second->emitGetValue(os);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os << varName << " ";
|
||||||
|
}
|
||||||
|
os << "))\n";
|
||||||
|
};
|
||||||
|
// Get baseline values (deterministic, always valid)
|
||||||
|
emitGetValueCmd();
|
||||||
|
if (!parsePhaseValues(os, solvedValuesr)) return false;
|
||||||
|
|
||||||
|
// Try diversity: add random constraint, re-check. If sat, get
|
||||||
|
// updated (more diverse) values. If unsat, keep baseline values.
|
||||||
|
os << "(assert ";
|
||||||
|
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
|
||||||
|
os << ")\n";
|
||||||
|
os << "(check-sat)\n";
|
||||||
|
if (checkSat(os)) {
|
||||||
|
emitGetValueCmd();
|
||||||
|
(void)parsePhaseValues(os, solvedValuesr);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VlRandomizer::parsePhaseValues(std::istream& is,
|
||||||
|
std::map<std::string, std::string>& solvedValuesr) {
|
||||||
|
// Parse ((name value) ...): one paren-depth counter drives every match.
|
||||||
|
char c = 0;
|
||||||
|
is >> c; // outer '('
|
||||||
|
if (c != '(') return false;
|
||||||
|
int depth = 1;
|
||||||
|
std::string tokens[2];
|
||||||
|
std::string cur;
|
||||||
|
int fields = 0;
|
||||||
|
const auto flush = [&]() {
|
||||||
|
if (cur.empty()) return;
|
||||||
|
if (fields < 2) tokens[fields] = cur;
|
||||||
|
++fields;
|
||||||
|
cur.clear();
|
||||||
|
};
|
||||||
|
while (depth > 0 && is.get(c)) {
|
||||||
|
if (c == '(') {
|
||||||
|
++depth;
|
||||||
|
if (depth >= 3) cur += c;
|
||||||
|
} else if (c == ')') {
|
||||||
|
--depth;
|
||||||
|
if (depth >= 2) {
|
||||||
|
cur += c;
|
||||||
|
} else if (depth == 1) {
|
||||||
|
flush();
|
||||||
|
if (fields == 2) solvedValuesr[tokens[0]] = tokens[1];
|
||||||
|
fields = 0;
|
||||||
|
}
|
||||||
|
} else if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
||||||
|
if (depth >= 3) {
|
||||||
|
cur += c;
|
||||||
|
} else {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cur += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef VL_DEBUG
|
#ifdef VL_DEBUG
|
||||||
void VlRandomizer::dump() const {
|
void VlRandomizer::dump() const {
|
||||||
for (const auto& var : m_vars) {
|
for (const auto& var : m_vars) {
|
||||||
|
|
|
||||||
|
|
@ -104,13 +104,32 @@ public:
|
||||||
count_cache[base_name] = count;
|
count_cache[base_name] = count;
|
||||||
return 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>
|
template <typename T>
|
||||||
class VlRandomArrayVarTemplate final : public VlRandomVar {
|
class VlRandomArrayVarTemplate final : public VlRandomVar {
|
||||||
|
// Static key widths per level for the empty-array declaration fallback
|
||||||
|
const std::vector<size_t> m_fallbackIdxWidths;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VlRandomArrayVarTemplate(const std::string& name, int width, void* datap, int dimension,
|
VlRandomArrayVarTemplate(const std::string& name, int width, void* datap, int dimension,
|
||||||
std::uint32_t randModeIdx)
|
std::uint32_t randModeIdx, const std::vector<size_t>& idxWidths = {})
|
||||||
: VlRandomVar{name, width, datap, dimension, randModeIdx} {}
|
: VlRandomVar{name, width, datap, dimension, randModeIdx}
|
||||||
|
, m_fallbackIdxWidths{idxWidths} {}
|
||||||
void* datap(int idx) const override {
|
void* datap(int idx) const override {
|
||||||
const std::string indexed_name = name() + std::to_string(idx);
|
const std::string indexed_name = name() + std::to_string(idx);
|
||||||
const auto it = m_arrVarsRefp->find(indexed_name);
|
const auto it = m_arrVarsRefp->find(indexed_name);
|
||||||
|
|
@ -175,7 +194,13 @@ public:
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (dimension() > 0) {
|
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() << ")";
|
s << "(_ BitVec " << width() << ")";
|
||||||
for (int i = 0; i < dimension(); ++i) s << ")";
|
for (int i = 0; i < dimension(); ++i) s << ")";
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -217,8 +242,7 @@ class VlRandomizer VL_NOT_FINAL {
|
||||||
std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back)
|
std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back)
|
||||||
// variables
|
// variables
|
||||||
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
|
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
|
||||||
std::vector<std::string> m_unique_arrays;
|
std::vector<std::string> m_unique_arrays; // Arrays whose elements must be distinct
|
||||||
std::map<std::string, uint32_t> m_unique_array_sizes;
|
|
||||||
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
|
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
|
||||||
const VlQueue<CData>* m_static_randmodep = nullptr; // Static rand_mode state (shared)
|
const VlQueue<CData>* m_static_randmodep = nullptr; // Static rand_mode state (shared)
|
||||||
std::unordered_set<std::string> m_staticVars; // Names of static rand vars
|
std::unordered_set<std::string> m_staticVars; // Names of static rand vars
|
||||||
|
|
@ -233,14 +257,37 @@ class VlRandomizer VL_NOT_FINAL {
|
||||||
|
|
||||||
// PRIVATE METHODS
|
// PRIVATE METHODS
|
||||||
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
|
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
|
||||||
bool parseSolution(std::iostream& os, bool log = false);
|
bool parseSolution(std::iostream& os);
|
||||||
bool checkSat(std::iostream& os);
|
bool checkSat(std::iostream& os);
|
||||||
|
// Assert the maximal compatible soft-constraint set onto the open session.
|
||||||
|
void relaxSoftConstraints(std::iostream& os);
|
||||||
// Indices of the "a<N>" literals named by (get-unsat-assumptions).
|
// Indices of the "a<N>" literals named by (get-unsat-assumptions).
|
||||||
std::vector<int> readUnsatAssumptions(std::iostream& os);
|
std::vector<int> readUnsatAssumptions(std::iostream& os);
|
||||||
|
void reportUnsatSetup(std::iostream& os, const std::vector<std::string>& uniqueExprs);
|
||||||
|
void reportUnsatCore(std::iostream& os);
|
||||||
void emitRandcExclusions(std::ostream& os) const; // Emit randc exclusion constraints
|
void emitRandcExclusions(std::ostream& os) const; // Emit randc exclusion constraints
|
||||||
void recordRandcValues(); // Record solved randc values for future exclusion
|
void recordRandcValues(); // Record solved randc values for future exclusion
|
||||||
size_t hashConstraints() const;
|
size_t hashConstraints(const std::vector<std::string>& extras) const;
|
||||||
bool nextPhased(VlRNG& rngr); // Phased solving for solve...before
|
bool nextRandomize(VlRNG& 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, const std::vector<std::string>& uniqueExprs);
|
||||||
|
void solveDiversity(VlRNG& rngr, std::iostream& os);
|
||||||
|
void solveDiversityPins(VlRNG& rngr, std::iostream& os);
|
||||||
|
void solveDiversityXor(VlRNG& rngr, std::iostream& os);
|
||||||
|
// 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, const std::vector<std::string>& uniqueExprs);
|
||||||
|
bool solvePhases(VlRNG& rngr, const std::vector<std::vector<std::string>>& layers,
|
||||||
|
const std::vector<std::string>& uniqueExprs);
|
||||||
|
bool solvePhaseValues(std::iostream& os, VlRNG& rngr,
|
||||||
|
const std::vector<std::string>& layerVars,
|
||||||
|
std::map<std::string, std::string>& solvedValuesr);
|
||||||
|
bool parsePhaseValues(std::istream& is, std::map<std::string, std::string>& solvedValuesr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
|
|
@ -377,12 +424,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register queue of non-struct types
|
// 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
|
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()) {
|
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||||
if (m_vars.find(name) == m_vars.end()) {
|
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);
|
name, width, &var, dimension, randmodeIdx);
|
||||||
}
|
}
|
||||||
if (dimension > 0) {
|
if (dimension > 0) {
|
||||||
|
|
@ -394,9 +441,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register queue of structs
|
// Register queue of structs
|
||||||
template <typename T>
|
template <typename T, size_t N_MaxSize>
|
||||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
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()) {
|
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||||
}
|
}
|
||||||
|
|
@ -433,9 +480,11 @@ public:
|
||||||
write_var(VlAssocArray<T_Key, T_Value>& var, int width, const char* name, int dimension,
|
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()) {
|
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||||
if (m_vars.find(name) == m_vars.end()) {
|
if (m_vars.find(name) == m_vars.end()) {
|
||||||
|
std::vector<size_t> keyWidths;
|
||||||
|
VlRandomAssocKeyWidths<VlAssocArray<T_Key, T_Value>>::push(keyWidths);
|
||||||
m_vars[name]
|
m_vars[name]
|
||||||
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
|
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
|
||||||
name, width, &var, dimension, randmodeIdx);
|
name, width, &var, dimension, randmodeIdx, keyWidths);
|
||||||
}
|
}
|
||||||
if (dimension > 0) {
|
if (dimension > 0) {
|
||||||
m_index = 0;
|
m_index = 0;
|
||||||
|
|
@ -465,11 +514,10 @@ public:
|
||||||
++m_index;
|
++m_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the "Sender" API for the generated code
|
// This is the "Sender" API for the generated code.
|
||||||
void rand_unique(const std::string& name, uint32_t size) {
|
// The elements to make distinct are taken from the array element table at
|
||||||
m_unique_arrays.push_back(name);
|
// solve time, so a container resized by the solver is handled correctly.
|
||||||
m_unique_array_sizes[name] = size;
|
void rand_unique(const std::string& name) { m_unique_arrays.push_back(name); }
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively record all elements in an unpacked array
|
// Recursively record all elements in an unpacked array
|
||||||
template <typename T, std::size_t N_Depth>
|
template <typename T, std::size_t N_Depth>
|
||||||
|
|
@ -488,8 +536,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively record all elements in a queue
|
// Recursively record all elements in a queue
|
||||||
template <typename T>
|
template <typename T, size_t N_MaxSize>
|
||||||
void record_arr_table(VlQueue<T>& var, const std::string& name, int dimension,
|
void record_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
|
||||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||||
if ((dimension > 0) && (var.size() != 0)) {
|
if ((dimension > 0) && (var.size() != 0)) {
|
||||||
idxWidths.push_back(32);
|
idxWidths.push_back(32);
|
||||||
|
|
@ -522,7 +570,8 @@ public:
|
||||||
idxWidths.push_back(idx_width);
|
idxWidths.push_back(idx_width);
|
||||||
indices.insert(indices.end(), integral_index.begin(), integral_index.end());
|
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
|
// Cleanup indices and widths
|
||||||
idxWidths.pop_back();
|
idxWidths.pop_back();
|
||||||
|
|
@ -562,8 +611,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively process VlQueue of structs
|
// Recursively process VlQueue of structs
|
||||||
template <typename T>
|
template <typename T, size_t N_MaxSize>
|
||||||
void record_struct_arr(VlQueue<T>& var, const std::string& name, int dimension,
|
void record_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
|
||||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||||
if ((dimension > 0) && (var.size() != 0)) {
|
if ((dimension > 0) && (var.size() != 0)) {
|
||||||
idxWidths.push_back(32);
|
idxWidths.push_back(32);
|
||||||
|
|
@ -596,7 +645,7 @@ public:
|
||||||
|
|
||||||
std::string result = oss.str();
|
std::string result = oss.str();
|
||||||
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
|
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);
|
idxWidths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -724,11 +773,20 @@ public:
|
||||||
bool basicStdRandomization(VlAssocArray<T_Key, T_Value>& value, size_t width) {
|
bool basicStdRandomization(VlAssocArray<T_Key, T_Value>& value, size_t width) {
|
||||||
T_Key key;
|
T_Key key;
|
||||||
for (int exists = value.first(key); exists; exists = value.next(key)) {
|
for (int exists = value.first(key); exists; exists = value.next(key)) {
|
||||||
basicStdRandomization(value.at(key), width);
|
basicStdRandomization(value.atWrite(key), width);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool next() { return VlRandomizer::next(m_rng); }
|
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
|
#endif // Guard
|
||||||
|
|
|
||||||
|
|
@ -327,7 +327,7 @@ VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VlAssocArray<T_Key, T
|
||||||
T_Value value;
|
T_Value value;
|
||||||
os >> index;
|
os >> index;
|
||||||
os >> value;
|
os >> value;
|
||||||
rhs.at(index) = value;
|
rhs.atWrite(index) = value;
|
||||||
}
|
}
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
// 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.
|
// 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
|
#ifdef VL_HAVE_SSE2
|
||||||
// Similar to cvtSDataToStr but only the bottom 8 byte lanes are used
|
// Similar to cvtSDataToStr but only the bottom 8 byte lanes are used
|
||||||
const __m128i a = _mm_cvtsi32_si128(value);
|
const __m128i a = _mm_cvtsi32_si128(value);
|
||||||
|
|
@ -471,7 +471,7 @@ static inline void cvtCDataToStr(char* dstp, CData value) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cvtSDataToStr(char* dstp, SData value) {
|
inline void cvtSDataToStr(char* dstp, SData value) {
|
||||||
#ifdef VL_HAVE_SSE2
|
#ifdef VL_HAVE_SSE2
|
||||||
// We want each bit in the 16-bit input value to end up in a byte lane
|
// 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
|
// 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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cvtIDataToStr(char* dstp, IData value) {
|
inline void cvtIDataToStr(char* dstp, IData value) {
|
||||||
#ifdef VL_HAVE_AVX2
|
#ifdef VL_HAVE_AVX2
|
||||||
// Similar to cvtSDataToStr but the bottom 16-bits are processed in the
|
// Similar to cvtSDataToStr but the bottom 16-bits are processed in the
|
||||||
// top half of the YMM registers
|
// top half of the YMM registers
|
||||||
|
|
@ -526,7 +526,7 @@ static inline void cvtIDataToStr(char* dstp, IData value) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cvtQDataToStr(char* dstp, QData value) {
|
inline void cvtQDataToStr(char* dstp, QData value) {
|
||||||
cvtIDataToStr(dstp, value >> 32);
|
cvtIDataToStr(dstp, value >> 32);
|
||||||
cvtIDataToStr(dstp + 32, value);
|
cvtIDataToStr(dstp + 32, value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ public:
|
||||||
};
|
};
|
||||||
static_assert(sizeof(WDataInP) == sizeof(EData*), "WDataInP should be a single pointer");
|
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>
|
template <std::size_t N_Words>
|
||||||
bool VlWide<N_Words>::operator<(const VlWide<N_Words>& rhs) const VL_PURE {
|
bool VlWide<N_Words>::operator<(const VlWide<N_Words>& rhs) const VL_PURE {
|
||||||
|
|
@ -337,6 +337,7 @@ public:
|
||||||
|
|
||||||
~VlProcess() {
|
~VlProcess() {
|
||||||
if (m_parentp) m_parentp->detach(this);
|
if (m_parentp) m_parentp->detach(this);
|
||||||
|
if (t_currentp == this) t_currentp = m_parentp.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void attach(VlProcess* childp) { m_children.insert(childp); }
|
void attach(VlProcess* childp) { m_children.insert(childp); }
|
||||||
|
|
@ -931,7 +932,7 @@ public:
|
||||||
VlQueue min(T_Func with_func) const {
|
VlQueue min(T_Func with_func) const {
|
||||||
if (m_deque.empty()) return VlQueue{};
|
if (m_deque.empty()) return VlQueue{};
|
||||||
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend(),
|
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 with_func(0, a) < with_func(0, b);
|
||||||
});
|
});
|
||||||
return VlQueue::consV(*it);
|
return VlQueue::consV(*it);
|
||||||
|
|
@ -945,7 +946,7 @@ public:
|
||||||
VlQueue max(T_Func with_func) const {
|
VlQueue max(T_Func with_func) const {
|
||||||
if (m_deque.empty()) return VlQueue{};
|
if (m_deque.empty()) return VlQueue{};
|
||||||
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend(),
|
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 with_func(0, a) < with_func(0, b);
|
||||||
});
|
});
|
||||||
return VlQueue::consV(*it);
|
return VlQueue::consV(*it);
|
||||||
|
|
@ -1119,9 +1120,7 @@ public:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// Setting. Verilog: assoc[index] = v
|
// Setting. Verilog: assoc[index] = v
|
||||||
// Can't just overload operator[] or provide a "at" reference to set,
|
T_Value& atWrite(const T_Key& index) {
|
||||||
// because we need to be able to insert only when the value is set
|
|
||||||
T_Value& at(const T_Key& index) {
|
|
||||||
const auto it = m_map.find(index);
|
const auto it = m_map.find(index);
|
||||||
if (it == m_map.end()) {
|
if (it == m_map.end()) {
|
||||||
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
|
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
|
||||||
|
|
@ -1137,7 +1136,7 @@ public:
|
||||||
}
|
}
|
||||||
// Setting as a chained operation
|
// Setting as a chained operation
|
||||||
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
||||||
at(index) = value;
|
atWrite(index) = value;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
VlAssocArray& setDefault(const T_Value& value) {
|
VlAssocArray& setDefault(const T_Value& value) {
|
||||||
|
|
@ -1394,7 +1393,7 @@ void VL_READMEM_N(bool hex, int bits, const std::string& filename,
|
||||||
QData addr;
|
QData addr;
|
||||||
std::string data;
|
std::string data;
|
||||||
if (rmem.get(addr /*ref*/, data /*ref*/)) {
|
if (rmem.get(addr /*ref*/, data /*ref*/)) {
|
||||||
rmem.setData(&(obj.at(addr)), data);
|
rmem.setData(&(obj.atWrite(addr)), data);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -2069,8 +2068,24 @@ struct VlNull final {
|
||||||
operator T*() const {
|
operator T*() const {
|
||||||
return nullptr;
|
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
|
// Verilog class reference container
|
||||||
|
|
@ -2223,7 +2238,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T_Lhs, typename T_Out>
|
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) {
|
if (!in) {
|
||||||
outr = VlNull{};
|
outr = VlNull{};
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -2237,7 +2252,7 @@ static inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T_Lhs>
|
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{};
|
outr = VlNull{};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -539,7 +539,7 @@ public:
|
||||||
for (auto idx : index()) m_fullname += "[" + std::to_string(idx) + "]";
|
for (auto idx : index()) m_fullname += "[" + std::to_string(idx) + "]";
|
||||||
return m_fullname.c_str();
|
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* varDatap() const override { return m_varDatap; }
|
||||||
void createPrevDatap() {
|
void createPrevDatap() {
|
||||||
if (VL_UNLIKELY(!m_prevDatap)) {
|
if (VL_UNLIKELY(!m_prevDatap)) {
|
||||||
|
|
@ -1027,6 +1027,26 @@ struct VerilatedVpiTimedCbsCmp final {
|
||||||
class VerilatedVpiError;
|
class VerilatedVpiError;
|
||||||
void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset);
|
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 {
|
class VerilatedVpiImp final {
|
||||||
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maximum callback reason
|
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maximum callback reason
|
||||||
using VpioCbList = std::list<VerilatedVpiCbHolder>;
|
using VpioCbList = std::list<VerilatedVpiCbHolder>;
|
||||||
|
|
@ -1169,6 +1189,72 @@ public:
|
||||||
s().m_cbCallList.clear();
|
s().m_cbCallList.clear();
|
||||||
return called;
|
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 {
|
static bool callValueCbs() VL_MT_UNSAFE_ONE {
|
||||||
assertOneCheck();
|
assertOneCheck();
|
||||||
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
|
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
|
||||||
|
|
@ -1187,15 +1273,10 @@ public:
|
||||||
VerilatedVpiCbHolder& ho = *it++;
|
VerilatedVpiCbHolder& ho = *it++;
|
||||||
VerilatedVpioVar* const varop
|
VerilatedVpioVar* const varop
|
||||||
= reinterpret_cast<VerilatedVpioVar*>(ho.cb_datap()->obj);
|
= reinterpret_cast<VerilatedVpioVar*>(ho.cb_datap()->obj);
|
||||||
void* const newDatap = varop->varDatap();
|
if (valueDiffersFromPrev(varop)) {
|
||||||
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) {
|
|
||||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
|
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
|
||||||
ho.id(), varop->fullname(),
|
ho.id(), varop->fullname(),
|
||||||
*(static_cast<CData*>(newDatap))););
|
*(static_cast<CData*>(varop->varDatap()))););
|
||||||
update.insert(varop);
|
update.insert(varop);
|
||||||
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
||||||
(ho.cb_rtnp())(ho.cb_datap());
|
(ho.cb_rtnp())(ho.cb_datap());
|
||||||
|
|
@ -1203,9 +1284,7 @@ public:
|
||||||
}
|
}
|
||||||
if (was_last) break;
|
if (was_last) break;
|
||||||
}
|
}
|
||||||
for (const VerilatedVpioVar* const ip : update) {
|
for (const VerilatedVpioVar* const varop : update) updatePrev(varop);
|
||||||
std::memcpy(ip->prevDatap(), ip->varDatap(), ip->entSize());
|
|
||||||
}
|
|
||||||
return called;
|
return called;
|
||||||
}
|
}
|
||||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
||||||
|
|
@ -2410,7 +2489,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||||
return vop->castVpiHandle();
|
return vop->castVpiHandle();
|
||||||
}
|
}
|
||||||
default:
|
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));
|
VerilatedVpiError::strFromVpiCallbackReason(reason));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -2856,7 +2935,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||||
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
|
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
|
||||||
}
|
}
|
||||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
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));
|
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -2870,7 +2949,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||||
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
|
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
|
||||||
}
|
}
|
||||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
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));
|
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -3031,8 +3110,9 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
|
VL_VPI_ERROR_(__FILE__, __LINE__,
|
||||||
__func__, VerilatedVpiError::strFromVpiProp(property));
|
"%s: Unsupported property '%s', nothing will be returned", __func__,
|
||||||
|
VerilatedVpiError::strFromVpiProp(property));
|
||||||
return vpiUndefined;
|
return vpiUndefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3195,18 +3275,6 @@ static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
|
||||||
va_end(args);
|
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>
|
template <typename T>
|
||||||
VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount,
|
VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount,
|
||||||
size_t addOffset) {
|
size_t addOffset) {
|
||||||
|
|
@ -3262,21 +3330,25 @@ VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename 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 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)
|
return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset)
|
||||||
| ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi)
|
| ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi)
|
||||||
<< (wordBits - info.m_bitOffset));
|
<< (wordBits - info.m_bitOffset));
|
||||||
|
}
|
||||||
return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset;
|
return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) {
|
T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
|
||||||
const size_t wordBits = sizeof(T) * 8;
|
|
||||||
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
||||||
|
return vl_vpi_get_word_gen(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void vl_vpi_put_word_gen(VarAccessInfo<T> info, T word) {
|
||||||
|
const size_t wordBits = sizeof(T) * 8;
|
||||||
if (info.m_maskHi) {
|
if (info.m_maskHi) {
|
||||||
info.m_datap[info.m_wordOffset + 1]
|
info.m_datap[info.m_wordOffset + 1]
|
||||||
= (info.m_datap[info.m_wordOffset + 1] & ~info.m_maskHi)
|
= (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);
|
| ((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
|
// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds
|
||||||
// addOffset: additional read bitoffset
|
// addOffset: additional read bitoffset
|
||||||
QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
|
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());
|
VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
|
||||||
return;
|
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*/,
|
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());
|
__func__, vop->fullname());
|
||||||
return nullptr;
|
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;
|
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);
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||||
if (VL_UNLIKELY(!vop)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vop->type() != vpiRegArray) {
|
if (vop->type() != vpiRegArray) {
|
||||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type '%s' for '%s'", __func__,
|
||||||
VerilatedVpiError::strFromVpiObjType(vop->type()));
|
VerilatedVpiError::strFromVpiObjType(vop->type()), vop->name());
|
||||||
return;
|
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);
|
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||||
if (VL_UNLIKELY(!vop)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vop->type() != vpiRegArray) {
|
if (vop->type() != vpiRegArray) {
|
||||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
|
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle type '%s' for '%s'", __func__,
|
||||||
VerilatedVpiError::strFromVpiObjType(vop->type()));
|
VerilatedVpiError::strFromVpiObjType(vop->type()), vop->name());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -332,10 +332,19 @@
|
||||||
|
|
||||||
#ifdef VL_GCOV
|
#ifdef VL_GCOV
|
||||||
extern "C" void __gcov_dump();
|
extern "C" void __gcov_dump();
|
||||||
|
extern "C" void __gcov_reset();
|
||||||
// Dump internal code coverage data before e.g. std::abort()
|
// Dump internal code coverage data before e.g. std::abort()
|
||||||
# define VL_GCOV_DUMP() __gcov_dump()
|
# 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
|
#else
|
||||||
# define VL_GCOV_DUMP()
|
# define VL_GCOV_DUMP()
|
||||||
|
# define VL_GCOV_DUMP_RESET()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
@ -368,6 +377,7 @@ extern "C" void __gcov_dump();
|
||||||
#define __STDC_FORMAT_MACROS
|
#define __STDC_FORMAT_MACROS
|
||||||
|
|
||||||
// Now that C++ requires these standard types the vl types are deprecated
|
// Now that C++ requires these standard types the vl types are deprecated
|
||||||
|
#include <cstddef> // offsetof (used by generated VlVarTableEntry tables)
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
@ -460,6 +470,7 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||||
// Integer size macros
|
// Integer size macros
|
||||||
|
|
||||||
#define VL_BYTESIZE 8 ///< Bits in a CData / byte
|
#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_SHORTSIZE 16 ///< Bits in a SData / short
|
||||||
#define VL_IDATASIZE 32 ///< Bits in an IData / word
|
#define VL_IDATASIZE 32 ///< Bits in an IData / word
|
||||||
#define VL_QUADSIZE 64 ///< Bits in a QData / quadword
|
#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
|
#endif
|
||||||
|
|
||||||
/// Return number of bytes argument-number of bits needs (1 bit=1 byte)
|
/// 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)
|
/// 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
|
// Number of Words/EDatas a quad requires
|
||||||
#define VL_WQ_WORDS_E VL_WORDS_I(VL_QUADSIZE)
|
#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
|
// #defines, to avoid requiring math.h on all compile runs
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#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);
|
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);
|
return (n < 0) ? std::ceil(n-0.5) : std::floor(n + 0.5);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
|
||||||
|
|
@ -169,17 +169,19 @@ void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// Highly unportable. Sorry
|
// 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"};
|
std::ifstream is{"/proc/self/status"};
|
||||||
if (!is) return;
|
if (!is) return;
|
||||||
std::string line;
|
std::string line;
|
||||||
uint64_t vmPeak = 0;
|
uint64_t vmHwm = 0;
|
||||||
uint64_t vmRss = 0;
|
uint64_t vmRss = 0;
|
||||||
uint64_t vmSwap = 0;
|
uint64_t vmSwap = 0;
|
||||||
std::string field;
|
std::string field;
|
||||||
while (std::getline(is, line)) {
|
while (std::getline(is, line)) {
|
||||||
if (line.rfind("VmPeak:", 0) == 0) {
|
if (line.rfind("VmHWM:", 0) == 0) {
|
||||||
std::stringstream ss{line};
|
std::stringstream ss{line};
|
||||||
ss >> field >> vmPeak;
|
ss >> field >> vmHwm;
|
||||||
} else if (line.rfind("VmRSS:", 0) == 0) {
|
} else if (line.rfind("VmRSS:", 0) == 0) {
|
||||||
std::stringstream ss{line};
|
std::stringstream ss{line};
|
||||||
ss >> field >> vmRss;
|
ss >> field >> vmRss;
|
||||||
|
|
@ -188,7 +190,7 @@ void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
|
||||||
ss >> field >> vmSwap;
|
ss >> field >> vmSwap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
peakr = vmPeak * 1024;
|
peakr = vmHwm * 1024;
|
||||||
currentr = (vmRss + vmSwap) * 1024;
|
currentr = (vmRss + vmSwap) * 1024;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
# These flags tested against verible-v0.0-4080-ga0a8d8eb
|
||||||
|
|
||||||
verible-verilog-format \
|
verible-verilog-format \
|
||||||
--inplace \
|
--inplace \
|
||||||
--wrap_end_else_clauses \
|
--wrap_end_else_clauses \
|
||||||
|
|
|
||||||
|
|
@ -33,4 +33,4 @@ sphinxcontrib-spelling==8.0.2
|
||||||
yamlfix==1.19.1
|
yamlfix==1.19.1
|
||||||
yapf==0.43.0
|
yapf==0.43.0
|
||||||
|
|
||||||
git+https://github.com/antmicro/astsee.git
|
git+https://github.com/antmicro/astsee.git@222480a8ec13b312809ea4acc08c81b4c0f4da2f
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,7 @@ set(HEADERS
|
||||||
V3OrderGraph.h
|
V3OrderGraph.h
|
||||||
V3OrderInternal.h
|
V3OrderInternal.h
|
||||||
V3OrderMoveGraph.h
|
V3OrderMoveGraph.h
|
||||||
|
V3OrderMTaskGraph.h
|
||||||
V3Os.h
|
V3Os.h
|
||||||
V3PairingHeap.h
|
V3PairingHeap.h
|
||||||
V3Param.h
|
V3Param.h
|
||||||
|
|
@ -156,6 +157,7 @@ set(HEADERS
|
||||||
V3ParseImp.h
|
V3ParseImp.h
|
||||||
V3PchAstMT.h
|
V3PchAstMT.h
|
||||||
V3PchAstNoMT.h
|
V3PchAstNoMT.h
|
||||||
|
V3PoolAllocator.h
|
||||||
V3PreExpr.h
|
V3PreExpr.h
|
||||||
V3PreLex.h
|
V3PreLex.h
|
||||||
V3PreProc.h
|
V3PreProc.h
|
||||||
|
|
@ -170,7 +172,6 @@ set(HEADERS
|
||||||
V3Sampled.h
|
V3Sampled.h
|
||||||
V3Sched.h
|
V3Sched.h
|
||||||
V3Scope.h
|
V3Scope.h
|
||||||
V3Scoreboard.h
|
|
||||||
V3SenExprBuilder.h
|
V3SenExprBuilder.h
|
||||||
V3SenTree.h
|
V3SenTree.h
|
||||||
V3Simulate.h
|
V3Simulate.h
|
||||||
|
|
@ -319,6 +320,9 @@ set(COMMON_SOURCES
|
||||||
V3Order.cpp
|
V3Order.cpp
|
||||||
V3OrderGraphBuilder.cpp
|
V3OrderGraphBuilder.cpp
|
||||||
V3OrderMoveGraph.cpp
|
V3OrderMoveGraph.cpp
|
||||||
|
V3OrderMTaskContraction.cpp
|
||||||
|
V3OrderMTaskFixHazards.cpp
|
||||||
|
V3OrderMTaskGraph.cpp
|
||||||
V3OrderParallel.cpp
|
V3OrderParallel.cpp
|
||||||
V3OrderProcessDomains.cpp
|
V3OrderProcessDomains.cpp
|
||||||
V3OrderSerial.cpp
|
V3OrderSerial.cpp
|
||||||
|
|
@ -346,7 +350,6 @@ set(COMMON_SOURCES
|
||||||
V3SchedUtil.cpp
|
V3SchedUtil.cpp
|
||||||
V3SchedVirtIface.cpp
|
V3SchedVirtIface.cpp
|
||||||
V3Scope.cpp
|
V3Scope.cpp
|
||||||
V3Scoreboard.cpp
|
|
||||||
V3Slice.cpp
|
V3Slice.cpp
|
||||||
V3Split.cpp
|
V3Split.cpp
|
||||||
V3SplitVar.cpp
|
V3SplitVar.cpp
|
||||||
|
|
|
||||||
|
|
@ -309,6 +309,9 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||||
V3Order.o \
|
V3Order.o \
|
||||||
V3OrderGraphBuilder.o \
|
V3OrderGraphBuilder.o \
|
||||||
V3OrderMoveGraph.o \
|
V3OrderMoveGraph.o \
|
||||||
|
V3OrderMTaskContraction.o \
|
||||||
|
V3OrderMTaskFixHazards.o \
|
||||||
|
V3OrderMTaskGraph.o \
|
||||||
V3OrderParallel.o \
|
V3OrderParallel.o \
|
||||||
V3OrderProcessDomains.o \
|
V3OrderProcessDomains.o \
|
||||||
V3OrderSerial.o \
|
V3OrderSerial.o \
|
||||||
|
|
@ -330,7 +333,6 @@ RAW_OBJS_PCH_ASTNOMT = \
|
||||||
V3SchedUtil.o \
|
V3SchedUtil.o \
|
||||||
V3SchedVirtIface.o \
|
V3SchedVirtIface.o \
|
||||||
V3Scope.o \
|
V3Scope.o \
|
||||||
V3Scoreboard.o \
|
|
||||||
V3Slice.o \
|
V3Slice.o \
|
||||||
V3Split.o \
|
V3Split.o \
|
||||||
V3SplitVar.o \
|
V3SplitVar.o \
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,13 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
// Active class functions
|
// Active class functions
|
||||||
|
|
||||||
class ActiveTopVisitor final : public VNVisitor {
|
class ActiveTopVisitor final : public VNVisitor {
|
||||||
|
// NODE STATE
|
||||||
|
// AstVarScope::user1() // bool. Processed
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
|
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
||||||
static bool isInitial(AstNode* nodep) {
|
static bool isInitial(AstNode* nodep) {
|
||||||
const VNUser1InUse user1InUse;
|
const VNUser1InUse user1InUse;
|
||||||
// Return true if no variables that read.
|
// Return true if no variables that read.
|
||||||
|
|
|
||||||
116
src/V3Assert.cpp
116
src/V3Assert.cpp
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include "V3AstUserAllocator.h"
|
#include "V3AstUserAllocator.h"
|
||||||
#include "V3Stats.h"
|
#include "V3Stats.h"
|
||||||
|
#include "V3UniqueNames.h"
|
||||||
|
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
|
|
@ -89,6 +90,58 @@ public:
|
||||||
explicit DefaultDisablePropagateVisitor(AstNetlist* nodep) { iterate(nodep); }
|
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
|
} // namespace
|
||||||
|
|
||||||
void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) {
|
void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) {
|
||||||
|
|
@ -96,6 +149,11 @@ void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) {
|
||||||
{ DefaultDisablePropagateVisitor{nodep}; }
|
{ DefaultDisablePropagateVisitor{nodep}; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void V3AssertCommon::lowerSequenceEvents(AstNetlist* nodep) {
|
||||||
|
{ SeqEventLowerVisitor{nodep}; }
|
||||||
|
V3Global::dumpCheckGlobalTree("assertseqevent", 0, dumpTreeEitherLevel() >= 3);
|
||||||
|
}
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// AssertDeFutureVisitor
|
// AssertDeFutureVisitor
|
||||||
// If any AstFuture, then move all non-future varrefs to be one cycle behind,
|
// If any AstFuture, then move all non-future varrefs to be one cycle behind,
|
||||||
|
|
@ -228,6 +286,9 @@ class AssertVisitor final : public VNVisitor {
|
||||||
AstNode* m_failsp = nullptr; // Current fail statement
|
AstNode* m_failsp = nullptr; // Current fail statement
|
||||||
AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion
|
AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion
|
||||||
AstFinal* m_finalp = nullptr; // Current final block
|
AstFinal* m_finalp = nullptr; // Current final block
|
||||||
|
VDouble0 m_statLiftedCaseExprs; // Count of purified case expressions
|
||||||
|
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||||
|
V3UniqueNames m_caseTempNames{"__VCase"};
|
||||||
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
|
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
|
||||||
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
|
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
|
||||||
m_modExpr2Sen2DelayedAlwaysp;
|
m_modExpr2Sen2DelayedAlwaysp;
|
||||||
|
|
@ -326,8 +387,7 @@ class AssertVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AstSampled* newSampledExpr(AstNodeExpr* nodep) {
|
AstSampled* newSampledExpr(AstNodeExpr* nodep) {
|
||||||
AstSampled* const sampledp = new AstSampled{nodep->fileline(), nodep, nodep->dtypep()};
|
return new AstSampled{nodep->fileline(), nodep, nodep->dtypep(), true};
|
||||||
return sampledp;
|
|
||||||
}
|
}
|
||||||
AstVarRef* newMonitorNumVarRefp(const AstNode* nodep, VAccess access) {
|
AstVarRef* newMonitorNumVarRefp(const AstNode* nodep, VAccess access) {
|
||||||
if (!m_monitorNumVarp) {
|
if (!m_monitorNumVarp) {
|
||||||
|
|
@ -542,14 +602,19 @@ class AssertVisitor final : public VNVisitor {
|
||||||
|
|
||||||
bool selfDestruct = false;
|
bool selfDestruct = false;
|
||||||
bool passspGated = 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;
|
++m_statCover;
|
||||||
if (!v3Global.opt.coverageUser()) {
|
if (seqEvent) {
|
||||||
|
// Keep the event-fire action, with no coverage bucket
|
||||||
|
} else if (!v3Global.opt.coverageUser()) {
|
||||||
selfDestruct = true;
|
selfDestruct = true;
|
||||||
} else {
|
} else {
|
||||||
// V3Coverage assigned us a bucket to increment.
|
// V3Coverage assigned us a bucket to increment.
|
||||||
AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc);
|
AstCoverInc* const covincp = VN_AS(coverp->coverincsp(), CoverInc);
|
||||||
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
|
UASSERT_OBJ(covincp, coverp, "Missing AstCoverInc under assertion");
|
||||||
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
||||||
if (message != "") covincp->declp()->comment(message);
|
if (message != "") covincp->declp()->comment(message);
|
||||||
if (passsp) {
|
if (passsp) {
|
||||||
|
|
@ -593,7 +658,8 @@ class AssertVisitor final : public VNVisitor {
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
bool passspAlreadyGated = false;
|
bool passspAlreadyGated = false;
|
||||||
if (passsp && VN_IS(passsp, If)) passspAlreadyGated = VN_AS(passsp, If)->user1();
|
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(),
|
passsp = newIfAssertPassOn(passsp, nodep->directive(), nodep->userType(),
|
||||||
/*vacuous=*/false);
|
/*vacuous=*/false);
|
||||||
}
|
}
|
||||||
|
|
@ -603,7 +669,7 @@ class AssertVisitor final : public VNVisitor {
|
||||||
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
|
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
|
||||||
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
|
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
|
||||||
// Add assertOn check last, for better combining
|
// 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 (sentreep) bodysp = new AstAlways{flp, VAlwaysKwd::ALWAYS, sentreep, bodysp};
|
||||||
|
|
||||||
if (passsp && !passsp->backp()) VL_DO_DANGLING(pushDeletep(passsp), passsp);
|
if (passsp && !passsp->backp()) VL_DO_DANGLING(pushDeletep(passsp), passsp);
|
||||||
|
|
@ -755,6 +821,28 @@ class AssertVisitor final : public VNVisitor {
|
||||||
|
|
||||||
//========== Case assertions
|
//========== Case assertions
|
||||||
void visit(AstCase* nodep) override {
|
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);
|
iterateChildren(nodep);
|
||||||
if (!nodep->user1SetOnce()) {
|
if (!nodep->user1SetOnce()) {
|
||||||
bool has_default = false;
|
bool has_default = false;
|
||||||
|
|
@ -983,11 +1071,7 @@ class AssertVisitor final : public VNVisitor {
|
||||||
= new AstSenItem{fl, VEdgeType::ET_CHANGED,
|
= new AstSenItem{fl, VEdgeType::ET_CHANGED,
|
||||||
// Clone so get VarRef or VarXRef as needed
|
// Clone so get VarRef or VarXRef as needed
|
||||||
varrefp->cloneTree(false)};
|
varrefp->cloneTree(false)};
|
||||||
if (!monSenItemsp) {
|
monSenItemsp = AstNode::addNextNull(monSenItemsp, senItemp);
|
||||||
monSenItemsp = senItemp;
|
|
||||||
} else {
|
|
||||||
monSenItemsp->addNext(senItemp);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1127,12 +1211,11 @@ class AssertVisitor final : public VNVisitor {
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
VL_RESTORER(m_modPastNum);
|
VL_RESTORER(m_modPastNum);
|
||||||
VL_RESTORER(m_modStrobeNum);
|
VL_RESTORER(m_modStrobeNum);
|
||||||
VL_RESTORER(m_modExpr2Sen2DelayedAlwaysp);
|
|
||||||
VL_RESTORER(m_finalp);
|
VL_RESTORER(m_finalp);
|
||||||
|
VL_RESTORER_CLEAR(m_modExpr2Sen2DelayedAlwaysp);
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
m_modPastNum = 0;
|
m_modPastNum = 0;
|
||||||
m_modStrobeNum = 0;
|
m_modStrobeNum = 0;
|
||||||
m_modExpr2Sen2DelayedAlwaysp.clear();
|
|
||||||
m_finalp = nullptr;
|
m_finalp = nullptr;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|
@ -1180,6 +1263,9 @@ public:
|
||||||
V3Stats::addStat("Assertions, $past variables", m_statPastVars);
|
V3Stats::addStat("Assertions, $past variables", m_statPastVars);
|
||||||
V3Stats::addStat("Assertions, assertOn checks combined", m_statAssertOnCombined);
|
V3Stats::addStat("Assertions, assertOn checks combined", m_statAssertOnCombined);
|
||||||
V3Stats::addStat("Assertions, assertOn checks hoisted", m_statAssertOnHoisted);
|
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 {
|
class V3AssertCommon final {
|
||||||
public:
|
public:
|
||||||
static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED;
|
static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED;
|
||||||
|
static void lowerSequenceEvents(AstNetlist* nodep) VL_MT_DISABLED;
|
||||||
};
|
};
|
||||||
|
|
||||||
class V3Assert final {
|
class V3Assert final {
|
||||||
|
|
|
||||||
1057
src/V3AssertNfa.cpp
1057
src/V3AssertNfa.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -79,6 +79,14 @@ private:
|
||||||
|
|
||||||
// METHODS
|
// 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,
|
AstSenTree* newSenTree(AstNode* nodep, AstSenTree* useTreep = nullptr,
|
||||||
AstNodeCoverOrAssert* cassertp = nullptr) {
|
AstNodeCoverOrAssert* cassertp = nullptr) {
|
||||||
// Create sentree based on clocked or default clock
|
// 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
|
// #1step means the value that is sampled is always the signal's last value
|
||||||
// before the clock edge (IEEE 1800-2023 14.4)
|
// before the clock edge (IEEE 1800-2023 14.4)
|
||||||
AstSampled* const sampledp
|
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};
|
AstAssign* const assignp = new AstAssign{flp, refp, sampledp};
|
||||||
m_clockingp->addNextHere(new AstAlways{
|
m_clockingp->addNextHere(new AstAlways{
|
||||||
flp, VAlwaysKwd::ALWAYS,
|
flp, VAlwaysKwd::ALWAYS,
|
||||||
|
|
@ -683,6 +691,7 @@ private:
|
||||||
void visit(AstFalling* nodep) override {
|
void visit(AstFalling* nodep) override {
|
||||||
if (nodep->user1SetOnce()) return;
|
if (nodep->user1SetOnce()) return;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||||
FileLine* const fl = nodep->fileline();
|
FileLine* const fl = nodep->fileline();
|
||||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||||
|
|
@ -696,6 +705,7 @@ private:
|
||||||
void visit(AstFell* nodep) override {
|
void visit(AstFell* nodep) override {
|
||||||
if (nodep->user1SetOnce()) return;
|
if (nodep->user1SetOnce()) return;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||||
FileLine* const fl = nodep->fileline();
|
FileLine* const fl = nodep->fileline();
|
||||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||||
|
|
@ -712,11 +722,13 @@ private:
|
||||||
void visit(AstFuture* nodep) override {
|
void visit(AstFuture* nodep) override {
|
||||||
if (nodep->user1SetOnce()) return;
|
if (nodep->user1SetOnce()) return;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||||
AstSenTree* const sentreep = nodep->sentreep();
|
AstSenTree* const sentreep = nodep->sentreep();
|
||||||
if (sentreep) VL_DO_DANGLING(pushDeletep(sentreep->unlinkFrBack()), sentreep);
|
if (sentreep) VL_DO_DANGLING(pushDeletep(sentreep->unlinkFrBack()), sentreep);
|
||||||
nodep->sentreep(newSenTree(nodep));
|
nodep->sentreep(newSenTree(nodep));
|
||||||
}
|
}
|
||||||
void visit(AstPast* nodep) override {
|
void visit(AstPast* nodep) override {
|
||||||
|
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||||
if (nodep->sentreep()) return; // Already processed
|
if (nodep->sentreep()) return; // Already processed
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
nodep->sentreep(newSenTree(nodep));
|
nodep->sentreep(newSenTree(nodep));
|
||||||
|
|
@ -774,6 +786,7 @@ private:
|
||||||
void visit(AstRising* nodep) override {
|
void visit(AstRising* nodep) override {
|
||||||
if (nodep->user1SetOnce()) return;
|
if (nodep->user1SetOnce()) return;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||||
FileLine* const fl = nodep->fileline();
|
FileLine* const fl = nodep->fileline();
|
||||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||||
|
|
@ -787,6 +800,7 @@ private:
|
||||||
void visit(AstRose* nodep) override {
|
void visit(AstRose* nodep) override {
|
||||||
if (nodep->user1SetOnce()) return;
|
if (nodep->user1SetOnce()) return;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||||
FileLine* const fl = nodep->fileline();
|
FileLine* const fl = nodep->fileline();
|
||||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||||
|
|
@ -849,7 +863,8 @@ private:
|
||||||
|
|
||||||
// Assertion condition check
|
// Assertion condition check
|
||||||
AstLoop* const loopp = new AstLoop{flp};
|
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 AstLoopTest{flp, loopp, new AstLogNot{flp, condp}});
|
||||||
loopp->addStmtsp(new AstEventControl{flp, sentreep, nullptr});
|
loopp->addStmtsp(new AstEventControl{flp, sentreep, nullptr});
|
||||||
|
|
||||||
|
|
@ -916,6 +931,7 @@ private:
|
||||||
void visit(AstStable* nodep) override {
|
void visit(AstStable* nodep) override {
|
||||||
if (nodep->user1SetOnce()) return;
|
if (nodep->user1SetOnce()) return;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||||
FileLine* const fl = nodep->fileline();
|
FileLine* const fl = nodep->fileline();
|
||||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||||
AstSenTree* sentreep = nodep->sentreep();
|
AstSenTree* sentreep = nodep->sentreep();
|
||||||
|
|
@ -931,6 +947,7 @@ private:
|
||||||
void visit(AstSteady* nodep) override {
|
void visit(AstSteady* nodep) override {
|
||||||
if (nodep->user1SetOnce()) return;
|
if (nodep->user1SetOnce()) return;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||||
FileLine* const fl = nodep->fileline();
|
FileLine* const fl = nodep->fileline();
|
||||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||||
|
|
@ -941,6 +958,10 @@ private:
|
||||||
nodep->replaceWith(exprp);
|
nodep->replaceWith(exprp);
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
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.
|
// Validate repetition count: must be a non-negative elaboration-time constant.
|
||||||
// Shared by goto [->N] and nonconsecutive [=N] repetition.
|
// Shared by goto [->N] and nonconsecutive [=N] repetition.
|
||||||
|
|
@ -1203,11 +1224,7 @@ private:
|
||||||
AstNode* const nextp = stmtp->nextp();
|
AstNode* const nextp = stmtp->nextp();
|
||||||
if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) {
|
if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) {
|
||||||
assignp->unlinkFrBack();
|
assignp->unlinkFrBack();
|
||||||
if (!matchAssignsp) {
|
matchAssignsp = AstNode::addNextNull(matchAssignsp, assignp);
|
||||||
matchAssignsp = assignp;
|
|
||||||
} else {
|
|
||||||
matchAssignsp->addNext(assignp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
stmtp = nextp;
|
stmtp = nextp;
|
||||||
}
|
}
|
||||||
|
|
@ -1233,11 +1250,7 @@ private:
|
||||||
AstNodeExpr* const assignRhsp = assignp->rhsp()->unlinkFrBack();
|
AstNodeExpr* const assignRhsp = assignp->rhsp()->unlinkFrBack();
|
||||||
AstAssignDly* const dlyp = new AstAssignDly{flp, assignLhsp, assignRhsp};
|
AstAssignDly* const dlyp = new AstAssignDly{flp, assignLhsp, assignRhsp};
|
||||||
VL_DO_DANGLING(pushDeletep(assignp), assignp);
|
VL_DO_DANGLING(pushDeletep(assignp), assignp);
|
||||||
if (!matchAssignsp) {
|
matchAssignsp = AstNode::addNextNull(matchAssignsp, dlyp);
|
||||||
matchAssignsp = dlyp;
|
|
||||||
} else {
|
|
||||||
matchAssignsp->addNext(dlyp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
stmtp = nextp;
|
stmtp = nextp;
|
||||||
}
|
}
|
||||||
|
|
@ -1318,8 +1331,8 @@ private:
|
||||||
// this tick, p is not required. For s_until_with, p must be true on the q tick too.
|
// 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 rawLhsp = nodep->lhsp()->unlinkFrBack();
|
||||||
AstNodeExpr* const rawRhsp = nodep->rhsp()->unlinkFrBack();
|
AstNodeExpr* const rawRhsp = nodep->rhsp()->unlinkFrBack();
|
||||||
AstSampled* const lhsp = new AstSampled{flp, rawLhsp, rawLhsp->dtypep()};
|
AstSampled* const lhsp = new AstSampled{flp, rawLhsp, rawLhsp->dtypep(), true};
|
||||||
AstSampled* const rhsp = new AstSampled{flp, rawRhsp, rawRhsp->dtypep()};
|
AstSampled* const rhsp = new AstSampled{flp, rawRhsp, rawRhsp->dtypep(), true};
|
||||||
AstNodeExpr* finalCondp = rhsp->cloneTreePure(false);
|
AstNodeExpr* finalCondp = rhsp->cloneTreePure(false);
|
||||||
if (nodep->isOverlapping()) {
|
if (nodep->isOverlapping()) {
|
||||||
finalCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), finalCondp};
|
finalCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), finalCondp};
|
||||||
|
|
@ -1466,7 +1479,9 @@ private:
|
||||||
iterateAndNextNull(nodep->sensesp());
|
iterateAndNextNull(nodep->sensesp());
|
||||||
if (m_senip && m_senip != nodep->sensesp())
|
if (m_senip && m_senip != nodep->sensesp())
|
||||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Only one PSL clock allowed per assertion");
|
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));
|
nodep->disablep(m_defaultDisablep->condp()->cloneTreePure(true));
|
||||||
}
|
}
|
||||||
m_disablep = nodep->disablep();
|
m_disablep = nodep->disablep();
|
||||||
|
|
@ -1476,7 +1491,7 @@ private:
|
||||||
if (!VN_AS(nodep->backp(), NodeCoverOrAssert)->immediate()) {
|
if (!VN_AS(nodep->backp(), NodeCoverOrAssert)->immediate()) {
|
||||||
const AstNodeDType* const propDtp = nodep->propp()->dtypep();
|
const AstNodeDType* const propDtp = nodep->propp()->dtypep();
|
||||||
nodep->propp(new AstSampled{nodep->fileline(), nodep->propp()->unlinkFrBack(),
|
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
|
// cover counts non-vacuous matches only (IEEE 1800-2023 16.15.2), so an
|
||||||
// implication antecedent must hold; assert passes vacuously instead.
|
// implication antecedent must hold; assert passes vacuously instead.
|
||||||
|
|
|
||||||
|
|
@ -1542,14 +1542,14 @@ string AstNode::instanceStr() const {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
void AstNode::v3errorEnd(const std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
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) {
|
if (!m_fileline) {
|
||||||
V3Error::v3errorEnd(str, instanceStrExtra, nullptr);
|
V3Error::v3errorEnd(str, "", nullptr);
|
||||||
} else {
|
} 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;
|
std::ostringstream nsstr;
|
||||||
nsstr << str.str();
|
nsstr << str.str();
|
||||||
if (debug()) {
|
if (debug()) {
|
||||||
|
|
@ -1735,12 +1735,15 @@ AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) {
|
||||||
if (castable == VCastable::DYNAMIC_CLASS) return node2p->dtypep();
|
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) {
|
while (classDtypep1) {
|
||||||
const VCastable castable = computeCastable(classDtypep1, node2p->dtypep(), node2p);
|
const VCastable castable = computeCastable(classDtypep1, node2p->dtypep(), node2p);
|
||||||
if (castable == VCastable::COMPATIBLE) return classDtypep1;
|
if (castable == VCastable::COMPATIBLE) return classDtypep1;
|
||||||
const AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
|
AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
|
||||||
classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr;
|
if (!extendsp) break;
|
||||||
|
AstNodeDType* const edtp
|
||||||
|
= extendsp->dtypep() ? extendsp->dtypep() : extendsp->childDTypep();
|
||||||
|
classDtypep1 = VN_AS(edtp->skipRefp(), ClassRefDType);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -730,6 +730,7 @@ public:
|
||||||
|
|
||||||
// ACCESSORS for specific types
|
// ACCESSORS for specific types
|
||||||
// Alas these can't be virtual or they break when passed a nullptr
|
// Alas these can't be virtual or they break when passed a nullptr
|
||||||
|
bool isDisableQueuePushSelfStmt();
|
||||||
inline bool isClassHandleValue() const;
|
inline bool isClassHandleValue() const;
|
||||||
inline bool isNull() const;
|
inline bool isNull() const;
|
||||||
inline bool isZero() const;
|
inline bool isZero() const;
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
class VAttrType final {
|
||||||
public:
|
public:
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
|
||||||
|
|
@ -584,6 +584,7 @@ class AstClassRefDType final : public AstNodeDType {
|
||||||
//
|
//
|
||||||
// @astgen ptr := m_classp : Optional[AstClass] // data type pointed to, BELOW the AstTypedef
|
// @astgen ptr := m_classp : Optional[AstClass] // data type pointed to, BELOW the AstTypedef
|
||||||
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Package hierarchy
|
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Package hierarchy
|
||||||
|
bool m_rawPointer = false; // Emit as a non-owning C++ pointer rather than VlClassRef
|
||||||
public:
|
public:
|
||||||
AstClassRefDType(FileLine* fl, AstClass* classp, AstPin* paramsp)
|
AstClassRefDType(FileLine* fl, AstClass* classp, AstPin* paramsp)
|
||||||
: ASTGEN_SUPER_ClassRefDType(fl)
|
: ASTGEN_SUPER_ClassRefDType(fl)
|
||||||
|
|
@ -595,7 +596,8 @@ public:
|
||||||
// METHODS
|
// METHODS
|
||||||
bool sameNode(const AstNode* samep) const override {
|
bool sameNode(const AstNode* samep) const override {
|
||||||
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
|
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;
|
bool similarDTypeNode(const AstNodeDType* samep) const override;
|
||||||
void dump(std::ostream& str = std::cout) const override;
|
void dump(std::ostream& str = std::cout) const override;
|
||||||
|
|
@ -613,6 +615,9 @@ public:
|
||||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||||
AstClass* classp() const VL_MT_STABLE { return m_classp; }
|
AstClass* classp() const VL_MT_STABLE { return m_classp; }
|
||||||
void classp(AstClass* nodep) { m_classp = nodep; }
|
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; }
|
bool isCompound() const override { return true; }
|
||||||
};
|
};
|
||||||
class AstConstDType final : public AstNodeDType {
|
class AstConstDType final : public AstNodeDType {
|
||||||
|
|
@ -968,7 +973,7 @@ class AstMemberDType final : public AstNodeDType {
|
||||||
string m_tag; // Holds the string of the verilator tag -- used in JSON output.
|
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
|
int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member
|
||||||
bool m_constrainedRand = false;
|
bool m_constrainedRand = false;
|
||||||
// UNSUP: int m_randType; // Randomization type (IEEE)
|
VRandAttr m_rand; // Randomizability of this member (rand, randc, etc)
|
||||||
public:
|
public:
|
||||||
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
|
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
|
||||||
AstNode* valuep)
|
AstNode* valuep)
|
||||||
|
|
@ -1025,6 +1030,8 @@ public:
|
||||||
}
|
}
|
||||||
bool isConstrainedRand() const { return m_constrainedRand; }
|
bool isConstrainedRand() const { return m_constrainedRand; }
|
||||||
void markConstrainedRand(bool flag) { m_constrainedRand = flag; }
|
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 {
|
class AstNBACommitQueueDType final : public AstNodeDType {
|
||||||
// @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable
|
// @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable
|
||||||
|
|
|
||||||
|
|
@ -905,7 +905,7 @@ public:
|
||||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||||
// There's no classOrPackagep(); use classOrPackageNodep() to get Node,
|
// There's no classOrPackagep(); use classOrPackageNodep() to get Node,
|
||||||
// or iterating to package with classOrPackageSkipp()
|
// or iterating to package with classOrPackageSkipp()
|
||||||
AstNodeModule* classOrPackageSkipp() const;
|
AstNodeModule* classOrPackageSkipp(const bool doRefs = true) const;
|
||||||
AstNode* classOrPackageNodep() const { return m_classOrPackageNodep; }
|
AstNode* classOrPackageNodep() const { return m_classOrPackageNodep; }
|
||||||
void classOrPackageNodep(AstNode* nodep) { m_classOrPackageNodep = nodep; }
|
void classOrPackageNodep(AstNode* nodep) { m_classOrPackageNodep = nodep; }
|
||||||
void classOrPackagep(AstNodeModule* nodep) {
|
void classOrPackagep(AstNodeModule* nodep) {
|
||||||
|
|
@ -2262,24 +2262,25 @@ public:
|
||||||
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
||||||
};
|
};
|
||||||
class AstSConsRep final : public AstNodeExpr {
|
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
|
// 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
|
// op3 := maxCountp -- max repetition count (M); nullptr when exact or unbounded
|
||||||
//
|
//
|
||||||
// Encoding:
|
// Encoding:
|
||||||
// [*N]: countp=N, maxCountp=nullptr, unbounded=false
|
// [*N]: countp=N, maxCountp=nullptr, unbounded=false
|
||||||
// [*N:M]: countp=N, maxCountp=M, 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=1, maxCountp=nullptr, unbounded=true (= [*1:$])
|
||||||
// [*]: countp=0, maxCountp=nullptr, unbounded=true (= [*0:$])
|
// [*]: countp=0, maxCountp=nullptr, unbounded=true (= [*0:$])
|
||||||
//
|
//
|
||||||
// Lowering:
|
// Lowering:
|
||||||
// Exact [*N] standalone: V3AssertPre saturating counter
|
// 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 op1 := exprp : AstNodeExpr
|
||||||
// @astgen op2 := countp : AstNodeExpr
|
// @astgen op2 := countp : AstNodeExpr
|
||||||
// @astgen op3 := maxCountp : Optional[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:
|
public:
|
||||||
// Exact [*N]
|
// Exact [*N]
|
||||||
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
|
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
|
||||||
|
|
@ -2287,7 +2288,7 @@ public:
|
||||||
this->exprp(exprp);
|
this->exprp(exprp);
|
||||||
this->countp(countp);
|
this->countp(countp);
|
||||||
}
|
}
|
||||||
// Range [*N:M] or unbounded [+]/[*]
|
// Range [*N:M] or unbounded [*N:$]/[+]/[*]
|
||||||
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp, AstNodeExpr* maxCountp,
|
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp, AstNodeExpr* maxCountp,
|
||||||
bool unbounded)
|
bool unbounded)
|
||||||
: ASTGEN_SUPER_SConsRep(fl)
|
: ASTGEN_SUPER_SConsRep(fl)
|
||||||
|
|
@ -2554,19 +2555,26 @@ public:
|
||||||
class AstSampled final : public AstNodeExpr {
|
class AstSampled final : public AstNodeExpr {
|
||||||
// Verilog $sampled
|
// Verilog $sampled
|
||||||
// @astgen op1 := exprp : AstNode<AstNodeExpr|AstPropSpec>
|
// @astgen op1 := exprp : AstNode<AstNodeExpr|AstPropSpec>
|
||||||
|
bool m_internal : 1; // Internally created, not from a source $sampled
|
||||||
public:
|
public:
|
||||||
AstSampled(FileLine* fl, AstNode* exprp, AstNodeDType* dtypep)
|
AstSampled(FileLine* fl, AstNode* exprp, AstNodeDType* dtypep, bool internal = false)
|
||||||
: ASTGEN_SUPER_Sampled(fl) {
|
: ASTGEN_SUPER_Sampled(fl)
|
||||||
|
, m_internal{internal} {
|
||||||
this->exprp(exprp);
|
this->exprp(exprp);
|
||||||
this->dtypep(dtypep);
|
this->dtypep(dtypep);
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstSampled;
|
ASTGEN_MEMBERS_AstSampled;
|
||||||
|
void dump(std::ostream& str) const override;
|
||||||
|
void dumpJson(std::ostream& str) const override;
|
||||||
string emitVerilog() override { return "$sampled(%l)"; }
|
string emitVerilog() override { return "$sampled(%l)"; }
|
||||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||||
int instrCount() const override { return 0; }
|
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; }
|
bool isSystemFunc() const override { return true; }
|
||||||
};
|
};
|
||||||
class AstScopeName final : public AstNodeExpr {
|
class AstScopeName final : public AstNodeExpr {
|
||||||
|
|
@ -3258,6 +3266,7 @@ public:
|
||||||
bool sizeMattersRhs() const override { return false; }
|
bool sizeMattersRhs() const override { return false; }
|
||||||
bool isSystemFunc() const override { return true; }
|
bool isSystemFunc() const override { return true; }
|
||||||
int instrCount() const override { return widthInstrs() * 64; }
|
int instrCount() const override { return widthInstrs() * 64; }
|
||||||
|
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||||
};
|
};
|
||||||
class AstFUngetC final : public AstNodeBiop {
|
class AstFUngetC final : public AstNodeBiop {
|
||||||
public:
|
public:
|
||||||
|
|
@ -3978,12 +3987,16 @@ public:
|
||||||
class AstSAnd final : public AstNodeBiop {
|
class AstSAnd final : public AstNodeBiop {
|
||||||
// Sequence 'and' (IEEE 1800-2023 16.9.5): both operand sequences must match.
|
// 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.
|
// Operates on match sets, not values. For boolean operands, lowered to AstLogAnd.
|
||||||
|
const bool m_propertyControl; // Parser-generated property if/case branch conjunction
|
||||||
public:
|
public:
|
||||||
AstSAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
AstSAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, bool propertyControl = false)
|
||||||
: ASTGEN_SUPER_SAnd(fl, lhsp, rhsp) {
|
: ASTGEN_SUPER_SAnd(fl, lhsp, rhsp)
|
||||||
|
, m_propertyControl{propertyControl} {
|
||||||
dtypeSetBit();
|
dtypeSetBit();
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstSAnd;
|
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 {
|
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||||
out.opLogAnd(lhs, rhs);
|
out.opLogAnd(lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
@ -3997,6 +4010,10 @@ public:
|
||||||
bool sizeMattersRhs() const override { return false; }
|
bool sizeMattersRhs() const override { return false; }
|
||||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||||
bool isMultiCycleSva() const override { return true; }
|
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 {
|
class AstSIntersect final : public AstNodeBiop {
|
||||||
// Sequence 'intersect' (IEEE 1800-2023 16.9.6): both operands match with equal length.
|
// Sequence 'intersect' (IEEE 1800-2023 16.9.6): both operands match with equal length.
|
||||||
|
|
@ -4886,9 +4903,11 @@ public:
|
||||||
bool cleanRhs() const override { return true; }
|
bool cleanRhs() const override { return true; }
|
||||||
bool sizeMattersLhs() const override { return false; }
|
bool sizeMattersLhs() const override { return false; }
|
||||||
bool sizeMattersRhs() 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 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; }
|
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||||
int instrCount() const override { return widthInstrs(); }
|
int instrCount() const override { return widthInstrs(); }
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
||||||
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
||||||
string m_name; // Name of task
|
string m_name; // Name of task
|
||||||
string m_cname; // Name of task if DPI import
|
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)
|
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
|
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
|
||||||
bool m_taskPublic : 1; // Public task
|
bool m_taskPublic : 1; // Public task
|
||||||
|
|
@ -199,6 +200,9 @@ public:
|
||||||
void dpiOpenChild(bool flag) { m_dpiOpenChild = flag; }
|
void dpiOpenChild(bool flag) { m_dpiOpenChild = flag; }
|
||||||
bool dpiTask() const { return m_dpiTask; }
|
bool dpiTask() const { return m_dpiTask; }
|
||||||
void dpiTask(bool flag) { m_dpiTask = flag; }
|
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; }
|
bool isConstructor() const { return m_isConstructor; }
|
||||||
void isConstructor(bool flag) { m_isConstructor = flag; }
|
void isConstructor(bool flag) { m_isConstructor = flag; }
|
||||||
bool isHideLocal() const { return m_isHideLocal; }
|
bool isHideLocal() const { return m_isHideLocal; }
|
||||||
|
|
@ -504,6 +508,7 @@ class AstCFunc final : public AstNode {
|
||||||
// @astgen op1 := argsp : List[AstVar] // Argument (and return value) variables
|
// @astgen op1 := argsp : List[AstVar] // Argument (and return value) variables
|
||||||
// @astgen op2 := varsp : List[AstVar] // Local variables
|
// @astgen op2 := varsp : List[AstVar] // Local variables
|
||||||
// @astgen op3 := stmtsp : List[AstNode]
|
// @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
|
// @astgen ptr := m_scopep : Optional[AstScope] // Scope that function is under
|
||||||
string m_name;
|
string m_name;
|
||||||
|
|
@ -511,6 +516,7 @@ class AstCFunc final : public AstNode {
|
||||||
string m_rtnType; // void, bool, or other return type
|
string m_rtnType; // void, bool, or other return type
|
||||||
string m_argTypes; // Argument types
|
string m_argTypes; // Argument types
|
||||||
string m_ifdef; // #ifdef symbol around this function
|
string m_ifdef; // #ifdef symbol around this function
|
||||||
|
string m_cDecl; // Custom DPI-C function declaration
|
||||||
VBoolOrUnknown m_isConst; // Function is declared const (*this not changed)
|
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_isStatic : 1; // Function is static (no need for a 'this' pointer)
|
||||||
bool m_isTrace : 1; // Function is related to tracing
|
bool m_isTrace : 1; // Function is related to tracing
|
||||||
|
|
@ -640,6 +646,9 @@ public:
|
||||||
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
|
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
|
||||||
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
|
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
|
||||||
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
|
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
|
||||||
|
bool dpiCDeclOverride() const { return !m_cDecl.empty(); }
|
||||||
|
const string& dpiCDecl() const { return m_cDecl; }
|
||||||
|
void dpiCDecl(const string& cDecl) { m_cDecl = cDecl; }
|
||||||
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
|
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
|
||||||
void recursive(bool flag) { m_recursive = flag; }
|
void recursive(bool flag) { m_recursive = flag; }
|
||||||
bool recursive() const { return m_recursive; }
|
bool recursive() const { return m_recursive; }
|
||||||
|
|
@ -650,20 +659,10 @@ public:
|
||||||
void cost(int cost) { m_cost = cost; }
|
void cost(int cost) { m_cost = cost; }
|
||||||
// Special methods
|
// Special methods
|
||||||
bool emptyBody() const {
|
bool emptyBody() const {
|
||||||
return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp() && !isVirtual()
|
return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp() && !scopeNamep() && !isVirtual()
|
||||||
&& !dpiImportPrototype();
|
&& !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 {
|
class AstCUse final : public AstNode {
|
||||||
// C++ use of a class or #include; indicates need of forward declaration
|
// C++ use of a class or #include; indicates need of forward declaration
|
||||||
// Parents: NODEMODULE
|
// Parents: NODEMODULE
|
||||||
|
|
@ -844,7 +843,7 @@ class AstClocking final : public AstNode {
|
||||||
// @astgen op2 := itemsp : List[AstNode]
|
// @astgen op2 := itemsp : List[AstNode]
|
||||||
// @astgen op3 := eventp : Optional[AstVar]
|
// @astgen op3 := eventp : Optional[AstVar]
|
||||||
std::string m_name; // Clocking block name
|
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
|
const bool m_isGlobal; // True if global clocking
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -865,6 +864,7 @@ public:
|
||||||
bool isDefault() const { return m_isDefault; }
|
bool isDefault() const { return m_isDefault; }
|
||||||
bool isGlobal() const { return m_isGlobal; }
|
bool isGlobal() const { return m_isGlobal; }
|
||||||
AstVar* ensureEventp(bool childDType = false);
|
AstVar* ensureEventp(bool childDType = false);
|
||||||
|
void makeDefault() { m_isDefault = true; }
|
||||||
};
|
};
|
||||||
class AstClockingItem final : public AstNode {
|
class AstClockingItem final : public AstNode {
|
||||||
// Parents: CLOCKING
|
// Parents: CLOCKING
|
||||||
|
|
@ -1042,22 +1042,6 @@ public:
|
||||||
void isStatic(bool flag) { m_isStatic = flag; }
|
void isStatic(bool flag) { m_isStatic = flag; }
|
||||||
bool isStatic() const { return m_isStatic; }
|
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 {
|
class AstCoverBin final : public AstNode {
|
||||||
// Captures data for a coverpoint 'bins' declaration
|
// Captures data for a coverpoint 'bins' declaration
|
||||||
// @astgen op1 := rangesp : List[AstNode]
|
// @astgen op1 := rangesp : List[AstNode]
|
||||||
|
|
@ -1222,6 +1206,16 @@ public:
|
||||||
ASTGEN_MEMBERS_AstDefParam;
|
ASTGEN_MEMBERS_AstDefParam;
|
||||||
bool sameNode(const AstNode*) const override { return true; }
|
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 {
|
class AstDefaultDisable final : public AstNode {
|
||||||
// @astgen op1 := condp : AstNodeExpr
|
// @astgen op1 := condp : AstNodeExpr
|
||||||
|
|
||||||
|
|
@ -1679,17 +1673,24 @@ class AstPropSpec final : public AstNode {
|
||||||
// @astgen op1 := sensesp : Optional[AstSenItem]
|
// @astgen op1 := sensesp : Optional[AstSenItem]
|
||||||
// @astgen op2 := disablep : Optional[AstNodeExpr]
|
// @astgen op2 := disablep : Optional[AstNodeExpr]
|
||||||
// @astgen op3 := propp : AstNode
|
// @astgen op3 := propp : AstNode
|
||||||
|
VPropStrength m_propStrength = VPropStrength::DEFAULT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AstPropSpec(FileLine* fl, AstSenItem* sensesp, AstNodeExpr* disablep, AstNode* propp)
|
AstPropSpec(FileLine* fl, AstSenItem* sensesp, AstNodeExpr* disablep, AstNode* propp,
|
||||||
: ASTGEN_SUPER_PropSpec(fl) {
|
VPropStrength propStrength = VPropStrength::DEFAULT)
|
||||||
|
: ASTGEN_SUPER_PropSpec(fl)
|
||||||
|
, m_propStrength{propStrength} {
|
||||||
this->sensesp(sensesp);
|
this->sensesp(sensesp);
|
||||||
this->disablep(disablep);
|
this->disablep(disablep);
|
||||||
this->propp(propp);
|
this->propp(propp);
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstPropSpec;
|
ASTGEN_MEMBERS_AstPropSpec;
|
||||||
|
void dump(std::ostream& str) const override;
|
||||||
|
void dumpJson(std::ostream& str) const override;
|
||||||
bool hasDType() const override VL_MT_SAFE {
|
bool hasDType() const override VL_MT_SAFE {
|
||||||
return true;
|
return true;
|
||||||
} // Used under Cover, which expects a bool child
|
} // Used under Cover, which expects a bool child
|
||||||
|
VPropStrength propStrength() const { return m_propStrength; }
|
||||||
};
|
};
|
||||||
class AstPull final : public AstNode {
|
class AstPull final : public AstNode {
|
||||||
// @astgen op1 := lhsp : AstNodeExpr
|
// @astgen op1 := lhsp : AstNodeExpr
|
||||||
|
|
@ -2784,11 +2785,14 @@ class AstCoverCross final : public AstNodeFuncCovItem {
|
||||||
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
|
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
|
||||||
// @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items;
|
// @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items;
|
||||||
// // post-LinkParse: empty
|
// // post-LinkParse: empty
|
||||||
|
// @astgen op4 := iffp : Optional[AstNodeExpr] // Conditional sampling guard
|
||||||
public:
|
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) {
|
: ASTGEN_SUPER_CoverCross(fl, name) {
|
||||||
UASSERT(itemsp, "AstCoverCross requires at least one coverpoint reference");
|
UASSERT(itemsp, "AstCoverCross requires at least one coverpoint reference");
|
||||||
addItemsp(itemsp);
|
addItemsp(itemsp);
|
||||||
|
this->iffp(iffp);
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstCoverCross;
|
ASTGEN_MEMBERS_AstCoverCross;
|
||||||
void dump(std::ostream& str) const override;
|
void dump(std::ostream& str) const override;
|
||||||
|
|
@ -2889,6 +2893,7 @@ class AstClass final : public AstNodeModule {
|
||||||
// @astgen op4 := extendsp : List[AstClassExtends]
|
// @astgen op4 := extendsp : List[AstClassExtends]
|
||||||
// MEMBERS
|
// MEMBERS
|
||||||
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
|
// @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
|
uint32_t m_declTokenNum; // Declaration token number
|
||||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||||
bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?)
|
bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?)
|
||||||
|
|
@ -2913,6 +2918,10 @@ public:
|
||||||
bool timescaleMatters() const override { return false; }
|
bool timescaleMatters() const override { return false; }
|
||||||
AstClassPackage* classOrPackagep() const VL_MT_STABLE { return m_classOrPackagep; }
|
AstClassPackage* classOrPackagep() const VL_MT_STABLE { return m_classOrPackagep; }
|
||||||
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
|
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(); }
|
AstNode* membersp() const VL_MT_STABLE { return stmtsp(); }
|
||||||
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
|
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
|
||||||
bool isCovergroup() const { return m_covergroup; }
|
bool isCovergroup() const { return m_covergroup; }
|
||||||
|
|
|
||||||
|
|
@ -297,6 +297,16 @@ public:
|
||||||
AstSenTree* sentreep() const { return m_sentreep; }
|
AstSenTree* sentreep() const { return m_sentreep; }
|
||||||
void clearSentreep() { m_sentreep = nullptr; }
|
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 {
|
class AstCReturn final : public AstNodeStmt {
|
||||||
// C++ return from a function
|
// C++ return from a function
|
||||||
// @astgen op1 := lhsp : AstNodeExpr
|
// @astgen op1 := lhsp : AstNodeExpr
|
||||||
|
|
@ -451,6 +461,21 @@ public:
|
||||||
bool sameNode(const AstNode* samep) const override { return true; } // Ignore name in comments
|
bool sameNode(const AstNode* samep) const override { return true; } // Ignore name in comments
|
||||||
virtual bool showAt() const { return m_showAt; }
|
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 {
|
class AstConstraintExpr final : public AstNodeStmt {
|
||||||
// Constraint expression
|
// Constraint expression
|
||||||
// @astgen op1 := exprp : AstNodeExpr
|
// @astgen op1 := exprp : AstNodeExpr
|
||||||
|
|
@ -1568,15 +1593,23 @@ class AstFork final : public AstNodeBlock {
|
||||||
//
|
//
|
||||||
// @astgen op3 := forksp : List[AstBegin]
|
// @astgen op3 := forksp : List[AstBegin]
|
||||||
const VJoinType m_joinType; // Join keyword type
|
const VJoinType m_joinType; // Join keyword type
|
||||||
|
bool m_immediateStart = false; // Fork starts before its parent blocks or exits
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AstFork(FileLine* fl, VJoinType joinType, const string& name = "")
|
AstFork(FileLine* fl, VJoinType joinType, const string& name = "")
|
||||||
: ASTGEN_SUPER_Fork(fl, name)
|
: ASTGEN_SUPER_Fork(fl, name)
|
||||||
, m_joinType{joinType} {}
|
, m_joinType{joinType} {}
|
||||||
ASTGEN_MEMBERS_AstFork;
|
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(); }
|
bool isTimingControl() const override { return !joinType().joinNone(); }
|
||||||
void dump(std::ostream& str) const override;
|
void dump(std::ostream& str) const override;
|
||||||
void dumpJson(std::ostream& str) const override;
|
void dumpJson(std::ostream& str) const override;
|
||||||
VJoinType joinType() const { return m_joinType; }
|
VJoinType joinType() const { return m_joinType; }
|
||||||
|
bool immediateStart() const { return m_immediateStart; }
|
||||||
|
void immediateStart(bool flag) { m_immediateStart = flag; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// === AstNodeCoverOrAssert ===
|
// === AstNodeCoverOrAssert ===
|
||||||
|
|
@ -1612,6 +1645,8 @@ class AstCover final : public AstNodeCoverOrAssert {
|
||||||
// @astgen op3 := coverincsp: List[AstNode] // Coverage node
|
// @astgen op3 := coverincsp: List[AstNode] // Coverage node
|
||||||
bool m_isCoverSeq = false; // 'cover sequence' (IEEE 1800-2023 16.14.3): fires per
|
bool m_isCoverSeq = false; // 'cover sequence' (IEEE 1800-2023 16.14.3): fires per
|
||||||
// end-of-match, not per property success
|
// 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:
|
public:
|
||||||
ASTGEN_MEMBERS_AstCover;
|
ASTGEN_MEMBERS_AstCover;
|
||||||
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type,
|
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type,
|
||||||
|
|
@ -1622,6 +1657,8 @@ public:
|
||||||
void dumpJson(std::ostream& str) const override;
|
void dumpJson(std::ostream& str) const override;
|
||||||
bool isCoverSeq() const { return m_isCoverSeq; }
|
bool isCoverSeq() const { return m_isCoverSeq; }
|
||||||
void isCoverSeq(bool flag) { m_isCoverSeq = flag; }
|
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 {
|
class AstRestrict final : public AstNodeCoverOrAssert {
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,21 @@ int AstNodeSel::bitConst() const {
|
||||||
return (constp ? constp->toSInt() : 0);
|
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::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||||
void AstNodeStmt::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
void AstNodeStmt::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||||
|
|
||||||
|
|
@ -468,6 +483,14 @@ void AstSConsRep::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonBoolFuncIf(str, unbounded);
|
dumpJsonBoolFuncIf(str, unbounded);
|
||||||
dumpJsonGen(str);
|
dumpJsonGen(str);
|
||||||
} // LCOV_EXCL_STOP
|
} // 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 {
|
void AstPropAlways::dump(std::ostream& str) const {
|
||||||
this->AstNodeExpr::dump(str);
|
this->AstNodeExpr::dump(str);
|
||||||
if (isStrong()) str << " [strong]";
|
if (isStrong()) str << " [strong]";
|
||||||
|
|
@ -1179,7 +1202,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
||||||
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
|
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
|
||||||
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
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)) {
|
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
||||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||||
info.m_type = EmitCUtil::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
|
info.m_type = EmitCUtil::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
|
||||||
|
|
@ -1401,6 +1425,9 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
|
||||||
} else if (VN_IS(nodep, WildcardSel)) {
|
} else if (VN_IS(nodep, WildcardSel)) {
|
||||||
nodep = VN_AS(nodep, WildcardSel)->fromp();
|
nodep = VN_AS(nodep, WildcardSel)->fromp();
|
||||||
continue;
|
continue;
|
||||||
|
} else if (VN_IS(nodep, CMethodHard)) {
|
||||||
|
nodep = VN_AS(nodep, CMethodHard)->fromp();
|
||||||
|
continue;
|
||||||
} else if (overMembers && VN_IS(nodep, MemberSel)) {
|
} else if (overMembers && VN_IS(nodep, MemberSel)) {
|
||||||
nodep = VN_AS(nodep, MemberSel)->fromp();
|
nodep = VN_AS(nodep, MemberSel)->fromp();
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1977,6 +2004,18 @@ string AstBasicDType::prettyDTypeName(bool) const {
|
||||||
void AstNodeExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
void AstNodeExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||||
void AstNodeExpr::dumpJson(std::ostream& str) const { dumpJsonGen(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() {
|
AstConst::~AstConst() {
|
||||||
// Only rare constants carry originating parameter-name metadata. For all other AstConst nodes,
|
// 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
|
// the V3Number bit keeps this destructor from touching AstNetlist's side table. When the bit
|
||||||
|
|
@ -2022,6 +2061,8 @@ bool AstNodeExpr::isLValue() const {
|
||||||
return varrefp->access().isWriteOrRW();
|
return varrefp->access().isWriteOrRW();
|
||||||
} else if (const AstMemberSel* const memberselp = VN_CAST(this, MemberSel)) {
|
} else if (const AstMemberSel* const memberselp = VN_CAST(this, MemberSel)) {
|
||||||
return memberselp->access().isWriteOrRW();
|
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)) {
|
} else if (const AstSel* const selp = VN_CAST(this, Sel)) {
|
||||||
return selp->fromp()->isLValue();
|
return selp->fromp()->isLValue();
|
||||||
} else if (const AstNodeSel* const nodeSelp = VN_CAST(this, NodeSel)) {
|
} else if (const AstNodeSel* const nodeSelp = VN_CAST(this, NodeSel)) {
|
||||||
|
|
@ -2181,8 +2222,22 @@ void AstClassRefDType::dump(std::ostream& str) const {
|
||||||
} else {
|
} else {
|
||||||
str << " -> UNLINKED";
|
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 {
|
void AstClassRefDType::dumpSmall(std::ostream& str) const {
|
||||||
this->AstNodeDType::dumpSmall(str);
|
this->AstNodeDType::dumpSmall(str);
|
||||||
str << "class:" << name();
|
str << "class:" << name();
|
||||||
|
|
@ -2241,9 +2296,11 @@ void AstNodeCoverOrAssert::dumpJson(std::ostream& str) const {
|
||||||
void AstCover::dump(std::ostream& str) const {
|
void AstCover::dump(std::ostream& str) const {
|
||||||
this->AstNodeCoverOrAssert::dump(str);
|
this->AstNodeCoverOrAssert::dump(str);
|
||||||
if (isCoverSeq()) str << " [COVERSEQ]";
|
if (isCoverSeq()) str << " [COVERSEQ]";
|
||||||
|
if (isSeqEvent()) str << " [SEQEVENT]";
|
||||||
}
|
}
|
||||||
void AstCover::dumpJson(std::ostream& str) const {
|
void AstCover::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonBoolFuncIf(str, isCoverSeq);
|
dumpJsonBoolFuncIf(str, isCoverSeq);
|
||||||
|
dumpJsonBoolFuncIf(str, isSeqEvent);
|
||||||
this->AstNodeCoverOrAssert::dumpJson(str);
|
this->AstNodeCoverOrAssert::dumpJson(str);
|
||||||
}
|
}
|
||||||
void AstClocking::dump(std::ostream& str) const {
|
void AstClocking::dump(std::ostream& str) const {
|
||||||
|
|
@ -2486,12 +2543,14 @@ const char* AstLoopTest::broken() const {
|
||||||
void AstMemberDType::dump(std::ostream& str) const {
|
void AstMemberDType::dump(std::ostream& str) const {
|
||||||
this->AstNodeDType::dump(str);
|
this->AstNodeDType::dump(str);
|
||||||
if (isConstrainedRand()) str << " [CONSTRAINEDRAND]";
|
if (isConstrainedRand()) str << " [CONSTRAINEDRAND]";
|
||||||
|
if (rand().isRandomizable()) str << " [" << rand() << "]";
|
||||||
if (name() != "") str << " name=" << name();
|
if (name() != "") str << " name=" << name();
|
||||||
if (tag() != "") str << " tag=" << tag();
|
if (tag() != "") str << " tag=" << tag();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AstMemberDType::dumpJson(std::ostream& str) const {
|
void AstMemberDType::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonBoolFuncIf(str, isConstrainedRand);
|
dumpJsonBoolFuncIf(str, isConstrainedRand);
|
||||||
|
if (rand().isRandomizable()) dumpJsonStr(str, "rand", rand().ascii());
|
||||||
dumpJsonStrFunc(str, name);
|
dumpJsonStrFunc(str, name);
|
||||||
dumpJsonStrFunc(str, tag);
|
dumpJsonStrFunc(str, tag);
|
||||||
dumpJsonGen(str);
|
dumpJsonGen(str);
|
||||||
|
|
@ -3032,6 +3091,14 @@ void AstSFormatF::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonBoolFuncIf(str, exprFormat);
|
dumpJsonBoolFuncIf(str, exprFormat);
|
||||||
dumpJsonBoolFuncIf(str, optionalFormat);
|
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 {
|
void AstSel::dump(std::ostream& str) const {
|
||||||
this->AstNodeBiop::dump(str);
|
this->AstNodeBiop::dump(str);
|
||||||
str << " widthConst=" << this->widthConst();
|
str << " widthConst=" << this->widthConst();
|
||||||
|
|
@ -3401,7 +3468,7 @@ void AstClassOrPackageRef::dump(std::ostream& str) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void AstClassOrPackageRef::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
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* foundp = m_classOrPackageNodep;
|
||||||
AstNode* lastp = nullptr;
|
AstNode* lastp = nullptr;
|
||||||
while (foundp != lastp) {
|
while (foundp != lastp) {
|
||||||
|
|
@ -3409,11 +3476,12 @@ AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp() const {
|
||||||
if (AstNodeDType* const anodep = VN_CAST(foundp, NodeDType)) {
|
if (AstNodeDType* const anodep = VN_CAST(foundp, NodeDType)) {
|
||||||
foundp = anodep->skipRefOrNullp();
|
foundp = anodep->skipRefOrNullp();
|
||||||
}
|
}
|
||||||
if (const AstTypedef* const anodep = VN_CAST(foundp, Typedef)) {
|
if (doRefs) {
|
||||||
foundp = anodep->subDTypep();
|
if (const AstTypedef* const anodep = VN_CAST(foundp, Typedef)) {
|
||||||
}
|
foundp = anodep->subDTypep();
|
||||||
if (const AstClassRefDType* const anodep = VN_CAST(foundp, ClassRefDType)) {
|
} else if (const AstClassRefDType* const anodep = VN_CAST(foundp, ClassRefDType)) {
|
||||||
foundp = anodep->classp();
|
foundp = anodep->classp();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return VN_CAST(foundp, NodeModule);
|
return VN_CAST(foundp, NodeModule);
|
||||||
|
|
@ -3600,9 +3668,11 @@ void AstCoverInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||||
void AstFork::dump(std::ostream& str) const {
|
void AstFork::dump(std::ostream& str) const {
|
||||||
this->AstNodeBlock::dump(str);
|
this->AstNodeBlock::dump(str);
|
||||||
str << " [" << joinType() << "]";
|
str << " [" << joinType() << "]";
|
||||||
|
if (immediateStart()) str << " [IMMEDIATE]";
|
||||||
}
|
}
|
||||||
void AstFork::dumpJson(std::ostream& str) const {
|
void AstFork::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonStr(str, "joinType", joinType().ascii());
|
dumpJsonStr(str, "joinType", joinType().ascii());
|
||||||
|
dumpJsonBoolFuncIf(str, immediateStart);
|
||||||
dumpJsonGen(str);
|
dumpJsonGen(str);
|
||||||
}
|
}
|
||||||
void AstStop::dump(std::ostream& str) const {
|
void AstStop::dump(std::ostream& str) const {
|
||||||
|
|
@ -3793,7 +3863,7 @@ void AstDelay::dumpJson(std::ostream& str) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* AstDisable::broken() const {
|
const char* AstDisable::broken() const {
|
||||||
BROKEN_RTN((m_targetp && targetRefp()) || ((!m_targetp && !targetRefp())));
|
BROKEN_RTN(!m_targetp && !targetRefp());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
void AstDisable::dump(std::ostream& str) const {
|
void AstDisable::dump(std::ostream& str) const {
|
||||||
|
|
|
||||||
|
|
@ -137,12 +137,9 @@ class BeginVisitor final : public VNVisitor {
|
||||||
UINFO(8, " rename to " << nodep->name());
|
UINFO(8, " rename to " << nodep->name());
|
||||||
m_statep->userMarkChanged(nodep);
|
m_statep->userMarkChanged(nodep);
|
||||||
}
|
}
|
||||||
VL_RESTORER(m_displayScope);
|
VL_RESTORER_CLEAR(m_displayScope);
|
||||||
VL_RESTORER(m_namedScope);
|
VL_RESTORER_CLEAR(m_namedScope);
|
||||||
VL_RESTORER(m_unnamedScope);
|
VL_RESTORER_CLEAR(m_unnamedScope);
|
||||||
m_displayScope = "";
|
|
||||||
m_namedScope = "";
|
|
||||||
m_unnamedScope = "";
|
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstNodeProcedure* nodep) override {
|
void visit(AstNodeProcedure* nodep) override {
|
||||||
|
|
@ -163,14 +160,13 @@ class BeginVisitor final : public VNVisitor {
|
||||||
// naming; so that any begin's inside the function will rename
|
// naming; so that any begin's inside the function will rename
|
||||||
// inside the function.
|
// inside the function.
|
||||||
// Process children
|
// Process children
|
||||||
VL_RESTORER(m_displayScope);
|
VL_RESTORER_COPY(m_displayScope);
|
||||||
VL_RESTORER(m_ftaskp);
|
VL_RESTORER(m_ftaskp);
|
||||||
VL_RESTORER(m_liftedp);
|
VL_RESTORER(m_liftedp);
|
||||||
VL_RESTORER(m_namedScope);
|
VL_RESTORER_CLEAR(m_namedScope);
|
||||||
VL_RESTORER(m_unnamedScope);
|
VL_RESTORER_CLEAR(m_unnamedScope);
|
||||||
m_displayScope = dot(m_displayScope, nodep->name());
|
m_displayScope = dot(m_displayScope, nodep->name());
|
||||||
m_namedScope = "";
|
|
||||||
m_unnamedScope = "";
|
|
||||||
m_ftaskp = nodep;
|
m_ftaskp = nodep;
|
||||||
m_liftedp = nullptr;
|
m_liftedp = nullptr;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
|
@ -191,9 +187,9 @@ class BeginVisitor final : public VNVisitor {
|
||||||
void visit(AstGenBlock* nodep) override {
|
void visit(AstGenBlock* nodep) override {
|
||||||
// GenBlocks were only useful in variable creation, change names and delete
|
// GenBlocks were only useful in variable creation, change names and delete
|
||||||
UINFO(8, " " << nodep);
|
UINFO(8, " " << nodep);
|
||||||
VL_RESTORER(m_displayScope);
|
VL_RESTORER_COPY(m_displayScope);
|
||||||
VL_RESTORER(m_namedScope);
|
VL_RESTORER_COPY(m_namedScope);
|
||||||
VL_RESTORER(m_unnamedScope);
|
VL_RESTORER_COPY(m_unnamedScope);
|
||||||
UASSERT_OBJ(!m_keepBegins, nodep, "Should be able to eliminate all AstGenBlock");
|
UASSERT_OBJ(!m_keepBegins, nodep, "Should be able to eliminate all AstGenBlock");
|
||||||
dotNames(nodep->name(), nodep->fileline(), "__BEGIN__");
|
dotNames(nodep->name(), nodep->fileline(), "__BEGIN__");
|
||||||
iterateAndNextNull(nodep->itemsp());
|
iterateAndNextNull(nodep->itemsp());
|
||||||
|
|
@ -227,9 +223,9 @@ class BeginVisitor final : public VNVisitor {
|
||||||
void visit(AstBegin* nodep) override {
|
void visit(AstBegin* nodep) override {
|
||||||
// Begin blocks were only useful in variable creation, change names and delete
|
// Begin blocks were only useful in variable creation, change names and delete
|
||||||
UINFO(8, " " << nodep);
|
UINFO(8, " " << nodep);
|
||||||
VL_RESTORER(m_displayScope);
|
VL_RESTORER_COPY(m_displayScope);
|
||||||
VL_RESTORER(m_namedScope);
|
VL_RESTORER_COPY(m_namedScope);
|
||||||
VL_RESTORER(m_unnamedScope);
|
VL_RESTORER_COPY(m_unnamedScope);
|
||||||
{
|
{
|
||||||
VL_RESTORER(m_keepBegins);
|
VL_RESTORER(m_keepBegins);
|
||||||
m_keepBegins = false;
|
m_keepBegins = false;
|
||||||
|
|
@ -261,9 +257,9 @@ class BeginVisitor final : public VNVisitor {
|
||||||
void visit(AstNodeBlock* nodep) override {
|
void visit(AstNodeBlock* nodep) override {
|
||||||
// Begin/Fork blocks were only useful in variable creation, change names and delete
|
// Begin/Fork blocks were only useful in variable creation, change names and delete
|
||||||
UINFO(8, " " << nodep);
|
UINFO(8, " " << nodep);
|
||||||
VL_RESTORER(m_displayScope);
|
VL_RESTORER_COPY(m_displayScope);
|
||||||
VL_RESTORER(m_namedScope);
|
VL_RESTORER_COPY(m_namedScope);
|
||||||
VL_RESTORER(m_unnamedScope);
|
VL_RESTORER_COPY(m_unnamedScope);
|
||||||
{
|
{
|
||||||
VL_RESTORER(m_keepBegins);
|
VL_RESTORER(m_keepBegins);
|
||||||
m_keepBegins = VN_IS(nodep, Fork);
|
m_keepBegins = VN_IS(nodep, Fork);
|
||||||
|
|
@ -451,7 +447,7 @@ class BeginRelinkVisitor final : public VNVisitorConst {
|
||||||
private:
|
private:
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Input:
|
// Input:
|
||||||
// AstNodeFTask::user1p // Node replaced, rename it
|
// AstNodeFTask::user1p // bool. Node replaced, rename it
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
void visit(AstNodeFTaskRef* nodep) override {
|
void visit(AstNodeFTaskRef* nodep) override {
|
||||||
|
|
|
||||||
|
|
@ -252,18 +252,15 @@ private:
|
||||||
void visit(AstScope* nodep) override {
|
void visit(AstScope* nodep) override {
|
||||||
VL_RESTORER(m_inScope);
|
VL_RESTORER(m_inScope);
|
||||||
m_inScope = true;
|
m_inScope = true;
|
||||||
VL_RESTORER(m_cFuncNames);
|
VL_RESTORER_CLEAR(m_cFuncNames);
|
||||||
m_cFuncNames.clear();
|
|
||||||
processAndIterate(nodep);
|
processAndIterate(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstNodeModule* nodep) override {
|
void visit(AstNodeModule* nodep) override {
|
||||||
VL_RESTORER(m_cFuncNames);
|
VL_RESTORER_CLEAR(m_cFuncNames);
|
||||||
m_cFuncNames.clear();
|
|
||||||
processAndIterate(nodep);
|
processAndIterate(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstNodeUOrStructDType* nodep) override {
|
void visit(AstNodeUOrStructDType* nodep) override {
|
||||||
VL_RESTORER(m_cFuncNames);
|
VL_RESTORER_CLEAR(m_cFuncNames);
|
||||||
m_cFuncNames.clear();
|
|
||||||
processAndIterate(nodep);
|
processAndIterate(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstNodeVarRef* nodep) override {
|
void visit(AstNodeVarRef* nodep) override {
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
class CastVisitor final : public VNVisitor {
|
class CastVisitor final : public VNVisitor {
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Entire netlist:
|
// Entire netlist:
|
||||||
// AstNode::user1() // bool. Indicates node is of known size
|
// AstNode::user1() // bool. Node is of known size
|
||||||
const VNUser1InUse m_inuser1;
|
const VNUser1InUse m_inuser1;
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ class ClassVisitor final : public VNVisitor {
|
||||||
classScopep->aboveScopep(), classScopep->aboveCellp()};
|
classScopep->aboveScopep(), classScopep->aboveCellp()};
|
||||||
packagep->addStmtsp(scopep);
|
packagep->addStmtsp(scopep);
|
||||||
// Iterate
|
// Iterate
|
||||||
VL_RESTORER(m_prefix);
|
VL_RESTORER_CLEAR(m_prefix);
|
||||||
VL_RESTORER(m_classPackagep);
|
VL_RESTORER(m_classPackagep);
|
||||||
VL_RESTORER(m_classScopep);
|
VL_RESTORER(m_classScopep);
|
||||||
VL_RESTORER(m_packageScopep);
|
VL_RESTORER(m_packageScopep);
|
||||||
|
|
@ -129,7 +129,7 @@ class ClassVisitor final : public VNVisitor {
|
||||||
void visit(AstNodeModule* nodep) override {
|
void visit(AstNodeModule* nodep) override {
|
||||||
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
|
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
|
||||||
// Classes are always under a Package (perhaps $unit) or a module
|
// Classes are always under a Package (perhaps $unit) or a module
|
||||||
VL_RESTORER(m_prefix);
|
VL_RESTORER_CLEAR(m_prefix);
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
m_prefix = nodep->name() + "__03a__03a"; // ::
|
m_prefix = nodep->name() + "__03a__03a"; // ::
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
class CleanVisitor final : public VNVisitor {
|
class CleanVisitor final : public VNVisitor {
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Entire netlist:
|
// Entire netlist:
|
||||||
// AstNode::user() -> CleanState. For this node, 0==UNKNOWN
|
// AstNode::user1() -> CleanState. For this node, 0==UNKNOWN
|
||||||
// AstNode::user2() -> bool. True indicates widthMin has been propagated
|
// AstNode::user2() -> bool. True indicates widthMin has been propagated
|
||||||
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
|
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
|
||||||
const VNUser1InUse m_inuser1;
|
const VNUser1InUse m_inuser1;
|
||||||
|
|
|
||||||
|
|
@ -438,6 +438,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
||||||
|
|
||||||
// Traverse down to see AstConst or AstVarRef
|
// Traverse down to see AstConst or AstVarRef
|
||||||
LeafInfo findLeaf(AstNode* nodep, bool expectConst) {
|
LeafInfo findLeaf(AstNode* nodep, bool expectConst) {
|
||||||
|
if (!nodep->dtypep()->skipRefp()->isIntegralOrPacked()) return LeafInfo{};
|
||||||
LeafInfo info{m_lsb};
|
LeafInfo info{m_lsb};
|
||||||
{
|
{
|
||||||
VL_RESTORER(m_leafp);
|
VL_RESTORER(m_leafp);
|
||||||
|
|
@ -1708,8 +1709,7 @@ class ConstVisitor final : public VNVisitor {
|
||||||
const V3Number num{constp, subsize, constp->num()};
|
const V3Number num{constp, subsize, constp->num()};
|
||||||
nodep->lhsp(new AstConst{constp->fileline(), num});
|
nodep->lhsp(new AstConst{constp->fileline(), num});
|
||||||
VL_DO_DANGLING(pushDeletep(constp), constp);
|
VL_DO_DANGLING(pushDeletep(constp), constp);
|
||||||
UINFOTREE(9, nodep, "", "BI(EXTEND)-ou");
|
return false; // input node is still valid, keep going
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
bool operandBiExtendConstOver(const AstNodeBiop* nodep) {
|
bool operandBiExtendConstOver(const AstNodeBiop* nodep) {
|
||||||
// EQ(const{width32}, EXTEND(xx{width3})) -> constant
|
// EQ(const{width32}, EXTEND(xx{width3})) -> constant
|
||||||
|
|
@ -2069,7 +2069,7 @@ class ConstVisitor final : public VNVisitor {
|
||||||
rp->rhsp(bp);
|
rp->rhsp(bp);
|
||||||
rp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
rp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
||||||
if (VN_IS(rp->lhsp(), Const) && VN_IS(rp->rhsp(), Const)) replaceConst(rp);
|
if (VN_IS(rp->lhsp(), Const) && VN_IS(rp->rhsp(), Const)) replaceConst(rp);
|
||||||
// UINFOTREE(1, nodep, "", "repAsvConst_new");
|
iterate(nodep); // Proceed to fixed point
|
||||||
}
|
}
|
||||||
void replaceAsvLUp(AstNodeBiop* nodep) {
|
void replaceAsvLUp(AstNodeBiop* nodep) {
|
||||||
// BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r))
|
// BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r))
|
||||||
|
|
@ -2082,7 +2082,7 @@ class ConstVisitor final : public VNVisitor {
|
||||||
lp->lhsp(lrp);
|
lp->lhsp(lrp);
|
||||||
lp->rhsp(rp);
|
lp->rhsp(rp);
|
||||||
lp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
lp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
||||||
// UINFOTREE(1, nodep, "", "repAsvLUp_new");
|
iterate(nodep); // Proceed to fixed point
|
||||||
}
|
}
|
||||||
void replaceAsvRUp(AstNodeBiop* nodep) {
|
void replaceAsvRUp(AstNodeBiop* nodep) {
|
||||||
// BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr))
|
// BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr))
|
||||||
|
|
@ -2095,7 +2095,7 @@ class ConstVisitor final : public VNVisitor {
|
||||||
rp->lhsp(lp);
|
rp->lhsp(lp);
|
||||||
rp->rhsp(rrp);
|
rp->rhsp(rrp);
|
||||||
rp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
rp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
||||||
// UINFOTREE(1, nodep, "", "repAsvRUp_new");
|
iterate(nodep); // Proceed to fixed point
|
||||||
}
|
}
|
||||||
void replaceAndOr(AstNodeBiop* nodep) {
|
void replaceAndOr(AstNodeBiop* nodep) {
|
||||||
// OR (AND (CONSTll,lr), AND(CONSTrl==ll,rr)) -> AND (CONSTll, OR(lr,rr))
|
// OR (AND (CONSTll,lr), AND(CONSTrl==ll,rr)) -> AND (CONSTll, OR(lr,rr))
|
||||||
|
|
@ -2780,9 +2780,8 @@ class ConstVisitor final : public VNVisitor {
|
||||||
streamp->dtypeSetLogicUnsized(packedp->width(), packedp->widthMin(),
|
streamp->dtypeSetLogicUnsized(packedp->width(), packedp->widthMin(),
|
||||||
VSigning::UNSIGNED);
|
VSigning::UNSIGNED);
|
||||||
srcp = packedp;
|
srcp = packedp;
|
||||||
}
|
} else if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|
||||||
if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|
|| VN_IS(srcDTypep, UnpackArrayDType))) {
|
||||||
|| VN_IS(srcDTypep, UnpackArrayDType))) {
|
|
||||||
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
||||||
int blockSize = 1;
|
int blockSize = 1;
|
||||||
if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) {
|
if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) {
|
||||||
|
|
@ -4585,6 +4584,10 @@ class ConstVisitor final : public VNVisitor {
|
||||||
// Custom
|
// Custom
|
||||||
// Implied by AstIsUnbounded::numberOperate: V("AstIsUnbounded{$lhsp.castConst}", "replaceNum(nodep, 0)");
|
// Implied by AstIsUnbounded::numberOperate: V("AstIsUnbounded{$lhsp.castConst}", "replaceNum(nodep, 0)");
|
||||||
TREEOPV("AstIsUnbounded{$lhsp.castUnbounded}", "replaceNum(nodep, 1)");
|
TREEOPV("AstIsUnbounded{$lhsp.castUnbounded}", "replaceNum(nodep, 1)");
|
||||||
|
// Sampled value functions of a constant.
|
||||||
|
// $rose/$fell/$stable/$changed are lowered to $past by V3AssertPre, so they fold via AstPast
|
||||||
|
TREEOPV("AstSampled{$exprp.castConst}", "replaceWChild(nodep, VN_AS(nodep->exprp(), NodeExpr))");
|
||||||
|
TREEOPV("AstPast{$exprp.castConst, !$ticksp}", "replaceWChild(nodep, nodep->exprp())");
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
// Possible futures:
|
// Possible futures:
|
||||||
|
|
|
||||||
|
|
@ -279,8 +279,8 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
const AstNodeModule* const origModp = m_modp;
|
const AstNodeModule* const origModp = m_modp;
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
VL_RESTORER(m_state);
|
VL_RESTORER(m_state);
|
||||||
VL_RESTORER(m_exprTempNames);
|
VL_RESTORER_COPY(m_exprTempNames);
|
||||||
VL_RESTORER(m_funcTemps);
|
VL_RESTORER_COPY(m_funcTemps);
|
||||||
createHandle(nodep);
|
createHandle(nodep);
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
m_state.m_inModOff = false; // Haven't made top shell, so tops are real tops
|
m_state.m_inModOff = false; // Haven't made top shell, so tops are real tops
|
||||||
|
|
@ -294,8 +294,8 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
void visit(AstClass* nodep) override {
|
void visit(AstClass* nodep) override {
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
VL_RESTORER(m_state);
|
VL_RESTORER(m_state);
|
||||||
VL_RESTORER(m_exprTempNames);
|
VL_RESTORER_COPY(m_exprTempNames);
|
||||||
VL_RESTORER(m_funcTemps);
|
VL_RESTORER_COPY(m_funcTemps);
|
||||||
createHandle(nodep);
|
createHandle(nodep);
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
// Covergroup declarations are not executable statements; suppress line/expr/toggle
|
// Covergroup declarations are not executable statements; suppress line/expr/toggle
|
||||||
|
|
@ -356,8 +356,8 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
|
|
||||||
void visit(AstNodeFTask* nodep) override {
|
void visit(AstNodeFTask* nodep) override {
|
||||||
VL_RESTORER(m_ftaskp);
|
VL_RESTORER(m_ftaskp);
|
||||||
VL_RESTORER(m_exprTempNames);
|
VL_RESTORER_COPY(m_exprTempNames);
|
||||||
VL_RESTORER(m_funcTemps);
|
VL_RESTORER_COPY(m_funcTemps);
|
||||||
m_ftaskp = nodep;
|
m_ftaskp = nodep;
|
||||||
if (!nodep->dpiImport()) iterateProcedure(nodep);
|
if (!nodep->dpiImport()) iterateProcedure(nodep);
|
||||||
}
|
}
|
||||||
|
|
@ -385,8 +385,7 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
VL_RESTORER(m_state);
|
VL_RESTORER(m_state);
|
||||||
VL_RESTORER(m_exprStmtsp);
|
VL_RESTORER(m_exprStmtsp);
|
||||||
VL_RESTORER(m_inToggleOff);
|
VL_RESTORER(m_inToggleOff);
|
||||||
// skip properties for expresison coverage
|
m_exprStmtsp = nodep;
|
||||||
if (!VN_IS(nodep, Property)) m_exprStmtsp = nodep;
|
|
||||||
m_inToggleOff = true;
|
m_inToggleOff = true;
|
||||||
createHandle(nodep);
|
createHandle(nodep);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
|
@ -745,6 +744,11 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
newCoverInc(nodep->fileline(), declp, m_beginHier + "_vlCoverageUserTrace"));
|
newCoverInc(nodep->fileline(), declp, m_beginHier + "_vlCoverageUserTrace"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void visit(AstPropSpec* nodep) override {
|
||||||
|
VL_RESTORER(m_exprStmtsp);
|
||||||
|
m_exprStmtsp = nullptr;
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
void visit(AstStop* nodep) override {
|
void visit(AstStop* nodep) override {
|
||||||
UINFO(4, " STOP: " << nodep);
|
UINFO(4, " STOP: " << nodep);
|
||||||
m_state.m_on = false;
|
m_state.m_on = false;
|
||||||
|
|
@ -762,7 +766,7 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
void visit(AstGenBlock* nodep) override {
|
void visit(AstGenBlock* nodep) override {
|
||||||
// Similar to AstBegin
|
// Similar to AstBegin
|
||||||
VL_RESTORER(m_beginHier);
|
VL_RESTORER_COPY(m_beginHier);
|
||||||
if (nodep->name() != "") {
|
if (nodep->name() != "") {
|
||||||
m_beginHier = m_beginHier + (m_beginHier != "" ? "__DOT__" : "") + nodep->name();
|
m_beginHier = m_beginHier + (m_beginHier != "" ? "__DOT__" : "") + nodep->name();
|
||||||
}
|
}
|
||||||
|
|
@ -776,7 +780,7 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
// generate blocks; each point should get separate consideration.
|
// generate blocks; each point should get separate consideration.
|
||||||
// (Currently ignored for line coverage, since any generate iteration
|
// (Currently ignored for line coverage, since any generate iteration
|
||||||
// covers the code in that line.)
|
// covers the code in that line.)
|
||||||
VL_RESTORER(m_beginHier);
|
VL_RESTORER_COPY(m_beginHier);
|
||||||
VL_RESTORER(m_inToggleOff);
|
VL_RESTORER(m_inToggleOff);
|
||||||
VL_RESTORER(m_exprStmtsp);
|
VL_RESTORER(m_exprStmtsp);
|
||||||
m_exprStmtsp = nodep;
|
m_exprStmtsp = nodep;
|
||||||
|
|
@ -874,7 +878,7 @@ class CoverageVisitor final : public VNVisitor {
|
||||||
UASSERT_OBJ(m_exprs.empty(), nodep, "unexpected expression coverage garbage");
|
UASSERT_OBJ(m_exprs.empty(), nodep, "unexpected expression coverage garbage");
|
||||||
VL_RESTORER(m_seeking);
|
VL_RESTORER(m_seeking);
|
||||||
VL_RESTORER(m_objective);
|
VL_RESTORER(m_objective);
|
||||||
VL_RESTORER(m_exprs);
|
VL_RESTORER_CLEAR(m_exprs); // Already asserted above it's empty.
|
||||||
|
|
||||||
m_seeking = SEEKING;
|
m_seeking = SEEKING;
|
||||||
m_objective = false;
|
m_objective = false;
|
||||||
|
|
|
||||||
1626
src/V3Covergroup.cpp
1626
src/V3Covergroup.cpp
File diff suppressed because it is too large
Load Diff
218
src/V3Dfg.cpp
218
src/V3Dfg.cpp
|
|
@ -34,180 +34,6 @@ DfgGraph::~DfgGraph() {
|
||||||
forEachVertex([&](DfgVertex& vtx) { vtx.unlinkDelete(*this); });
|
forEachVertex([&](DfgVertex& vtx) { vtx.unlinkDelete(*this); });
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
|
||||||
// Create the new graph
|
|
||||||
DfgGraph* const clonep = new DfgGraph{name()};
|
|
||||||
|
|
||||||
// Map from original vertex to clone
|
|
||||||
std::unordered_map<const DfgVertex*, DfgVertex*> vtxp2clonep(size() * 2);
|
|
||||||
|
|
||||||
// Clone constVertices
|
|
||||||
for (const DfgConst& vtx : m_constVertices) {
|
|
||||||
DfgConst* const cp = new DfgConst{*clonep, vtx.fileline(), vtx.num()};
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
}
|
|
||||||
// Clone variable vertices
|
|
||||||
for (const DfgVertexVar& vtx : m_varVertices) {
|
|
||||||
const DfgVertexVar* const vp = vtx.as<DfgVertexVar>();
|
|
||||||
DfgVertexVar* cp = nullptr;
|
|
||||||
|
|
||||||
switch (vtx.type()) {
|
|
||||||
case VDfgType::VarArray: {
|
|
||||||
cp = new DfgVarArray{*clonep, vp->vscp()};
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VDfgType::VarPacked: {
|
|
||||||
cp = new DfgVarPacked{*clonep, vp->vscp()};
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
vtx.v3fatalSrc("Unhandled variable vertex type: " + vtx.typeName());
|
|
||||||
VL_UNREACHABLE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AstVarScope* const tmpForp = vp->tmpForp()) cp->tmpForp(tmpForp);
|
|
||||||
}
|
|
||||||
// Clone ast reference vertices
|
|
||||||
for (const DfgVertexAst& vtx : m_astVertices) { // LCOV_EXCL_START
|
|
||||||
switch (vtx.type()) {
|
|
||||||
case VDfgType::AstRd: {
|
|
||||||
const DfgAstRd* const vp = vtx.as<DfgAstRd>();
|
|
||||||
DfgAstRd* const cp = new DfgAstRd{*clonep, vp->exprp(), vp->inSenItem(), vp->inLoop()};
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
vtx.v3fatalSrc("Unhandled ast reference vertex type: " + vtx.typeName());
|
|
||||||
VL_UNREACHABLE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // LCOV_EXCL_STOP
|
|
||||||
// Clone operation vertices
|
|
||||||
for (const DfgVertex& vtx : m_opVertices) {
|
|
||||||
switch (vtx.type()) {
|
|
||||||
#include "V3Dfg__gen_clone_cases.h" // From ./astgen
|
|
||||||
case VDfgType::CReset: { // LCOV_EXCL_START - No algorithm actually hits this today
|
|
||||||
DfgCReset* const cp = new DfgCReset{*clonep, vtx.fileline(), vtx.dtype()};
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
break;
|
|
||||||
} // LCOV_EXCL_STOP
|
|
||||||
case VDfgType::MatchMasked: {
|
|
||||||
DfgMatchMasked* const cp = new DfgMatchMasked{*clonep, vtx.fileline(), vtx.dtype()};
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VDfgType::Sel: {
|
|
||||||
DfgSel* const cp = new DfgSel{*clonep, vtx.fileline(), vtx.dtype()};
|
|
||||||
cp->lsb(vtx.as<DfgSel>()->lsb());
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VDfgType::Rep: {
|
|
||||||
DfgRep* const cp = new DfgRep{*clonep, vtx.fileline(), vtx.dtype()};
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VDfgType::UnitArray: {
|
|
||||||
DfgUnitArray* const cp = new DfgUnitArray{*clonep, vtx.fileline(), vtx.dtype()};
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VDfgType::Mux: {
|
|
||||||
DfgMux* const cp = new DfgMux{*clonep, vtx.fileline(), vtx.dtype()};
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VDfgType::SpliceArray: {
|
|
||||||
DfgSpliceArray* const cp = new DfgSpliceArray{*clonep, vtx.fileline(), vtx.dtype()};
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VDfgType::SplicePacked: {
|
|
||||||
DfgSplicePacked* const cp = new DfgSplicePacked{*clonep, vtx.fileline(), vtx.dtype()};
|
|
||||||
vtxp2clonep.emplace(&vtx, cp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VDfgType::Logic: {
|
|
||||||
vtx.v3fatalSrc("DfgLogic cannot be cloned");
|
|
||||||
VL_UNREACHABLE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VDfgType::Unresolved: {
|
|
||||||
vtx.v3fatalSrc("DfgUnresolved cannot be cloned");
|
|
||||||
VL_UNREACHABLE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case VDfgType::AstRd: // LCOV_EXCL_START
|
|
||||||
case VDfgType::Const:
|
|
||||||
case VDfgType::VarArray:
|
|
||||||
case VDfgType::VarPacked: {
|
|
||||||
vtx.v3fatalSrc("Vertex should have been handled above: " + vtx.typeName());
|
|
||||||
VL_UNREACHABLE;
|
|
||||||
break;
|
|
||||||
} // LCOV_EXCL_STOP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UASSERT(size() == clonep->size(), "Size of clone should be the same");
|
|
||||||
|
|
||||||
// Constants have no inputs
|
|
||||||
// Hook up inputs of cloned variables
|
|
||||||
for (const DfgVertexVar& vtx : m_varVertices) {
|
|
||||||
DfgVertexVar* const cp = vtxp2clonep.at(&vtx)->as<DfgVertexVar>();
|
|
||||||
if (const DfgVertex* const srcp = vtx.srcp()) cp->srcp(vtxp2clonep.at(srcp));
|
|
||||||
if (const DfgVertex* const defp = vtx.defaultp()) cp->defaultp(vtxp2clonep.at(defp));
|
|
||||||
}
|
|
||||||
// Hook up inputs of cloned ast references
|
|
||||||
for (const DfgVertexAst& vtx : m_astVertices) { // LCOV_EXCL_START
|
|
||||||
switch (vtx.type()) {
|
|
||||||
case VDfgType::AstRd: {
|
|
||||||
const DfgAstRd* const vp = vtx.as<DfgAstRd>();
|
|
||||||
DfgAstRd* const cp = vtxp2clonep.at(&vtx)->as<DfgAstRd>();
|
|
||||||
if (const DfgVertex* const srcp = vp->srcp()) cp->srcp(vtxp2clonep.at(srcp));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
vtx.v3fatalSrc("Unhandled DfgVertexAst sub type: " + vtx.typeName());
|
|
||||||
VL_UNREACHABLE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // LCOV_EXCL_STOP
|
|
||||||
// Hook up inputs of cloned operation vertices
|
|
||||||
for (const DfgVertex& vtx : m_opVertices) {
|
|
||||||
if (vtx.is<DfgVertexVariadic>()) {
|
|
||||||
switch (vtx.type()) {
|
|
||||||
case VDfgType::SpliceArray:
|
|
||||||
case VDfgType::SplicePacked: {
|
|
||||||
const DfgVertexSplice* const vp = vtx.as<DfgVertexSplice>();
|
|
||||||
DfgVertexSplice* const cp = vtxp2clonep.at(vp)->as<DfgVertexSplice>();
|
|
||||||
vp->foreachDriver([&](const DfgVertex& src, uint32_t lo, FileLine* flp) {
|
|
||||||
cp->addDriver(vtxp2clonep.at(&src), lo, flp);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
vtx.v3fatalSrc("Unhandled DfgVertexVariadic sub type: " + vtx.typeName());
|
|
||||||
VL_UNREACHABLE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DfgVertex* const cp = vtxp2clonep.at(&vtx);
|
|
||||||
for (size_t i = 0; i < vtx.nInputs(); ++i) {
|
|
||||||
cp->inputp(i, vtxp2clonep.at(vtx.inputp(i)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::unique_ptr<DfgGraph>{clonep};
|
|
||||||
}
|
|
||||||
|
|
||||||
void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
|
void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
|
||||||
if (otherps.empty()) return;
|
if (otherps.empty()) return;
|
||||||
|
|
||||||
|
|
@ -353,6 +179,40 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const DfgPrev* const prevVtxp = vtx.cast<DfgPrev>()) {
|
||||||
|
const AstVarScope* const vscp = prevVtxp->vscp();
|
||||||
|
os << toDotId(vtx);
|
||||||
|
// Begin attributes
|
||||||
|
os << " [";
|
||||||
|
// Begin 'label'
|
||||||
|
os << "label=\"";
|
||||||
|
// Name
|
||||||
|
os << vscp->prettyName();
|
||||||
|
// Address
|
||||||
|
os << '\n' << cvtToHex(prevVtxp);
|
||||||
|
// Type and fanout
|
||||||
|
os << '\n';
|
||||||
|
prevVtxp->dtype().astDtypep()->dumpSmall(os);
|
||||||
|
os << " / F" << prevVtxp->fanout();
|
||||||
|
// End 'label'
|
||||||
|
os << '"';
|
||||||
|
// Shape
|
||||||
|
if (prevVtxp->isPacked()) {
|
||||||
|
os << ", shape=box";
|
||||||
|
} else if (prevVtxp->isArray()) {
|
||||||
|
os << ", shape=box3d";
|
||||||
|
} else {
|
||||||
|
prevVtxp->v3fatalSrc("Unhandled variable type");
|
||||||
|
}
|
||||||
|
// Color
|
||||||
|
const char* const colorp = "mediumorchid1"; // Purple
|
||||||
|
os << ", style=filled";
|
||||||
|
os << ", fillcolor=\"" << colorp << "\"";
|
||||||
|
// End attributes
|
||||||
|
os << "]\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (const DfgConst* const constVtxp = vtx.cast<DfgConst>()) {
|
if (const DfgConst* const constVtxp = vtx.cast<DfgConst>()) {
|
||||||
const V3Number& num = constVtxp->num();
|
const V3Number& num = constVtxp->num();
|
||||||
|
|
||||||
|
|
@ -620,6 +480,12 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataT
|
||||||
dfg.addVertex(*this);
|
dfg.addVertex(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DfgVertex::unsafe() const {
|
||||||
|
if (is<DfgMux>()) return true;
|
||||||
|
if (is<DfgArraySel>()) return !as<DfgArraySel>()->bitp()->is<DfgConst>();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void DfgVertex::typeCheck(const DfgGraph& dfg) const {
|
void DfgVertex::typeCheck(const DfgGraph& dfg) const {
|
||||||
|
|
||||||
#define CHECK(cond, msg) \
|
#define CHECK(cond, msg) \
|
||||||
|
|
@ -647,6 +513,10 @@ void DfgVertex::typeCheck(const DfgGraph& dfg) const {
|
||||||
CHECK(!v.srcp() || v.srcp()->dtype() == v.dtype(), "'srcp' should match");
|
CHECK(!v.srcp() || v.srcp()->dtype() == v.dtype(), "'srcp' should match");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case VDfgType::Prev: {
|
||||||
|
CHECK(isPacked() || isArray(), "Should be Packed or Array type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
case VDfgType::SpliceArray:
|
case VDfgType::SpliceArray:
|
||||||
case VDfgType::SplicePacked: {
|
case VDfgType::SplicePacked: {
|
||||||
const DfgVertexSplice& v = *as<DfgVertexSplice>();
|
const DfgVertexSplice& v = *as<DfgVertexSplice>();
|
||||||
|
|
|
||||||
13
src/V3Dfg.h
13
src/V3Dfg.h
|
|
@ -195,6 +195,8 @@ public:
|
||||||
UASSERT_OBJ(m_dtype.isPacked(), this, "Non packed vertex has no 'width'");
|
UASSERT_OBJ(m_dtype.isPacked(), this, "Non packed vertex has no 'width'");
|
||||||
return m_dtype.size();
|
return m_dtype.size();
|
||||||
}
|
}
|
||||||
|
// Has terminating side-effect
|
||||||
|
bool unsafe() const;
|
||||||
|
|
||||||
// Type check vertex (for debugging)
|
// Type check vertex (for debugging)
|
||||||
void typeCheck(const DfgGraph& dfg) const;
|
void typeCheck(const DfgGraph& dfg) const;
|
||||||
|
|
@ -485,9 +487,6 @@ public:
|
||||||
for (const DfgVertex& vtx : m_opVertices) f(vtx);
|
for (const DfgVertex& vtx : m_opVertices) f(vtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an identical, independent copy of this graph. Vertex and edge order might differ.
|
|
||||||
std::unique_ptr<DfgGraph> clone() const VL_MT_DISABLED;
|
|
||||||
|
|
||||||
// Merge contents of other graphs into this graph. Deletes the other graphs.
|
// Merge contents of other graphs into this graph. Deletes the other graphs.
|
||||||
// DfgVertexVar instances representing the same Ast variable are unified.
|
// DfgVertexVar instances representing the same Ast variable are unified.
|
||||||
void mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) VL_MT_DISABLED;
|
void mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) VL_MT_DISABLED;
|
||||||
|
|
@ -817,6 +816,7 @@ bool DfgVertex::isCheaperThanLoad() const {
|
||||||
if (is<DfgConst>()) return true;
|
if (is<DfgConst>()) return true;
|
||||||
// Variables
|
// Variables
|
||||||
if (is<DfgVertexVar>()) return true;
|
if (is<DfgVertexVar>()) return true;
|
||||||
|
if (is<DfgPrev>()) return true;
|
||||||
// Array sels are just address computation, but the address itself can be expensive
|
// Array sels are just address computation, but the address itself can be expensive
|
||||||
if (const DfgArraySel* aselp = cast<DfgArraySel>()) {
|
if (const DfgArraySel* aselp = cast<DfgArraySel>()) {
|
||||||
if (aselp->bitp()->is<DfgMatchMasked>()) return false;
|
if (aselp->bitp()->is<DfgMatchMasked>()) return false;
|
||||||
|
|
@ -830,6 +830,13 @@ bool DfgVertex::isCheaperThanLoad() const {
|
||||||
const uint32_t msb = lsb + selp->width() - 1;
|
const uint32_t msb = lsb + selp->width() - 1;
|
||||||
return VL_BITWORD_E(msb) == VL_BITWORD_E(lsb);
|
return VL_BITWORD_E(msb) == VL_BITWORD_E(lsb);
|
||||||
}
|
}
|
||||||
|
// Replication of a single cheap bit. Each word of the result is the same
|
||||||
|
// mask computed by negating that bit, so recomputing it at each use costs
|
||||||
|
// no more than the load it replaces.
|
||||||
|
if (const DfgRep* const repp = cast<DfgRep>()) {
|
||||||
|
const DfgVertex* const srcp = repp->srcp();
|
||||||
|
return srcp->width() == 1 && srcp->isCheaperThanLoad();
|
||||||
|
}
|
||||||
// Zero extend of a cheap vertex - Extend(_) was converted to Concat(0, _)
|
// Zero extend of a cheap vertex - Extend(_) was converted to Concat(0, _)
|
||||||
if (const DfgConcat* const catp = cast<DfgConcat>()) {
|
if (const DfgConcat* const catp = cast<DfgConcat>()) {
|
||||||
if (catp->width() > VL_QUADSIZE) return false;
|
if (catp->width() > VL_QUADSIZE) return false;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -100,10 +100,10 @@ class V3DfgBreakCyclesContext final : public V3DfgSubContext {
|
||||||
public:
|
public:
|
||||||
// STATE
|
// STATE
|
||||||
VDouble0 m_nFixed; // Number of graphs that became acyclic
|
VDouble0 m_nFixed; // Number of graphs that became acyclic
|
||||||
VDouble0 m_nImproved; // Number of graphs that were imporoved but still cyclic
|
VDouble0 m_nImproved; // Number of graphs that were improved but still cyclic
|
||||||
VDouble0 m_nUnchanged; // Number of graphs that were left unchanged
|
VDouble0 m_nUnchanged; // Number of graphs that were left unchanged
|
||||||
VDouble0 m_nTrivial; // Number of graphs that were not changed
|
|
||||||
VDouble0 m_nImprovements; // Number of changes made to graphs
|
VDouble0 m_nImprovements; // Number of changes made to graphs
|
||||||
|
VDouble0 m_nPrevInserted; // Number of Prev vertices inserted
|
||||||
|
|
||||||
private:
|
private:
|
||||||
V3DfgBreakCyclesContext()
|
V3DfgBreakCyclesContext()
|
||||||
|
|
@ -112,8 +112,8 @@ private:
|
||||||
addStat("made acyclic", m_nFixed);
|
addStat("made acyclic", m_nFixed);
|
||||||
addStat("improved", m_nImproved);
|
addStat("improved", m_nImproved);
|
||||||
addStat("left unchanged", m_nUnchanged);
|
addStat("left unchanged", m_nUnchanged);
|
||||||
addStat("trivial", m_nTrivial);
|
|
||||||
addStat("changes applied", m_nImprovements);
|
addStat("changes applied", m_nImprovements);
|
||||||
|
addStat("prev vertices inserted", m_nPrevInserted);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
class V3DfgCseContext final : public V3DfgSubContext {
|
class V3DfgCseContext final : public V3DfgSubContext {
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ class V3DfgCse final {
|
||||||
case VDfgType::CReset:
|
case VDfgType::CReset:
|
||||||
case VDfgType::VarArray:
|
case VDfgType::VarArray:
|
||||||
case VDfgType::VarPacked:
|
case VDfgType::VarPacked:
|
||||||
|
case VDfgType::Prev:
|
||||||
case VDfgType::AstRd: // LCOV_EXCL_STOP
|
case VDfgType::AstRd: // LCOV_EXCL_STOP
|
||||||
vtx.v3fatalSrc("Hash should have been pre-computed");
|
vtx.v3fatalSrc("Hash should have been pre-computed");
|
||||||
|
|
||||||
|
|
@ -173,6 +174,7 @@ class V3DfgCse final {
|
||||||
// Special vertices
|
// Special vertices
|
||||||
case VDfgType::Const: return a.as<DfgConst>()->num().isCaseEq(b.as<DfgConst>()->num());
|
case VDfgType::Const: return a.as<DfgConst>()->num().isCaseEq(b.as<DfgConst>()->num());
|
||||||
case VDfgType::CReset: return false;
|
case VDfgType::CReset: return false;
|
||||||
|
case VDfgType::Prev: return false;
|
||||||
|
|
||||||
case VDfgType::VarArray:
|
case VDfgType::VarArray:
|
||||||
case VDfgType::VarPacked: // CSE does not combine variables
|
case VDfgType::VarPacked: // CSE does not combine variables
|
||||||
|
|
@ -307,9 +309,9 @@ class V3DfgCse final {
|
||||||
for (const DfgVertexVar& vtx : dfg.varVertices()) m_hashCache[vtx] = V3Hash{++varHash};
|
for (const DfgVertexVar& vtx : dfg.varVertices()) m_hashCache[vtx] = V3Hash{++varHash};
|
||||||
// Pre-hash Ast references, these are all unique like variables
|
// Pre-hash Ast references, these are all unique like variables
|
||||||
for (const DfgVertexAst& vtx : dfg.astVertices()) m_hashCache[vtx] = V3Hash{++varHash};
|
for (const DfgVertexAst& vtx : dfg.astVertices()) m_hashCache[vtx] = V3Hash{++varHash};
|
||||||
// Pre-hash CReset vertices, these are all unique
|
// Pre-hash CReset and Prev vertices, these are all unique
|
||||||
for (const DfgVertex& vtx : dfg.opVertices()) {
|
for (const DfgVertex& vtx : dfg.opVertices()) {
|
||||||
if (vtx.is<DfgCReset>()) m_hashCache[vtx] = V3Hash{++varHash};
|
if (vtx.is<DfgCReset>() || vtx.is<DfgPrev>()) m_hashCache[vtx] = V3Hash{++varHash};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Similarly pre-hash constants for speed. While we don't combine constants, we do want
|
// Similarly pre-hash constants for speed. While we don't combine constants, we do want
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,10 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||||
m_resultp = new AstVarRef{vtxp->fileline(), vtxp->vscp(), VAccess::READ};
|
m_resultp = new AstVarRef{vtxp->fileline(), vtxp->vscp(), VAccess::READ};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void visit(DfgPrev* vtxp) override {
|
||||||
|
m_resultp = new AstVarRef{vtxp->fileline(), vtxp->vscp(), VAccess::READ};
|
||||||
|
}
|
||||||
|
|
||||||
void visit(DfgConst* vtxp) override { //
|
void visit(DfgConst* vtxp) override { //
|
||||||
m_resultp = new AstConst{vtxp->fileline(), vtxp->num()};
|
m_resultp = new AstConst{vtxp->fileline(), vtxp->num()};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,8 @@ class DataflowOptimize final {
|
||||||
// - bit2: Read by logic in same module/netlist not represented in DFG
|
// - bit2: Read by logic in same module/netlist not represented in DFG
|
||||||
// - bit3: Written by logic in same module/netlist not represented in DFG
|
// - bit3: Written by logic in same module/netlist not represented in DFG
|
||||||
// - bit4: Has READWRITE references
|
// - bit4: Has READWRITE references
|
||||||
// - bit31-5: Reference count, how many DfgVertexVar represent this variable
|
// - bit5: Has DfgPrev instance
|
||||||
|
// - bit31-6: Reference count, how many DfgVertexVar represent this variable
|
||||||
//
|
//
|
||||||
// AstNode::user2/user3/user4 can be used by various DFG algorithms
|
// AstNode::user2/user3/user4 can be used by various DFG algorithms
|
||||||
const VNUser1InUse m_user1InUse;
|
const VNUser1InUse m_user1InUse;
|
||||||
|
|
@ -116,69 +117,44 @@ class DataflowOptimize final {
|
||||||
V3DfgPasses::synthesize(dfg, m_ctx);
|
V3DfgPasses::synthesize(dfg, m_ctx);
|
||||||
endOfStage("synthesize", dfg, {});
|
endOfStage("synthesize", dfg, {});
|
||||||
|
|
||||||
// Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a
|
// Extract the cyclic sub-graphs, so breakCycles can operate on small graphs,
|
||||||
// DAG, and large, mostly acyclic graphs could not be optimized due to the presence of
|
// make all of them acyclic, then merge them back to the main graph
|
||||||
// small cycles.
|
{
|
||||||
std::vector<std::unique_ptr<DfgGraph>> cyclicComps = dfg.extractCyclicComponents("cyclic");
|
std::vector<std::unique_ptr<DfgGraph>> comps = dfg.extractCyclicComponents("cyclic");
|
||||||
endOfStage("extractCyclic", dfg, cyclicComps);
|
for (const auto& cp : comps) V3DfgPasses::breakCycles(*cp, m_ctx);
|
||||||
|
dfg.mergeGraphs(std::move(comps));
|
||||||
// Attempt to convert cyclic components into acyclic ones
|
endOfStage("breakCycles", dfg, {});
|
||||||
std::vector<std::unique_ptr<DfgGraph>> madeAcyclicComponents;
|
|
||||||
if (v3Global.opt.fDfgBreakCycles()) {
|
|
||||||
for (auto it = cyclicComps.begin(); it != cyclicComps.end();) {
|
|
||||||
auto result = V3DfgPasses::breakCycles(**it, m_ctx);
|
|
||||||
if (!result.first) {
|
|
||||||
// No improvement, moving on.
|
|
||||||
++it;
|
|
||||||
} else if (!result.second) {
|
|
||||||
// Improved, but still cyclic. Replace the original cyclic component.
|
|
||||||
*it = std::move(result.first);
|
|
||||||
++it;
|
|
||||||
} else {
|
|
||||||
// Result became acyclic. Move to madeAcyclicComponents, delete original.
|
|
||||||
madeAcyclicComponents.emplace_back(std::move(result.first));
|
|
||||||
it = cyclicComps.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Merge those that were made acyclic back to the graph, this enables optimizing more
|
|
||||||
dfg.mergeGraphs(std::move(madeAcyclicComponents));
|
|
||||||
endOfStage("breakCycles", dfg, cyclicComps);
|
|
||||||
|
|
||||||
// Remove redundant selects
|
// Split the now entirely acyclic DFG into [weakly] connected components
|
||||||
V3DfgPasses::removeSelects(dfg, m_ctx.m_removeSelectsContext);
|
std::vector<std::unique_ptr<DfgGraph>> comps = dfg.splitIntoComponents("acyclic");
|
||||||
for (std::unique_ptr<DfgGraph>& compp : cyclicComps) {
|
|
||||||
V3DfgPasses::removeSelects(*compp, m_ctx.m_removeSelectsContext);
|
|
||||||
}
|
|
||||||
endOfStage("removeSelects", dfg, cyclicComps);
|
|
||||||
|
|
||||||
// Split the acyclic DFG into [weakly] connected components
|
|
||||||
std::vector<std::unique_ptr<DfgGraph>> acyclicComps = dfg.splitIntoComponents("acyclic");
|
|
||||||
UASSERT(dfg.size() == 0, "DfgGraph should have become empty");
|
UASSERT(dfg.size() == 0, "DfgGraph should have become empty");
|
||||||
endOfStage("splitAcyclic", dfg, acyclicComps);
|
endOfStage("splitAcyclic", dfg, comps);
|
||||||
|
|
||||||
// Optimize each acyclic component
|
// Main pass pipeline - optimize each acyclic component
|
||||||
for (auto& cp : acyclicComps) V3DfgPasses::inlineVars(*cp);
|
{
|
||||||
endOfStage("inlineVars", dfg, acyclicComps);
|
for (auto& cp : comps) V3DfgPasses::removeSelects(*cp, m_ctx.m_removeSelectsContext);
|
||||||
for (auto& cp : acyclicComps) V3DfgPasses::cse(*cp, m_ctx.m_cseContext0);
|
endOfStage("removeSelects", dfg, comps);
|
||||||
endOfStage("cse0", dfg, acyclicComps);
|
for (const auto& cp : comps) V3DfgPasses::inlineVars(*cp);
|
||||||
for (auto& cp : acyclicComps) V3DfgPasses::binToOneHot(*cp, m_ctx.m_binToOneHotContext);
|
endOfStage("inlineVars", dfg, comps);
|
||||||
endOfStage("binToOneHot", dfg, acyclicComps);
|
for (auto& cp : comps) V3DfgPasses::cse(*cp, m_ctx.m_cseContext0);
|
||||||
for (auto& cp : acyclicComps) V3DfgPasses::peephole(*cp, m_ctx.m_peepholeContext);
|
endOfStage("cse0", dfg, comps);
|
||||||
endOfStage("peephole", dfg, acyclicComps);
|
for (auto& cp : comps) V3DfgPasses::binToOneHot(*cp, m_ctx.m_binToOneHotContext);
|
||||||
// Accumulate patterns for reporting
|
endOfStage("binToOneHot", dfg, comps);
|
||||||
if (v3Global.opt.dumpDfgPatterns()) {
|
for (auto& cp : comps) V3DfgPasses::peephole(*cp, m_ctx.m_peepholeContext);
|
||||||
V3DfgPasses::dumpPatterns(acyclicComps);
|
endOfStage("peephole", dfg, comps);
|
||||||
endOfStage("dumpPatterns");
|
|
||||||
|
// Accumulate patterns for reporting
|
||||||
|
if (v3Global.opt.dumpDfgPatterns()) V3DfgPasses::dumpPatterns(comps);
|
||||||
|
|
||||||
|
for (auto& cp : comps) V3DfgPasses::pushDownSels(*cp, m_ctx.m_pushDownSelsContext);
|
||||||
|
endOfStage("pushDownSels", dfg, comps);
|
||||||
|
for (auto& cp : comps) V3DfgPasses::cse(*cp, m_ctx.m_cseContext1);
|
||||||
|
endOfStage("cse1", dfg, comps);
|
||||||
}
|
}
|
||||||
for (auto& cp : acyclicComps) V3DfgPasses::pushDownSels(*cp, m_ctx.m_pushDownSelsContext);
|
|
||||||
endOfStage("pushDownSels", dfg, acyclicComps);
|
|
||||||
for (auto& cp : acyclicComps) V3DfgPasses::cse(*cp, m_ctx.m_cseContext1);
|
|
||||||
endOfStage("cse1", dfg, acyclicComps);
|
|
||||||
|
|
||||||
// Merge everything back under the main DFG
|
// Merge everything back under the main graph
|
||||||
dfg.mergeGraphs(std::move(acyclicComps));
|
dfg.mergeGraphs(std::move(comps));
|
||||||
dfg.mergeGraphs(std::move(cyclicComps));
|
|
||||||
endOfStage("optimized", dfg, {});
|
endOfStage("optimized", dfg, {});
|
||||||
|
|
||||||
// Regularize the graph after merging it all back together so all
|
// Regularize the graph after merging it all back together so all
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,13 @@ void V3DfgPasses::removeUnobservable(DfgGraph& dfg, V3DfgContext& dfgCtx) {
|
||||||
&& !vVtxp->hasExtWrRefs() //
|
&& !vVtxp->hasExtWrRefs() //
|
||||||
&& !vVtxp->hasModWrRefs();
|
&& !vVtxp->hasModWrRefs();
|
||||||
VL_DO_DANGLING(vVtxp->unlinkDelete(dfg), vVtxp);
|
VL_DO_DANGLING(vVtxp->unlinkDelete(dfg), vVtxp);
|
||||||
if (srcp) VL_DO_DANGLING(srcp->unlinkDelete(dfg), srcp);
|
if (srcp) {
|
||||||
|
srcp->foreachSource([&](DfgVertex& src) {
|
||||||
|
src.as<DfgLogic>()->setDrivesUnusedVars();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
VL_DO_DANGLING(srcp->unlinkDelete(dfg), srcp);
|
||||||
|
}
|
||||||
if (delAst) {
|
if (delAst) {
|
||||||
VL_DO_DANGLING(vscp->unlinkFrBack()->deleteTree(), vscp);
|
VL_DO_DANGLING(vscp->unlinkFrBack()->deleteTree(), vscp);
|
||||||
++ctx.m_varsDeleted;
|
++ctx.m_varsDeleted;
|
||||||
|
|
|
||||||
|
|
@ -43,15 +43,10 @@ void removeUnobservable(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||||
void synthesize(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
void synthesize(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||||
// Remove redundant selects
|
// Remove redundant selects
|
||||||
void removeSelects(DfgGraph& dfg, V3DfgRemoveSelectsContext& ctx) VL_MT_DISABLED;
|
void removeSelects(DfgGraph& dfg, V3DfgRemoveSelectsContext& ctx) VL_MT_DISABLED;
|
||||||
// Attempt to make the given cyclic graph into an acyclic, or "less cyclic"
|
// Make the given cyclic graph into an acyclic one, by tracing drivers, and
|
||||||
// equivalent. If the returned pointer is null, then no improvement was
|
// if that is unsuccessful, by inserting Prev vertices. The given graph will
|
||||||
// possible on the input graph. Otherwise the returned graph is an improvement
|
// always become acyclic after this pass.
|
||||||
// on the input graph, with at least some cycles eliminated. The returned
|
void breakCycles(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||||
// graph is always independent of the original. If an imporoved graph is
|
|
||||||
// returned, then the returned 'bool' flag indicated if the returned graph is
|
|
||||||
// acyclic (flag 'true'), or still cyclic (flag 'false').
|
|
||||||
std::pair<std::unique_ptr<DfgGraph>, bool> //
|
|
||||||
breakCycles(const DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
|
||||||
// Construct binary to oneHot decoders
|
// Construct binary to oneHot decoders
|
||||||
void binToOneHot(DfgGraph&, V3DfgBinToOneHotContext&) VL_MT_DISABLED;
|
void binToOneHot(DfgGraph&, V3DfgBinToOneHotContext&) VL_MT_DISABLED;
|
||||||
// Common subexpression elimination
|
// Common subexpression elimination
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
void deleteVertex(DfgVertex* vtxp) {
|
void deleteVertex(DfgVertex* vtxp) {
|
||||||
UASSERT_OBJ(!m_vInfo[vtxp].m_workListIndex, vtxp, "Deleted Vertex is in work list");
|
UASSERT_OBJ(!m_vInfo[vtxp].m_workListIndex, vtxp, "Deleted Vertex is in work list");
|
||||||
UASSERT_OBJ(!vtxp->hasSinks(), vtxp, "Should not delete used vertex");
|
UASSERT_OBJ(!vtxp->hasSinks(), vtxp, "Should not delete used vertex");
|
||||||
|
const DfgVertexVar* const varp = vtxp->cast<DfgVertexVar>();
|
||||||
|
UASSERT_OBJ(!varp || !varp->hasPrev(), vtxp, "Deleting variable consumed via DfgPrev");
|
||||||
|
|
||||||
// Invalidate cache entry
|
// Invalidate cache entry
|
||||||
m_cache.invalidate(vtxp);
|
m_cache.invalidate(vtxp);
|
||||||
|
|
@ -293,7 +295,6 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
// This pass only removes variables that are either not driven in this graph,
|
// This pass only removes variables that are either not driven in this graph,
|
||||||
// or are not observable outside the graph. If there is also no external write
|
// or are not observable outside the graph. If there is also no external write
|
||||||
// to the variable and no references in other graph then delete the Ast var too.
|
// to the variable and no references in other graph then delete the Ast var too.
|
||||||
const DfgVertexVar* const varp = vtxp->cast<DfgVertexVar>();
|
|
||||||
if (varp && !varp->isVolatile() && !varp->hasDfgRefs()) {
|
if (varp && !varp->isVolatile() && !varp->hasDfgRefs()) {
|
||||||
m_ctx.m_deleteps.push_back(varp->vscp());
|
m_ctx.m_deleteps.push_back(varp->vscp());
|
||||||
VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp);
|
VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp);
|
||||||
|
|
@ -2269,7 +2270,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(DfgLogAnd* const vtxp) override {
|
void visit(DfgLogAnd* const vtxp) override {
|
||||||
if (binary(vtxp)) return;
|
if (binary(vtxp) || vtxp->rhsp()->unsafe()) return;
|
||||||
|
|
||||||
DfgVertex* const lhsp = vtxp->lhsp();
|
DfgVertex* const lhsp = vtxp->lhsp();
|
||||||
DfgVertex* const rhsp = vtxp->rhsp();
|
DfgVertex* const rhsp = vtxp->rhsp();
|
||||||
|
|
@ -2287,11 +2288,11 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(DfgLogIf* const vtxp) override {
|
void visit(DfgLogIf* const vtxp) override {
|
||||||
if (binary(vtxp)) return;
|
if (binary(vtxp) || vtxp->rhsp()->unsafe()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(DfgLogOr* const vtxp) override {
|
void visit(DfgLogOr* const vtxp) override {
|
||||||
if (binary(vtxp)) return;
|
if (binary(vtxp) || vtxp->rhsp()->unsafe()) return;
|
||||||
|
|
||||||
DfgVertex* const lhsp = vtxp->lhsp();
|
DfgVertex* const lhsp = vtxp->lhsp();
|
||||||
DfgVertex* const rhsp = vtxp->rhsp();
|
DfgVertex* const rhsp = vtxp->rhsp();
|
||||||
|
|
@ -2548,7 +2549,13 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
if (DfgShiftL* const lShiftLp = lhsp->cast<DfgShiftL>()) {
|
if (DfgShiftL* const lShiftLp = lhsp->cast<DfgShiftL>()) {
|
||||||
if (!lShiftLp->hasMultipleSinks() && rhsp->dtype() == lShiftLp->rhsp()->dtype()) {
|
if (!lShiftLp->hasMultipleSinks() && rhsp->dtype() == lShiftLp->rhsp()->dtype()) {
|
||||||
APPLYING(REPLACE_SHIFTL_SHIFTL) {
|
APPLYING(REPLACE_SHIFTL_SHIFTL) {
|
||||||
DfgAdd* const addp = make<DfgAdd>(rhsp, rhsp, lShiftLp->rhsp());
|
// Fold '(a << b) << c' to 'a << (b + c)'. Compute 'b + c'
|
||||||
|
// one bit wider than the amounts so it cannot overflow.
|
||||||
|
FileLine* const flp = vtxp->fileline();
|
||||||
|
const DfgDataType& sumDType = DfgDataType::packed(rhsp->width() + 1);
|
||||||
|
DfgVertex* const bp = make<DfgExtend>(flp, sumDType, lShiftLp->rhsp());
|
||||||
|
DfgVertex* const cp = make<DfgExtend>(flp, sumDType, rhsp);
|
||||||
|
DfgAdd* const addp = make<DfgAdd>(flp, sumDType, bp, cp);
|
||||||
replace(make<DfgShiftL>(vtxp, lShiftLp->lhsp(), addp));
|
replace(make<DfgShiftL>(vtxp, lShiftLp->lhsp(), addp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -2637,7 +2644,13 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
if (DfgShiftR* const lShiftRp = lhsp->cast<DfgShiftR>()) {
|
if (DfgShiftR* const lShiftRp = lhsp->cast<DfgShiftR>()) {
|
||||||
if (!lShiftRp->hasMultipleSinks() && rhsp->dtype() == lShiftRp->rhsp()->dtype()) {
|
if (!lShiftRp->hasMultipleSinks() && rhsp->dtype() == lShiftRp->rhsp()->dtype()) {
|
||||||
APPLYING(REPLACE_SHIFTR_SHIFTR) {
|
APPLYING(REPLACE_SHIFTR_SHIFTR) {
|
||||||
DfgAdd* const addp = make<DfgAdd>(rhsp, rhsp, lShiftRp->rhsp());
|
// Fold '(a >> b) >> c' to 'a >> (b + c)'. Compute 'b + c'
|
||||||
|
// one bit wider than the amounts so it cannot overflow.
|
||||||
|
FileLine* const flp = vtxp->fileline();
|
||||||
|
const DfgDataType& sumDType = DfgDataType::packed(rhsp->width() + 1);
|
||||||
|
DfgVertex* const bp = make<DfgExtend>(flp, sumDType, lShiftRp->rhsp());
|
||||||
|
DfgVertex* const cp = make<DfgExtend>(flp, sumDType, rhsp);
|
||||||
|
DfgAdd* const addp = make<DfgAdd>(flp, sumDType, bp, cp);
|
||||||
replace(make<DfgShiftR>(vtxp, lShiftRp->lhsp(), addp));
|
replace(make<DfgShiftR>(vtxp, lShiftRp->lhsp(), addp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -2890,13 +2903,13 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vtxp->dtype() == m_bitDType) {
|
if (vtxp->dtype() == m_bitDType) {
|
||||||
if (isSame(condp, thenp)) { // a ? a : b becomes a | b
|
if (isSame(condp, thenp) && !elsep->unsafe()) { // a ? a : b becomes a | b
|
||||||
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_COND) {
|
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_COND) {
|
||||||
replace(make<DfgOr>(vtxp, condp, elsep));
|
replace(make<DfgOr>(vtxp, condp, elsep));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isSame(condp, elsep)) { // a ? b : a becomes a & b
|
if (isSame(condp, elsep) && !thenp->unsafe()) { // a ? b : a becomes a & b
|
||||||
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_COND) {
|
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_COND) {
|
||||||
replace(make<DfgAnd>(vtxp, condp, thenp));
|
replace(make<DfgAnd>(vtxp, condp, thenp));
|
||||||
return;
|
return;
|
||||||
|
|
@ -2905,28 +2918,28 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vtxp->width() <= VL_QUADSIZE) {
|
if (vtxp->width() <= VL_QUADSIZE) {
|
||||||
if (isZero(thenp)) { // a ? 0 : b becomes ~a & b
|
if (isZero(thenp) && !elsep->unsafe()) { // a ? 0 : b becomes ~a & b
|
||||||
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) {
|
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) {
|
||||||
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
|
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
|
||||||
replace(make<DfgAnd>(vtxp, maskp, elsep));
|
replace(make<DfgAnd>(vtxp, maskp, elsep));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isOnes(thenp)) { // a ? 1 : b becomes a | b
|
if (isOnes(thenp) && !elsep->unsafe()) { // a ? 1 : b becomes a | b
|
||||||
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ONES) {
|
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ONES) {
|
||||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||||
replace(make<DfgOr>(vtxp, maskp, elsep));
|
replace(make<DfgOr>(vtxp, maskp, elsep));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isZero(elsep)) { // a ? b : 0 becomes a & b
|
if (isZero(elsep) && !thenp->unsafe()) { // a ? b : 0 becomes a & b
|
||||||
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ZERO) {
|
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ZERO) {
|
||||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||||
replace(make<DfgAnd>(vtxp, maskp, thenp));
|
replace(make<DfgAnd>(vtxp, maskp, thenp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isOnes(elsep)) { // a ? b : 1 becomes ~a | b
|
if (isOnes(elsep) && !thenp->unsafe()) { // a ? b : 1 becomes ~a | b
|
||||||
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ONES) {
|
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ONES) {
|
||||||
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
|
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
|
||||||
replace(make<DfgOr>(vtxp, maskp, thenp));
|
replace(make<DfgOr>(vtxp, maskp, thenp));
|
||||||
|
|
@ -2935,7 +2948,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DfgOr* const tOrp = thenp->cast<DfgOr>()) {
|
if (DfgOr* const tOrp = thenp->cast<DfgOr>()) {
|
||||||
if (isSame(tOrp->lhsp(), elsep)) { // a ? b | c : b becomes b | (a & c)
|
if (isSame(tOrp->lhsp(), elsep)
|
||||||
|
&& !tOrp->rhsp()->unsafe()) { // a ? b | c : b becomes b | (a & c)
|
||||||
APPLYING(REPLACE_COND_THEN_OR_LHS) {
|
APPLYING(REPLACE_COND_THEN_OR_LHS) {
|
||||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||||
DfgAnd* const andp = make<DfgAnd>(vtxp, maskp, tOrp->rhsp());
|
DfgAnd* const andp = make<DfgAnd>(vtxp, maskp, tOrp->rhsp());
|
||||||
|
|
@ -2943,7 +2957,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isSame(tOrp->rhsp(), elsep)) { // a ? b | c : c becomes c | (a & b)
|
if (isSame(tOrp->rhsp(), elsep)
|
||||||
|
&& !tOrp->lhsp()->unsafe()) { // a ? b | c : c becomes c | (a & b)
|
||||||
APPLYING(REPLACE_COND_THEN_OR_RHS) {
|
APPLYING(REPLACE_COND_THEN_OR_RHS) {
|
||||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||||
DfgAnd* const andp = make<DfgAnd>(vtxp, maskp, tOrp->lhsp());
|
DfgAnd* const andp = make<DfgAnd>(vtxp, maskp, tOrp->lhsp());
|
||||||
|
|
|
||||||
|
|
@ -56,16 +56,6 @@ class DfgRegularize final {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_set<const DfgVertexVar*> gatherCyclicVariables() {
|
|
||||||
DfgUserMap<uint64_t> vtx2Scc = m_dfg.makeUserMap<uint64_t>();
|
|
||||||
V3DfgPasses::colorStronglyConnectedComponents(m_dfg, vtx2Scc);
|
|
||||||
std::unordered_set<const DfgVertexVar*> circularVariables;
|
|
||||||
for (const DfgVertexVar& vtx : m_dfg.varVertices()) {
|
|
||||||
if (vtx2Scc[vtx]) circularVariables.emplace(&vtx);
|
|
||||||
}
|
|
||||||
return circularVariables;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isUnused(const DfgVertex& vtx) {
|
static bool isUnused(const DfgVertex& vtx) {
|
||||||
if (vtx.hasSinks()) return false;
|
if (vtx.hasSinks()) return false;
|
||||||
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
|
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
|
||||||
|
|
@ -73,6 +63,7 @@ class DfgRegularize final {
|
||||||
UASSERT_OBJ(!varp->hasDfgRefs(), varp, "Should not have refs in other DfgGraph");
|
UASSERT_OBJ(!varp->hasDfgRefs(), varp, "Should not have refs in other DfgGraph");
|
||||||
if (varp->hasModWrRefs()) return false;
|
if (varp->hasModWrRefs()) return false;
|
||||||
if (varp->hasExtRefs()) return false;
|
if (varp->hasExtRefs()) return false;
|
||||||
|
if (varp->hasPrev()) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -87,6 +78,9 @@ class DfgRegularize final {
|
||||||
&aVtx, "Mismatched vertices");
|
&aVtx, "Mismatched vertices");
|
||||||
UASSERT_OBJ(!aVtx.is<DfgVertexVar>(), &aVtx, "Should be an operation vertex");
|
UASSERT_OBJ(!aVtx.is<DfgVertexVar>(), &aVtx, "Should be an operation vertex");
|
||||||
|
|
||||||
|
// Prev is just a variable reference
|
||||||
|
if (aVtx.is<DfgPrev>()) return false;
|
||||||
|
|
||||||
if (bVtx.hasMultipleSinks()) {
|
if (bVtx.hasMultipleSinks()) {
|
||||||
// Add a temporary if it's cheaper to store and load from memory than recompute
|
// Add a temporary if it's cheaper to store and load from memory than recompute
|
||||||
if (!aVtx.isCheaperThanLoad()) return true;
|
if (!aVtx.isCheaperThanLoad()) return true;
|
||||||
|
|
@ -119,10 +113,6 @@ class DfgRegularize final {
|
||||||
}
|
}
|
||||||
|
|
||||||
void eliminateVars() {
|
void eliminateVars() {
|
||||||
// Although we could eliminate some circular variables, doing so would
|
|
||||||
// make UNOPTFLAT traces fairly usesless, so we will not do so.
|
|
||||||
const std::unordered_set<const DfgVertexVar*> circularVariables = gatherCyclicVariables();
|
|
||||||
|
|
||||||
// Worklist based algoritm
|
// Worklist based algoritm
|
||||||
DfgWorklist workList{m_dfg};
|
DfgWorklist workList{m_dfg};
|
||||||
|
|
||||||
|
|
@ -141,7 +131,7 @@ class DfgRegularize final {
|
||||||
});
|
});
|
||||||
// Delete corresponsing Ast variable at the end
|
// Delete corresponsing Ast variable at the end
|
||||||
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
|
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
|
||||||
m_ctx.m_deleteps.push_back(varp->vscp());
|
if (!varp->hasPrev()) m_ctx.m_deleteps.push_back(varp->vscp());
|
||||||
}
|
}
|
||||||
// Remove the unused vertex
|
// Remove the unused vertex
|
||||||
vtx.unlinkDelete(m_dfg);
|
vtx.unlinkDelete(m_dfg);
|
||||||
|
|
@ -173,7 +163,7 @@ class DfgRegularize final {
|
||||||
UASSERT_OBJ(!varp->hasDfgRefs(), varp, "Should not have refs in other DfgGraph");
|
UASSERT_OBJ(!varp->hasDfgRefs(), varp, "Should not have refs in other DfgGraph");
|
||||||
|
|
||||||
// Do not eliminate circular variables - need to preserve UNOPTFLAT traces
|
// Do not eliminate circular variables - need to preserve UNOPTFLAT traces
|
||||||
if (circularVariables.count(varp)) return;
|
if (varp->hasPrev()) return;
|
||||||
|
|
||||||
// Do not inline if partially driven (the partial driver network can't be fed into
|
// Do not inline if partially driven (the partial driver network can't be fed into
|
||||||
// arbitrary logic. TODO: we should peeophole these away entirely)
|
// arbitrary logic. TODO: we should peeophole these away entirely)
|
||||||
|
|
|
||||||
|
|
@ -2005,6 +2005,11 @@ static void dfgSelectLogicForSynthesis(DfgGraph& dfg) {
|
||||||
for (DfgVertex& vtx : dfg.opVertices()) {
|
for (DfgVertex& vtx : dfg.opVertices()) {
|
||||||
DfgLogic* const logicp = vtx.cast<DfgLogic>();
|
DfgLogic* const logicp = vtx.cast<DfgLogic>();
|
||||||
if (!logicp) continue;
|
if (!logicp) continue;
|
||||||
|
// If drives an unused variable, synthesize it so the partial logic can be removed
|
||||||
|
if (logicp->drivesUnusedVars()) {
|
||||||
|
worklist.push_front(*logicp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Blocks corresponding to continuous assignments
|
// Blocks corresponding to continuous assignments
|
||||||
if (logicp->nodep()->keyword() == VAlwaysKwd::CONT_ASSIGN) {
|
if (logicp->nodep()->keyword() == VAlwaysKwd::CONT_ASSIGN) {
|
||||||
worklist.push_front(*logicp);
|
worklist.push_front(*logicp);
|
||||||
|
|
@ -2016,10 +2021,8 @@ static void dfgSelectLogicForSynthesis(DfgGraph& dfg) {
|
||||||
worklist.push_front(*logicp);
|
worklist.push_front(*logicp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Simple blocks driving exactly 1 variable, e.g if (rst) a = b else a = c;
|
// Blocks driving exactly 1 variable
|
||||||
if (!logicp->hasMultipleSinks() && cfg.nBlocks() <= 4 && cfg.nEdges() <= 4) {
|
if (!logicp->hasMultipleSinks()) worklist.push_front(*logicp);
|
||||||
worklist.push_front(*logicp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now expand to cover all logic driving the same set of variables and mark
|
// Now expand to cover all logic driving the same set of variables and mark
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,8 @@ protected:
|
||||||
*DfgDataType::fromAst(vscp->varp()->dtypep())}
|
*DfgDataType::fromAst(vscp->varp()->dtypep())}
|
||||||
, m_vscp{vscp} {
|
, m_vscp{vscp} {
|
||||||
// Increment reference count
|
// Increment reference count
|
||||||
m_vscp->user1(m_vscp->user1() + 0x20);
|
m_vscp->user1(m_vscp->user1() + 0x40);
|
||||||
UASSERT_OBJ((m_vscp->user1() >> 5) > 0, m_vscp, "Reference count overflow");
|
UASSERT_OBJ((m_vscp->user1() >> 6) > 0, m_vscp, "Reference count overflow");
|
||||||
// Allocate sources
|
// Allocate sources
|
||||||
newInput();
|
newInput();
|
||||||
newInput();
|
newInput();
|
||||||
|
|
@ -72,8 +72,8 @@ protected:
|
||||||
public:
|
public:
|
||||||
~DfgVertexVar() {
|
~DfgVertexVar() {
|
||||||
// Decrement reference count
|
// Decrement reference count
|
||||||
m_vscp->user1(m_vscp->user1() - 0x20);
|
m_vscp->user1(m_vscp->user1() - 0x40);
|
||||||
UASSERT_OBJ((m_vscp->user1() >> 5) >= 0, m_vscp, "Reference count underflow");
|
UASSERT_OBJ((m_vscp->user1() >> 6) >= 0, m_vscp, "Reference count underflow");
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_DfgVertexVar;
|
ASTGEN_MEMBERS_DfgVertexVar;
|
||||||
|
|
||||||
|
|
@ -87,7 +87,6 @@ public:
|
||||||
std::string srcName(size_t idx) const override final { return idx ? "defaultp" : "srcp"; }
|
std::string srcName(size_t idx) const override final { return idx ? "defaultp" : "srcp"; }
|
||||||
|
|
||||||
// The Ast variable this vertex representess
|
// The Ast variable this vertex representess
|
||||||
// AstVar* varp() const { return m_varp; }
|
|
||||||
AstVarScope* vscp() const { return m_vscp; }
|
AstVarScope* vscp() const { return m_vscp; }
|
||||||
|
|
||||||
// If this is a temporary, the Ast variable it stands for, or same as
|
// If this is a temporary, the Ast variable it stands for, or same as
|
||||||
|
|
@ -100,7 +99,7 @@ public:
|
||||||
void driverFileLine(FileLine* flp) { m_driverFileLine = flp; }
|
void driverFileLine(FileLine* flp) { m_driverFileLine = flp; }
|
||||||
|
|
||||||
// Variable referenced from other DFG in the same module/netlist
|
// Variable referenced from other DFG in the same module/netlist
|
||||||
bool hasDfgRefs() const { return m_vscp->user1() >> 6; } // I.e.: (nodep()->user1() >> 5) > 1
|
bool hasDfgRefs() const { return m_vscp->user1() >> 7; } // I.e.: (nodep()->user1() >> 6) > 1
|
||||||
|
|
||||||
// Variable referenced from Ast code in the same module/netlist
|
// Variable referenced from Ast code in the same module/netlist
|
||||||
static bool hasModWrRefs(const AstVarScope* nodep) { return nodep->user1() & 0x08; }
|
static bool hasModWrRefs(const AstVarScope* nodep) { return nodep->user1() & 0x08; }
|
||||||
|
|
@ -121,8 +120,15 @@ public:
|
||||||
static bool hasRWRefs(const AstVarScope* nodep) { return nodep->user1() & 0x10; }
|
static bool hasRWRefs(const AstVarScope* nodep) { return nodep->user1() & 0x10; }
|
||||||
static void setHasRWRefs(AstVarScope* nodep) { nodep->user1(nodep->user1() | 0x10); }
|
static void setHasRWRefs(AstVarScope* nodep) { nodep->user1(nodep->user1() | 0x10); }
|
||||||
|
|
||||||
// True iff the value of this variable is read outside this DfgGraph
|
// There exists a DfgPrev vertex for this variable
|
||||||
|
static bool hasPrev(const AstVarScope* nodep) { return nodep->user1() & 0x20; }
|
||||||
|
bool hasPrev() const { return hasPrev(m_vscp); }
|
||||||
|
|
||||||
|
// True iff this variable is consumed without an explicit sink: its value is read outside
|
||||||
|
// this DfgGraph, or a DfgPrev reads it within this graph.
|
||||||
bool isObserved() const {
|
bool isObserved() const {
|
||||||
|
// A DfgPrev reads this variable
|
||||||
|
if (hasPrev()) return true;
|
||||||
// A DfgVarVertex is written in exactly one DfgGraph, and might be read in an arbitrary
|
// A DfgVarVertex is written in exactly one DfgGraph, and might be read in an arbitrary
|
||||||
// number of other DfgGraphs. If it's driven in this DfgGraph, it's read in others.
|
// number of other DfgGraphs. If it's driven in this DfgGraph, it's read in others.
|
||||||
if (hasDfgRefs()) return srcp() || defaultp();
|
if (hasDfgRefs()) return srcp() || defaultp();
|
||||||
|
|
@ -162,6 +168,33 @@ public:
|
||||||
ASTGEN_MEMBERS_DfgVarPacked;
|
ASTGEN_MEMBERS_DfgVarPacked;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DfgPrev final : public DfgVertex {
|
||||||
|
// Previous value of variable, before any updates made in this graph.
|
||||||
|
// Used to break combinational cycles.
|
||||||
|
friend class DfgVertex;
|
||||||
|
friend class DfgVisitor;
|
||||||
|
|
||||||
|
AstVarScope* const m_vscp; // The AstVarScope associated with this vertex (not owned)
|
||||||
|
|
||||||
|
public:
|
||||||
|
DfgPrev(DfgGraph& dfg, AstVarScope* vscp)
|
||||||
|
: DfgVertex{dfg, dfgType(), vscp->varp()->fileline(),
|
||||||
|
*DfgDataType::fromAst(vscp->varp()->dtypep())}
|
||||||
|
, m_vscp{vscp} {
|
||||||
|
UASSERT_OBJ(!DfgVertexVar::hasPrev(vscp), vscp, "Variable already has a DfgPrev");
|
||||||
|
m_vscp->user1(m_vscp->user1() | 0x20); // Mark having a DfgPrev
|
||||||
|
}
|
||||||
|
~DfgPrev() {
|
||||||
|
m_vscp->user1(m_vscp->user1() & ~0x20); // Unmark having a DfgPrev
|
||||||
|
}
|
||||||
|
ASTGEN_MEMBERS_DfgPrev;
|
||||||
|
|
||||||
|
// The Ast variable this vertex representess
|
||||||
|
AstVarScope* vscp() const { return m_vscp; }
|
||||||
|
|
||||||
|
std::string srcName(size_t) const override final { return ""; }
|
||||||
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Ast reference vertices
|
// Ast reference vertices
|
||||||
|
|
||||||
|
|
@ -512,6 +545,7 @@ class DfgLogic final : public DfgVertexVariadic {
|
||||||
AstScope* const m_scopep; // The AstScope m_nodep is under, iff scoped
|
AstScope* const m_scopep; // The AstScope m_nodep is under, iff scoped
|
||||||
const std::unique_ptr<CfgGraph> m_cfgp;
|
const std::unique_ptr<CfgGraph> m_cfgp;
|
||||||
std::vector<DfgVertex*> m_synth; // Vertices this logic was synthesized into
|
std::vector<DfgVertex*> m_synth; // Vertices this logic was synthesized into
|
||||||
|
bool m_drivesUnusedVars = false; // Logic drives unused variables
|
||||||
bool m_selectedForSynthesis = false; // Logic selected for synthesis
|
bool m_selectedForSynthesis = false; // Logic selected for synthesis
|
||||||
bool m_nonSynthesizable = false; // Logic is not synthesizeable (by DfgSynthesis)
|
bool m_nonSynthesizable = false; // Logic is not synthesizeable (by DfgSynthesis)
|
||||||
bool m_reverted = false; // Logic was synthesized (in part if non-synthesizable) then reverted
|
bool m_reverted = false; // Logic was synthesized (in part if non-synthesizable) then reverted
|
||||||
|
|
@ -538,6 +572,8 @@ public:
|
||||||
const CfgGraph& cfg() const { return *m_cfgp; }
|
const CfgGraph& cfg() const { return *m_cfgp; }
|
||||||
std::vector<DfgVertex*>& synth() { return m_synth; }
|
std::vector<DfgVertex*>& synth() { return m_synth; }
|
||||||
const std::vector<DfgVertex*>& synth() const { return m_synth; }
|
const std::vector<DfgVertex*>& synth() const { return m_synth; }
|
||||||
|
bool drivesUnusedVars() const { return m_drivesUnusedVars; }
|
||||||
|
void setDrivesUnusedVars() { m_drivesUnusedVars = true; }
|
||||||
bool selectedForSynthesis() const { return m_selectedForSynthesis; }
|
bool selectedForSynthesis() const { return m_selectedForSynthesis; }
|
||||||
void setSelectedForSynthesis() { m_selectedForSynthesis = true; }
|
void setSelectedForSynthesis() { m_selectedForSynthesis = true; }
|
||||||
bool nonSynthesizable() const { return m_nonSynthesizable; }
|
bool nonSynthesizable() const { return m_nonSynthesizable; }
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,10 @@ void EmitCBaseVisitorConst::emitCDefaultConstructor(const AstNodeModule* const m
|
||||||
void EmitCBaseVisitorConst::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp,
|
void EmitCBaseVisitorConst::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp,
|
||||||
bool withScope) {
|
bool withScope) {
|
||||||
if (funcp->slow()) putns(funcp, "VL_ATTR_COLD ");
|
if (funcp->slow()) putns(funcp, "VL_ATTR_COLD ");
|
||||||
|
if (funcp->dpiCDeclOverride()) {
|
||||||
|
putns(funcp, funcp->dpiCDecl() + ";\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!funcp->isDestructor()) {
|
if (!funcp->isDestructor()) {
|
||||||
putns(funcp, funcp->rtnTypeVoid());
|
putns(funcp, funcp->rtnTypeVoid());
|
||||||
puts(" ");
|
puts(" ");
|
||||||
|
|
|
||||||
|
|
@ -485,7 +485,7 @@ void EmitCFunc::emitVarReset(const string& prefix, AstVar* varp, bool constructi
|
||||||
const auto& mapr = initarp->map();
|
const auto& mapr = initarp->map();
|
||||||
for (const auto& itr : mapr) {
|
for (const auto& itr : mapr) {
|
||||||
AstNode* const valuep = itr.second->valuep();
|
AstNode* const valuep = itr.second->valuep();
|
||||||
emitSetVarConstant(newPrefix + ".at(" + cvtToStr(itr.first) + ")",
|
emitSetVarConstant(newPrefix + ".atWrite(" + cvtToStr(itr.first) + ")",
|
||||||
VN_AS(valuep, Const));
|
VN_AS(valuep, Const));
|
||||||
}
|
}
|
||||||
} else if (VN_IS(dtypep, WildcardArrayDType)) {
|
} else if (VN_IS(dtypep, WildcardArrayDType)) {
|
||||||
|
|
@ -496,7 +496,7 @@ void EmitCFunc::emitVarReset(const string& prefix, AstVar* varp, bool constructi
|
||||||
const auto& mapr = initarp->map();
|
const auto& mapr = initarp->map();
|
||||||
for (const auto& itr : mapr) {
|
for (const auto& itr : mapr) {
|
||||||
AstNode* const valuep = itr.second->valuep();
|
AstNode* const valuep = itr.second->valuep();
|
||||||
emitSetVarConstant(newPrefix + ".at(" + cvtToStr(itr.first) + ")",
|
emitSetVarConstant(newPrefix + ".atWrite(" + cvtToStr(itr.first) + ")",
|
||||||
VN_AS(valuep, Const));
|
VN_AS(valuep, Const));
|
||||||
}
|
}
|
||||||
} else if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
} else if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||||
|
|
@ -541,8 +541,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
|
||||||
depth + 1, suffix + ".atDefault()", nullptr);
|
depth + 1, suffix + ".atDefault()", nullptr);
|
||||||
} else if (VN_IS(dtypep, CDType)) {
|
} else if (VN_IS(dtypep, CDType)) {
|
||||||
return ""; // Constructor does it
|
return ""; // Constructor does it
|
||||||
} else if (VN_IS(dtypep, ClassRefDType)) {
|
} else if (const AstClassRefDType* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||||
return ""; // Constructor does it
|
return adtypep->rawPointer() ? varNameProtected + suffix + " = nullptr;\n" : "";
|
||||||
} else if (VN_IS(dtypep, IfaceRefDType)) {
|
} else if (VN_IS(dtypep, IfaceRefDType)) {
|
||||||
return varNameProtected + suffix + " = nullptr;\n";
|
return varNameProtected + suffix + " = nullptr;\n";
|
||||||
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||||
|
|
|
||||||
|
|
@ -732,7 +732,8 @@ public:
|
||||||
}
|
}
|
||||||
void visit(AstAssocSel* nodep) override {
|
void visit(AstAssocSel* nodep) override {
|
||||||
iterateAndNextConstNull(nodep->fromp());
|
iterateAndNextConstNull(nodep->fromp());
|
||||||
putnbs(nodep, ".at(");
|
const std::string atFunc = nodep->isLValue() ? ".atWrite(" : ".at(";
|
||||||
|
putnbs(nodep, atFunc);
|
||||||
AstAssocArrayDType* const adtypep
|
AstAssocArrayDType* const adtypep
|
||||||
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), AssocArrayDType);
|
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), AssocArrayDType);
|
||||||
UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type");
|
UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type");
|
||||||
|
|
@ -741,7 +742,8 @@ public:
|
||||||
}
|
}
|
||||||
void visit(AstWildcardSel* nodep) override {
|
void visit(AstWildcardSel* nodep) override {
|
||||||
iterateAndNextConstNull(nodep->fromp());
|
iterateAndNextConstNull(nodep->fromp());
|
||||||
putnbs(nodep, ".at(");
|
const std::string atFunc = nodep->isLValue() ? ".atWrite(" : ".at(";
|
||||||
|
putnbs(nodep, atFunc);
|
||||||
AstWildcardArrayDType* const adtypep
|
AstWildcardArrayDType* const adtypep
|
||||||
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), WildcardArrayDType);
|
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), WildcardArrayDType);
|
||||||
UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
|
UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue