Merge branch 'verilator:master' into fix/force-unpacked-bitselect

This commit is contained in:
Nikolai Kumar 2026-06-08 21:11:01 -05:00 committed by GitHub
commit c9ec8730a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
92 changed files with 2980 additions and 1346 deletions

2
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,2 @@
---
blank_issues_enabled: false

View File

@ -7,18 +7,34 @@ assignees: ''
---
<!--
Thanks for taking the time to report this.
Can you please attach an example that shows the issue or missing feature? (Must be openly licensed, completely self-contained so can directly run what you provide. Ideally use test_regress format, see https://veripool.org/guide/latest/contributing.html?highlight=test_regress#reporting-bugs)
Please avoid attaching screenshots that show text - you can convert images to text using e.g. https://ocr.space
Please remove this block when composing, and all other blocks starting with < !--.
-->
Can you please attach an example that shows the issue or missing feature?
<!-- Please replace this with your answer. Examples must be openly licensed, completely self-contained so can directly run what you provide. Ideally use test_regress format, see https://veripool.org/guide/latest/contributing.html?highlight=test_regress#reporting-bugs -->
What output from that test indicates it is wrong, and what is the correct or expected output? (Or, please make test self-checking if possible.)
<!-- Please replace this with your answer -->
What 'verilator' command line do we use to run your example?
<!-- Please replace this with your answer -->
What 'verilator --version' are you using? Did you try it with the git master version? Did you try it with other simulators?
<!-- Please replace this with your answer -->
What OS and distribution are you using?
<!-- Please replace this with your answer -->
May we assist you in trying to fix this in Verilator yourself?
(Please avoid attaching screenshots that show text - you can convert images to text using e.g. https://ocr.space)
<!-- Please replace this with your answer - then hit "Preview" to see the formatted result before submitting. -->

View File

@ -7,6 +7,14 @@ assignees: ''
---
<!--
Thanks for taking the time to report this.
If reporting a bug or requesting a feature please hit BACK on your browser and use a different issue templates.
Please remove this block when composing, and all other blocks starting with < !--.
-->
How may we help - what is your question?
(If reporting a bug or requesting a feature please hit BACK on your browser and use a different issue templates.)
<!-- Please replace this with your answer - then hit "Preview" to see the formatted result before submitting. -->

View File

@ -182,7 +182,7 @@ jobs:
uses: actions/upload-artifact@v7
with:
path: repo/notification
name: coverage-pr-notification
name: pr-notification
# Create GitHub issue for failed scheduled jobs
# This should always be the last job (we want an issue if anything breaks)

View File

@ -38,11 +38,11 @@ jobs:
./configure
make venv
source .venv/bin/activate
make -j 2 format CLANGFORMAT=clang-format-18
make -j 4 format CLANGFORMAT=clang-format-18
git status
- name: Push
run: |-
if [ -n "$(git status --porcelain)" ]; then
git commit . -m "Apply 'make format'" &&
git commit . -m "Apply 'make format' [ci skip]" &&
git push origin
fi

View File

@ -10,7 +10,7 @@ on:
paths: ["ci/**", ".github/workflows"]
workflow_dispatch:
workflow_run:
workflows: ["Code coverage"]
workflows: ["Code coverage", "RTLMeter"]
types: [completed]
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
@ -35,7 +35,7 @@ jobs:
name: Build content
runs-on: ubuntu-24.04
outputs:
coverage-pr-run-ids: ${{ steps.build.outputs.coverage-pr-run-ids }}
pr-run-ids: ${{ steps.build.outputs.pr-run-ids }}
steps:
- name: Checkout
uses: actions/checkout@v6
@ -44,7 +44,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
run: |
bash -x ./ci/ci-pages.bash
./ci/ci-pages.bash
ls -lsha
tree -L 3 pages
- name: Upload pages artifact
@ -83,5 +83,5 @@ jobs:
- name: Comment on PR
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
COVERAGE_PR_RUN_IDS: ${{ needs.build.outputs.coverage-pr-run-ids }}
run: bash -x ./ci/ci-pages-notify.bash
PR_RUN_IDS: ${{ needs.build.outputs.pr-run-ids }}
run: ./ci/ci-pages-notify.bash

View File

@ -15,6 +15,14 @@ on:
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:
@ -26,8 +34,10 @@ env:
jobs:
build:
runs-on: ${{ inputs.runs-on }}
name: Build
runs-on: ${{ inputs.runs-on }}
outputs:
archive: ${{ steps.create-archive.outputs.archive }}
steps:
- name: Install dependencies
run: |
@ -43,13 +53,17 @@ jobs:
uses: actions/cache@v5
with:
path: ccache
key: rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}
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@v6
with:
path: repo
ref: ${{ inputs.sha }}
fetch-depth: 0 # Required for 'git describe' used for 'verilator --version'
- name: Configure
@ -67,11 +81,16 @@ jobs:
run: make install
- name: Tar up installation
run: tar --posix -c -z -f verilator-rtlmeter.tar.gz install
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@v7
with:
path: verilator-rtlmeter.tar.gz
name: verilator-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}
path: ${{ steps.create-archive.outputs.archive }}
name: ${{ steps.create-archive.outputs.archive }}
overwrite: true

View File

@ -19,6 +19,15 @@ on:
description: "Compiler to use: 'gcc' or 'clang'"
type: string
required: true
verilator-archive-new:
description: "Name of the installation archive artifact from reusable-rtlmeter-build, new version"
type: string
required: true
verilator-archive-old:
description: "Name of the installation archive artifact from reusable-rtlmeter-build, old version"
type: string
required: false
default: ""
# Note: The combination of 'cases' and 'run-name' must be unique for all
# invocations of this workflow within a run of the parent workflow.
# These two are used together to generate a unique results file name.
@ -63,24 +72,6 @@ jobs:
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev || \
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev
- name: Download Verilator installation archive
uses: actions/download-artifact@v8
with:
name: verilator-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}
- name: Unpack Verilator installation archive
run: |
tar -x -z -f verilator-rtlmeter.tar.gz
echo "${{ github.workspace }}/install/bin" >> $GITHUB_PATH
- name: Use saved ccache
if: ${{ env.CCACHE_DISABLE == 0 }}
uses: actions/cache@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 }}
- name: Checkout RTLMeter
uses: actions/checkout@v6
with:
@ -91,32 +82,59 @@ jobs:
working-directory: rtlmeter
run: make venv
- name: Compile cases
- name: Use saved ccache
if: ${{ env.CCACHE_DISABLE == 0 }}
uses: actions/cache@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
########################################################################
- name: Download Verilator installation archive - new
uses: actions/download-artifact@v8
with:
name: ${{ inputs.verilator-archive-new }}
- name: Unpack Verilator installation archive - new
run: |
tar -x -z -f ${{ inputs.verilator-archive-new }}
mv install verilator-new
- name: Compile cases - new
working-directory: rtlmeter
run: |
export PATH="${{ github.workspace }}/verilator-new/bin:$PATH"
./rtlmeter run --timeout 60 --verbose --cases='${{inputs.cases}}' --compileArgs='${{inputs.compileArgs}}' --executeArgs='${{inputs.executeArgs}}' --nExecute=0
ccache -svv
- name: Execute cases
- name: Execute cases - new
working-directory: rtlmeter
continue-on-error: true # Do not fail on error, so we can at least save the successful results
run: |
export PATH="${{ github.workspace }}/verilator-new/bin:$PATH"
./rtlmeter run --timeout 60 --verbose --cases='${{inputs.cases}}' --compileArgs='${{inputs.compileArgs}}' --executeArgs='${{inputs.executeArgs}}'
- name: Collate results
- name: Collate results - new
id: results
working-directory: rtlmeter
run: |
export PATH="${{ github.workspace }}/verilator-new/bin:$PATH"
# Use 'inputs.cases' and 'inputs.run-name' to generate a unique file name
hash=$(md5sum <<< '${{ inputs.cases }} ${{ inputs.run-name }}' | awk '{print $1}')
echo "hash=${hash}" >> $GITHUB_OUTPUT
./rtlmeter collate --runName "${{ inputs.run-name }}" > ../results-${hash}.json
- name: Report results
- name: Report results - new
working-directory: rtlmeter
run: |
export PATH="${{ github.workspace }}/verilator-new/bin:$PATH"
./rtlmeter report --steps '*' --metrics '*' ../results-${{ steps.results.outputs.hash }}.json
- name: Upload results
- name: Upload results - new
uses: actions/upload-artifact@v7
with:
path: results-${{ steps.results.outputs.hash }}.json
@ -124,7 +142,65 @@ jobs:
overwrite: true
retention-days: 2
- name: Report status
- name: Report status - new
working-directory: rtlmeter
run: |- # This will fail the job if any of the runs failed
export PATH="${{ github.workspace }}/verilator-new/bin:$PATH"
./rtlmeter run --verbose --cases='${{inputs.cases}}' --compileArgs='${{inputs.compileArgs}}' --executeArgs='${{inputs.executeArgs}}'
# Clean up for run with old version
rm -rf work
rm -rf ${{ github.workspace }}/verilator-new
########################################################################
# Run with old Verilator **on same machine for consistency**
########################################################################
- name: Download Verilator installation archive - old
if: ${{ inputs.verilator-archive-old != '' }}
uses: actions/download-artifact@v8
with:
name: ${{ inputs.verilator-archive-old }}
- name: Unpack Verilator installation archive - old
if: ${{ inputs.verilator-archive-old != '' }}
run: |
tar -x -z -f ${{ inputs.verilator-archive-old }}
mv install verilator-old
- name: Compile cases - old
if: ${{ inputs.verilator-archive-old != '' }}
working-directory: rtlmeter
run: |
export PATH="${{ github.workspace }}/verilator-old/bin:$PATH"
./rtlmeter run --timeout 60 --verbose --cases='${{inputs.cases}}' --compileArgs='${{inputs.compileArgs}}' --executeArgs='${{inputs.executeArgs}}' --nExecute=0
ccache -svv
- name: Execute cases - old
if: ${{ inputs.verilator-archive-old != '' }}
working-directory: rtlmeter
run: |
export PATH="${{ github.workspace }}/verilator-old/bin:$PATH"
./rtlmeter run --timeout 60 --verbose --cases='${{inputs.cases}}' --compileArgs='${{inputs.compileArgs}}' --executeArgs='${{inputs.executeArgs}}'
- name: Collate results - old
if: ${{ inputs.verilator-archive-old != '' }}
working-directory: rtlmeter
run: |
export PATH="${{ github.workspace }}/verilator-old/bin:$PATH"
./rtlmeter collate --runName "${{ inputs.run-name }}" > ../reference-${{ steps.results.outputs.hash }}.json
- name: Report results - old
if: ${{ inputs.verilator-archive-old != '' }}
working-directory: rtlmeter
run: |
export PATH="${{ github.workspace }}/verilator-old/bin:$PATH"
./rtlmeter report --steps '*' --metrics '*' ../reference-${{ steps.results.outputs.hash }}.json
- name: Upload results - old
if: ${{ inputs.verilator-archive-old != '' }}
uses: actions/upload-artifact@v7
with:
path: reference-${{ steps.results.outputs.hash }}.json
name: rtlmeter-${{ inputs.tag }}-reference-${{ steps.results.outputs.hash }}
overwrite: true
retention-days: 2

View File

@ -1,48 +0,0 @@
---
# DESCRIPTION: Github actions config
# This name is key to badges in README.rst, so we use the name build
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: RTLMeter PR results
on:
workflow_run:
workflows: [RTLMeter]
types: [completed]
jobs:
publish:
name: Publish
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
permissions:
actions: read
pull-requests: write
steps:
- name: Download report
uses: actions/download-artifact@v8
with:
name: rtlmeter-pr-results
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Download PR number
uses: actions/download-artifact@v8
with:
name: pr-number
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
# Use the Verilator CI app to post the comment
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@v3.2.0
with:
app-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-pull-requests: write
- name: Comment on PR
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
ls -la
cat report.txt
gh pr --repo ${{ github.repository }} comment $(cat pr-number.txt) --body-file report.txt

View File

@ -40,179 +40,229 @@ jobs:
(github.event_name == 'workflow_dispatch') ||
(github.event_name == 'push')
runs-on: ubuntu-24.04
outputs:
old-sha: ${{ steps.start.outputs.old-sha }}
cases: ${{ steps.cases.outputs.cases }}
steps:
- name: Startup
run: echo
- name: Checkout
uses: actions/checkout@v6
build-gcc:
name: Build GCC
- name: Startup
id: start
env:
GH_TOKEN: ${{ github.token }}
run: |
[[ "${{ github.event_name }}" == 'pull_request' ]] || exit 0
# For a pull request, 'github.sha' is the test merge commit. Its
# first parent is the target branch commit it was merged with for this run.
OLD_SHA="$(gh api "repos/${{ github.repository }}/commits/${{ github.sha }}" --jq '.parents[0].sha')"
echo "old-sha=$OLD_SHA" >> "$GITHUB_OUTPUT"
- name: Load RTLMeter cases
id: cases
run: echo "cases=$(ci/ci-rtlmeter-cases.py --event-name ${{ github.event_name }})" >> "$GITHUB_OUTPUT"
build-gcc-new:
name: Build New Verilator - GCC
needs: start
uses: ./.github/workflows/reusable-rtlmeter-build.yml
with:
runs-on: ubuntu-24.04
cc: gcc
sha: ${{ github.sha }}
build-clang:
name: Build Clang
build-clang-new:
name: Build New Verilator - Clang
needs: start
uses: ./.github/workflows/reusable-rtlmeter-build.yml
with:
runs-on: ubuntu-24.04
cc: clang
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
with:
runs-on: ubuntu-24.04
cc: gcc
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
with:
runs-on: ubuntu-24.04
cc: clang
sha: ${{ needs.start.outputs.old-sha }}
run-gcc:
name: Run GCC | ${{ matrix.cases }}
needs: build-gcc
needs:
- start
- build-gcc-new
- build-gcc-old
# Run even if 'build-*-old' was skipped (no old SHA), but not if any
# dependency failed, the workflow was cancelled, or 'start' was skipped
# (e.g. the workflow was not enabled, so there is nothing to run against).
if: ${{ !failure() && !cancelled() && needs.start.result == 'success' }}
uses: ./.github/workflows/reusable-rtlmeter-run.yml
with:
tag: gcc
runs-on: ubuntu-24.04
cc: gcc
verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }}
verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }}
cases: ${{ matrix.cases }}
run-name: "gcc"
compileArgs: ""
executeArgs: ""
strategy:
fail-fast: false
max-parallel: ${{ github.event == 'schedule' && 2 || 7 }}
fail-fast: ${{ github.event_name == 'pull_request' }}
max-parallel: 7
matrix:
cases:
- "BlackParrot:1x1:*"
- "BlackParrot:4x4:*"
- "Caliptra:default:*"
- "NVDLA:*"
- "OpenPiton:1x1:*"
- "OpenPiton:4x4:*"
- "OpenTitan:*"
- "Servant:serv:*"
- "Servant:qerv:*"
- "VeeR-EH1:asic*"
- "VeeR-EH1:default*"
- "VeeR-EH1:hiperf*"
- "VeeR-EH2:asic*"
- "VeeR-EH2:default*"
- "VeeR-EH2:hiperf*"
- "VeeR-EL2:asic*"
- "VeeR-EL2:default*"
- "VeeR-EL2:hiperf*"
- "Vortex:mini:*"
- "Vortex:sane:*"
- "XiangShan:default-chisel3:* !*:linux"
- "XiangShan:default-chisel6:* !*:linux"
- "XiangShan:mini-chisel3:* !*:linux"
- "XiangShan:mini-chisel6:* !*:linux"
- "XuanTie-E902:*"
- "XuanTie-E906:*"
- "XuanTie-C906:*"
- "XuanTie-C910:*"
cases: ${{ fromJSON(needs.start.outputs.cases)['gcc'] }}
run-clang:
name: Run Clang | ${{ matrix.cases }}
needs: build-clang
needs:
- start
- build-clang-new
- build-clang-old
# Run even if 'build-*-old' was skipped (no old SHA), but not if any
# dependency failed, the workflow was cancelled, or 'start' was skipped
# (e.g. the workflow was not enabled, so there is nothing to run against).
if: ${{ !failure() && !cancelled() && needs.start.result == 'success' }}
uses: ./.github/workflows/reusable-rtlmeter-run.yml
with:
tag: clang
runs-on: ubuntu-24.04
cc: clang
verilator-archive-new: ${{ needs.build-clang-new.outputs.archive }}
verilator-archive-old: ${{ needs.build-clang-old.outputs.archive }}
cases: ${{ matrix.cases }}
run-name: "clang --threads 4"
compileArgs: "--threads 4"
executeArgs: ""
strategy:
fail-fast: false
max-parallel: ${{ github.event == 'schedule' && 2 || 7 }}
fail-fast: ${{ github.event_name == 'pull_request' }}
max-parallel: 7
matrix:
cases:
- "BlackParrot:1x1:*"
- "BlackParrot:4x4:*"
- "Caliptra:default:*"
- "NVDLA:*"
- "OpenPiton:1x1:*"
- "OpenPiton:4x4:*"
- "OpenTitan:*"
- "Servant:serv:*"
- "Servant:qerv:*"
- "VeeR-EH1:asic*"
- "VeeR-EH1:default*"
- "VeeR-EH1:hiperf*"
- "VeeR-EH2:asic*"
- "VeeR-EH2:default*"
- "VeeR-EH2:hiperf*"
- "VeeR-EL2:asic*"
- "VeeR-EL2:default*"
- "VeeR-EL2:hiperf*"
- "Vortex:mini:*"
- "Vortex:sane:*"
- "XiangShan:default-chisel3:* !*:linux"
- "XiangShan:default-chisel6:* !*:linux"
- "XiangShan:mini-chisel3:* !*:linux"
- "XiangShan:mini-chisel6:* !*:linux"
- "XuanTie-E902:*"
- "XuanTie-E906:*"
- "XuanTie-C906:*"
- "XuanTie-C910:*"
cases: ${{ fromJSON(needs.start.outputs.cases)['clang'] }}
run-gcc-hier:
name: Run GCC hier | ${{ matrix.cases }}
needs: build-gcc
needs:
- start
- build-gcc-new
- build-gcc-old
# Run even if 'build-*-old' was skipped (no old SHA), but not if any
# dependency failed, the workflow was cancelled, or 'start' was skipped
# (e.g. the workflow was not enabled, so there is nothing to run against).
if: ${{ !failure() && !cancelled() && needs.start.result == 'success' }}
uses: ./.github/workflows/reusable-rtlmeter-run.yml
with:
tag: gcc-hier
runs-on: ubuntu-24.04
cc: gcc
verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }}
verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }}
cases: ${{ matrix.cases }}
run-name: "gcc --hierarchical"
compileArgs: "--hierarchical"
executeArgs: ""
strategy:
fail-fast: false
max-parallel: ${{ github.event == 'schedule' && 2 || 7 }}
fail-fast: ${{ github.event_name == 'pull_request' }}
max-parallel: 6
matrix:
cases:
- "BlackParrot:1x1:* !-hier"
- "BlackParrot:4x4:* !-hier"
- "NVDLA:* !-hier"
- "OpenPiton:1x1:* !-hier"
- "OpenPiton:4x4:* !-hier"
- "OpenPiton:8x8:* !-hier"
- "OpenPiton:16x16:dhry !-hier"
- "XuanTie-C910:* !-hier"
cases: ${{ fromJSON(needs.start.outputs.cases)['gcc-hier'] }}
combine-results:
name: Combine results
needs: [run-gcc, run-clang, run-gcc-hier]
# Run if any of the dependencies have run, even if failed.
# That is: do not run if all skipped, or the workflow was cancelled.
if: ${{ (contains(needs.*.result, 'success') || contains(needs.*.result, 'failure')) && !cancelled() }}
# Skip if cancelled.
# On PRs, run if something succeeded and nothing failed.
# On non-PRs, run if anything succeeded or failed (so partial results are still published).
if: >-
${{ !cancelled() && (
(github.event_name == 'pull_request' && (contains(needs.*.result, 'success') && !contains(needs.*.result, 'failure')))
|| (github.event_name != 'pull_request' && (contains(needs.*.result, 'success') || contains(needs.*.result, 'failure')))
)}}
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
tag: [gcc, clang, gcc-hier]
outputs:
# The run tags, derived from the 'run-*' dependency job names
run-tags: ${{ steps.tags.outputs.tags }}
env:
GH_TOKEN: ${{ github.token }}
# 'gh' resolves the repo from git otherwise, but this job has no checkout
# of this repo at the workspace root
GH_REPO: ${{ github.repository }}
steps:
# Derive the run tags from the dependency job names ('run-<tag>')
- name: Determine run tags
id: tags
run: echo "tags=$(jq -r 'keys | map(sub("^run-"; "")) | join(" ")' <<< '${{ toJSON(needs) }}')" >> "$GITHUB_OUTPUT"
- name: Checkout RTLMeter
uses: actions/checkout@v6
with:
repository: "verilator/rtlmeter"
path: rtlmeter
- name: Setup RTLMeter venv
working-directory: rtlmeter
run: make venv
- name: Download all results
uses: actions/download-artifact@v8
with:
pattern: rtlmeter-${{ matrix.tag }}-results-*
path: all-results-${{ matrix.tag }}
merge-multiple: true
run: |
for tag in ${{ steps.tags.outputs.tags }}; do
mkdir artifacts all-results-$tag
gh run download ${{ github.run_id }} --pattern "rtlmeter-$tag-results-*" --dir artifacts
mv $(find artifacts -name "*.json") all-results-$tag/
rm -rf artifacts
done
- name: Combine results
working-directory: rtlmeter
run: |
./rtlmeter collate ../all-results-${{ matrix.tag }}/*.json > ../all-results-${{ matrix.tag }}.json
for tag in ${{ steps.tags.outputs.tags }}; do
./rtlmeter collate ../all-results-$tag/*.json > ../all-results-$tag.json
done
- name: Upload combined results
uses: actions/upload-artifact@v7
with:
path: all-results-${{ matrix.tag }}.json
name: all-results-${{ matrix.tag }}
path: all-results-*.json
name: all-results
overwrite: true
retention-days: 30
# The reference (old) results only exist for pull requests, where the
# 'build-*-old' jobs run and the run jobs produce '*-reference-*' artifacts.
- name: Download reference results
if: ${{ github.event_name == 'pull_request' }}
run: |
for tag in ${{ steps.tags.outputs.tags }}; do
mkdir artifacts all-reference-$tag
gh run download ${{ github.run_id }} --pattern "rtlmeter-$tag-reference-*" --dir artifacts
mv $(find artifacts -name "*.json") all-reference-$tag/
rm -rf artifacts
done
- name: Combine reference results
if: ${{ github.event_name == 'pull_request' }}
working-directory: rtlmeter
run: |
for tag in ${{ steps.tags.outputs.tags }}; do
./rtlmeter collate ../all-reference-$tag/*.json > ../all-reference-$tag.json
done
- name: Upload reference results
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@v7
with:
path: all-reference-*.json
name: all-reference
overwrite: true
retention-days: 30
@ -230,9 +280,8 @@ jobs:
- name: Download combined results
uses: actions/download-artifact@v8
with:
pattern: all-results-*
name: all-results
path: results
merge-multiple: true
- name: Upload published results
uses: actions/upload-artifact@v7
with:
@ -276,7 +325,7 @@ jobs:
prepare-pr-results:
name: Prepare Pull Request results
needs: combine-results
if: ${{ github.event_name == 'pull_request' && github.repository == 'verilator/verilator' && contains(needs.*.result, 'success') && !cancelled() }}
if: ${{ github.event_name == 'pull_request' && github.repository == 'verilator/verilator' && needs.combine-results.result == 'success' }}
runs-on: ubuntu-24.04
permissions:
actions: read
@ -286,106 +335,51 @@ jobs:
with:
repository: "verilator/rtlmeter"
path: rtlmeter
- name: Setup RTLMeter venv
working-directory: rtlmeter
run: make venv
- name: Download combined results
uses: actions/download-artifact@v8
- name: Checkout Verilator
uses: actions/checkout@v6
with:
pattern: all-results-*
path: all-results
merge-multiple: true
- name: Get scheduled run info
id: scheduled-info
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ID=$(gh run --repo ${{ github.repository }} list --workflow RTLMeter --event schedule --status success --limit 1 --json databaseId --jq ".[0].databaseId")
echo "id=$ID" >> $GITHUB_OUTPUT
URL=$(gh run --repo ${{ github.repository }} view $ID --json url --jq ".url")
echo "url=$URL" >> $GITHUB_OUTPUT
NUM=$(gh run --repo ${{ github.repository }} view $ID --json number --jq ".number")
echo "num=$NUM" >> $GITHUB_OUTPUT
DATE=$(gh run --repo ${{ github.repository }} view $ID --json createdAt --jq ".createdAt")
echo "date=$DATE" >> $GITHUB_OUTPUT
- name: Download scheduled run results
uses: actions/download-artifact@v8
with:
name: published-results
path: nightly-results
run-id: ${{ steps.scheduled-info.outputs.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Compare results
working-directory: rtlmeter
run: |
for tag in gcc clang gcc-hier; do
ADATA=../nightly-results/all-results-${tag}.json
BDATA=../all-results/all-results-${tag}.json
touch ../verilate-${tag}.txt
touch ../execute-${tag}.txt
touch ../cppbuild-${tag}.txt
if [[ ! -e $ADATA ]]; then
continue
fi
./rtlmeter compare --cases '* !Example:* !*:hello' --steps "verilate" --metrics "elapsed memory" $ADATA $BDATA > ../verilate-${tag}.txt
cat ../verilate-${tag}.txt
./rtlmeter compare --cases '* !Example:* !*:hello' --steps "execute" --metrics "speed memory elapsed" $ADATA $BDATA > ../execute-${tag}.txt
cat ../execute-${tag}.txt
./rtlmeter compare --cases '* !Example:* !*:hello' --steps "cppbuild" --metrics "elapsed memory cpu codeSize" $ADATA $BDATA > ../cppbuild-${tag}.txt
cat ../cppbuild-${tag}.txt
done
path: verilator
- name: Create report
id: report
working-directory: verilator
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -x
NUM=$(gh run --repo ${{ github.repository }} view ${{ github.run_id }} --json number --jq ".number")
URL=$(gh run --repo ${{ github.repository }} view ${{ github.run_id }} --json url --jq ".url")
echo -n "Performance metrics for PR workflow [#$NUM]($URL) (B) compared to scheduled run" > report.txt
echo -n " [#${{ steps.scheduled-info.outputs.num }}](${{ steps.scheduled-info.outputs.url }}) (A)" >> report.txt
echo " from ${{ steps.scheduled-info.outputs.date }}" >> report.txt
for tag in gcc clang gcc-hier; do
echo "" >> report.txt
if [[ $tag == "gcc" ]]; then
echo "<details open>" >> report.txt
else
echo "<details>" >> report.txt
fi
echo -n "<summary><strong><em>" >> report.txt
jq -rj ".[0].runName" all-results/all-results-${tag}.json >> report.txt
echo "</em></strong></summary>" >> report.txt
awk -v RS= -v tag=${tag} '{print > sprintf("frag-%02d-verilate-%s.txt",NR,tag)}' verilate-${tag}.txt
awk -v RS= -v tag=${tag} '{print > sprintf("frag-%02d-execute-%s.txt" ,NR,tag)}' execute-${tag}.txt
awk -v RS= -v tag=${tag} '{print > sprintf("frag-$02d-cppbuild-%s.txt",NR,tag)}' cppbuild-${tag}.txt
for f in $(ls -1 frag-*-verilate-${tag}.txt | sort) $(ls -1 frag-*-execute-${tag}.txt | sort) $(ls -1 frag-*-cppbuild-${tag}.txt | sort); do
if [[ $f == frag-01-verilate-${tag}.txt || $f == frag-01-execute-${tag}.txt ]]; then
echo "<details open>" >> report.txt
else
echo "<details>" >> report.txt
fi
echo -n "<summary>" >> report.txt
head -n 1 $f | tr -d '\n' >> report.txt
echo "</summary>" >> report.txt
echo '<pre>' >> report.txt
tail -n +2 $f >> report.txt
echo '</pre>' >> report.txt
echo "</details>" >> report.txt
done
echo "</details>" >> report.txt
done
cat report.txt
ln -s ../rtlmeter rtlmeter
gh repo set-default ${{ github.repository }}
# Create run report - save status to fail job if the script did
STATUS=0
ci/ci-rtlmeter-report.bash ${{ github.run_id }} ${{ github.sha }} ${{ needs.combine-results.outputs.run-tags }} || STATUS=$?
echo "status=$STATUS" >> "$GITHUB_OUTPUT"
# Create the report artifact
mkdir ../report-artifact
mv rtlmeter-report/report ../report-artifact/
echo ${{ github.event.number }} > ../report-artifact/pr-number.txt
# Create the notification artifact
mkdir ../notification-artifact
mv rtlmeter-report/notification.txt ../notification-artifact/body.txt
echo ${{ github.event.number }} > ../notification-artifact/pr-number.txt
- name: Upload report
uses: actions/upload-artifact@v7
with:
path: report.txt
name: rtlmeter-pr-results
- name: Save PR number
run: echo ${{ github.event.number }} > pr-number.txt
- name: Upload PR number
path: report-artifact
name: rtlmeter-report
- name: Upload notification
uses: actions/upload-artifact@v7
with:
path: pr-number.txt
name: pr-number
path: notification-artifact
name: pr-notification
- name: Report status
run: exit ${{ steps.report.outputs.status }}
# Create GitHub issue for failed scheduled jobs
# This should always be the last job (we want an issue if anything breaks)

22
Changes
View File

@ -21,6 +21,15 @@ Verilator 5.049 devel
**Other:**
* Add `+verilator+log+file` (#4505) (#7645). [Tracy Narine]
* Add peak memory usage to `--stats`. [Geza Lore, Testorrent USA, Inc.]
* Add error on mixed-initialization (#7352) (#7357).
* Add `--coverage-per-instance` (#7636). [Yogish Sekhar]
* Add NOTREDOP error on reduction and negation operators (#7417) (#7623) (#7624).
* Add hierarchy-aware reporting to `verilator_coverage` (#7657). [Yogish Sekhar]
* Improve `--coverage-fsm` (#7490) (#7529) (#7561) (#7573) (#7619). [Yogish Sekhar]
* Change `+verilator+seed` to default to 1, and 0 to randomly select (#7325) (#7516). [Miguel]
* Change JSON to include parameter constant mnemonics for FSM Coverage (#7531). [Yogish Sekhar]
* Support printing enum names for %p and %s (#5523) (#7338 repair) (#7521) (#7527). [Nick Brereton]
* Support weak `until` / `until_with` property operators (#7290) (#7548) (#7685). [Yilou Wang]
* Support `s_eventually` (#7291) (#7508). [Bartłomiej Chmiel, Antmicro Ltd.]
@ -44,20 +53,12 @@ Verilator 5.049 devel
* Support generic interface arrays (#7604). [Krzysztof Bieganski, Antmicro Ltd.]
* Support FSM detection in primitive wrappers (#7607). [Yogish Sekhar]
* Support busses with mix of pullup/pulldown (#7632). [Lucas Amaral]
* Support forceable on unpacked array variables (#7677) (#7678). [Nikolai Kumar]
* Support pre/post increment/decrement inside && and || (#7683). [Nick Brereton]
* Support streaming into 2-D unpacked arrays (#7686) (#7687). [Paul Swirhun]
* Support if/if-else in properties (#7692). [Artur Bieniek, Antmicro Ltd.]
* Support process::self().srand() (#7695). [Igor Zaworski, Antmicro Ltd.]
* Support MacOS lldb (#7697). [Tracy Narine]
* Add `+verilator+log+file` (#4505) (#7645). [Tracy Narine]
* Add peak memory usage to `--stats`. [Geza Lore, Testorrent USA, Inc.]
* Add error on mixed-initialization (#7352) (#7357).
* Add `--coverage-per-instance` (#7636). [Yogish Sekhar]
* Add NOTREDOP error on reduction and negation operators (#7417) (#7623) (#7624).
* Add hierarchy-aware reporting to `verilator_coverage` (#7657). [Yogish Sekhar]
* Improve `--coverage-fsm` (#7490) (#7529) (#7561) (#7573) (#7619). [Yogish Sekhar]
* Change `+verilator+seed` to default to 1, and 0 to randomly select (#7325) (#7516). [Miguel]
* Change JSON to include parameter constant mnemonics for FSM Coverage (#7531). [Yogish Sekhar]
* Optimize emitting to_string() for compiler speedup (#7468). [Jakub Michalski, Antmicro Ltd.]
* Optimize additional DFG peephole cases (#7553). [Varun Koyyalagunta, Testorrent USA, Inc.]
* Optimize forced signal handling (#7554 partial) (#7572) (#7594) (#7596). [Krzysztof Bieganski, Artur Bieniek, Antmicro Ltd.]
@ -68,6 +69,8 @@ Verilator 5.049 devel
* Optimize formatting functions (#7701) (#7702) (#7703). [Geza Lore, Testorrent USA, Inc.]
* Optimize DPI import argument passing (#7704). [Geza Lore, Testorrent USA, Inc.]
* Optimize runtime assertOn() checks (#7707). [Geza Lore, Testorrent USA, Inc.]
* Optimize $countones and $onehot in DFG. [Geza Lore, Testorrent USA, Inc.]
* Optimize procedural loop unrolling. [Geza Lore, Testorrent USA, Inc.]
* Fix TSP variable ordering for mtasks (#5342) (#7610). [Muzaffer Kal]
* Fix inlining static initializer in V3Gate (#5381) (#7503). [Andrew Nolte] [Geza Lore, Testorrent USA, Inc.]
* Fix segmentation fault when using --trace with --lib-create (#7299) (#7518). [anonkey]
@ -96,6 +99,7 @@ Verilator 5.049 devel
* Fix internal error on consecutive repetition with N > 256 (#7552) (#7603). [Yilou Wang]
* Fix inherited rand array with .size + foreach constraint (#7558) (#7650). [Yilou Wang]
* Fix biased bit distribution under value < (1 << N) constraints (#7563) (#7684). [Yilou Wang]
* Fix HIERPARAM lint checking (#7570) (#7690). [em2machine]
* Fix display of %m in non-first argument (#7574).
* Fix floating point compile warning on min/max delays.
* Fix force of unpacked arrays (#7579) (#7580). [Zubin Jain]

View File

@ -535,6 +535,7 @@ PY_PROGRAMS = \
# Python files, subject to format but not lint
PY_FILES = \
$(PY_PROGRAMS) \
ci/*.py \
test_regress/t/*.py \
# Python files, test_regress tests

View File

@ -4,7 +4,7 @@
# SPDX-FileCopyrightText: 2025 Geza Lore
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# Notify PRs via comment that their coverage reports are available
# Notify PRs via comment that their workflow reports are available
# Get the current repo URL - might differ on a fork
readonly REPO_URL=$(gh repo view --json url --jq .url)
@ -13,7 +13,7 @@ readonly REPO_URL=$(gh repo view --json url --jq .url)
ARTIFACTS_ROOT=artifacts
mkdir -p ${ARTIFACTS_ROOT}
for RUN_ID in ${COVERAGE_PR_RUN_IDS//,/ }; do
for RUN_ID in ${PR_RUN_IDS//,/ }; do
echo "@@@ Processing run ${RUN_ID}"
# Create workflow artifacts directory
@ -21,7 +21,7 @@ for RUN_ID in ${COVERAGE_PR_RUN_IDS//,/ }; do
mkdir -p ${ARTIFACTS_DIR}
# Download artifact of this run, if exists
gh run download ${RUN_ID} --name coverage-pr-notification --dir ${ARTIFACTS_DIR} || true
gh run download ${RUN_ID} --name pr-notification --dir ${ARTIFACTS_DIR} || true
ls -lsha ${ARTIFACTS_DIR}
# Move on if no notification is required
@ -35,7 +35,7 @@ for RUN_ID in ${COVERAGE_PR_RUN_IDS//,/ }; do
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 == "coverage-pr-notification") | .id')
ARTIFACT_ID=$(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}"

View File

@ -5,8 +5,7 @@
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# This scipt build the content of the GitHub Pages for the repository.
# Currently this only hosts code coverage reports, but it would be possible to
# add any other contents to the page in parallel here.
# Currently this hosts code coverage reports, and PR RTLMeter detailed reports.
# Developer note: You should be able to run this script in your local checkout
# if you have GitHub CLI (command 'gh') setup, authenticated ('gh auth login'),
@ -25,6 +24,9 @@ if [[ -z "$GITHUB_OUTPUT" ]]; then
GITHUB_OUTPUT=github-output.txt
fi
# Run IDs of PR jobs processed
PR_RUN_IDS=""
# Populates ${PAGES_ROOT}/coverage-reports
compile_coverage_reports() {
# We will process all runs up to and including this date. This is chosen to be
@ -42,7 +44,7 @@ compile_coverage_reports() {
jq "." completedRuns.json
# Create artifacts root directory
local ARTIFACTS_ROOT=artifacts
local ARTIFACTS_ROOT=artifacts-coverage
mkdir -p ${ARTIFACTS_ROOT}
# Create coverage reports root directory
@ -53,9 +55,6 @@ compile_coverage_reports() {
local CONTENTS=contents.tmp
echo > ${CONTENTS}
# Run IDs of PR jobs processed
local PR_RUN_IDS=""
# Iterate over all unique event types that triggered the workflows
for EVENT in $(jq -r 'map(.event) | sort | unique | .[]' completedRuns.json); do
echo "@@@ Processing '${EVENT}' runs"
@ -72,13 +71,7 @@ compile_coverage_reports() {
jq "." workflow.json
# Record run ID of PR job
if [[ $EVENT == "pull_request" ]]; then
if [[ -z "$PR_RUN_IDS" ]]; then
PR_RUN_IDS="$RUN_ID"
else
PR_RUN_IDS="$PR_RUN_IDS,$RUN_ID"
fi
fi
[[ $EVENT != "pull_request" ]] || PR_RUN_IDS="$PR_RUN_IDS $RUN_ID"
# Create workflow artifacts directory
local ARTIFACTS_DIR=${ARTIFACTS_ROOT}/${RUN_ID}
@ -148,7 +141,7 @@ CONTENTS_TEMPLATE
<body>
$(cat ${CONTENTS})
<h4>Assembled $(date --iso-8601=minutes --utc)</h1>
<h4>Assembled $(date --iso-8601=minutes --utc)</h4>
<body>
</html>
@ -156,12 +149,136 @@ INDEX_TEMPLATE
# Report size
du -shc ${COVERAGE_ROOT}/*
}
# Set output
echo "coverage-pr-run-ids=${PR_RUN_IDS}" >> $GITHUB_OUTPUT
# Populates ${PAGES_ROOT}/rtlmeter-reports
compile_rtlmeter_reports() {
# We will process all runs up to and including this date. This is chosen to be
# slightly less than the artifact retention period for simplicity.
local OLDEST=$(date --date="28 days ago" --iso-8601=date)
# Gather all RTLMeter workflow runs within the time window
gh run list -w rtlmeter.yml --limit 1000 --created ">=${OLDEST}" --json "databaseId,event,status,conclusion,createdAt,number" > recentRuns.json
echo @@@ Recent runs:
jq "." recentRuns.json
# Select completd runs that were not cancelled or skipped, sort by descending run number
jq 'sort_by(-.number) | map(select(.status == "completed" and (.conclusion == "success" or .conclusion == "failure")))' recentRuns.json > completedRuns.json
echo @@@ Completed with success or failure:
jq "." completedRuns.json
# Create artifacts root directory
local ARTIFACTS_ROOT=artifacts-rtlmeter
mkdir -p ${ARTIFACTS_ROOT}
# Create rtlmeter reports root directory
local RTLMETER_ROOT=${PAGES_ROOT}/rtlmeter-reports
mkdir -p ${RTLMETER_ROOT}
# Create index page contents fragment
local CONTENTS=contents.tmp
echo > ${CONTENTS}
# Iterate over all unique event types that triggered the workflows
for EVENT in $(jq -r 'map(.event) | sort | unique | .[]' completedRuns.json); do
echo "@@@ Processing '${EVENT}' runs"
# Emit section header if a report exists with this event type
EMIT_SECTION_HEADER=1
# For each worfklow run that was triggered by this event type
for RUN_ID in $(jq ".[] | select(.event == \"${EVENT}\") |.databaseId" completedRuns.json); do
echo "@@@ Processing run ${RUN_ID}"
# Extract the info of this run
jq ".[] | select(.databaseId == $RUN_ID)" completedRuns.json > workflow.json
jq "." workflow.json
# Record run ID of PR job
[[ $EVENT != "pull_request" ]] || PR_RUN_IDS="$PR_RUN_IDS $RUN_ID"
# Create workflow artifacts directory
local ARTIFACTS_DIR=${ARTIFACTS_ROOT}/${RUN_ID}
mkdir -p ${ARTIFACTS_DIR}
# Download artifacts of this run, if exists
gh run download ${RUN_ID} --name rtlmeter-report --dir ${ARTIFACTS_DIR} || true
ls -lsha ${ARTIFACTS_DIR}
# Move on if no RTLMeter report is available
if [ ! -d ${ARTIFACTS_DIR}/report ]; then
echo "No RTLMeter report found"
continue
fi
echo "RTLMeter report found"
# Emit section header
if [[ -n $EMIT_SECTION_HEADER ]]; then
unset EMIT_SECTION_HEADER
echo "<h4>RTLMeter reports for '${EVENT}' runs:</h4>" >> ${CONTENTS}
fi
# Create pages subdirectory
mv ${ARTIFACTS_DIR}/report ${RTLMETER_ROOT}/${RUN_ID}
# Add index page content
local WORKFLOW_CREATED=$(jq -r '.createdAt' workflow.json)
local WOFKRLOW_NUMBER=$(jq -r '.number' workflow.json)
cat >> ${CONTENTS} <<CONTENTS_TEMPLATE
Run <a href="${RUN_ID}/index.html">#${WOFKRLOW_NUMBER}</a>
| GitHub: <a href="${REPO_URL}/actions/runs/${RUN_ID}">${RUN_ID}</a>
| started at: ${WORKFLOW_CREATED}
CONTENTS_TEMPLATE
if [ -e ${ARTIFACTS_DIR}/pr-number.txt ]; then
local PRNUMBER=$(cat ${ARTIFACTS_DIR}/pr-number.txt)
echo " | Pull request: <a href=\"${REPO_URL}/pull/${PRNUMBER}\">#${PRNUMBER}</a>" >> ${CONTENTS}
fi
echo "<br>" >> ${CONTENTS}
done
# Section break
if [[ -z "$EMIT_SECTION_HEADER" ]]; then
echo "<hr>" >> ${CONTENTS}
fi
done
# Write rtlmeter report index.html
cat > ${RTLMETER_ROOT}/index.html <<INDEX_TEMPLATE
<html>
<head>
<title>Verilator CI RTLMeter reports</title>
<style>
body {
font-family: courier, serif;
background-color: #f3f3f3;
a {
color: #008fd7;
}
}
</style>
</head>
<body>
$(cat ${CONTENTS})
<h4>Assembled $(date --iso-8601=minutes --utc)</h4>
<body>
</html>
INDEX_TEMPLATE
# Report size
du -shc ${RTLMETER_ROOT}/*
}
# Compilie coverage reports
compile_coverage_reports;
# Compile RTLMeter reports
compile_rtlmeter_reports;
# You can build any other content here to be put under ${PAGES_ROOT}
# Set output to all affected unique PR run IDs (comma separated, no trailing comma)
IDS=$(echo ${PR_RUN_IDS} | xargs -n1 | sort -u | paste -sd,)
echo "pr-run-ids=${IDS}" >> $GITHUB_OUTPUT

210
ci/ci-rtlmeter-cases.py Executable file
View File

@ -0,0 +1,210 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: RTLMeter cases to run in the rtlmeter.yml CI workflow
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
###############################################################################
# Defines the RTLMeter cases to run in CI. This is consumed by the 'start' job
# in rtlmeter.yml, which captures the printed JSON and feeds it to the run job
# matrices.
###############################################################################
import argparse
import json
# Cases to run for scheduled runs, keyyed by the 'run tag' used in rtlmeter.yml
# The associated priority is roughly the number of minutes it takes to run the
# job, the emitted job matrix will try to start longest jobs first.
# Cases to run for non-PR jobs (e.g.: nightly schedule)
# fmt: off
CASES = {
"gcc": [
("BlackParrot:1x1:*", 5),
("BlackParrot:2x2:*", 14),
("BlackParrot:4x4:*", 25),
("Caliptra:default:*", 19),
("NVDLA:*", 29),
("OpenPiton:1x1:*", 4),
("OpenPiton:2x2:*", 7),
("OpenPiton:4x4:*", 10),
("OpenTitan:*", 16),
("Servant:qerv:*", 2),
("Servant:serv:*", 2),
("VeeR-EH1:asic:*", 4),
("VeeR-EH1:default:*", 6),
("VeeR-EH1:hiperf:*", 5),
("VeeR-EH2:asic:*", 13),
("VeeR-EH2:default:*", 6),
("VeeR-EH2:hiperf:*", 12),
("VeeR-EL2:asic:*", 4),
("VeeR-EL2:default:*", 4),
("VeeR-EL2:hiperf:*", 4),
("Vortex:mini:*", 3),
("Vortex:sane:*", 6),
("XiangShan:default-chisel3:* !*:linux", 31),
("XiangShan:default-chisel6:* !*:linux", 28),
("XiangShan:mini-chisel3:* !*:linux", 13),
("XiangShan:mini-chisel6:* !*:linux", 15),
("XuanTie-C906:*", 5),
("XuanTie-C910:*", 9),
("XuanTie-E902:*", 5),
("XuanTie-E906:*", 6),
],
"clang": [
("BlackParrot:1x1:*", 5),
("BlackParrot:2x2:*", 9),
("BlackParrot:4x4:*", 12),
("Caliptra:default:*", 16),
("NVDLA:*", 28),
("OpenPiton:1x1:*", 5),
("OpenPiton:2x2:*", 6),
("OpenPiton:4x4:*", 8),
("OpenTitan:*", 10),
("Servant:qerv:*", 17),
("Servant:serv:*", 15),
("VeeR-EH1:asic:*", 6),
("VeeR-EH1:default:*", 8),
("VeeR-EH1:hiperf:*", 5),
("VeeR-EH2:asic:*", 9),
("VeeR-EH2:default:*", 8),
("VeeR-EH2:hiperf:*", 7),
("VeeR-EL2:asic:*", 4),
("VeeR-EL2:default:*", 5),
("VeeR-EL2:hiperf:*", 5),
("Vortex:mini:*", 3),
("Vortex:sane:*", 5),
("XiangShan:default-chisel3:* !*:linux", 21),
("XiangShan:default-chisel6:* !*:linux", 19),
("XiangShan:mini-chisel3:* !*:linux", 8),
("XiangShan:mini-chisel6:* !*:linux", 8),
("XuanTie-C906:*", 3),
("XuanTie-C910:*", 7),
("XuanTie-E902:*", 7),
("XuanTie-E906:*", 7),
],
"gcc-hier": [
("BlackParrot:1x1:*", 4),
("BlackParrot:2x2:*", 16),
("BlackParrot:4x4:*", 25),
("NVDLA:*", 25),
("OpenPiton:16x16:dhry", 16),
("OpenPiton:1x1:*", 4),
("OpenPiton:2x2:*", 4),
("OpenPiton:4x4:*", 4),
("OpenPiton:8x8:*", 6),
("XuanTie-C910:*", 7),
],
}
# fmt: on
# Cases to run for PR jobs
# fmt: off
CASES_PR = {
"gcc": [
("BlackParrot:1x1:*", 5),
("BlackParrot:4x4:*", 25),
("Caliptra:default:*", 19),
("NVDLA:*", 29),
("OpenPiton:1x1:*", 4),
("OpenPiton:4x4:*", 10),
("OpenTitan:*", 16),
("Servant:qerv:*", 2),
("Servant:serv:*", 2),
("VeeR-EH1:asic:*", 4),
("VeeR-EH1:default:*", 6),
("VeeR-EH1:hiperf:*", 5),
("VeeR-EH2:asic:*", 13),
("VeeR-EH2:default:*", 6),
("VeeR-EH2:hiperf:*", 12),
("VeeR-EL2:asic:*", 4),
("VeeR-EL2:default:*", 4),
("VeeR-EL2:hiperf:*", 4),
("Vortex:mini:*", 3),
("Vortex:sane:*", 6),
("XiangShan:default-chisel3:* !*:linux", 31),
("XiangShan:default-chisel6:* !*:linux", 28),
("XiangShan:mini-chisel3:* !*:linux", 13),
("XiangShan:mini-chisel6:* !*:linux", 15),
("XuanTie-C906:*", 5),
("XuanTie-C910:*", 9),
("XuanTie-E902:*", 5),
("XuanTie-E906:*", 6),
],
"clang": [
("BlackParrot:1x1:*", 5),
("BlackParrot:4x4:*", 12),
("Caliptra:default:*", 16),
("NVDLA:*", 28),
("OpenPiton:1x1:*", 5),
("OpenPiton:4x4:*", 8),
("OpenTitan:*", 10),
("Servant:qerv:*", 17),
("Servant:serv:*", 15),
("VeeR-EH1:asic:*", 6),
("VeeR-EH1:default:*", 8),
("VeeR-EH1:hiperf:*", 5),
("VeeR-EH2:asic:*", 9),
("VeeR-EH2:default:*", 8),
("VeeR-EH2:hiperf:*", 7),
("VeeR-EL2:asic:*", 4),
("VeeR-EL2:default:*", 5),
("VeeR-EL2:hiperf:*", 5),
("Vortex:mini:*", 3),
("Vortex:sane:*", 5),
("XiangShan:default-chisel3:* !*:linux", 21),
("XiangShan:default-chisel6:* !*:linux", 19),
("XiangShan:mini-chisel3:* !*:linux", 8),
("XiangShan:mini-chisel6:* !*:linux", 8),
("XuanTie-C906:*", 3),
("XuanTie-C910:*", 7),
("XuanTie-E902:*", 7),
("XuanTie-E906:*", 7),
],
"gcc-hier": [
("BlackParrot:1x1:*", 4),
("BlackParrot:4x4:*", 25),
("NVDLA:*", 25),
("OpenPiton:16x16:dhry", 16),
("OpenPiton:1x1:*", 4),
("OpenPiton:4x4:*", 4),
("OpenPiton:8x8:*", 6),
("XuanTie-C910:*", 7),
],
}
# fmt: on
parser = argparse.ArgumentParser(
description="Print the RTLMeter CI cases as JSON, keyed by run tag")
parser.add_argument("--event-name",
required=True,
help="GitHub event name that triggered the workflow")
parser.add_argument(
"--ci-testing",
action="store_true",
help="Print only the shortest (lowest priority) case per tag, to reduce CI testing time")
args = parser.parse_args()
# Pull requests use the reduced 'CASES_PR' set, other events (e.g.: the nightly
# schedule) use the full 'CASES' set.
is_pr = args.event_name == "pull_request"
selected = CASES_PR if is_pr else CASES
cases = {}
for tag, entries in selected.items():
# Highest priority (longest) first
ordered = sorted(entries, key=lambda cp: cp[1], reverse=True)
# While tweaking the CI, keep only the shortest (lowest priority) case to minimise churn time
if args.ci_testing:
ordered = ordered[-1:]
# Case filters appended to every case for this tag/event
suffix = ""
# Filters out the non-hierarchical case variants for the hierarchical job
if tag == "gcc-hier":
suffix += " !-hier"
# Filter out hello tests on pull requests, these just add measurement noise
if is_pr:
suffix += " !*:hello"
cases[tag] = [case + suffix for case, _ in ordered]
print(json.dumps(cases, separators=(",", ":")))

163
ci/ci-rtlmeter-report.bash Executable file
View File

@ -0,0 +1,163 @@
#!/usr/bin/env bash
# DESCRIPTION: Verilator: CI script for 'rtlmeter.yml' PR results
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# This script builds the content of the response comment posted on PRs
# at the end of RTLMeter runs.
# Developer note: You should be able to run this script in your local checkout
# if you have GitHub CLI (command 'gh') setup, authenticated ('gh auth login'),
# and have set a default repository ('gh repo set-default').
# Trace when running in the CI
[ "$GITHUB_ACTIONS" != "true" ] || set -x
# Arguments:
# 1. run ID
# 2. SHA of the event that triggered the run (for a PR, the test merge commit)
# rest: run tags
RUN_ID=$1; shift
RUN_SHA=$1; shift
RUNS="$@"
# $VERILATOR_CHECKOUT/ci directory
SCRIPT_DIR=$(readlink -f $(dirname ${BASH_SOURCE[0]}))
# Move into a temporary directory
rm -rf rtlmeter-report
mkdir rtlmeter-report
pushd rtlmeter-report &> /dev/null
TMP_DIR=$(readlink -f .)
# Download the combined results. 'combine-results' uploads a single artifact
# for each version, holding the per-tag JSON files (e.g. all-results-gcc.json):
# - 'all-results' for the new version
# - 'all-reference' for the old reference version
mkdir new ref
NEW_DIR=$(readlink -f new)
REF_DIR=$(readlink -f ref)
gh run download ${RUN_ID} --name all-results --dir $NEW_DIR
gh run download ${RUN_ID} --name all-reference --dir $REF_DIR
# Get Some metadata about the runs
RUN_URL=$(gh run view $RUN_ID --json url --jq ".url")
RUN_NUM=$(gh run view $RUN_ID --json number --jq ".number")
# Repository owner and name of the default repository, used to build the
# GitHub Pages URL of the detailed report. The owner is lowercased, as required
# for the '<owner>.github.io' Pages domain. Resolved here, while still in the
# default repository's git context (before the 'cd rtlmeter' below).
PAGES_OWNER=$(gh repo view --json owner --jq '.owner.login' | tr '[:upper:]' '[:lower:]')
PAGES_NAME=$(gh repo view --json name --jq '.name')
# The 'old' reference was built from the target branch base commit, which is
# the first parent of the triggering merge commit (same as the 'start' job in
# rtlmeter.yml). Resolved here, while still in the default repository's git
# context (before the 'cd rtlmeter' below).
REF_SHA=$(gh api "repos/{owner}/{repo}/commits/$RUN_SHA" --jq '.parents[0].sha')
# Go back to root directory
popd &> /dev/null
# Go to RTLMeter directory
cd rtlmeter
# Compare results
SUMMARY_ARGS=()
for r in $RUNS; do
CMP_JSON=$TMP_DIR/cmp-$r.json
# Gather args for summary script
SUMMARY_ARGS+=($NEW_DIR/all-results-$r.json $CMP_JSON)
# Create JSON
./rtlmeter compare --format json --steps "*" --metrics "*" \
$REF_DIR/all-reference-$r.json $NEW_DIR/all-results-$r.json > $CMP_JSON
# Also create detailed tables
./rtlmeter compare --format ascii --steps 'verilate' --metrics '*' $REF_DIR/all-reference-$r.json $NEW_DIR/all-results-$r.json > $TMP_DIR/verilate-$r.txt
./rtlmeter compare --format ascii --steps 'cppbuild' --metrics '*' $REF_DIR/all-reference-$r.json $NEW_DIR/all-results-$r.json > $TMP_DIR/cppbuild-$r.txt
./rtlmeter compare --format ascii --steps 'execute' --metrics '*' $REF_DIR/all-reference-$r.json $NEW_DIR/all-results-$r.json > $TMP_DIR/execute-$r.txt
# Chop them at new lines, into one table per file
awk -v RS= -v prefix=$TMP_DIR/$r-frag '{print > sprintf("%s-verilate-%02d.txt",prefix,NR)}' $TMP_DIR/verilate-$r.txt
awk -v RS= -v prefix=$TMP_DIR/$r-frag '{print > sprintf("%s-cppbuild-%02d.txt",prefix,NR)}' $TMP_DIR/cppbuild-$r.txt
awk -v RS= -v prefix=$TMP_DIR/$r-frag '{print > sprintf("%s-execute-%02d.txt" ,prefix,NR)}' $TMP_DIR/execute-$r.txt
done
# Create summary, suppress failure, but save the status reported to pass back.
STATUS=0
venv/bin/python3 $SCRIPT_DIR/ci-rtlmeter-report.py ${SUMMARY_ARGS[@]} > $TMP_DIR/summary.txt || STATUS=$?
# Print it
cat $TMP_DIR/summary.txt
# Create notification comment content
NOTIFICATION=$TMP_DIR/notification.txt
cat > $NOTIFICATION <<NOTIFICATION_TEMPLATE
Performance metrics for PR workflow [#$RUN_NUM]($RUN_URL), comparing:
- target branch commit [${REF_SHA:0:9}](https://github.com/${PAGES_OWNER}/${PAGES_NAME}/commit/${REF_SHA}) (A)
- with PR merge commit [${RUN_SHA:0:9}](https://github.com/${PAGES_OWNER}/${PAGES_NAME}/commit/${RUN_SHA}) (B)
<details open>
<summary>Summary of all runs</summary>
<pre>
$(cat $TMP_DIR/summary.txt)
</pre>
</details>
The reported numbers are the geometric means of the ratios of the metrics over all cases,
less than 1 is a regression, greater than 1 is an improvement.
The jobs run on variable and noisy runners, so some variance is expected between runs.
Detailed report: [${RUN_ID}](https://${PAGES_OWNER}.github.io/${PAGES_NAME}/rtlmeter-reports/${RUN_ID}/index.html)
NOTIFICATION_TEMPLATE
# Create detailed report
REPORT=$TMP_DIR/body.html
cat > $REPORT <<SUMMARY_TEMPLATE
<h3>Summary of all runs</h3>
<pre>
$(cat $TMP_DIR/summary.txt)
</pre>
SUMMARY_TEMPLATE
echo "<h3>Detailed results</h3>" >> $REPORT
for r in $RUNS; do
RUN_NAME=$(jq -rj ".[0].runName" $NEW_DIR/all-results-$r.json)
echo "<h4>$RUN_NAME</h4>" >> $REPORT
for f in $(ls -1 $TMP_DIR/$r-frag-verilate-*.txt | sort) \
$(ls -1 $TMP_DIR/$r-frag-cppbuild-*.txt | sort) \
$(ls -1 $TMP_DIR/$r-frag-execute-*.txt | sort); do
cat >> $REPORT <<DETAIL_TAMPLATE
<details>
<summary>$(head -n 1 $f | tr -d '\n')</summary>
<pre>
$(tail -n +2 $f)
</pre>
</details>
DETAIL_TAMPLATE
done
done
# Turn the report into a proper HTML page
mkdir -p ${TMP_DIR}/report
cat > ${TMP_DIR}/report/index.html <<INDEX_TEMPLATE
<html>
<head>
<title>Verilator RTLMeter report #${RUN_NUM}</title>
<style>
body {
font-family: courier, serif;
background-color: #f3f3f3;
a {
color: #008fd7;
}
}
</style>
</head>
<body>
$(cat ${TMP_DIR}/body.html)
</body>
</html>
INDEX_TEMPLATE
exit $STATUS

137
ci/ci-rtlmeter-report.py Normal file
View File

@ -0,0 +1,137 @@
# DESCRIPTION: Verilator: CI script for 'rtlmeter.yml' PR results
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
###############################################################################
# This script runs with the venv of **RTLMeter**
###############################################################################
import sys
import json
import tabulate
from typing import Final, List
tabulate.PRESERVE_WHITESPACE = True
tabulate.MIN_PADDING = 0
_ASCII_TABLE_FORMAT: Final = tabulate.TableFormat(
lineabove=tabulate.Line("╒═", "", "═╤═", "═╕"),
linebelowheader=tabulate.Line("╞═", "", "═╪═", "═╡"),
linebelow=tabulate.Line("╘═", "", "═╧═", "═╛"),
headerrow=tabulate.DataRow("", "", ""),
datarow=tabulate.DataRow("", "", ""),
linebetweenrows=None,
padding=0,
with_header_hide=None,
)
def printTable(table: List[List[str]], **kwargs) -> None:
print(tabulate.tabulate(table, tablefmt=_ASCII_TABLE_FORMAT, **kwargs))
# fmt: off
stepMetric = (
# Step, Metric, Status Brackets
("execute", "speed", ("", 0.96, "⚠️", 0.98, "", 1.02, "💡", 1.04, "")),
("execute", "memory", ("", 0.90, "⚠️", 0.95, "", 1.05, "💡", 1.10, "")),
("verilate", "cpu", ("", 0.96, "⚠️", 0.98, "", 1.02, "💡", 1.04, "")),
("verilate", "memory", ("", 0.90, "⚠️", 0.95, "", 1.05, "💡", 1.10, "")),
("cppbuild", "cpu", ("", 0.96, "⚠️", 0.98, "", 1.02, "💡", 1.04, "")),
("cppbuild", "memory", ("", 0.90, "⚠️", 0.95, "", 1.05, "💡", 1.10, "")),
("cppbuild", "codeSize",("", 0.96, "⚠️", 0.98, "", 1.02, "💡", 1.04, "")),
("cppbuild", "elapsed", ("", 0.70, "⚠️", 0.85, "", 1.15, "💡", 1.30, "")),
)
badgeLegend = [
("", "Likely regression"),
("⚠️", "Possible regression"),
("", "Within acceptable range"),
("💡", "Possible improvement"),
("", "Likely improvement"),
]
# fmt: on
changedCycles = []
table = []
badgesToExplain = set()
for ref, cmp in zip(sys.argv[1::2], sys.argv[2::2]):
with open(ref, "r", encoding="utf-8") as f:
ref_json = json.load(f)[0]
with open(cmp, "r", encoding="utf-8") as f:
cmp_json = json.load(f)
if table:
table.append(tabulate.SEPARATING_LINE)
runName = ref_json["runName"]
# Check simulated cycles match - it's ok to crash if this entry does not exist
for entry in cmp_json["execute"]["clocks"]["table"]:
case, _, _, (refCycles, _), (newCycles, _), _, _ = entry
refCycles = int(refCycles)
newCycles = int(newCycles)
if refCycles != newCycles:
changedCycles.append([runName, case, refCycles, newCycles])
# Check metrics
for step, metric, brackets in stepMetric:
if step not in cmp_json:
continue
data = cmp_json[step]
if metric not in data:
continue
data = data[metric]
minGain = float("inf")
maxGain = float("-inf")
meanGain = 1
count = 0
for _, _, _, (a, _), (b, _), g, _ in data["table"]:
# for wall clock and CPU time, ignore small values that just add noise
if metric == "elapsed" or metric == "cpu":
if a < 30 or b < 30:
continue
minGain = min(minGain, g)
maxGain = max(maxGain, g)
meanGain *= g
count += 1
if count == 0:
continue
meanGain = meanGain**(1 / count)
status = brackets[0]
for limit, badge in zip(brackets[1::2], brackets[2::2]):
if meanGain >= limit:
status = badge
badgesToExplain.add(status)
table.append([
runName, step, ref_json["metrics"][metric]["header"], f"{meanGain:.2f}x {status} ",
f"{minGain:.2f}x", f"{maxGain:.2f}x", f"{count}"
])
# Print changed cycles if any
if changedCycles:
print("❌ simulated cycles changed (must be fixed):")
printTable(changedCycles,
headers=("Run", "Case", "Old Cycles", "New Cycles"),
colalign=("left", "left", "right", "right"),
disable_numparse=True)
print()
# Print results
printTable(
table,
headers=("Run", "Step", "Metric", "Improvement", "Min", "Max", "Samples"),
colalign=("left", "left", "left", "right", "right", "right", "right"),
disable_numparse=True,
)
# Explain badges
print()
for badge, legend in badgeLegend:
if badge in badgesToExplain:
print(f" {badge} : {legend}")
# Fail job if status changed
sys.exit(0 if not changedCycles else 1)

View File

@ -421,6 +421,15 @@ AC_DEFUN([_MY_LDLIBS_CHECK_OPT],
_MY_LDLIBS_CHECK_IFELSE($2, $1="$$1 $2")
])
AC_DEFUN([_MY_LDLIBS_CHECK_SET],
[# _MY_LDLIBS_CHECK_SET(variable, flag) -- Check if linker supports specific
# options. If it does, set variable to flag, only if not previously set
# (so the first supported flag wins and the rest are not tested).
if test "$$1" = ""; then
_MY_LDLIBS_CHECK_IFELSE($2, $1="$2")
fi
])
# Add the coverage flags early as they influence later checks.
if test "$CFG_WITH_DEV_GCOV" = "yes"; then
_MY_CXX_CHECK_OPT(CXX,--coverage)
@ -589,11 +598,6 @@ if test "$CFG_ENABLE_PARTIAL_STATIC" = "yes"; then
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -static-libgcc)
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -static-libstdc++)
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -Xlinker -gc-sections)
LTCMALLOC="-Wl,--whole-archive -l:libtcmalloc_minimal.a -Wl,--no-whole-archive"
LJEMALLOC="-Wl,--whole-archive -l:libjemalloc.a -Wl,--no-whole-archive"
else
LTCMALLOC=-ltcmalloc_minimal
LJEMALLOC=-ljemalloc
fi
AC_SUBST(CFG_LDFLAGS_SRC)
AC_SUBST(CFG_LDFLAGS_VERILATED)
@ -609,51 +613,56 @@ _MY_LDLIBS_CHECK_OPT(CFG_LIBS, -latomic)
_MY_LDLIBS_CHECK_OPT(CFG_LIBS, -lbcrypt)
_MY_LDLIBS_CHECK_OPT(CFG_LIBS, -lpsapi)
# Check if jemalloc is available based on --enable-jemalloc
# jemalloc is preferred over tcmalloc when both are available
# Figure out which malloc library to use:
# - Prefer jemalloc over tcmalloc (it's faster)
# - If partial static link enabled, prefer static libraries,
# but fall back on dynamic if static is not available
# Test for jemalloc
CFG_HAVE_JEMALLOC=no
_MY_LDLIBS_CHECK_IFELSE(
$LJEMALLOC,
[if test "$CFG_WITH_JEMALLOC" != "no"; then
CFG_LIBS="$LJEMALLOC $CFG_LIBS";
CFG_HAVE_JEMALLOC=yes;
# If using jemalloc, add some extra options to make the compiler not assume
# it is using its own versions of the standard library functions
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-malloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-calloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-realloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-free)
if test "$CFG_WITH_JEMALLOC" != "no"; then
if test "$CFG_ENABLE_PARTIAL_STATIC" = "yes"; then
_MY_LDLIBS_CHECK_SET(CFG_LIBS_MALLOC, -Xlinker --whole-archive -l:libjemalloc.a -Xlinker --no-whole-archive)
fi
_MY_LDLIBS_CHECK_SET(CFG_LIBS_MALLOC, -ljemalloc)
if test "$CFG_LIBS_MALLOC" != ""; then
CFG_HAVE_JEMALLOC=yes
AC_DEFINE([HAVE_JEMALLOC],[1],[Defined if have jemalloc])
fi],
[if test "$CFG_WITH_JEMALLOC" = "yes"; then
AC_MSG_ERROR([--enable-jemalloc was given but test for ${LJEMALLOC} failed])
fi])
elif test "$CFG_WITH_JEMALLOC" = "yes"; then
AC_MSG_ERROR([--enable-jemalloc was given but jemalloc is not available])
fi
fi
AC_SUBST(HAVE_JEMALLOC)
# Check if tcmalloc is available based on --enable-tcmalloc
# Only use tcmalloc if jemalloc was not found/enabled
if test "$CFG_HAVE_JEMALLOC" = "yes"; then
AC_MSG_NOTICE([jemalloc found, skipping tcmalloc check])
else
# Test for tcmalloc
CFG_HAVE_TCMALLOC=no
_MY_LDLIBS_CHECK_IFELSE(
$LTCMALLOC,
[if test "$CFG_WITH_TCMALLOC" != "no"; then
CFG_LIBS="$LTCMALLOC $CFG_LIBS";
CFG_HAVE_TCMALLOC=yes;
# If using tcmalloc, add some extra options to make the compiler not assume
# it is using its own versions of the standard library functions
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-malloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-calloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-realloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-free)
if test "$CFG_HAVE_JEMALLOC" = "yes"; then
AC_MSG_NOTICE([jemalloc found, skipping check for tcmalloc])
elif test "$CFG_WITH_TCMALLOC" != "no"; then
if test "$CFG_ENABLE_PARTIAL_STATIC" = "yes"; then
_MY_LDLIBS_CHECK_SET(CFG_LIBS_MALLOC, -Xlinker --whole-archive -l:libtcmalloc_minimal.a -Xlinker --no-whole-archive)
fi
_MY_LDLIBS_CHECK_SET(CFG_LIBS_MALLOC, -ltcmalloc_minimal)
if test "$CFG_LIBS_MALLOC" != ""; then
CFG_HAVE_TCMALLOC=yes
AC_DEFINE([HAVE_TCMALLOC],[1],[Defined if have tcmalloc])
fi],
[if test "$CFG_WITH_TCMALLOC" = "yes"; then
AC_MSG_ERROR([--enable-tcmalloc was given but test for ${LTCMALLOC} failed])
fi])
elif test "$CFG_WITH_TCMALLOC" = "yes"; then
AC_MSG_ERROR([--enable-tcmalloc was given but tcmalloc is not available])
fi
fi
AC_SUBST(HAVE_TCMALLOC)
if test "$CFG_HAVE_JEMALLOC" = "yes" || test "$CFG_HAVE_TCMALLOC" = "yes"; then
# If using custom malloc, add some extra options to make the compiler not assume
# it is using its own versions of the standard library functions
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-malloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-calloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-realloc)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-free)
fi
# Finished with libraries
CFG_LIBS="$CFG_LIBS_MALLOC $CFG_LIBS"
AC_SUBST(CFG_LIBS)
# Need C++14 at least

View File

@ -97,10 +97,10 @@ def process() -> None:
line,
flags=re.IGNORECASE)
if m:
mid = m.group(2)
if ("#" + mid) not in msgs[key]:
bug_id = m.group(2)
if ("#" + bug_id) not in msgs[key]:
# print("K %s" % line)
msgs[key] += ' (#' + mid + ')'
msgs[key] += ' (#' + bug_id + ')'
if not msgs:
print("No Changes need to be inserted.")

View File

@ -253,8 +253,7 @@ class AssertVisitor final : public VNVisitor {
}
}
AstSampled* newSampledExpr(AstNodeExpr* nodep) {
AstSampled* const sampledp = new AstSampled{nodep->fileline(), nodep};
sampledp->dtypeFrom(nodep);
AstSampled* const sampledp = new AstSampled{nodep->fileline(), nodep, nodep->dtypep()};
return sampledp;
}
AstVarRef* newMonitorNumVarRefp(const AstNode* nodep, VAccess access) {

View File

@ -182,8 +182,7 @@ struct BuildResult final {
};
static AstNodeExpr* sampled(AstNodeExpr* exprp) {
AstSampled* const sp = new AstSampled{exprp->fileline(), exprp};
sp->dtypeFrom(exprp);
AstSampled* const sp = new AstSampled{exprp->fileline(), exprp, exprp->dtypep()};
return sp;
}
@ -838,13 +837,14 @@ class SvaNfaBuilder final {
BuildResult buildUntil(AstUntil* nodep, SvaStateVertex* entryVtxp, bool isTopLevelStep) {
FileLine* const flp = nodep->fileline();
if (!isTopLevelStep) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'until' in complex property expression");
nodep->v3warn(E_UNSUPPORTED, "Unsupported: '" << nodep->verilogKwd()
<< "' in complex property expression");
return BuildResult::failWithError();
}
if (nodep->isStrong()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: s_until"
<< (nodep->isOverlapping() ? "_with" : "")
<< " (in property expresion)");
<< " (in property expression)");
return BuildResult::failWithError();
}
AstNodeExpr* const lhsp = nodep->lhsp();
@ -853,7 +853,8 @@ class SvaNfaBuilder final {
return ep->exists([](const AstNodeExpr* np) { return np->isMultiCycleSva(); });
};
if (hasSeq(lhsp) || hasSeq(rhsp)) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'until' in complex property expression");
nodep->v3warn(E_UNSUPPORTED, "Unsupported: '" << nodep->verilogKwd()
<< "' in complex property expression");
return BuildResult::failWithError();
}
@ -1926,16 +1927,16 @@ class AssertNfaVisitor final : public VNVisitor {
// Bare `assert property (p until q)` with boolean operands stays on
// V3AssertPre's AstLoop lowering, which preserves per-attempt action-block
// firings that this NFA's single-bit aggregated state cannot. NFA still
// owns strong forms, sequence operands, and any embedding inside a
// multi-cycle context (implication consequent, or/and operands, etc.).
// firings that this NFA's single-bit aggregated state cannot. Strong bare
// forms are also lowered there. NFA still owns sequence operands and any
// embedding inside a multi-cycle context (implication consequent, or/and
// operands, etc.).
static bool isBareTopLevelUntil(AstNode* propp) {
AstNode* p = propp;
if (AstPropSpec* const specp = VN_CAST(p, PropSpec)) p = specp->propp();
while (AstLogNot* const notp = VN_CAST(p, LogNot)) p = notp->lhsp();
AstUntil* const untilp = VN_CAST(p, Until);
if (!untilp) return false;
if (untilp->isStrong()) return false;
const auto hasSeq = [](const AstNodeExpr* ep) {
return ep->exists([](const AstNodeExpr* np) { return np->isMultiCycleSva(); });
};

View File

@ -366,8 +366,8 @@ private:
if (skewp->num().is1Step()) {
// #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)};
sampledp->dtypeFrom(exprp);
AstSampled* const sampledp
= new AstSampled{flp, exprp->cloneTreePure(false), exprp->dtypep()};
AstAssign* const assignp = new AstAssign{flp, refp, sampledp};
m_clockingp->addNextHere(new AstAlways{
flp, VAlwaysKwd::ALWAYS,
@ -413,7 +413,7 @@ private:
}
}
void visit(AstDelay* nodep) override {
m_hasCycleDelay = true;
m_hasCycleDelay |= nodep->isCycleDelay();
// Only cycle delays are relevant in this stage; also only process once
if (!nodep->isCycleDelay()) {
if (m_inSynchDrive) {
@ -811,7 +811,6 @@ private:
static AstStmtExpr* getProcessAssocArrayDelete(AstVarRef* const refp) {
// Constructs refp.delete(std::process::self()) statement
FileLine* const flp = refp->fileline();
refp->classOrPackagep(v3Global.rootp()->stdPackageProcessp());
AstCMethodHard* const deletep = new AstCMethodHard{
flp, refp, VCMethod::ASSOC_ERASE, v3Global.rootp()->stdPackageProcessSelfp(flp)};
deletep->dtypep(refp->findVoidDType());
@ -819,7 +818,6 @@ private:
}
static AstNodeExpr* getProcessAssocArraySize(AstVarRef* const refp) {
// Constructs refp.size() statement
refp->classOrPackagep(v3Global.rootp()->stdPackageProcessp());
AstCMethodHard* const sizep
= new AstCMethodHard{refp->fileline(), refp, VCMethod::ASSOC_SIZE};
sizep->dtypep(refp->findBasicDType(VBasicDTypeKwd::UINT32));
@ -846,7 +844,7 @@ private:
// Assertion condition check
AstLoop* const loopp = new AstLoop{flp};
AstNodeExpr* const condp = new AstSampled{flp, nodep->exprp()->unlinkFrBack()};
AstNodeExpr* const condp = new AstSampled{flp, nodep->exprp()->unlinkFrBack(), nullptr};
loopp->addStmtsp(new AstLoopTest{flp, loopp, new AstLogNot{flp, condp}});
loopp->addStmtsp(new AstEventControl{flp, sentreep, nullptr});
@ -1300,17 +1298,121 @@ private:
}
void visit(AstUntil* nodep) override {
FileLine* const flp = nodep->fileline();
if (m_pexprp) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'until' in complex property expression");
nodep->replaceWith(new AstConst{flp, AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
UASSERT_OBJ(
!m_pexprp, nodep,
"'" << nodep->verilogKwd()
<< "' in complex property expression should have been rejected by V3AssertNfa");
if (nodep->isStrong()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: s_until"
<< (nodep->isOverlapping() ? "_with" : "")
<< " (in property expresion)");
nodep->replaceWith(new AstConst{flp, AstConst::BitFalse{}});
// p s_until q / p s_until_with q: q must eventually be true. Until then, p must
// be true on every sampled tick. For s_until, check q first: when q is true on
// this tick, p is not required. For s_until_with, p must be true on the q tick too.
AstNodeExpr* const rawLhsp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* const rawRhsp = nodep->rhsp()->unlinkFrBack();
AstSampled* const lhsp = new AstSampled{flp, rawLhsp, rawLhsp->dtypep()};
AstSampled* const rhsp = new AstSampled{flp, rawRhsp, rawRhsp->dtypep()};
AstNodeExpr* finalCondp = rhsp->cloneTreePure(false);
if (nodep->isOverlapping()) {
finalCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), finalCondp};
}
// Track active assertion attempts. Only the count is needed to emit final failures.
AstVar* const activep = new AstVar{flp, VVarType::MODULETEMP, m_activeNames.get(""),
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
activep->lifetime(VLifetime::STATIC_EXPLICIT);
m_modp->addStmtsp(activep);
AstVar* const donep
= new AstVar{flp, VVarType::BLOCKTEMP, "__VassertDone", nodep->findBitDType()};
donep->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
auto setDone = [&]() {
return new AstAssign{flp, new AstVarRef{flp, donep, VAccess::WRITE},
new AstConst{flp, AstConst::BitTrue{}}};
};
AstLoop* const loopp = new AstLoop{flp};
AstAssign* const decrementVar = new AstAssign{
flp, new AstVarRef{flp, activep, VAccess::WRITE},
new AstSub{flp, new AstVarRef{flp, activep, VAccess::READ}, new AstConst{flp, 1}}};
loopp->addStmtsp(new AstLoopTest{
flp, loopp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}});
{
AstBegin* const passp = new AstBegin{flp, "", nullptr, true};
passp->addStmtsp(new AstPExprClause{flp});
passp->addStmtsp(decrementVar);
passp->addStmtsp(setDone());
AstNodeExpr* passCondp = rhsp;
if (nodep->isOverlapping()) {
passCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), passCondp};
}
loopp->addStmtsp(new AstIf{flp, passCondp, passp});
}
{
AstBegin* const failp = new AstBegin{flp, "", nullptr, true};
failp->addStmtsp(new AstPExprClause{flp, false});
failp->addStmtsp(decrementVar->cloneTree(false));
failp->addStmtsp(setDone());
loopp->addStmtsp(new AstIf{
flp,
new AstLogAnd{flp,
new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}},
new AstLogNot{flp, lhsp}},
failp});
}
AstDelay* const delayp = new AstDelay{flp, new AstConst{flp, 1}, false};
delayp->timeunit(m_modp->timeunit());
loopp->addStmtsp(new AstIf{
flp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}, delayp});
// Main assertion block
AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true};
bodyp->addStmtsp(donep);
bodyp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, donep, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}});
FileLine* const flp = activep->fileline();
bodyp->addStmtsp(
new AstAssign{flp, new AstVarRef{flp, activep, VAccess::WRITE},
new AstAdd{flp, new AstVarRef{flp, activep, VAccess::READ},
new AstConst{flp, 1}}});
bodyp->addStmtsp(loopp);
// Validate assertion condition for each active assert
AstVar* const activeCountp = new AstVar{flp, VVarType::BLOCKTEMP, "__VassertCount",
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
activeCountp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
AstAssign* const initActiveCountp
= new AstAssign{flp, new AstVarRef{flp, activeCountp, VAccess::WRITE},
new AstVarRef{flp, activep, VAccess::READ}};
AstLoop* const finalLoopp = new AstLoop{flp};
finalLoopp->addStmtsp(
new AstLoopTest{flp, finalLoopp,
new AstNeq{flp, new AstVarRef{flp, activeCountp, VAccess::READ},
new AstConst{flp, 0}}});
finalLoopp->addStmtsp(new AstIf{flp, finalCondp, new AstPExprClause{flp},
new AstPExprClause{flp, false}});
finalLoopp->addStmtsp(
new AstAssign{flp, new AstVarRef{flp, activeCountp, VAccess::WRITE},
new AstSub{flp, new AstVarRef{flp, activeCountp, VAccess::READ},
new AstConst{flp, 1}}});
// Final assertion block
AstBegin* const finalp = new AstBegin{flp, "", nullptr, true};
finalp->addStmtsp(activeCountp);
finalp->addStmtsp(initActiveCountp);
finalp->addStmtsp(finalLoopp);
AstPExpr* const pexprp = new AstPExpr{flp, bodyp, finalp, nodep->dtypep()};
VL_RESTORER(m_pexprp);
VL_RESTORER(m_hasCycleDelay);
m_pexprp = pexprp;
m_hasCycleDelay = false;
iterate(bodyp);
iterate(finalp);
UASSERT_OBJ(!m_hasCycleDelay, nodep,
"s_until cycle delay should have been handled by V3AssertNfa");
nodep->replaceWith(pexprp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
@ -1369,8 +1471,8 @@ private:
iterateNull(nodep->disablep());
if (!VN_AS(nodep->backp(), NodeCoverOrAssert)->immediate()) {
const AstNodeDType* const propDtp = nodep->propp()->dtypep();
nodep->propp(new AstSampled{nodep->fileline(), nodep->propp()->unlinkFrBack()});
nodep->propp()->dtypeFrom(propDtp);
nodep->propp(new AstSampled{nodep->fileline(), nodep->propp()->unlinkFrBack(),
propDtp->dtypep()});
}
iterate(nodep->propp());
}

View File

@ -370,6 +370,24 @@ public:
// clang-format on
return names[m_e];
}
// True for attributes that read an operand's type rather than its value, such as $bits.
bool isTypeQuery() const {
switch (m_e) {
case DIM_BITS:
case DIM_BITS_OR_NUMBER:
case DIM_DIMENSIONS:
case DIM_HIGH:
case DIM_INCREMENT:
case DIM_LEFT:
case DIM_LOW:
case DIM_RIGHT:
case DIM_SIZE:
case DIM_UNPK_DIMENSIONS:
case TYPEID:
case TYPENAME: return true;
default: return false;
}
}
VAttrType()
: m_e{ILLEGAL} {}
// cppcheck-suppress noExplicitConstructor

View File

@ -2519,9 +2519,10 @@ class AstSampled final : public AstNodeExpr {
// Verilog $sampled
// @astgen op1 := exprp : AstNode<AstNodeExpr|AstPropSpec>
public:
AstSampled(FileLine* fl, AstNode* exprp)
AstSampled(FileLine* fl, AstNode* exprp, AstNodeDType* dtypep)
: ASTGEN_SUPER_Sampled(fl) {
this->exprp(exprp);
this->dtypep(dtypep);
}
ASTGEN_MEMBERS_AstSampled;
string emitVerilog() override { return "$sampled(%l)"; }
@ -2906,6 +2907,9 @@ public:
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
string verilogKwd() const override {
return (isStrong() ? "s_" : string()) + "until" + (isOverlapping() ? "_with" : "");
}
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
@ -5784,6 +5788,7 @@ public:
};
class AstOneHot final : public AstNodeUniop {
// True if only single bit set in vector
// @astgen makeDfgVertex
public:
AstOneHot(FileLine* fl, AstNodeExpr* lhsp)
: ASTGEN_SUPER_OneHot(fl, lhsp) {
@ -5801,6 +5806,7 @@ public:
};
class AstOneHot0 final : public AstNodeUniop {
// True if only single bit, or no bits set in vector
// @astgen makeDfgVertex
public:
AstOneHot0(FileLine* fl, AstNodeExpr* lhsp)
: ASTGEN_SUPER_OneHot0(fl, lhsp) {

View File

@ -559,13 +559,11 @@ public:
namespace V3Cfg {
// Compute AstVars live on entry to given CFG. That is, variables that might
// Compute AstVarScopes live on entry to given CFG. That is, variables that might
// be read before wholly assigned in the CFG. Returns nullptr if the analysis
// failed due to unhandled statements or data types involved in the CFG.
// On success, returns a vector of AstVar or AstVarScope nodes live on entry.
std::unique_ptr<std::vector<AstVar*>> liveVars(const CfgGraph&);
// Same as liveVars, but return AstVarScopes insted
// failed due to unhandled statements involved in the CFG. The analysis is
// conservative and may return variables that are not actually live on entry.
// On success, returns a vector of AstVarScope nodes that might be live on entry.
std::unique_ptr<std::vector<AstVarScope*>> liveVarScopes(const CfgGraph&);
} //namespace V3Cfg

View File

@ -25,25 +25,19 @@
#include "V3Ast.h"
#include "V3Cfg.h"
#include <limits>
#include <memory>
#include <unordered_map>
VL_DEFINE_DEBUG_FUNCTIONS;
template <bool T_Scoped>
class CfgLiveVariables final : VNVisitorConst {
// TYPES
using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
// State associted with each basic block
struct BlockState final {
// Variables used in block, before a complete assignment in the same block
std::unordered_set<Variable*> m_gen;
std::unordered_set<AstVarScope*> m_gen;
// Variables that are assigned a complete value in the basic block
std::unordered_set<Variable*> m_kill;
std::unordered_set<Variable*> m_liveIn; // Variables live on entry to the block
std::unordered_set<Variable*> m_liveOut; // Variables live on exit from the block
std::unordered_set<AstVarScope*> m_kill;
std::unordered_set<AstVarScope*> m_liveIn; // Variables live on entry to the block
std::unordered_set<AstVarScope*> m_liveOut; // Variables live on exit from the block
bool m_isOnWorkList = false; // Block is on work list
bool m_wasProcessed = false; // Already processed at least once
};
@ -56,14 +50,6 @@ class CfgLiveVariables final : VNVisitorConst {
bool m_abort = false; // Abort analysis - unhandled construct
// METHODS
static Variable* getTarget(const AstNodeVarRef* refp) {
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
if VL_CONSTEXPR_CXX17 (T_Scoped) {
return reinterpret_cast<Variable*>(refp->varScopep());
} else {
return reinterpret_cast<Variable*>(refp->varp());
}
}
// This is to match DFG, but can be extended independently - eventually should handle all
static bool isSupportedPackedDType(const AstNodeDType* dtypep) {
@ -81,52 +67,41 @@ class CfgLiveVariables final : VNVisitorConst {
}
// Check and return if variable is incompatible
bool incompatible(Variable* varp) {
if (!isSupportedPackedDType(varp->dtypep())) return true;
const AstVar* astVarp = nullptr;
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
if VL_CONSTEXPR_CXX17 (T_Scoped) {
astVarp = reinterpret_cast<AstVarScope*>(varp)->varp();
} else {
astVarp = reinterpret_cast<AstVar*>(varp);
}
if (astVarp->ignoreSchedWrite()) return true;
bool incompatible(AstVarScope* vscp) {
if (!isSupportedPackedDType(vscp->dtypep())) return true;
if (vscp->varp()->ignoreSchedWrite()) return true;
return false;
}
void updateGen(AstNode* nodep) {
UASSERT_OBJ(!m_abort, nodep, "Doing useless work");
m_abort = nodep->exists([&](const AstNodeVarRef* refp) {
// Cross reference is ambiguous
if (VN_IS(refp, VarXRef)) return true;
m_abort = nodep->exists([&](const AstVarRef* refp) {
// Only care about reads
if (refp->access().isWriteOnly()) return false;
// Grab referenced variable
Variable* const tgtp = getTarget(refp);
AstVarScope* const vscp = refp->varScopep();
// Bail if not a compatible type
if (incompatible(tgtp)) return true;
if (incompatible(vscp)) return true;
// Add to gen set, if not killed - assume whole variable read
if (m_currp->m_kill.count(tgtp)) return false;
m_currp->m_gen.emplace(tgtp);
if (m_currp->m_kill.count(vscp)) return false;
m_currp->m_gen.emplace(vscp);
return false;
});
}
void updateKill(AstNode* nodep) {
UASSERT_OBJ(!m_abort, nodep, "Doing useless work");
m_abort = nodep->exists([&](const AstNodeVarRef* refp) {
// Cross reference is ambiguous
if (VN_IS(refp, VarXRef)) return true;
m_abort = nodep->exists([&](const AstVarRef* refp) {
// Only care about writes
if (refp->access().isReadOnly()) return false;
// Grab referenced variable
Variable* const tgtp = getTarget(refp);
AstVarScope* const vscp = refp->varScopep();
// Bail if not a compatible type
if (incompatible(tgtp)) return true;
if (incompatible(vscp)) return true;
// If whole written, add to kill set
if (refp->nextp()) return false;
if (VN_IS(refp->abovep(), Sel)) return false;
m_currp->m_kill.emplace(tgtp);
m_currp->m_kill.emplace(vscp);
return false;
});
}
@ -144,8 +119,8 @@ class CfgLiveVariables final : VNVisitorConst {
BlockState& state = m_blockState[bb];
// liveIn = gen union (liveOut - kill)
std::unordered_set<Variable*> liveIn = state.m_gen;
for (Variable* const varp : state.m_liveOut) {
std::unordered_set<AstVarScope*> liveIn = state.m_gen;
for (AstVarScope* const varp : state.m_liveOut) {
if (state.m_kill.count(varp)) continue;
liveIn.insert(varp);
}
@ -219,25 +194,23 @@ class CfgLiveVariables final : VNVisitorConst {
}
public:
static std::unique_ptr<std::vector<Variable*>> apply(const CfgGraph& cfg) {
static std::unique_ptr<std::vector<AstVarScope*>> apply(const CfgGraph& cfg) {
CfgLiveVariables analysis{cfg};
// If failed, return nullptr
if (analysis.m_abort) return nullptr;
// Gather variables live in to the entry blockstd::unique_ptr<std::vector<Variable*>>
const std::unordered_set<Variable*>& lin = analysis.m_blockState[cfg.enter()].m_liveIn;
std::vector<Variable*>* const resultp = new std::vector<Variable*>{lin.begin(), lin.end()};
// Gather variables live in to the entry blockstd::unique_ptr<std::vector<AstVarScope*>>
const std::unordered_set<AstVarScope*>& lin = analysis.m_blockState[cfg.enter()].m_liveIn;
std::vector<AstVarScope*>* const resultp
= new std::vector<AstVarScope*>{lin.begin(), lin.end()};
// Sort for stability
std::stable_sort(resultp->begin(), resultp->end(), [](Variable* ap, Variable* bp) { //
return ap->name() < bp->name();
});
return std::unique_ptr<std::vector<Variable*>>{resultp};
std::stable_sort(resultp->begin(), resultp->end(),
[](AstVarScope* ap, AstVarScope* bp) { //
return ap->name() < bp->name();
});
return std::unique_ptr<std::vector<AstVarScope*>>{resultp};
}
};
std::unique_ptr<std::vector<AstVar*>> V3Cfg::liveVars(const CfgGraph& cfg) {
return CfgLiveVariables</* T_Scoped: */ false>::apply(cfg);
}
std::unique_ptr<std::vector<AstVarScope*>> V3Cfg::liveVarScopes(const CfgGraph& cfg) {
return CfgLiveVariables</* T_Scoped: */ true>::apply(cfg);
return CfgLiveVariables::apply(cfg);
}

View File

@ -91,6 +91,11 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
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::Sel: {
DfgSel* const cp = new DfgSel{*clonep, vtx.fileline(), vtx.dtype()};
cp->lsb(vtx.as<DfgSel>()->lsb());
@ -132,11 +137,14 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
VL_UNREACHABLE;
break;
}
default: {
vtx.v3fatalSrc("Unhandled operation vertex type: " + vtx.typeName());
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");
@ -618,6 +626,10 @@ void DfgVertex::typeCheck(const DfgGraph& dfg) const {
CHECK(isPacked(), "Should be Packed type");
return;
}
case VDfgType::CReset: {
CHECK(isPacked() || isArray(), "Should be Packed or Array type");
return;
}
case VDfgType::AstRd: {
const DfgAstRd& v = *as<DfgAstRd>();
CHECK(v.isPacked() || v.isArray(), "Should be Packed or Array type");
@ -774,7 +786,9 @@ void DfgVertex::typeCheck(const DfgGraph& dfg) const {
case VDfgType::LogNot:
case VDfgType::RedAnd:
case VDfgType::RedOr:
case VDfgType::RedXor: {
case VDfgType::RedXor:
case VDfgType::OneHot:
case VDfgType::OneHot0: {
CHECK(dtype() == DfgDataType::packed(1), "Should be 1-bit");
CHECK(inputp(0)->isPacked(), "Operand should be Packed type");
return;

View File

@ -125,6 +125,12 @@ class ColorStronglyConnectedComponents final {
// Initialize state of operation vertices
for (const DfgVertex& vtx : m_dfg.opVertices()) {
// If it has no inputs or no outputs, it cannot be part of a non-trivial SCC.
if (!vtx.nInputs() || !vtx.hasSinks()) {
index(vtx) = 0;
component(vtx) = 0;
continue;
}
index(vtx) = UNASSIGNED;
component(vtx) = UNASSIGNED;
}

View File

@ -52,6 +52,7 @@ class V3DfgCse final {
// Special vertices
case VDfgType::Const: // LCOV_EXCL_START
case VDfgType::CReset:
case VDfgType::VarArray:
case VDfgType::VarPacked:
case VDfgType::AstRd: // LCOV_EXCL_STOP
@ -110,6 +111,8 @@ class V3DfgCse final {
case VDfgType::NeqCase:
case VDfgType::NeqWild:
case VDfgType::Not:
case VDfgType::OneHot:
case VDfgType::OneHot0:
case VDfgType::Or:
case VDfgType::Pow:
case VDfgType::PowSS:
@ -168,6 +171,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::VarArray:
case VDfgType::VarPacked: // CSE does not combine variables
@ -232,6 +236,8 @@ class V3DfgCse final {
case VDfgType::NeqCase:
case VDfgType::NeqWild:
case VDfgType::Not:
case VDfgType::OneHot:
case VDfgType::OneHot0:
case VDfgType::Or:
case VDfgType::Pow:
case VDfgType::PowSS:
@ -299,6 +305,10 @@ 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
for (const DfgVertex& vtx : dfg.opVertices()) {
if (vtx.is<DfgCReset>()) m_hashCache[vtx] = V3Hash{++varHash};
}
// Similarly pre-hash constants for speed. While we don't combine constants, we do want
// expressions using the same constants to be combined, so we do need to hash equal

View File

@ -227,6 +227,13 @@ class DfgToAstVisitor final : DfgVisitor {
void visit(DfgConst* vtxp) override { //
m_resultp = new AstConst{vtxp->fileline(), vtxp->num()};
}
void visit(DfgCReset* vtxp) override {
DfgVertex* const sinkp = vtxp->singleSink();
UASSERT_OBJ(sinkp, vtxp, "CReset should only have one sink");
UASSERT_OBJ(sinkp->is<DfgVertexVar>(), sinkp, "CReset should drive a variable");
AstVar* const varp = sinkp->as<DfgVertexVar>()->vscp()->varp();
m_resultp = new AstCReset{vtxp->fileline(), varp, false};
}
void visit(DfgRep* vtxp) override {
FileLine* const flp = vtxp->fileline();

View File

@ -120,6 +120,8 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
// Partial driver cannot be inlined
if (srcp->is<DfgVertexSplice>()) continue;
if (srcp->is<DfgUnitArray>()) continue;
// Don't inline CReset
if (srcp->is<DfgCReset>()) continue;
// Okie dokie, here we go ...
vtx.replaceWith(srcp);
}

View File

@ -138,11 +138,14 @@ template <> const DfgDataType& resultDType<DfgXor> (const DfgVertex* lhsp
// Unary constant folding
template<typename Vertex> void foldOp(V3Number& out, const V3Number& src);
template <> void foldOp<DfgCountOnes> (V3Number& out, const V3Number& src) { out.opCountOnes(src); }
template <> void foldOp<DfgExtend> (V3Number& out, const V3Number& src) { out.opAssign(src); }
template <> void foldOp<DfgExtendS> (V3Number& out, const V3Number& src) { out.opExtendS(src, src.width()); }
template <> void foldOp<DfgLogNot> (V3Number& out, const V3Number& src) { out.opLogNot(src); }
template <> void foldOp<DfgNegate> (V3Number& out, const V3Number& src) { out.opNegate(src); }
template <> void foldOp<DfgNot> (V3Number& out, const V3Number& src) { out.opNot(src); }
template <> void foldOp<DfgOneHot> (V3Number& out, const V3Number& src) { out.opOneHot(src); }
template <> void foldOp<DfgOneHot0> (V3Number& out, const V3Number& src) { out.opOneHot0(src); }
template <> void foldOp<DfgRedAnd> (V3Number& out, const V3Number& src) { out.opRedAnd(src); }
template <> void foldOp<DfgRedOr> (V3Number& out, const V3Number& src) { out.opRedOr(src); }
template <> void foldOp<DfgRedXor> (V3Number& out, const V3Number& src) { out.opRedXor(src); }
@ -1155,7 +1158,7 @@ class V3DfgPeephole final : public DfgVisitor {
// Sel from a partial variable (including narrowed vertex)
if (DfgVarPacked* const varp = fromp->cast<DfgVarPacked>()) {
if (varp->srcp() && !varp->isVolatile()) {
if (varp->srcp() && !varp->isVolatile() && !varp->srcp()->is<DfgCReset>()) {
// Must be a splice, otherwise it would have been inlined
DfgSplicePacked* splicep = varp->srcp()->as<DfgSplicePacked>();
DfgVertex* driverp = nullptr;
@ -1193,6 +1196,10 @@ class V3DfgPeephole final : public DfgVisitor {
// DfgVertexUnary
//=========================================================================
void visit(DfgCountOnes* const vtxp) override {
if (foldUnary(vtxp)) return;
}
void visit(DfgExtend* const vtxp) override {
if (foldUnary(vtxp)) return;
@ -1325,6 +1332,14 @@ class V3DfgPeephole final : public DfgVisitor {
}
}
void visit(DfgOneHot* const vtxp) override {
if (foldUnary(vtxp)) return;
}
void visit(DfgOneHot0* const vtxp) override {
if (foldUnary(vtxp)) return;
}
void visit(DfgRedOr* const vtxp) override {
if (optimizeReduction(vtxp)) return;
}

View File

@ -89,6 +89,9 @@ class V3DfgPushDownSels final {
m_stack.push_back(&vtx);
}
for (DfgConst& vtx : m_dfg.constVertices()) m_stack.push_back(&vtx);
for (DfgVertex& vtx : m_dfg.opVertices()) {
if (!vtx.nInputs()) m_stack.push_back(&vtx);
}
// Reverse post order number to assign to next vertex
uint32_t rpoNext = m_dfg.size();

View File

@ -100,6 +100,9 @@ class DfgRegularize final {
// No need to add a temporary if the single sink is a variable already
if (sink.is<DfgVertexVar>()) return false;
// CReset always needs to be driving a variable
if (aVtx.is<DfgCReset>()) return true;
// Do not inline expressions into a loop body
if (const DfgAstRd* const astRdp = sink.cast<DfgAstRd>()) { return astRdp->inLoop(); }

View File

@ -384,6 +384,24 @@ class AstToDfgConverter final : public VNVisitor {
nodep->user2p(vtxp);
}
}
void visit(AstCReset* nodep) override {
UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting");
UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex");
if (unhandled(nodep)) return;
const DfgDataType* const dtypep = DfgDataType::fromAst(nodep->dtypep());
if (!dtypep) {
m_foundUnhandled = true;
++m_ctx.m_conv.nonRepDType;
return;
}
UASSERT_OBJ(!nodep->constructing(), nodep,
"CReset should be non-constructing at this stage");
DfgVertex* const vtxp = make<DfgCReset>(nodep->fileline(), *dtypep);
nodep->user2p(vtxp);
}
void visit(AstReplicate* nodep) override {
UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting");
UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex");

View File

@ -248,6 +248,14 @@ public:
}
};
class DfgCReset final : public DfgVertexNullary {
public:
DfgCReset(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
: DfgVertexNullary{dfg, dfgType(), flp, dtype} {}
ASTGEN_MEMBERS_DfgCReset;
};
//------------------------------------------------------------------------------
// Unary vertices - 1 inputs

View File

@ -2722,10 +2722,14 @@ class ParamVisitor final : public VNVisitor {
// LCOV_EXCL_STOP
}
void checkParamNotHier(AstNode* valuep) {
if (!valuep) return;
valuep->foreachAndNext([&](const AstNodeExpr* exprp) {
if (const AstVarXRef* const refp = VN_CAST(exprp, VarXRef)) {
// Flag hierarchical refs in a parameter value. Single top-down pass.
void checkParamNotHierRecurse(AstNode* nodep, bool underTypeQuery = false) {
for (; nodep; nodep = nodep->nextp()) {
// Refs read only for their type ($bits etc.) are allowed
const AstAttrOf* const attrp = VN_CAST(nodep, AttrOf);
const bool childUnderQuery
= underTypeQuery || (attrp && attrp->attrType().isTypeQuery());
if (const AstVarXRef* const refp = VN_CAST(nodep, VarXRef)) {
// Allow hierarchical ref to interface params through interface/modport ports
// or local interface instances
bool isIfaceRef = false;
@ -2735,18 +2739,21 @@ class ParamVisitor final : public VNVisitor {
= !refname.empty()
&& (m_ifacePortNames.count(refname) || m_ifaceInstCells.count(refname));
}
if (!isIfaceRef) {
if (!isIfaceRef && !underTypeQuery) {
refp->v3warn(HIERPARAM, "Parameter values cannot use hierarchical values"
" (IEEE 1800-2023 6.20.2)");
}
} else if (const AstNodeFTaskRef* refp = VN_CAST(exprp, NodeFTaskRef)) {
} else if (const AstNodeFTaskRef* const refp = VN_CAST(nodep, NodeFTaskRef)) {
if (refp->dotted() != "") {
refp->v3error("Parameter values cannot call hierarchical functions"
" (IEEE 1800-2023 6.20.2)");
}
}
});
checkParamNotHierRecurse(nodep->op1p(), childUnderQuery);
checkParamNotHierRecurse(nodep->op2p(), childUnderQuery);
checkParamNotHierRecurse(nodep->op3p(), childUnderQuery);
checkParamNotHierRecurse(nodep->op4p(), childUnderQuery);
}
}
// Deparameterize and constify nested interface cells within ifaceModp.
@ -2888,7 +2895,7 @@ class ParamVisitor final : public VNVisitor {
iterateChildren(nodep);
}
void visit(AstCell* nodep) override {
checkParamNotHier(nodep->paramsp());
checkParamNotHierRecurse(nodep->paramsp());
if (VN_IS(nodep->modp(), Iface)) m_ifaceInstCells.emplace(nodep->name(), nodep);
visitCellOrClassRef(nodep, VN_IS(nodep->modp(), Iface));
}
@ -2896,7 +2903,7 @@ class ParamVisitor final : public VNVisitor {
if (nodep->ifacep()) visitCellOrClassRef(nodep, true);
}
void visit(AstClassRefDType* nodep) override {
checkParamNotHier(nodep->paramsp());
checkParamNotHierRecurse(nodep->paramsp());
visitCellOrClassRef(nodep, false);
}
void visit(AstClassOrPackageRef* nodep) override {
@ -2913,7 +2920,7 @@ class ParamVisitor final : public VNVisitor {
if (nodep->isIfaceRef()) { m_ifacePortNames.insert(nodep->name()); }
iterateChildren(nodep);
if (nodep->isParam()) {
checkParamNotHier(nodep->valuep());
checkParamNotHierRecurse(nodep->valuep());
if (!nodep->valuep() && !VN_IS(m_modp, Class)) {
nodep->v3error("Parameter without default value is never given value"
<< " (IEEE 1800-2023 6.20.1): " << nodep->prettyNameQ());

View File

@ -94,6 +94,69 @@ void V3ParseImp::importIfInStd(FileLine* fileline, const string& id, bool doImpo
}
}
AstNodeExpr* V3ParseImp::makePropertyCase(FileLine* flp, AstNodeExpr* exprp, AstCaseItem* itemsp) {
AstNodeExpr* resultp = nullptr;
AstNodeExpr* matchedp = nullptr;
AstNodeExpr* defaultPropp = nullptr;
FileLine* defaultFlp = flp;
if (!itemsp) {
flp->v3error("Property case statement with no items");
exprp->deleteTree();
return new AstConst{flp, AstConst::BitTrue{}};
}
for (AstCaseItem *nextp, *itemp = itemsp; itemp; itemp = nextp) {
nextp = VN_AS(itemp->nextp(), CaseItem);
AstNodeExpr* const propp = VN_AS(itemp->stmtsp()->unlinkFrBack(), NodeExpr);
if (itemp->isDefault()) {
if (defaultPropp) {
itemp->v3error("Multiple default statements in property case statement");
defaultPropp->deleteTree();
exprp->deleteTree();
return new AstConst{flp, AstConst::BitTrue{}};
}
defaultFlp = itemp->fileline();
defaultPropp = propp;
continue;
}
AstNodeExpr* itemMatchp = nullptr;
for (AstNodeExpr *condNextp, *condp = itemp->condsp(); condp; condp = condNextp) {
condNextp = VN_AS(condp->nextp(), NodeExpr);
condp->unlinkFrBack();
AstNodeExpr* const eqp
= new AstEqCase{condp->fileline(), exprp->cloneTreePure(false), condp};
itemMatchp = itemMatchp ? new AstLogOr{itemp->fileline(), itemMatchp, eqp} : eqp;
}
UASSERT_OBJ(itemMatchp, itemp, "Property case item without condition");
AstNodeExpr* const guardp
= matchedp
? new AstLogAnd{itemp->fileline(), itemMatchp->cloneTreePure(false),
new AstLogNot{itemp->fileline(), matchedp->cloneTreePure(false)}}
: itemMatchp->cloneTreePure(false);
AstNodeExpr* const branchp = new AstImplication{itemp->fileline(), guardp, propp, true};
resultp = resultp ? new AstSAnd{flp, resultp, branchp} : branchp;
matchedp = matchedp ? new AstLogOr{itemp->fileline(), matchedp, itemMatchp} : itemMatchp;
}
itemsp->deleteTree();
if (defaultPropp) {
if (!matchedp) {
exprp->deleteTree();
return defaultPropp;
}
AstNodeExpr* const noMatchp
= static_cast<AstNodeExpr*>(new AstLogNot{defaultFlp, matchedp->cloneTreePure(false)});
AstNodeExpr* const branchp = new AstImplication{defaultFlp, noMatchp, defaultPropp, true};
resultp = new AstSAnd{flp, resultp, branchp};
}
matchedp->deleteTree();
exprp->deleteTree();
return resultp;
}
void V3ParseImp::lexPpline(const char* textp) {
// Handle lexer `line directive
// FileLine* const prevFl = lexFileline();

View File

@ -309,6 +309,7 @@ public:
void dumpTokensAhead(int line) VL_MT_DISABLED;
static void candidatePli(VSpellCheck* spellerp) VL_MT_DISABLED;
void importIfInStd(FileLine* fileline, const string& id, bool doImport);
AstNodeExpr* makePropertyCase(FileLine* flp, AstNodeExpr* exprp, AstCaseItem* itemsp);
private:
void preprocDumps(std::ostream& os);

View File

@ -210,7 +210,7 @@ class SliceVisitor final : public VNVisitor {
UINFO(9, " cloneSliceSel(" << elements << "," << elemIdx << ") " << nodep);
AstNodeExpr* const exprp = VN_AS(snodep->exprp(), NodeExpr);
AstNodeExpr* const selp = cloneAndSel(exprp, elements, elemIdx, needPure);
return new AstSampled{nodep->fileline(), selp};
return new AstSampled{nodep->fileline(), selp, nullptr};
} else if (AstExprStmt* const snodep = VN_CAST(nodep, ExprStmt)) {
UINFO(9, " cloneExprStmt(" << elements << "," << elemIdx << ") " << nodep);
AstNodeExpr* const resultSelp

View File

@ -64,16 +64,69 @@ struct UnrollStats final {
Stat m_nUnrolledIters{"Unrolled iterations"};
};
//######################################################################
// Variable bindings
class UnrolllBindings final {
// NODE STATE
// AstVarScope::user1() int: index of the binding in m_bindings
const VNUser1InUse m_inuser1;
// STATE
// Map from AstVarScope::user1() to current variable value, nullptr if not bound - idx 0 unused
std::vector<AstConst*> m_curr{nullptr};
// Stack of binding checkpoints
std::vector<std::vector<AstConst*>> m_checkpoints;
public:
// CONSTRUCTOR
UnrolllBindings() = default;
~UnrolllBindings() = default;
VL_UNCOPYABLE(UnrolllBindings);
VL_UNMOVABLE(UnrolllBindings);
// METHODS
void clear() {
m_curr.resize(1);
m_checkpoints.clear();
AstNode::user1ClearTree();
}
void checkpoint() { m_checkpoints.push_back(m_curr); }
void revert() {
m_curr = std::move(m_checkpoints.back());
m_checkpoints.pop_back();
}
void commit() { m_checkpoints.pop_back(); }
void set(AstVarScope* vscp, AstConst* valp) {
if (!vscp->user1()) {
vscp->user1(m_curr.size());
m_curr.push_back(nullptr);
}
UINFO(6, "Binding SET " << vscp->name() << " / " << vscp->user1() << " := " << valp);
m_curr[vscp->user1()] = valp;
}
AstConst* get(AstVarScope* vscp) {
// It's possible after a revert that user1 is set, but the vector is shorter, pad up
if (static_cast<size_t>(vscp->user1()) >= m_curr.size()) {
m_curr.resize(vscp->user1() + 1, nullptr);
}
AstConst* const valp = vscp->user1() ? m_curr[vscp->user1()] : nullptr;
UINFO(6, "Binding GET " << vscp->name() << " / " << vscp->user1() << " == " << valp);
return valp;
}
};
//######################################################################
// Unroll one AstLoop
class UnrollOneVisitor final : VNVisitor {
// NODE STATE
// AstVarScope::user1p() AstConst: Value of this AstVarScope
const VNUser1InUse m_user1InUse;
// STATE
UnrollStats& m_stats; // Statistics tracking
UnrolllBindings& m_bindings; // Variable bindings
AstLoop* const m_loopp; // The loop we are trying to unroll
AstNode* m_stmtsp = nullptr; // Resulting unrolled statement
size_t m_unrolledSize = 0; // Number of nodes in unrolled loop
@ -204,11 +257,11 @@ class UnrollOneVisitor final : VNVisitor {
AstVarRef* const refp = VN_CAST(np, VarRef);
if (!refp) return;
// Ignore if the referenced variable has no binding
AstConst* const valp = VN_AS(refp->varScopep()->user1p(), Const);
AstConst* const valp = m_bindings.get(refp->varScopep());
if (!valp) return;
// If writen, remove the binding
if (refp->access().isWriteOrRW()) {
refp->varScopep()->user1p(nullptr);
m_bindings.set(refp->varScopep(), nullptr);
return;
}
// Otherwise add it to the list of variables to substitute
@ -219,7 +272,7 @@ class UnrollOneVisitor final : VNVisitor {
// Actually substitute the variables that still have bindings
for (AstVarRef* const refp : toSubstitute) {
// Pick up bound value
AstConst* const valp = VN_AS(refp->varScopep()->user1p(), Const);
AstConst* const valp = m_bindings.get(refp->varScopep());
// Binding might have been removed after adding to 'toSubstitute'
if (!valp) continue;
// Substitute it
@ -230,8 +283,9 @@ class UnrollOneVisitor final : VNVisitor {
}
// CONSTRUCTOR
UnrollOneVisitor(UnrollStats& stats, AstLoop* loopp)
UnrollOneVisitor(UnrollStats& stats, UnrolllBindings& bindings, AstLoop* loopp)
: m_stats{stats}
, m_bindings{bindings}
, m_loopp{loopp} {
UASSERT_OBJ(!loopp->contsp(), loopp, "'contsp' only used before LinkJump");
// Do not unroll if we are told not to
@ -239,25 +293,9 @@ class UnrollOneVisitor final : VNVisitor {
cantUnroll(loopp, m_stats.m_nPragmaDisabled);
return;
}
// Gather variable bindings from the preceding statements
for (AstNode *succp = loopp, *currp = loopp->backp(); currp->nextp() == succp;
succp = currp, currp = currp->backp()) {
AstAssign* const assignp = VN_CAST(currp, Assign);
if (!assignp) break;
AstConst* const valp = VN_CAST(V3Const::constifyEdit(assignp->rhsp()), Const);
if (!valp) break;
AstVarRef* const lhsp = VN_CAST(assignp->lhsp(), VarRef);
if (!lhsp) break;
// Don't bind if volatile
if (lhsp->varp()->isForced() || lhsp->varp()->isSigUserRWPublic()) continue;
// Don't overwrite a later binding
if (lhsp->varScopep()->user1p()) continue;
// Set up the binding
lhsp->varScopep()->user1p(valp);
}
// Attempt to unroll the loop
const size_t iterLimit
= m_unrollFull ? v3Global.opt.unrollLimit() : v3Global.opt.unrollCount();
const size_t iterLimit = m_unrollFull ? v3Global.opt.unrollLimit() //
: v3Global.opt.unrollCount();
size_t iterCount = 0;
do {
if (iterCount > iterLimit) {
@ -316,7 +354,7 @@ class UnrollOneVisitor final : VNVisitor {
AstVarRef* const lhsp = VN_CAST(nodep->lhsp(), VarRef);
AstConst* const valp = VN_CAST(V3Const::constifyEdit(nodep->rhsp()), Const);
if (lhsp && valp && !lhsp->varp()->isForced() && !lhsp->varp()->isSigUserRWPublic()) {
lhsp->varScopep()->user1p(valp);
m_bindings.set(lhsp->varScopep(), valp);
return;
}
// Otherwise just like a generic statement
@ -340,12 +378,32 @@ class UnrollOneVisitor final : VNVisitor {
// Remove trailing dead code
if (nodep->nextp()) pushDeletep(nodep->nextp()->unlinkFrBackWithNext());
}
void visit(AstLoop* nodep) override {
m_bindings.checkpoint();
std::pair<AstNode*, bool> pair = UnrollOneVisitor::apply(m_stats, m_bindings, nodep);
// If failed, revert the bindings and process the loop as a generic statement
if (!pair.second) {
m_bindings.revert();
process(nodep);
return;
}
// Commit the bindings and replace the loop with the unrolled code
m_bindings.commit();
if (pair.first) {
nodep->replaceWith(pair.first);
} else {
nodep->unlinkFrBack();
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
public:
// Unroll the given loop. Returns the resulting statements and the number of
// iterations unrolled (0 if unrolling failed);
static std::pair<AstNode*, bool> apply(UnrollStats& stats, AstLoop* loopp) {
UnrollOneVisitor visitor{stats, loopp};
// Unroll the given loop. Returns the resulting statements and a flag indicating success
static std::pair<AstNode*, bool> apply(UnrollStats& stats, UnrolllBindings& bindings,
AstLoop* loopp) {
UnrollOneVisitor visitor{stats, bindings, loopp};
// If successfully unrolled, return the resulting list of statements - might be empty
if (visitor.m_ok) return {visitor.m_stmtsp, true};
// Otherwise delete intermediate results
@ -358,13 +416,40 @@ public:
// Unroll all AstLoop statements
class UnrollAllVisitor final : VNVisitor {
// STATE - Statistic tracking
UnrollStats m_stats;
// STATE
UnrollStats m_stats; // Statistic tracking
UnrolllBindings m_bindings; // Variable bindings
// VISIT
void visit(AstLoop* nodep) override {
// Gather variable bindings from the preceding statements
m_bindings.clear();
for (AstNode *succp = nodep, *currp = nodep->backp(); true;
succp = currp, currp = currp->backp()) {
// If the current statement is in higher list, proceed carefully
if (currp->nextp() != succp) {
// Jump block is OK, there is only one way to get here
if (VN_IS(currp, JumpBlock)) continue;
// TODO: if?
// Otherwise we dont' know the control flow, give up
break;
}
AstAssign* const assignp = VN_CAST(currp, Assign);
if (!assignp) break;
AstConst* const valp = VN_CAST(V3Const::constifyEdit(assignp->rhsp()), Const);
if (!valp) break;
AstVarRef* const lhsp = VN_CAST(assignp->lhsp(), VarRef);
if (!lhsp) break;
// Don't bind if volatile
if (lhsp->varp()->isForced() || lhsp->varp()->isSigUserRWPublic()) continue;
// Don't overwrite a later binding
if (m_bindings.get(lhsp->varScopep())) continue;
// Set up the binding
m_bindings.set(lhsp->varScopep(), valp);
}
// Attempt to unroll this loop
const std::pair<AstNode*, bool> pair = UnrollOneVisitor::apply(m_stats, nodep);
const std::pair<AstNode*, bool> pair = UnrollOneVisitor::apply(m_stats, m_bindings, nodep);
// If failed, carry on with nested loop
if (!pair.second) {

View File

@ -1714,6 +1714,13 @@ class WidthVisitor final : public VNVisitor {
}
void visit(AstUntil* nodep) override {
if (nodep->isStrong()
&& (v3Global.opt.timing().isSetFalse() || !v3Global.opt.timing().isSetTrue())) {
nodep->v3warn(E_NOTIMING, nodep->verilogKwd() << " requires --timing");
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return;
}
assertAtExpr(nodep);
if (m_vup->prelim()) {
iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH);
@ -1926,6 +1933,25 @@ class WidthVisitor final : public VNVisitor {
if (nodep->iffp()) iterateCheckBool(nodep, "iff condition", nodep->iffp(), BOTH);
userIterateAndNext(nodep->optionsp(), nullptr);
}
void visit(AstCoverBin* nodep) override {
// Bin range/value entries are self-determined constant expressions (IEEE 1800-2023
// 19.5). Width each plain single-value entry self-determined so a referenced
// parameter acquires a dtype, then constify so the reference folds to the AstConst
// value that V3Covergroup requires. AstInsideRange entries fold their own bounds in
// visit(AstInsideRange).
for (AstNode *nextp, *itemp = nodep->rangesp(); itemp; itemp = nextp) {
nextp = itemp->nextp();
if (VN_IS(itemp, InsideRange)) {
userIterate(itemp, nullptr);
} else {
userIterate(itemp, WidthVP{SELF, BOTH}.p());
V3Const::constifyEdit(itemp); // itemp may change
}
}
userIterateAndNext(nodep->iffp(), nullptr);
userIterateAndNext(nodep->arraySizep(), nullptr);
userIterateAndNext(nodep->transp(), nullptr);
}
void visit(AstPow* nodep) override {
// Pow is special, output sign only depends on LHS sign, but
// function result depends on both signs
@ -3566,17 +3592,20 @@ class WidthVisitor final : public VNVisitor {
}
void visit(AstInsideRange* nodep) override {
// Just do each side; AstInside will rip these nodes out later.
// When m_vup is null, this range appears outside a normal expression context (e.g.
// in a covergroup bin declaration). Pre-fold constant arithmetic in that case
// (e.g., AstNegate(Const) -> Const) so children have their types set before widthing.
// We cannot do this unconditionally: in a normal 'inside' expression (m_vup set),
// range bounds may be enum refs not yet widthed, and constifyEdit would crash.
if (!m_vup) {
// This range appears outside a normal expression context (e.g. in a covergroup
// bin declaration). Width the bounds self-determined first so each bound (and any
// referenced parameter) has a dtype, then constify so constant arithmetic is folded
// (e.g. AstNegate(Const) -> Const, or a parameter reference -> Const). Folding
// preserves the now-present dtype, so no bound reaches V3WidthCommit without one.
userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p());
V3Const::constifyEdit(nodep->lhsp()); // lhsp may change
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
} else {
userIterateAndNext(nodep->lhsp(), m_vup);
userIterateAndNext(nodep->rhsp(), m_vup);
}
userIterateAndNext(nodep->lhsp(), m_vup);
userIterateAndNext(nodep->rhsp(), m_vup);
nodep->dtypeFrom(nodep->lhsp());
}

View File

@ -4531,7 +4531,7 @@ system_f_or_t_expr_call<nodeExprp>: // IEEE: part of system_tf_call (can be tas
| yD_ROSE '(' expr ',' expr ')' { $$ = new AstRose{$1, $3, GRAMMARP->createSenTreeChanged($1, $5)}; }
| yD_ROSE_GCLK '(' expr ')' { $$ = new AstRose{$1, $3, GRAMMARP->createGlobalClockSenTree($1)}; }
| yD_RTOI '(' expr ')' { $$ = new AstRToIS{$1, $3}; }
| yD_SAMPLED '(' expr ')' { $$ = new AstSampled{$1, $3}; }
| yD_SAMPLED '(' expr ')' { $$ = new AstSampled{$1, $3, $3->dtypep()}; }
| yD_SFORMATF '(' exprDispList ')' { $$ = new AstSFormatF{$1, AstSFormatF::ExprFormat{}, $3, 'd', false}; }
| yD_SHORTREALTOBITS '(' expr ')' { $$ = new AstRealToBits{$1, $3}; UNSUPREAL($1); }
| yD_SIGNED '(' expr ')' { $$ = new AstSigned{$1, $3}; }
@ -6678,13 +6678,9 @@ property_spec<propSpecp>: // IEEE: property_spec
property_exprCaseIf<nodeExprp>: // IEEE: part of property_expr for if/case
yCASE '(' expr/*expression_or_dist*/ ')' property_case_itemList yENDCASE
{ $$ = new AstConst{$1, AstConst::BitFalse{}};
BBUNSUP($<fl>1, "Unsupported: property case expression");
DEL($3, $5); }
{ $$ = PARSEP->makePropertyCase($1, $3, $5); }
| yCASE '(' expr/*expression_or_dist*/ ')' yENDCASE
{ $$ = new AstConst{$1, AstConst::BitFalse{}};
BBUNSUP($<fl>1, "Unsupported: property case expression");
DEL($3); }
{ $$ = PARSEP->makePropertyCase($1, $3, nullptr); }
| yIF '(' expr/*expression_or_dist*/ ')' pexpr %prec prIF
{ $$ = new AstImplication{$1, $3, $5, true}; }
| yIF '(' expr/*expression_or_dist*/ ')' pexpr yELSE pexpr
@ -6695,16 +6691,15 @@ property_exprCaseIf<nodeExprp>: // IEEE: part of property_expr for if/case
property_case_itemList<caseItemp>: // IEEE: {property_case_item}
property_case_item { $$ = $1; }
| property_case_itemList ',' property_case_item { $$ = addNextNull($1, $3); }
| property_case_itemList property_case_item { $$ = addNextNull($1, $2); }
;
property_case_item<caseItemp>: // ==IEEE: property_case_item
// // IEEE: expression_or_dist { ',' expression_or_dist } ':' property_statement
// // IEEE: expression_or_dist { ',' expression_or_dist } ':' property_expr
// // IEEE 1800-2012 changed from property_statement to property_expr
// // IEEE 1800-2017 changed to require the semicolon
caseCondList ':' pexpr { $$ = new AstCaseItem{$2, $1, $3}; }
| caseCondList ':' pexpr ';' { $$ = new AstCaseItem{$2, $1, $3}; }
| yDEFAULT pexpr { $$ = new AstCaseItem{$1, nullptr, $2}; }
caseCondList ':' pexpr ';' { $$ = new AstCaseItem{$2, $1, $3}; }
| yDEFAULT pexpr ';' { $$ = new AstCaseItem{$1, nullptr, $2}; }
| yDEFAULT ':' pexpr ';' { $$ = new AstCaseItem{$1, nullptr, $3}; }
;

View File

@ -48,6 +48,7 @@
//END_MODULE_NAME--------------------------------------------------------------
//See also: https://github.com/twosigma/verilator_support
// verilog_format: off
// verilator lint_off COMBDLY,INITIALDLY,LATCH,MULTIDRIVEN,UNSIGNED,WIDTH
// BEGINNING OF MODULE

View File

@ -1,5 +1,5 @@
%Warning-DEPRECATED: t/t_clk_first_deprecated.v:12:14: sc_clock is ignored
12 | input clk /*verilator sc_clock*/;
%Warning-DEPRECATED: t/t_clk_first_deprecated.v:11:14: sc_clock is ignored
11 | input clk /*verilator sc_clock*/;
| ^~~~~~~~~~~~~~~~~~~~~~
... For warning description see https://verilator.org/warn/DEPRECATED?v=latest
... Use "/* verilator lint_off DEPRECATED */" and lint_on around source to disable this message.

View File

@ -4,8 +4,7 @@
// SPDX-FileCopyrightText: 2003 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
module t ( /*AUTOARG*/
// Inputs
module t (
clk
);

View File

@ -1,6 +1,6 @@
%Error: t/t_constraint_pure_nonabs_bad.v:8:20: Illegal to have 'pure constraint' in non-abstract class (IEEE 1800-2023 18.5.2)
%Error: t/t_constraint_pure_nonabs_bad.v:8:19: Illegal to have 'pure constraint' in non-abstract class (IEEE 1800-2023 18.5.2)
: ... note: In instance 't'
8 | pure constraint raintBad;
| ^~~~~~~~
8 | pure constraint raintBad;
| ^~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -5,7 +5,7 @@
// SPDX-License-Identifier: CC0-1.0
class NonAsbstract;
pure constraint raintBad; // Bad: Not in abstract class
pure constraint raintBad; // Bad: Not in abstract class
endclass
module t;

View File

@ -0,0 +1,5 @@
cg.cp.maxv: 2
cg.cp.minv: 1
cg.cp.negative: 1
cg.cp.positive: 3
cg.cp.zero: 1

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
import coverage_covergroup_common
test.scenarios('vlt')
coverage_covergroup_common.run(test)

View File

@ -0,0 +1,76 @@
// DESCRIPTION: Verilator: Verilog Test module - parameter/localparam refs in bins
// This file ONLY is placed into the Public Domain, for any use, without warranty.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Test: Referencing a parameter/localparam identifier inside a covergroup
// `bins` value or range. This previously triggered an internal error
// (V3WidthCommit.cpp: No dtype) because the folded constant had no dtype.
// Expected: compiles and the bounds behave exactly like numeric literals.
// verilog_format: off
`define stop $stop
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t #(
parameter int PMIN = -100
) (
input clk
);
localparam int LMAX = 100;
int signed value;
covergroup cg;
cp: coverpoint value {
bins negative = {[PMIN : -1]}; // parameter as range lower bound
bins zero = {0};
bins positive = {[1 : LMAX]}; // localparam as range upper bound
bins maxv = {LMAX}; // localparam as single value
bins minv = {PMIN}; // parameter as single value
}
endgroup
cg cg_inst = new;
int cyc = 0;
always @(posedge clk) begin
cyc <= cyc + 1;
case (cyc)
0: value <= -100; // Hit negative + minv
1: value <= 0; // Hit zero
2: value <= 50; // Hit positive
3: value <= 100; // Hit positive (dup) + maxv
4: begin
$write("*-* All Finished *-*\n");
$finish;
end
endcase
cg_inst.sample();
// Coverage progression (NBA committed before sample() within always block).
// A final sample() also runs on the $finish cycle (value still 100), so the
// positive/maxv hit counts are one higher than the number of distinct stimuli.
// cyc=0: value=-100 -> negative + minv -> 2/5=40%
// cyc=1: value=0 -> zero -> 3/5=60%
// cyc=2: value=50 -> positive -> 4/5=80%
// cyc=3: value=100 -> positive(dup) + maxv -> 5/5=100%
if (cyc == 0) begin
`checkr(cg_inst.get_inst_coverage(), 40.0);
end
if (cyc == 1) begin
`checkr(cg_inst.get_inst_coverage(), 60.0);
end
if (cyc == 2) begin
`checkr(cg_inst.get_inst_coverage(), 80.0);
end
if (cyc == 3) begin
`checkr(cg_inst.get_inst_coverage(), 100.0);
end
end
endmodule

View File

@ -62,6 +62,13 @@ module t (
//verilator lint_on WIDTH
`signal(FOLD_UNARY_Extend, tmp_FOLD_UNARY_Extend);
`signal(FOLD_UNARY_ExtendS, tmp_FOLD_UNARY_ExtendS);
`signal(FOLD_UNARY_CountOnes, $countones(const_a));
`signal(FOLD_UNARY_OneHot, $onehot(const_a));
`signal(FOLD_UNARY_OneHot0, $onehot0(const_a));
`signal(FOLD_UNARY_OneHot_A, $onehot(const_a[0]));
`signal(FOLD_UNARY_OneHot_B, $onehot(~const_a[0]));
`signal(FOLD_UNARY_OneHot0_A, $onehot0(const_a[0]));
`signal(FOLD_UNARY_OneHot0_B, $onehot0(~const_a[0]));
`signal(FOLD_BINARY_Add, const_a + const_b);
`signal(FOLD_BINARY_And, const_a & const_b);

View File

@ -570,4 +570,11 @@ module t (
wire logic [7:0] func_3 = pkg::branchy(rand_a[7:0], rand_b[7:0]);
`signal(FUNC_3, func_3);
logic [1:0] via_creset;
always_comb begin
automatic logic [1:0] a /* = AstCReset */;
via_creset = a + 2'd1;
end
`signal(VIA_CRESET, via_creset);
endmodule

View File

@ -8,12 +8,13 @@
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
import collections
test.scenarios('dist')
Tabs_Exempt_Re = r'(\.out$)|(/fstcpp)|(Makefile)|(\.mk$)|(\.mk\.in$)|test_regress/t/t_preproc\.v|install-sh'
Unicode_Exempt_Re = r'(Changes$|CONTRIBUTORS$|LICENSES?|contributors.rst$|spelling.txt$)'
Unicode_Exempt_Re = r'(Changes$|CONTRIBUTORS$|LICENSES?|contributors.rst$|spelling.txt$|ci-rtlmeter-report.py)'
def get_source_files():
@ -28,6 +29,39 @@ def get_source_files():
return files
def check_verilog_indent(filename, contents):
# Check for proper spacing
# This isn't automated as part of CI action auto-format because .out files
# might break as a result
indents = collections.defaultdict(int)
is_on = True
for line in contents.splitlines():
if re.search(r'// verilog_format: off', line):
is_on = False
if re.search(r'// verilog_format: on', line):
is_on = True
if not is_on:
continue
# verilog-format puts input/output as double levels, ignore
if re.match(r' *(input|output|inout) ', line):
continue
m = re.match(r'^ *', line)
num_spaces = m.end() if m else 0
if num_spaces > 0:
indents[num_spaces] += 1
# pprint(indents)
spacing = 2
if (indents[3] + indents[6] + indents[9]) > (indents[2] + indents[4] + indents[6]) * 2:
spacing = 3
elif indents[2] == 0 and indents[4] > 1: # Hard because two levels of 2-space indents gives 4
spacing = 4
if spacing != 2:
warns[filename] = "Indents should be 2 spaces per level, not what seems to be " + str(
spacing) + "/level in: " + filename
if not os.path.exists(test.root + "/.git"):
test.skip("Not in a git repository")
@ -46,7 +80,7 @@ for filename in sorted(files.keys()):
continue # Ignore binary files
if contents != "" and contents[-1] != "\n":
contents += "\n"
warns[filename] = "Missing trailing newline (add one) in " + filename
warns[filename] = "Missing trailing newline (add one) in: " + filename
if "\r" in contents:
contents = re.sub(r'\r', '', contents)
warns[filename] = "Carriage returns (remove them) in: " + filename
@ -84,7 +118,7 @@ for filename in sorted(files.keys()):
warns[filename] += " (last character is ASCII " + str(ord(line[-1])) + ")"
if not eol_ws_exempt and re.search(r'\n\n+$', contents): # Regexp repeated above
warns[filename] = "Trailing newlines at EOF in " + filename
warns[filename] = "Trailing newlines at EOF in: " + filename
# Unicode checker; should this be done in another file?
# No way to auto-fix.
@ -92,7 +126,10 @@ for filename in sorted(files.keys()):
m = re.search(r'(([^ \t\r\n\x20-\x7e]).*)', contents)
if not unicode_exempt and m:
warns[filename] = "Warning: non-ASCII contents '" + m.group(2) + "' at '" + m.group(
1) + "' in " + filename
1) + "' in: " + filename
if re.search(r'\.s?vh?$', filename):
check_verilog_indent(filename, contents)
fcount += 1
@ -106,7 +143,8 @@ if len(warns):
msg += "Updated files with whitespace errors: " + ' '.join(sorted(warns.keys())) + "\n"
else:
msg += "Files have whitespace errors: " + ' '.join(sorted(warns.keys())) + "\n"
msg += "To auto-fix: HARNESS_UPDATE_GOLDEN=1 {command} or --golden\n"
msg += "To auto-fix (some): HARNESS_UPDATE_GOLDEN=1 {command} or --golden\n"
msg += "If change any Verilog then remember to update .out files too (with --golden)\n"
for filename in sorted(warns.keys()):
msg += warns[filename] + "\n"
test.error(msg)

View File

@ -9,29 +9,26 @@
// See t_dpi_accessors.v for details of the test. This file should be included
// by the top level module to define all the accessors needed.
// Use the macros to provide the desire access to our data. First simple
// access to the registers, array elements and wires. For consistency with
// simulators, we do not attempt to write wires.
`RW_ACCESS([0:0], a, {t.i_test_sub.a});
`RW_ACCESS([7:0], b, {t.i_test_sub.b});
`RW_ACCESS([7:0], mem32, {t.i_test_sub.mem[32]});
`R_ACCESS ([0:0], c, {t.i_test_sub.c});
`R_ACCESS ([7:0], d, {t.i_test_sub.d});
`RW_ACCESS([7:0], e, {t.i_test_sub.e});
`RW_ACCESS([7:0], f, {t.i_test_sub.f});
// Use the macros to provide the desire access to our data. First simple
// access to the registers, array elements and wires. For consistency with
// simulators, we do not attempt to write wires.
`RW_ACCESS([0:0], a, {t.i_test_sub.a});
`RW_ACCESS([7:0], b, {t.i_test_sub.b});
`RW_ACCESS([7:0], mem32, {t.i_test_sub.mem[32]});
`R_ACCESS([0:0], c, {t.i_test_sub.c});
`R_ACCESS([7:0], d, {t.i_test_sub.d});
`RW_ACCESS([7:0], e, {t.i_test_sub.e});
`RW_ACCESS([7:0], f, {t.i_test_sub.f});
// Slices of vectors and array elements. For consistency with simulators,
// we do not attempt to write wire slices.
`RW_ACCESS([3:0], b_slice, {t.i_test_sub.b[3:0]});
`RW_ACCESS([4:0], mem32_slice,
{t.i_test_sub.mem[32][7:6], t.i_test_sub.mem[32][2:0]});
`R_ACCESS([5:0], d_slice, {t.i_test_sub.d[6:1]});
// Slices of vectors and array elements. For consistency with simulators,
// we do not attempt to write wire slices.
`RW_ACCESS([3:0], b_slice, {t.i_test_sub.b[3:0]});
`RW_ACCESS([4:0], mem32_slice, {t.i_test_sub.mem[32][7:6], t.i_test_sub.mem[32][2:0]});
`R_ACCESS([5:0], d_slice, {t.i_test_sub.d[6:1]});
// Complex registers, one with distinct read and write. We avoid use of
// wires for consistency with simulators.
`RW_ACCESS([14:0], l1, {t.i_test_sub.b[3:0],
t.i_test_sub.mem[32][7:6],
t.i_test_sub.e[6:1],
t.i_test_sub.mem[32][2:0]});
`R_ACCESS([7:0], l2, {t.i_test_sub.e[7:4], t.i_test_sub.f[3:0]});
`W_ACCESS([7:0], l2, {t.i_test_sub.e[5:2], t.i_test_sub.f[5:2]});
// Complex registers, one with distinct read and write. We avoid use of
// wires for consistency with simulators.
`RW_ACCESS([14:0], l1, {t.i_test_sub.b[3:0], t.i_test_sub.mem[32][7:6], t.i_test_sub.e[6:1],
t.i_test_sub.mem[32][2:0]});
`R_ACCESS([7:0], l2, {t.i_test_sub.e[7:4], t.i_test_sub.f[3:0]});
`W_ACCESS([7:0], l2, {t.i_test_sub.e[5:2], t.i_test_sub.f[5:2]});

View File

@ -9,20 +9,22 @@
// See t_dpi_accessors.v for details of the test. This file should be included
// by the top level module to define the generic accessor macros.
// verilog_format: off
// Accessor macros, to keep stuff concise
`define R_ACCESS(type_spec, name, expr) \
export "DPI-C" function name``_read; \
function bit type_spec name``_read; \
name``_read = (expr); \
endfunction
export "DPI-C" function name``_read; \
function bit type_spec name``_read; \
name``_read = (expr); \
endfunction
`define W_ACCESS(type_spec, name, expr) \
export "DPI-C" task name``_write; \
task name``_write; \
input bit type_spec in; \
expr = in; \
endtask
`define W_ACCESS(type_spec, name, expr) \
export "DPI-C" task name``_write; \
task name``_write; \
input bit type_spec in; \
expr = in; \
endtask
`define RW_ACCESS(type_spec, name, expr) \
`R_ACCESS (type_spec, name, expr); \
`W_ACCESS (type_spec, name, expr)
`R_ACCESS (type_spec, name, expr); \
`W_ACCESS (type_spec, name, expr)

View File

@ -4,13 +4,16 @@
// SPDX-FileCopyrightText: 2026 Nikolai Kumar
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define checkh(g,e) do if ((g) !==(e)) begin $write("%%Error: %s:%0d: got=%x exp=%x\n", `__FILE__,`__LINE__, (g),(e)); $stop; end while(0)
`ifdef CMT
`define FORCEABLE /*verilator forceable*/
`define FORCEABLE /*verilator forceable*/
`else
`define FORCEABLE
`define FORCEABLE
`endif
// verilog_format: on
module t (input wire clk, output reg [31:0] cyc);
initial cyc = 0;
always @(posedge clk) cyc <= cyc + 1;

View File

@ -11,204 +11,204 @@ module t (clk);
logic reset;
reg [255:0] v2_0;
reg [255:0] v1_0;
reg [255:0] v1_1;
reg [255:0] v1_2;
reg [255:0] v1_3;
reg [255:0] v1_4;
reg [255:0] v1_5;
reg [255:0] v1_6;
reg [255:0] v1_7;
// verilator lint_off MULTIDRIVEN
reg [255:0] dummy;
// verilator lint_on MULTIDRIVEN
reg [255:0] v2_0;
reg [255:0] v1_0;
reg [255:0] v1_1;
reg [255:0] v1_2;
reg [255:0] v1_3;
reg [255:0] v1_4;
reg [255:0] v1_5;
reg [255:0] v1_6;
reg [255:0] v1_7;
// verilator lint_off MULTIDRIVEN
reg [255:0] dummy;
// verilator lint_on MULTIDRIVEN
Calculate calc0(.clk(clk), .reset(reset), .v1_0(v1_0), .v1_1(dummy), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy));
Calculate calc1(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(v1_1), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy));
Calculate calc2(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(v1_2), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy));
Calculate calc3(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(dummy), .v1_3(v1_3), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy));
Calculate calc4(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(dummy), .v1_3(dummy), .v1_4(v1_4), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy));
Calculate calc5(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(v1_5), .v1_6(dummy), .v1_7(dummy));
Calculate calc6(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(v1_6), .v1_7(dummy));
Calculate calc7(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(v1_7));
always @ (posedge clk) v2_0 <= v1_0 + v1_1 + v1_2 + v1_3 + v1_4 + v1_5 + v1_6 + v1_7;
Check chk(.clk(clk), .reset(reset), .v2_0(v2_0));
Calculate calc0(.clk(clk), .reset(reset), .v1_0(v1_0), .v1_1(dummy), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy));
Calculate calc1(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(v1_1), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy));
Calculate calc2(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(v1_2), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy));
Calculate calc3(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(dummy), .v1_3(v1_3), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy));
Calculate calc4(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(dummy), .v1_3(dummy), .v1_4(v1_4), .v1_5(dummy), .v1_6(dummy), .v1_7(dummy));
Calculate calc5(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(v1_5), .v1_6(dummy), .v1_7(dummy));
Calculate calc6(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(v1_6), .v1_7(dummy));
Calculate calc7(.clk(clk), .reset(reset), .v1_0(dummy), .v1_1(dummy), .v1_2(dummy), .v1_3(dummy), .v1_4(dummy), .v1_5(dummy), .v1_6(dummy), .v1_7(v1_7));
always @ (posedge clk) v2_0 <= v1_0 + v1_1 + v1_2 + v1_3 + v1_4 + v1_5 + v1_6 + v1_7;
Check chk(.clk(clk), .reset(reset), .v2_0(v2_0));
endmodule
module Check(input clk, output logic reset, input reg [255:0] v2_0);
integer cyc=0;
always @ (posedge clk) begin
cyc <= cyc + 1;
always @ (posedge clk) begin
cyc <= cyc + 1;
`ifdef TEST_VERBOSE
$write("[%0t] rst=%0x v0_0=%0x v1_0=%0x result=%0x\n", $time, reset, v0_0, v1_0, v2_0);
$write("[%0t] rst=%0x v0_0=%0x v1_0=%0x result=%0x\n", $time, reset, v0_0, v1_0, v2_0);
`endif
if (cyc==0) begin
reset <= 1;
end
else if (cyc==10) begin
reset <= 0;
end
if (cyc==0) begin
reset <= 1;
end
else if (cyc==10) begin
reset <= 0;
end
`ifndef SIM_CYCLES
`define SIM_CYCLES 99
`endif
else if (cyc==`SIM_CYCLES) begin
if (v2_0 != 256'd2017) $stop;
$write("VARS=64 WIDTH=256 WORKINGSET=2KB\n");
$write("*-* All Finished *-*\n");
$finish;
end
end
else if (cyc==`SIM_CYCLES) begin
if (v2_0 != 256'd2017) $stop;
$write("VARS=64 WIDTH=256 WORKINGSET=2KB\n");
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module Calculate(input clk,
input reset,
output reg [255:0] v1_0,
output reg [255:0] v1_1,
output reg [255:0] v1_2,
output reg [255:0] v1_3,
output reg [255:0] v1_4,
output reg [255:0] v1_5,
output reg [255:0] v1_6,
output reg [255:0] v1_7
);
reg [255:0] v0_0;
reg [255:0] v0_1;
reg [255:0] v0_2;
reg [255:0] v0_3;
reg [255:0] v0_4;
reg [255:0] v0_5;
reg [255:0] v0_6;
reg [255:0] v0_7;
reg [255:0] v0_8;
reg [255:0] v0_9;
reg [255:0] v0_10;
reg [255:0] v0_11;
reg [255:0] v0_12;
reg [255:0] v0_13;
reg [255:0] v0_14;
reg [255:0] v0_15;
reg [255:0] v0_16;
reg [255:0] v0_17;
reg [255:0] v0_18;
reg [255:0] v0_19;
reg [255:0] v0_20;
reg [255:0] v0_21;
reg [255:0] v0_22;
reg [255:0] v0_23;
reg [255:0] v0_24;
reg [255:0] v0_25;
reg [255:0] v0_26;
reg [255:0] v0_27;
reg [255:0] v0_28;
reg [255:0] v0_29;
reg [255:0] v0_30;
reg [255:0] v0_31;
reg [255:0] v0_32;
reg [255:0] v0_33;
reg [255:0] v0_34;
reg [255:0] v0_35;
reg [255:0] v0_36;
reg [255:0] v0_37;
reg [255:0] v0_38;
reg [255:0] v0_39;
reg [255:0] v0_40;
reg [255:0] v0_41;
reg [255:0] v0_42;
reg [255:0] v0_43;
reg [255:0] v0_44;
reg [255:0] v0_45;
reg [255:0] v0_46;
reg [255:0] v0_47;
reg [255:0] v0_48;
reg [255:0] v0_49;
reg [255:0] v0_50;
reg [255:0] v0_51;
reg [255:0] v0_52;
reg [255:0] v0_53;
reg [255:0] v0_54;
reg [255:0] v0_55;
reg [255:0] v0_56;
reg [255:0] v0_57;
reg [255:0] v0_58;
reg [255:0] v0_59;
reg [255:0] v0_60;
reg [255:0] v0_61;
reg [255:0] v0_62;
reg [255:0] v0_63;
input reset,
output reg [255:0] v1_0,
output reg [255:0] v1_1,
output reg [255:0] v1_2,
output reg [255:0] v1_3,
output reg [255:0] v1_4,
output reg [255:0] v1_5,
output reg [255:0] v1_6,
output reg [255:0] v1_7
);
reg [255:0] v0_0;
reg [255:0] v0_1;
reg [255:0] v0_2;
reg [255:0] v0_3;
reg [255:0] v0_4;
reg [255:0] v0_5;
reg [255:0] v0_6;
reg [255:0] v0_7;
reg [255:0] v0_8;
reg [255:0] v0_9;
reg [255:0] v0_10;
reg [255:0] v0_11;
reg [255:0] v0_12;
reg [255:0] v0_13;
reg [255:0] v0_14;
reg [255:0] v0_15;
reg [255:0] v0_16;
reg [255:0] v0_17;
reg [255:0] v0_18;
reg [255:0] v0_19;
reg [255:0] v0_20;
reg [255:0] v0_21;
reg [255:0] v0_22;
reg [255:0] v0_23;
reg [255:0] v0_24;
reg [255:0] v0_25;
reg [255:0] v0_26;
reg [255:0] v0_27;
reg [255:0] v0_28;
reg [255:0] v0_29;
reg [255:0] v0_30;
reg [255:0] v0_31;
reg [255:0] v0_32;
reg [255:0] v0_33;
reg [255:0] v0_34;
reg [255:0] v0_35;
reg [255:0] v0_36;
reg [255:0] v0_37;
reg [255:0] v0_38;
reg [255:0] v0_39;
reg [255:0] v0_40;
reg [255:0] v0_41;
reg [255:0] v0_42;
reg [255:0] v0_43;
reg [255:0] v0_44;
reg [255:0] v0_45;
reg [255:0] v0_46;
reg [255:0] v0_47;
reg [255:0] v0_48;
reg [255:0] v0_49;
reg [255:0] v0_50;
reg [255:0] v0_51;
reg [255:0] v0_52;
reg [255:0] v0_53;
reg [255:0] v0_54;
reg [255:0] v0_55;
reg [255:0] v0_56;
reg [255:0] v0_57;
reg [255:0] v0_58;
reg [255:0] v0_59;
reg [255:0] v0_60;
reg [255:0] v0_61;
reg [255:0] v0_62;
reg [255:0] v0_63;
always @ (posedge clk) v0_0 <= reset ? 256'd1 : v0_1;
always @ (posedge clk) v0_1 <= reset ? 256'd1 : v0_2;
always @ (posedge clk) v0_2 <= reset ? 256'd2 : v0_3;
always @ (posedge clk) v0_3 <= reset ? 256'd3 : v0_4;
always @ (posedge clk) v0_4 <= reset ? 256'd4 : v0_5;
always @ (posedge clk) v0_5 <= reset ? 256'd5 : v0_6;
always @ (posedge clk) v0_6 <= reset ? 256'd6 : v0_7;
always @ (posedge clk) v0_7 <= reset ? 256'd7 : v0_0;
always @ (posedge clk) v0_8 <= reset ? 256'd8 : v0_9;
always @ (posedge clk) v0_9 <= reset ? 256'd9 : v0_10;
always @ (posedge clk) v0_10 <= reset ? 256'd10 : v0_11;
always @ (posedge clk) v0_11 <= reset ? 256'd11 : v0_12;
always @ (posedge clk) v0_12 <= reset ? 256'd12 : v0_13;
always @ (posedge clk) v0_13 <= reset ? 256'd13 : v0_14;
always @ (posedge clk) v0_14 <= reset ? 256'd14 : v0_15;
always @ (posedge clk) v0_15 <= reset ? 256'd15 : v0_8;
always @ (posedge clk) v0_16 <= reset ? 256'd16 : v0_17;
always @ (posedge clk) v0_17 <= reset ? 256'd17 : v0_18;
always @ (posedge clk) v0_18 <= reset ? 256'd18 : v0_19;
always @ (posedge clk) v0_19 <= reset ? 256'd19 : v0_20;
always @ (posedge clk) v0_20 <= reset ? 256'd20 : v0_21;
always @ (posedge clk) v0_21 <= reset ? 256'd21 : v0_22;
always @ (posedge clk) v0_22 <= reset ? 256'd22 : v0_23;
always @ (posedge clk) v0_23 <= reset ? 256'd23 : v0_16;
always @ (posedge clk) v0_24 <= reset ? 256'd24 : v0_25;
always @ (posedge clk) v0_25 <= reset ? 256'd25 : v0_26;
always @ (posedge clk) v0_26 <= reset ? 256'd26 : v0_27;
always @ (posedge clk) v0_27 <= reset ? 256'd27 : v0_28;
always @ (posedge clk) v0_28 <= reset ? 256'd28 : v0_29;
always @ (posedge clk) v0_29 <= reset ? 256'd29 : v0_30;
always @ (posedge clk) v0_30 <= reset ? 256'd30 : v0_31;
always @ (posedge clk) v0_31 <= reset ? 256'd31 : v0_24;
always @ (posedge clk) v0_32 <= reset ? 256'd32 : v0_33;
always @ (posedge clk) v0_33 <= reset ? 256'd33 : v0_34;
always @ (posedge clk) v0_34 <= reset ? 256'd34 : v0_35;
always @ (posedge clk) v0_35 <= reset ? 256'd35 : v0_36;
always @ (posedge clk) v0_36 <= reset ? 256'd36 : v0_37;
always @ (posedge clk) v0_37 <= reset ? 256'd37 : v0_38;
always @ (posedge clk) v0_38 <= reset ? 256'd38 : v0_39;
always @ (posedge clk) v0_39 <= reset ? 256'd39 : v0_32;
always @ (posedge clk) v0_40 <= reset ? 256'd40 : v0_41;
always @ (posedge clk) v0_41 <= reset ? 256'd41 : v0_42;
always @ (posedge clk) v0_42 <= reset ? 256'd42 : v0_43;
always @ (posedge clk) v0_43 <= reset ? 256'd43 : v0_44;
always @ (posedge clk) v0_44 <= reset ? 256'd44 : v0_45;
always @ (posedge clk) v0_45 <= reset ? 256'd45 : v0_46;
always @ (posedge clk) v0_46 <= reset ? 256'd46 : v0_47;
always @ (posedge clk) v0_47 <= reset ? 256'd47 : v0_40;
always @ (posedge clk) v0_48 <= reset ? 256'd48 : v0_49;
always @ (posedge clk) v0_49 <= reset ? 256'd49 : v0_50;
always @ (posedge clk) v0_50 <= reset ? 256'd50 : v0_51;
always @ (posedge clk) v0_51 <= reset ? 256'd51 : v0_52;
always @ (posedge clk) v0_52 <= reset ? 256'd52 : v0_53;
always @ (posedge clk) v0_53 <= reset ? 256'd53 : v0_54;
always @ (posedge clk) v0_54 <= reset ? 256'd54 : v0_55;
always @ (posedge clk) v0_55 <= reset ? 256'd55 : v0_48;
always @ (posedge clk) v0_56 <= reset ? 256'd56 : v0_57;
always @ (posedge clk) v0_57 <= reset ? 256'd57 : v0_58;
always @ (posedge clk) v0_58 <= reset ? 256'd58 : v0_59;
always @ (posedge clk) v0_59 <= reset ? 256'd59 : v0_60;
always @ (posedge clk) v0_60 <= reset ? 256'd60 : v0_61;
always @ (posedge clk) v0_61 <= reset ? 256'd61 : v0_62;
always @ (posedge clk) v0_62 <= reset ? 256'd62 : v0_63;
always @ (posedge clk) v0_63 <= reset ? 256'd63 : v0_56;
always @ (posedge clk) v0_0 <= reset ? 256'd1 : v0_1;
always @ (posedge clk) v0_1 <= reset ? 256'd1 : v0_2;
always @ (posedge clk) v0_2 <= reset ? 256'd2 : v0_3;
always @ (posedge clk) v0_3 <= reset ? 256'd3 : v0_4;
always @ (posedge clk) v0_4 <= reset ? 256'd4 : v0_5;
always @ (posedge clk) v0_5 <= reset ? 256'd5 : v0_6;
always @ (posedge clk) v0_6 <= reset ? 256'd6 : v0_7;
always @ (posedge clk) v0_7 <= reset ? 256'd7 : v0_0;
always @ (posedge clk) v0_8 <= reset ? 256'd8 : v0_9;
always @ (posedge clk) v0_9 <= reset ? 256'd9 : v0_10;
always @ (posedge clk) v0_10 <= reset ? 256'd10 : v0_11;
always @ (posedge clk) v0_11 <= reset ? 256'd11 : v0_12;
always @ (posedge clk) v0_12 <= reset ? 256'd12 : v0_13;
always @ (posedge clk) v0_13 <= reset ? 256'd13 : v0_14;
always @ (posedge clk) v0_14 <= reset ? 256'd14 : v0_15;
always @ (posedge clk) v0_15 <= reset ? 256'd15 : v0_8;
always @ (posedge clk) v0_16 <= reset ? 256'd16 : v0_17;
always @ (posedge clk) v0_17 <= reset ? 256'd17 : v0_18;
always @ (posedge clk) v0_18 <= reset ? 256'd18 : v0_19;
always @ (posedge clk) v0_19 <= reset ? 256'd19 : v0_20;
always @ (posedge clk) v0_20 <= reset ? 256'd20 : v0_21;
always @ (posedge clk) v0_21 <= reset ? 256'd21 : v0_22;
always @ (posedge clk) v0_22 <= reset ? 256'd22 : v0_23;
always @ (posedge clk) v0_23 <= reset ? 256'd23 : v0_16;
always @ (posedge clk) v0_24 <= reset ? 256'd24 : v0_25;
always @ (posedge clk) v0_25 <= reset ? 256'd25 : v0_26;
always @ (posedge clk) v0_26 <= reset ? 256'd26 : v0_27;
always @ (posedge clk) v0_27 <= reset ? 256'd27 : v0_28;
always @ (posedge clk) v0_28 <= reset ? 256'd28 : v0_29;
always @ (posedge clk) v0_29 <= reset ? 256'd29 : v0_30;
always @ (posedge clk) v0_30 <= reset ? 256'd30 : v0_31;
always @ (posedge clk) v0_31 <= reset ? 256'd31 : v0_24;
always @ (posedge clk) v0_32 <= reset ? 256'd32 : v0_33;
always @ (posedge clk) v0_33 <= reset ? 256'd33 : v0_34;
always @ (posedge clk) v0_34 <= reset ? 256'd34 : v0_35;
always @ (posedge clk) v0_35 <= reset ? 256'd35 : v0_36;
always @ (posedge clk) v0_36 <= reset ? 256'd36 : v0_37;
always @ (posedge clk) v0_37 <= reset ? 256'd37 : v0_38;
always @ (posedge clk) v0_38 <= reset ? 256'd38 : v0_39;
always @ (posedge clk) v0_39 <= reset ? 256'd39 : v0_32;
always @ (posedge clk) v0_40 <= reset ? 256'd40 : v0_41;
always @ (posedge clk) v0_41 <= reset ? 256'd41 : v0_42;
always @ (posedge clk) v0_42 <= reset ? 256'd42 : v0_43;
always @ (posedge clk) v0_43 <= reset ? 256'd43 : v0_44;
always @ (posedge clk) v0_44 <= reset ? 256'd44 : v0_45;
always @ (posedge clk) v0_45 <= reset ? 256'd45 : v0_46;
always @ (posedge clk) v0_46 <= reset ? 256'd46 : v0_47;
always @ (posedge clk) v0_47 <= reset ? 256'd47 : v0_40;
always @ (posedge clk) v0_48 <= reset ? 256'd48 : v0_49;
always @ (posedge clk) v0_49 <= reset ? 256'd49 : v0_50;
always @ (posedge clk) v0_50 <= reset ? 256'd50 : v0_51;
always @ (posedge clk) v0_51 <= reset ? 256'd51 : v0_52;
always @ (posedge clk) v0_52 <= reset ? 256'd52 : v0_53;
always @ (posedge clk) v0_53 <= reset ? 256'd53 : v0_54;
always @ (posedge clk) v0_54 <= reset ? 256'd54 : v0_55;
always @ (posedge clk) v0_55 <= reset ? 256'd55 : v0_48;
always @ (posedge clk) v0_56 <= reset ? 256'd56 : v0_57;
always @ (posedge clk) v0_57 <= reset ? 256'd57 : v0_58;
always @ (posedge clk) v0_58 <= reset ? 256'd58 : v0_59;
always @ (posedge clk) v0_59 <= reset ? 256'd59 : v0_60;
always @ (posedge clk) v0_60 <= reset ? 256'd60 : v0_61;
always @ (posedge clk) v0_61 <= reset ? 256'd61 : v0_62;
always @ (posedge clk) v0_62 <= reset ? 256'd62 : v0_63;
always @ (posedge clk) v0_63 <= reset ? 256'd63 : v0_56;
always @ (posedge clk) v1_0 <= v0_0 + v0_1 + v0_2 + v0_3 + v0_4 + v0_5 + v0_6 + v0_7;
always @ (posedge clk) v1_1 <= v0_8 + v0_9 + v0_10 + v0_11 + v0_12 + v0_13 + v0_14 + v0_15;
always @ (posedge clk) v1_2 <= v0_16 + v0_17 + v0_18 + v0_19 + v0_20 + v0_21 + v0_22 + v0_23;
always @ (posedge clk) v1_3 <= v0_24 + v0_25 + v0_26 + v0_27 + v0_28 + v0_29 + v0_30 + v0_31;
always @ (posedge clk) v1_4 <= v0_32 + v0_33 + v0_34 + v0_35 + v0_36 + v0_37 + v0_38 + v0_39;
always @ (posedge clk) v1_5 <= v0_40 + v0_41 + v0_42 + v0_43 + v0_44 + v0_45 + v0_46 + v0_47;
always @ (posedge clk) v1_6 <= v0_48 + v0_49 + v0_50 + v0_51 + v0_52 + v0_53 + v0_54 + v0_55;
always @ (posedge clk) v1_7 <= v0_56 + v0_57 + v0_58 + v0_59 + v0_60 + v0_61 + v0_62 + v0_63;
always @ (posedge clk) v1_0 <= v0_0 + v0_1 + v0_2 + v0_3 + v0_4 + v0_5 + v0_6 + v0_7;
always @ (posedge clk) v1_1 <= v0_8 + v0_9 + v0_10 + v0_11 + v0_12 + v0_13 + v0_14 + v0_15;
always @ (posedge clk) v1_2 <= v0_16 + v0_17 + v0_18 + v0_19 + v0_20 + v0_21 + v0_22 + v0_23;
always @ (posedge clk) v1_3 <= v0_24 + v0_25 + v0_26 + v0_27 + v0_28 + v0_29 + v0_30 + v0_31;
always @ (posedge clk) v1_4 <= v0_32 + v0_33 + v0_34 + v0_35 + v0_36 + v0_37 + v0_38 + v0_39;
always @ (posedge clk) v1_5 <= v0_40 + v0_41 + v0_42 + v0_43 + v0_44 + v0_45 + v0_46 + v0_47;
always @ (posedge clk) v1_6 <= v0_48 + v0_49 + v0_50 + v0_51 + v0_52 + v0_53 + v0_54 + v0_55;
always @ (posedge clk) v1_7 <= v0_56 + v0_57 + v0_58 + v0_59 + v0_60 + v0_61 + v0_62 + v0_63;
endmodule

View File

@ -4,6 +4,7 @@
// SPDX-FileCopyrightText: 2003 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define foo bar
`ifdef foo
`ifdef baz `else

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=['--binary'])
test.execute()
test.passes()

View File

@ -0,0 +1,67 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// $bits() of interface member signals used as a child module parameter value.
// $bits reads the type, not the hierarchical value, so it is legal in a
// parameter (IEEE 1800-2023 6.20.2 forbids hierarchical values only).
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
interface axi_if #(
parameter int ID_W = 8,
parameter int ADDR_W = 32
);
logic [ID_W-1:0] AWID;
logic [ADDR_W-1:0] AWADDR;
logic [7:0] AWLEN;
endinterface
module chkmod #(
parameter int PAYLOAD_WIDTH = 1,
parameter int EXPECT = 1
);
initial `checkh(PAYLOAD_WIDTH, EXPECT);
endmodule
module bridge #(
parameter int EXPECT = 1
) (
axi_if axi
);
// $bits of a concat of interface members
chkmod #(
.PAYLOAD_WIDTH($bits({axi.AWID, axi.AWADDR, axi.AWLEN})),
.EXPECT(EXPECT)
) u_concat ();
// $bits of a single interface member
chkmod #(
.PAYLOAD_WIDTH($bits(axi.AWADDR)),
.EXPECT(EXPECT - 12 - 8)
) u_single ();
endmodule
module t;
axi_if #(
.ID_W(12),
.ADDR_W(64)
) if0 (); // 12 + 64 + 8 = 84
axi_if #(
.ID_W(12),
.ADDR_W(16)
) if1 (); // 12 + 16 + 8 = 36
bridge #(.EXPECT(84)) dut0 (.axi(if0));
bridge #(.EXPECT(36)) dut1 (.axi(if1));
initial begin
#1;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,6 +1,6 @@
%Error: t/t_lint_in_inc_bad_2.vh:9:7: syntax error, unexpected if, expecting '('
9 | if if if;
| ^~
%Error: t/t_lint_in_inc_bad_2.vh:9:6: syntax error, unexpected if, expecting '('
9 | if if if;
| ^~
t/t_lint_in_inc_bad_1.vh:8:1: ... note: In file included from 't_lint_in_inc_bad_1.vh'
t/t_lint_in_inc_bad.v:8:1: ... note: In file included from 't_lint_in_inc_bad.v'
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.

View File

@ -5,6 +5,6 @@
// SPDX-License-Identifier: CC0-1.0
module x;
// Syntax error
if if if;
// Syntax error
if if if;
endmodule

View File

@ -1,7 +1,7 @@
%Warning-LATCH: t/t_lint_latch_bad_3.v:19:1: Latch inferred for signal 'o5' (not all control paths of combinational always assign a value)
%Warning-LATCH: t/t_lint_latch_bad_3.v:30:3: Latch inferred for signal 'o5' (not all control paths of combinational always assign a value)
: ... Suggest use of always_latch for intentional latches
19 | always_comb
| ^~~~~~~~~~~
30 | always_comb
| ^~~~~~~~~~~
... For warning description see https://verilator.org/warn/LATCH?v=latest
... Use "/* verilator lint_off LATCH */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -4,72 +4,73 @@
// SPDX-FileCopyrightText: 2020 Julien Margetts
// SPDX-License-Identifier: Unlicense
module t (/*AUTOARG*/ reset, a, b, c, en, o1, o2, o3, o4, o5);
input reset;
input a;
input b;
input c;
input en;
output reg o1; // Always assigned
output reg o2; // "
output reg o3; // "
output reg o4; // "
output reg o5; // Latch
module t ( /*AUTOARG*/
reset,
a,
b,
c,
en,
o1,
o2,
o3,
o4,
o5
);
input reset;
input a;
input b;
input c;
input en;
output reg o1; // Always assigned
output reg o2; // "
output reg o3; // "
output reg o4; // "
output reg o5; // Latch
always_comb
if (reset)
begin
o1 = 1'b0;
o2 = 1'b0;
o3 = 1'b0;
o4 = 1'b0;
o5 = 1'b0;
end
else
begin
o1 = 1'b1;
if (en)
begin
always_comb
if (reset) begin
o1 = 1'b0;
o2 = 1'b0;
if (a)
begin
o3 = a;
o5 = 1'b1;
end
else
begin
o3 = ~a;
o5 = a;
end
// o3 is not assigned in either path of this if/else
// but no latch because always assigned above
if (c)
begin
o2 = a ^ b;
o4 = 1'b1;
end
else
o4 = ~a ^ b;
o2 = 1'b1;
end
else
begin
o2 = 1'b1;
if (b)
begin
o3 = ~a | b;
o5 = ~b;
end
else
begin
o3 = a & ~b;
// No assignment to o5, expect Warning-LATCH
end
o3 = 1'b0;
o4 = 1'b0;
end
end
o5 = 1'b0;
end
else begin
o1 = 1'b1;
if (en) begin
o2 = 1'b0;
if (a) begin
o3 = a;
o5 = 1'b1;
end
else begin
o3 = ~a;
o5 = a;
end
// o3 is not assigned in either path of this if/else
// but no latch because always assigned above
if (c) begin
o2 = a ^ b;
o4 = 1'b1;
end
else o4 = ~a ^ b;
o2 = 1'b1;
end
else begin
o2 = 1'b1;
if (b) begin
o3 = ~a | b;
o5 = ~b;
end
else begin
o3 = a & ~b;
// No assignment to o5, expect Warning-LATCH
end
o4 = 1'b0;
end
end
endmodule

View File

@ -5,6 +5,6 @@
// SPDX-License-Identifier: CC0-1.0
module sub;
// verilator lint_off WIDTHTRUNC
int warn_sub = 64'h1; // Suppressed
// verilator lint_off WIDTHTRUNC
int warn_sub = 64'h1; // Suppressed
endmodule

View File

@ -5,248 +5,248 @@
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/
// Outputs
ign, ign2, ign3, c_wright_32, c_wleft_32, ign4, ign4s,
// Inputs
clk
);
// Outputs
ign, ign2, ign3, c_wright_32, c_wleft_32, ign4, ign4s,
// Inputs
clk
);
input clk;
output [31:0] ign;
output [3:0] ign2;
output [11:0] ign3;
input clk;
output [31:0] ign;
output [3:0] ign2;
output [11:0] ign3;
parameter [95:0] P6 = 6;
localparam P64 = (1 << P6);
parameter [95:0] P6 = 6;
localparam P64 = (1 << P6);
// verilator lint_off WIDTH
localparam [4:0] PBIG23 = 1'b1 << ~73'b0;
localparam [3:0] PBIG29 = 4'b1 << 33'h100000000;
// verilator lint_on WIDTH
// verilator lint_off WIDTH
localparam [4:0] PBIG23 = 1'b1 << ~73'b0;
localparam [3:0] PBIG29 = 4'b1 << 33'h100000000;
// verilator lint_on WIDTH
reg [31:0] iright;
reg signed [31:0] irights;
reg [31:0] ileft;
reg [P64-1:0] qright;
reg signed [P64-1:0] qrights;
reg [P64-1:0] qleft;
reg [95:0] wright;
reg signed [95:0] wrights;
reg [95:0] wleft;
reg [31:0] iright;
reg signed [31:0] irights;
reg [31:0] ileft;
reg [P64-1:0] qright;
reg signed [P64-1:0] qrights;
reg [P64-1:0] qleft;
reg [95:0] wright;
reg signed [95:0] wrights;
reg [95:0] wleft;
reg [31:0] q_iright;
reg signed [31:0] q_irights;
reg [31:0] q_ileft;
reg [P64-1:0] q_qright;
reg signed [P64-1:0] q_qrights;
reg [P64-1:0] q_qleft;
reg [95:0] q_wright;
reg signed [95:0] q_wrights;
reg [95:0] q_wleft;
reg [31:0] q_iright;
reg signed [31:0] q_irights;
reg [31:0] q_ileft;
reg [P64-1:0] q_qright;
reg signed [P64-1:0] q_qrights;
reg [P64-1:0] q_qleft;
reg [95:0] q_wright;
reg signed [95:0] q_wrights;
reg [95:0] q_wleft;
reg [31:0] w_iright;
reg signed [31:0] w_irights;
reg [31:0] w_ileft;
reg [P64-1:0] w_qright;
reg signed [P64-1:0] w_qrights;
reg [P64-1:0] w_qleft;
reg [95:0] w_wright;
reg signed [95:0] w_wrights;
reg [95:0] w_wleft;
reg [31:0] w_iright;
reg signed [31:0] w_irights;
reg [31:0] w_ileft;
reg [P64-1:0] w_qright;
reg signed [P64-1:0] w_qrights;
reg [P64-1:0] w_qleft;
reg [95:0] w_wright;
reg signed [95:0] w_wrights;
reg [95:0] w_wleft;
reg [31:0] iamt;
reg [63:0] qamt;
reg [95:0] wamt;
reg [31:0] iamt;
reg [63:0] qamt;
reg [95:0] wamt;
output reg [95:0] c_wright_32;
output reg [95:0] c_wleft_32;
output reg [95:0] c_wright_32;
output reg [95:0] c_wleft_32;
reg [63:0] crc = 64'h5aef0c8d_d70a4497;
wire [95:0] rand_96 = {crc[63:32] | crc[31:0], crc};
reg [63:0] crc = 64'h5aef0c8d_d70a4497;
wire [95:0] rand_96 = {crc[63:32] | crc[31:0], crc};
assign ign = {31'h0, clk} >>> 4'bx; // bug760
assign ign2 = {iamt[1:0] >> {22{iamt[5:2]}}, iamt[1:0] << (0 <<< iamt[5:2])}; // bug1174
assign ign3 = {iamt[1:0] >> {22{iamt[5:2]}},
iamt[1:0] >> {11{iamt[5:2]}},
$signed(iamt[1:0]) >>> {22{iamt[5:2]}},
$signed(iamt[1:0]) >>> {11{iamt[5:2]}},
iamt[1:0] << {22{iamt[5:2]}},
iamt[1:0] << {11{iamt[5:2]}}};
assign ign = {31'h0, clk} >>> 4'bx; // bug760
assign ign2 = {iamt[1:0] >> {22{iamt[5:2]}}, iamt[1:0] << (0 <<< iamt[5:2])}; // bug1174
assign ign3 = {iamt[1:0] >> {22{iamt[5:2]}},
iamt[1:0] >> {11{iamt[5:2]}},
$signed(iamt[1:0]) >>> {22{iamt[5:2]}},
$signed(iamt[1:0]) >>> {11{iamt[5:2]}},
iamt[1:0] << {22{iamt[5:2]}},
iamt[1:0] << {11{iamt[5:2]}}};
wire [95:0] wamtt = {iamt,iamt,iamt};
output wire [95:0] ign4;
assign ign4 = wamtt >> {11{iamt[5:2]}};
output wire signed [95:0] ign4s;
assign ign4s = $signed(wamtt) >>> {11{iamt[5:2]}};
wire [95:0] wamtt = {iamt,iamt,iamt};
output wire [95:0] ign4;
assign ign4 = wamtt >> {11{iamt[5:2]}};
output wire signed [95:0] ign4s;
assign ign4s = $signed(wamtt) >>> {11{iamt[5:2]}};
always @* begin
iright = 32'h819b018a >> iamt;
irights = 32'sh819b018a >>> signed'(iamt);
ileft = 32'h819b018a << iamt;
qright = 64'hf784bf8f_12734089 >> iamt;
qrights = 64'shf784bf8f_12734089 >>> signed'(iamt);
qleft = 64'hf784bf8f_12734089 << iamt;
wright = 96'hf784bf8f_12734089_190abe48 >> iamt;
wrights = 96'shf784bf8f_12734089_190abe48 >>> signed'(iamt);
wleft = 96'hf784bf8f_12734089_190abe48 << iamt;
always @* begin
iright = 32'h819b018a >> iamt;
irights = 32'sh819b018a >>> signed'(iamt);
ileft = 32'h819b018a << iamt;
qright = 64'hf784bf8f_12734089 >> iamt;
qrights = 64'shf784bf8f_12734089 >>> signed'(iamt);
qleft = 64'hf784bf8f_12734089 << iamt;
wright = 96'hf784bf8f_12734089_190abe48 >> iamt;
wrights = 96'shf784bf8f_12734089_190abe48 >>> signed'(iamt);
wleft = 96'hf784bf8f_12734089_190abe48 << iamt;
q_iright = 32'h819b018a >> qamt;
q_irights = 32'sh819b018a >>> signed'(qamt);
q_ileft = 32'h819b018a << qamt;
q_qright = 64'hf784bf8f_12734089 >> qamt;
q_qrights = 64'shf784bf8f_12734089 >>> signed'(qamt);
q_qleft = 64'hf784bf8f_12734089 << qamt;
q_wright = 96'hf784bf8f_12734089_190abe48 >> qamt;
q_wrights = 96'shf784bf8f_12734089_190abe48 >>> signed'(qamt);
q_wleft = 96'hf784bf8f_12734089_190abe48 << qamt;
q_iright = 32'h819b018a >> qamt;
q_irights = 32'sh819b018a >>> signed'(qamt);
q_ileft = 32'h819b018a << qamt;
q_qright = 64'hf784bf8f_12734089 >> qamt;
q_qrights = 64'shf784bf8f_12734089 >>> signed'(qamt);
q_qleft = 64'hf784bf8f_12734089 << qamt;
q_wright = 96'hf784bf8f_12734089_190abe48 >> qamt;
q_wrights = 96'shf784bf8f_12734089_190abe48 >>> signed'(qamt);
q_wleft = 96'hf784bf8f_12734089_190abe48 << qamt;
w_iright = 32'h819b018a >> wamt;
w_irights = 32'sh819b018a >>> signed'(wamt);
w_ileft = 32'h819b018a << wamt;
w_qright = 64'hf784bf8f_12734089 >> wamt;
w_qrights = 64'shf784bf8f_12734089 >>> signed'(wamt);
w_qleft = 64'hf784bf8f_12734089 << wamt;
w_wright = 96'hf784bf8f_12734089_190abe48 >> wamt;
w_wrights = 96'shf784bf8f_12734089_190abe48 >>> signed'(wamt);
w_wleft = 96'hf784bf8f_12734089_190abe48 << wamt;
w_iright = 32'h819b018a >> wamt;
w_irights = 32'sh819b018a >>> signed'(wamt);
w_ileft = 32'h819b018a << wamt;
w_qright = 64'hf784bf8f_12734089 >> wamt;
w_qrights = 64'shf784bf8f_12734089 >>> signed'(wamt);
w_qleft = 64'hf784bf8f_12734089 << wamt;
w_wright = 96'hf784bf8f_12734089_190abe48 >> wamt;
w_wrights = 96'shf784bf8f_12734089_190abe48 >>> signed'(wamt);
w_wleft = 96'hf784bf8f_12734089_190abe48 << wamt;
c_wright_32 = rand_96 >> 32;
c_wleft_32 = rand_96 << 32;
end
c_wright_32 = rand_96 >> 32;
c_wleft_32 = rand_96 << 32;
end
integer cyc; initial cyc=1;
always @ (posedge clk) begin
if (cyc!=0) begin
cyc <= cyc + 1;
crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]};
integer cyc; initial cyc=1;
always @ (posedge clk) begin
if (cyc!=0) begin
cyc <= cyc + 1;
crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]};
`ifdef TEST_VERBOSE
$write("%d %x %x %x %x %x %x\n", cyc, ileft, iright, qleft, qright, wleft, wright);
$write("%d %x %x %x %x %x %x\n", cyc, ileft, iright, qleft, qright, wleft, wright);
`endif
if (cyc==1) begin
iamt <= 0;
qamt <= 0;
wamt <= 0;
if (P64 != 64) $stop;
if (5'b10110>>2 != 5'b00101) $stop;
if (5'b10110>>>2 != 5'b00101) $stop; // Note it cares about sign-ness
if (5'b10110<<2 != 5'b11000) $stop;
if (5'b10110<<<2 != 5'b11000) $stop;
if (5'sb10110>>2 != 5'sb00101) $stop;
if (5'sb10110>>>2 != 5'sb11101) $stop;
if (5'sb10110<<2 != 5'sb11000) $stop;
if (5'sb10110<<<2 != 5'sb11000) $stop;
// Allow >64 bit shifts if the shift amount is a constant
if ((64'sh458c2de282e30f8b >> 68'sh4) !== 64'sh0458c2de282e30f8) $stop;
end
if (cyc==2) begin
iamt <= 28;
qamt <= 28;
wamt <= 28;
if (ileft != 32'h819b018a) $stop;
if (iright != 32'h819b018a) $stop;
if (irights != 32'h819b018a) $stop;
if (qleft != 64'hf784bf8f_12734089) $stop;
if (qright != 64'hf784bf8f_12734089) $stop;
if (qrights != 64'hf784bf8f_12734089) $stop;
if (wleft != 96'hf784bf8f12734089190abe48) $stop;
if (wright != 96'hf784bf8f12734089190abe48) $stop;
if (wrights != 96'hf784bf8f12734089190abe48) $stop;
end
if (cyc==3) begin
iamt <= 31;
qamt <= 31;
wamt <= 31;
if (ileft != 32'ha0000000) $stop;
if (iright != 32'h8) $stop;
if (irights != 32'hfffffff8) $stop;
if (qleft != 64'hf127340890000000) $stop;
if (qright != 64'h0000000f784bf8f1) $stop;
if (qrights != 64'hffffffff784bf8f1) $stop;
if (wleft != 96'hf12734089190abe480000000) $stop;
if (wright != 96'h0000000f784bf8f127340891) $stop;
if (wrights != 96'hffffffff784bf8f127340891) $stop;
end
if (cyc==4) begin
iamt <= 32;
qamt <= 32;
wamt <= 32;
if (ileft != 32'h0) $stop;
if (iright != 32'h1) $stop;
if (qleft != 64'h8939a04480000000) $stop;
if (qright != 64'h00000001ef097f1e) $stop;
end
if (cyc==5) begin
iamt <= 33;
qamt <= 33;
wamt <= 33;
if (ileft != 32'h0) $stop;
if (iright != 32'h0) $stop;
if (qleft != 64'h1273408900000000) $stop;
if (qright != 64'h00000000f784bf8f) $stop;
end
if (cyc==6) begin
iamt <= 64;
qamt <= 64;
wamt <= 64;
if (ileft != 32'h0) $stop;
if (iright != 32'h0) $stop;
if (qleft != 64'h24e6811200000000) $stop;
if (qright != 64'h000000007bc25fc7) $stop;
end
if (cyc==7) begin
iamt <= 128;
qamt <= 128;
wamt <= 128;
if (ileft != 32'h0) $stop;
if (iright != 32'h0) $stop;
if (qleft != 64'h0) $stop;
if (qright != 64'h0) $stop;
end
if (cyc==8) begin
iamt <= 100;
qamt <= {32'h10, 32'h0};
wamt <= {32'h10, 64'h0};
if (ileft != '0) $stop;
if (iright != '0) $stop;
if (irights != '1) $stop;
if (qleft != '0) $stop;
if (qright != '0) $stop;
if (qrights != '1) $stop;
if (wleft != '0) $stop;
if (wright != '0) $stop;
if (wrights != '1) $stop;
end
if (cyc==19) begin
$write("*-* All Finished *-*\n");
$finish;
end
// General rule to test all q's
if (cyc != 0) begin
if (ileft != q_ileft) $stop;
if (iright != q_iright) $stop;
if (irights != q_irights) $stop;
if (qleft != q_qleft) $stop;
if (qright != q_qright) $stop;
if (qrights != q_qrights) $stop;
if (wleft != q_wleft) $stop;
if (wright != q_wright) $stop;
if (wrights != q_wrights) $stop;
if (ileft != w_ileft) $stop;
if (iright != w_iright) $stop;
if (irights != w_irights) $stop;
if (qleft != w_qleft) $stop;
if (qright != w_qright) $stop;
if (qrights != w_qrights) $stop;
if (wleft != w_wleft) $stop;
if (wright != w_wright) $stop;
if (wrights != w_wrights) $stop;
if (c_wright_32 << 32 != {rand_96[95:32], 32'd0}) $stop;
if (c_wleft_32 >> 32 != {32'd0, rand_96[63:0]}) $stop;
end
if (cyc==1) begin
iamt <= 0;
qamt <= 0;
wamt <= 0;
if (P64 != 64) $stop;
if (5'b10110>>2 != 5'b00101) $stop;
if (5'b10110>>>2 != 5'b00101) $stop; // Note it cares about sign-ness
if (5'b10110<<2 != 5'b11000) $stop;
if (5'b10110<<<2 != 5'b11000) $stop;
if (5'sb10110>>2 != 5'sb00101) $stop;
if (5'sb10110>>>2 != 5'sb11101) $stop;
if (5'sb10110<<2 != 5'sb11000) $stop;
if (5'sb10110<<<2 != 5'sb11000) $stop;
// Allow >64 bit shifts if the shift amount is a constant
if ((64'sh458c2de282e30f8b >> 68'sh4) !== 64'sh0458c2de282e30f8) $stop;
end
end
if (cyc==2) begin
iamt <= 28;
qamt <= 28;
wamt <= 28;
if (ileft != 32'h819b018a) $stop;
if (iright != 32'h819b018a) $stop;
if (irights != 32'h819b018a) $stop;
if (qleft != 64'hf784bf8f_12734089) $stop;
if (qright != 64'hf784bf8f_12734089) $stop;
if (qrights != 64'hf784bf8f_12734089) $stop;
if (wleft != 96'hf784bf8f12734089190abe48) $stop;
if (wright != 96'hf784bf8f12734089190abe48) $stop;
if (wrights != 96'hf784bf8f12734089190abe48) $stop;
end
if (cyc==3) begin
iamt <= 31;
qamt <= 31;
wamt <= 31;
if (ileft != 32'ha0000000) $stop;
if (iright != 32'h8) $stop;
if (irights != 32'hfffffff8) $stop;
if (qleft != 64'hf127340890000000) $stop;
if (qright != 64'h0000000f784bf8f1) $stop;
if (qrights != 64'hffffffff784bf8f1) $stop;
if (wleft != 96'hf12734089190abe480000000) $stop;
if (wright != 96'h0000000f784bf8f127340891) $stop;
if (wrights != 96'hffffffff784bf8f127340891) $stop;
end
if (cyc==4) begin
iamt <= 32;
qamt <= 32;
wamt <= 32;
if (ileft != 32'h0) $stop;
if (iright != 32'h1) $stop;
if (qleft != 64'h8939a04480000000) $stop;
if (qright != 64'h00000001ef097f1e) $stop;
end
if (cyc==5) begin
iamt <= 33;
qamt <= 33;
wamt <= 33;
if (ileft != 32'h0) $stop;
if (iright != 32'h0) $stop;
if (qleft != 64'h1273408900000000) $stop;
if (qright != 64'h00000000f784bf8f) $stop;
end
if (cyc==6) begin
iamt <= 64;
qamt <= 64;
wamt <= 64;
if (ileft != 32'h0) $stop;
if (iright != 32'h0) $stop;
if (qleft != 64'h24e6811200000000) $stop;
if (qright != 64'h000000007bc25fc7) $stop;
end
if (cyc==7) begin
iamt <= 128;
qamt <= 128;
wamt <= 128;
if (ileft != 32'h0) $stop;
if (iright != 32'h0) $stop;
if (qleft != 64'h0) $stop;
if (qright != 64'h0) $stop;
end
if (cyc==8) begin
iamt <= 100;
qamt <= {32'h10, 32'h0};
wamt <= {32'h10, 64'h0};
if (ileft != '0) $stop;
if (iright != '0) $stop;
if (irights != '1) $stop;
if (qleft != '0) $stop;
if (qright != '0) $stop;
if (qrights != '1) $stop;
if (wleft != '0) $stop;
if (wright != '0) $stop;
if (wrights != '1) $stop;
end
if (cyc==19) begin
$write("*-* All Finished *-*\n");
$finish;
end
// General rule to test all q's
if (cyc != 0) begin
if (ileft != q_ileft) $stop;
if (iright != q_iright) $stop;
if (irights != q_irights) $stop;
if (qleft != q_qleft) $stop;
if (qright != q_qright) $stop;
if (qrights != q_qrights) $stop;
if (wleft != q_wleft) $stop;
if (wright != q_wright) $stop;
if (wrights != q_wrights) $stop;
if (ileft != w_ileft) $stop;
if (iright != w_iright) $stop;
if (irights != w_irights) $stop;
if (qleft != w_qleft) $stop;
if (qright != w_qright) $stop;
if (qrights != w_qrights) $stop;
if (wleft != w_wleft) $stop;
if (wright != w_wright) $stop;
if (wrights != w_wrights) $stop;
if (c_wright_32 << 32 != {rand_96[95:32], 32'd0}) $stop;
if (c_wleft_32 >> 32 != {32'd0, rand_96[63:0]}) $stop;
end
end
end
endmodule

View File

@ -8,27 +8,24 @@
// SPDX-FileCopyrightText: 2021 Dan Petrisko
// SPDX-License-Identifier: CC0-1.0
module top(/*AUTOARG*/
// Inputs
clk
);
input clk;
module top (
input clk
);
always_ff @(posedge clk) begin
$write("*-* All Finished *-*\n");
$finish();
end
always_ff @(posedge clk) begin
$write("*-* All Finished *-*\n");
$finish();
end
endmodule
module faketop(/*AUTOARG*/
);
module faketop;
top top();
top top ();
// Stop immediately if this module is instantiated
// Stop immediately if this module is instantiated
initial begin
$stop();
end
end
endmodule

View File

@ -51,4 +51,12 @@
: ... note: In instance 't'
38 | assert property (@(e) s_eventually 1'h1);
| ^~~~~~~~~~~~
%Error-NOTIMING: t/t_notiming.v:39:31: s_until requires --timing
: ... note: In instance 't'
39 | assert property (@(e) 1'h1 s_until 1'h1);
| ^~~~~~~
%Error-NOTIMING: t/t_notiming.v:40:31: s_until_with requires --timing
: ... note: In instance 't'
40 | assert property (@(e) 1'h1 s_until_with 1'h1);
| ^~~~~~~~~~~~
%Error: Exiting due to

View File

@ -36,6 +36,8 @@ module t;
s.get();
end
assert property (@(e) s_eventually 1'h1);
assert property (@(e) 1'h1 s_until 1'h1);
assert property (@(e) 1'h1 s_until_with 1'h1);
endmodule
`ifdef VERILATOR_TIMING

View File

@ -6,6 +6,6 @@
module xx;
xx // intentional error
xx // intentional error
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(timing_loop=True, verilator_flags2=['--assert', '--timing'])
test.execute()
test.passes()

View File

@ -0,0 +1,90 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t (
input clk
);
int cyc = 0;
logic [1:0] sel;
logic a;
logic b;
int linear_f = 0;
int default_f = 0;
int vacuous_f = 0;
int delay_f = 0;
always_comb begin
sel = cyc[1:0];
a = 1'b1;
b = 1'b1;
end
property p_delay(logic [1:0] delay);
case (delay)
2'd0 : a && b;
2'd1 : a ##2 b;
2'd2 : a ##4 b;
2'd3 : a ##8 b;
default: 0;
endcase
endproperty
property p_linear_search;
case (sel)
2'd0 : 1'b0;
2'd0, 2'd1 : 1'b1;
default 1'b1;
endcase
endproperty
property p_default_ignored_during_search;
case (sel)
default: 1'b0;
2'd2 : 1'b1;
endcase
endproperty
property p_no_default_vacuous_success;
case (sel)
2'd0 : 1'b0;
endcase
endproperty
property p_only_default;
case (sel)
default: 1'b1;
endcase
endproperty
assert property (@(posedge clk) p_delay(sel)) else delay_f++;
assert property (@(posedge clk) p_linear_search) else linear_f++;
assert property (@(posedge clk) p_default_ignored_during_search) else default_f++;
assert property (@(posedge clk) p_no_default_vacuous_success) else vacuous_f++;
assert property (@(posedge clk) p_only_default) else `stop;
always @(posedge clk) begin
cyc <= cyc + 1;
end
always @(negedge clk) begin
if (cyc == 16) begin
`checkd(delay_f, 0);
`checkd(linear_f, 4);
`checkd(default_f, 12);
`checkd(vacuous_f, 4);
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,8 @@
%Error: t/t_property_case_bad.v:13:7: Multiple default statements in property case statement
13 | default: 1'b0;
| ^~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_property_case_bad.v:18:5: Property case statement with no items
18 | case (sel)
| ^~~~
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,21 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t;
logic [1:0] sel;
property p1;
case (sel)
default: 1'b1;
default: 1'b0;
endcase
endproperty
property p2;
case (sel)
endcase
endproperty
endmodule

View File

@ -1,50 +1,44 @@
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:26:13: Unsupported: strong (in property expression)
26 | strong(a);
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:30:11: Unsupported: weak (in property expression)
30 | weak(a);
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:24:11: Unsupported: strong (in property expression)
24 | strong(a);
| ^
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:58:7: Unsupported: nexttime (in property expression)
58 | nexttime a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:62:7: Unsupported: nexttime[] (in property expression)
62 | nexttime [2] a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:66:7: Unsupported: s_nexttime (in property expression)
66 | s_nexttime a;
| ^~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:70:7: Unsupported: s_nexttime[] (in property expression)
70 | s_nexttime [2] a;
| ^~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:74:7: Unsupported: nexttime (in property expression)
74 | nexttime always a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:78:7: Unsupported: nexttime[] (in property expression)
78 | nexttime [2] always a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:82:7: Unsupported: nexttime[] (in property expression)
82 | nexttime [2] always a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:86:7: Unsupported: nexttime (in property expression)
86 | nexttime s_eventually a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:90:16: Unsupported: s_eventually[] (in property expression)
90 | nexttime s_eventually [2:$] always a;
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:90:7: Unsupported: nexttime (in property expression)
90 | nexttime s_eventually [2:$] always a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:109:27: Unsupported: property argument data type
109 | property p_arg_propery(property inprop);
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:112:27: Unsupported: sequence argument data type
112 | property p_arg_seqence(sequence inseq);
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:117:7: Unsupported: property case expression
117 | case (a) endcase
| ^~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:120:7: Unsupported: property case expression
120 | case (a) default: b; endcase
| ^~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:28:9: Unsupported: weak (in property expression)
28 | weak(a);
| ^
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:56:5: Unsupported: nexttime (in property expression)
56 | nexttime a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:60:5: Unsupported: nexttime[] (in property expression)
60 | nexttime [2] a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:64:5: Unsupported: s_nexttime (in property expression)
64 | s_nexttime a;
| ^~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:68:5: Unsupported: s_nexttime[] (in property expression)
68 | s_nexttime [2] a;
| ^~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:72:5: Unsupported: nexttime (in property expression)
72 | nexttime always a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:76:5: Unsupported: nexttime[] (in property expression)
76 | nexttime [2] always a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:80:5: Unsupported: nexttime[] (in property expression)
80 | nexttime [2] always a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:84:5: Unsupported: nexttime (in property expression)
84 | nexttime s_eventually a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:88:14: Unsupported: s_eventually[] (in property expression)
88 | nexttime s_eventually [2:$] always a;
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:88:5: Unsupported: nexttime (in property expression)
88 | nexttime s_eventually [2:$] always a;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:107:26: Unsupported: property argument data type
107 | property p_arg_propery(property inprop);
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:110:26: Unsupported: sequence argument data type
110 | property p_arg_seqence(sequence inseq);
| ^~~~~~~~
%Error: Exiting due to

View File

@ -1,26 +1,26 @@
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:130:22: Unsupported: Unclocked assertion
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:115:21: Unsupported: Unclocked assertion
: ... note: In instance 't'
130 | assert property ((s_eventually a) implies (s_eventually a));
| ^~~~~~~~~~~~
115 | assert property ((s_eventually a) implies (s_eventually a));
| ^~~~~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:130:47: Unsupported: Unclocked assertion
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:115:46: Unsupported: Unclocked assertion
: ... note: In instance 't'
130 | assert property ((s_eventually a) implies (s_eventually a));
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:130:4: Unsupported: Unclocked assertion
115 | assert property ((s_eventually a) implies (s_eventually a));
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:115:3: Unsupported: Unclocked assertion
: ... note: In instance 't'
130 | assert property ((s_eventually a) implies (s_eventually a));
| ^~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:132:22: Unsupported: Unclocked assertion
115 | assert property ((s_eventually a) implies (s_eventually a));
| ^~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:117:21: Unsupported: Unclocked assertion
: ... note: In instance 't'
132 | assert property ((s_eventually a) iff (s_eventually a));
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:132:43: Unsupported: Unclocked assertion
117 | assert property ((s_eventually a) iff (s_eventually a));
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:117:42: Unsupported: Unclocked assertion
: ... note: In instance 't'
132 | assert property ((s_eventually a) iff (s_eventually a));
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:132:4: Unsupported: Unclocked assertion
117 | assert property ((s_eventually a) iff (s_eventually a));
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:117:3: Unsupported: Unclocked assertion
: ... note: In instance 't'
132 | assert property ((s_eventually a) iff (s_eventually a));
| ^~~~~~
117 | assert property ((s_eventually a) iff (s_eventually a));
| ^~~~~~
%Error: Exiting due to

View File

@ -4,137 +4,122 @@
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/
// Inputs
clk
);
module t (
input clk
);
input clk;
bit a;
bit b;
bit c;
int cyc = 0;
bit a;
bit b;
bit c;
int cyc = 0;
always @(posedge clk) begin
cyc <= cyc + 1;
end
always @(posedge clk) begin
cyc <= cyc + 1;
end
`ifdef PARSING_TIME
// NOTE this grammar hasn't been checked with other simulators,
// is here just to avoid uncovered code lines in the grammar.
property p_strong;
strong(a);
endproperty
// NOTE this grammar hasn't been checked with other simulators,
// is here just to avoid uncovered code lines in the grammar.
property p_strong;
strong(a);
endproperty
property p_weak;
weak(a);
endproperty
property p_weak;
weak(a);
endproperty
property p_until;
a until b;
endproperty
property p_until;
a until b;
endproperty
property p_suntil;
a s_until b;
endproperty
property p_suntil;
a s_until b;
endproperty
property p_untilwith;
a until_with b;
endproperty
property p_untilwith;
a until_with b;
endproperty
property p_suntilwith;
a s_until_with b;
endproperty
property p_suntilwith;
a s_until_with b;
endproperty
property p_poundminuspound1;
a #-# b;
endproperty
property p_poundminuspound1;
a #-# b;
endproperty
property p_poundeqpound;
a #=# b;
endproperty
property p_poundeqpound;
a #=# b;
endproperty
property p_nexttime;
nexttime a;
endproperty
property p_nexttime;
nexttime a;
endproperty
property p_nexttime2;
nexttime [2] a;
endproperty
property p_nexttime2;
nexttime [2] a;
endproperty
property p_snexttime;
s_nexttime a;
endproperty
property p_snexttime;
s_nexttime a;
endproperty
property p_snexttime2;
s_nexttime [2] a;
endproperty
property p_snexttime2;
s_nexttime [2] a;
endproperty
property p_nexttime_always;
nexttime always a;
endproperty
property p_nexttime_always;
nexttime always a;
endproperty
property p_nexttime_always2;
nexttime [2] always a;
endproperty
property p_nexttime_always2;
nexttime [2] always a;
endproperty
property p_nexttime_eventually2;
nexttime [2] always a;
endproperty
property p_nexttime_eventually2;
nexttime [2] always a;
endproperty
property p_nexttime_seventually;
nexttime s_eventually a;
endproperty
property p_nexttime_seventually;
nexttime s_eventually a;
endproperty
property p_nexttime_seventually2;
nexttime s_eventually [2:$] always a;
endproperty
property p_nexttime_seventually2;
nexttime s_eventually [2:$] always a;
endproperty
property p_accepton;
accept_on (a) b;
endproperty
property p_accepton;
accept_on (a) b;
endproperty
property p_syncaccepton;
sync_accept_on (a) b;
endproperty
property p_syncaccepton;
sync_accept_on (a) b;
endproperty
property p_rejecton;
reject_on (a) b;
endproperty
property p_rejecton;
reject_on (a) b;
endproperty
property p_syncrejecton;
sync_reject_on (a) b;
endproperty
property p_syncrejecton;
sync_reject_on (a) b;
endproperty
property p_arg_propery(property inprop);
inprop;
endproperty
property p_arg_seqence(sequence inseq);
inseq;
endproperty
property p_case_1;
case (a) endcase
endproperty
property p_case_2;
case (a) default: b; endcase
endproperty
property p_if;
if (a) b
endproperty
property p_ifelse;
if (a) b else c
endproperty
property p_arg_propery(property inprop);
inprop;
endproperty
property p_arg_seqence(sequence inseq);
inseq;
endproperty
`endif
assert property ((s_eventually a) implies (s_eventually a));
assert property ((s_eventually a) implies (s_eventually a));
assert property ((s_eventually a) iff (s_eventually a));
assert property ((s_eventually a) iff (s_eventually a));
always @(posedge clk) begin
if (cyc == 10) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
always @(posedge clk) begin
if (cyc == 10) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(timing_loop=True, verilator_flags2=['--timing'])
test.execute()
test.passes()

View File

@ -0,0 +1,35 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// s_eventually (strong eventually) inside an interface used to trigger an
// internal error in V3Scope ("Can't locate varref scope").
interface my_if (
input logic clk
);
logic a;
assert property (@(posedge clk) s_eventually a);
endinterface
module t ( /*AUTOARG*/);
bit clk = 0;
initial forever #1 clk = ~clk;
integer cyc = 0;
my_if u_if (.clk(clk));
initial u_if.a = 0;
always @(posedge clk) begin
cyc <= cyc + 1;
u_if.a <= (cyc >= 3);
if (cyc == 10) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -1,16 +1,16 @@
%Warning-WIDTHTRUNC: t/t_property_sexpr_unsup.v:72:14: Logical operator IMPLICATION expects 1 bit on the RHS, but RHS's CMETHODHARD 'at' generates 8 bits.
%Warning-WIDTHTRUNC: t/t_property_sexpr_unsup.v:80:14: Logical operator IMPLICATION expects 1 bit on the RHS, but RHS's CMETHODHARD 'at' generates 8 bits.
: ... note: In instance 't.ieee'
72 | $rose(a) |-> q[0];
80 | $rose(a) |-> q[0];
| ^~~
... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest
... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message.
%Warning-WIDTHTRUNC: t/t_property_sexpr_unsup.v:77:29: Logical operator SEXPR expects 1 bit on the exprp, but exprp's CMETHODHARD 'at' generates 8 bits.
%Warning-WIDTHTRUNC: t/t_property_sexpr_unsup.v:85:29: Logical operator SEXPR expects 1 bit on the exprp, but exprp's CMETHODHARD 'at' generates 8 bits.
: ... note: In instance 't.ieee'
77 | ($rose(a), l_b = b) |-> ##[3:10] q[l_b];
85 | ($rose(a), l_b = b) |-> ##[3:10] q[l_b];
| ^~
%Warning-WIDTHTRUNC: t/t_property_sexpr_unsup.v:98:31: Logical operator SEXPR expects 1 bit on the exprp, but exprp's VARREF 'b' generates 32 bits.
: ... note: In instance 't.ieee'
98 | assert property (@clk not a ##1 b);
%Warning-WIDTHTRUNC: t/t_property_sexpr_unsup.v:106:31: Logical operator SEXPR expects 1 bit on the exprp, but exprp's VARREF 'b' generates 32 bits.
: ... note: In instance 't.ieee'
106 | assert property (@clk not a ##1 b);
| ^~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:47:39: Unsupported: 'until' in complex property expression
: ... note: In instance 't'
@ -21,16 +21,32 @@
: ... note: In instance 't'
49 | assert property (@(posedge clk) val until (val ##1 val)) $display("[%0t] sequence in until, fileline:%d", $time, 49);
| ^~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:51:39: Unsupported: s_until (in property expresion)
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:51:44: Unsupported: s_until (in property expression)
: ... note: In instance 't'
51 | assert property (@(posedge clk) val s_until !val) $display("[%0t] s_until, fileline:%d", $time, 51);
51 | assert property (@(posedge clk) ##1 (val s_until val)) $display("[%0t] s_until in sequence, fileline:%d", $time, 51);
| ^~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:53:39: Unsupported: s_until (in property expression)
: ... note: In instance 't'
53 | assert property (@(posedge clk) val s_until val s_until val) $display("[%0t] nested s_until, fileline:%d", $time, 53);
| ^~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:53:39: Unsupported: s_until_with (in property expresion)
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:55:39: Unsupported: s_until (in property expression)
: ... note: In instance 't'
53 | assert property (@(posedge clk) val s_until_with val) $display("[%0t] s_until_with, fileline:%d", $time, 53);
55 | assert property (@(posedge clk) val s_until (val ##1 val)) $display("[%0t] sequence in until_with, fileline:%d", $time, 55);
| ^~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:57:44: Unsupported: s_until_with (in property expression)
: ... note: In instance 't'
57 | assert property (@(posedge clk) ##1 (val s_until_with val)) $display("[%0t] s_until_with in sequence, fileline:%d", $time, 57);
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:59:39: Unsupported: s_until_with (in property expression)
: ... note: In instance 't'
59 | assert property (@(posedge clk) val s_until_with val s_until_with val) $display("[%0t] nested s_until_with, fileline:%d", $time, 59);
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:55:40: Unsupported: 'until' in complex property expression
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:61:39: Unsupported: s_until_with (in property expression)
: ... note: In instance 't'
55 | assert property (@(posedge clk) (val until val) or val) $display("[%0t] until inside or, fileline:%d", $time, 55);
61 | assert property (@(posedge clk) val s_until_with (val ##1 val)) $display("[%0t] sequence in until_with, fileline:%d", $time, 61);
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:63:40: Unsupported: 'until' in complex property expression
: ... note: In instance 't'
63 | assert property (@(posedge clk) (val until val) or val) $display("[%0t] until inside or, fileline:%d", $time, 63);
| ^~~~~
%Error: Exiting due to

View File

@ -7,16 +7,32 @@
: ... note: In instance 't'
49 | assert property (@(posedge clk) val until (val ##1 val)) $display("[%0t] sequence in until, fileline:%d", $time, 49);
| ^~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:51:39: Unsupported: s_until (in property expresion)
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:51:44: Unsupported: s_until (in property expression)
: ... note: In instance 't'
51 | assert property (@(posedge clk) val s_until !val) $display("[%0t] s_until, fileline:%d", $time, 51);
51 | assert property (@(posedge clk) ##1 (val s_until val)) $display("[%0t] s_until in sequence, fileline:%d", $time, 51);
| ^~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:53:39: Unsupported: s_until (in property expression)
: ... note: In instance 't'
53 | assert property (@(posedge clk) val s_until val s_until val) $display("[%0t] nested s_until, fileline:%d", $time, 53);
| ^~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:53:39: Unsupported: s_until_with (in property expresion)
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:55:39: Unsupported: s_until (in property expression)
: ... note: In instance 't'
53 | assert property (@(posedge clk) val s_until_with val) $display("[%0t] s_until_with, fileline:%d", $time, 53);
55 | assert property (@(posedge clk) val s_until (val ##1 val)) $display("[%0t] sequence in until_with, fileline:%d", $time, 55);
| ^~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:57:44: Unsupported: s_until_with (in property expression)
: ... note: In instance 't'
57 | assert property (@(posedge clk) ##1 (val s_until_with val)) $display("[%0t] s_until_with in sequence, fileline:%d", $time, 57);
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:59:39: Unsupported: s_until_with (in property expression)
: ... note: In instance 't'
59 | assert property (@(posedge clk) val s_until_with val s_until_with val) $display("[%0t] nested s_until_with, fileline:%d", $time, 59);
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:55:40: Unsupported: 'until' in complex property expression
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:61:39: Unsupported: s_until_with (in property expression)
: ... note: In instance 't'
55 | assert property (@(posedge clk) (val until val) or val) $display("[%0t] until inside or, fileline:%d", $time, 55);
61 | assert property (@(posedge clk) val s_until_with (val ##1 val)) $display("[%0t] sequence in until_with, fileline:%d", $time, 61);
| ^~~~~~~~~~~~
%Error-UNSUPPORTED: t/t_property_sexpr_unsup.v:63:40: Unsupported: 'until' in complex property expression
: ... note: In instance 't'
63 | assert property (@(posedge clk) (val until val) or val) $display("[%0t] until inside or, fileline:%d", $time, 63);
| ^~~~~
%Error: Exiting due to

View File

@ -48,9 +48,17 @@ module t ( /*AUTOARG*/
assert property (@(posedge clk) val until (val ##1 val)) $display("[%0t] sequence in until, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) val s_until !val) $display("[%0t] s_until, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) ##1 (val s_until val)) $display("[%0t] s_until in sequence, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) val s_until_with val) $display("[%0t] s_until_with, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) val s_until val s_until val) $display("[%0t] nested s_until, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) val s_until (val ##1 val)) $display("[%0t] sequence in until_with, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) ##1 (val s_until_with val)) $display("[%0t] s_until_with in sequence, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) val s_until_with val s_until_with val) $display("[%0t] nested s_until_with, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) val s_until_with (val ##1 val)) $display("[%0t] sequence in until_with, fileline:%d", $time, `__LINE__);
assert property (@(posedge clk) (val until val) or val) $display("[%0t] until inside or, fileline:%d", $time, `__LINE__);

View File

@ -46,6 +46,40 @@ module t ( /*AUTOARG*/
results[5].passs++;
else results[5].fails++;
assert property (@(posedge clk) 0 s_until 1)
results[6].passs++;
else results[6].fails++;
assert property (@(posedge clk) cyc < 5 s_until cyc >= 5)
results[7].passs++;
else results[7].fails++;
assert property (@(posedge clk) cyc % 3 == 0 s_until cyc % 5 == 0)
results[8].passs++;
else results[8].fails++;
// Check that s_until accepts immediately when RHS is true, even if LHS is false.
assert property (@(posedge clk) cyc % 2 == 0 s_until 1)
results[9].passs++;
else results[9].fails++;
// Check that s_until_with requires LHS when RHS is true on the same tick.
assert property (@(posedge clk) 0 s_until_with 1)
results[10].passs++;
else results[10].fails++;
assert property (@(posedge clk) 1 s_until_with cyc >= 5)
results[11].passs++;
else results[11].fails++;
assert property (@(posedge clk) cyc <= 5 s_until_with cyc >= 5)
results[12].passs++;
else results[12].fails++;
assert property (@(posedge clk) cyc < 5 s_until_with cyc >= 5)
results[13].passs++;
else results[13].fails++;
always @(edge clk) begin
++cyc;
if (cyc == MAX) begin
@ -54,6 +88,14 @@ module t ( /*AUTOARG*/
expected[3] = '{0, 7};
expected[4] = '{5, 2};
expected[5] = '{2, 5};
expected[6] = '{0, 7};
expected[7] = '{0, 7};
expected[8] = '{5, 2};
expected[9] = '{0, 7};
expected[10] = '{7, 0};
expected[11] = '{0, 7};
expected[12] = '{4, 3};
expected[13] = '{7, 0};
`checkh(results, expected);
$write("*-* All Finished *-*\n");
$finish;

View File

@ -66,122 +66,122 @@ module top();
HIJ_struct HIJ;
initial begin
// struct ab
ab = '{0, 0}; //constant member by position
if (ab.a != 0) $stop;
if (ab.b != 0) $stop;
// struct ab
ab = '{0, 0}; //constant member by position
if (ab.a != 0) $stop;
if (ab.b != 0) $stop;
ab = '{default: 0}; //default value
if (ab.a != 0) $stop;
if (ab.b != 0) $stop;
ab = '{default: 0}; //default value
if (ab.a != 0) $stop;
if (ab.b != 0) $stop;
ab = '{int: 1, shortint: 0}; //data type and default value
if (ab.a != 1) $stop;
if (ab.b != 0) $stop;
ab = '{int: 1, shortint: 0}; //data type and default value
if (ab.a != 1) $stop;
if (ab.b != 0) $stop;
abkey[1:0] = '{'{a:1, b:2}, '{int:2, shortint:3}}; // member: value & data_type: value
if (abkey[1].a != 1) $stop;
if (abkey[1].b != 2) $stop;
if (abkey[0].a != 2) $stop;
if (abkey[0].b != 3) $stop;
abkey[1:0] = '{'{a:1, b:2}, '{int:2, shortint:3}}; // member: value & data_type: value
if (abkey[1].a != 1) $stop;
if (abkey[1].b != 2) $stop;
if (abkey[0].a != 2) $stop;
if (abkey[0].b != 3) $stop;
// struct st
st = '{1, 2+k}; //constant member by position
if (st.x != 1) $stop;
if (st.y != 2+k) $stop;
// struct st
st = '{1, 2+k}; //constant member by position
if (st.x != 1) $stop;
if (st.y != 2+k) $stop;
st = '{x:2, y:3+k}; //member: value
if (st.x != 2) $stop;
if (st.y != 3+k) $stop;
st = '{x:2, y:3+k}; //member: value
if (st.x != 2) $stop;
if (st.y != 3+k) $stop;
st = '{int:2, int:3+k}; //data_type: value override
if (st.x != 3+k) $stop;
if (st.y != 3+k) $stop;
st = '{int:2, int:3+k}; //data_type: value override
if (st.x != 3+k) $stop;
if (st.y != 3+k) $stop;
// struct sa
sa = '{default:'1};
if (sa.a != '1) $stop;
if (sa.b != '1) $stop;
if (sa.c != '1) $stop;
if (sa.s != '1) $stop;
// struct sa
sa = '{default:'1};
if (sa.a != '1) $stop;
if (sa.b != '1) $stop;
if (sa.c != '1) $stop;
if (sa.s != '1) $stop;
sa = '{default:'1, int: 5};
if (sa.a != '1) $stop;
if (sa.b != '1) $stop;
if (sa.c != '1) $stop;
if (sa.s != 5) $stop;
sa = '{default:'1, int: 5};
if (sa.a != '1) $stop;
if (sa.b != '1) $stop;
if (sa.c != '1) $stop;
if (sa.s != 5) $stop;
sa = '{default:'1, int: 5, b: 0};
if (sa.a != '1) $stop;
if (sa.b != 0) $stop;
if (sa.c != '1) $stop;
if (sa.s != 5) $stop;
sa = '{default:'1, int: 5, b: 0};
if (sa.a != '1) $stop;
if (sa.b != 0) $stop;
if (sa.c != '1) $stop;
if (sa.s != 5) $stop;
// struct DEF
DEF = '{A:1, BC1:'{B:2, C:3}, BC2:'{B:4,C:5}};
if (DEF.A != 1) $stop;
if (DEF.BC1.B != 2) $stop;
if (DEF.BC1.C != 3) $stop;
if (DEF.BC2.B != 4) $stop;
if (DEF.BC2.C != 5) $stop;
// struct DEF
DEF = '{A:1, BC1:'{B:2, C:3}, BC2:'{B:4,C:5}};
if (DEF.A != 1) $stop;
if (DEF.BC1.B != 2) $stop;
if (DEF.BC1.C != 3) $stop;
if (DEF.BC2.B != 4) $stop;
if (DEF.BC2.C != 5) $stop;
DEF = '{int:0, BC1:'{int:10}, BC2:'{default:5}};
if (DEF.A != 0) $stop;
if (DEF.BC1.B != 10) $stop;
if (DEF.BC1.C != 10) $stop;
if (DEF.BC2.B != 5) $stop;
if (DEF.BC2.C != 5) $stop;
DEF = '{int:0, BC1:'{int:10}, BC2:'{default:5}};
if (DEF.A != 0) $stop;
if (DEF.BC1.B != 10) $stop;
if (DEF.BC1.C != 10) $stop;
if (DEF.BC2.B != 5) $stop;
if (DEF.BC2.C != 5) $stop;
DEF = '{default:1, BC1:'{int:10}, BC2:'{default:5}};
if (DEF.A != 1) $stop;
if (DEF.BC1.B != 10) $stop;
if (DEF.BC1.C != 10) $stop;
if (DEF.BC2.B != 5) $stop;
if (DEF.BC2.C != 5) $stop;
DEF = '{default:1, BC1:'{int:10}, BC2:'{default:5}};
if (DEF.A != 1) $stop;
if (DEF.BC1.B != 10) $stop;
if (DEF.BC1.C != 10) $stop;
if (DEF.BC2.B != 5) $stop;
if (DEF.BC2.C != 5) $stop;
DEF = '{default:10};
if (DEF.A != 10) $stop;
if (DEF.BC1.B != 10) $stop;
if (DEF.BC1.C != 10) $stop;
if (DEF.BC2.B != 10) $stop;
if (DEF.BC2.C != 10) $stop;
DEF = '{default:10};
if (DEF.A != 10) $stop;
if (DEF.BC1.B != 10) $stop;
if (DEF.BC1.C != 10) $stop;
if (DEF.BC2.B != 10) $stop;
if (DEF.BC2.C != 10) $stop;
DEF = '{int:10};
if (DEF.A != 10) $stop;
if (DEF.BC1.B != 10) $stop;
if (DEF.BC1.C != 10) $stop;
if (DEF.BC2.B != 10) $stop;
if (DEF.BC2.C != 10) $stop;
DEF = '{int:10};
if (DEF.A != 10) $stop;
if (DEF.BC1.B != 10) $stop;
if (DEF.BC1.C != 10) $stop;
if (DEF.BC2.B != 10) $stop;
if (DEF.BC2.C != 10) $stop;
// struct HIJ
HIJ = '{int:10, default: 5};
if (HIJ.A != 10) $stop;
if (HIJ.BC1.B != 10) $stop;
if (HIJ.BC1.C != 10) $stop;
if (HIJ.BC1.DE1.D != 10) $stop;
if (HIJ.BC1.DE1.E != 10) $stop;
if (HIJ.BC1.DE1.FG1.F != 10) $stop;
if (HIJ.BC1.DE1.FG1.G != 5) $stop;
// struct HIJ
HIJ = '{int:10, default: 5};
if (HIJ.A != 10) $stop;
if (HIJ.BC1.B != 10) $stop;
if (HIJ.BC1.C != 10) $stop;
if (HIJ.BC1.DE1.D != 10) $stop;
if (HIJ.BC1.DE1.E != 10) $stop;
if (HIJ.BC1.DE1.FG1.F != 10) $stop;
if (HIJ.BC1.DE1.FG1.G != 5) $stop;
HIJ = '{shortint:10, default: 5};
if (HIJ.A != 5) $stop;
if (HIJ.BC1.B != 5) $stop;
if (HIJ.BC1.C != 5) $stop;
if (HIJ.BC1.DE1.D != 5) $stop;
if (HIJ.BC1.DE1.E != 5) $stop;
if (HIJ.BC1.DE1.FG1.F != 5) $stop;
if (HIJ.BC1.DE1.FG1.G != 10) $stop;
HIJ = '{shortint:10, default: 5};
if (HIJ.A != 5) $stop;
if (HIJ.BC1.B != 5) $stop;
if (HIJ.BC1.C != 5) $stop;
if (HIJ.BC1.DE1.D != 5) $stop;
if (HIJ.BC1.DE1.E != 5) $stop;
if (HIJ.BC1.DE1.FG1.F != 5) $stop;
if (HIJ.BC1.DE1.FG1.G != 10) $stop;
$write("*-* All Finished *-*\n");
$finish;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -52,4 +52,12 @@
38 | assert property (@(e) s_eventually 1'h1);
| ^~~~~~~~~~~~
... For error description see https://verilator.org/warn/NOTIMING?v=latest
%Error-NOTIMING: t/t_notiming.v:39:31: s_until requires --timing
: ... note: In instance 't'
39 | assert property (@(e) 1'h1 s_until 1'h1);
| ^~~~~~~
%Error-NOTIMING: t/t_notiming.v:40:31: s_until_with requires --timing
: ... note: In instance 't'
40 | assert property (@(e) 1'h1 s_until_with 1'h1);
| ^~~~~~~~~~~~
%Error: Exiting due to

View File

@ -20,6 +20,6 @@ test.compile(verilator_flags2=['--unroll-count 4 --unroll-stmts 9999 --stats -DT
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled loops\s+(\d+)', 5)
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled iterations\s+(\d+)', 25)
test.file_grep(test.stats,
r'Optimizations, Loop unrolling, Failed - reached --unroll-count\s+(\d+)', 2)
r'Optimizations, Loop unrolling, Failed - reached --unroll-count\s+(\d+)', 7)
test.passes()

View File

@ -31,4 +31,33 @@ loop_6 3
loop_6 4
loop_6 5
stopping loop_6
loop_7 0
loop_8 0
loop_8 1
loop_8 2
loop_8 3
loop_7 1
loop_8 0
loop_8 1
loop_8 2
loop_8 3
loop_7 2
loop_8 0
loop_8 1
loop_8 2
loop_8 3
loop_7 3
loop_8 0
loop_8 1
loop_8 2
loop_8 3
loop_7 4
loop_8 0
loop_8 1
loop_8 2
loop_8 3
loop_9 0
loop_9 1
loop_9 2
loop_9 3
*-* All Finished *-*

View File

@ -26,7 +26,7 @@ test.file_grep(test.stats,
test.file_grep(test.stats,
r'Optimizations, Loop unrolling, Failed - unknown loop condition\s+(\d+)', 0)
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Pragma unroll_disable\s+(\d+)', 0)
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled loops\s+(\d+)', 6)
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled iterations\s+(\d+)', 40)
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled loops\s+(\d+)', 13)
test.file_grep(test.stats, r'Optimizations, Loop unrolling, Unrolled iterations\s+(\d+)', 77)
test.passes()

View File

@ -12,6 +12,8 @@ module t;
return ++static_loop_cond < 8;
endfunction
int nonConst3 = $c("3");
initial begin
// Basic loop
for (int i = 0; i < 3; ++i) begin : loop_0
@ -59,6 +61,22 @@ module t;
end
end
end
// Outer condition updated in inner loop
begin
automatic int i = 0;
while (i < 5) begin : loop_7
$display("loop_7 %0d", i);
for (int j = 0 ; j < 4; ++j) begin : loop_8
$display("loop_8 %0d", j);
if (j == 3) ++i;
end
end
end
// Data dependent break
for (int i = 0; i < 5; ++i) begin : loop_9
$display("loop_9 %0d", i);
if (i == nonConst3) break;
end
// Done
$write("*-* All Finished *-*\n");
$finish;

View File

@ -5,5 +5,5 @@
// SPDX-License-Identifier: CC0-1.0
module sub;
int warn_sub = 64'h1;
int warn_sub = 64'h1;
endmodule