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:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
|
|
@ -29,272 +30,141 @@ concurrency:
|
|||
jobs:
|
||||
|
||||
build-2604-gcc:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
name: Build | 26.04 | gcc
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: gcc
|
||||
runs-on: ubuntu-26.04
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: linux
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: ubuntu-26.04, cc: gcc, asan: 0}
|
||||
|
||||
build-2604-clang:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
name: Build | 26.04 | clang
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: clang
|
||||
dev-asan: true # Build (and run) with address sanitizer
|
||||
runs-on: ubuntu-26.04
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: linux
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: ubuntu-26.04, cc: clang, asan: 1}
|
||||
|
||||
build-2404-gcc:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
name: Build | 24.04 | gcc
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: gcc
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: linux
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: ubuntu-24.04, cc: gcc, asan: 0}
|
||||
|
||||
build-2404-clang:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
name: Build | 24.04 | clang
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: clang
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: linux
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: ubuntu-24.04, cc: clang, asan: 0}
|
||||
|
||||
build-2204-gcc:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
name: Build | 22.04 | gcc
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: gcc
|
||||
runs-on: ubuntu-22.04
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: linux
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: ubuntu-22.04, cc: gcc, asan: 0}
|
||||
|
||||
build-osx-gcc:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
build-macos-15-clang:
|
||||
name: Build | macos-15 | clang
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: clang
|
||||
runs-on: macos-15
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: osx
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: macos-15, cc: gcc, asan: 0}
|
||||
|
||||
build-osx-clang:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
sha: ${{ github.sha }}
|
||||
os: ${{ matrix.os }}
|
||||
os-name: osx
|
||||
cc: ${{ matrix.cc }}
|
||||
dev-asan: ${{ matrix.asan }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: macos-15, cc: clang, asan: 0}
|
||||
|
||||
build-windows:
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {os: windows-2025-vs2026, cc: msvc}
|
||||
env:
|
||||
CI_OS_NAME: win
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
name: Build | windows-2025-vs2026 | msvc
|
||||
runs-on: windows-2025-vs2026
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
path: repo
|
||||
- name: Cache $CCACHE_DIR
|
||||
- name: Cache win_flex_bison
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: msbuild-msvc-cmake
|
||||
path: ${{ github.workspace }}/win_flex_bison
|
||||
key: win_flex_bison
|
||||
- name: compile
|
||||
env:
|
||||
WIN_FLEX_BISON: ${{ github.workspace }}/.ccache
|
||||
run: ./ci/ci-win-compile.ps1
|
||||
- name: test build
|
||||
run: ./ci/ci-win-test.ps1
|
||||
- name: Zip up repository
|
||||
run: Compress-Archive -LiteralPath install -DestinationPath verilator.zip
|
||||
- name: Upload zip archive
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: ${{ github.workspace }}/repo/verilator.zip
|
||||
name: verilator-win.zip
|
||||
|
||||
test-2604-gcc:
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
name: Test | 26.04 | gcc | ${{ matrix.suite }}
|
||||
needs: build-2604-gcc
|
||||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build-2604-gcc.outputs.archive }}
|
||||
os: ${{ matrix.os }}
|
||||
cc: ${{ matrix.cc }}
|
||||
reloc: ${{ matrix.reloc }}
|
||||
cc: gcc
|
||||
runs-on: ubuntu-26.04
|
||||
suite: ${{ matrix.suite }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Ubuntu 26.04 gcc
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-1}
|
||||
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-2}
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||
|
||||
test-2604-clang:
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
name: Test | 26.04 | clang | ${{ matrix.suite }}
|
||||
needs: build-2604-clang
|
||||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build-2604-clang.outputs.archive }}
|
||||
os: ${{ matrix.os }}
|
||||
cc: ${{ matrix.cc }}
|
||||
reloc: ${{ matrix.reloc }}
|
||||
cc: clang
|
||||
runs-on: ubuntu-26.04
|
||||
suite: ${{ matrix.suite }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Ubuntu 26.04 clang
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-0}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-3}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-1}
|
||||
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-2}
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||
|
||||
test-2404-gcc:
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
name: Test | 24.04 | gcc | ${{ matrix.suite }}
|
||||
needs: build-2404-gcc
|
||||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build-2404-gcc.outputs.archive }}
|
||||
os: ${{ matrix.os }}
|
||||
cc: ${{ matrix.cc }}
|
||||
reloc: ${{ matrix.reloc }}
|
||||
cc: gcc
|
||||
runs-on: ubuntu-24.04
|
||||
suite: ${{ matrix.suite }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Ubuntu 24.04 gcc
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-1}
|
||||
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-2}
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||
|
||||
test-2404-clang:
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
name: Test | 24.04 | clang | ${{ matrix.suite }}
|
||||
needs: build-2404-clang
|
||||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build-2404-clang.outputs.archive }}
|
||||
os: ${{ matrix.os }}
|
||||
cc: ${{ matrix.cc }}
|
||||
reloc: ${{ matrix.reloc }}
|
||||
cc: clang
|
||||
reloc: true # Test with relocated installation
|
||||
runs-on: ubuntu-24.04
|
||||
suite: ${{ matrix.suite }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Ubuntu 24.04 clang
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-0}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-3}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-1}
|
||||
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-2}
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||
|
||||
test-2204-gcc:
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
|
||||
name: Test | 22.04 | gcc | ${{ matrix.suite }}
|
||||
needs: build-2204-gcc
|
||||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build-2204-gcc.outputs.archive }}
|
||||
os: ${{ matrix.os }}
|
||||
cc: ${{ matrix.cc }}
|
||||
reloc: ${{ matrix.reloc }}
|
||||
cc: gcc
|
||||
runs-on: ubuntu-22.04
|
||||
suite: ${{ matrix.suite }}
|
||||
dev-gcov: 0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Ubuntu 22.04 gcc
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-1}
|
||||
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-2}
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
|
||||
|
||||
lint-py:
|
||||
name: Lint Python
|
||||
|
|
|
|||
|
|
@ -16,5 +16,5 @@ jobs:
|
|||
name: "'docs/CONTRIBUTORS' was signed"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- run: test_regress/t/t_dist_contributors.py
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ on:
|
|||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
|
|
@ -38,15 +39,13 @@ jobs:
|
|||
(github.event_name == 'workflow_dispatch')
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
cc: gcc
|
||||
dev-gcov: true
|
||||
runs-on: ubuntu-24.04
|
||||
# For pull requests, build the head of the pull request branch, not the
|
||||
# merge commit, otherwise patch coverage would include the changes
|
||||
# between the root of the pull request and the target branch
|
||||
sha: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
os: ubuntu-24.04
|
||||
os-name: linux
|
||||
cc: gcc
|
||||
dev-asan: 0
|
||||
dev-gcov: 1
|
||||
|
||||
test:
|
||||
name: Test | ${{ matrix.test }}${{ matrix.num }}
|
||||
|
|
@ -54,11 +53,10 @@ jobs:
|
|||
uses: ./.github/workflows/reusable-test.yml
|
||||
with:
|
||||
archive: ${{ needs.build.outputs.archive }}
|
||||
os: ubuntu-24.04
|
||||
cc: gcc
|
||||
reloc: 0
|
||||
dev-gcov: true
|
||||
runs-on: ubuntu-24.04
|
||||
suite: ${{ matrix.test }}${{ matrix.num }}
|
||||
dev-gcov: 1
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
|
@ -74,7 +72,7 @@ jobs:
|
|||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
|
||||
- name: Download code coverage data
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
|
|
@ -121,7 +119,7 @@ jobs:
|
|||
|
||||
- name: Unpack repository archive
|
||||
run: |
|
||||
tar -x -z -f ${{ needs.build.outputs.archive }}
|
||||
tar --zstd -x -f ${{ needs.build.outputs.archive }}
|
||||
ls -lsha
|
||||
|
||||
- name: Download code coverage data
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
|
||||
- name: Extract context variables
|
||||
run: |
|
||||
|
|
@ -54,7 +54,7 @@ jobs:
|
|||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
|
||||
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
|
||||
with:
|
||||
images: |
|
||||
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
||||
|
|
@ -64,21 +64,21 @@ jobs:
|
|||
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4
|
||||
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
|
||||
with:
|
||||
buildkitd-flags: --debug
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Build and Push to Docker
|
||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
|
||||
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
context: ${{ env.build_context }}
|
||||
|
|
|
|||
|
|
@ -11,35 +11,44 @@ on:
|
|||
permissions:
|
||||
contents: write
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: repo
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-24.04
|
||||
name: Ubuntu 24.04 | format
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CI_RUNS_ON: ubuntu-24.04
|
||||
CI_COMMIT: ${{ github.sha }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
path: repo
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install packages for build
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
run: |
|
||||
sudo apt install clang-format-18 || \
|
||||
sudo apt install clang-format-18
|
||||
git config --global user.email "action@example.com"
|
||||
git config --global user.name "github action"
|
||||
- name: Format code
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
autoconf
|
||||
./configure
|
||||
make venv
|
||||
|
||||
- name: Set up Python venv
|
||||
uses: ./repo/.github/actions/setup-venv
|
||||
with:
|
||||
save: ${{ github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: Format code
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
make -j 4 format CLANGFORMAT=clang-format-18
|
||||
git status
|
||||
|
||||
- name: Push
|
||||
run: |-
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ name: Pages
|
|||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths: ["ci/**", ".github/workflows"]
|
||||
paths: ["ci/**", ".github/workflows/**"]
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: ["Code coverage", "RTLMeter"]
|
||||
|
|
@ -22,8 +22,11 @@ permissions:
|
|||
# Allow only one concurrent deployment, skipping runs queued between the run
|
||||
# in-progress and latest queued. However, do NOT cancel in-progress runs as we
|
||||
# want to allow these deployments to complete.
|
||||
# A skipped-upstream run does no work (see the build job's if:), so put it in its
|
||||
# own throwaway group: otherwise, as the newest queued run, it would cancel a
|
||||
# pending real run and then deploy nothing.
|
||||
concurrency:
|
||||
group: "pages"
|
||||
group: ${{ (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'skipped') && format('pages-skip-{0}', github.run_id) || 'pages' }}
|
||||
cancel-in-progress: false
|
||||
|
||||
defaults:
|
||||
|
|
@ -34,11 +37,15 @@ jobs:
|
|||
build:
|
||||
name: Build content
|
||||
runs-on: ubuntu-24.04
|
||||
# A skipped upstream run (e.g. Code coverage / RTLMeter on a branch push)
|
||||
# still fires workflow_run; don't rebuild and redeploy for it. deploy and
|
||||
# notify need this job, so they cascade-skip too.
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion != 'skipped' }}
|
||||
outputs:
|
||||
pr-run-ids: ${{ steps.build.outputs.pr-run-ids }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
- name: Build pages
|
||||
id: build
|
||||
env:
|
||||
|
|
@ -58,9 +65,16 @@ jobs:
|
|||
runs-on: ubuntu-24.04
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
url: ${{ steps.deploy-2.outputs.page_url || steps.deploy-1.outputs.page_url }}
|
||||
steps:
|
||||
# GitHub's Pages backend intermittently fails a deployment mid-sync, retry
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deploy-1
|
||||
continue-on-error: true
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
|
||||
- name: Deploy to GitHub Pages (retry)
|
||||
id: deploy-2
|
||||
if: steps.deploy-1.outcome == 'failure'
|
||||
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
|
||||
|
||||
notify:
|
||||
|
|
@ -70,7 +84,7 @@ jobs:
|
|||
if: ${{ github.repository == 'verilator/verilator' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
# Use the Verilator CI app to post the comment
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
|
|
|
|||
|
|
@ -7,37 +7,47 @@ name: reusable-build
|
|||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cc:
|
||||
description: "Compiler to use: 'gcc' or 'clang'"
|
||||
required: true
|
||||
type: string
|
||||
ccwarn:
|
||||
description: "Build Verilator with warnings treated as errors (--enable-ccwarn)"
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
dev-asan:
|
||||
description: "Build Verilator with the address sanitizer (--enable-dev-asan)"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
dev-gcov:
|
||||
description: "Build Verilator with gcov instrumentation (--enable-dev-gcov)"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
install:
|
||||
description: "Archive the Verilator installation, not the repo tree"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
runs-on:
|
||||
description: "Runner to build on, e.g. ubuntu-24.04"
|
||||
required: true
|
||||
type: string
|
||||
sha:
|
||||
description: "Commit SHA to build"
|
||||
required: true
|
||||
type: string
|
||||
os: # e.g. ubuntu-24.04
|
||||
required: true
|
||||
type: string
|
||||
cc: # 'clang' or 'gcc'
|
||||
required: true
|
||||
type: string
|
||||
os-name: # 'linux' or 'osx'
|
||||
required: true
|
||||
type: string
|
||||
dev-asan:
|
||||
required: true
|
||||
type: number
|
||||
dev-gcov:
|
||||
required: true
|
||||
type: number
|
||||
outputs:
|
||||
archive:
|
||||
description: "Name of the built repository archive artifact"
|
||||
description: "Name of the built archive artifact"
|
||||
value: ${{ jobs.build.outputs.archive }}
|
||||
|
||||
env:
|
||||
CI_OS_NAME: ${{ inputs.os-name }}
|
||||
CCACHE_COMPRESS: 1
|
||||
CACHE_BASE_KEY: build-${{ inputs.runs-on }}-${{ inputs.cc }}${{ inputs.ccwarn && '-ccwarn' || '' }}${{ inputs.dev-asan && '-asan' || '' }}${{ inputs.dev-gcov && '-gcov' || '' }}
|
||||
CCACHE_COMPILERCHECK: content
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
INSTALL_DIR: ${{ github.workspace }}/install
|
||||
RELOC_DIR: ${{ github.workspace }}/relloc
|
||||
|
||||
defaults:
|
||||
run:
|
||||
|
|
@ -48,26 +58,28 @@ jobs:
|
|||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ${{ inputs.os }}
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
outputs:
|
||||
archive: ${{ steps.create-archive.outputs.archive }}
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
CI_DEV_ASAN: ${{ inputs.dev-asan }}
|
||||
CI_DEV_GCOV: ${{ inputs.dev-gcov }}
|
||||
CI_RUNS_ON: ${{ inputs.os }}
|
||||
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
|
||||
CACHE_BASE_KEY: build-${{ inputs.os }}-${{ inputs.cc }}
|
||||
CCACHE_MAXSIZE: 1000M # Per build matrix entry (* 5 = 5000M in total)
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
path: repo
|
||||
ref: ${{ inputs.sha }}
|
||||
fetch-depth: ${{ inputs.dev-gcov && '0' || '1' }} # Coverage flow needs full history
|
||||
# Coverage needs full history; install needs it for the 'git describe'
|
||||
# behind 'verilator --version'
|
||||
fetch-depth: ${{ (inputs.install || inputs.dev-gcov) && '0' || '1' }}
|
||||
|
||||
- name: Configure ccache
|
||||
run: |
|
||||
# ccache is unreliable on macOS, and does nothing with 'gcc --coverage'
|
||||
if [ "${{ startsWith(inputs.runs-on, 'macos') }}" = true ] || [ "${{ inputs.dev-gcov }}" = true ]; then
|
||||
echo "CCACHE_DISABLE=1" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Cache $CCACHE_DIR
|
||||
if: ${{ env.CCACHE_DISABLE != '1' }}
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||
|
|
@ -75,25 +87,44 @@ jobs:
|
|||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ env.CACHE_KEY }}-${{ inputs.sha }}
|
||||
restore-keys: |
|
||||
${{ env.CACHE_KEY }}-
|
||||
${{ env.CACHE_KEY }}
|
||||
|
||||
- name: Install packages for build
|
||||
run: ./ci/ci-install.bash
|
||||
run: ./ci/ci-install.bash build
|
||||
|
||||
- name: Build
|
||||
run: ./ci/ci-script.bash
|
||||
run: |
|
||||
./ci/ci-build.bash \
|
||||
--prefix ${{ github.workspace }}/install \
|
||||
--compiler ${{ inputs.cc }} \
|
||||
--light-debug \
|
||||
${{ inputs.ccwarn && '--ccwarn' || '' }} \
|
||||
${{ inputs.dev-asan && '--asan' || '' }} \
|
||||
${{ inputs.dev-gcov && '--gcov' || '' }}
|
||||
|
||||
- name: Create repository archive
|
||||
- name: Install
|
||||
if: ${{ inputs.install }}
|
||||
run: make install
|
||||
|
||||
- name: Create archive
|
||||
id: create-archive
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
# Name of the archive must be unique based on the build parameters
|
||||
ARCHIVE=verilator-${{ inputs.sha }}-${{ inputs.os }}-${{ inputs.cc }}-${{ inputs.dev-asan }}-${{ inputs.dev-gcov }}.tar.gz
|
||||
tar --posix -c -z -f $ARCHIVE repo
|
||||
# Archive name, unique per build; flavour tags mark non-default builds
|
||||
ARCHIVE="verilator-${{ inputs.sha }}-${{ inputs.runs-on }}-${{ inputs.cc }}"
|
||||
ARCHIVE="$ARCHIVE${{ inputs.ccwarn && '-ccwarn' || '' }}"
|
||||
ARCHIVE="$ARCHIVE${{ inputs.dev-asan && '-asan' || '' }}"
|
||||
ARCHIVE="$ARCHIVE${{ inputs.dev-gcov && '-gcov' || '' }}"
|
||||
ARCHIVE="$ARCHIVE.tar.zst"
|
||||
# zstd compresses faster than gzip and decompresses far faster in the many downstream jobs
|
||||
ZSTD_NBTHREADS=0 tar --posix --zstd -c -f "$ARCHIVE" ${{ inputs.install && 'install' || 'repo' }}
|
||||
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload repository archive
|
||||
- name: Upload archive
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
|
||||
name: ${{ steps.create-archive.outputs.archive }}
|
||||
overwrite: true
|
||||
# Archive is already zstd-compressed; skip upload-artifact's zip pass
|
||||
compression-level: 0
|
||||
|
|
|
|||
|
|
@ -7,14 +7,6 @@ name: reusable-lint-py
|
|||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
CI_RUNS_ON: ubuntu-22.04
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
|
@ -27,23 +19,22 @@ jobs:
|
|||
name: Sub-lint | Python
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
path: repo
|
||||
|
||||
- name: Install packages for build
|
||||
run: ./ci/ci-install.bash
|
||||
- name: Install dependencies
|
||||
run: ./ci/ci-install.bash lint-py
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
autoconf
|
||||
./configure --enable-longtests --enable-ccwarn
|
||||
|
||||
- name: Install python dependencies
|
||||
run: |
|
||||
sudo apt install python3-clang || \
|
||||
sudo apt install python3-clang
|
||||
make venv
|
||||
- name: Set up Python venv
|
||||
uses: ./repo/.github/actions/setup-venv
|
||||
with:
|
||||
save: ${{ github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: Lint
|
||||
run: |-
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
---
|
||||
# DESCRIPTION: Github actions config
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: reusable-rtlmeter-build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
runs-on:
|
||||
description: "Runner to use, e.g.: ubuntu-24.04"
|
||||
type: string
|
||||
required: true
|
||||
cc:
|
||||
description: "Compiler to use: 'gcc' or 'clang'"
|
||||
type: string
|
||||
required: true
|
||||
sha:
|
||||
description: "Git SHA to build"
|
||||
type: string
|
||||
required: true
|
||||
outputs:
|
||||
archive:
|
||||
description: "Name of the built installation archive artifact"
|
||||
value: ${{ jobs.build.outputs.archive }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/ccache
|
||||
CCACHE_MAXSIZE: 512M
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
outputs:
|
||||
archive: ${{ steps.create-archive.outputs.archive }}
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
echo "path-exclude /usr/share/doc/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
echo "path-exclude /usr/share/man/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
sudo apt update || \
|
||||
sudo apt update
|
||||
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev || \
|
||||
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev
|
||||
|
||||
- name: Use saved ccache
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
with:
|
||||
path: ccache
|
||||
key: rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
restore-keys: |
|
||||
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}-${{ github.run_id }}
|
||||
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}
|
||||
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
path: repo
|
||||
ref: ${{ inputs.sha }}
|
||||
fetch-depth: 0 # Required for 'git describe' used for 'verilator --version'
|
||||
|
||||
- name: Configure
|
||||
working-directory: repo
|
||||
run: |
|
||||
autoconf
|
||||
./configure --prefix=${{ github.workspace }}/install CXX=${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
|
||||
|
||||
- name: Make
|
||||
working-directory: repo
|
||||
run: make -j $(nproc)
|
||||
|
||||
- name: Install
|
||||
working-directory: repo
|
||||
run: make install
|
||||
|
||||
- name: Tar up installation
|
||||
id: create-archive
|
||||
run: |
|
||||
SHA=$(git -C repo rev-parse HEAD)
|
||||
ARCHIVE=verilator-$SHA-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}.tar.gz
|
||||
tar --posix -c -z -f $ARCHIVE install
|
||||
echo "archive=$ARCHIVE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Verilator installation archive
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
path: ${{ steps.create-archive.outputs.archive }}
|
||||
name: ${{ steps.create-archive.outputs.archive }}
|
||||
overwrite: true
|
||||
|
|
@ -20,11 +20,11 @@ on:
|
|||
type: string
|
||||
required: true
|
||||
verilator-archive-new:
|
||||
description: "Name of the installation archive artifact from reusable-rtlmeter-build, new version"
|
||||
description: "Name of the installation archive artifact from reusable-build, new version"
|
||||
type: string
|
||||
required: true
|
||||
verilator-archive-old:
|
||||
description: "Name of the installation archive artifact from reusable-rtlmeter-build, old version"
|
||||
description: "Name of the installation archive artifact from reusable-build, old version"
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
|
|
@ -53,8 +53,8 @@ defaults:
|
|||
shell: bash
|
||||
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/ccache
|
||||
CCACHE_MAXSIZE: 512M
|
||||
# RTLMeter measures compile/execute times, so caching must stay off to keep
|
||||
# timings representative; ccache is still on PATH but acts as a pass-through
|
||||
CCACHE_DISABLE: 1
|
||||
|
||||
jobs:
|
||||
|
|
@ -73,7 +73,7 @@ jobs:
|
|||
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev
|
||||
|
||||
- name: Checkout RTLMeter
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
repository: "verilator/rtlmeter"
|
||||
path: rtlmeter
|
||||
|
|
@ -82,14 +82,6 @@ jobs:
|
|||
working-directory: rtlmeter
|
||||
run: make venv
|
||||
|
||||
- name: Use saved ccache
|
||||
if: ${{ env.CCACHE_DISABLE == 0 }}
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: rtlmeter-run-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.cases }}-${{ inputs.compileArgs }}-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
restore-keys: rtlmeter-run-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.cases }}-${{ inputs.compileArgs }}
|
||||
|
||||
########################################################################
|
||||
# Run with new Verilator
|
||||
########################################################################
|
||||
|
|
@ -101,7 +93,7 @@ jobs:
|
|||
|
||||
- name: Unpack Verilator installation archive - new
|
||||
run: |
|
||||
tar -x -z -f ${{ inputs.verilator-archive-new }}
|
||||
tar --zstd -x -f ${{ inputs.verilator-archive-new }}
|
||||
mv install verilator-new
|
||||
|
||||
- name: Compile cases - new
|
||||
|
|
@ -164,7 +156,7 @@ jobs:
|
|||
- name: Unpack Verilator installation archive - old
|
||||
if: ${{ inputs.verilator-archive-old != '' }}
|
||||
run: |
|
||||
tar -x -z -f ${{ inputs.verilator-archive-old }}
|
||||
tar --zstd -x -f ${{ inputs.verilator-archive-old }}
|
||||
mv install verilator-old
|
||||
|
||||
- name: Compile cases - old
|
||||
|
|
|
|||
|
|
@ -11,29 +11,37 @@ on:
|
|||
description: "Name of the repository archive artifact from reusable-build"
|
||||
required: true
|
||||
type: string
|
||||
os: # e.g. ubuntu-24.04
|
||||
required: true
|
||||
type: string
|
||||
cc: # gcc or clang
|
||||
required: true
|
||||
type: string
|
||||
reloc: # 0 or 1
|
||||
required: true
|
||||
type: number
|
||||
suite: # e.g. dist-vlt-0
|
||||
cc:
|
||||
description: "Compiler to use: 'gcc' or 'clang'"
|
||||
required: true
|
||||
type: string
|
||||
dev-gcov:
|
||||
description: "Collect gcov coverage data from the test run"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
reloc:
|
||||
description: "Relocate the installation before testing"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
runs-on:
|
||||
description: "Runner to test on, e.g. ubuntu-24.04"
|
||||
required: true
|
||||
type: number
|
||||
type: string
|
||||
suite:
|
||||
description: "Test suite to run, e.g. dist-vlt-0"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_COMPILERCHECK: content
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
INSTALL_DIR: ${{ github.workspace }}/install
|
||||
RELOC_DIR: ${{ github.workspace }}/relloc
|
||||
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
|
|
@ -43,15 +51,8 @@ defaults:
|
|||
jobs:
|
||||
|
||||
test:
|
||||
runs-on: ${{ inputs.os }}
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
name: Test
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: test
|
||||
CI_RUNS_ON: ${{ inputs.os }}
|
||||
CI_RELOC: ${{inputs.reloc }}
|
||||
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
|
||||
CACHE_BASE_KEY: test-${{ inputs.os }}-${{ inputs.cc }}-${{inputs.reloc }}-${{ inputs.suite }}
|
||||
CCACHE_MAXSIZE: 100M # Per build per suite (* 5 * 5 = 2500M in total)
|
||||
steps:
|
||||
|
||||
- name: Download repository archive
|
||||
|
|
@ -63,32 +64,43 @@ jobs:
|
|||
- name: Unpack repository archive
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
tar -x -z -f ${{ inputs.archive }}
|
||||
tar --zstd -x -f ${{ inputs.archive }}
|
||||
ls -lsha
|
||||
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2
|
||||
- name: Configure ccache
|
||||
run: |
|
||||
# ccache is unreliable on macOS, and does nothing with 'gcc --coverage'
|
||||
if [ "${{ startsWith(inputs.runs-on, 'macos') }}" = true ] || [ "${{ inputs.dev-gcov }}" = true ]; then
|
||||
echo "CCACHE_DISABLE=1" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
# Test-job ccache is stored as an artifact, not actions/cache
|
||||
- name: Restore ccache
|
||||
id: ccache
|
||||
if: ${{ env.CCACHE_DISABLE != '1' }}
|
||||
uses: ./repo/.github/actions/artifact-cache
|
||||
with:
|
||||
mode: restore
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ env.CACHE_KEY }}-
|
||||
key: ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.suite }}
|
||||
|
||||
- name: Install test dependencies
|
||||
run: |
|
||||
./ci/ci-install.bash
|
||||
make venv
|
||||
./ci/ci-install.bash test
|
||||
|
||||
- name: Set up Python venv
|
||||
uses: ./repo/.github/actions/setup-venv
|
||||
with:
|
||||
save: ${{ github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: Test
|
||||
id: run-test
|
||||
continue-on-error: true
|
||||
env:
|
||||
TESTS: ${{ inputs.suite }}
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
./ci/ci-script.bash
|
||||
./ci/ci-test.bash \
|
||||
--suite ${{ inputs.suite }} \
|
||||
${{ inputs.reloc && format('--reloc {0}/reloc', github.workspace) || '' }}
|
||||
|
||||
- name: Combine code coverage data
|
||||
if: ${{ inputs.dev-gcov }}
|
||||
|
|
@ -104,6 +116,16 @@ jobs:
|
|||
path: ${{ github.workspace }}/repo/obj_coverage/verilator-${{ inputs.suite }}.info
|
||||
name: code-coverage-${{ inputs.suite }}
|
||||
|
||||
- name: Save ccache
|
||||
if: ${{ env.CCACHE_DISABLE != '1' && !cancelled() }}
|
||||
uses: ./repo/.github/actions/artifact-cache
|
||||
with:
|
||||
mode: save
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ steps.ccache.outputs.key }}
|
||||
# Keep master's cache around longer; PR/branch caches churn faster
|
||||
retention-days: ${{ github.ref == 'refs/heads/master' && 7 || 3 }}
|
||||
|
||||
- name: Fail job if a test failed
|
||||
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
|
||||
run: |-
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ jobs:
|
|||
cases: ${{ steps.cases.outputs.cases }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
|
||||
- name: Startup
|
||||
id: start
|
||||
|
|
@ -65,39 +65,47 @@ jobs:
|
|||
build-gcc-new:
|
||||
name: Build New Verilator - GCC
|
||||
needs: start
|
||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
runs-on: ubuntu-24.04
|
||||
cc: gcc
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ github.sha }}
|
||||
|
||||
build-clang-new:
|
||||
name: Build New Verilator - Clang
|
||||
needs: start
|
||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
runs-on: ubuntu-24.04
|
||||
cc: clang
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ github.sha }}
|
||||
|
||||
build-gcc-old:
|
||||
name: Build Old Verilator - GCC
|
||||
needs: start
|
||||
if: ${{ needs.start.outputs.old-sha != '' }}
|
||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
runs-on: ubuntu-24.04
|
||||
cc: gcc
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ needs.start.outputs.old-sha }}
|
||||
|
||||
build-clang-old:
|
||||
name: Build Old Verilator - Clang
|
||||
needs: start
|
||||
if: ${{ needs.start.outputs.old-sha != '' }}
|
||||
uses: ./.github/workflows/reusable-rtlmeter-build.yml
|
||||
uses: ./.github/workflows/reusable-build.yml
|
||||
with:
|
||||
runs-on: ubuntu-24.04
|
||||
cc: clang
|
||||
ccwarn: false
|
||||
install: true
|
||||
runs-on: ubuntu-24.04
|
||||
sha: ${{ needs.start.outputs.old-sha }}
|
||||
|
||||
run-gcc:
|
||||
|
|
@ -208,7 +216,7 @@ jobs:
|
|||
run: echo "tags=$(jq -r 'keys | map(sub("^run-"; "")) | join(" ")' <<< '${{ toJSON(needs) }}')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Checkout RTLMeter
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
repository: "verilator/rtlmeter"
|
||||
path: rtlmeter
|
||||
|
|
@ -298,7 +306,7 @@ jobs:
|
|||
repositories: verilator-rtlmeter-results
|
||||
permission-contents: write
|
||||
- name: Checkout verilator-rtlmeter-results
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
repository: "verilator/verilator-rtlmeter-results"
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
|
|
@ -331,7 +339,7 @@ jobs:
|
|||
actions: read
|
||||
steps:
|
||||
- name: Checkout RTLMeter
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
repository: "verilator/rtlmeter"
|
||||
path: rtlmeter
|
||||
|
|
@ -341,7 +349,7 @@ jobs:
|
|||
run: make venv
|
||||
|
||||
- name: Checkout Verilator
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
path: verilator
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15)
|
|||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||
project(
|
||||
Verilator
|
||||
VERSION 5.050
|
||||
VERSION 5.051
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
|
@ -155,11 +155,12 @@ install(
|
|||
foreach(
|
||||
program
|
||||
verilator
|
||||
verilator_gantt
|
||||
verilator_ccache_report
|
||||
verilator_coverage
|
||||
verilator_difftree
|
||||
verilator_profcfunc
|
||||
verilator_gantt
|
||||
verilator_includer
|
||||
verilator_profcfunc
|
||||
)
|
||||
install(PROGRAMS bin/${program} TYPE BIN)
|
||||
endforeach()
|
||||
|
|
|
|||
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!
|
||||
|
||||
|
||||
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
|
||||
==========================
|
||||
|
||||
|
|
@ -5738,7 +5857,7 @@ Verilator 3.104 2003-04-30
|
|||
**Major:**
|
||||
|
||||
* Indicate direction of ports with VL_IN and VL_OUT.
|
||||
* Allow $c32, etc, to specify width of the $c statement for VCS.
|
||||
* Allow $c32, etc, to specify width of the $c statement.
|
||||
* Numerous performance improvements, worth about 25%
|
||||
|
||||
**Minor:**
|
||||
|
|
|
|||
|
|
@ -536,6 +536,7 @@ PY_PROGRAMS = \
|
|||
src/vlcovgen \
|
||||
test_regress/*.py \
|
||||
test_regress/t/*.pf \
|
||||
test_regress/t/randomize_solver_tamper.py \
|
||||
|
||||
# Python files, subject to format but not lint
|
||||
PY_FILES = \
|
||||
|
|
|
|||
|
|
@ -636,6 +636,7 @@ description of these arguments.
|
|||
|
||||
=for VL_SPHINX_EXTRACT "_build/gen/args_verilated.rst"
|
||||
|
||||
+verilator+assert+lock Lock assertion status changes at startup
|
||||
+verilator+coverage+file+<filename> Set coverage output filename
|
||||
+verilator+debug Enable debugging
|
||||
+verilator+debugi+<value> Enable debugging at a level
|
||||
|
|
|
|||
|
|
@ -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
|
||||
# DESCRIPTION: Verilator: CI dependency install script
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2020 Geza Lore
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
################################################################################
|
||||
|
|
@ -10,141 +10,168 @@
|
|||
# required by the particular build stage.
|
||||
################################################################################
|
||||
|
||||
# Installs system packages with sudo; never run locally
|
||||
if [ "$GITHUB_ACTIONS" != "true" ]; then
|
||||
echo "ERROR: $(basename "$0") must only be run in GitHub Actions CI" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
cd $(dirname "$0")/..
|
||||
|
||||
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."
|
||||
export PERL_LWP_SSL_VERIFY_HOSTNAME=0
|
||||
echo "check_certificate = off" >> ~/.wgetrc
|
||||
|
||||
fatal() {
|
||||
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
|
||||
if [ "$HOST_OS" = "linux" ]; then
|
||||
# Avoid slow "processing triggers for man db"
|
||||
echo "path-exclude /usr/share/doc/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
echo "path-exclude /usr/share/man/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
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
|
||||
|
||||
install-wavediff() {
|
||||
source ci/docker/buildenv/wavetools.conf
|
||||
local _base_url="https://github.com/hudson-trading/wavetools/releases/download/${WAVETOOLS_VERSION}"
|
||||
local _platform
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
if [ "$HOST_OS" = "linux" ]; then
|
||||
_platform="linux-x86_64"
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
elif [ "$HOST_OS" = "macOS" ]; then
|
||||
_platform="macos-arm64"
|
||||
elif [ "$CI_OS_NAME" = "windows" ]; then
|
||||
_platform="windows-x86_64"
|
||||
else
|
||||
echo "WARNING: No wavetools binary available for CI_OS_NAME=$CI_OS_NAME, skipping"
|
||||
echo "WARNING: No wavetools binary available for HOST_OS=$HOST_OS, skipping"
|
||||
return 0
|
||||
fi
|
||||
local _tmpdir
|
||||
_tmpdir=$(mktemp -d)
|
||||
local _archive="wavetools-${WAVETOOLS_VERSION}-${_platform}"
|
||||
if [ "$CI_OS_NAME" = "windows" ]; then
|
||||
wget -q -O "${_tmpdir}/${_archive}.zip" "${_base_url}/${_archive}.zip"
|
||||
unzip -o "${_tmpdir}/${_archive}.zip" -d "${_tmpdir}"
|
||||
else
|
||||
wget -q -O "${_tmpdir}/${_archive}.tar.gz" "${_base_url}/${_archive}.tar.gz"
|
||||
tar -xzf "${_tmpdir}/${_archive}.tar.gz" -C "${_tmpdir}"
|
||||
fi
|
||||
wget -q -O "${_tmpdir}/${_archive}.tar.gz" "${_base_url}/${_archive}.tar.gz"
|
||||
tar -xzf "${_tmpdir}/${_archive}.tar.gz" -C "${_tmpdir}"
|
||||
sudo cp "${_tmpdir}/${_archive}/wavediff" /usr/local/bin/wavediff
|
||||
rm -rf "${_tmpdir}"
|
||||
}
|
||||
|
||||
if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
if [ "$STAGE" = "build" ]; then
|
||||
##############################################################################
|
||||
# Dependencies of jobs in the 'build' stage, i.e.: packages required to
|
||||
# build Verilator
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
sudo apt-get update ||
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes ccache help2man libfl-dev ||
|
||||
sudo apt-get install --yes ccache help2man libfl-dev
|
||||
if [[ ! "$CI_RUNS_ON" =~ "ubuntu-22.04" ]]; then
|
||||
# Some conflict of libunwind verison on 22.04, can live without it for now
|
||||
sudo apt-get install --yes libjemalloc-dev ||
|
||||
sudo apt-get install --yes libjemalloc-dev
|
||||
fi
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev ||
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev
|
||||
if [ "$HOST_OS" = "linux" ]; then
|
||||
if [ "$DISTRO_ID" = "ubuntu" ]; then
|
||||
PACKAGES=(
|
||||
bear
|
||||
ccache
|
||||
help2man
|
||||
libfl-dev
|
||||
libsystemc-dev
|
||||
mold
|
||||
)
|
||||
# libunwind conflict on 22.04, can live without libjemalloc there
|
||||
if [ "$DISTRO_VERSION" != "22.04" ]; then
|
||||
PACKAGES+=(libjemalloc-dev)
|
||||
fi
|
||||
sudo apt-get update ||
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes "${PACKAGES[@]}" ||
|
||||
sudo apt-get install --yes "${PACKAGES[@]}"
|
||||
fi
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
sudo apt-get install --yes bear mold ||
|
||||
sudo apt-get install --yes bear mold
|
||||
fi
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
elif [ "$HOST_OS" = "macOS" ]; then
|
||||
PACKAGES=(
|
||||
autoconf
|
||||
bison
|
||||
ccache
|
||||
flex
|
||||
gperftools
|
||||
help2man
|
||||
perl
|
||||
)
|
||||
brew update ||
|
||||
brew update
|
||||
brew install ccache perl gperftools autoconf bison flex help2man ||
|
||||
brew install ccache perl gperftools autoconf bison flex help2man
|
||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
sudo pkg install -y autoconf bison ccache gmake perl5
|
||||
brew install "${PACKAGES[@]}" ||
|
||||
brew install "${PACKAGES[@]}"
|
||||
else
|
||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
||||
fatal "Unknown HOST_OS: '$HOST_OS'"
|
||||
fi
|
||||
|
||||
if [ -n "$CCACHE_DIR" ]; then
|
||||
mkdir -p "$CCACHE_DIR"
|
||||
fi
|
||||
elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
elif [ "$STAGE" = "test" ]; then
|
||||
##############################################################################
|
||||
# Dependencies of jobs in the 'test' stage, i.e.: packages required to
|
||||
# run the tests
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
sudo apt-get update ||
|
||||
sudo apt-get update
|
||||
# libfl-dev needed for internal coverage's test runs
|
||||
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3 ||
|
||||
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3
|
||||
# Required for test_regress/t/t_dist_attributes.py
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
sudo apt-get install --yes python3-clang mold ||
|
||||
sudo apt-get install --yes python3-clang mold
|
||||
if [ "$HOST_OS" = "linux" ]; then
|
||||
if [ "$DISTRO_ID" = "ubuntu" ]; then
|
||||
PACKAGES=(
|
||||
ccache
|
||||
gdb
|
||||
jq
|
||||
lcov
|
||||
libfl-dev
|
||||
libsystemc-dev
|
||||
mold
|
||||
python3-clang
|
||||
z3
|
||||
)
|
||||
sudo apt-get update ||
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes "${PACKAGES[@]}" ||
|
||||
sudo apt-get install --yes "${PACKAGES[@]}"
|
||||
fi
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev ||
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev
|
||||
fi
|
||||
fi
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
elif [ "$HOST_OS" = "macOS" ]; then
|
||||
PACKAGES=(
|
||||
ccache
|
||||
jq
|
||||
perl
|
||||
z3
|
||||
)
|
||||
brew update ||
|
||||
brew update
|
||||
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
|
||||
brew install ccache perl jq z3
|
||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
# fst2vcd fails with "Could not open '<input file>', exiting."
|
||||
sudo pkg install -y ccache gmake perl5 python3 jq z3
|
||||
brew install "${PACKAGES[@]}" ||
|
||||
brew install "${PACKAGES[@]}"
|
||||
else
|
||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
||||
fatal "Unknown HOST_OS: '$HOST_OS'"
|
||||
fi
|
||||
# Common installs
|
||||
install-wavediff
|
||||
# Workaround -fsanitize=address crash
|
||||
sudo sysctl -w vm.mmap_rnd_bits=28
|
||||
elif [ "$STAGE" = "lint-py" ]; then
|
||||
# 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
|
||||
##############################################################################
|
||||
# Unknown build stage
|
||||
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
|
||||
fatal "Unknown stage '$STAGE' (expected 'build', 'test' or 'lint-py')"
|
||||
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
|
||||
gh pr comment $(cat ${ARTIFACTS_DIR}/pr-number.txt) --body-file ${ARTIFACTS_DIR}/body.txt
|
||||
|
||||
# Get the artifact ID
|
||||
ARTIFACT_ID=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
|
||||
# Get the artifact IDs. Note there can be more than one artifact named
|
||||
# 'pr-notification' for a single run, as the artifacts endpoint lists
|
||||
# artifacts across all run attempts, and a re-run uploads a new one while
|
||||
# keeping the previous attempt's artifact.
|
||||
ARTIFACT_IDS=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
|
||||
|
||||
# Delete it, so we only notify once
|
||||
gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}"
|
||||
# Delete them all, so we only notify once
|
||||
for ARTIFACT_ID in ${ARTIFACT_IDS}; do
|
||||
gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}"
|
||||
done
|
||||
done
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
|
||||
$NPROC = $env:NUMBER_OF_PROCESSORS
|
||||
|
||||
# Absolute path for the win_flex_bison install; CMake reads this from the environment
|
||||
$env:WIN_FLEX_BISON = "$(Resolve-Path ..)/win_flex_bison"
|
||||
|
||||
if (-Not (Test-Path $env:WIN_FLEX_BISON/win_bison.exe)) {
|
||||
git clone --depth 1 https://github.com/lexxmark/winflexbison
|
||||
cd winflexbison
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. --install-prefix $PWD/../../../.ccache
|
||||
cmake --build . --config Release -j 3
|
||||
cmake --install . --prefix $PWD/../../../.ccache
|
||||
cmake .. --install-prefix $env:WIN_FLEX_BISON
|
||||
cmake --build . --config Release -j $NPROC
|
||||
cmake --install . --prefix $env:WIN_FLEX_BISON
|
||||
cd ../..
|
||||
}
|
||||
|
||||
# Enter the MSVC developer shell so cl/link are on PATH for the Ninja generator
|
||||
$VsPath = & "${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe" -latest -products * -property installationPath
|
||||
Import-Module "$VsPath/Common7/Tools/Microsoft.VisualStudio.DevShell.dll"
|
||||
Enter-VsDevShell -VsInstallPath $VsPath -SkipAutomaticLocation -DevCmdArguments "-arch=x64 -host_arch=x64"
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. --install-prefix $PWD/../install
|
||||
cmake --build . --config Release -j 3
|
||||
# Ninja saturates all cores; the MSBuild generator only parallelizes across
|
||||
# projects and Verilator is a single target, so it compiles serially. /Od skips
|
||||
# optimization: this job only checks that Verilator builds and can verilate an
|
||||
# example, so an optimized binary is not needed and the codegen time dominates.
|
||||
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release --install-prefix $PWD/../install "-DCMAKE_CXX_FLAGS_RELEASE=/Od /DNDEBUG"
|
||||
cmake --build .
|
||||
cmake --install . --prefix $PWD/../install
|
||||
|
|
|
|||
|
|
@ -7,13 +7,17 @@
|
|||
|
||||
Set-PSDebug -Trace 1
|
||||
|
||||
$NPROC = $env:NUMBER_OF_PROCESSORS
|
||||
|
||||
cd install
|
||||
$Env:VERILATOR_ROOT=$PWD
|
||||
cd examples/cmake_tracing_c
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --config Release -j 3
|
||||
# /Od skips optimization; this only checks the example verilates and builds, so
|
||||
# an optimized binary is not needed (see ci-win-compile.ps1).
|
||||
cmake .. "-DCMAKE_CXX_FLAGS_RELEASE=/Od /DNDEBUG"
|
||||
cmake --build . --config Release -j $NPROC
|
||||
|
||||
# TODO put this back in, see issue# 5163
|
||||
# Release/example.exe
|
||||
|
|
|
|||
44
configure.ac
44
configure.ac
|
|
@ -12,7 +12,7 @@
|
|||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.050 2026-07-01],
|
||||
AC_INIT([Verilator],[5.051 devel],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
|
|
@ -129,6 +129,21 @@ AC_ARG_ENABLE([dev-gcov],
|
|||
AC_SUBST(CFG_WITH_DEV_GCOV)
|
||||
AC_MSG_RESULT($CFG_WITH_DEV_GCOV)
|
||||
|
||||
# Flag to reduce the debug info in the debug executable to minimize its size
|
||||
AC_MSG_CHECKING(whether to reduce debug info in the debug executable)
|
||||
AC_ARG_ENABLE([light-debug],
|
||||
[AS_HELP_STRING([--enable-light-debug],
|
||||
[Reduce the amount of debug information in the debug
|
||||
Verilator executable to minimize its size. Enable
|
||||
slight optimization. Enough for backtraces only.])],
|
||||
[case "${enableval}" in
|
||||
yes) CFG_WITH_LIGHT_DEBUG=yes ;;
|
||||
no) CFG_WITH_LIGHT_DEBUG=no ;;
|
||||
*) AC_MSG_ERROR([bad value '${enableval}' for --enable-light-debug]) ;;
|
||||
esac],
|
||||
CFG_WITH_LIGHT_DEBUG=no)
|
||||
AC_MSG_RESULT($CFG_WITH_LIGHT_DEBUG)
|
||||
|
||||
# Special Substitutions - CFG_WITH_DEFENV
|
||||
AC_MSG_CHECKING(whether to use hardcoded paths)
|
||||
AC_ARG_ENABLE([defenv],
|
||||
|
|
@ -492,6 +507,11 @@ _MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-std=gnu++20)
|
|||
AC_SUBST(CFG_CXXFLAGS_COROUTINES)
|
||||
AC_SUBST(HAVE_COROUTINES)
|
||||
|
||||
# Check if the C++ compiler supports ThreadSanitizer
|
||||
_MY_CXX_CHECK_IFELSE(-fsanitize=thread,
|
||||
[AC_DEFINE([HAVE_TSAN],[1],[Defined if ThreadSanitizer is supported by $CXX])])
|
||||
AC_SUBST(HAVE_TSAN)
|
||||
|
||||
# Flags for compiling Verilator internals including parser always
|
||||
if test "$CFG_WITH_DEV_ASAN" = "yes"; then
|
||||
_MY_CXX_CHECK_IFELSE(-fsanitize=address -DVL_ASAN,
|
||||
|
|
@ -518,15 +538,27 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PARSER,-Wno-unused)
|
|||
AC_SUBST(CFG_CXXFLAGS_PARSER)
|
||||
|
||||
# Flags for compiling the debug version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
|
||||
if test "$CFG_WITH_DEV_GCOV" = "no"; then # Do not optimize for the coverage build
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-Og)
|
||||
if test "$CFG_WITH_LIGHT_DEBUG" = "yes"; then
|
||||
# Slight optimization and minimal compressed debug info. This is enough for
|
||||
# --gdb/--gdbbt backtraces, but omits the bulk of the debug info, which
|
||||
# significantly reduces object file sizes. For CI or release builds.
|
||||
if test "$CFG_WITH_DEV_GCOV" = "no"; then # Do not optimize for the coverage build
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-Og)
|
||||
fi
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-ggdb1)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-fdebug-info-for-profiling) # For fully-qualified names with clang
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-gz)
|
||||
else
|
||||
# Full debug: no optimization, full debug info, for development.
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-O0)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-ggdb)
|
||||
fi
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-ggdb)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-gz)
|
||||
AC_SUBST(CFG_CXXFLAGS_DBG)
|
||||
|
||||
# Flags for linking the debug version of Verilator (in addition to above CFG_LDFLAGS_SRC)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_DBG,-gz)
|
||||
if test "$CFG_WITH_LIGHT_DEBUG" = "yes"; then
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_DBG,-gz)
|
||||
fi
|
||||
AC_SUBST(CFG_LDFLAGS_DBG)
|
||||
|
||||
# Flags for compiling the optimized version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
24bit-xjkp
|
||||
404allen404
|
||||
Adam Bagley
|
||||
Adam Kostrzewski
|
||||
|
|
@ -14,6 +15,7 @@ Adrian Sampson
|
|||
Adrien Le Masle
|
||||
أحمد المحمودي (Ahmed El-Mahmoudy)
|
||||
Aidan McNay
|
||||
Aisha Salimgereyeva
|
||||
Aleksander Kiryk
|
||||
Alex Chadwick
|
||||
Alex Solomatnikov
|
||||
|
|
@ -26,16 +28,20 @@ Andrei Kostovski
|
|||
Andrew Miloradovsky
|
||||
Andrew Nolte
|
||||
Andrew Voznytsa
|
||||
Andrii Andrieiev
|
||||
anonkey
|
||||
Anthony Donlon
|
||||
Anthony Moore
|
||||
apocelipes
|
||||
Arkadiusz Kozdra
|
||||
Arthur Rosa
|
||||
Artur Bieniek
|
||||
AUDIY
|
||||
Aylon Chaim Porat
|
||||
Bartosz Skorowski
|
||||
Bartłomiej Chmiel
|
||||
Benjamin Collier
|
||||
BRDR LIFE
|
||||
Brian Li
|
||||
Cameron Kirk
|
||||
Cameron Waite
|
||||
|
|
@ -53,27 +59,35 @@ Dan Ruelas-Petrisko
|
|||
Daniel Bates
|
||||
Danny Oler
|
||||
Dave Sargeant
|
||||
David Garau
|
||||
David Horton
|
||||
David Ledger
|
||||
David Metz
|
||||
David Stanford
|
||||
David Turner
|
||||
dependabot[bot]
|
||||
Dercury
|
||||
Demin Han
|
||||
Diego Roux
|
||||
Dominick Grochowina
|
||||
Don Williamson
|
||||
Dragon-Git
|
||||
Drew Ranck
|
||||
Drew Taussig
|
||||
Driss Hafdi
|
||||
Edgar E. Iglesias
|
||||
em2machine
|
||||
emmettifelts
|
||||
Eric Mejdrich
|
||||
Eric Müller
|
||||
Eric Rippey
|
||||
Eunseo Song
|
||||
Eryk Szpotański
|
||||
Ethan Sifferman
|
||||
Eunseo Song
|
||||
Eyck Jentzsch
|
||||
Fabian Keßler-Schulz
|
||||
Fan Shupei
|
||||
february cozzocrea
|
||||
Felix Neumärker
|
||||
Felix Yan
|
||||
Frans Skarman
|
||||
|
|
@ -108,6 +122,8 @@ Ilya Barkov
|
|||
Iru Cai
|
||||
Ivan Vnučec
|
||||
Iztok Jeras
|
||||
JAEUK LEE
|
||||
Jaeuk Lee (이재욱)
|
||||
Jake Merdich
|
||||
Jakub Michalski
|
||||
Jakub Wasilewski
|
||||
|
|
@ -122,6 +138,8 @@ Jamie Iles
|
|||
Jan Van Winkel
|
||||
Jean Berniolles
|
||||
Jean-Nicolas Strauss
|
||||
Jeffrey Song
|
||||
jeffrey.song
|
||||
Jens Yuechao Liu
|
||||
Jeremy Bennett
|
||||
Jesse Taube
|
||||
|
|
@ -147,6 +165,7 @@ Josep Sans
|
|||
Joseph Nwabueze
|
||||
Josh Redford
|
||||
Joshua Leahy
|
||||
JOTEGO (Jose Tejada)
|
||||
Julian Carrier
|
||||
Julian Daube
|
||||
Julie Schwartz
|
||||
|
|
@ -183,6 +202,7 @@ Maarten De Braekeleer
|
|||
Maciej Sobkowski
|
||||
Marcel Chang
|
||||
Marco Bartoli
|
||||
Marco Brambilla
|
||||
Marco Widmer
|
||||
Mariusz Glebocki
|
||||
Markus Krause
|
||||
|
|
@ -208,6 +228,7 @@ Mladen Slijepcevic
|
|||
Morten Borup Petersen
|
||||
Mostafa Gamal
|
||||
Moubarak Jeje
|
||||
Muzaffer Kal
|
||||
Nandu Raj
|
||||
Natan Kreimer
|
||||
Nathan Graybeal
|
||||
|
|
@ -218,8 +239,10 @@ Nikolai Kumar
|
|||
Nikolay Puzanov
|
||||
Nolan Poe
|
||||
Oleh Maksymenko
|
||||
Patrick Creighton
|
||||
Patrick Stewart
|
||||
Paul Bowen-Huggett
|
||||
Paul Campbell
|
||||
Paul Swirhun
|
||||
Paul Wright
|
||||
Pawel Jewstafjew
|
||||
|
|
@ -262,15 +285,19 @@ Sergey Chusov
|
|||
Sergey Fedorov
|
||||
Sergi Granell
|
||||
Seth Pellegrino
|
||||
Shashvat Prabhu
|
||||
Shogo Yamazaki
|
||||
Shou-Li Hsu
|
||||
spomatasmd
|
||||
Srinivasan Venkataramanan
|
||||
Stefan Wallentowitz
|
||||
Stephen Henry
|
||||
Steven Hugg
|
||||
Stuart Morris
|
||||
sumpster
|
||||
Szymon Gizler
|
||||
Sören Tempel
|
||||
Sumanth Kadiyala
|
||||
Teng Huang
|
||||
Thomas Aldrian
|
||||
Thomas Brown
|
||||
|
|
@ -291,6 +318,7 @@ Tracy Narine
|
|||
Trung Nguyen
|
||||
Tudor Timi
|
||||
Tymoteusz Blazejczyk
|
||||
Tyrone Marhguy
|
||||
Udaya Raj Subedi
|
||||
Udi Finkelstein
|
||||
Unai Martinez-Corral
|
||||
|
|
@ -308,9 +336,11 @@ Wolfgang Mayerwieser
|
|||
Xi Zhang
|
||||
Yan Xu
|
||||
Yangyu Chen
|
||||
Yilin Li
|
||||
Yilou Wang
|
||||
Yinan Xu
|
||||
Yoda Lee
|
||||
Yogish Sekhar
|
||||
Yossi Nivin
|
||||
Yu-Sheng Lin
|
||||
Yuri Victorovich
|
||||
|
|
@ -321,17 +351,7 @@ Zhen Yan
|
|||
Zhou Shen
|
||||
Zhouyi Shen
|
||||
Zixi Li
|
||||
apocelipes
|
||||
dependabot[bot]
|
||||
february cozzocrea
|
||||
sumpster
|
||||
em2machine
|
||||
emmettifelts
|
||||
Zubin Jain
|
||||
Àlex Torregrosa
|
||||
Ícaro Lima
|
||||
Yogish Sekhar
|
||||
24bit-xjkp
|
||||
Zubin Jain
|
||||
Muzaffer Kal
|
||||
Yilin Li
|
||||
Shashvat Prabhu
|
||||
Sunimali Rathnayake
|
||||
|
|
|
|||
|
|
@ -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
|
||||
commented example.
|
||||
|
||||
Top level IO signals are read and written as members of the model. You call
|
||||
the model's ``eval()`` method to evaluate the model. When the simulation is
|
||||
complete call the model's ``final()`` method to execute any SystemVerilog
|
||||
final blocks, and complete any assertions. If using :vlopt:`--timing`,
|
||||
there are two additional functions for checking if there are any events
|
||||
pending in the simulation due to delays, and for retrieving the simulation
|
||||
time of the next delayed event. See :ref:`Evaluation Loop`.
|
||||
Top level IO signals are read and written as members of the model. All
|
||||
inputs must be sanitized, that is have no bits set above those
|
||||
corresponding to the width of the Verilog construct;
|
||||
:vlopt:`--runtime-debug` will assert this is correct.
|
||||
|
||||
Call the model's ``eval()`` method to evaluate the model. When the
|
||||
simulation is complete call the model's ``final()`` method to execute any
|
||||
SystemVerilog final blocks, and complete any assertions. If using
|
||||
:vlopt:`--timing`, there are two additional functions for checking if there
|
||||
are any events pending in the simulation due to delays, and for retrieving
|
||||
the simulation time of the next delayed event. See :ref:`Evaluation Loop`.
|
||||
|
||||
|
||||
Connecting to SystemC
|
||||
|
|
@ -179,7 +183,7 @@ DPI Example
|
|||
In the SYSTEMC example above, if you wanted to import C++ functions into
|
||||
Verilog, put in our.v:
|
||||
|
||||
.. code-block:: sv
|
||||
.. code-block::
|
||||
|
||||
import "DPI-C" function int add (input int a, input int b);
|
||||
|
||||
|
|
@ -211,7 +215,7 @@ Verilator extends the DPI format to allow using the same scheme to
|
|||
efficiently add system functions. Use a dollar-sign prefixed system
|
||||
function name for the import, but note it must be escaped.
|
||||
|
||||
.. code-block:: sv
|
||||
.. code-block::
|
||||
|
||||
import "DPI-C" function integer \$myRand;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,13 @@ Summary:
|
|||
|
||||
Options:
|
||||
|
||||
.. option:: +verilator+assert+lock
|
||||
|
||||
Only allow command line options to disable / enable assertions.
|
||||
Disables RTL from changing assertion handling via ``$asserton``,
|
||||
``$assertoff``, and ``$assertcontrol``. Also prevents ``VerilatedContext*``
|
||||
assertion control functions from updating assertion handling.
|
||||
|
||||
.. option:: +verilator+coverage+file+<filename>
|
||||
|
||||
When a model was Verilated using :vlopt:`--coverage`, sets the filename
|
||||
|
|
@ -114,7 +121,7 @@ Options:
|
|||
When a model was Verilated using :vlopt:`--x-initial unique
|
||||
<--x-initial>`, sets the simulation runtime initialization technique. 0
|
||||
= Reset to zeros. 1 = Reset to all-ones. 2 = Randomize. See
|
||||
:ref:`Unknown States`.
|
||||
:ref:`Unknown States`. Default is 0.
|
||||
|
||||
.. option:: +verilator+seed+<value>
|
||||
|
||||
|
|
|
|||
|
|
@ -715,6 +715,10 @@ Summary:
|
|||
|
||||
.. option:: -fno-dfg-break-cycles
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions before 5.052:
|
||||
|
||||
Rarely needed. Disable breaking combinational cycles during DFG.
|
||||
|
||||
.. option:: -fno-dfg-peephole
|
||||
|
|
@ -936,7 +940,7 @@ Summary:
|
|||
:file:`*.mk` files.
|
||||
|
||||
Feature may be one of the following: COROUTINES, DEV_ASAN, DEV_GCOV,
|
||||
SYSTEMC.
|
||||
SYSTEMC, TSAN.
|
||||
|
||||
.. option:: --getenv <variable>
|
||||
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ verilator_coverage Arguments
|
|||
|
||||
.. option:: --filter-type <regex>
|
||||
|
||||
Skips records of coverage types that matches with <regex>
|
||||
Keeps records of coverage types that matches with <regex>
|
||||
Possible values are `toggle`, `line`, `branch`, `expr`, `covergroup`,
|
||||
`user`, `fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The
|
||||
default value is `\*`.
|
||||
|
|
|
|||
|
|
@ -294,6 +294,25 @@ or "`ifdef`"'s may break other tools.
|
|||
(if appropriate :vlopt:`--coverage` flags are passed) after being
|
||||
disabled earlier with :option:`/*verilator&32;coverage_off*/`.
|
||||
|
||||
.. option:: /*verilator&32;dpi_c_decl "<C function declaration>"*/
|
||||
|
||||
Specifies C function declaration that will be emitted for given DPI-C
|
||||
function into the Verilator-generated __Dpi.h header, replacing the declaration
|
||||
that Verilator would build by default using the Verilog function signature.
|
||||
|
||||
This enables use of C functions with types not specified in the
|
||||
standard. For example, it enables use of functions that return ``char*``:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
module t;
|
||||
import "DPI-C" function string getenv(input string arg) /*verilator dpi_c_decl "char* getenv(const char*)"*/;
|
||||
|
||||
initial begin
|
||||
$display("%s", getenv("HOME"));
|
||||
end
|
||||
endmodule
|
||||
|
||||
.. option:: /*verilator&32;fargs <arguments>*/
|
||||
|
||||
For Verilator developers only. When a source file containing these `fargs`
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ The information in this report is:
|
|||
|
||||
.. describe:: "allocated 123 MB"
|
||||
|
||||
Total memory used during simulation in megabytes.
|
||||
Peak resident memory used during simulation in megabytes.
|
||||
|
||||
|
||||
.. _benchmarking & optimization:
|
||||
|
|
|
|||
|
|
@ -623,5 +623,5 @@ The information in this report is:
|
|||
|
||||
.. describe:: "allocated 123 MB"
|
||||
|
||||
Total memory used during build by Verilator executable (excludes
|
||||
:vlopt:`--build` compiler's usage) in megabytes.
|
||||
Peak resident memory used by the Verilator executable during build
|
||||
(excludes :vlopt:`--build` compiler's usage) in megabytes.
|
||||
|
|
|
|||
|
|
@ -1469,11 +1469,50 @@ List Of Warnings
|
|||
q <= d;
|
||||
end
|
||||
|
||||
A further case is when a signal named as a clocking block ``output`` is
|
||||
also driven by a continuous assignment, or is named as an ``output`` of a
|
||||
second clocking block. The clocking block drives the signal, so the design
|
||||
and the testbench contend for it and the synchronous drive may be silently
|
||||
lost. Declare the clocking block ``input`` if the intent is only to
|
||||
observe the signal.
|
||||
|
||||
Ignoring this warning may hide clock domain crossing, timing, or
|
||||
portability bugs. It may also cause longer simulation runtimes due to
|
||||
reduced optimizations.
|
||||
|
||||
|
||||
.. option:: MULTIDRIVENPROC
|
||||
|
||||
Warns that the whole of a variable is driven by more than one plain
|
||||
``always`` block. Unlike the :option:`MULTIDRIVEN` cases, plain
|
||||
``always`` blocks carry no ``always_comb``/``always_ff`` intent, so this
|
||||
is legal SystemVerilog rather than an IEEE 1800 violation. It is,
|
||||
however, typically a synthesis error: hardware cannot have a signal
|
||||
driven by two separate sequential blocks, so the design usually will not
|
||||
behave as the RTL simulation suggests.
|
||||
|
||||
Disabled by default as this is a code-style warning; it will simulate
|
||||
correctly.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. include:: ../../docs/gen/ex_MULTIDRIVENPROC_faulty.rst
|
||||
|
||||
Results in:
|
||||
|
||||
.. include:: ../../docs/gen/ex_MULTIDRIVENPROC_msg.rst
|
||||
|
||||
Also warns when a signal named as a clocking block ``output`` is driven
|
||||
by a plain ``always`` block. Driving a signal from both a clocking block
|
||||
and a plain ``always`` block is a deliberate idiom in some testbenches,
|
||||
so it is reported as MULTIDRIVENPROC rather than under the on-by-default
|
||||
:option:`MULTIDRIVEN`.
|
||||
|
||||
To fix, drive the signal from a single ``always`` block, or use
|
||||
``always_ff``/``always_comb`` if the intent is a single specialized
|
||||
process.
|
||||
|
||||
|
||||
.. option:: MULTITOP
|
||||
|
||||
.. TODO better example
|
||||
|
|
@ -2089,6 +2128,25 @@ List Of Warnings
|
|||
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
|
||||
|
||||
Warns that Verilator does not support certain constructs in
|
||||
|
|
|
|||
|
|
@ -477,6 +477,7 @@ Syms
|
|||
Synopsys
|
||||
SystemC
|
||||
SystemVerilog
|
||||
Szpotanski
|
||||
Takatsukasa
|
||||
Tambe
|
||||
Tarik
|
||||
|
|
@ -537,6 +538,7 @@ Verilog
|
|||
Vighnesh
|
||||
Viktor
|
||||
Vilp
|
||||
VlRNG
|
||||
VlWide
|
||||
Vlip
|
||||
Vm
|
||||
|
|
@ -644,6 +646,7 @@ casez
|
|||
casted
|
||||
castro
|
||||
cb
|
||||
cbValueChange
|
||||
ccache
|
||||
ccall
|
||||
cdc
|
||||
|
|
@ -707,6 +710,7 @@ de
|
|||
dearray
|
||||
deassign
|
||||
debugi
|
||||
deduplication
|
||||
defenv
|
||||
defname
|
||||
defparam
|
||||
|
|
@ -947,6 +951,7 @@ misconnected
|
|||
misconversion
|
||||
misdetecting
|
||||
misoptimized
|
||||
misoptimizing
|
||||
missized
|
||||
mk
|
||||
mno
|
||||
|
|
@ -1174,6 +1179,7 @@ tcmalloc
|
|||
tcmalloc
|
||||
tenghtt
|
||||
testbench
|
||||
testbenches
|
||||
threadsafe
|
||||
threashold
|
||||
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
|
||||
|
||||
void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE {
|
||||
VerilatedContext* const contextp = Verilated::threadContextp();
|
||||
contextp->finishPendingInc();
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||
vl_finish(filename, linenum, hier);
|
||||
contextp->finishPendingDec();
|
||||
}});
|
||||
}
|
||||
|
||||
void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
|
||||
// Classify now, so a queued request is pending from the moment it is posted
|
||||
VerilatedContext* const contextp = Verilated::threadContextp();
|
||||
const bool stop = contextp->stopRequestReserve(maybe);
|
||||
if (stop) contextp->finishPendingInc();
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||
vl_stop_maybe(filename, linenum, hier, maybe);
|
||||
contextp->stopRequestRelease();
|
||||
if (stop) contextp->finishPendingDec();
|
||||
}});
|
||||
}
|
||||
|
||||
void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
|
||||
VerilatedContext* const contextp = Verilated::threadContextp();
|
||||
contextp->finishPendingInc();
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||
vl_fatal(filename, linenum, hier, msg);
|
||||
contextp->finishPendingDec();
|
||||
}});
|
||||
}
|
||||
|
||||
|
|
@ -563,9 +575,10 @@ IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE {
|
|||
}
|
||||
|
||||
IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
|
||||
if (Verilated::threadContextp()->randReset() == 0) return 0;
|
||||
const int randReset = Verilated::threadContextp()->randReset();
|
||||
if (randReset == 0) return 0;
|
||||
IData data = ~0;
|
||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
||||
if (randReset != 1) { // if 2, randomize
|
||||
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
||||
data = rng.rand64();
|
||||
}
|
||||
|
|
@ -574,9 +587,10 @@ IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT
|
|||
}
|
||||
|
||||
QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
|
||||
if (Verilated::threadContextp()->randReset() == 0) return 0;
|
||||
const int randReset = Verilated::threadContextp()->randReset();
|
||||
if (randReset == 0) return 0;
|
||||
QData data = ~0ULL;
|
||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
||||
if (randReset != 1) { // if 2, randomize
|
||||
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
||||
data = rng.rand64();
|
||||
}
|
||||
|
|
@ -586,10 +600,17 @@ QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT
|
|||
|
||||
WDataOutP VL_SCOPED_RAND_RESET_W(int obits, WDataOutP outwp, uint64_t scopeHash,
|
||||
uint64_t salt) VL_MT_UNSAFE {
|
||||
if (Verilated::threadContextp()->randReset() != 2) { return VL_RAND_RESET_W(obits, outwp); }
|
||||
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
||||
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = rng.rand64();
|
||||
outwp[VL_WORDS_I(obits) - 1] = rng.rand64() & VL_MASK_E(obits);
|
||||
const int words = VL_WORDS_I(obits);
|
||||
const int randReset = Verilated::threadContextp()->randReset();
|
||||
if (randReset == 0) {
|
||||
VL_MEMSET_ZERO_W(outwp, words);
|
||||
} else if (randReset == 1) {
|
||||
VL_MEMSET_ONES_W(outwp, words);
|
||||
} else {
|
||||
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
|
||||
for (int i = 0; i < words; ++i) outwp[i] = rng.rand64();
|
||||
}
|
||||
outwp[words - 1] &= VL_MASK_E(obits);
|
||||
return outwp;
|
||||
}
|
||||
|
||||
|
|
@ -614,30 +635,14 @@ WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint64_t sco
|
|||
}
|
||||
|
||||
IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
|
||||
if (Verilated::threadContextp()->randReset() == 0) return 0;
|
||||
const int randReset = Verilated::threadContextp()->randReset();
|
||||
if (randReset == 0) return 0;
|
||||
IData data = ~0;
|
||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
||||
data = VL_RANDOM_I();
|
||||
}
|
||||
if (randReset != 1) data = VL_RANDOM_I(); // if 2, randomize
|
||||
data &= VL_MASK_I(obits);
|
||||
return data;
|
||||
}
|
||||
|
||||
QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
|
||||
if (Verilated::threadContextp()->randReset() == 0) return 0;
|
||||
QData data = ~0ULL;
|
||||
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
|
||||
data = VL_RANDOM_Q();
|
||||
}
|
||||
data &= VL_MASK_Q(obits);
|
||||
return data;
|
||||
}
|
||||
|
||||
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_I(32);
|
||||
outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_I(32) & VL_MASK_E(obits);
|
||||
return outwp;
|
||||
}
|
||||
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
// Not inlined to speed up compilation of slowpath code
|
||||
return VL_ZERO_W(obits, outwp);
|
||||
|
|
@ -3087,6 +3092,7 @@ VerilatedContext::Serialized::Serialized() {
|
|||
|
||||
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
|
||||
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
||||
if (assertCtlsLocked()) return;
|
||||
// Set all assert and directive types when true, clear otherwise.
|
||||
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
|
||||
}
|
||||
|
|
@ -3105,16 +3111,22 @@ uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
|
|||
}
|
||||
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
if (assertCtlsLocked()) return;
|
||||
m_s.m_assertOn |= assertOnMask(types, directives);
|
||||
}
|
||||
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
if (assertCtlsLocked()) return;
|
||||
m_s.m_assertOn &= ~assertOnMask(types, directives);
|
||||
}
|
||||
bool VerilatedContext::assertCtlsLocked() const VL_MT_SAFE { return m_ns.m_assertCtlsLocked; }
|
||||
void VerilatedContext::assertCtlsLocked(bool flag) VL_MT_SAFE { m_ns.m_assertCtlsLocked = flag; }
|
||||
void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the
|
||||
// selected bits until Unlock; On/Off/Kill leave locked bits unchanged.
|
||||
// +verilator+assert+lock freezes everything, including Lock/Unlock itself.
|
||||
if (assertCtlsLocked()) return;
|
||||
const uint32_t mask = assertOnMask(types, directives);
|
||||
const uint32_t lockedMask = mask & ~m_s.m_assertLock;
|
||||
switch (controlType) {
|
||||
|
|
@ -3286,6 +3298,15 @@ void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
|
|||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_gotFinish = flag;
|
||||
}
|
||||
bool VerilatedContext::stopRequestReserve(bool maybe) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
const int reserved = ++m_ns.m_stopReserved;
|
||||
return !maybe || m_s.m_errorCount + reserved >= m_s.m_errorLimit;
|
||||
}
|
||||
void VerilatedContext::stopRequestRelease() VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
--m_ns.m_stopReserved;
|
||||
}
|
||||
bool VerilatedContext::executingFinal() const VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_executingFinal;
|
||||
|
|
@ -3556,7 +3577,9 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
|||
if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
|
||||
std::string str;
|
||||
uint64_t u64;
|
||||
if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
|
||||
if (arg == "+verilator+assert+lock") {
|
||||
assertCtlsLocked(true);
|
||||
} else if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
|
||||
coverageFilename(str);
|
||||
} else if (arg == "+verilator+debug") {
|
||||
Verilated::debug(4);
|
||||
|
|
@ -3575,7 +3598,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
|||
logFilename(str);
|
||||
logOutputToFile(false /* append */);
|
||||
} else if (arg == "+verilator+noassert") {
|
||||
assertOn(false);
|
||||
// Set directly on to avoid conflicts with +verilator+assert+lock
|
||||
m_s.m_assertOn = 0;
|
||||
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+start+", u64)) {
|
||||
profExecStart(u64);
|
||||
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)) {
|
||||
|
|
@ -3891,7 +3915,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
|
|||
// When running internal code coverage (gcc --coverage, as opposed to
|
||||
// verilator --coverage), dump coverage data to properly cover failing
|
||||
// tests.
|
||||
VL_GCOV_DUMP();
|
||||
VL_GCOV_DUMP_RESET();
|
||||
}
|
||||
|
||||
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { addCbExit(cb, datap); }
|
||||
|
|
@ -4142,6 +4166,50 @@ VerilatedVar* VerilatedScope::varInsert(const char* namep, void* datap, bool isP
|
|||
return &(m_varsp->find(namep)->second);
|
||||
}
|
||||
|
||||
void VerilatedScope::varsInsertFromTable(const VlVarTableEntry* entp, size_t n,
|
||||
void* basep) VL_MT_UNSAFE {
|
||||
// Table-driven equivalent of a run of varInsert()/varInsertSized() calls; see VlVarTableEntry.
|
||||
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
|
||||
uint8_t* const base = static_cast<uint8_t*>(basep);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const VlVarTableEntry& e = entp[i];
|
||||
void* const datap = base + e.byteOffset;
|
||||
const VerilatedVarFlags vlflags = static_cast<VerilatedVarFlags>(e.vlflags);
|
||||
VerilatedVar var{e.namep, datap, e.vltype, vlflags, e.udims, e.pdims, /*isParam=*/false};
|
||||
for (int d = 0; d < e.udims; ++d) {
|
||||
var.m_unpacked[d].m_left = e.dims[2 * d];
|
||||
var.m_unpacked[d].m_right = e.dims[2 * d + 1];
|
||||
}
|
||||
for (int d = 0; d < e.pdims; ++d) {
|
||||
var.m_packed[d].m_left = e.dims[2 * (e.udims + d)];
|
||||
var.m_packed[d].m_right = e.dims[2 * (e.udims + d) + 1];
|
||||
}
|
||||
// Recompute the flattened DPI packed range now dims are known (see
|
||||
// VerilatedVarProps::initPacked)
|
||||
if (e.pdims == 1) {
|
||||
var.m_packedDpi = var.m_packed.front();
|
||||
} else if (e.pdims > 1) {
|
||||
int packedSize = 1;
|
||||
for (int d = 0; d < e.pdims; ++d) packedSize *= var.m_packed[d].elements();
|
||||
var.m_packedDpi = VerilatedRange{packedSize - 1, 0};
|
||||
}
|
||||
m_varsp->emplace(e.namep, std::move(var));
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedScope::scopesConstructFromTable(const VlScopeTableEntry* entp, size_t n,
|
||||
VerilatedSyms* symsp) VL_MT_UNSAFE {
|
||||
// Table-driven equivalent of a run of 'new VerilatedScope{...}' statements; see
|
||||
// VlScopeTableEntry. The generated Syms class derives VerilatedSyms as its sole primary
|
||||
// base at offset 0, so symsp doubles as the base for the offsetof-baked member addresses.
|
||||
uint8_t* const base = reinterpret_cast<uint8_t*>(symsp);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const VlScopeTableEntry& e = entp[i];
|
||||
VerilatedScope** const slotp = reinterpret_cast<VerilatedScope**>(base + e.ptrOffset);
|
||||
*slotp = new VerilatedScope{symsp, e.namep, e.identp, e.defnamep, e.timeunit, e.type};
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
|
||||
VerilatedVarType vltype, int vlflags, int udims,
|
||||
uint32_t entSize...) VL_MT_UNSAFE {
|
||||
|
|
|
|||
|
|
@ -160,6 +160,21 @@ enum VerilatedVarFlags : uint32_t {
|
|||
VLVF_NET = (1 << 15) // Net object
|
||||
};
|
||||
|
||||
// One VPI-visible variable, consumed by VerilatedScope::varsInsertFromTable();
|
||||
// replaces per-variable varInsert() calls, which compiles faster at scale.
|
||||
struct VlVarTableEntry final {
|
||||
static constexpr int kMaxDims = 3; // Max packed+unpacked dims a table row holds
|
||||
const char* namep; // VPI-facing (protected) variable name, string literal
|
||||
size_t byteOffset; // offsetof of storage member from module instance base
|
||||
VerilatedVarType vltype;
|
||||
uint32_t vlflags; // Direction + flags (VLVD_*/VLVF_*)
|
||||
uint8_t udims; // udims + pdims <= kMaxDims
|
||||
uint8_t pdims;
|
||||
// (left,right) pairs: unpacked dims first, then packed; int32_t since large
|
||||
// unpacked memories exceed int16 range
|
||||
int32_t dims[kMaxDims * 2];
|
||||
};
|
||||
|
||||
// IEEE 1800-2023 Table 20-6
|
||||
enum class VerilatedAssertType : uint8_t {
|
||||
ASSERT_TYPE_CONCURRENT = (1 << 0),
|
||||
|
|
@ -372,6 +387,8 @@ private:
|
|||
static uint32_t assertOnMask(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_PURE;
|
||||
static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1;
|
||||
// No termination request has stamped m_finishPendingTime yet
|
||||
static constexpr uint64_t TIME_UNSET = ~0ULL;
|
||||
|
||||
protected:
|
||||
// TYPES
|
||||
|
|
@ -430,6 +447,12 @@ protected:
|
|||
struct NonSerialized final { // Non-serialized information
|
||||
// These are reloaded from on command-line settings, so do not need to persist
|
||||
// Fast path
|
||||
// A worker queues $finish before the main thread callback can set m_gotFinish.
|
||||
std::atomic<uint32_t> m_finishPending{0}; // Number of queued $finish callbacks
|
||||
std::atomic<uint64_t> m_finishPendingTime{TIME_UNSET}; // Time of the first callback
|
||||
std::atomic<bool> m_assertCtlsLocked{
|
||||
false}; // When true, all assertion-control updates are ignored
|
||||
int m_stopReserved = 0; // Posted $stop requests not yet executed
|
||||
bool m_executingFinal = false; // Running generated final() code
|
||||
uint64_t m_profExecStart = 1; // +prof+exec+start time
|
||||
uint32_t m_profExecWindow = 2; // +prof+exec+window size
|
||||
|
|
@ -510,6 +533,12 @@ public:
|
|||
/// Clear enabled status for given assertion types
|
||||
void assertOnClear(VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||
/// Return if assertion-control updates are locked. When locked, RTL assert
|
||||
// control statements ($asserton/$assertoff/$assertcontrol) are ignored, as
|
||||
// are updates from the C++ API.
|
||||
bool assertCtlsLocked() const VL_MT_SAFE;
|
||||
/// Lock/unlock assertion-control updates.
|
||||
void assertCtlsLocked(bool flag) VL_MT_SAFE;
|
||||
/// Apply assertion control for given control, assertion, and directive types
|
||||
void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||
|
|
@ -669,6 +698,27 @@ public:
|
|||
|
||||
// METHODS - public but for internal use only
|
||||
|
||||
// Internal: Track $finish/$stop callbacks queued by worker threads
|
||||
bool finishPending() const VL_MT_SAFE { return m_ns.m_finishPending.load() != 0; }
|
||||
void finishPendingInc() VL_MT_SAFE {
|
||||
++m_ns.m_finishPending;
|
||||
uint64_t unset = TIME_UNSET;
|
||||
m_ns.m_finishPendingTime.compare_exchange_strong(unset, time());
|
||||
}
|
||||
void finishPendingDec() VL_MT_SAFE {
|
||||
const uint32_t previous = m_ns.m_finishPending.fetch_sub(1);
|
||||
assert(previous > 0);
|
||||
if (previous == 1 && !gotFinish()) m_ns.m_finishPendingTime = TIME_UNSET;
|
||||
}
|
||||
// Internal: Time of the first termination request, else the current time
|
||||
uint64_t finishPendingTime() const VL_MT_SAFE {
|
||||
const uint64_t stamped = m_ns.m_finishPendingTime.load();
|
||||
return stamped == TIME_UNSET ? time() : stamped;
|
||||
}
|
||||
// Internal: Reserve a posted $stop, returning true if it reaches the termination limit
|
||||
bool stopRequestReserve(bool maybe) VL_MT_SAFE;
|
||||
void stopRequestRelease() VL_MT_SAFE;
|
||||
|
||||
// Internal: access to implementation class
|
||||
VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); }
|
||||
const VerilatedContextImp* impp() const VL_MT_SAFE {
|
||||
|
|
@ -756,6 +806,8 @@ public: // But for internal use only
|
|||
// Verilator scope information class
|
||||
// Used for internal VPI implementation, and introspection into scopes
|
||||
|
||||
struct VlScopeTableEntry; // Defined below VerilatedScope; used by scopesConstructFromTable()
|
||||
|
||||
class VerilatedScope final {
|
||||
public:
|
||||
enum Type : uint8_t {
|
||||
|
|
@ -792,6 +844,9 @@ public: // But internals only - called from verilated modules, VerilatedSyms
|
|||
void* forceReadSignalData, const char* forceReadSignalName,
|
||||
std::pair<VerilatedVar*, VerilatedVar*> forceControlSignals,
|
||||
int udims, int pdims...) VL_MT_UNSAFE;
|
||||
void varsInsertFromTable(const VlVarTableEntry* entp, size_t n, void* basep) VL_MT_UNSAFE;
|
||||
static void scopesConstructFromTable(const VlScopeTableEntry* entp, size_t n,
|
||||
VerilatedSyms* symsp) VL_MT_UNSAFE;
|
||||
// ACCESSORS
|
||||
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
|
||||
const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }
|
||||
|
|
@ -807,6 +862,17 @@ public: // But internals only - called from verilated modules, VerilatedSyms
|
|||
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 {
|
||||
public:
|
||||
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
|
||||
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
|
||||
# Compiler flags when creating a precompiled header
|
||||
CFG_CXXFLAGS_PCH = -x c++-header
|
||||
CFG_CXXFLAGS_PCH = -c -x c++-header
|
||||
# Compiler option to put in front of filename to read precompiled header
|
||||
CFG_CXXFLAGS_PCH_I = @CFG_CXXFLAGS_PCH_I@
|
||||
# Compiler's filename prefix for precompiled headers, .gch if clang, empty if GCC
|
||||
|
|
|
|||
|
|
@ -75,24 +75,24 @@ class VerilatedCovImp;
|
|||
ccontextp->_insertp("hier", name, __VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData,
|
||||
const IData oldData) {
|
||||
inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData,
|
||||
const IData oldData) {
|
||||
const IData chgData = newData ^ oldData;
|
||||
for (int i = 0; i < width; ++i) {
|
||||
*(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const QData newData,
|
||||
const QData oldData) {
|
||||
inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const QData newData,
|
||||
const QData oldData) {
|
||||
const QData chgData = newData ^ oldData;
|
||||
for (int i = 0; i < width; ++i) {
|
||||
*(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData,
|
||||
WDataInP oldData) {
|
||||
inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData,
|
||||
WDataInP oldData) {
|
||||
for (int i = 0; i < VL_WORDS_I(width); ++i) {
|
||||
const EData chgData = newData[i] ^ oldData[i];
|
||||
if (chgData) {
|
||||
|
|
@ -104,8 +104,8 @@ static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WData
|
|||
}
|
||||
}
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic<uint32_t>* covp,
|
||||
const IData newData, const IData oldData) VL_MT_SAFE {
|
||||
inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic<uint32_t>* covp,
|
||||
const IData newData, const IData oldData) VL_MT_SAFE {
|
||||
const IData chgData = newData ^ oldData;
|
||||
for (int i = 0; i < width; ++i) {
|
||||
if (VL_BITISSET_I(chgData, i)) {
|
||||
|
|
@ -114,8 +114,8 @@ static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic<uint32_t>
|
|||
}
|
||||
}
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic<uint32_t>* covp,
|
||||
const QData newData, const QData oldData) VL_MT_SAFE {
|
||||
inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic<uint32_t>* covp,
|
||||
const QData newData, const QData oldData) VL_MT_SAFE {
|
||||
const QData chgData = newData ^ oldData;
|
||||
for (int i = 0; i < width; ++i) {
|
||||
if (VL_BITISSET_Q(chgData, i)) {
|
||||
|
|
@ -124,8 +124,8 @@ static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic<uint32_t>
|
|||
}
|
||||
}
|
||||
|
||||
static inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic<uint32_t>* covp,
|
||||
WDataInP newData, WDataInP oldData) VL_MT_SAFE {
|
||||
inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic<uint32_t>* covp, WDataInP newData,
|
||||
WDataInP oldData) VL_MT_SAFE {
|
||||
for (int i = 0; i < VL_WORDS_I(width); ++i) {
|
||||
const EData chgData = newData[i] ^ oldData[i];
|
||||
if (chgData) {
|
||||
|
|
|
|||
|
|
@ -41,9 +41,13 @@ enum class VlCovBinKind : uint8_t {
|
|||
|
||||
//=============================================================================
|
||||
// VlCoverpointIf
|
||||
/// Read-side view of a coverpoint. The writer queries bins by index; the
|
||||
/// implementor computes names/kinds on demand. Bounded bin count, so random
|
||||
/// access by index is the primary usage.
|
||||
/// Read-side view of a coverpoint -- a named, index-addressable set of bins
|
||||
/// with a coverage fraction. A cross is also a coverpoint from this view: its
|
||||
/// auto cross bins (one per element of the Cartesian product of the feeding
|
||||
/// coverpoints' Normal bins) are all Normal, and their names are built on
|
||||
/// demand by concatenating the feeding coverpoints' bin names. The writer
|
||||
/// queries bins by index; the implementor computes names/kinds on demand.
|
||||
/// Bounded bin count, so random access by index is the primary usage.
|
||||
|
||||
class VlCoverpointIf VL_NOT_FINAL {
|
||||
public:
|
||||
|
|
@ -51,11 +55,11 @@ public:
|
|||
virtual ~VlCoverpointIf() = default;
|
||||
|
||||
// METHODS
|
||||
// All bins, across every set; index range [0, binCount())
|
||||
virtual int binCount() const = 0;
|
||||
// Bin name in declaration order (e.g. "myBin" or "b[3]")
|
||||
virtual std::string binName(int i) const = 0;
|
||||
virtual VlCovBinKind binKind(int i) const = 0;
|
||||
// All bins, across every set; index range [0, binCount()).
|
||||
virtual uint32_t binCount() const = 0;
|
||||
// Bin name in declaration order (e.g. "myBin" or "b[3]"); for a cross,
|
||||
// the concatenated cross bin name (e.g. "b1_x_b2_x_b3")
|
||||
virtual std::string binName(uint32_t i) const = 0;
|
||||
// Bins covered / effective total (Normal set only) for the coverage calc
|
||||
virtual void coverageParts(double& covered, double& total) const = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,34 +23,57 @@
|
|||
|
||||
#include "verilated_covergroup.h"
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
// This file is compiled whenever covergroups are used, with or without
|
||||
// "verilator --coverage" (see V3Global::verilatedCppFiles). Bin counts are
|
||||
// members of the covergroup objects themselves, so sampling, bin naming, and
|
||||
// coverage queries such as get_inst_coverage() all work with no coverage
|
||||
// database present. VL_COVER_INSERT does not copy a count; it hands the
|
||||
// database the address of a counter to read at write time. Only that
|
||||
// publication step needs verilated_cov.cpp, which is compiled solely under
|
||||
// --coverage, so only the registerBins() bodies are gated on VM_COVERAGE.
|
||||
#if VM_COVERAGE
|
||||
#include "verilated_cov.h"
|
||||
#endif
|
||||
|
||||
void VlCoverpoint::init(const char* hier, uint32_t atLeast, int nBins) {
|
||||
void VlCoverpoint::init(const char* hier, uint32_t atLeast, uint32_t nBins) {
|
||||
m_hier = hier;
|
||||
m_atLeast = atLeast;
|
||||
m_total = nBins;
|
||||
m_counts.assign(nBins, 0);
|
||||
m_crossIdx.assign(nBins, -1);
|
||||
m_crossToBin.clear();
|
||||
}
|
||||
|
||||
void VlCoverpoint::addNamer(VlCovBinKind set, int count, VlCovBinNaming naming, const char* name,
|
||||
const char* file, int line, int col) {
|
||||
void VlCoverpoint::addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming,
|
||||
const char* name, const char* file, int line, int col) {
|
||||
m_namers.emplace_back(set, count, m_nextBase, naming, name, file, line, col);
|
||||
if (set == VlCovBinKind::KIND_NORMAL) {
|
||||
// Assign each Normal bin a cross index, and record the inverse map.
|
||||
for (uint32_t b = m_nextBase; b < m_nextBase + count; ++b) {
|
||||
m_crossIdx[b] = static_cast<int>(m_crossToBin.size());
|
||||
m_crossToBin.push_back(b);
|
||||
}
|
||||
m_normal += count;
|
||||
}
|
||||
m_nextBase += count;
|
||||
if (set == VlCovBinKind::KIND_NORMAL) m_normal += count;
|
||||
}
|
||||
|
||||
const VlCovNamer& VlCoverpoint::namerFor(int i) const {
|
||||
// Namers are appended in ascending, contiguous index order covering [0, m_total),
|
||||
// and i is always a valid bin index, so the matching namer always exists.
|
||||
std::string VlCoverpoint::normalBinName(uint32_t crossIdx) const {
|
||||
// Build the bin name based on the bin index
|
||||
return binName(m_crossToBin[crossIdx]);
|
||||
}
|
||||
|
||||
const VlCovNamer& VlCoverpoint::namerFor(uint32_t i) const {
|
||||
// Namers are appended in ascending order covering [0, m_total),
|
||||
for (const VlCovNamer& nm : m_namers) {
|
||||
if (i < nm.base() + nm.count()) return nm;
|
||||
}
|
||||
VL_UNREACHABLE;
|
||||
VL_UNREACHABLE; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
std::string VlCoverpoint::binName(int i) const {
|
||||
std::string VlCoverpoint::binName(uint32_t i) const {
|
||||
const VlCovNamer& nm = namerFor(i);
|
||||
std::string name = nm.name();
|
||||
if (nm.naming() == VlCovBinNaming::Array) name += '[' + std::to_string(i - nm.base()) + ']';
|
||||
|
|
@ -59,7 +82,7 @@ std::string VlCoverpoint::binName(int i) const {
|
|||
|
||||
#if VM_COVERAGE
|
||||
void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* page) {
|
||||
for (int i = 0; i < binCount(); ++i) {
|
||||
for (uint32_t i = 0; i < binCount(); ++i) {
|
||||
const VlCovNamer& nm = namerFor(i);
|
||||
const VlCovBinKind kind = binKind(i);
|
||||
const std::string binp = binName(i);
|
||||
|
|
@ -81,3 +104,89 @@ void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* pa
|
|||
}
|
||||
}
|
||||
#endif // VM_COVERAGE
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverCross
|
||||
|
||||
void VlCoverCross::init(const char* hier, uint32_t dims, VlCoverpoint* const* cps,
|
||||
const char* file, int line, int col) {
|
||||
m_hier = hier;
|
||||
m_file = file;
|
||||
m_line = line;
|
||||
m_col = col;
|
||||
m_dims = dims;
|
||||
m_cps.assign(cps, cps + dims);
|
||||
m_cpBinCounts.resize(dims);
|
||||
// Accumulate in 64 bits so the overflow check itself cannot overflow.
|
||||
uint64_t product = 1;
|
||||
for (uint32_t d = 0; d < dims; ++d) {
|
||||
m_cpBinCounts[d] = cps[d]->normalBinCount();
|
||||
product *= m_cpBinCounts[d];
|
||||
if (VL_UNLIKELY(product > UINT32_MAX)) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(file, line, "", "Cross has too many auto bins to represent");
|
||||
} // LCOV_EXCL_STOP
|
||||
}
|
||||
m_numAutoBins = static_cast<uint32_t>(product);
|
||||
// stride[d] = product of the Normal bin counts of all dimensions after d.
|
||||
// Counts down with an offset so the unsigned index never wraps below zero.
|
||||
m_stride.assign(dims, 1);
|
||||
for (uint32_t d = dims; d > 1; --d) m_stride[d - 2] = m_stride[d - 1] * m_cpBinCounts[d - 1];
|
||||
m_flatCounts.assign(m_numAutoBins, 0);
|
||||
}
|
||||
|
||||
void VlCoverCross::iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx) {
|
||||
const uint32_t hits = cps[dim]->hitCount();
|
||||
const uint32_t* const list = cps[dim]->hitList();
|
||||
const bool last = (dim == m_dims - 1);
|
||||
const uint32_t stride = m_stride[dim];
|
||||
for (uint32_t hit = 0; hit < hits; ++hit) {
|
||||
const uint32_t idx = baseIdx + list[hit] * stride;
|
||||
if (last) {
|
||||
incrementTuple(idx);
|
||||
} else {
|
||||
iterateProduct(cps, dim + 1, idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VlCoverCross::sample(VlCoverpoint* const* cps) {
|
||||
// Fast path: if any dimension had no Normal-bin hit, the cross cannot hit.
|
||||
for (uint32_t d = 0; d < m_dims; ++d) {
|
||||
if (cps[d]->hitCount() == 0) return;
|
||||
}
|
||||
iterateProduct(cps, 0, 0);
|
||||
}
|
||||
|
||||
std::string VlCoverCross::binName(uint32_t flat) const {
|
||||
// Built on demand by concatenating each coverpoint's own bin name.
|
||||
std::string name;
|
||||
for (uint32_t d = 0; d < m_dims; ++d) {
|
||||
const uint32_t crossIdx = (flat / m_stride[d]) % m_cpBinCounts[d];
|
||||
if (d > 0) name += "_x_";
|
||||
name += m_cps[d]->normalBinName(crossIdx);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
#if VM_COVERAGE
|
||||
void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* page) {
|
||||
// Register every auto cross bin (zero-count bins included), so the report
|
||||
// shows the full Cartesian product of cross bins. Names are built on the fly.
|
||||
const std::string lineStr = std::to_string(m_line);
|
||||
const std::string colStr = std::to_string(m_col);
|
||||
for (uint32_t flat = 0; flat < binCount(); ++flat) {
|
||||
const std::string bin = binName(flat); // "b1_x_b2_x_..."
|
||||
// cross_bins metadata: the same components joined by ',' (not read by the report)
|
||||
std::string crossBins;
|
||||
for (uint32_t d = 0; d < m_dims; ++d) {
|
||||
const uint32_t crossIdx = (flat / m_stride[d]) % m_cpBinCounts[d];
|
||||
if (d > 0) crossBins += ",";
|
||||
crossBins += m_cps[d]->normalBinName(crossIdx);
|
||||
}
|
||||
const std::string full = m_hier + "." + bin;
|
||||
VL_COVER_INSERT(covcontextp, full.c_str(), &m_flatCounts[flat], "page", page, "filename",
|
||||
m_file, "lineno", lineStr.c_str(), "column", colStr.c_str(), "bin",
|
||||
bin.c_str(), "cross", "1", "cross_bins", crossBins.c_str());
|
||||
}
|
||||
}
|
||||
#endif // VM_COVERAGE
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@
|
|||
/// it in the constructor (init + add*Namer), increments bins from sample(),
|
||||
/// and registers via registerBins().
|
||||
///
|
||||
/// Collection and coverage queries are always available; only registerBins(),
|
||||
/// which publishes bin counters to the coverage database, requires VM_COVERAGE.
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#ifndef VERILATOR_VERILATED_COVERGROUP_H_
|
||||
|
|
@ -49,8 +52,8 @@ enum class VlCovBinNaming : uint8_t {
|
|||
class VlCovNamer final {
|
||||
// MEMBERS
|
||||
VlCovBinKind m_set; // which set the bins belong to
|
||||
int m_count; // bins this namer covers (1 for Single)
|
||||
int m_base; // first bin index (declaration order), assigned on append
|
||||
uint32_t m_count; // bins this namer covers (1 for Single)
|
||||
uint32_t m_base; // first bin index (declaration order), assigned on append
|
||||
VlCovBinNaming m_naming; // how bin names are built
|
||||
const char* m_name; // bin name (Single) or array base name (Array)
|
||||
const char* m_file; // declaration file
|
||||
|
|
@ -59,8 +62,8 @@ class VlCovNamer final {
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCovNamer(VlCovBinKind set, int count, int base, VlCovBinNaming naming, const char* name,
|
||||
const char* file, int line, int col)
|
||||
VlCovNamer(VlCovBinKind set, uint32_t count, uint32_t base, VlCovBinNaming naming,
|
||||
const char* name, const char* file, int line, int col)
|
||||
: m_set{set}
|
||||
, m_count{count}
|
||||
, m_base{base}
|
||||
|
|
@ -72,8 +75,8 @@ public:
|
|||
|
||||
// METHODS
|
||||
VlCovBinKind set() const { return m_set; }
|
||||
int count() const { return m_count; }
|
||||
int base() const { return m_base; }
|
||||
uint32_t count() const { return m_count; }
|
||||
uint32_t base() const { return m_base; }
|
||||
VlCovBinNaming naming() const { return m_naming; }
|
||||
const char* name() const { return m_name; }
|
||||
const char* file() const { return m_file; }
|
||||
|
|
@ -87,19 +90,30 @@ public:
|
|||
/// bin's set/name come from the owning namer. coverage() is computed on demand
|
||||
/// by scanning bin counts, keeping the sample() hot path a plain counter bump.
|
||||
|
||||
class VlCoverpoint final : public VlCoverpointIf {
|
||||
// MEMBERS
|
||||
// Base coverpoint runtime (read side + collection logic, no hit-list storage).
|
||||
// VlCoverpointT<MaxHits> adds the inline hit-list array and the incrementBin write
|
||||
// path; the cross holds VlCoverpoint* and reads via hitCount()/hitList().
|
||||
class VlCoverpoint VL_NOT_FINAL : public VlCoverpointIf {
|
||||
protected:
|
||||
// MEMBERS (protected so VlCoverpointT::incrementBin can update them)
|
||||
std::string m_hier; // "covergroup.coverpoint"
|
||||
uint32_t m_atLeast = 1; // option.at_least (coverpoint-wide)
|
||||
int m_total = 0; // bins across all sets
|
||||
int m_normal = 0; // Normal bins (coverage denominator)
|
||||
int m_nextBase = 0; // running append cursor
|
||||
uint32_t m_total = 0; // bins across all sets
|
||||
uint32_t m_normal = 0; // Normal bins (coverage denominator)
|
||||
uint32_t m_nextBase = 0; // running append cursor
|
||||
std::vector<uint32_t> m_counts; // [m_total], one per bin
|
||||
std::vector<VlCovNamer> m_namers; // appended in declaration order
|
||||
// [m_total] full bin idx -> cross idx (Normal-only), -1 otherwise. The only
|
||||
// signed index here: -1 marks a non-Normal bin, which incrementBin filters on.
|
||||
std::vector<int> m_crossIdx;
|
||||
// [m_normal] inverse of m_crossIdx: cross idx -> full bin idx, appended in cross-index order
|
||||
std::vector<uint32_t> m_crossToBin;
|
||||
uint32_t m_hitCount = 0; // entries valid in the hit list this sample
|
||||
|
||||
private:
|
||||
// PRIVATE METHODS
|
||||
const VlCovNamer& namerFor(int i) const; // obtain the bin-specific name producer
|
||||
void addNamer(VlCovBinKind set, int count, VlCovBinNaming naming, const char* name,
|
||||
const VlCovNamer& namerFor(uint32_t i) const; // obtain the bin-specific name producer
|
||||
void addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming, const char* name,
|
||||
const char* file, int line, int col);
|
||||
|
||||
public:
|
||||
|
|
@ -108,31 +122,44 @@ public:
|
|||
|
||||
// METHODS
|
||||
// ---- configuration (from generated constructor) ----
|
||||
void init(const char* hier, uint32_t atLeast, int nBins);
|
||||
void init(const char* hier, uint32_t atLeast, uint32_t nBins);
|
||||
void addSingleNamer(VlCovBinKind set, const char* name, const char* file, int line, int col) {
|
||||
addNamer(set, 1, VlCovBinNaming::Single, name, file, line, col);
|
||||
}
|
||||
void addArrayNamer(VlCovBinKind set, int count, const char* name, const char* file, int line,
|
||||
int col) {
|
||||
void addArrayNamer(VlCovBinKind set, uint32_t count, const char* name, const char* file,
|
||||
int line, int col) {
|
||||
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
|
||||
}
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
|
||||
// ---- hot path (from generated sample()) ----
|
||||
void incrementBin(int i) { ++m_counts[i]; } // Normal bin: count only
|
||||
void recordHit(int i) { ++m_counts[i]; } // Ignore/Illegal/Default: count only
|
||||
// Clear the hit list at the start of each sample() for a cross-fed coverpoint.
|
||||
void clearHitList() { m_hitCount = 0; }
|
||||
// Ignore/Illegal/Default: count only; never propagates to cross coverage.
|
||||
void recordHit(uint32_t i) { ++m_counts[i]; }
|
||||
// incrementBin (Normal bin: count + hit-list append) lives in VlCoverpointT<MaxHits>,
|
||||
// where MaxHits is the gen-time max per-sample bin overlap.
|
||||
|
||||
// ---- cross support (read by VlCoverCross) ----
|
||||
uint32_t hitCount() const { return m_hitCount; }
|
||||
virtual const uint32_t* hitList() const = 0; // provided by VlCoverpointT
|
||||
uint32_t normalBinCount() const { return m_normal; } // cross dimension size (Normal bins)
|
||||
std::string normalBinName(uint32_t crossIdx) const; // name of the crossIdx-th Normal bin
|
||||
|
||||
// ---- VlCoverpointIf ----
|
||||
int binCount() const override { return m_total; }
|
||||
std::string binName(int i) const override;
|
||||
VlCovBinKind binKind(int i) const override { return namerFor(i).set(); }
|
||||
uint32_t binCount() const override { return m_total; }
|
||||
std::string binName(uint32_t i) const override;
|
||||
// Deliberately not on VlCoverpointIf: only registerBins() needs it, via the
|
||||
// concrete coverpoint. A cross has all-Normal bins and exposes no kind, so the
|
||||
// interface omits it; add it back only if a writer needs it polymorphically.
|
||||
VlCovBinKind binKind(uint32_t i) const { return namerFor(i).set(); }
|
||||
void coverageParts(double& covered, double& total) const override {
|
||||
// Count Normal bins that reached option.at_least on demand, so the hot
|
||||
// path (incrementBin) stays a plain counter bump.
|
||||
int numCovered = 0;
|
||||
uint32_t numCovered = 0;
|
||||
for (const VlCovNamer& nm : m_namers) {
|
||||
if (nm.set() != VlCovBinKind::KIND_NORMAL) continue;
|
||||
for (int i = nm.base(); i < nm.base() + nm.count(); ++i) {
|
||||
for (uint32_t i = nm.base(); i < nm.base() + nm.count(); ++i) {
|
||||
if (m_counts[i] >= m_atLeast) ++numCovered;
|
||||
}
|
||||
}
|
||||
|
|
@ -141,4 +168,90 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverpointT
|
||||
/// Concrete coverpoint with an inline hit-list array sized to MaxHits -- the
|
||||
/// gen-time maximum number of Normal bins one sample value can match (1 for the
|
||||
/// common non-overlapping case). The bound is a compile-time constant, so for
|
||||
/// MaxHits == 1 incrementBin collapses to a single store. Generated code holds
|
||||
/// the coverpoint as VlCoverpointT<K> and calls incrementBin via the concrete
|
||||
/// type; the cross reads it polymorphically through VlCoverpoint*.
|
||||
|
||||
template <uint32_t MaxHits>
|
||||
class VlCoverpointT final : public VlCoverpoint {
|
||||
// MEMBERS
|
||||
uint32_t m_hits[MaxHits]; // cross indices of Normal bins hit this sample
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCoverpointT() = default;
|
||||
|
||||
// METHODS
|
||||
// Normal bin: bump count and append the bin's cross index to the hit list.
|
||||
// m_hitCount can never exceed MaxHits (the gen-time overlap bound), so no hit
|
||||
// is ever dropped; the bound check is a compile-time-folded safety net.
|
||||
void incrementBin(uint32_t i) {
|
||||
++m_counts[i];
|
||||
// m_crossIdx is signed only to carry the -1 "not a Normal bin" marker;
|
||||
// the >= 0 test below is what makes every stored hit index unsigned-safe.
|
||||
const int cx = m_crossIdx[i];
|
||||
if (cx >= 0 && m_hitCount < MaxHits) m_hits[m_hitCount++] = static_cast<uint32_t>(cx);
|
||||
}
|
||||
const uint32_t* hitList() const override { return m_hits; }
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoverCross
|
||||
/// Per-instance auto cross runtime. Holds flat uint32_t[] storage over the
|
||||
/// Cartesian product of the feeding coverpoints' Normal bins. Each sample()
|
||||
/// walks the coverpoint hit lists (O(hits), not O(product)). Bin names are
|
||||
/// built on demand from the coverpoints, so no per-bin name is stored.
|
||||
|
||||
class VlCoverCross final : public VlCoverpointIf {
|
||||
// MEMBERS
|
||||
std::string m_hier; // "covergroup.cross"
|
||||
const char* m_file = nullptr; // Cross declaration file (registration metadata)
|
||||
int m_line = 0; // Cross declaration line
|
||||
int m_col = 0; // Cross declaration column
|
||||
uint32_t m_dims = 0; // Number of feeding coverpoints
|
||||
// Cross bin indexes are unsigned, like the coverpoint bin indexes they are
|
||||
// built from. init() fatals if the product would exceed UINT32_MAX, so every
|
||||
// index computed here provably fits. That bound is far beyond anything
|
||||
// storable anyway: m_flatCounts alone would need 16GB.
|
||||
uint32_t m_numAutoBins = 0; // Product of per-dim Normal bin counts
|
||||
uint32_t m_numCovered = 0; // Distinct bins hit >= 1 (maintained incrementally)
|
||||
std::vector<uint32_t> m_cpBinCounts; // [m_dims] Normal bin count per dimension
|
||||
std::vector<uint32_t> m_stride; // [m_dims] Flat-index stride per dimension
|
||||
std::vector<uint32_t> m_flatCounts; // [m_numAutoBins] Per-bin hit counts
|
||||
std::vector<VlCoverpoint*> m_cps; // Feeding coverpoints (the only name source)
|
||||
|
||||
// PRIVATE METHODS
|
||||
void iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx);
|
||||
void incrementTuple(uint32_t idx) {
|
||||
if (m_flatCounts[idx]++ == 0) ++m_numCovered;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlCoverCross() = default;
|
||||
|
||||
// METHODS
|
||||
// ---- configuration (from generated constructor, after coverpoints init'd) ----
|
||||
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
|
||||
int line, int col);
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
|
||||
// ---- hot path (from generated sample(), after all coverpoints sampled) ----
|
||||
void sample(VlCoverpoint* const* cps);
|
||||
|
||||
// ---- VlCoverpointIf ----
|
||||
// A cross is a coverpoint whose bins are the auto cross bins (all Normal).
|
||||
uint32_t binCount() const override { return m_numAutoBins; }
|
||||
std::string binName(uint32_t flat) const override;
|
||||
void coverageParts(double& covered, double& total) const override {
|
||||
covered = m_numCovered;
|
||||
total = m_numAutoBins;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -37,68 +37,68 @@
|
|||
// SETTING OPERATORS
|
||||
|
||||
// Convert svBitVecVal to Verilator internal data
|
||||
static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
||||
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_Q_SVBV(int obits, QData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_Q_SVBV(int obits, QData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_Q(obits) & VL_SET_QII(lwp[1], lwp[0]);
|
||||
}
|
||||
static inline void VL_SET_I_SVBV(int obits, IData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_I_SVBV(int obits, IData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0];
|
||||
}
|
||||
static inline void VL_SET_S_SVBV(int obits, SData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_S_SVBV(int obits, SData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0];
|
||||
}
|
||||
static inline void VL_SET_C_SVBV(int obits, CData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_C_SVBV(int obits, CData& out, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0];
|
||||
}
|
||||
|
||||
// Convert Verilator internal data to svBitVecVal
|
||||
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
||||
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; }
|
||||
static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE {
|
||||
inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; }
|
||||
inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE {
|
||||
VL_SET_WQ(WDataOutP::external(owp), ld);
|
||||
}
|
||||
|
||||
// Convert svLogicVecVal to Verilator internal data
|
||||
// Note these functions ignore X/Z in svLogicVecVal
|
||||
static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval;
|
||||
owp[words - 1] = lwp[words - 1].aval & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_Q_SVLV(int obits, QData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_Q_SVLV(int obits, QData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_Q(obits) & VL_SET_QII(lwp[1].aval, lwp[0].aval);
|
||||
}
|
||||
static inline void VL_SET_I_SVLV(int obits, IData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_I_SVLV(int obits, IData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0].aval;
|
||||
}
|
||||
static inline void VL_SET_S_SVLV(int obits, SData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_S_SVLV(int obits, SData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0].aval;
|
||||
}
|
||||
static inline void VL_SET_C_SVLV(int obits, CData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_C_SVLV(int obits, CData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
out = VL_MASK_I(obits) & lwp[0].aval;
|
||||
}
|
||||
|
||||
// Convert Verilator internal data to svLogicVecVal
|
||||
// Note these functions never create X/Z in svLogicVecVal
|
||||
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
|
||||
inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words; ++i) owp[i].bval = 0;
|
||||
for (int i = 0; i < words - 1; ++i) owp[i].aval = lwp[i];
|
||||
owp[words - 1].aval = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, const IData ld) VL_MT_SAFE {
|
||||
inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, const IData ld) VL_MT_SAFE {
|
||||
owp[0].aval = ld;
|
||||
owp[0].bval = 0;
|
||||
}
|
||||
static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, const QData ld) VL_MT_SAFE {
|
||||
inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, const QData ld) VL_MT_SAFE {
|
||||
VlWide<2> lwp;
|
||||
VL_SET_WQ(lwp, ld);
|
||||
owp[0].aval = lwp[0];
|
||||
|
|
|
|||
|
|
@ -98,8 +98,10 @@ void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
|||
const VerilatedLockGuard lock{m_mutex};
|
||||
Super::closeBase();
|
||||
emitTimeChangeMaybe();
|
||||
if (m_fst) m_fst->close(); // LCOV_EXCL_BR_LINE
|
||||
m_fst = nullptr;
|
||||
if (m_fst) {
|
||||
m_fst->close();
|
||||
VL_DO_CLEAR(delete m_fst, m_fst = nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -129,7 +129,7 @@ public:
|
|||
void wait_report() {
|
||||
if (m_pidExited) return;
|
||||
#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) {
|
||||
std::stringstream msg;
|
||||
msg << "Subprocess command `" << m_cmd[0];
|
||||
|
|
@ -208,8 +208,10 @@ public:
|
|||
// Child
|
||||
close(fd_stdin[P_WR]);
|
||||
dup2(fd_stdin[P_RD], STDIN_FILENO);
|
||||
close(fd_stdin[P_RD]);
|
||||
close(fd_stdout[P_RD]);
|
||||
dup2(fd_stdout[P_WR], STDOUT_FILENO);
|
||||
close(fd_stdout[P_WR]);
|
||||
execvp(cmd[0], const_cast<char* const*>(cmd));
|
||||
std::stringstream msg;
|
||||
msg << "VlRProcess::open: execvp(" << cmd[0] << ")";
|
||||
|
|
@ -441,11 +443,14 @@ void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits) {
|
|||
os << ')';
|
||||
}
|
||||
|
||||
size_t VlRandomizer::hashConstraints() const {
|
||||
size_t VlRandomizer::hashConstraints(const std::vector<std::string>& extras) const {
|
||||
size_t h = 0;
|
||||
for (const auto& c : m_constraints) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -478,36 +483,20 @@ void VlRandomizer::recordRandcValues() {
|
|||
}
|
||||
}
|
||||
|
||||
bool VlRandomizer::next_check_only(VlRNG& rngr) {
|
||||
m_checkOnly = true;
|
||||
const bool result = next(rngr);
|
||||
m_checkOnly = false;
|
||||
return result;
|
||||
}
|
||||
bool VlRandomizer::next_check_only(VlRNG& rngr) { return nextRandomize(rngr, true); }
|
||||
|
||||
bool VlRandomizer::next(VlRNG& rngr) {
|
||||
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);
|
||||
bool VlRandomizer::next(VlRNG& rngr) { return nextRandomize(rngr, false); }
|
||||
|
||||
if (it != m_vars.end()) {
|
||||
std::string distinctExpr = "(__Vbv (distinct";
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
char hexIdx[12];
|
||||
sprintf(hexIdx, "#x%08x", i);
|
||||
distinctExpr += " (select " + it->first + " " + hexIdx + ")";
|
||||
}
|
||||
distinctExpr += "))";
|
||||
m_constraints.push_back(distinctExpr);
|
||||
}
|
||||
}
|
||||
bool VlRandomizer::nextRandomize(VlRNG& rngr, bool checkOnly) {
|
||||
if (!checkOnly && m_vars.empty() && m_unique_arrays.empty()) return true;
|
||||
if (checkOnly && m_vars.empty()) return true; // No rand members: trivially SAT
|
||||
m_checkOnly = checkOnly;
|
||||
const std::vector<std::string> uniqueExprs = buildUniqueExprs();
|
||||
|
||||
// Randc exclusion-based cycling: exclude previously used values per randc var.
|
||||
// When solver returns unsat (all values exhausted), clear history for new cycle.
|
||||
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)
|
||||
if (currentHash != m_randcConstraintHash) {
|
||||
m_randcUsedValues.clear();
|
||||
|
|
@ -516,83 +505,103 @@ bool VlRandomizer::next(VlRNG& rngr) {
|
|||
}
|
||||
|
||||
// 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
|
||||
const bool hasRandc = !m_randcVarNames.empty();
|
||||
for (int attempt = 0; attempt < (hasRandc ? 2 : 1); ++attempt) {
|
||||
std::iostream& os = getSolver();
|
||||
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";
|
||||
// Lets the scalar pin path learn which free-bit assumptions conflict.
|
||||
os << "(set-option :produce-unsat-assumptions true)\n";
|
||||
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";
|
||||
// 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";
|
||||
}
|
||||
emitDefines(os);
|
||||
emitDeclares(os, m_checkOnly);
|
||||
emitAsserts(os, uniqueExprs, false);
|
||||
|
||||
// randc exclusions vs. a pinned current value would make every check
|
||||
// trivially UNSAT after the first cycle.
|
||||
if (!m_checkOnly) emitRandcExclusions(os);
|
||||
|
||||
const size_t nSoft = m_softConstraints.size();
|
||||
bool sat = false;
|
||||
if (nSoft > 0) {
|
||||
// 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);
|
||||
}
|
||||
relaxSoftConstraints(os);
|
||||
os << "(check-sat)\n";
|
||||
const bool sat = parseSolution(os);
|
||||
|
||||
if (!sat) {
|
||||
os << "(reset)\n";
|
||||
|
|
@ -606,116 +615,120 @@ bool VlRandomizer::next(VlRNG& rngr) {
|
|||
// the solver's free assignment.
|
||||
if (m_checkOnly) return false;
|
||||
// Genuine unsat: report via unsat-core
|
||||
os << "(set-option :produce-unsat-cores true)\n";
|
||||
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;
|
||||
reportUnsatSetup(os, uniqueExprs);
|
||||
os << "(reset)\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_checkOnly) {
|
||||
bool hasArray = false;
|
||||
for (const auto& var : m_vars) {
|
||||
if (var.second->dimension() > 0) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
solveDiversity(rngr, os);
|
||||
// Check-only must not advance randc cycle state.
|
||||
recordRandcValues();
|
||||
}
|
||||
|
||||
// Check-only must not advance randc cycle state.
|
||||
if (!m_checkOnly) recordRandcValues();
|
||||
|
||||
os << "(reset)\n";
|
||||
return true;
|
||||
}
|
||||
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) {
|
||||
std::string result;
|
||||
do { std::getline(os, result); } while (result.empty());
|
||||
if (!readNonBlankLine(os, result)) return false;
|
||||
return result == "sat";
|
||||
}
|
||||
|
||||
std::vector<int> VlRandomizer::readUnsatAssumptions(std::iostream& os) {
|
||||
os << "(get-unsat-assumptions)\n";
|
||||
std::string line;
|
||||
do { std::getline(os, line); } while (line.empty());
|
||||
// The response lists only "a<N>" literals; collect each full integer run.
|
||||
void VlRandomizer::relaxSoftConstraints(std::iostream& os) {
|
||||
// Re-add softs highest-priority first, dropping incompatible ones.
|
||||
const size_t nSoft = m_softConstraints.size();
|
||||
if (nSoft == 0) return;
|
||||
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::string num;
|
||||
for (const char c : line) {
|
||||
for (const char c : reply) {
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
num += c;
|
||||
} else if (!num.empty()) {
|
||||
|
|
@ -727,60 +740,66 @@ std::vector<int> VlRandomizer::readUnsatAssumptions(std::iostream& os) {
|
|||
return idxs;
|
||||
}
|
||||
|
||||
bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
|
||||
std::string sat;
|
||||
do { std::getline(os, sat); } while (sat == "");
|
||||
if (sat == "unsat") {
|
||||
if (!log) return false;
|
||||
os << "(get-unsat-core) \n";
|
||||
sat.clear();
|
||||
std::getline(os, sat);
|
||||
std::vector<int> numbers;
|
||||
std::string currentNum;
|
||||
for (const char c : sat) {
|
||||
if (std::isdigit(c)) {
|
||||
currentNum += c;
|
||||
numbers.push_back(std::stoi(currentNum));
|
||||
currentNum.clear();
|
||||
}
|
||||
}
|
||||
if (Verilated::threadContextp()->warnUnsatConstr()) {
|
||||
for (const int n : numbers) {
|
||||
if (n < m_constraints_line.size()) {
|
||||
const std::string& constraint_info = m_constraints_line[n];
|
||||
// Parse "filename:linenum source" format
|
||||
const size_t colon_pos = constraint_info.find(':');
|
||||
if (colon_pos != std::string::npos) {
|
||||
const std::string filename = constraint_info.substr(0, colon_pos);
|
||||
const size_t space_pos = constraint_info.find(" ", colon_pos);
|
||||
std::string linenum_str;
|
||||
std::string source;
|
||||
if (space_pos != std::string::npos) {
|
||||
linenum_str
|
||||
= constraint_info.substr(colon_pos + 1, space_pos - colon_pos - 1);
|
||||
source = constraint_info.substr(space_pos + 3);
|
||||
} else {
|
||||
linenum_str = constraint_info.substr(colon_pos + 1);
|
||||
}
|
||||
const int linenum = std::stoi(linenum_str);
|
||||
std::string msg = "UNSATCONSTR: Unsatisfied constraint";
|
||||
if (!source.empty()) {
|
||||
// Trim leading whitespace and add quotes
|
||||
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());
|
||||
} else {
|
||||
VL_PRINTF("%%Warning-UNSATCONSTR: Unsatisfied constraint: %s\n",
|
||||
constraint_info.c_str());
|
||||
}
|
||||
std::vector<int> VlRandomizer::readUnsatAssumptions(std::iostream& os) {
|
||||
os << "(get-unsat-assumptions)\n";
|
||||
std::string line;
|
||||
if (!readNonBlankLine(os, line)) return {};
|
||||
// The response lists only "a<N>" literals; collect each full integer run.
|
||||
return scanIntRuns(line);
|
||||
}
|
||||
|
||||
// Re-solve with named asserts so an unsat core can name the failing constraints
|
||||
void VlRandomizer::reportUnsatSetup(std::iostream& os,
|
||||
const std::vector<std::string>& uniqueExprs) {
|
||||
os << "(set-option :produce-unsat-cores true)\n";
|
||||
os << "(set-logic QF_ABV)\n";
|
||||
emitDefines(os);
|
||||
emitDeclares(os, false);
|
||||
emitAsserts(os, uniqueExprs, true);
|
||||
os << "(check-sat)\n";
|
||||
std::string status;
|
||||
if (!readNonBlankLine(os, status)) return;
|
||||
if (status == "unsat") reportUnsatCore(os);
|
||||
}
|
||||
|
||||
void VlRandomizer::reportUnsatCore(std::iostream& os) {
|
||||
os << "(get-unsat-core)\n";
|
||||
std::string reply;
|
||||
std::getline(os, reply);
|
||||
const std::vector<int> numbers = scanIntRuns(reply);
|
||||
if (Verilated::threadContextp()->warnUnsatConstr()) {
|
||||
for (const int n : numbers) {
|
||||
if (static_cast<size_t>(n) < m_constraints_line.size()) {
|
||||
const std::string& constraint_info = m_constraints_line[n];
|
||||
// Parse "filename:linenum source" format, parts optional
|
||||
std::string filename;
|
||||
int linenum = 0;
|
||||
std::string source = constraint_info;
|
||||
const size_t colon_pos = constraint_info.find(':');
|
||||
if (colon_pos != std::string::npos) {
|
||||
filename = constraint_info.substr(0, colon_pos);
|
||||
const size_t space_pos = constraint_info.find(" ", colon_pos);
|
||||
const size_t num_end
|
||||
= space_pos == std::string::npos ? constraint_info.size() : space_pos;
|
||||
linenum = std::atoi(
|
||||
constraint_info.substr(colon_pos + 1, num_end - colon_pos - 1).c_str());
|
||||
source = space_pos == std::string::npos
|
||||
? ""
|
||||
: constraint_info.substr(space_pos + 3);
|
||||
}
|
||||
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") {
|
||||
std::stringstream msg;
|
||||
msg << "Internal: Solver error: " << sat;
|
||||
|
|
@ -789,25 +808,29 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
|
|||
return false;
|
||||
}
|
||||
|
||||
os << "(get-value (";
|
||||
std::stringstream getValueStr;
|
||||
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);
|
||||
}
|
||||
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))
|
||||
char c;
|
||||
os >> c;
|
||||
if (c != '(') {
|
||||
if (!(os >> c) || c != '(') {
|
||||
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
||||
"Internal: Unable to parse solver's response: invalid S-expression");
|
||||
return false;
|
||||
}
|
||||
while (true) {
|
||||
os >> c;
|
||||
if (!(os >> c)) return false;
|
||||
if (c == ')') break;
|
||||
if (c != '(') {
|
||||
VL_WARN_MT(__FILE__, __LINE__, "randomize",
|
||||
|
|
@ -912,6 +935,7 @@ void VlRandomizer::clearConstraints() {
|
|||
m_constraints_line.clear();
|
||||
m_solveBefore.clear();
|
||||
m_softConstraints.clear();
|
||||
m_unique_arrays.clear(); // Re-registered by constraint setup
|
||||
// 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);
|
||||
}
|
||||
|
||||
bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
||||
// 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})
|
||||
bool VlRandomizer::buildSolveLayers(std::vector<std::vector<std::string>>& layersr) {
|
||||
std::map<std::string, std::set<std::string>> graph;
|
||||
std::map<std::string, int> inDegree;
|
||||
std::set<std::string> solveBeforeVars;
|
||||
|
|
@ -953,18 +971,12 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
|||
if (inDegree.find(after) == inDegree.end()) inDegree[after] = 0;
|
||||
}
|
||||
|
||||
// Compute in-degrees (after depends on before, so edge is before->after,
|
||||
// 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.
|
||||
// "solve x before y": edge x -> y, in-degree of y increases
|
||||
for (const auto& entry : graph) {
|
||||
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;
|
||||
|
||||
while (!remaining.empty()) {
|
||||
std::vector<std::string> currentLayer;
|
||||
for (const auto& var : remaining) {
|
||||
|
|
@ -981,22 +993,38 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
|||
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
|
||||
// (all solve_before vars are independent, no actual ordering required)
|
||||
if (layers.size() <= 1) {
|
||||
// Clear solve_before temporarily and call normal next()
|
||||
const auto saved = std::move(m_solveBefore);
|
||||
m_solveBefore.clear();
|
||||
const bool result = next(rngr);
|
||||
m_solveBefore = std::move(saved);
|
||||
return result;
|
||||
const char* VlRandomizer::phasedLogic() const {
|
||||
for (const auto& var : m_vars) {
|
||||
if (var.second->dimension() == 0) continue;
|
||||
if (!var.second->hasMatchingElements(m_arr_vars, var.second->name())) return "ALL";
|
||||
}
|
||||
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
|
||||
const char* const logicp = phasedLogic();
|
||||
|
||||
for (size_t phase = 0; phase < layers.size(); phase++) {
|
||||
const bool isFinalPhase = (phase == layers.size() - 1);
|
||||
|
|
@ -1004,143 +1032,45 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
|||
std::iostream& os = getSolver();
|
||||
if (!os) return false;
|
||||
|
||||
// Solver session setup
|
||||
os << "(set-option :produce-models true)\n";
|
||||
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";
|
||||
os << "(set-logic " << logicp << ")\n";
|
||||
emitDefines(os);
|
||||
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) {
|
||||
os << "(assert (= " << entry.first << " " << entry.second << "))\n";
|
||||
}
|
||||
|
||||
// Assert ALL constraints
|
||||
for (const std::string& constraint : m_constraints) {
|
||||
os << "(assert (= #b1 " << constraint << "))\n";
|
||||
}
|
||||
emitAsserts(os, uniqueExprs, false);
|
||||
|
||||
// Randc: exclude previously used values
|
||||
emitRandcExclusions(os);
|
||||
|
||||
// Soft constraints participate in every phase, priority-ordered.
|
||||
relaxSoftConstraints(os);
|
||||
|
||||
// Initial check-sat WITHOUT diversity (guaranteed sat if constraints are consistent)
|
||||
os << "(check-sat)\n";
|
||||
|
||||
if (isFinalPhase) {
|
||||
// Final phase: use parseSolution to write ALL values to memory
|
||||
bool sat = parseSolution(os, true);
|
||||
const bool sat = parseSolution(os);
|
||||
if (!sat) {
|
||||
if (!m_randcVarNames.empty()) m_randcUsedValues.clear();
|
||||
os << "(reset)\n";
|
||||
return false;
|
||||
}
|
||||
solveDiversityXor(rngr, os);
|
||||
// Record solved randc values for future exclusion
|
||||
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";
|
||||
} else {
|
||||
// Intermediate phase: extract values for current layer variables only
|
||||
std::string satResponse;
|
||||
do { std::getline(os, satResponse); } while (satResponse.empty());
|
||||
|
||||
if (satResponse != "sat") {
|
||||
if (!checkSat(os)) {
|
||||
os << "(reset)\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
if (!solvePhaseValues(os, rngr, layers[phase], solvedValues)) {
|
||||
os << "(reset)\n";
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
|
@ -1148,6 +1078,86 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
|||
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
|
||||
void VlRandomizer::dump() const {
|
||||
for (const auto& var : m_vars) {
|
||||
|
|
|
|||
|
|
@ -104,13 +104,32 @@ public:
|
|||
count_cache[base_name] = count;
|
||||
return count;
|
||||
}
|
||||
bool hasMatchingElements(const ArrayInfoMap& arr_vars, const std::string& base_name) const {
|
||||
return arr_vars.find(base_name + "0") != arr_vars.end();
|
||||
}
|
||||
};
|
||||
// SMT key width per associative-array level (string = 128, integral = 8 * sizeof).
|
||||
template <typename T>
|
||||
struct VlRandomAssocKeyWidths final {
|
||||
static void push(std::vector<size_t>&) {}
|
||||
};
|
||||
template <typename T_Key, typename T_Value>
|
||||
struct VlRandomAssocKeyWidths<VlAssocArray<T_Key, T_Value>> final {
|
||||
static void push(std::vector<size_t>& widths) {
|
||||
widths.push_back(std::is_same<T_Key, std::string>::value ? 128 : sizeof(T_Key) * 8);
|
||||
VlRandomAssocKeyWidths<T_Value>::push(widths);
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
class VlRandomArrayVarTemplate final : public VlRandomVar {
|
||||
// Static key widths per level for the empty-array declaration fallback
|
||||
const std::vector<size_t> m_fallbackIdxWidths;
|
||||
|
||||
public:
|
||||
VlRandomArrayVarTemplate(const std::string& name, int width, void* datap, int dimension,
|
||||
std::uint32_t randModeIdx)
|
||||
: VlRandomVar{name, width, datap, dimension, randModeIdx} {}
|
||||
std::uint32_t randModeIdx, const std::vector<size_t>& idxWidths = {})
|
||||
: VlRandomVar{name, width, datap, dimension, randModeIdx}
|
||||
, m_fallbackIdxWidths{idxWidths} {}
|
||||
void* datap(int idx) const override {
|
||||
const std::string indexed_name = name() + std::to_string(idx);
|
||||
const auto it = m_arrVarsRefp->find(indexed_name);
|
||||
|
|
@ -175,7 +194,13 @@ public:
|
|||
}
|
||||
} else {
|
||||
if (dimension() > 0) {
|
||||
for (int i = 0; i < dimension(); ++i) s << "(Array (_ BitVec 32) ";
|
||||
// Empty array: declare from the static key widths, not a 32-bit default.
|
||||
for (int i = 0; i < dimension(); ++i) {
|
||||
const size_t idxWidth = i < static_cast<int>(m_fallbackIdxWidths.size())
|
||||
? m_fallbackIdxWidths[i]
|
||||
: 32;
|
||||
s << "(Array (_ BitVec " << idxWidth << ") ";
|
||||
}
|
||||
s << "(_ BitVec " << width() << ")";
|
||||
for (int i = 0; i < dimension(); ++i) s << ")";
|
||||
} else {
|
||||
|
|
@ -217,8 +242,7 @@ class VlRandomizer VL_NOT_FINAL {
|
|||
std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back)
|
||||
// variables
|
||||
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
|
||||
std::vector<std::string> m_unique_arrays;
|
||||
std::map<std::string, uint32_t> m_unique_array_sizes;
|
||||
std::vector<std::string> m_unique_arrays; // Arrays whose elements must be distinct
|
||||
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
|
||||
const VlQueue<CData>* m_static_randmodep = nullptr; // Static rand_mode state (shared)
|
||||
std::unordered_set<std::string> m_staticVars; // Names of static rand vars
|
||||
|
|
@ -233,14 +257,37 @@ class VlRandomizer VL_NOT_FINAL {
|
|||
|
||||
// PRIVATE METHODS
|
||||
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);
|
||||
// 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).
|
||||
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 recordRandcValues(); // Record solved randc values for future exclusion
|
||||
size_t hashConstraints() const;
|
||||
bool nextPhased(VlRNG& rngr); // Phased solving for solve...before
|
||||
size_t hashConstraints(const std::vector<std::string>& extras) const;
|
||||
bool nextRandomize(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:
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -377,12 +424,12 @@ public:
|
|||
}
|
||||
|
||||
// Register queue of non-struct types
|
||||
template <typename T>
|
||||
template <typename T, size_t N_MaxSize>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlQueue<T>& var, int width, const char* name, int dimension,
|
||||
write_var(VlQueue<T, N_MaxSize>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (m_vars.find(name) == m_vars.end()) {
|
||||
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T>>>(
|
||||
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T, N_MaxSize>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
}
|
||||
if (dimension > 0) {
|
||||
|
|
@ -394,9 +441,9 @@ public:
|
|||
}
|
||||
|
||||
// Register queue of structs
|
||||
template <typename T>
|
||||
template <typename T, size_t N_MaxSize>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlQueue<T>& var, int width, const char* name, int dimension,
|
||||
write_var(VlQueue<T, N_MaxSize>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
}
|
||||
|
|
@ -433,9 +480,11 @@ public:
|
|||
write_var(VlAssocArray<T_Key, T_Value>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (m_vars.find(name) == m_vars.end()) {
|
||||
std::vector<size_t> keyWidths;
|
||||
VlRandomAssocKeyWidths<VlAssocArray<T_Key, T_Value>>::push(keyWidths);
|
||||
m_vars[name]
|
||||
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
name, width, &var, dimension, randmodeIdx, keyWidths);
|
||||
}
|
||||
if (dimension > 0) {
|
||||
m_index = 0;
|
||||
|
|
@ -465,11 +514,10 @@ public:
|
|||
++m_index;
|
||||
}
|
||||
|
||||
// This is the "Sender" API for the generated code
|
||||
void rand_unique(const std::string& name, uint32_t size) {
|
||||
m_unique_arrays.push_back(name);
|
||||
m_unique_array_sizes[name] = size;
|
||||
}
|
||||
// This is the "Sender" API for the generated code.
|
||||
// The elements to make distinct are taken from the array element table at
|
||||
// solve time, so a container resized by the solver is handled correctly.
|
||||
void rand_unique(const std::string& name) { m_unique_arrays.push_back(name); }
|
||||
|
||||
// Recursively record all elements in an unpacked array
|
||||
template <typename T, std::size_t N_Depth>
|
||||
|
|
@ -488,8 +536,8 @@ public:
|
|||
}
|
||||
|
||||
// Recursively record all elements in a queue
|
||||
template <typename T>
|
||||
void record_arr_table(VlQueue<T>& var, const std::string& name, int dimension,
|
||||
template <typename T, size_t N_MaxSize>
|
||||
void record_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
if ((dimension > 0) && (var.size() != 0)) {
|
||||
idxWidths.push_back(32);
|
||||
|
|
@ -522,7 +570,8 @@ public:
|
|||
idxWidths.push_back(idx_width);
|
||||
indices.insert(indices.end(), integral_index.begin(), integral_index.end());
|
||||
|
||||
record_arr_table(var.at(key), indexed_name, dimension - 1, indices, idxWidths);
|
||||
record_arr_table(var.atWrite(key), indexed_name, dimension - 1, indices,
|
||||
idxWidths);
|
||||
|
||||
// Cleanup indices and widths
|
||||
idxWidths.pop_back();
|
||||
|
|
@ -562,8 +611,8 @@ public:
|
|||
}
|
||||
|
||||
// Recursively process VlQueue of structs
|
||||
template <typename T>
|
||||
void record_struct_arr(VlQueue<T>& var, const std::string& name, int dimension,
|
||||
template <typename T, size_t N_MaxSize>
|
||||
void record_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
if ((dimension > 0) && (var.size() != 0)) {
|
||||
idxWidths.push_back(32);
|
||||
|
|
@ -596,7 +645,7 @@ public:
|
|||
|
||||
std::string result = oss.str();
|
||||
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
|
||||
record_struct_arr(var.at(key), name + "." + result, dimension - 1, indices,
|
||||
record_struct_arr(var.atWrite(key), name + "." + result, dimension - 1, indices,
|
||||
idxWidths);
|
||||
}
|
||||
}
|
||||
|
|
@ -724,11 +773,20 @@ public:
|
|||
bool basicStdRandomization(VlAssocArray<T_Key, T_Value>& value, size_t width) {
|
||||
T_Key key;
|
||||
for (int exists = value.first(key); exists; exists = value.next(key)) {
|
||||
basicStdRandomization(value.at(key), width);
|
||||
basicStdRandomization(value.atWrite(key), width);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool next() { return VlRandomizer::next(m_rng); }
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
//Helper method for dynamic array handling in SMT expressions
|
||||
|
||||
inline std::string vlToSolverHex(const IData& value) {
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << std::setfill('0') << std::setw(8) << value;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VlAssocArray<T_Key, T
|
|||
T_Value value;
|
||||
os >> index;
|
||||
os >> value;
|
||||
rhs.at(index) = value;
|
||||
rhs.atWrite(index) = value;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -449,7 +449,7 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::initLib(const std::string& name) VL_MT_
|
|||
// All of these take a destination pointer where the string will be emitted,
|
||||
// and a value to convert. There are a couple of variants for efficiency.
|
||||
|
||||
static inline void cvtCDataToStr(char* dstp, CData value) {
|
||||
inline void cvtCDataToStr(char* dstp, CData value) {
|
||||
#ifdef VL_HAVE_SSE2
|
||||
// Similar to cvtSDataToStr but only the bottom 8 byte lanes are used
|
||||
const __m128i a = _mm_cvtsi32_si128(value);
|
||||
|
|
@ -471,7 +471,7 @@ static inline void cvtCDataToStr(char* dstp, CData value) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtSDataToStr(char* dstp, SData value) {
|
||||
inline void cvtSDataToStr(char* dstp, SData value) {
|
||||
#ifdef VL_HAVE_SSE2
|
||||
// We want each bit in the 16-bit input value to end up in a byte lane
|
||||
// within the 128-bit XMM register. Note that x86 is little-endian and we
|
||||
|
|
@ -507,7 +507,7 @@ static inline void cvtSDataToStr(char* dstp, SData value) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtIDataToStr(char* dstp, IData value) {
|
||||
inline void cvtIDataToStr(char* dstp, IData value) {
|
||||
#ifdef VL_HAVE_AVX2
|
||||
// Similar to cvtSDataToStr but the bottom 16-bits are processed in the
|
||||
// top half of the YMM registers
|
||||
|
|
@ -526,7 +526,7 @@ static inline void cvtIDataToStr(char* dstp, IData value) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtQDataToStr(char* dstp, QData value) {
|
||||
inline void cvtQDataToStr(char* dstp, QData value) {
|
||||
cvtIDataToStr(dstp, value >> 32);
|
||||
cvtIDataToStr(dstp + 32, value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ public:
|
|||
};
|
||||
static_assert(sizeof(WDataInP) == sizeof(EData*), "WDataInP should be a single pointer");
|
||||
|
||||
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
|
||||
inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
|
||||
|
||||
template <std::size_t N_Words>
|
||||
bool VlWide<N_Words>::operator<(const VlWide<N_Words>& rhs) const VL_PURE {
|
||||
|
|
@ -337,6 +337,7 @@ public:
|
|||
|
||||
~VlProcess() {
|
||||
if (m_parentp) m_parentp->detach(this);
|
||||
if (t_currentp == this) t_currentp = m_parentp.get();
|
||||
}
|
||||
|
||||
void attach(VlProcess* childp) { m_children.insert(childp); }
|
||||
|
|
@ -931,7 +932,7 @@ public:
|
|||
VlQueue min(T_Func with_func) const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend(),
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
[&with_func](const T_Value& a, const T_Value& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
return VlQueue::consV(*it);
|
||||
|
|
@ -945,7 +946,7 @@ public:
|
|||
VlQueue max(T_Func with_func) const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend(),
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
[&with_func](const T_Value& a, const T_Value& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
return VlQueue::consV(*it);
|
||||
|
|
@ -1119,9 +1120,7 @@ public:
|
|||
return 1;
|
||||
}
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(const T_Key& index) {
|
||||
T_Value& atWrite(const T_Key& index) {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
|
||||
|
|
@ -1137,7 +1136,7 @@ public:
|
|||
}
|
||||
// Setting as a chained operation
|
||||
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
||||
at(index) = value;
|
||||
atWrite(index) = value;
|
||||
return *this;
|
||||
}
|
||||
VlAssocArray& setDefault(const T_Value& value) {
|
||||
|
|
@ -1394,7 +1393,7 @@ void VL_READMEM_N(bool hex, int bits, const std::string& filename,
|
|||
QData addr;
|
||||
std::string data;
|
||||
if (rmem.get(addr /*ref*/, data /*ref*/)) {
|
||||
rmem.setData(&(obj.at(addr)), data);
|
||||
rmem.setData(&(obj.atWrite(addr)), data);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
@ -2069,8 +2068,24 @@ struct VlNull final {
|
|||
operator T*() const {
|
||||
return nullptr;
|
||||
}
|
||||
template <class T>
|
||||
bool operator==(T* rhs) const {
|
||||
return !rhs;
|
||||
}
|
||||
template <class T>
|
||||
bool operator==(const T* rhs) const {
|
||||
return !rhs;
|
||||
}
|
||||
};
|
||||
inline bool operator==(const void* ptr, VlNull) { return !ptr; }
|
||||
|
||||
template <class T>
|
||||
inline bool operator==(T* lhs, VlNull) {
|
||||
return !lhs;
|
||||
}
|
||||
template <class T>
|
||||
inline bool operator==(const T* lhs, VlNull) {
|
||||
return !lhs;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog class reference container
|
||||
|
|
@ -2223,7 +2238,7 @@ public:
|
|||
};
|
||||
|
||||
template <typename T_Lhs, typename T_Out>
|
||||
static inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr) {
|
||||
inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr) {
|
||||
if (!in) {
|
||||
outr = VlNull{};
|
||||
return true;
|
||||
|
|
@ -2237,7 +2252,7 @@ static inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr
|
|||
}
|
||||
|
||||
template <typename T_Lhs>
|
||||
static inline bool VL_CAST_DYNAMIC(VlNull, VlClassRef<T_Lhs>& outr) {
|
||||
inline bool VL_CAST_DYNAMIC(VlNull, VlClassRef<T_Lhs>& outr) {
|
||||
outr = VlNull{};
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -539,7 +539,7 @@ public:
|
|||
for (auto idx : index()) m_fullname += "[" + std::to_string(idx) + "]";
|
||||
return m_fullname.c_str();
|
||||
}
|
||||
void* prevDatap() const { return m_prevDatap; }
|
||||
uint8_t* prevDatap() const { return m_prevDatap; }
|
||||
void* varDatap() const override { return m_varDatap; }
|
||||
void createPrevDatap() {
|
||||
if (VL_UNLIKELY(!m_prevDatap)) {
|
||||
|
|
@ -1027,6 +1027,26 @@ struct VerilatedVpiTimedCbsCmp final {
|
|||
class VerilatedVpiError;
|
||||
void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset);
|
||||
|
||||
// Information about how to access packed array data.
|
||||
// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word
|
||||
// boundaries, in which case m_maskHi != 0.
|
||||
template <typename T>
|
||||
struct VarAccessInfo final {
|
||||
T* m_datap; // Typed pointer to packed array base address
|
||||
size_t m_bitOffset; // Data start location (bit offset)
|
||||
size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only)
|
||||
T m_maskLo; // Access mask for m_datap[m_wordOffset]
|
||||
T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only)
|
||||
};
|
||||
template <typename T>
|
||||
VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount,
|
||||
size_t addOffset);
|
||||
template <typename T>
|
||||
T vl_vpi_get_word_gen(VarAccessInfo<T> info);
|
||||
|
||||
template <typename T>
|
||||
void vl_vpi_put_word_gen(VarAccessInfo<T> info, T word);
|
||||
|
||||
class VerilatedVpiImp final {
|
||||
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maximum callback reason
|
||||
using VpioCbList = std::list<VerilatedVpiCbHolder>;
|
||||
|
|
@ -1169,6 +1189,72 @@ public:
|
|||
s().m_cbCallList.clear();
|
||||
return called;
|
||||
}
|
||||
template <typename T>
|
||||
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p size=%d\n",
|
||||
varop->fullname(), *(static_cast<CData*>(varop->varDatap())),
|
||||
*(varop->prevDatap()), varop->varDatap(), varop->prevDatap(),
|
||||
varop->entSize()););
|
||||
if (varop->bitSize() == 1) {
|
||||
T* const prevDatap = reinterpret_cast<T*>(
|
||||
varop->prevDatap()); // Was malloced when we added the callback
|
||||
const VarAccessInfo<T> currInfo
|
||||
= vl_vpi_var_access_info<T>(varop, varop->bitSize(), 0);
|
||||
VarAccessInfo<T> prevInfo = currInfo;
|
||||
prevInfo.m_datap = prevDatap;
|
||||
return vl_vpi_get_word_gen(currInfo) != vl_vpi_get_word_gen(prevInfo);
|
||||
}
|
||||
return std::memcmp(varop->prevDatap(), varop->varDatap(), varop->entSize()) != 0;
|
||||
}
|
||||
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) {
|
||||
switch (varop->varp()->vltype()) {
|
||||
case VLVT_UINT8: return valueDiffersFromPrev<CData>(varop);
|
||||
case VLVT_UINT16: return valueDiffersFromPrev<SData>(varop);
|
||||
case VLVT_UINT32: return valueDiffersFromPrev<IData>(varop);
|
||||
case VLVT_UINT64: return valueDiffersFromPrev<QData>(varop);
|
||||
case VLVT_WDATA:
|
||||
return valueDiffersFromPrev<EData>(varop);
|
||||
// LCOV_EXCL_START - Would require earlier type check to not catch that
|
||||
default:
|
||||
const std::string msg
|
||||
= "Unsupported type (" + std::to_string(varop->varp()->vltype()) + ")";
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
return true;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static void updatePrev(const VerilatedVpioVar* const varop) {
|
||||
if (varop->bitSize() == 1) {
|
||||
const VarAccessInfo<T> currInfo
|
||||
= vl_vpi_var_access_info<T>(varop, varop->bitSize(), 0);
|
||||
VarAccessInfo<T> prevInfo = currInfo;
|
||||
T* const prevDatap = reinterpret_cast<T*>(varop->prevDatap());
|
||||
prevInfo.m_datap = prevDatap;
|
||||
const T currWord = vl_vpi_get_word_gen(currInfo);
|
||||
vl_vpi_put_word_gen(prevInfo, currWord);
|
||||
assert(std::memcmp(varop->prevDatap(), varop->varDatap(), varop->entSize()) == 0);
|
||||
} else {
|
||||
std::memcpy(varop->prevDatap(), varop->varDatap(), varop->entSize());
|
||||
}
|
||||
}
|
||||
static void updatePrev(const VerilatedVpioVar* const varop) {
|
||||
switch (varop->varp()->vltype()) {
|
||||
case VLVT_UINT8: updatePrev<CData>(varop); break;
|
||||
case VLVT_UINT16: updatePrev<SData>(varop); break;
|
||||
case VLVT_UINT32: updatePrev<IData>(varop); break;
|
||||
case VLVT_UINT64: updatePrev<QData>(varop); break;
|
||||
case VLVT_WDATA:
|
||||
updatePrev<EData>(varop);
|
||||
break;
|
||||
// LCOV_EXCL_START - Would require earlier type check to not catch that
|
||||
default:
|
||||
const std::string msg
|
||||
= "Unsupported type (" + std::to_string(varop->varp()->vltype()) + ")";
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
static bool callValueCbs() VL_MT_UNSAFE_ONE {
|
||||
assertOneCheck();
|
||||
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
|
||||
|
|
@ -1187,15 +1273,10 @@ public:
|
|||
VerilatedVpiCbHolder& ho = *it++;
|
||||
VerilatedVpioVar* const varop
|
||||
= reinterpret_cast<VerilatedVpioVar*>(ho.cb_datap()->obj);
|
||||
void* const newDatap = varop->varDatap();
|
||||
void* const prevDatap = varop->prevDatap(); // Was malloced when we added the callback
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
|
||||
varop->fullname(), *(static_cast<CData*>(newDatap)),
|
||||
*(static_cast<CData*>(prevDatap)), newDatap, prevDatap););
|
||||
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
|
||||
if (valueDiffersFromPrev(varop)) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
|
||||
ho.id(), varop->fullname(),
|
||||
*(static_cast<CData*>(newDatap))););
|
||||
*(static_cast<CData*>(varop->varDatap()))););
|
||||
update.insert(varop);
|
||||
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
||||
(ho.cb_rtnp())(ho.cb_datap());
|
||||
|
|
@ -1203,9 +1284,7 @@ public:
|
|||
}
|
||||
if (was_last) break;
|
||||
}
|
||||
for (const VerilatedVpioVar* const ip : update) {
|
||||
std::memcpy(ip->prevDatap(), ip->varDatap(), ip->entSize());
|
||||
}
|
||||
for (const VerilatedVpioVar* const varop : update) updatePrev(varop);
|
||||
return called;
|
||||
}
|
||||
static void dumpCbs() VL_MT_UNSAFE_ONE;
|
||||
|
|
@ -2410,7 +2489,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
|||
return vop->castVpiHandle();
|
||||
}
|
||||
default:
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported callback type %s", __func__,
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported callback type '%s'", __func__,
|
||||
VerilatedVpiError::strFromVpiCallbackReason(reason));
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -2856,7 +2935,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
|||
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
|
||||
}
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
|
||||
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
|
||||
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -2870,7 +2949,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
|||
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
|
||||
}
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
|
||||
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
|
||||
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -3031,8 +3110,9 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
|
|||
[[fallthrough]];
|
||||
}
|
||||
default:
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
|
||||
__func__, VerilatedVpiError::strFromVpiProp(property));
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__,
|
||||
"%s: Unsupported property '%s', nothing will be returned", __func__,
|
||||
VerilatedVpiError::strFromVpiProp(property));
|
||||
return vpiUndefined;
|
||||
}
|
||||
}
|
||||
|
|
@ -3195,18 +3275,6 @@ static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
// Information about how to access packed array data.
|
||||
// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word
|
||||
// boundaries, in which case m_maskHi != 0.
|
||||
template <typename T>
|
||||
struct VarAccessInfo final {
|
||||
T* m_datap; // Typed pointer to packed array base address
|
||||
size_t m_bitOffset; // Data start location (bit offset)
|
||||
size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only)
|
||||
T m_maskLo; // Access mask for m_datap[m_wordOffset]
|
||||
T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only)
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount,
|
||||
size_t addOffset) {
|
||||
|
|
@ -3262,21 +3330,25 @@ VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
|
||||
T vl_vpi_get_word_gen(VarAccessInfo<T> info) {
|
||||
const size_t wordBits = sizeof(T) * 8;
|
||||
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
||||
if (info.m_maskHi)
|
||||
if (info.m_maskHi) {
|
||||
return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset)
|
||||
| ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi)
|
||||
<< (wordBits - info.m_bitOffset));
|
||||
}
|
||||
return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) {
|
||||
const size_t wordBits = sizeof(T) * 8;
|
||||
T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
|
||||
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
||||
return vl_vpi_get_word_gen(info);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void vl_vpi_put_word_gen(VarAccessInfo<T> info, T word) {
|
||||
const size_t wordBits = sizeof(T) * 8;
|
||||
if (info.m_maskHi) {
|
||||
info.m_datap[info.m_wordOffset + 1]
|
||||
= (info.m_datap[info.m_wordOffset + 1] & ~info.m_maskHi)
|
||||
|
|
@ -3287,6 +3359,12 @@ void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, s
|
|||
| ((word << info.m_bitOffset) & info.m_maskLo);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) {
|
||||
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
|
||||
vl_vpi_put_word_gen(info, word);
|
||||
}
|
||||
|
||||
// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds
|
||||
// addOffset: additional read bitoffset
|
||||
QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
|
||||
|
|
@ -3500,7 +3578,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
|
|||
VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
|
||||
return;
|
||||
}
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
|
||||
}
|
||||
|
||||
vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/,
|
||||
|
|
@ -3841,7 +3919,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
|||
__func__, vop->fullname());
|
||||
return nullptr;
|
||||
}
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -4236,13 +4314,13 @@ void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
|
|||
|
||||
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vop->type() != vpiRegArray) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
|
||||
VerilatedVpiError::strFromVpiObjType(vop->type()));
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type '%s' for '%s'", __func__,
|
||||
VerilatedVpiError::strFromVpiObjType(vop->type()), vop->name());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -4418,13 +4496,13 @@ void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
|
|||
|
||||
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vop->type() != vpiRegArray) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
|
||||
VerilatedVpiError::strFromVpiObjType(vop->type()));
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle type '%s' for '%s'", __func__,
|
||||
VerilatedVpiError::strFromVpiObjType(vop->type()), vop->name());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -332,10 +332,19 @@
|
|||
|
||||
#ifdef VL_GCOV
|
||||
extern "C" void __gcov_dump();
|
||||
extern "C" void __gcov_reset();
|
||||
// Dump internal code coverage data before e.g. std::abort()
|
||||
# define VL_GCOV_DUMP() __gcov_dump()
|
||||
// Dump, then re-arm dumping; dumping is one-shot, so without the reset a dump
|
||||
// on a nonfatal path would silently discard everything counted after it
|
||||
# define VL_GCOV_DUMP_RESET() \
|
||||
do { \
|
||||
__gcov_dump(); \
|
||||
__gcov_reset(); \
|
||||
} while (false)
|
||||
#else
|
||||
# define VL_GCOV_DUMP()
|
||||
# define VL_GCOV_DUMP_RESET()
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
|
|
@ -368,6 +377,7 @@ extern "C" void __gcov_dump();
|
|||
#define __STDC_FORMAT_MACROS
|
||||
|
||||
// Now that C++ requires these standard types the vl types are deprecated
|
||||
#include <cstddef> // offsetof (used by generated VlVarTableEntry tables)
|
||||
#include <cstdint>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
|
|
@ -460,6 +470,7 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
|||
// Integer size macros
|
||||
|
||||
#define VL_BYTESIZE 8 ///< Bits in a CData / byte
|
||||
#define VL_BYTESIZE_LOG2 3 ///< log2(VL_BYTESIZE)
|
||||
#define VL_SHORTSIZE 16 ///< Bits in a SData / short
|
||||
#define VL_IDATASIZE 32 ///< Bits in an IData / word
|
||||
#define VL_QUADSIZE 64 ///< Bits in a QData / quadword
|
||||
|
|
@ -472,9 +483,9 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
|||
#endif
|
||||
|
||||
/// Return number of bytes argument-number of bits needs (1 bit=1 byte)
|
||||
#define VL_BYTES_I(nbits) (((nbits) + (VL_BYTESIZE - 1)) / VL_BYTESIZE)
|
||||
#define VL_BYTES_I(nbits) (((nbits) + (VL_BYTESIZE - 1)) >> VL_BYTESIZE_LOG2)
|
||||
/// Return Words/EDatas in argument-number of bits needs (1 bit=1 word)
|
||||
#define VL_WORDS_I(nbits) (((nbits) + (VL_EDATASIZE - 1)) / VL_EDATASIZE)
|
||||
#define VL_WORDS_I(nbits) (((nbits) + (VL_EDATASIZE - 1)) >> VL_EDATASIZE_LOG2)
|
||||
// Number of Words/EDatas a quad requires
|
||||
#define VL_WQ_WORDS_E VL_WORDS_I(VL_QUADSIZE)
|
||||
|
||||
|
|
@ -536,10 +547,10 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
|||
// #defines, to avoid requiring math.h on all compile runs
|
||||
|
||||
#ifdef _MSC_VER
|
||||
static inline double VL_TRUNC(double n) {
|
||||
inline double VL_TRUNC(double n) {
|
||||
return (n < 0) ? std::ceil(n) : std::floor(n);
|
||||
}
|
||||
static inline double VL_ROUND(double n) {
|
||||
inline double VL_ROUND(double n) {
|
||||
return (n < 0) ? std::ceil(n-0.5) : std::floor(n + 0.5);
|
||||
}
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -169,17 +169,19 @@ void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
|
|||
}
|
||||
#else
|
||||
// Highly unportable. Sorry
|
||||
// Use VmHWM (peak resident), matching Windows PeakWorkingSetSize and macOS resident_size_max.
|
||||
// VmHWM excludes pages swapped out before the peak; /proc has no peak-(RSS+Swap) counter.
|
||||
std::ifstream is{"/proc/self/status"};
|
||||
if (!is) return;
|
||||
std::string line;
|
||||
uint64_t vmPeak = 0;
|
||||
uint64_t vmHwm = 0;
|
||||
uint64_t vmRss = 0;
|
||||
uint64_t vmSwap = 0;
|
||||
std::string field;
|
||||
while (std::getline(is, line)) {
|
||||
if (line.rfind("VmPeak:", 0) == 0) {
|
||||
if (line.rfind("VmHWM:", 0) == 0) {
|
||||
std::stringstream ss{line};
|
||||
ss >> field >> vmPeak;
|
||||
ss >> field >> vmHwm;
|
||||
} else if (line.rfind("VmRSS:", 0) == 0) {
|
||||
std::stringstream ss{line};
|
||||
ss >> field >> vmRss;
|
||||
|
|
@ -188,7 +190,7 @@ void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
|
|||
ss >> field >> vmSwap;
|
||||
}
|
||||
}
|
||||
peakr = vmPeak * 1024;
|
||||
peakr = vmHwm * 1024;
|
||||
currentr = (vmRss + vmSwap) * 1024;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
# These flags tested against verible-v0.0-4080-ga0a8d8eb
|
||||
|
||||
verible-verilog-format \
|
||||
--inplace \
|
||||
--wrap_end_else_clauses \
|
||||
|
|
|
|||
|
|
@ -33,4 +33,4 @@ sphinxcontrib-spelling==8.0.2
|
|||
yamlfix==1.19.1
|
||||
yapf==0.43.0
|
||||
|
||||
git+https://github.com/antmicro/astsee.git
|
||||
git+https://github.com/antmicro/astsee.git@222480a8ec13b312809ea4acc08c81b4c0f4da2f
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ set(HEADERS
|
|||
V3OrderGraph.h
|
||||
V3OrderInternal.h
|
||||
V3OrderMoveGraph.h
|
||||
V3OrderMTaskGraph.h
|
||||
V3Os.h
|
||||
V3PairingHeap.h
|
||||
V3Param.h
|
||||
|
|
@ -156,6 +157,7 @@ set(HEADERS
|
|||
V3ParseImp.h
|
||||
V3PchAstMT.h
|
||||
V3PchAstNoMT.h
|
||||
V3PoolAllocator.h
|
||||
V3PreExpr.h
|
||||
V3PreLex.h
|
||||
V3PreProc.h
|
||||
|
|
@ -170,7 +172,6 @@ set(HEADERS
|
|||
V3Sampled.h
|
||||
V3Sched.h
|
||||
V3Scope.h
|
||||
V3Scoreboard.h
|
||||
V3SenExprBuilder.h
|
||||
V3SenTree.h
|
||||
V3Simulate.h
|
||||
|
|
@ -319,6 +320,9 @@ set(COMMON_SOURCES
|
|||
V3Order.cpp
|
||||
V3OrderGraphBuilder.cpp
|
||||
V3OrderMoveGraph.cpp
|
||||
V3OrderMTaskContraction.cpp
|
||||
V3OrderMTaskFixHazards.cpp
|
||||
V3OrderMTaskGraph.cpp
|
||||
V3OrderParallel.cpp
|
||||
V3OrderProcessDomains.cpp
|
||||
V3OrderSerial.cpp
|
||||
|
|
@ -346,7 +350,6 @@ set(COMMON_SOURCES
|
|||
V3SchedUtil.cpp
|
||||
V3SchedVirtIface.cpp
|
||||
V3Scope.cpp
|
||||
V3Scoreboard.cpp
|
||||
V3Slice.cpp
|
||||
V3Split.cpp
|
||||
V3SplitVar.cpp
|
||||
|
|
|
|||
|
|
@ -309,6 +309,9 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3Order.o \
|
||||
V3OrderGraphBuilder.o \
|
||||
V3OrderMoveGraph.o \
|
||||
V3OrderMTaskContraction.o \
|
||||
V3OrderMTaskFixHazards.o \
|
||||
V3OrderMTaskGraph.o \
|
||||
V3OrderParallel.o \
|
||||
V3OrderProcessDomains.o \
|
||||
V3OrderSerial.o \
|
||||
|
|
@ -330,7 +333,6 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3SchedUtil.o \
|
||||
V3SchedVirtIface.o \
|
||||
V3Scope.o \
|
||||
V3Scoreboard.o \
|
||||
V3Slice.o \
|
||||
V3Split.o \
|
||||
V3SplitVar.o \
|
||||
|
|
|
|||
|
|
@ -36,11 +36,13 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
// Active class functions
|
||||
|
||||
class ActiveTopVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// AstVarScope::user1() // bool. Processed
|
||||
|
||||
// STATE
|
||||
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
|
||||
|
||||
// METHODS
|
||||
|
||||
static bool isInitial(AstNode* nodep) {
|
||||
const VNUser1InUse user1InUse;
|
||||
// Return true if no variables that read.
|
||||
|
|
|
|||
116
src/V3Assert.cpp
116
src/V3Assert.cpp
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "V3AstUserAllocator.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
|
|
@ -89,6 +90,58 @@ public:
|
|||
explicit DefaultDisablePropagateVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
||||
// Lower a sequence used as an event control ('@seq', IEEE 1800-2023 9.4.2.4) into a
|
||||
// synthesized event fired by an internal 'cover sequence' on each end-of-match
|
||||
class SeqEventLowerVisitor final : public VNVisitor {
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
V3UniqueNames m_names{"__VseqEvent"}; // Synthesized event names
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstSenItem* nodep) override {
|
||||
AstFuncRef* const funcrefp = VN_CAST(nodep->sensp(), FuncRef);
|
||||
if (funcrefp && VN_IS(funcrefp->taskp(), Sequence)) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstVar* const eventp = new AstVar{flp, VVarType::MODULETEMP, m_names.get(nodep),
|
||||
m_modp->findBasicDType(VBasicDTypeKwd::EVENT)};
|
||||
eventp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
m_modp->addStmtsp(eventp);
|
||||
v3Global.setHasEvents();
|
||||
funcrefp->unlinkFrBack();
|
||||
nodep->sensp(new AstVarRef{flp, eventp, VAccess::READ});
|
||||
const bool automaticActual = funcrefp->exists([](const AstNodeVarRef* refp) {
|
||||
return refp->varp() && refp->varp()->lifetime().isAutomatic();
|
||||
});
|
||||
if (automaticActual) {
|
||||
nodep->v3error("Arguments to a sequence used as an event control must be"
|
||||
" static (IEEE 1800-2023 9.4.2.4)");
|
||||
VN_AS(funcrefp->taskp(), Sequence)->isReferenced(false);
|
||||
VL_DO_DANGLING(pushDeletep(funcrefp), funcrefp);
|
||||
return;
|
||||
}
|
||||
AstFireEvent* const firep
|
||||
= new AstFireEvent{flp, new AstVarRef{flp, eventp, VAccess::WRITE}, false};
|
||||
AstCover* const coverp
|
||||
= new AstCover{flp, new AstPropSpec{flp, nullptr, nullptr, funcrefp}, firep,
|
||||
VAssertType::CONCURRENT};
|
||||
coverp->isCoverSeq(true);
|
||||
coverp->isSeqEvent(true);
|
||||
m_modp->addStmtsp(coverp);
|
||||
return;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
explicit SeqEventLowerVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) {
|
||||
|
|
@ -96,6 +149,11 @@ void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) {
|
|||
{ DefaultDisablePropagateVisitor{nodep}; }
|
||||
}
|
||||
|
||||
void V3AssertCommon::lowerSequenceEvents(AstNetlist* nodep) {
|
||||
{ SeqEventLowerVisitor{nodep}; }
|
||||
V3Global::dumpCheckGlobalTree("assertseqevent", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// AssertDeFutureVisitor
|
||||
// If any AstFuture, then move all non-future varrefs to be one cycle behind,
|
||||
|
|
@ -228,6 +286,9 @@ class AssertVisitor final : public VNVisitor {
|
|||
AstNode* m_failsp = nullptr; // Current fail statement
|
||||
AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion
|
||||
AstFinal* m_finalp = nullptr; // Current final block
|
||||
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
|
||||
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
|
||||
m_modExpr2Sen2DelayedAlwaysp;
|
||||
|
|
@ -326,8 +387,7 @@ class AssertVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
AstSampled* newSampledExpr(AstNodeExpr* nodep) {
|
||||
AstSampled* const sampledp = new AstSampled{nodep->fileline(), nodep, nodep->dtypep()};
|
||||
return sampledp;
|
||||
return new AstSampled{nodep->fileline(), nodep, nodep->dtypep(), true};
|
||||
}
|
||||
AstVarRef* newMonitorNumVarRefp(const AstNode* nodep, VAccess access) {
|
||||
if (!m_monitorNumVarp) {
|
||||
|
|
@ -542,14 +602,19 @@ class AssertVisitor final : public VNVisitor {
|
|||
|
||||
bool selfDestruct = false;
|
||||
bool passspGated = false;
|
||||
if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
|
||||
const AstCover* const coverp = VN_CAST(nodep, Cover);
|
||||
// A sequence event control is not an assertion directive; no assertion control
|
||||
const bool seqEvent = coverp && coverp->isSeqEvent();
|
||||
if (coverp) {
|
||||
++m_statCover;
|
||||
if (!v3Global.opt.coverageUser()) {
|
||||
if (seqEvent) {
|
||||
// Keep the event-fire action, with no coverage bucket
|
||||
} else if (!v3Global.opt.coverageUser()) {
|
||||
selfDestruct = true;
|
||||
} else {
|
||||
// V3Coverage assigned us a bucket to increment.
|
||||
AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc);
|
||||
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
|
||||
AstCoverInc* const covincp = VN_AS(coverp->coverincsp(), CoverInc);
|
||||
UASSERT_OBJ(covincp, coverp, "Missing AstCoverInc under assertion");
|
||||
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
||||
if (message != "") covincp->declp()->comment(message);
|
||||
if (passsp) {
|
||||
|
|
@ -593,7 +658,8 @@ class AssertVisitor final : public VNVisitor {
|
|||
FileLine* const flp = nodep->fileline();
|
||||
bool passspAlreadyGated = false;
|
||||
if (passsp && VN_IS(passsp, If)) passspAlreadyGated = VN_AS(passsp, If)->user1();
|
||||
if (passsp && !passspGated && !passspAlreadyGated && !VN_IS(propExprp, PExpr)) {
|
||||
if (passsp && !passspGated && !passspAlreadyGated && !VN_IS(propExprp, PExpr)
|
||||
&& !seqEvent) {
|
||||
passsp = newIfAssertPassOn(passsp, nodep->directive(), nodep->userType(),
|
||||
/*vacuous=*/false);
|
||||
}
|
||||
|
|
@ -603,7 +669,7 @@ class AssertVisitor final : public VNVisitor {
|
|||
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
|
||||
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
|
||||
// Add assertOn check last, for better combining
|
||||
bodysp = newIfAssertOn(bodysp, nodep->directive(), nodep->userType());
|
||||
if (!seqEvent) bodysp = newIfAssertOn(bodysp, nodep->directive(), nodep->userType());
|
||||
if (sentreep) bodysp = new AstAlways{flp, VAlwaysKwd::ALWAYS, sentreep, bodysp};
|
||||
|
||||
if (passsp && !passsp->backp()) VL_DO_DANGLING(pushDeletep(passsp), passsp);
|
||||
|
|
@ -755,6 +821,28 @@ class AssertVisitor final : public VNVisitor {
|
|||
|
||||
//========== Case assertions
|
||||
void visit(AstCase* nodep) override {
|
||||
// Introduce temporary variable for AstCase if needed - it is done here and not in V3Case
|
||||
// because this phase is before V3Scope and V3Case is not. Doing it before V3Scope ensures
|
||||
// that V3Scope will take care of a scope creation
|
||||
// We also need to do it before V3Begin, co that pragmas like `unique0` also work correctly
|
||||
if (!nodep->exprp()->isPure()) {
|
||||
++m_statLiftedCaseExprs;
|
||||
FileLine* const fl = nodep->exprp()->fileline();
|
||||
AstVar* const varp = new AstVar{fl, VVarType::BLOCKTEMP, m_caseTempNames.get(nodep),
|
||||
nodep->exprp()->dtypep()};
|
||||
AstNodeExpr* const origp = nodep->exprp()->unlinkFrBack();
|
||||
nodep->addHereThisAsNext(
|
||||
new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, origp});
|
||||
nodep->exprp(new AstVarRef{fl, varp, VAccess::READ});
|
||||
if (m_ftaskp) {
|
||||
varp->funcLocal(true);
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
m_ftaskp->stmtsp()->addHereThisAsNext(varp);
|
||||
} else {
|
||||
m_modp->stmtsp()->addHereThisAsNext(varp);
|
||||
}
|
||||
VIsCached::clearCacheTree();
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
bool has_default = false;
|
||||
|
|
@ -983,11 +1071,7 @@ class AssertVisitor final : public VNVisitor {
|
|||
= new AstSenItem{fl, VEdgeType::ET_CHANGED,
|
||||
// Clone so get VarRef or VarXRef as needed
|
||||
varrefp->cloneTree(false)};
|
||||
if (!monSenItemsp) {
|
||||
monSenItemsp = senItemp;
|
||||
} else {
|
||||
monSenItemsp->addNext(senItemp);
|
||||
}
|
||||
monSenItemsp = AstNode::addNextNull(monSenItemsp, senItemp);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1127,12 +1211,11 @@ class AssertVisitor final : public VNVisitor {
|
|||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_modPastNum);
|
||||
VL_RESTORER(m_modStrobeNum);
|
||||
VL_RESTORER(m_modExpr2Sen2DelayedAlwaysp);
|
||||
VL_RESTORER(m_finalp);
|
||||
VL_RESTORER_CLEAR(m_modExpr2Sen2DelayedAlwaysp);
|
||||
m_modp = nodep;
|
||||
m_modPastNum = 0;
|
||||
m_modStrobeNum = 0;
|
||||
m_modExpr2Sen2DelayedAlwaysp.clear();
|
||||
m_finalp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
|
@ -1180,6 +1263,9 @@ public:
|
|||
V3Stats::addStat("Assertions, $past variables", m_statPastVars);
|
||||
V3Stats::addStat("Assertions, assertOn checks combined", m_statAssertOnCombined);
|
||||
V3Stats::addStat("Assertions, assertOn checks hoisted", m_statAssertOnHoisted);
|
||||
V3Stats::addStat("Assertions, lifted impure case expressions", m_statLiftedCaseExprs);
|
||||
// Rewrites can change purity, e.g. by compiling out assertion statements with --no-assert
|
||||
VIsCached::clearCacheTree();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
class V3AssertCommon final {
|
||||
public:
|
||||
static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
static void lowerSequenceEvents(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
class V3Assert final {
|
||||
|
|
|
|||
1057
src/V3AssertNfa.cpp
1057
src/V3AssertNfa.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -79,6 +79,14 @@ private:
|
|||
|
||||
// METHODS
|
||||
|
||||
static void checkSamplingFuncDType(AstNodeExpr* nodep, const AstNode* exprp) {
|
||||
const AstNodeDType* const dtypep = exprp->dtypep()->skipRefp();
|
||||
if (!dtypep->isIntegralOrPacked()) {
|
||||
nodep->v3error("Expected numeric type, but got a " << dtypep->prettyDTypeNameQ()
|
||||
<< " data type");
|
||||
}
|
||||
}
|
||||
|
||||
AstSenTree* newSenTree(AstNode* nodep, AstSenTree* useTreep = nullptr,
|
||||
AstNodeCoverOrAssert* cassertp = nullptr) {
|
||||
// Create sentree based on clocked or default clock
|
||||
|
|
@ -372,7 +380,7 @@ private:
|
|||
// #1step means the value that is sampled is always the signal's last value
|
||||
// before the clock edge (IEEE 1800-2023 14.4)
|
||||
AstSampled* const sampledp
|
||||
= new AstSampled{flp, exprp->cloneTreePure(false), exprp->dtypep()};
|
||||
= new AstSampled{flp, exprp->cloneTreePure(false), exprp->dtypep(), true};
|
||||
AstAssign* const assignp = new AstAssign{flp, refp, sampledp};
|
||||
m_clockingp->addNextHere(new AstAlways{
|
||||
flp, VAlwaysKwd::ALWAYS,
|
||||
|
|
@ -683,6 +691,7 @@ private:
|
|||
void visit(AstFalling* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
|
|
@ -696,6 +705,7 @@ private:
|
|||
void visit(AstFell* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
|
|
@ -712,11 +722,13 @@ private:
|
|||
void visit(AstFuture* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
AstSenTree* const sentreep = nodep->sentreep();
|
||||
if (sentreep) VL_DO_DANGLING(pushDeletep(sentreep->unlinkFrBack()), sentreep);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
}
|
||||
void visit(AstPast* nodep) override {
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
|
|
@ -774,6 +786,7 @@ private:
|
|||
void visit(AstRising* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
|
|
@ -787,6 +800,7 @@ private:
|
|||
void visit(AstRose* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
|
|
@ -849,7 +863,8 @@ private:
|
|||
|
||||
// Assertion condition check
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
AstNodeExpr* const condp = new AstSampled{flp, nodep->exprp()->unlinkFrBack(), nullptr};
|
||||
AstSampled* const condp
|
||||
= new AstSampled{flp, nodep->exprp()->unlinkFrBack(), nullptr, true};
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, new AstLogNot{flp, condp}});
|
||||
loopp->addStmtsp(new AstEventControl{flp, sentreep, nullptr});
|
||||
|
||||
|
|
@ -916,6 +931,7 @@ private:
|
|||
void visit(AstStable* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
AstSenTree* sentreep = nodep->sentreep();
|
||||
|
|
@ -931,6 +947,7 @@ private:
|
|||
void visit(AstSteady* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
|
||||
|
|
@ -941,6 +958,10 @@ private:
|
|||
nodep->replaceWith(exprp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstSampled* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->internal()) checkSamplingFuncDType(nodep, nodep->exprp());
|
||||
}
|
||||
|
||||
// Validate repetition count: must be a non-negative elaboration-time constant.
|
||||
// Shared by goto [->N] and nonconsecutive [=N] repetition.
|
||||
|
|
@ -1203,11 +1224,7 @@ private:
|
|||
AstNode* const nextp = stmtp->nextp();
|
||||
if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) {
|
||||
assignp->unlinkFrBack();
|
||||
if (!matchAssignsp) {
|
||||
matchAssignsp = assignp;
|
||||
} else {
|
||||
matchAssignsp->addNext(assignp);
|
||||
}
|
||||
matchAssignsp = AstNode::addNextNull(matchAssignsp, assignp);
|
||||
}
|
||||
stmtp = nextp;
|
||||
}
|
||||
|
|
@ -1233,11 +1250,7 @@ private:
|
|||
AstNodeExpr* const assignRhsp = assignp->rhsp()->unlinkFrBack();
|
||||
AstAssignDly* const dlyp = new AstAssignDly{flp, assignLhsp, assignRhsp};
|
||||
VL_DO_DANGLING(pushDeletep(assignp), assignp);
|
||||
if (!matchAssignsp) {
|
||||
matchAssignsp = dlyp;
|
||||
} else {
|
||||
matchAssignsp->addNext(dlyp);
|
||||
}
|
||||
matchAssignsp = AstNode::addNextNull(matchAssignsp, dlyp);
|
||||
}
|
||||
stmtp = nextp;
|
||||
}
|
||||
|
|
@ -1318,8 +1331,8 @@ private:
|
|||
// this tick, p is not required. For s_until_with, p must be true on the q tick too.
|
||||
AstNodeExpr* const rawLhsp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const rawRhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstSampled* const lhsp = new AstSampled{flp, rawLhsp, rawLhsp->dtypep()};
|
||||
AstSampled* const rhsp = new AstSampled{flp, rawRhsp, rawRhsp->dtypep()};
|
||||
AstSampled* const lhsp = new AstSampled{flp, rawLhsp, rawLhsp->dtypep(), true};
|
||||
AstSampled* const rhsp = new AstSampled{flp, rawRhsp, rawRhsp->dtypep(), true};
|
||||
AstNodeExpr* finalCondp = rhsp->cloneTreePure(false);
|
||||
if (nodep->isOverlapping()) {
|
||||
finalCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), finalCondp};
|
||||
|
|
@ -1466,7 +1479,9 @@ private:
|
|||
iterateAndNextNull(nodep->sensesp());
|
||||
if (m_senip && m_senip != nodep->sensesp())
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Only one PSL clock allowed per assertion");
|
||||
if (!nodep->disablep() && m_defaultDisablep) {
|
||||
const AstCover* const coverp = VN_CAST(nodep->backp(), Cover);
|
||||
const bool seqEvent = coverp && coverp->isSeqEvent();
|
||||
if (!nodep->disablep() && m_defaultDisablep && !seqEvent) {
|
||||
nodep->disablep(m_defaultDisablep->condp()->cloneTreePure(true));
|
||||
}
|
||||
m_disablep = nodep->disablep();
|
||||
|
|
@ -1476,7 +1491,7 @@ private:
|
|||
if (!VN_AS(nodep->backp(), NodeCoverOrAssert)->immediate()) {
|
||||
const AstNodeDType* const propDtp = nodep->propp()->dtypep();
|
||||
nodep->propp(new AstSampled{nodep->fileline(), nodep->propp()->unlinkFrBack(),
|
||||
propDtp->dtypep()});
|
||||
propDtp->dtypep(), true});
|
||||
}
|
||||
// cover counts non-vacuous matches only (IEEE 1800-2023 16.15.2), so an
|
||||
// implication antecedent must hold; assert passes vacuously instead.
|
||||
|
|
|
|||
|
|
@ -1542,14 +1542,14 @@ string AstNode::instanceStr() const {
|
|||
return "";
|
||||
}
|
||||
void AstNode::v3errorEnd(const std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
// Don't look for instance name when warning is disabled.
|
||||
// In case of large number of warnings, this can
|
||||
// take significant amount of time
|
||||
const string instanceStrExtra
|
||||
= m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr();
|
||||
if (!m_fileline) {
|
||||
V3Error::v3errorEnd(str, instanceStrExtra, nullptr);
|
||||
V3Error::v3errorEnd(str, "", nullptr);
|
||||
} else {
|
||||
// Don't look for instance name when warning is disabled.
|
||||
// In case of large number of warnings, this can
|
||||
// take significant amount of time
|
||||
const string instanceStrExtra
|
||||
= m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr();
|
||||
std::ostringstream nsstr;
|
||||
nsstr << str.str();
|
||||
if (debug()) {
|
||||
|
|
@ -1735,12 +1735,15 @@ AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) {
|
|||
if (castable == VCastable::DYNAMIC_CLASS) return node2p->dtypep();
|
||||
}
|
||||
|
||||
AstClassRefDType* classDtypep1 = VN_CAST(node1p->dtypep(), ClassRefDType);
|
||||
AstClassRefDType* classDtypep1 = VN_CAST(node1p->dtypep()->skipRefp(), ClassRefDType);
|
||||
while (classDtypep1) {
|
||||
const VCastable castable = computeCastable(classDtypep1, node2p->dtypep(), node2p);
|
||||
if (castable == VCastable::COMPATIBLE) return classDtypep1;
|
||||
const AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
|
||||
classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr;
|
||||
AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
|
||||
if (!extendsp) break;
|
||||
AstNodeDType* const edtp
|
||||
= extendsp->dtypep() ? extendsp->dtypep() : extendsp->childDTypep();
|
||||
classDtypep1 = VN_AS(edtp->skipRefp(), ClassRefDType);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -730,6 +730,7 @@ public:
|
|||
|
||||
// ACCESSORS for specific types
|
||||
// Alas these can't be virtual or they break when passed a nullptr
|
||||
bool isDisableQueuePushSelfStmt();
|
||||
inline bool isClassHandleValue() const;
|
||||
inline bool isNull() const;
|
||||
inline bool isZero() const;
|
||||
|
|
|
|||
|
|
@ -299,6 +299,34 @@ constexpr VAssertType::en operator|(VAssertType::en lhs, VAssertType::en rhs) {
|
|||
|
||||
// ######################################################################
|
||||
|
||||
class VPropStrength final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
DEFAULT = 0, // Resolve from assertion/coverage context
|
||||
WEAK,
|
||||
STRONG,
|
||||
};
|
||||
enum en m_e;
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VPropStrength(en _e)
|
||||
: m_e{_e} {}
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"default", "weak", "strong"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VPropStrength& lhs, const VPropStrength& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
constexpr bool operator==(const VPropStrength& lhs, VPropStrength::en rhs) {
|
||||
return lhs.m_e == rhs;
|
||||
}
|
||||
constexpr bool operator!=(const VPropStrength& lhs, VPropStrength::en rhs) {
|
||||
return lhs.m_e != rhs;
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VAttrType final {
|
||||
public:
|
||||
// clang-format off
|
||||
|
|
|
|||
|
|
@ -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_classOrPackagep : Optional[AstNodeModule] // Package hierarchy
|
||||
bool m_rawPointer = false; // Emit as a non-owning C++ pointer rather than VlClassRef
|
||||
public:
|
||||
AstClassRefDType(FileLine* fl, AstClass* classp, AstPin* paramsp)
|
||||
: ASTGEN_SUPER_ClassRefDType(fl)
|
||||
|
|
@ -595,7 +596,8 @@ public:
|
|||
// METHODS
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
|
||||
return (m_classp == asamep->m_classp && m_classOrPackagep == asamep->m_classOrPackagep);
|
||||
return (m_classp == asamep->m_classp && m_classOrPackagep == asamep->m_classOrPackagep
|
||||
&& m_rawPointer == asamep->m_rawPointer);
|
||||
}
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
|
|
@ -613,6 +615,9 @@ public:
|
|||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
AstClass* classp() const VL_MT_STABLE { return m_classp; }
|
||||
void classp(AstClass* nodep) { m_classp = nodep; }
|
||||
bool rawPointer() const { return m_rawPointer; }
|
||||
void rawPointer(bool flag) { m_rawPointer = flag; }
|
||||
static void selfTest();
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
class AstConstDType final : public AstNodeDType {
|
||||
|
|
@ -968,7 +973,7 @@ class AstMemberDType final : public AstNodeDType {
|
|||
string m_tag; // Holds the string of the verilator tag -- used in JSON output.
|
||||
int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member
|
||||
bool m_constrainedRand = false;
|
||||
// UNSUP: int m_randType; // Randomization type (IEEE)
|
||||
VRandAttr m_rand; // Randomizability of this member (rand, randc, etc)
|
||||
public:
|
||||
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
|
||||
AstNode* valuep)
|
||||
|
|
@ -1025,6 +1030,8 @@ public:
|
|||
}
|
||||
bool isConstrainedRand() const { return m_constrainedRand; }
|
||||
void markConstrainedRand(bool flag) { m_constrainedRand = flag; }
|
||||
VRandAttr rand() const { return m_rand; }
|
||||
void rand(const VRandAttr flag) { m_rand = flag; }
|
||||
};
|
||||
class AstNBACommitQueueDType final : public AstNodeDType {
|
||||
// @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable
|
||||
|
|
|
|||
|
|
@ -905,7 +905,7 @@ public:
|
|||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
// There's no classOrPackagep(); use classOrPackageNodep() to get Node,
|
||||
// or iterating to package with classOrPackageSkipp()
|
||||
AstNodeModule* classOrPackageSkipp() const;
|
||||
AstNodeModule* classOrPackageSkipp(const bool doRefs = true) const;
|
||||
AstNode* classOrPackageNodep() const { return m_classOrPackageNodep; }
|
||||
void classOrPackageNodep(AstNode* nodep) { m_classOrPackageNodep = nodep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) {
|
||||
|
|
@ -2262,24 +2262,25 @@ public:
|
|||
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
|
||||
};
|
||||
class AstSConsRep final : public AstNodeExpr {
|
||||
// Consecutive repetition [*N], [*N:M], [+], [*] (IEEE 1800-2023 16.9.2)
|
||||
// Consecutive repetition [*N], [*N:M], [*N:$], [+], [*] (IEEE 1800-2023 16.9.2)
|
||||
// op1 := exprp -- the repeated expression
|
||||
// op2 := countp -- min repetition count (N); always a positive constant after V3Width
|
||||
// op2 := countp -- min repetition count (N); always a non-negative constant after V3Width
|
||||
// op3 := maxCountp -- max repetition count (M); nullptr when exact or unbounded
|
||||
//
|
||||
// Encoding:
|
||||
// [*N]: countp=N, maxCountp=nullptr, unbounded=false
|
||||
// [*N:M]: countp=N, maxCountp=M, unbounded=false
|
||||
// [*N:$]: countp=N, maxCountp=nullptr, unbounded=true
|
||||
// [+]: countp=1, maxCountp=nullptr, unbounded=true (= [*1:$])
|
||||
// [*]: countp=0, maxCountp=nullptr, unbounded=true (= [*0:$])
|
||||
//
|
||||
// Lowering:
|
||||
// Exact [*N] standalone: V3AssertPre saturating counter
|
||||
// All other forms and all SExpr-contained forms: V3AssertProp PExpr loop
|
||||
// All other forms and all SExpr-contained forms: V3AssertNfa
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := countp : AstNodeExpr
|
||||
// @astgen op3 := maxCountp : Optional[AstNodeExpr]
|
||||
const bool m_unbounded = false; // True for [+] and [*] (upper bound is $)
|
||||
const bool m_unbounded = false; // True when the upper bound is $
|
||||
public:
|
||||
// Exact [*N]
|
||||
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
|
||||
|
|
@ -2287,7 +2288,7 @@ public:
|
|||
this->exprp(exprp);
|
||||
this->countp(countp);
|
||||
}
|
||||
// Range [*N:M] or unbounded [+]/[*]
|
||||
// Range [*N:M] or unbounded [*N:$]/[+]/[*]
|
||||
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp, AstNodeExpr* maxCountp,
|
||||
bool unbounded)
|
||||
: ASTGEN_SUPER_SConsRep(fl)
|
||||
|
|
@ -2554,19 +2555,26 @@ public:
|
|||
class AstSampled final : public AstNodeExpr {
|
||||
// Verilog $sampled
|
||||
// @astgen op1 := exprp : AstNode<AstNodeExpr|AstPropSpec>
|
||||
bool m_internal : 1; // Internally created, not from a source $sampled
|
||||
public:
|
||||
AstSampled(FileLine* fl, AstNode* exprp, AstNodeDType* dtypep)
|
||||
: ASTGEN_SUPER_Sampled(fl) {
|
||||
AstSampled(FileLine* fl, AstNode* exprp, AstNodeDType* dtypep, bool internal = false)
|
||||
: ASTGEN_SUPER_Sampled(fl)
|
||||
, m_internal{internal} {
|
||||
this->exprp(exprp);
|
||||
this->dtypep(dtypep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSampled;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string emitVerilog() override { return "$sampled(%l)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
int instrCount() const override { return 0; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
return m_internal == VN_DBG_AS(samep, Sampled)->m_internal;
|
||||
}
|
||||
bool internal() const { return m_internal; }
|
||||
bool isSystemFunc() const override { return true; }
|
||||
};
|
||||
class AstScopeName final : public AstNodeExpr {
|
||||
|
|
@ -3258,6 +3266,7 @@ public:
|
|||
bool sizeMattersRhs() const override { return false; }
|
||||
bool isSystemFunc() const override { return true; }
|
||||
int instrCount() const override { return widthInstrs() * 64; }
|
||||
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
};
|
||||
class AstFUngetC final : public AstNodeBiop {
|
||||
public:
|
||||
|
|
@ -3978,12 +3987,16 @@ public:
|
|||
class AstSAnd final : public AstNodeBiop {
|
||||
// Sequence 'and' (IEEE 1800-2023 16.9.5): both operand sequences must match.
|
||||
// Operates on match sets, not values. For boolean operands, lowered to AstLogAnd.
|
||||
const bool m_propertyControl; // Parser-generated property if/case branch conjunction
|
||||
public:
|
||||
AstSAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_SAnd(fl, lhsp, rhsp) {
|
||||
AstSAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, bool propertyControl = false)
|
||||
: ASTGEN_SUPER_SAnd(fl, lhsp, rhsp)
|
||||
, m_propertyControl{propertyControl} {
|
||||
dtypeSetBit();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSAnd;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opLogAnd(lhs, rhs);
|
||||
}
|
||||
|
|
@ -3997,6 +4010,10 @@ public:
|
|||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_LINE
|
||||
return m_propertyControl == VN_DBG_AS(samep, SAnd)->m_propertyControl; // LCOV_EXCL_LINE
|
||||
}
|
||||
bool propertyControl() const { return m_propertyControl; }
|
||||
};
|
||||
class AstSIntersect final : public AstNodeBiop {
|
||||
// Sequence 'intersect' (IEEE 1800-2023 16.9.6): both operands match with equal length.
|
||||
|
|
@ -4886,9 +4903,11 @@ public:
|
|||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
bool isGateOptimizable() const override { return false; } // AssocSel creates on miss
|
||||
bool isGateOptimizable() const override {
|
||||
return !isLValue(); // AssocSel creates on miss
|
||||
}
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() override { return false; } // AssocSel creates on miss
|
||||
bool isPure() override { return !isLValue(); } // AssocSel creates on miss
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
|||
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
||||
string m_name; // Name of task
|
||||
string m_cname; // Name of task if DPI import
|
||||
string m_dpiCDecl; // Custom DPI-C function declaration
|
||||
string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8)
|
||||
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
|
||||
bool m_taskPublic : 1; // Public task
|
||||
|
|
@ -199,6 +200,9 @@ public:
|
|||
void dpiOpenChild(bool flag) { m_dpiOpenChild = flag; }
|
||||
bool dpiTask() const { return m_dpiTask; }
|
||||
void dpiTask(bool flag) { m_dpiTask = flag; }
|
||||
bool dpiCDeclOverride() const { return !m_dpiCDecl.empty(); }
|
||||
const string& dpiCDecl() const { return m_dpiCDecl; }
|
||||
void dpiCDecl(const string& cDecl) { m_dpiCDecl = cDecl; }
|
||||
bool isConstructor() const { return m_isConstructor; }
|
||||
void isConstructor(bool flag) { m_isConstructor = flag; }
|
||||
bool isHideLocal() const { return m_isHideLocal; }
|
||||
|
|
@ -504,6 +508,7 @@ class AstCFunc final : public AstNode {
|
|||
// @astgen op1 := argsp : List[AstVar] // Argument (and return value) variables
|
||||
// @astgen op2 := varsp : List[AstVar] // Local variables
|
||||
// @astgen op3 := stmtsp : List[AstNode]
|
||||
// @astgen op4 := scopeNamep : Optional[AstScopeName] // Scoping context for DPI export
|
||||
//
|
||||
// @astgen ptr := m_scopep : Optional[AstScope] // Scope that function is under
|
||||
string m_name;
|
||||
|
|
@ -511,6 +516,7 @@ class AstCFunc final : public AstNode {
|
|||
string m_rtnType; // void, bool, or other return type
|
||||
string m_argTypes; // Argument types
|
||||
string m_ifdef; // #ifdef symbol around this function
|
||||
string m_cDecl; // Custom DPI-C function declaration
|
||||
VBoolOrUnknown m_isConst; // Function is declared const (*this not changed)
|
||||
bool m_isStatic : 1; // Function is static (no need for a 'this' pointer)
|
||||
bool m_isTrace : 1; // Function is related to tracing
|
||||
|
|
@ -640,6 +646,9 @@ public:
|
|||
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
|
||||
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
|
||||
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
|
||||
bool dpiCDeclOverride() const { return !m_cDecl.empty(); }
|
||||
const string& dpiCDecl() const { return m_cDecl; }
|
||||
void dpiCDecl(const string& cDecl) { m_cDecl = cDecl; }
|
||||
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
|
|
@ -650,20 +659,10 @@ public:
|
|||
void cost(int cost) { m_cost = cost; }
|
||||
// Special methods
|
||||
bool emptyBody() const {
|
||||
return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp() && !isVirtual()
|
||||
return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp() && !scopeNamep() && !isVirtual()
|
||||
&& !dpiImportPrototype();
|
||||
}
|
||||
};
|
||||
class AstCLocalScope final : public AstNode {
|
||||
// Pack statements into an unnamed scope when generating C++
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
public:
|
||||
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_CLocalScope(fl) {
|
||||
addStmtsp(stmtsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCLocalScope;
|
||||
};
|
||||
class AstCUse final : public AstNode {
|
||||
// C++ use of a class or #include; indicates need of forward declaration
|
||||
// Parents: NODEMODULE
|
||||
|
|
@ -844,7 +843,7 @@ class AstClocking final : public AstNode {
|
|||
// @astgen op2 := itemsp : List[AstNode]
|
||||
// @astgen op3 := eventp : Optional[AstVar]
|
||||
std::string m_name; // Clocking block name
|
||||
const bool m_isDefault; // True if default clocking
|
||||
bool m_isDefault; // True if default clocking
|
||||
const bool m_isGlobal; // True if global clocking
|
||||
|
||||
public:
|
||||
|
|
@ -865,6 +864,7 @@ public:
|
|||
bool isDefault() const { return m_isDefault; }
|
||||
bool isGlobal() const { return m_isGlobal; }
|
||||
AstVar* ensureEventp(bool childDType = false);
|
||||
void makeDefault() { m_isDefault = true; }
|
||||
};
|
||||
class AstClockingItem final : public AstNode {
|
||||
// Parents: CLOCKING
|
||||
|
|
@ -1042,22 +1042,6 @@ public:
|
|||
void isStatic(bool flag) { m_isStatic = flag; }
|
||||
bool isStatic() const { return m_isStatic; }
|
||||
};
|
||||
class AstConstraintBefore final : public AstNode {
|
||||
// Constraint solve before item
|
||||
// @astgen op1 := lhssp : List[AstNodeExpr]
|
||||
// @astgen op2 := rhssp : List[AstNodeExpr]
|
||||
public:
|
||||
AstConstraintBefore(FileLine* fl, AstNodeExpr* lhssp, AstNodeExpr* rhssp)
|
||||
: ASTGEN_SUPER_ConstraintBefore(fl) {
|
||||
addLhssp(lhssp);
|
||||
addRhssp(rhssp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConstraintBefore;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
|
||||
class AstCoverBin final : public AstNode {
|
||||
// Captures data for a coverpoint 'bins' declaration
|
||||
// @astgen op1 := rangesp : List[AstNode]
|
||||
|
|
@ -1222,6 +1206,16 @@ public:
|
|||
ASTGEN_MEMBERS_AstDefParam;
|
||||
bool sameNode(const AstNode*) const override { return true; }
|
||||
};
|
||||
class AstDefaultClocking final : public AstNode {
|
||||
std::string m_name; // Clocking block name
|
||||
|
||||
public:
|
||||
AstDefaultClocking(FileLine* fl, const std::string& name)
|
||||
: ASTGEN_SUPER_DefaultClocking(fl)
|
||||
, m_name{name} {}
|
||||
ASTGEN_MEMBERS_AstDefaultClocking;
|
||||
std::string name() const override VL_MT_STABLE { return m_name; }
|
||||
};
|
||||
class AstDefaultDisable final : public AstNode {
|
||||
// @astgen op1 := condp : AstNodeExpr
|
||||
|
||||
|
|
@ -1679,17 +1673,24 @@ class AstPropSpec final : public AstNode {
|
|||
// @astgen op1 := sensesp : Optional[AstSenItem]
|
||||
// @astgen op2 := disablep : Optional[AstNodeExpr]
|
||||
// @astgen op3 := propp : AstNode
|
||||
VPropStrength m_propStrength = VPropStrength::DEFAULT;
|
||||
|
||||
public:
|
||||
AstPropSpec(FileLine* fl, AstSenItem* sensesp, AstNodeExpr* disablep, AstNode* propp)
|
||||
: ASTGEN_SUPER_PropSpec(fl) {
|
||||
AstPropSpec(FileLine* fl, AstSenItem* sensesp, AstNodeExpr* disablep, AstNode* propp,
|
||||
VPropStrength propStrength = VPropStrength::DEFAULT)
|
||||
: ASTGEN_SUPER_PropSpec(fl)
|
||||
, m_propStrength{propStrength} {
|
||||
this->sensesp(sensesp);
|
||||
this->disablep(disablep);
|
||||
this->propp(propp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstPropSpec;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
bool hasDType() const override VL_MT_SAFE {
|
||||
return true;
|
||||
} // Used under Cover, which expects a bool child
|
||||
VPropStrength propStrength() const { return m_propStrength; }
|
||||
};
|
||||
class AstPull final : public AstNode {
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
|
|
@ -2784,11 +2785,14 @@ class AstCoverCross final : public AstNodeFuncCovItem {
|
|||
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
|
||||
// @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items;
|
||||
// // post-LinkParse: empty
|
||||
// @astgen op4 := iffp : Optional[AstNodeExpr] // Conditional sampling guard
|
||||
public:
|
||||
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp)
|
||||
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp,
|
||||
AstNodeExpr* iffp = nullptr)
|
||||
: ASTGEN_SUPER_CoverCross(fl, name) {
|
||||
UASSERT(itemsp, "AstCoverCross requires at least one coverpoint reference");
|
||||
addItemsp(itemsp);
|
||||
this->iffp(iffp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCoverCross;
|
||||
void dump(std::ostream& str) const override;
|
||||
|
|
@ -2889,6 +2893,7 @@ class AstClass final : public AstNodeModule {
|
|||
// @astgen op4 := extendsp : List[AstClassExtends]
|
||||
// MEMBERS
|
||||
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
|
||||
// @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class
|
||||
uint32_t m_declTokenNum; // Declaration token number
|
||||
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||
bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?)
|
||||
|
|
@ -2913,6 +2918,10 @@ public:
|
|||
bool timescaleMatters() const override { return false; }
|
||||
AstClassPackage* classOrPackagep() const VL_MT_STABLE { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
|
||||
AstClass* covergroupEnclosingClassp() const VL_MT_STABLE {
|
||||
return m_covergroupEnclosingClassp;
|
||||
}
|
||||
void covergroupEnclosingClassp(AstClass* classp) { m_covergroupEnclosingClassp = classp; }
|
||||
AstNode* membersp() const VL_MT_STABLE { return stmtsp(); }
|
||||
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
|
||||
bool isCovergroup() const { return m_covergroup; }
|
||||
|
|
|
|||
|
|
@ -297,6 +297,16 @@ public:
|
|||
AstSenTree* sentreep() const { return m_sentreep; }
|
||||
void clearSentreep() { m_sentreep = nullptr; }
|
||||
};
|
||||
class AstCLocalScope final : public AstNodeStmt {
|
||||
// Pack statements into an unnamed scope when generating C++
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
public:
|
||||
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_CLocalScope(fl) {
|
||||
addStmtsp(stmtsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCLocalScope;
|
||||
};
|
||||
class AstCReturn final : public AstNodeStmt {
|
||||
// C++ return from a function
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
|
|
@ -451,6 +461,21 @@ public:
|
|||
bool sameNode(const AstNode* samep) const override { return true; } // Ignore name in comments
|
||||
virtual bool showAt() const { return m_showAt; }
|
||||
};
|
||||
class AstConstraintBefore final : public AstNodeStmt {
|
||||
// Constraint solve before item
|
||||
// @astgen op1 := lhssp : List[AstNodeExpr]
|
||||
// @astgen op2 := rhssp : List[AstNodeExpr]
|
||||
public:
|
||||
AstConstraintBefore(FileLine* fl, AstNodeExpr* lhssp, AstNodeExpr* rhssp)
|
||||
: ASTGEN_SUPER_ConstraintBefore(fl) {
|
||||
addLhssp(lhssp);
|
||||
addRhssp(rhssp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConstraintBefore;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstConstraintExpr final : public AstNodeStmt {
|
||||
// Constraint expression
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
|
|
@ -1568,15 +1593,23 @@ class AstFork final : public AstNodeBlock {
|
|||
//
|
||||
// @astgen op3 := forksp : List[AstBegin]
|
||||
const VJoinType m_joinType; // Join keyword type
|
||||
bool m_immediateStart = false; // Fork starts before its parent blocks or exits
|
||||
|
||||
public:
|
||||
AstFork(FileLine* fl, VJoinType joinType, const string& name = "")
|
||||
: ASTGEN_SUPER_Fork(fl, name)
|
||||
, m_joinType{joinType} {}
|
||||
ASTGEN_MEMBERS_AstFork;
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstFork* const asamep = VN_DBG_AS(samep, Fork);
|
||||
return joinType() == asamep->joinType() && immediateStart() == asamep->immediateStart();
|
||||
}
|
||||
bool isTimingControl() const override { return !joinType().joinNone(); }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
VJoinType joinType() const { return m_joinType; }
|
||||
bool immediateStart() const { return m_immediateStart; }
|
||||
void immediateStart(bool flag) { m_immediateStart = flag; }
|
||||
};
|
||||
|
||||
// === AstNodeCoverOrAssert ===
|
||||
|
|
@ -1612,6 +1645,8 @@ class AstCover final : public AstNodeCoverOrAssert {
|
|||
// @astgen op3 := coverincsp: List[AstNode] // Coverage node
|
||||
bool m_isCoverSeq = false; // 'cover sequence' (IEEE 1800-2023 16.14.3): fires per
|
||||
// end-of-match, not per property success
|
||||
bool m_isSeqEvent = false; // Synthesized for a sequence used as an event control
|
||||
// (IEEE 1800-2023 9.4.2.4)
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstCover;
|
||||
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type,
|
||||
|
|
@ -1622,6 +1657,8 @@ public:
|
|||
void dumpJson(std::ostream& str) const override;
|
||||
bool isCoverSeq() const { return m_isCoverSeq; }
|
||||
void isCoverSeq(bool flag) { m_isCoverSeq = flag; }
|
||||
bool isSeqEvent() const { return m_isSeqEvent; }
|
||||
void isSeqEvent(bool flag) { m_isSeqEvent = flag; }
|
||||
};
|
||||
class AstRestrict final : public AstNodeCoverOrAssert {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -108,6 +108,21 @@ int AstNodeSel::bitConst() const {
|
|||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
bool AstNode::isDisableQueuePushSelfStmt() {
|
||||
// Detect LinkJump-generated registration:
|
||||
// __VprocessQueue_*.push_back(std::process::self())
|
||||
AstStmtExpr* const stmtExprp = VN_CAST(this, StmtExpr);
|
||||
if (!stmtExprp) return false;
|
||||
AstCMethodHard* const methodp = VN_CAST(stmtExprp->exprp(), CMethodHard);
|
||||
if (!methodp || methodp->name() != "push_back") return false;
|
||||
AstNode* const basep = AstArraySel::baseFromp(methodp->fromp(), false);
|
||||
if (AstVarRef* const refp = VN_CAST(basep, VarRef)) return refp->varp()->processQueue();
|
||||
if (AstMemberSel* const selp = VN_CAST(basep, MemberSel)) {
|
||||
return selp->varp() && selp->varp()->processQueue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AstNodeStmt::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
void AstNodeStmt::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
|
||||
|
|
@ -468,6 +483,14 @@ void AstSConsRep::dumpJson(std::ostream& str) const {
|
|||
dumpJsonBoolFuncIf(str, unbounded);
|
||||
dumpJsonGen(str);
|
||||
} // LCOV_EXCL_STOP
|
||||
void AstSAnd::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (propertyControl()) str << " [PROPERTY_CONTROL]";
|
||||
}
|
||||
void AstSAnd::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, propertyControl);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstPropAlways::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (isStrong()) str << " [strong]";
|
||||
|
|
@ -1179,7 +1202,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
|||
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = "VlClassRef<" + EmitCUtil::prefixNameProtect(adtypep) + ">";
|
||||
const string className = EmitCUtil::prefixNameProtect(adtypep);
|
||||
info.m_type = adtypep->rawPointer() ? className + "*" : "VlClassRef<" + className + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = EmitCUtil::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
|
||||
|
|
@ -1401,6 +1425,9 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
|
|||
} else if (VN_IS(nodep, WildcardSel)) {
|
||||
nodep = VN_AS(nodep, WildcardSel)->fromp();
|
||||
continue;
|
||||
} else if (VN_IS(nodep, CMethodHard)) {
|
||||
nodep = VN_AS(nodep, CMethodHard)->fromp();
|
||||
continue;
|
||||
} else if (overMembers && VN_IS(nodep, MemberSel)) {
|
||||
nodep = VN_AS(nodep, MemberSel)->fromp();
|
||||
continue;
|
||||
|
|
@ -1977,6 +2004,18 @@ string AstBasicDType::prettyDTypeName(bool) const {
|
|||
void AstNodeExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
void AstNodeExpr::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
|
||||
void AstPropSpec::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (propStrength() != VPropStrength::DEFAULT) {
|
||||
str << " [" << VString::upcase(propStrength().ascii()) << "]";
|
||||
}
|
||||
}
|
||||
void AstPropSpec::dumpJson(std::ostream& str) const {
|
||||
if (propStrength() != VPropStrength::DEFAULT)
|
||||
dumpJsonStr(str, "strength", propStrength().ascii());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
AstConst::~AstConst() {
|
||||
// Only rare constants carry originating parameter-name metadata. For all other AstConst nodes,
|
||||
// the V3Number bit keeps this destructor from touching AstNetlist's side table. When the bit
|
||||
|
|
@ -2022,6 +2061,8 @@ bool AstNodeExpr::isLValue() const {
|
|||
return varrefp->access().isWriteOrRW();
|
||||
} else if (const AstMemberSel* const memberselp = VN_CAST(this, MemberSel)) {
|
||||
return memberselp->access().isWriteOrRW();
|
||||
} else if (const AstStructSel* const structselp = VN_CAST(this, StructSel)) {
|
||||
return structselp->fromp()->isLValue();
|
||||
} else if (const AstSel* const selp = VN_CAST(this, Sel)) {
|
||||
return selp->fromp()->isLValue();
|
||||
} else if (const AstNodeSel* const nodeSelp = VN_CAST(this, NodeSel)) {
|
||||
|
|
@ -2181,8 +2222,22 @@ void AstClassRefDType::dump(std::ostream& str) const {
|
|||
} else {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
if (rawPointer()) str << " [RAWPTR]";
|
||||
}
|
||||
void AstClassRefDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, rawPointer);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstClassRefDType::selfTest() {
|
||||
FileLine* const fl = new FileLine{FileLine::commandLineFilename()};
|
||||
AstClassRefDType* const owningp = new AstClassRefDType{fl, nullptr, nullptr};
|
||||
AstClassRefDType* const rawp = new AstClassRefDType{fl, nullptr, nullptr};
|
||||
rawp->rawPointer(true);
|
||||
UASSERT_OBJ(!owningp->sameNode(rawp) && !rawp->sameNode(owningp) && rawp->sameNode(rawp), rawp,
|
||||
"Raw class pointer must have distinct type identity");
|
||||
VL_DO_DANGLING(owningp->deleteTree(), owningp);
|
||||
VL_DO_DANGLING(rawp->deleteTree(), rawp);
|
||||
}
|
||||
void AstClassRefDType::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstClassRefDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "class:" << name();
|
||||
|
|
@ -2241,9 +2296,11 @@ void AstNodeCoverOrAssert::dumpJson(std::ostream& str) const {
|
|||
void AstCover::dump(std::ostream& str) const {
|
||||
this->AstNodeCoverOrAssert::dump(str);
|
||||
if (isCoverSeq()) str << " [COVERSEQ]";
|
||||
if (isSeqEvent()) str << " [SEQEVENT]";
|
||||
}
|
||||
void AstCover::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isCoverSeq);
|
||||
dumpJsonBoolFuncIf(str, isSeqEvent);
|
||||
this->AstNodeCoverOrAssert::dumpJson(str);
|
||||
}
|
||||
void AstClocking::dump(std::ostream& str) const {
|
||||
|
|
@ -2486,12 +2543,14 @@ const char* AstLoopTest::broken() const {
|
|||
void AstMemberDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (isConstrainedRand()) str << " [CONSTRAINEDRAND]";
|
||||
if (rand().isRandomizable()) str << " [" << rand() << "]";
|
||||
if (name() != "") str << " name=" << name();
|
||||
if (tag() != "") str << " tag=" << tag();
|
||||
}
|
||||
|
||||
void AstMemberDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, isConstrainedRand);
|
||||
if (rand().isRandomizable()) dumpJsonStr(str, "rand", rand().ascii());
|
||||
dumpJsonStrFunc(str, name);
|
||||
dumpJsonStrFunc(str, tag);
|
||||
dumpJsonGen(str);
|
||||
|
|
@ -3032,6 +3091,14 @@ void AstSFormatF::dumpJson(std::ostream& str) const {
|
|||
dumpJsonBoolFuncIf(str, exprFormat);
|
||||
dumpJsonBoolFuncIf(str, optionalFormat);
|
||||
}
|
||||
void AstSampled::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (internal()) str << " [INTERNAL]";
|
||||
}
|
||||
void AstSampled::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, internal);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstSel::dump(std::ostream& str) const {
|
||||
this->AstNodeBiop::dump(str);
|
||||
str << " widthConst=" << this->widthConst();
|
||||
|
|
@ -3401,7 +3468,7 @@ void AstClassOrPackageRef::dump(std::ostream& str) const {
|
|||
}
|
||||
}
|
||||
void AstClassOrPackageRef::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp() const {
|
||||
AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp(const bool doRefs) const {
|
||||
AstNode* foundp = m_classOrPackageNodep;
|
||||
AstNode* lastp = nullptr;
|
||||
while (foundp != lastp) {
|
||||
|
|
@ -3409,11 +3476,12 @@ AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp() const {
|
|||
if (AstNodeDType* const anodep = VN_CAST(foundp, NodeDType)) {
|
||||
foundp = anodep->skipRefOrNullp();
|
||||
}
|
||||
if (const AstTypedef* const anodep = VN_CAST(foundp, Typedef)) {
|
||||
foundp = anodep->subDTypep();
|
||||
}
|
||||
if (const AstClassRefDType* const anodep = VN_CAST(foundp, ClassRefDType)) {
|
||||
foundp = anodep->classp();
|
||||
if (doRefs) {
|
||||
if (const AstTypedef* const anodep = VN_CAST(foundp, Typedef)) {
|
||||
foundp = anodep->subDTypep();
|
||||
} else if (const AstClassRefDType* const anodep = VN_CAST(foundp, ClassRefDType)) {
|
||||
foundp = anodep->classp();
|
||||
}
|
||||
}
|
||||
}
|
||||
return VN_CAST(foundp, NodeModule);
|
||||
|
|
@ -3600,9 +3668,11 @@ void AstCoverInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
|||
void AstFork::dump(std::ostream& str) const {
|
||||
this->AstNodeBlock::dump(str);
|
||||
str << " [" << joinType() << "]";
|
||||
if (immediateStart()) str << " [IMMEDIATE]";
|
||||
}
|
||||
void AstFork::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "joinType", joinType().ascii());
|
||||
dumpJsonBoolFuncIf(str, immediateStart);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstStop::dump(std::ostream& str) const {
|
||||
|
|
@ -3793,7 +3863,7 @@ void AstDelay::dumpJson(std::ostream& str) const {
|
|||
}
|
||||
|
||||
const char* AstDisable::broken() const {
|
||||
BROKEN_RTN((m_targetp && targetRefp()) || ((!m_targetp && !targetRefp())));
|
||||
BROKEN_RTN(!m_targetp && !targetRefp());
|
||||
return nullptr;
|
||||
}
|
||||
void AstDisable::dump(std::ostream& str) const {
|
||||
|
|
|
|||
|
|
@ -137,12 +137,9 @@ class BeginVisitor final : public VNVisitor {
|
|||
UINFO(8, " rename to " << nodep->name());
|
||||
m_statep->userMarkChanged(nodep);
|
||||
}
|
||||
VL_RESTORER(m_displayScope);
|
||||
VL_RESTORER(m_namedScope);
|
||||
VL_RESTORER(m_unnamedScope);
|
||||
m_displayScope = "";
|
||||
m_namedScope = "";
|
||||
m_unnamedScope = "";
|
||||
VL_RESTORER_CLEAR(m_displayScope);
|
||||
VL_RESTORER_CLEAR(m_namedScope);
|
||||
VL_RESTORER_CLEAR(m_unnamedScope);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
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
|
||||
// inside the function.
|
||||
// Process children
|
||||
VL_RESTORER(m_displayScope);
|
||||
VL_RESTORER_COPY(m_displayScope);
|
||||
VL_RESTORER(m_ftaskp);
|
||||
VL_RESTORER(m_liftedp);
|
||||
VL_RESTORER(m_namedScope);
|
||||
VL_RESTORER(m_unnamedScope);
|
||||
VL_RESTORER_CLEAR(m_namedScope);
|
||||
VL_RESTORER_CLEAR(m_unnamedScope);
|
||||
m_displayScope = dot(m_displayScope, nodep->name());
|
||||
m_namedScope = "";
|
||||
m_unnamedScope = "";
|
||||
|
||||
m_ftaskp = nodep;
|
||||
m_liftedp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -191,9 +187,9 @@ class BeginVisitor final : public VNVisitor {
|
|||
void visit(AstGenBlock* nodep) override {
|
||||
// GenBlocks were only useful in variable creation, change names and delete
|
||||
UINFO(8, " " << nodep);
|
||||
VL_RESTORER(m_displayScope);
|
||||
VL_RESTORER(m_namedScope);
|
||||
VL_RESTORER(m_unnamedScope);
|
||||
VL_RESTORER_COPY(m_displayScope);
|
||||
VL_RESTORER_COPY(m_namedScope);
|
||||
VL_RESTORER_COPY(m_unnamedScope);
|
||||
UASSERT_OBJ(!m_keepBegins, nodep, "Should be able to eliminate all AstGenBlock");
|
||||
dotNames(nodep->name(), nodep->fileline(), "__BEGIN__");
|
||||
iterateAndNextNull(nodep->itemsp());
|
||||
|
|
@ -227,9 +223,9 @@ class BeginVisitor final : public VNVisitor {
|
|||
void visit(AstBegin* nodep) override {
|
||||
// Begin blocks were only useful in variable creation, change names and delete
|
||||
UINFO(8, " " << nodep);
|
||||
VL_RESTORER(m_displayScope);
|
||||
VL_RESTORER(m_namedScope);
|
||||
VL_RESTORER(m_unnamedScope);
|
||||
VL_RESTORER_COPY(m_displayScope);
|
||||
VL_RESTORER_COPY(m_namedScope);
|
||||
VL_RESTORER_COPY(m_unnamedScope);
|
||||
{
|
||||
VL_RESTORER(m_keepBegins);
|
||||
m_keepBegins = false;
|
||||
|
|
@ -261,9 +257,9 @@ class BeginVisitor final : public VNVisitor {
|
|||
void visit(AstNodeBlock* nodep) override {
|
||||
// Begin/Fork blocks were only useful in variable creation, change names and delete
|
||||
UINFO(8, " " << nodep);
|
||||
VL_RESTORER(m_displayScope);
|
||||
VL_RESTORER(m_namedScope);
|
||||
VL_RESTORER(m_unnamedScope);
|
||||
VL_RESTORER_COPY(m_displayScope);
|
||||
VL_RESTORER_COPY(m_namedScope);
|
||||
VL_RESTORER_COPY(m_unnamedScope);
|
||||
{
|
||||
VL_RESTORER(m_keepBegins);
|
||||
m_keepBegins = VN_IS(nodep, Fork);
|
||||
|
|
@ -451,7 +447,7 @@ class BeginRelinkVisitor final : public VNVisitorConst {
|
|||
private:
|
||||
// NODE STATE
|
||||
// Input:
|
||||
// AstNodeFTask::user1p // Node replaced, rename it
|
||||
// AstNodeFTask::user1p // bool. Node replaced, rename it
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
|
|
|
|||
|
|
@ -252,18 +252,15 @@ private:
|
|||
void visit(AstScope* nodep) override {
|
||||
VL_RESTORER(m_inScope);
|
||||
m_inScope = true;
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
VL_RESTORER_CLEAR(m_cFuncNames);
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
VL_RESTORER_CLEAR(m_cFuncNames);
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeUOrStructDType* nodep) override {
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
VL_RESTORER_CLEAR(m_cFuncNames);
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
class CastVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNode::user1() // bool. Indicates node is of known size
|
||||
// AstNode::user1() // bool. Node is of known size
|
||||
const VNUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ class ClassVisitor final : public VNVisitor {
|
|||
classScopep->aboveScopep(), classScopep->aboveCellp()};
|
||||
packagep->addStmtsp(scopep);
|
||||
// Iterate
|
||||
VL_RESTORER(m_prefix);
|
||||
VL_RESTORER_CLEAR(m_prefix);
|
||||
VL_RESTORER(m_classPackagep);
|
||||
VL_RESTORER(m_classScopep);
|
||||
VL_RESTORER(m_packageScopep);
|
||||
|
|
@ -129,7 +129,7 @@ class ClassVisitor final : public VNVisitor {
|
|||
void visit(AstNodeModule* nodep) override {
|
||||
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
|
||||
// Classes are always under a Package (perhaps $unit) or a module
|
||||
VL_RESTORER(m_prefix);
|
||||
VL_RESTORER_CLEAR(m_prefix);
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = nodep;
|
||||
m_prefix = nodep->name() + "__03a__03a"; // ::
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
class CleanVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// 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
|
||||
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
|
||||
const VNUser1InUse m_inuser1;
|
||||
|
|
|
|||
|
|
@ -438,6 +438,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
|||
|
||||
// Traverse down to see AstConst or AstVarRef
|
||||
LeafInfo findLeaf(AstNode* nodep, bool expectConst) {
|
||||
if (!nodep->dtypep()->skipRefp()->isIntegralOrPacked()) return LeafInfo{};
|
||||
LeafInfo info{m_lsb};
|
||||
{
|
||||
VL_RESTORER(m_leafp);
|
||||
|
|
@ -1708,8 +1709,7 @@ class ConstVisitor final : public VNVisitor {
|
|||
const V3Number num{constp, subsize, constp->num()};
|
||||
nodep->lhsp(new AstConst{constp->fileline(), num});
|
||||
VL_DO_DANGLING(pushDeletep(constp), constp);
|
||||
UINFOTREE(9, nodep, "", "BI(EXTEND)-ou");
|
||||
return true;
|
||||
return false; // input node is still valid, keep going
|
||||
}
|
||||
bool operandBiExtendConstOver(const AstNodeBiop* nodep) {
|
||||
// EQ(const{width32}, EXTEND(xx{width3})) -> constant
|
||||
|
|
@ -2069,7 +2069,7 @@ class ConstVisitor final : public VNVisitor {
|
|||
rp->rhsp(bp);
|
||||
rp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
||||
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) {
|
||||
// BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r))
|
||||
|
|
@ -2082,7 +2082,7 @@ class ConstVisitor final : public VNVisitor {
|
|||
lp->lhsp(lrp);
|
||||
lp->rhsp(rp);
|
||||
lp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
||||
// UINFOTREE(1, nodep, "", "repAsvLUp_new");
|
||||
iterate(nodep); // Proceed to fixed point
|
||||
}
|
||||
void replaceAsvRUp(AstNodeBiop* nodep) {
|
||||
// BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr))
|
||||
|
|
@ -2095,7 +2095,7 @@ class ConstVisitor final : public VNVisitor {
|
|||
rp->lhsp(lp);
|
||||
rp->rhsp(rrp);
|
||||
rp->dtypeFrom(nodep); // Upper widthMin more likely correct
|
||||
// UINFOTREE(1, nodep, "", "repAsvRUp_new");
|
||||
iterate(nodep); // Proceed to fixed point
|
||||
}
|
||||
void replaceAndOr(AstNodeBiop* nodep) {
|
||||
// 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(),
|
||||
VSigning::UNSIGNED);
|
||||
srcp = packedp;
|
||||
}
|
||||
if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|
||||
|| VN_IS(srcDTypep, UnpackArrayDType))) {
|
||||
} else if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|
||||
|| VN_IS(srcDTypep, UnpackArrayDType))) {
|
||||
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
||||
int blockSize = 1;
|
||||
if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) {
|
||||
|
|
@ -4585,6 +4584,10 @@ class ConstVisitor final : public VNVisitor {
|
|||
// Custom
|
||||
// Implied by AstIsUnbounded::numberOperate: V("AstIsUnbounded{$lhsp.castConst}", "replaceNum(nodep, 0)");
|
||||
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
|
||||
|
||||
// Possible futures:
|
||||
|
|
|
|||
|
|
@ -279,8 +279,8 @@ class CoverageVisitor final : public VNVisitor {
|
|||
const AstNodeModule* const origModp = m_modp;
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_state);
|
||||
VL_RESTORER(m_exprTempNames);
|
||||
VL_RESTORER(m_funcTemps);
|
||||
VL_RESTORER_COPY(m_exprTempNames);
|
||||
VL_RESTORER_COPY(m_funcTemps);
|
||||
createHandle(nodep);
|
||||
m_modp = nodep;
|
||||
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 {
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_state);
|
||||
VL_RESTORER(m_exprTempNames);
|
||||
VL_RESTORER(m_funcTemps);
|
||||
VL_RESTORER_COPY(m_exprTempNames);
|
||||
VL_RESTORER_COPY(m_funcTemps);
|
||||
createHandle(nodep);
|
||||
m_modp = nodep;
|
||||
// Covergroup declarations are not executable statements; suppress line/expr/toggle
|
||||
|
|
@ -356,8 +356,8 @@ class CoverageVisitor final : public VNVisitor {
|
|||
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
VL_RESTORER(m_ftaskp);
|
||||
VL_RESTORER(m_exprTempNames);
|
||||
VL_RESTORER(m_funcTemps);
|
||||
VL_RESTORER_COPY(m_exprTempNames);
|
||||
VL_RESTORER_COPY(m_funcTemps);
|
||||
m_ftaskp = nodep;
|
||||
if (!nodep->dpiImport()) iterateProcedure(nodep);
|
||||
}
|
||||
|
|
@ -385,8 +385,7 @@ class CoverageVisitor final : public VNVisitor {
|
|||
VL_RESTORER(m_state);
|
||||
VL_RESTORER(m_exprStmtsp);
|
||||
VL_RESTORER(m_inToggleOff);
|
||||
// skip properties for expresison coverage
|
||||
if (!VN_IS(nodep, Property)) m_exprStmtsp = nodep;
|
||||
m_exprStmtsp = nodep;
|
||||
m_inToggleOff = true;
|
||||
createHandle(nodep);
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -745,6 +744,11 @@ class CoverageVisitor final : public VNVisitor {
|
|||
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 {
|
||||
UINFO(4, " STOP: " << nodep);
|
||||
m_state.m_on = false;
|
||||
|
|
@ -762,7 +766,7 @@ class CoverageVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstGenBlock* nodep) override {
|
||||
// Similar to AstBegin
|
||||
VL_RESTORER(m_beginHier);
|
||||
VL_RESTORER_COPY(m_beginHier);
|
||||
if (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.
|
||||
// (Currently ignored for line coverage, since any generate iteration
|
||||
// covers the code in that line.)
|
||||
VL_RESTORER(m_beginHier);
|
||||
VL_RESTORER_COPY(m_beginHier);
|
||||
VL_RESTORER(m_inToggleOff);
|
||||
VL_RESTORER(m_exprStmtsp);
|
||||
m_exprStmtsp = nodep;
|
||||
|
|
@ -874,7 +878,7 @@ class CoverageVisitor final : public VNVisitor {
|
|||
UASSERT_OBJ(m_exprs.empty(), nodep, "unexpected expression coverage garbage");
|
||||
VL_RESTORER(m_seeking);
|
||||
VL_RESTORER(m_objective);
|
||||
VL_RESTORER(m_exprs);
|
||||
VL_RESTORER_CLEAR(m_exprs); // Already asserted above it's empty.
|
||||
|
||||
m_seeking = SEEKING;
|
||||
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); });
|
||||
}
|
||||
|
||||
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) {
|
||||
if (otherps.empty()) return;
|
||||
|
||||
|
|
@ -353,6 +179,40 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
|||
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>()) {
|
||||
const V3Number& num = constVtxp->num();
|
||||
|
||||
|
|
@ -620,6 +480,12 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataT
|
|||
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 {
|
||||
|
||||
#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");
|
||||
return;
|
||||
}
|
||||
case VDfgType::Prev: {
|
||||
CHECK(isPacked() || isArray(), "Should be Packed or Array type");
|
||||
return;
|
||||
}
|
||||
case VDfgType::SpliceArray:
|
||||
case VDfgType::SplicePacked: {
|
||||
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'");
|
||||
return m_dtype.size();
|
||||
}
|
||||
// Has terminating side-effect
|
||||
bool unsafe() const;
|
||||
|
||||
// Type check vertex (for debugging)
|
||||
void typeCheck(const DfgGraph& dfg) const;
|
||||
|
|
@ -485,9 +487,6 @@ public:
|
|||
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.
|
||||
// DfgVertexVar instances representing the same Ast variable are unified.
|
||||
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;
|
||||
// Variables
|
||||
if (is<DfgVertexVar>()) return true;
|
||||
if (is<DfgPrev>()) return true;
|
||||
// Array sels are just address computation, but the address itself can be expensive
|
||||
if (const DfgArraySel* aselp = cast<DfgArraySel>()) {
|
||||
if (aselp->bitp()->is<DfgMatchMasked>()) return false;
|
||||
|
|
@ -830,6 +830,13 @@ bool DfgVertex::isCheaperThanLoad() const {
|
|||
const uint32_t msb = lsb + selp->width() - 1;
|
||||
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, _)
|
||||
if (const DfgConcat* const catp = cast<DfgConcat>()) {
|
||||
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:
|
||||
// STATE
|
||||
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_nTrivial; // Number of graphs that were not changed
|
||||
VDouble0 m_nImprovements; // Number of changes made to graphs
|
||||
VDouble0 m_nPrevInserted; // Number of Prev vertices inserted
|
||||
|
||||
private:
|
||||
V3DfgBreakCyclesContext()
|
||||
|
|
@ -112,8 +112,8 @@ private:
|
|||
addStat("made acyclic", m_nFixed);
|
||||
addStat("improved", m_nImproved);
|
||||
addStat("left unchanged", m_nUnchanged);
|
||||
addStat("trivial", m_nTrivial);
|
||||
addStat("changes applied", m_nImprovements);
|
||||
addStat("prev vertices inserted", m_nPrevInserted);
|
||||
}
|
||||
};
|
||||
class V3DfgCseContext final : public V3DfgSubContext {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ class V3DfgCse final {
|
|||
case VDfgType::CReset:
|
||||
case VDfgType::VarArray:
|
||||
case VDfgType::VarPacked:
|
||||
case VDfgType::Prev:
|
||||
case VDfgType::AstRd: // LCOV_EXCL_STOP
|
||||
vtx.v3fatalSrc("Hash should have been pre-computed");
|
||||
|
||||
|
|
@ -173,6 +174,7 @@ class V3DfgCse final {
|
|||
// Special vertices
|
||||
case VDfgType::Const: return a.as<DfgConst>()->num().isCaseEq(b.as<DfgConst>()->num());
|
||||
case VDfgType::CReset: return false;
|
||||
case VDfgType::Prev: return false;
|
||||
|
||||
case VDfgType::VarArray:
|
||||
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};
|
||||
// Pre-hash Ast references, these are all unique like variables
|
||||
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()) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -224,6 +224,10 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
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 { //
|
||||
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
|
||||
// - bit3: Written by logic in same module/netlist not represented in DFG
|
||||
// - 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
|
||||
const VNUser1InUse m_user1InUse;
|
||||
|
|
@ -116,69 +117,44 @@ class DataflowOptimize final {
|
|||
V3DfgPasses::synthesize(dfg, m_ctx);
|
||||
endOfStage("synthesize", dfg, {});
|
||||
|
||||
// Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a
|
||||
// DAG, and large, mostly acyclic graphs could not be optimized due to the presence of
|
||||
// small cycles.
|
||||
std::vector<std::unique_ptr<DfgGraph>> cyclicComps = dfg.extractCyclicComponents("cyclic");
|
||||
endOfStage("extractCyclic", dfg, cyclicComps);
|
||||
|
||||
// Attempt to convert cyclic components into acyclic ones
|
||||
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);
|
||||
}
|
||||
}
|
||||
// Extract the cyclic sub-graphs, so breakCycles can operate on small graphs,
|
||||
// make all of them acyclic, then merge them back to the main graph
|
||||
{
|
||||
std::vector<std::unique_ptr<DfgGraph>> comps = dfg.extractCyclicComponents("cyclic");
|
||||
for (const auto& cp : comps) V3DfgPasses::breakCycles(*cp, m_ctx);
|
||||
dfg.mergeGraphs(std::move(comps));
|
||||
endOfStage("breakCycles", dfg, {});
|
||||
}
|
||||
// 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
|
||||
V3DfgPasses::removeSelects(dfg, m_ctx.m_removeSelectsContext);
|
||||
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");
|
||||
// Split the now entirely acyclic DFG into [weakly] connected components
|
||||
std::vector<std::unique_ptr<DfgGraph>> comps = dfg.splitIntoComponents("acyclic");
|
||||
UASSERT(dfg.size() == 0, "DfgGraph should have become empty");
|
||||
endOfStage("splitAcyclic", dfg, acyclicComps);
|
||||
endOfStage("splitAcyclic", dfg, comps);
|
||||
|
||||
// Optimize each acyclic component
|
||||
for (auto& cp : acyclicComps) V3DfgPasses::inlineVars(*cp);
|
||||
endOfStage("inlineVars", dfg, acyclicComps);
|
||||
for (auto& cp : acyclicComps) V3DfgPasses::cse(*cp, m_ctx.m_cseContext0);
|
||||
endOfStage("cse0", dfg, acyclicComps);
|
||||
for (auto& cp : acyclicComps) V3DfgPasses::binToOneHot(*cp, m_ctx.m_binToOneHotContext);
|
||||
endOfStage("binToOneHot", dfg, acyclicComps);
|
||||
for (auto& cp : acyclicComps) V3DfgPasses::peephole(*cp, m_ctx.m_peepholeContext);
|
||||
endOfStage("peephole", dfg, acyclicComps);
|
||||
// Accumulate patterns for reporting
|
||||
if (v3Global.opt.dumpDfgPatterns()) {
|
||||
V3DfgPasses::dumpPatterns(acyclicComps);
|
||||
endOfStage("dumpPatterns");
|
||||
// Main pass pipeline - optimize each acyclic component
|
||||
{
|
||||
for (auto& cp : comps) V3DfgPasses::removeSelects(*cp, m_ctx.m_removeSelectsContext);
|
||||
endOfStage("removeSelects", dfg, comps);
|
||||
for (const auto& cp : comps) V3DfgPasses::inlineVars(*cp);
|
||||
endOfStage("inlineVars", dfg, comps);
|
||||
for (auto& cp : comps) V3DfgPasses::cse(*cp, m_ctx.m_cseContext0);
|
||||
endOfStage("cse0", dfg, comps);
|
||||
for (auto& cp : comps) V3DfgPasses::binToOneHot(*cp, m_ctx.m_binToOneHotContext);
|
||||
endOfStage("binToOneHot", dfg, comps);
|
||||
for (auto& cp : comps) V3DfgPasses::peephole(*cp, m_ctx.m_peepholeContext);
|
||||
endOfStage("peephole", dfg, comps);
|
||||
|
||||
// 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
|
||||
dfg.mergeGraphs(std::move(acyclicComps));
|
||||
dfg.mergeGraphs(std::move(cyclicComps));
|
||||
// Merge everything back under the main graph
|
||||
dfg.mergeGraphs(std::move(comps));
|
||||
endOfStage("optimized", dfg, {});
|
||||
|
||||
// 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->hasModWrRefs();
|
||||
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) {
|
||||
VL_DO_DANGLING(vscp->unlinkFrBack()->deleteTree(), vscp);
|
||||
++ctx.m_varsDeleted;
|
||||
|
|
|
|||
|
|
@ -43,15 +43,10 @@ void removeUnobservable(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
|||
void synthesize(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||
// Remove redundant selects
|
||||
void removeSelects(DfgGraph& dfg, V3DfgRemoveSelectsContext& ctx) VL_MT_DISABLED;
|
||||
// Attempt to make the given cyclic graph into an acyclic, or "less cyclic"
|
||||
// equivalent. If the returned pointer is null, then no improvement was
|
||||
// possible on the input graph. Otherwise the returned graph is an improvement
|
||||
// on the input graph, with at least some cycles eliminated. The returned
|
||||
// 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;
|
||||
// Make the given cyclic graph into an acyclic one, by tracing drivers, and
|
||||
// if that is unsuccessful, by inserting Prev vertices. The given graph will
|
||||
// always become acyclic after this pass.
|
||||
void breakCycles(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;
|
||||
// Construct binary to oneHot decoders
|
||||
void binToOneHot(DfgGraph&, V3DfgBinToOneHotContext&) VL_MT_DISABLED;
|
||||
// Common subexpression elimination
|
||||
|
|
|
|||
|
|
@ -266,6 +266,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
void deleteVertex(DfgVertex* vtxp) {
|
||||
UASSERT_OBJ(!m_vInfo[vtxp].m_workListIndex, vtxp, "Deleted Vertex is in work list");
|
||||
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
|
||||
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,
|
||||
// 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.
|
||||
const DfgVertexVar* const varp = vtxp->cast<DfgVertexVar>();
|
||||
if (varp && !varp->isVolatile() && !varp->hasDfgRefs()) {
|
||||
m_ctx.m_deleteps.push_back(varp->vscp());
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp);
|
||||
|
|
@ -2269,7 +2270,7 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
|
||||
void visit(DfgLogAnd* const vtxp) override {
|
||||
if (binary(vtxp)) return;
|
||||
if (binary(vtxp) || vtxp->rhsp()->unsafe()) return;
|
||||
|
||||
DfgVertex* const lhsp = vtxp->lhsp();
|
||||
DfgVertex* const rhsp = vtxp->rhsp();
|
||||
|
|
@ -2287,11 +2288,11 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
|
||||
void visit(DfgLogIf* const vtxp) override {
|
||||
if (binary(vtxp)) return;
|
||||
if (binary(vtxp) || vtxp->rhsp()->unsafe()) return;
|
||||
}
|
||||
|
||||
void visit(DfgLogOr* const vtxp) override {
|
||||
if (binary(vtxp)) return;
|
||||
if (binary(vtxp) || vtxp->rhsp()->unsafe()) return;
|
||||
|
||||
DfgVertex* const lhsp = vtxp->lhsp();
|
||||
DfgVertex* const rhsp = vtxp->rhsp();
|
||||
|
|
@ -2548,7 +2549,13 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
if (DfgShiftL* const lShiftLp = lhsp->cast<DfgShiftL>()) {
|
||||
if (!lShiftLp->hasMultipleSinks() && rhsp->dtype() == lShiftLp->rhsp()->dtype()) {
|
||||
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));
|
||||
return;
|
||||
}
|
||||
|
|
@ -2637,7 +2644,13 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
if (DfgShiftR* const lShiftRp = lhsp->cast<DfgShiftR>()) {
|
||||
if (!lShiftRp->hasMultipleSinks() && rhsp->dtype() == lShiftRp->rhsp()->dtype()) {
|
||||
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));
|
||||
return;
|
||||
}
|
||||
|
|
@ -2890,13 +2903,13 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
|
||||
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) {
|
||||
replace(make<DfgOr>(vtxp, condp, elsep));
|
||||
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) {
|
||||
replace(make<DfgAnd>(vtxp, condp, thenp));
|
||||
return;
|
||||
|
|
@ -2905,28 +2918,28 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
|
||||
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) {
|
||||
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
|
||||
replace(make<DfgAnd>(vtxp, maskp, elsep));
|
||||
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) {
|
||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||
replace(make<DfgOr>(vtxp, maskp, elsep));
|
||||
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) {
|
||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||
replace(make<DfgAnd>(vtxp, maskp, thenp));
|
||||
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) {
|
||||
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
|
||||
replace(make<DfgOr>(vtxp, maskp, thenp));
|
||||
|
|
@ -2935,7 +2948,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
}
|
||||
|
||||
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) {
|
||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||
DfgAnd* const andp = make<DfgAnd>(vtxp, maskp, tOrp->rhsp());
|
||||
|
|
@ -2943,7 +2957,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
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) {
|
||||
DfgVertex* const maskp = replicate(vtxp, condp);
|
||||
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) {
|
||||
if (vtx.hasSinks()) return false;
|
||||
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");
|
||||
if (varp->hasModWrRefs()) return false;
|
||||
if (varp->hasExtRefs()) return false;
|
||||
if (varp->hasPrev()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -87,6 +78,9 @@ class DfgRegularize final {
|
|||
&aVtx, "Mismatched vertices");
|
||||
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()) {
|
||||
// Add a temporary if it's cheaper to store and load from memory than recompute
|
||||
if (!aVtx.isCheaperThanLoad()) return true;
|
||||
|
|
@ -119,10 +113,6 @@ class DfgRegularize final {
|
|||
}
|
||||
|
||||
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
|
||||
DfgWorklist workList{m_dfg};
|
||||
|
||||
|
|
@ -141,7 +131,7 @@ class DfgRegularize final {
|
|||
});
|
||||
// Delete corresponsing Ast variable at the end
|
||||
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
|
||||
vtx.unlinkDelete(m_dfg);
|
||||
|
|
@ -173,7 +163,7 @@ class DfgRegularize final {
|
|||
UASSERT_OBJ(!varp->hasDfgRefs(), varp, "Should not have refs in other DfgGraph");
|
||||
|
||||
// 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
|
||||
// arbitrary logic. TODO: we should peeophole these away entirely)
|
||||
|
|
|
|||
|
|
@ -2005,6 +2005,11 @@ static void dfgSelectLogicForSynthesis(DfgGraph& dfg) {
|
|||
for (DfgVertex& vtx : dfg.opVertices()) {
|
||||
DfgLogic* const logicp = vtx.cast<DfgLogic>();
|
||||
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
|
||||
if (logicp->nodep()->keyword() == VAlwaysKwd::CONT_ASSIGN) {
|
||||
worklist.push_front(*logicp);
|
||||
|
|
@ -2016,10 +2021,8 @@ static void dfgSelectLogicForSynthesis(DfgGraph& dfg) {
|
|||
worklist.push_front(*logicp);
|
||||
continue;
|
||||
}
|
||||
// Simple blocks driving exactly 1 variable, e.g if (rst) a = b else a = c;
|
||||
if (!logicp->hasMultipleSinks() && cfg.nBlocks() <= 4 && cfg.nEdges() <= 4) {
|
||||
worklist.push_front(*logicp);
|
||||
}
|
||||
// Blocks driving exactly 1 variable
|
||||
if (!logicp->hasMultipleSinks()) worklist.push_front(*logicp);
|
||||
}
|
||||
|
||||
// Now expand to cover all logic driving the same set of variables and mark
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ protected:
|
|||
*DfgDataType::fromAst(vscp->varp()->dtypep())}
|
||||
, m_vscp{vscp} {
|
||||
// Increment reference count
|
||||
m_vscp->user1(m_vscp->user1() + 0x20);
|
||||
UASSERT_OBJ((m_vscp->user1() >> 5) > 0, m_vscp, "Reference count overflow");
|
||||
m_vscp->user1(m_vscp->user1() + 0x40);
|
||||
UASSERT_OBJ((m_vscp->user1() >> 6) > 0, m_vscp, "Reference count overflow");
|
||||
// Allocate sources
|
||||
newInput();
|
||||
newInput();
|
||||
|
|
@ -72,8 +72,8 @@ protected:
|
|||
public:
|
||||
~DfgVertexVar() {
|
||||
// Decrement reference count
|
||||
m_vscp->user1(m_vscp->user1() - 0x20);
|
||||
UASSERT_OBJ((m_vscp->user1() >> 5) >= 0, m_vscp, "Reference count underflow");
|
||||
m_vscp->user1(m_vscp->user1() - 0x40);
|
||||
UASSERT_OBJ((m_vscp->user1() >> 6) >= 0, m_vscp, "Reference count underflow");
|
||||
}
|
||||
ASTGEN_MEMBERS_DfgVertexVar;
|
||||
|
||||
|
|
@ -87,7 +87,6 @@ public:
|
|||
std::string srcName(size_t idx) const override final { return idx ? "defaultp" : "srcp"; }
|
||||
|
||||
// The Ast variable this vertex representess
|
||||
// AstVar* varp() const { return m_varp; }
|
||||
AstVarScope* vscp() const { return m_vscp; }
|
||||
|
||||
// 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; }
|
||||
|
||||
// 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
|
||||
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 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 {
|
||||
// A DfgPrev reads this variable
|
||||
if (hasPrev()) return true;
|
||||
// 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.
|
||||
if (hasDfgRefs()) return srcp() || defaultp();
|
||||
|
|
@ -162,6 +168,33 @@ public:
|
|||
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
|
||||
|
||||
|
|
@ -512,6 +545,7 @@ class DfgLogic final : public DfgVertexVariadic {
|
|||
AstScope* const m_scopep; // The AstScope m_nodep is under, iff scoped
|
||||
const std::unique_ptr<CfgGraph> m_cfgp;
|
||||
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_nonSynthesizable = false; // Logic is not synthesizeable (by DfgSynthesis)
|
||||
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; }
|
||||
std::vector<DfgVertex*>& synth() { 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; }
|
||||
void setSelectedForSynthesis() { m_selectedForSynthesis = true; }
|
||||
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,
|
||||
bool withScope) {
|
||||
if (funcp->slow()) putns(funcp, "VL_ATTR_COLD ");
|
||||
if (funcp->dpiCDeclOverride()) {
|
||||
putns(funcp, funcp->dpiCDecl() + ";\n");
|
||||
return;
|
||||
}
|
||||
if (!funcp->isDestructor()) {
|
||||
putns(funcp, funcp->rtnTypeVoid());
|
||||
puts(" ");
|
||||
|
|
|
|||
|
|
@ -485,7 +485,7 @@ void EmitCFunc::emitVarReset(const string& prefix, AstVar* varp, bool constructi
|
|||
const auto& mapr = initarp->map();
|
||||
for (const auto& itr : mapr) {
|
||||
AstNode* const valuep = itr.second->valuep();
|
||||
emitSetVarConstant(newPrefix + ".at(" + cvtToStr(itr.first) + ")",
|
||||
emitSetVarConstant(newPrefix + ".atWrite(" + cvtToStr(itr.first) + ")",
|
||||
VN_AS(valuep, Const));
|
||||
}
|
||||
} 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();
|
||||
for (const auto& itr : mapr) {
|
||||
AstNode* const valuep = itr.second->valuep();
|
||||
emitSetVarConstant(newPrefix + ".at(" + cvtToStr(itr.first) + ")",
|
||||
emitSetVarConstant(newPrefix + ".atWrite(" + cvtToStr(itr.first) + ")",
|
||||
VN_AS(valuep, Const));
|
||||
}
|
||||
} 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);
|
||||
} else if (VN_IS(dtypep, CDType)) {
|
||||
return ""; // Constructor does it
|
||||
} else if (VN_IS(dtypep, ClassRefDType)) {
|
||||
return ""; // Constructor does it
|
||||
} else if (const AstClassRefDType* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||
return adtypep->rawPointer() ? varNameProtected + suffix + " = nullptr;\n" : "";
|
||||
} else if (VN_IS(dtypep, IfaceRefDType)) {
|
||||
return varNameProtected + suffix + " = nullptr;\n";
|
||||
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
|
|
|
|||
|
|
@ -732,7 +732,8 @@ public:
|
|||
}
|
||||
void visit(AstAssocSel* nodep) override {
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
putnbs(nodep, ".at(");
|
||||
const std::string atFunc = nodep->isLValue() ? ".atWrite(" : ".at(";
|
||||
putnbs(nodep, atFunc);
|
||||
AstAssocArrayDType* const adtypep
|
||||
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), AssocArrayDType);
|
||||
UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type");
|
||||
|
|
@ -741,7 +742,8 @@ public:
|
|||
}
|
||||
void visit(AstWildcardSel* nodep) override {
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
putnbs(nodep, ".at(");
|
||||
const std::string atFunc = nodep->isLValue() ? ".atWrite(" : ".at(";
|
||||
putnbs(nodep, atFunc);
|
||||
AstWildcardArrayDType* const adtypep
|
||||
= VN_AS(nodep->fromp()->dtypep()->skipRefp(), WildcardArrayDType);
|
||||
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