Merge from master for release.
This commit is contained in:
commit
c233a39052
|
|
@ -155,7 +155,7 @@ jobs:
|
|||
- name: Zip up repository
|
||||
run: Compress-Archive -LiteralPath install -DestinationPath verilator.zip
|
||||
- name: Upload zip archive
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: ${{ github.workspace }}/repo/verilator.zip
|
||||
name: verilator-win.zip
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ jobs:
|
|||
uses: actions/checkout@v6
|
||||
|
||||
- name: Download code coverage data
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: code-coverage-*
|
||||
path: obj_coverage
|
||||
|
|
@ -90,7 +90,7 @@ jobs:
|
|||
find obj_coverage -type f | paste -sd, | sed "s/^/files=/" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v6
|
||||
with:
|
||||
disable_file_fixes: true
|
||||
disable_search: true
|
||||
|
|
@ -114,7 +114,7 @@ jobs:
|
|||
sudo apt install lcov
|
||||
|
||||
- name: Download repository archive
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ${{ needs.build.outputs.archive }}
|
||||
path: ${{ github.workspace }}
|
||||
|
|
@ -125,7 +125,7 @@ jobs:
|
|||
ls -lsha
|
||||
|
||||
- name: Download code coverage data
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: code-coverage-*
|
||||
path: repo/obj_coverage
|
||||
|
|
@ -170,14 +170,14 @@ jobs:
|
|||
fi
|
||||
|
||||
- name: Upload report
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: repo/obj_coverage
|
||||
name: coverage-report
|
||||
|
||||
- name: Upload notification
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: repo/notification
|
||||
name: coverage-pr-notification
|
||||
|
|
@ -193,7 +193,7 @@ jobs:
|
|||
# Creating issues requires elevated privilege
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v2.2.1
|
||||
uses: actions/create-github-app-token@v3.1.1
|
||||
with:
|
||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ jobs:
|
|||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: |
|
||||
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
||||
|
|
@ -64,21 +64,21 @@ jobs:
|
|||
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
with:
|
||||
buildkitd-flags: --debug
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Build and Push to Docker
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
context: ${{ env.build_context }}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
# Block PRs against stable branch
|
||||
name: "Check PR not against 'stable'"
|
||||
on:
|
||||
pull_request:
|
||||
branches: [stable]
|
||||
|
||||
jobs:
|
||||
block-pr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Fail and block PR
|
||||
run: |-
|
||||
echo "Error: Pull requests directly to 'stable' are not allowed. Please PR against 'master'."
|
||||
exit 1
|
||||
|
|
@ -48,7 +48,7 @@ jobs:
|
|||
ls -lsha
|
||||
tree -L 3 pages
|
||||
- name: Upload pages artifact
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
uses: actions/upload-pages-artifact@v5
|
||||
with:
|
||||
path: pages
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ jobs:
|
|||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: actions/deploy-pages@v4
|
||||
uses: actions/deploy-pages@v5
|
||||
|
||||
notify:
|
||||
name: Notify
|
||||
|
|
@ -74,7 +74,7 @@ jobs:
|
|||
# Use the Verilator CI app to post the comment
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v2.2.1
|
||||
uses: actions/create-github-app-token@v3.1.1
|
||||
with:
|
||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ jobs:
|
|||
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload repository archive
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
|
||||
name: ${{ steps.create-archive.outputs.archive }}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ jobs:
|
|||
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
sudo apt update || \
|
||||
sudo apt update
|
||||
sudo apt install ccache mold help2man libfl-dev libgoogle-perftools-dev libsystemc-dev || \
|
||||
sudo apt install ccache mold help2man libfl-dev libgoogle-perftools-dev libsystemc-dev
|
||||
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev || \
|
||||
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev
|
||||
|
||||
- name: Use saved ccache
|
||||
uses: actions/cache@v5
|
||||
|
|
@ -70,7 +70,7 @@ jobs:
|
|||
run: tar --posix -c -z -f verilator-rtlmeter.tar.gz install
|
||||
|
||||
- name: Upload Verilator installation archive
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: verilator-rtlmeter.tar.gz
|
||||
name: verilator-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}
|
||||
|
|
|
|||
|
|
@ -60,11 +60,11 @@ jobs:
|
|||
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
sudo apt update || \
|
||||
sudo apt update
|
||||
sudo apt install ccache mold libfl-dev libgoogle-perftools-dev libsystemc-dev || \
|
||||
sudo apt install ccache mold libfl-dev libgoogle-perftools-dev libsystemc-dev
|
||||
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@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: verilator-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ jobs:
|
|||
./rtlmeter report --steps '*' --metrics '*' ../results-${{ steps.results.outputs.hash }}.json
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: results-${{ steps.results.outputs.hash }}.json
|
||||
name: rtlmeter-${{ inputs.tag }}-results-${{ steps.results.outputs.hash }}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ jobs:
|
|||
steps:
|
||||
|
||||
- name: Download repository archive
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ${{ inputs.archive }}
|
||||
path: ${{ github.workspace }}
|
||||
|
|
@ -99,11 +99,13 @@ jobs:
|
|||
|
||||
- name: Upload code coverage data
|
||||
if: ${{ inputs.dev-gcov }}
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: ${{ github.workspace }}/repo/obj_coverage/verilator-${{ inputs.suite }}.info
|
||||
name: code-coverage-${{ inputs.suite }}
|
||||
|
||||
- name: Fail job if a test failed
|
||||
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
|
||||
run: exit 1
|
||||
run: |-
|
||||
echo "Click on '> Tests' arrow above (or on other steps), to expand it, and see the failure reasons"
|
||||
exit 1
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ jobs:
|
|||
pull-requests: write
|
||||
steps:
|
||||
- name: Download report
|
||||
uses: actions/download-artifact@v7
|
||||
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@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: pr-number
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
|
@ -34,7 +34,7 @@ jobs:
|
|||
# Use the Verilator CI app to post the comment
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v2.2.1
|
||||
uses: actions/create-github-app-token@v3.1.1
|
||||
with:
|
||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ on:
|
|||
- cron: '0 2 * * *' # Daily, starting at 02:00 UTC
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||
push:
|
||||
branches: ['ci-rtlmeter/**']
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
@ -35,7 +37,8 @@ jobs:
|
|||
&& vars.ENABLE_SCHEDULED_JOBS == 'true') ||
|
||||
(github.event_name == 'pull_request'
|
||||
&& contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter')) ||
|
||||
(github.event_name == 'workflow_dispatch')
|
||||
(github.event_name == 'workflow_dispatch') ||
|
||||
(github.event_name == 'push')
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Startup
|
||||
|
|
@ -75,12 +78,10 @@ jobs:
|
|||
matrix:
|
||||
cases:
|
||||
- "BlackParrot:1x1:*"
|
||||
- "BlackParrot:2x2:*"
|
||||
- "BlackParrot:4x4:*"
|
||||
- "Caliptra:default:*"
|
||||
- "NVDLA:*"
|
||||
- "OpenPiton:1x1:*"
|
||||
- "OpenPiton:2x2:*"
|
||||
- "OpenPiton:4x4:*"
|
||||
- "OpenTitan:*"
|
||||
- "VeeR-EH1:asic*"
|
||||
|
|
@ -121,12 +122,10 @@ jobs:
|
|||
matrix:
|
||||
cases:
|
||||
- "BlackParrot:1x1:*"
|
||||
- "BlackParrot:2x2:*"
|
||||
- "BlackParrot:4x4:*"
|
||||
- "Caliptra:default:*"
|
||||
- "NVDLA:*"
|
||||
- "OpenPiton:1x1:*"
|
||||
- "OpenPiton:2x2:*"
|
||||
- "OpenPiton:4x4:*"
|
||||
- "OpenTitan:*"
|
||||
- "VeeR-EH1:asic*"
|
||||
|
|
@ -167,11 +166,9 @@ jobs:
|
|||
matrix:
|
||||
cases:
|
||||
- "BlackParrot:1x1:* !-hier"
|
||||
- "BlackParrot:2x2:* !-hier"
|
||||
- "BlackParrot:4x4:* !-hier"
|
||||
- "NVDLA:* !-hier"
|
||||
- "OpenPiton:1x1:* !-hier"
|
||||
- "OpenPiton:2x2:* !-hier"
|
||||
- "OpenPiton:4x4:* !-hier"
|
||||
- "OpenPiton:8x8:* !-hier"
|
||||
- "OpenPiton:16x16:dhry !-hier"
|
||||
|
|
@ -198,7 +195,7 @@ jobs:
|
|||
working-directory: rtlmeter
|
||||
run: make venv
|
||||
- name: Download all results
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: rtlmeter-${{ matrix.tag }}-results-*
|
||||
path: all-results-${{ matrix.tag }}
|
||||
|
|
@ -208,7 +205,7 @@ jobs:
|
|||
run: |
|
||||
./rtlmeter collate ../all-results-${{ matrix.tag }}/*.json > ../all-results-${{ matrix.tag }}.json
|
||||
- name: Upload combined results
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: all-results-${{ matrix.tag }}.json
|
||||
name: all-results-${{ matrix.tag }}
|
||||
|
|
@ -227,20 +224,20 @@ jobs:
|
|||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Download combined results
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: all-results-*
|
||||
path: results
|
||||
merge-multiple: true
|
||||
- name: Upload published results
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: results/*.json
|
||||
name: published-results
|
||||
# Pushing to verilator/verilator-rtlmeter-results requires elevated permissions
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v2.2.1
|
||||
uses: actions/create-github-app-token@v3.1.1
|
||||
with:
|
||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||
|
|
@ -289,7 +286,7 @@ jobs:
|
|||
working-directory: rtlmeter
|
||||
run: make venv
|
||||
- name: Download combined results
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: all-results-*
|
||||
path: all-results
|
||||
|
|
@ -308,7 +305,7 @@ jobs:
|
|||
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@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: published-results
|
||||
path: nightly-results
|
||||
|
|
@ -374,14 +371,14 @@ jobs:
|
|||
done
|
||||
cat report.txt
|
||||
- name: Upload report
|
||||
uses: actions/upload-artifact@v6
|
||||
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
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: pr-number.txt
|
||||
name: pr-number
|
||||
|
|
@ -397,7 +394,7 @@ jobs:
|
|||
# Creating issues requires elevated privilege
|
||||
- name: Generate access token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v2.2.1
|
||||
uses: actions/create-github-app-token@v3.1.1
|
||||
with:
|
||||
app-id: ${{ vars.VERILATOR_CI_ID }}
|
||||
private-key: ${{ secrets.VERILATOR_CI_KEY }}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15)
|
|||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||
project(
|
||||
Verilator
|
||||
VERSION 5.046
|
||||
VERSION 5.048
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
|
|
|||
177
Changes
177
Changes
|
|
@ -8,6 +8,179 @@ The changes in each Verilator version are described below. The
|
|||
contributors that suggested or implemented a given issue are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.048 2026-04-26
|
||||
==========================
|
||||
|
||||
**Important:**
|
||||
|
||||
* Displaying Verilator generated FST files will require an updated version
|
||||
of Surfer (built from 'main', or the future numbered release 0.7.0).
|
||||
No GTKWave update is necessary (#7255).
|
||||
* Support jemalloc as the default allocator on Linux (#7250). [Yangyu Chen]
|
||||
For better performance, `apt install libjemalloc-dev` or equivalent before running ./configure.
|
||||
|
||||
**Other:**
|
||||
|
||||
* Add VPI callback support to `--main` (#7145).
|
||||
* Add `--func-recursion-depth` option (#7175) (#7179).
|
||||
* Add `+verilator+solver+file` for debugging constraint solver (#7242).
|
||||
* Add `--coverage-fsm` for experimental FSM state and arc coverage (#7412). [Yogish Sekhar]
|
||||
* Add printed summary to verilator_coverage (#7438) (#7462). [Yogish Sekhar]
|
||||
* Deprecate `--structs-packed` (#7222).
|
||||
* Remove DFG extract optimization pass (#7394). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Remove multi-threaded FST tracing (#7443). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Improve assignment-compatibility type check (#2843) (#5666) (#7052). [Pawel Kojma, Antmicro Ltd.]
|
||||
* Improve error message when variable used as data type (#7318). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Improve E_UNSUPPORTED warning messages (#7329). [Eunseo Song]
|
||||
* Improve NFA-based multi-cycle SVA evaluation engine (#7430). [Yilou Wang]
|
||||
* Change array tracing to dump left index to right index (#7205). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Change `--converge-limit` default to 10000 (#7209).
|
||||
* Support inout inside SV interface (#3466) (#7134). [Nick Brereton]
|
||||
* Support `##0` cycle delays (#4263) (#7298). [Yilou Wang]
|
||||
* Support multidimensional arrays of interfaces (#6230) (#7451). [em2machine]
|
||||
* Support array reduction methods with 'with' clause in constraints (#6455) (#6999). [Rahul Behl]
|
||||
* Support constraint imperfect distributions (#6811) (#7168). [Yilou Wang]
|
||||
* Support disable task by name (#6853) (#7136). [Nick Brereton]
|
||||
* Support procedural concurrent assertion simple cases (#6944).
|
||||
* Support force assignments to array elements of real type (#7048). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Support VPI array indexing in signal names (#7097) (#7187) (#7214) (#7289). [Christian Hecken, Heidelberg University]
|
||||
* Support soft constraint solving (#7124) (#7166). [Yilou Wang]
|
||||
* Support constraints on fixed-size array of class object members (#7170) (#7183). [Yilou Wang]
|
||||
* Support Z non-blocking assignment (#7192) (#496) (#7197). [Nick Brereton]
|
||||
* Support constraint `with` item.index array reduction (#7198). [Rahul Behl]
|
||||
* Support constant expressions without parentheses in `PATHPULSE$` (#7199). [Pawel Kojma, Antmicro Ltd.]
|
||||
* Support $sformat with runtime format string (#7212).
|
||||
* Support dist and solve...before inside foreach constraints (#7245) (#7253). [Yilou Wang]
|
||||
* Support array and struct info metadata in FST traces (#7255). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Support dynamic array .size in inline randomize() with constraints (#7258) (#7266). [Yilou Wang]
|
||||
* Support defparam with more than one dot (#7262). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support modport export/import task prototypes and out-of-block definitions (#7277). [Yilou Wang]
|
||||
* Support very wide $display arguments (#7280). [Jakub Michalski]
|
||||
* Support named sequence declarations and instances in assertions (#7283). [Yilou Wang]
|
||||
* Support `##`, `##[*]`, `##[+]`, `##[M:N]` cycle delays in assertions (#7284) (#7312) (#7377). [Yilou Wang]
|
||||
* Support boolean and/or in sequence expressions (#7285). [Yilou Wang]
|
||||
* Support property-local variables and sequence match items (#7286). [Yilou Wang]
|
||||
* Support 'until' and `until_with` property (#7290 partial) (#7399) (#7436). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Support implication operator with constraint_set (#7300) (#7448). [Yilou Wang]
|
||||
* Support array map() method (#7307) (#7316) (#7344). [Wei-Lun Chiu]
|
||||
* Support MacOS address sanitizer memory limit (#7308). [Marco Bartoli]
|
||||
* Support SVA goto repetition `[->N]` in concurrent assertions (#7310). [Yilou Wang]
|
||||
* Support consecutive repetition `[\*N]` in SVA properties (#7311). [Yilou Wang]
|
||||
* Support rise/fall delays (#7368). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support sequence intersect operator (#7374). [Yilou Wang]
|
||||
* Support sequence 'throughout' operator (#7378). [Yilou Wang]
|
||||
* Support sequence consecutive repetition `[*N:M]`, `[+]`, and `[*]` (#7379). [Yilou Wang]
|
||||
* Support sequence `first_match` operator (#7392). [Yilou Wang]
|
||||
* Support nonconsecutive repetition [=N] in sequence expressions (#7397). [Yilou Wang]
|
||||
* Support per-process RNG for process::srandom() and object seeding (#7408) (#7415) (#7408). [Yilou Wang]
|
||||
* Support 2**n expressions in constraint randomization (#7422). [Yilou Wang]
|
||||
* Support IEEE-compliant force/release handling (#7391). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Support sequence 'within' operator (#7461). [Yilou Wang]
|
||||
* Optimize trace code for faster compiles on repeated types (#6707) (#6832). [Todd Strader]
|
||||
* Optimize impure expressions and calls with new V3LiftExpr pass (#7141) (#7164). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize size of trace declaration object code (#7150). [Szymon Gizler, Antmicro Ltd.]
|
||||
* Optimize function call return value temporaries (#7152). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize conditional merging across some impure statements (#7159). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize reuse of existing associative terms in DfgPeephole. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize duplicate vertices and multiplexers in DfgPeephole (#7305). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize commutative vertex operands in DFG for better combining. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DFG peephole until a fixed point (#7309). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize comparisons with identical operands and $countones in DFG. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize more patterns in DfgPeephole (#7332). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize read references in DFG (#7354). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DFG only once, after scoping (#7362). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize more DFG peephole patterns (#7423) (#7452). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize DfgBreakCycles IndependentBits analysis ordering (#7446). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize select patterns in DfgPeephole. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize temporary insertion in DfgPeephole. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize arithmetic right shift (>>>) in DfgBreakCycles (#7447). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize temporary insertion in DFG (#7459). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize mix of Concat/Extend assignments (#7479). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix recursive default assignment for sub-arrays (#4589) (#7202). [Julian Carrier]
|
||||
* Fix tracing virtual interface member written from classes (#5044) (#7465). [Nikolay Puzanov]
|
||||
* Fix virtual interface member trigger convergence (#5116) (#7323). [Yilou Wang]
|
||||
* Fix shift width mismatch in constraint solver SMT emission (#5420) (#7265). [Yilou Wang]
|
||||
* Fix returning wrong type from static function in parameterized class (#5479) (#7387) (#7411) (#7418) (#7445) (#7450). [em2machine]
|
||||
* Fix randomize size+element queue constraints (#5582) (#7225). [Rahul Behl, Testorrent USA, Inc.]
|
||||
* Fix null assignment to virtual interfaces (#5974) (#5990). [Maxim Fonarev]
|
||||
* Fix typedef scope resolution for parameterized class aliases (#5977) (#7319). [Nick Brereton]
|
||||
* Fix lambda coroutines (#6106) (#7135). [Nick Brereton]
|
||||
* Fix super constructor calls with local variables (#6214) (#6933). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Fix parameter default comparison when value contains type cast (#6281) (#7369) (#6281). [Yilou Wang]
|
||||
* Fix `local::` false error in randomize() with on parameterized class (#6680) (#7293). [Yilou Wang]
|
||||
* Fix false recursive definition error (#6769) (#7118). [Alex Zhou]
|
||||
* Fix port assignment to large arrays (#6904).
|
||||
* Fix interface localparam dependencies and arbitrary nesting (#6936) (#7128) (#7188) (#7190). [em2machine]
|
||||
* Fix parameterized class typedef as interface type parameter (#7000) (#7006). [Leela Pakanati]
|
||||
* Fix errant integer promotion (#7012). [Todd Strader]
|
||||
* Fix randc solver hang with wide variables (#7068) (#7248). [Yilou Wang]
|
||||
* Fix coroutine trace setters (#7078 repair) (#7296). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Fix vpi_put_value not updating forced read value (#7092) (#7395) (#7092). [Christian Hecken, Heidelberg University]
|
||||
* Fix scheduling non-determinism (#7120) (#7162) (#7165). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix parameters inside std::randomize `with` clause (#7140). [Kamil Danecki, Antmicro Ltd.]
|
||||
* Fix forcing unpacked variables (#7149). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix wide conditional short circuiting (#7155).
|
||||
* Fix eliminating assignments to DPI-read variables (#7158). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix std::randomize() in static function with static class members (#7167) (#7169). [Yilou Wang]
|
||||
* Fix resolving default/non-default type parameters (#7171) (#7346) (#7380) (#7385) (#7398) (#7406) (#7398). [em2machine]
|
||||
* Fix recursive constant function in $unit scope (#7173) (#7174).
|
||||
* Fix class extend references between queues (#7195).
|
||||
* Fix library/hier_block tracing when top name is empty (#7200). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix virtual interface select from sub-interface instance (#7203) (#7370) (#7203). [Yilou Wang]
|
||||
* Fix VPI force of bit-selected signals (#7211) (#7301). [Christian Hecken]
|
||||
* Fix `$finish` to immediately stop executing code from non-final blocks (#7213 partial) (#7390).
|
||||
* Fix wrong $bits() for parameterized interface struct typedefs (#7218) (#7219). [em2machine]
|
||||
* Fix `dist` operator inside constraint if blocks (#7221) (#7224). [Rahul Behl, Testorrent USA, Inc.]
|
||||
* Fix array reduction in constraints crashing with class inheritance (#7226) (#7263). [Yilou Wang]
|
||||
* Fix soft cross-object constraint priority inversion (#7228) (#7233). [Yilou Wang]
|
||||
* Fix internal error when derived class calls this.randomize() with inherited rand members (#7229) (#7234). [Yilou Wang]
|
||||
* Fix enum range constraints missing for rand variables in sub-objects (#7230) (#7235). [Yilou Wang]
|
||||
* Fix vpi_put_value release on non-continuous signal (#7231) (#7241). [Christian Hecken]
|
||||
* Fix functions in generate block resulting in 'Broken link in node' (#7236) (#7367). [em2machine]
|
||||
* Fix tracing of typedefed 1D packed arrays with --trace-structs (#7237). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix rand variable used as array index in constraint evaluated as constant (#7238) (#7247). [Yilou Wang]
|
||||
* Fix --hierarchical dropping arguments in -f/-F files (#7240). [Clara Sparks]
|
||||
* Fix `final` asserts and $stop (#7249). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix MacOS clang PCH compile error with -o flag (#7251) (#7327). [Eunseo Song]
|
||||
* Fix vpi_put_value with vpiIntVal on VlWide data (#7256). [Christian Hecken]
|
||||
* Fix streaming with descending unpacked arrays and unpacked-to-queue (#7287). [Yilou Wang]
|
||||
* Fix MacOs lexer compile error (#7314) (#7315). [Tracy Narine]
|
||||
* Fix linking shared library with its dependencies (#7320). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix modport selection of virtual interface handle (#7321). [Yilou Wang]
|
||||
* Fix false ASSIGNIN on interface input ports driven from outside (#7322). [Yilou Wang]
|
||||
* Fix static initialization order for packages with class hierarchies (#7324). [Yilou Wang]
|
||||
* Fix sensitivity of signals to unrelated interface members (#7336). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix `disable iff` imply-delay statement linking (#7337). [Nick Brereton]
|
||||
* Fix lost `$stop` on implied assertion `$error` failures.
|
||||
* Fix wait() hang when interface uses process calls and VIF function (#7342). [Yilou Wang]
|
||||
* Fix error on illegal nand/nor binary operators (#7353).
|
||||
* Fix simple array assignment unrolling in slice optimization (#7359). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix missing temporary for DfgSplicePacked (#7361). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix virtual interface function calls binding to wrong instance (#7363). [Yilou Wang]
|
||||
* Fix false ASSIGNIN on interface input port connections (#7365). [Yilou Wang]
|
||||
* Fix string `inside` queue (#7373).
|
||||
* Fix subclass with rand_mode(0) getting randomized (#7376) (#7383). [Yilou Wang]
|
||||
* Fix VPI access to Verilog `force`-ed signals (#7381). [Christian Hecken, Heidelberg University]
|
||||
* Fix sampling of hierarchical references (#7386). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix virtual class inheritance false error (#7403) (#7405). [Nikolay Puzanov]
|
||||
* Fix CMake compiler coroutine flags (#7404). [Shogo Yamazaki]
|
||||
* Fix delete inside foreach skipping elements (#7407) (#7410)
|
||||
* Fix std::randomize in parameterized-derived class (#7409) (#7416). [Yilou Wang]
|
||||
* Fix virtual interface implied comparison with null (#7421). [Alex Solomatnikov]
|
||||
* Fix uvm_hdl_release_and_read to release value and check success (#7425). [Christian Hecken, Heidelberg University]
|
||||
* Fix inline constraint on array-indexed randomize target (#7431) (#7434). [Yilou Wang]
|
||||
* Fix modification of members of object with const handle (#7433). [Kamil Danecki, Antmicro Ltd.]
|
||||
* Fix `dist` under implication in constraints (#7440) (#7442). [Alex Solomatnikov] [Yilou Wang]
|
||||
* Fix std::randomize `inside` corrupting class-member queue operand (#7449) (#7456). [Yilou Wang]
|
||||
* Fix module parameters not re-evaluated upon instantiation (#7463) (#7477). [em2machine]
|
||||
* Fix function-in-constraint internal error on bare port return (#7473) (#7480). [Yilou Wang]
|
||||
* Fix access to static variable inside function (#7474) (#7475). [Alex Zhou]
|
||||
* Fix multiple inclusion of verilated_std.sv (#7478). [Larry Doolittle]
|
||||
* Fix std::randomize inside {typedef array} internal error (#7481). [Yilou Wang]
|
||||
* Fix module parameters not re-evaluated upon instantiation (#7463) (#7477). [em2machine]
|
||||
* Fix infinite recursion with VERILATOR_BIN (#7496) (#7497).
|
||||
|
||||
|
||||
Verilator 5.046 2026-02-28
|
||||
==========================
|
||||
|
||||
|
|
@ -31,7 +204,7 @@ Verilator 5.046 2026-02-28
|
|||
* Support solve..before constraints (#5647) (#7123). [Yilou Wang]
|
||||
* Support structure initial values (#6130).
|
||||
* Support proper automatic/static initialization, and remove STATICVAR warning (#6405) (#7086).
|
||||
* Support vpi_put/vpi_get forcing of signals (#5933) (#6704). [Christian Hecken]
|
||||
* Support vpi_put/vpi_get forcing of signals (#5933) (#6704). [Christian Hecken, Heidelberg University]
|
||||
* Support detailed failure info for constraint violations (#6617) (#6883). [Yilou Wang]
|
||||
* Support `unique` constraints (on 1D static arrays) (#6810) (#6878). [Srinivasan Venkataramanan]
|
||||
* Support complex expressions as std::randomize arguments (#6860). [Jakub Wasilewski, Antmicro Ltd.]
|
||||
|
|
@ -94,7 +267,7 @@ Verilator 5.046 2026-02-28
|
|||
* Fix `foreach` with mid-index empty commas (#6910).
|
||||
* Fix internal error when fork under always expression (#6911).
|
||||
* Fix error when calling non-static method (#6916). [Artur Bieniek, Antmicro Ltd.]
|
||||
* Fix memory leak in vpi_put_value and vpi_get_value (#6917). [Christian Hecken]
|
||||
* Fix memory leak in vpi_put_value and vpi_get_value (#6917). [Christian Hecken, Heidelberg University]
|
||||
* Fix interface internal type reference (#6920) (#6966). [Todd Strader]
|
||||
* Fix segfault after assignment pattern XOR error (#6928) (#6931). [emmettifelts]
|
||||
* Fix delayed initial assignment (#6929). [Todd Strader]
|
||||
|
|
|
|||
14
Makefile.in
14
Makefile.in
|
|
@ -398,9 +398,12 @@ CPPCHECK_RUNTIME_CPP = $(wildcard $(srcdir)/include/*.cpp)
|
|||
CPPCHECK_VLC_CPP = $(wildcard $(srcdir)/src/Vlc*.cpp)
|
||||
CPPCHECK_EXAMPLES_CPP = $(wildcard $(srcdir)/examples/*/*.cpp)
|
||||
CHECK_CPP = $(CPPCHECK_VERILATOR_CPP) $(CPPCHECK_RUNTIME_CPP) $(CPPCHECK_VLC_CPP) $(CPPCHECK_EXAMPLES_CPP)
|
||||
CHECK_H = $(wildcard \
|
||||
CHECK_H = $(filter-out \
|
||||
$(srcdir)/src/config_package.h \
|
||||
$(srcdir)/src/config_rev.h, \
|
||||
$(wildcard \
|
||||
$(srcdir)/include/*.h \
|
||||
$(srcdir)/src/*.h )
|
||||
$(srcdir)/src/*.h ))
|
||||
CHECK_YL = $(wildcard \
|
||||
$(srcdir)/src/*.y \
|
||||
$(srcdir)/src/*.l )
|
||||
|
|
@ -441,7 +444,9 @@ cppcheck:
|
|||
CLANGTIDY = clang-tidy
|
||||
CLANGTIDY_FLAGS = -config='' \
|
||||
-header-filter='.*' \
|
||||
-checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables,-cppcoreguidelines-avoid-goto,-modernize-avoid-c-arrays,-readability-magic-numbers,-readability-simplify-boolean-expr,-cppcoreguidelines-macro-usage' \
|
||||
-checks='*,-abseil-*,-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables,-cppcoreguidelines-avoid-goto,-modernize-avoid-c-arrays,-readability-magic-numbers,-readability-simplify-boolean-expr,-cppcoreguidelines-macro-usage' \
|
||||
|
||||
# Good checks: -checks='-*,bugprone-assignment-in-if-condition,google-build-using-namespace,google-readability-casting,modernize-use-default-member-init,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-nullptr,performance-faster-string-find,readability-isolate-declaration,readability-redundant-control-flow,readability-redundant-member-init,readability-redundant-string-cstr,readability-uppercase-literal-suffix
|
||||
|
||||
CLANGTIDY_DEP = $(subst .cpp,.cpp.tidy,$(CHECK_CPP))
|
||||
CLANGTIDY_DEFS = -DVL_DEBUG=1 -DVL_CPPCHECK=1
|
||||
|
|
@ -517,6 +522,7 @@ PY_PROGRAMS = \
|
|||
nodist/lint_py_test_filter \
|
||||
nodist/log_changes \
|
||||
nodist/uvm_pkg_packer \
|
||||
nodist/verilator_bisect \
|
||||
nodist/verilator_saif_diff \
|
||||
src/.gdbinit.py \
|
||||
src/astgen \
|
||||
|
|
@ -577,7 +583,7 @@ MBAKE = mbake
|
|||
MBAKE_FLAGS = format --config ./.bake.toml
|
||||
|
||||
format-exec:
|
||||
-chmod a+x test_regress/t/*.py
|
||||
-chmod a+x test_regress/t/t_*.py
|
||||
-chmod a-x test_regress/t/*.v
|
||||
|
||||
format-make mbake:
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ path = [
|
|||
".style.yapf",
|
||||
"CITATION.cff",
|
||||
"CPPLINT.cfg",
|
||||
"ci/docker/buildenv/wavetools.conf",
|
||||
"docs/CONTRIBUTORS",
|
||||
"docs/spelling.txt",
|
||||
"docs/verilated.dox",
|
||||
|
|
|
|||
|
|
@ -27,6 +27,21 @@ use vars qw($Debug @Opt_Verilator_Sw);
|
|||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
|
||||
# Guard against unbounded recursive re-entry. Hierarchical / --build flows
|
||||
# legitimately re-enter the wrapper a few levels deep (wrapper -> verilator_bin
|
||||
# -> make -> wrapper -> ...); a misconfigured VERILATOR_BIN, VERILATOR_GDB or
|
||||
# VERILATOR_VALGRIND that points back at this wrapper, however, recurses
|
||||
# without bound and exhausts the OS PID table. Use VERILATOR_RUNNING as a
|
||||
# depth counter and cap it well above any real nesting.
|
||||
my $verilator_running_max = 16;
|
||||
my $verilator_running_depth = ($ENV{VERILATOR_RUNNING} || 0) + 1;
|
||||
if ($verilator_running_depth > $verilator_running_max) {
|
||||
die "%Error: verilator: re-entered $verilator_running_depth levels deep"
|
||||
. " via \$VERILATOR_RUNNING; check that VERILATOR_BIN, VERILATOR_GDB"
|
||||
. " and VERILATOR_VALGRIND do not resolve back to this wrapper script.\n";
|
||||
}
|
||||
$ENV{VERILATOR_RUNNING} = $verilator_running_depth;
|
||||
|
||||
$Debug = 0;
|
||||
my $opt_aslr;
|
||||
my $opt_gdb;
|
||||
|
|
@ -149,7 +164,7 @@ if ($opt_gdb) {
|
|||
. verilator_bin()
|
||||
. " " . join(' ', @quoted_sw));
|
||||
} else {
|
||||
# Normal, non gdb
|
||||
# Normal, non-gdb
|
||||
run(ulimit_stack_unlimited() . aslr(1) . verilator_bin() . " " . join(' ', @quoted_sw));
|
||||
}
|
||||
|
||||
|
|
@ -213,14 +228,22 @@ sub ulimit_stack_unlimited {
|
|||
# AddressSanitizer doesn't work with 'ulimit -s unlimted'
|
||||
if (`${\(verilator_bin())} --get-supported DEV_ASAN` eq "1\n") {
|
||||
# Use host 'physical memory / #cores / 8' instead
|
||||
open(my $fh, "<", "/proc/meminfo") || die "Can't read host memory for asan";
|
||||
while (<$fh>) {
|
||||
if (m/MemTotal:\s+(\d+)\s+kB/) {
|
||||
$limit = int(int($1)/`nproc`/8);
|
||||
last;
|
||||
if ($^O eq "darwin") {
|
||||
my $membytes = `sysctl -n hw.memsize`;
|
||||
chomp $membytes;
|
||||
my $ncpu = `sysctl -n hw.ncpu`;
|
||||
chomp $ncpu;
|
||||
$limit = int(int($membytes / 1024) / $ncpu / 8);
|
||||
} else {
|
||||
open(my $fh, "<", "/proc/meminfo") || die "Can't read host memory for asan";
|
||||
while (<$fh>) {
|
||||
if (m/MemTotal:\s+(\d+)\s+kB/) {
|
||||
$limit = int(int($1)/`nproc`/8);
|
||||
last;
|
||||
}
|
||||
}
|
||||
close($fh);
|
||||
}
|
||||
close($fh);
|
||||
}
|
||||
system("ulimit -s $limit 2>/dev/null");
|
||||
my $status = $?;
|
||||
|
|
@ -345,10 +368,12 @@ detailed descriptions of these arguments.
|
|||
-CFLAGS <flags> C++ compiler arguments for makefile
|
||||
--compiler <compiler-name> Tune for specified C++ compiler
|
||||
--compiler-include Include additional header in the precompiled one
|
||||
--constraint-array-limit <size> Maximum array size for constraint array reduction
|
||||
--converge-limit <loops> Tune convergence settle time
|
||||
--coverage Enable all coverage
|
||||
--coverage-expr Enable expression coverage
|
||||
--coverage-expr-max <value> Maximum permutations allowed for an expression
|
||||
--coverage-fsm Enable FSM state/arc coverage
|
||||
--coverage-line Enable line coverage
|
||||
--coverage-max-width <width> Maximum array depth for coverage
|
||||
--coverage-toggle Enable toggle coverage
|
||||
|
|
@ -370,6 +395,7 @@ detailed descriptions of these arguments.
|
|||
--dump-<srcfile> Enable dumping everything in source file
|
||||
--dump-defines Show preprocessor defines with -E
|
||||
--dump-dfg Enable dumping DfgGraphs to .dot files
|
||||
--dump-dfg-patterns Enable dumping Dfg pattern statistics
|
||||
--dump-graph Enable dumping V3Graphs to .dot files
|
||||
--dump-inputs Enable dumping preprocessed input files
|
||||
--dump-tree Enable dumping Ast .tree files
|
||||
|
|
@ -390,6 +416,7 @@ detailed descriptions of these arguments.
|
|||
-f <file> Parse arguments from a file
|
||||
-FI <file> Force include of a file
|
||||
--flatten Force inlining of all modules, tasks and functions
|
||||
--func-recursion-depth <value> Maximum recursive constant function depth
|
||||
--future0 <option> Ignore an option for compatibility
|
||||
--future1 <option> Ignore an option with argument for compatibility
|
||||
-fno-<optimization> Disable internal optimization stage
|
||||
|
|
@ -496,7 +523,6 @@ detailed descriptions of these arguments.
|
|||
--no-std-package Prevent parsing standard package
|
||||
--no-std-waiver Prevent parsing standard lint waivers
|
||||
--no-stop-fail Do not call $stop when assertion fails
|
||||
--structs-packed Convert all unpacked structures to packed structures
|
||||
-sv Enable SystemVerilog parsing
|
||||
+systemverilogext+<ext> Synonym for +1800-2023ext+<ext>
|
||||
--threads <threads> Enable multithreading
|
||||
|
|
@ -517,7 +543,6 @@ detailed descriptions of these arguments.
|
|||
--trace-params Enable tracing of parameters
|
||||
--trace-saif Enable SAIF file creation
|
||||
--trace-structs Enable tracing structure names
|
||||
--trace-threads <threads> Enable FST waveform creation on separate threads
|
||||
--no-trace-top Do not emit traces for signals in the top module generated by verilator
|
||||
--trace-underscore Enable tracing of _signals
|
||||
--trace-vcd Enable VCD waveform creation
|
||||
|
|
@ -576,6 +601,7 @@ description of these arguments.
|
|||
+verilator+quiet Minimize additional printing
|
||||
+verilator+rand+reset+<value> Set random reset technique
|
||||
+verilator+seed+<value> Set random seed
|
||||
+verilator+solver+file+<filename> Set random solver log filename
|
||||
+verilator+V Show verbose version and config
|
||||
+verilator+version Show version and exit
|
||||
+verilator+wno+unsatconstr+<value> Disable constraint warnings
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ if (! GetOptions (
|
|||
pod2usage(-exitstatus => 2, -verbose => 0);
|
||||
}
|
||||
|
||||
# Normal, non gdb
|
||||
# Normal, non-gdb
|
||||
run(verilator_coverage_bin()
|
||||
. " " . join(' ', @Opt_Verilator_Sw));
|
||||
|
||||
|
|
@ -175,6 +175,7 @@ L<https://verilator.org/guide/latest/exe_verilator_coverage.html>.
|
|||
--annotate-points Annotates info from each coverage point.
|
||||
--filter-type <regex> Keep only records of given coverage type.
|
||||
--help Displays this message and version and exits.
|
||||
--include-reset-arcs Include reset arcs in FSM arc summaries.
|
||||
--rank Compute relative importance of tests.
|
||||
--unlink With --write, unlink all inputs
|
||||
--version Displays program version and exits.
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ elif [ "$CI_OS_NAME" = "osx" ]; then
|
|||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
MAKE=gmake
|
||||
else
|
||||
fatal "Unknown os: '$CI_OS_NAME'"
|
||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
||||
fi
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
|
|
@ -40,12 +40,32 @@ if [ "$CI_OS_NAME" = "linux" ]; then
|
|||
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
|
||||
fi
|
||||
|
||||
install-vcddiff() {
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
git clone https://github.com/veripool/vcddiff "$TMP_DIR"
|
||||
git -C "${TMP_DIR}" checkout 4db0d84a27e8f148b127e916fc71d650837955c5
|
||||
"$MAKE" -C "${TMP_DIR}"
|
||||
sudo cp "${TMP_DIR}/vcddiff" /usr/local/bin
|
||||
install-wavediff() {
|
||||
source ci/docker/buildenv/wavetools.conf
|
||||
local _base_url="https://github.com/hudson-trading/wavetools/releases/download/${WAVETOOLS_VERSION}"
|
||||
local _platform
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
_platform="linux-x86_64"
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
_platform="macos-arm64"
|
||||
elif [ "$CI_OS_NAME" = "windows" ]; then
|
||||
_platform="windows-x86_64"
|
||||
else
|
||||
echo "WARNING: No wavetools binary available for CI_OS_NAME=$CI_OS_NAME, skipping"
|
||||
return 0
|
||||
fi
|
||||
local _tmpdir
|
||||
_tmpdir=$(mktemp -d)
|
||||
local _archive="wavetools-${WAVETOOLS_VERSION}-${_platform}"
|
||||
if [ "$CI_OS_NAME" = "windows" ]; then
|
||||
wget -q -O "${_tmpdir}/${_archive}.zip" "${_base_url}/${_archive}.zip"
|
||||
unzip -o "${_tmpdir}/${_archive}.zip" -d "${_tmpdir}"
|
||||
else
|
||||
wget -q -O "${_tmpdir}/${_archive}.tar.gz" "${_base_url}/${_archive}.tar.gz"
|
||||
tar -xzf "${_tmpdir}/${_archive}.tar.gz" -C "${_tmpdir}"
|
||||
fi
|
||||
sudo cp "${_tmpdir}/${_archive}/wavediff" /usr/local/bin/wavediff
|
||||
rm -rf "${_tmpdir}"
|
||||
}
|
||||
|
||||
if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
|
|
@ -56,20 +76,22 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
|||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
sudo apt-get update ||
|
||||
sudo apt-get update
|
||||
sudo apt-get install ccache help2man libfl-dev ||
|
||||
sudo apt-get install ccache help2man libfl-dev
|
||||
sudo apt-get install --yes ccache help2man libfl-dev ||
|
||||
sudo apt-get install --yes ccache help2man libfl-dev
|
||||
if [[ ! "$CI_RUNS_ON" =~ "ubuntu-22.04" ]]; then
|
||||
# Some conflict of libunwind verison on 22.04, can live without it for now
|
||||
sudo apt-get install libgoogle-perftools-dev ||
|
||||
sudo apt-get install libgoogle-perftools-dev
|
||||
sudo apt-get install --yes libjemalloc-dev ||
|
||||
sudo apt-get install --yes libjemalloc-dev
|
||||
fi
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-20.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]]; then
|
||||
sudo apt-get install libsystemc libsystemc-dev ||
|
||||
sudo apt-get install libsystemc libsystemc-dev
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev ||
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev
|
||||
fi
|
||||
fi
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]]; then
|
||||
sudo apt-get install bear mold ||
|
||||
sudo apt-get install bear mold
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
sudo apt-get install --yes bear mold ||
|
||||
sudo apt-get install --yes bear mold
|
||||
fi
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
brew update ||
|
||||
|
|
@ -79,7 +101,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
|||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
sudo pkg install -y autoconf bison ccache gmake perl5
|
||||
else
|
||||
fatal "Unknown os: '$CI_OS_NAME'"
|
||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
||||
fi
|
||||
|
||||
if [ -n "$CCACHE_DIR" ]; then
|
||||
|
|
@ -94,16 +116,18 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
|||
sudo apt-get update ||
|
||||
sudo apt-get update
|
||||
# libfl-dev needed for internal coverage's test runs
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3 ||
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3
|
||||
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3 ||
|
||||
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3
|
||||
# Required for test_regress/t/t_dist_attributes.py
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]]; then
|
||||
sudo apt-get install python3-clang mold ||
|
||||
sudo apt-get install python3-clang mold
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
sudo apt-get install --yes python3-clang mold ||
|
||||
sudo apt-get install --yes python3-clang mold
|
||||
fi
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-20.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]]; then
|
||||
sudo apt-get install libsystemc-dev ||
|
||||
sudo apt-get install libsystemc-dev
|
||||
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
|
||||
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev ||
|
||||
sudo apt-get install --yes libsystemc libsystemc-dev
|
||||
fi
|
||||
fi
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
brew update
|
||||
|
|
@ -113,14 +137,14 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
|||
# fst2vcd fails with "Could not open '<input file>', exiting."
|
||||
sudo pkg install -y ccache gmake perl5 python3 jq z3
|
||||
else
|
||||
fatal "Unknown os: '$CI_OS_NAME'"
|
||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
||||
fi
|
||||
# Common installs
|
||||
install-vcddiff
|
||||
install-wavediff
|
||||
# Workaround -fsanitize=address crash
|
||||
sudo sysctl -w vm.mmap_rnd_bits=28
|
||||
else
|
||||
##############################################################################
|
||||
# Unknown build stage
|
||||
fatal "Unknown build stage: '$CI_BUILD_STAGE_NAME'"
|
||||
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
|||
export MAKE=gmake
|
||||
NPROC=$(sysctl -n hw.ncpu)
|
||||
else
|
||||
fatal "Unknown os: '$CI_OS_NAME'"
|
||||
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
|
||||
fi
|
||||
NPROC=$(expr $NPROC '+' 1)
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
|||
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=9/10
|
||||
;;
|
||||
*)
|
||||
fatal "Unknown test: $TESTS"
|
||||
fatal "Unknown TESTS: $TESTS"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
|
@ -204,5 +204,5 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
|||
else
|
||||
##############################################################################
|
||||
# Unknown build stage
|
||||
fatal "Unknown build stage: '$CI_BUILD_STAGE_NAME'"
|
||||
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ RUN apt-get update \
|
|||
libfl2 \
|
||||
libfl-dev \
|
||||
libclang-rt-18-dev \
|
||||
libgoogle-perftools-dev \
|
||||
libjemalloc-dev \
|
||||
libsystemc \
|
||||
libsystemc-dev \
|
||||
numactl \
|
||||
|
|
@ -53,10 +53,14 @@ RUN apt-get update \
|
|||
|
||||
WORKDIR /tmp
|
||||
|
||||
RUN git clone https://github.com/veripool/vcddiff.git && \
|
||||
make -C vcddiff && \
|
||||
cp -p vcddiff/vcddiff /usr/local/bin/vcddiff && \
|
||||
rm -rf vcddiff
|
||||
COPY wavetools.conf /tmp/wavetools.conf
|
||||
ARG WGET_EXTRA_ARGS=
|
||||
RUN . /tmp/wavetools.conf && \
|
||||
wget -q ${WGET_EXTRA_ARGS} -O /tmp/wavetools.tar.gz \
|
||||
"https://github.com/hudson-trading/wavetools/releases/download/${WAVETOOLS_VERSION}/wavetools-${WAVETOOLS_VERSION}-linux-x86_64.tar.gz" && \
|
||||
tar -xzf /tmp/wavetools.tar.gz -C /tmp && \
|
||||
cp -p /tmp/wavetools-${WAVETOOLS_VERSION}-linux-x86_64/wavediff /usr/local/bin/wavediff && \
|
||||
rm -rf /tmp/wavetools.tar.gz /tmp/wavetools-* /tmp/wavetools.conf
|
||||
|
||||
COPY build.sh /tmp/build.sh
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
WAVETOOLS_VERSION=v0.1.2
|
||||
|
|
@ -22,7 +22,7 @@ RUN apt-get update \
|
|||
help2man \
|
||||
libfl2 \
|
||||
libfl-dev \
|
||||
libgoogle-perftools-dev \
|
||||
libjemalloc-dev \
|
||||
numactl \
|
||||
perl \
|
||||
perl-doc \
|
||||
|
|
|
|||
59
configure.ac
59
configure.ac
|
|
@ -12,7 +12,7 @@
|
|||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.046 2026-02-28],
|
||||
AC_INIT([Verilator],[5.048 2026-04-26],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
|
|
@ -64,8 +64,8 @@ AC_ARG_ENABLE([dev-asan],
|
|||
[AS_HELP_STRING([--enable-dev-asan],
|
||||
[Enable compiling Verilator with ASAN
|
||||
AddressSanitizer for memory error detection.
|
||||
This disables tcmalloc. Does not affect
|
||||
Verilated models using ASAN.])],
|
||||
This disables tcmalloc and jemalloc. Does not
|
||||
affect Verilated models using ASAN.])],
|
||||
[case "${enableval}" in
|
||||
yes) CFG_WITH_DEV_ASAN=yes ;;
|
||||
no) CFG_WITH_DEV_ASAN=no ;;
|
||||
|
|
@ -93,6 +93,27 @@ else
|
|||
AC_MSG_RESULT($CFG_WITH_TCMALLOC)
|
||||
fi
|
||||
|
||||
# Flag to enable linking Verilator with jemalloc if available
|
||||
AC_MSG_CHECKING(whether to use jemalloc)
|
||||
AC_ARG_ENABLE([jemalloc],
|
||||
[AS_HELP_STRING([--enable-jemalloc],
|
||||
[Use libjemalloc for faster dynamic memory
|
||||
management in Verilator binary. Preferred over
|
||||
tcmalloc when both are available
|
||||
@<:@default=check@:>@])],
|
||||
[case "${enableval}" in
|
||||
yes) CFG_WITH_JEMALLOC=yes ;;
|
||||
no) CFG_WITH_JEMALLOC=no ;;
|
||||
*) AC_MSG_ERROR([bad value '${enableval}' for --enable-jemalloc]) ;;
|
||||
esac],
|
||||
[CFG_WITH_JEMALLOC=check;])
|
||||
if test "$CFG_WITH_DEV_ASAN" = "yes"; then
|
||||
CFG_WITH_JEMALLOC=no
|
||||
AC_MSG_RESULT("disabled by --enable-dev-asan")
|
||||
else
|
||||
AC_MSG_RESULT($CFG_WITH_JEMALLOC)
|
||||
fi
|
||||
|
||||
# Flag to enable code coverage build with gcov
|
||||
AC_MSG_CHECKING(whether to build for gcov code coverage collection)
|
||||
AC_ARG_ENABLE([dev-gcov],
|
||||
|
|
@ -473,6 +494,7 @@ fi
|
|||
AC_SUBST(HAVE_DEV_ASAN)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Qunused-arguments)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-shadow)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-unnecessary-virtual-specifier)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Wno-unused-parameter)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Xclang -fno-pch-timestamp)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-faligned-new)
|
||||
|
|
@ -532,6 +554,7 @@ m4_foreach([cflag],[
|
|||
[-Wno-tautological-bitwise-compare],
|
||||
[-Wno-tautological-compare],
|
||||
[-Wno-uninitialized],
|
||||
[-Wno-unnecessary-virtual-specifier],
|
||||
[-Wno-unused-but-set-parameter],
|
||||
[-Wno-unused-but-set-variable],
|
||||
[-Wno-unused-parameter],
|
||||
|
|
@ -567,8 +590,10 @@ if test "$CFG_ENABLE_PARTIAL_STATIC" = "yes"; then
|
|||
_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)
|
||||
|
|
@ -584,11 +609,38 @@ _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
|
||||
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)
|
||||
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])
|
||||
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
|
||||
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)
|
||||
|
|
@ -600,6 +652,7 @@ _MY_LDLIBS_CHECK_IFELSE(
|
|||
[if test "$CFG_WITH_TCMALLOC" = "yes"; then
|
||||
AC_MSG_ERROR([--enable-tcmalloc was given but test for ${LTCMALLOC} failed])
|
||||
fi])
|
||||
fi
|
||||
AC_SUBST(HAVE_TCMALLOC)
|
||||
AC_SUBST(CFG_LIBS)
|
||||
|
||||
|
|
|
|||
|
|
@ -58,8 +58,10 @@ Drew Ranck
|
|||
Drew Taussig
|
||||
Driss Hafdi
|
||||
Edgar E. Iglesias
|
||||
Eric Mejdrich
|
||||
Eric Müller
|
||||
Eric Rippey
|
||||
Eunseo Song
|
||||
Ethan Sifferman
|
||||
Eyck Jentzsch
|
||||
Fabian Keßler-Schulz
|
||||
|
|
@ -98,7 +100,9 @@ Iru Cai
|
|||
Ivan Vnučec
|
||||
Iztok Jeras
|
||||
Jake Merdich
|
||||
Jakub Michalski
|
||||
Jakub Wasilewski
|
||||
jalcim
|
||||
James Bailey
|
||||
James Hanlon
|
||||
James Hutchinson
|
||||
|
|
@ -133,6 +137,7 @@ Jose Loyola
|
|||
Josep Sans
|
||||
Joseph Nwabueze
|
||||
Josh Redford
|
||||
Julian Carrier
|
||||
Julian Daube
|
||||
Julie Schwartz
|
||||
Julien Margetts
|
||||
|
|
@ -160,9 +165,11 @@ Liam Braun
|
|||
Luca Colagrande
|
||||
Ludwig Rogiers
|
||||
Lukasz Dalek
|
||||
M2kar
|
||||
Maarten De Braekeleer
|
||||
Maciej Sobkowski
|
||||
Marcel Chang
|
||||
Marco Bartoli
|
||||
Marco Widmer
|
||||
Mariusz Glebocki
|
||||
Markus Krause
|
||||
|
|
@ -174,6 +181,7 @@ Mateusz Gancarz
|
|||
Matt Stroud
|
||||
Matthew Ballance
|
||||
Max Wipfli
|
||||
Maxim Fonarev
|
||||
Michael Bedford Taylor
|
||||
Michael Bikovitsky
|
||||
Michael Killough
|
||||
|
|
@ -191,6 +199,7 @@ Nathan Graybeal
|
|||
Nathan Kohagen
|
||||
Nathan Myers
|
||||
Nick Brereton
|
||||
Nikolay Puzanov
|
||||
Nolan Poe
|
||||
Oleh Maksymenko
|
||||
Patrick Stewart
|
||||
|
|
@ -214,6 +223,7 @@ Piotr Binkowski
|
|||
Qingyao Sun
|
||||
Quentin Corradi
|
||||
Rafal Kapuscik
|
||||
Rahul Behl
|
||||
Rasfunk
|
||||
Raynard Qiao
|
||||
Ricardo Barbedo
|
||||
|
|
@ -222,6 +232,7 @@ Risto Pejašinović
|
|||
Robert Balas
|
||||
Robin Heinemann
|
||||
Rodrigo Batista de Moraes
|
||||
Rowan Goemans
|
||||
Rupert Swarbrick
|
||||
Ryan Ziegler
|
||||
Ryszard Rozak
|
||||
|
|
@ -231,6 +242,7 @@ Sebastien Van Cauwenberghe
|
|||
Sergey Fedorov
|
||||
Sergi Granell
|
||||
Seth Pellegrino
|
||||
Shogo Yamazaki
|
||||
Shou-Li Hsu
|
||||
Srinivasan Venkataramanan
|
||||
Stefan Wallentowitz
|
||||
|
|
@ -251,6 +263,7 @@ Tom Manner
|
|||
Tomasz Gorochowik
|
||||
Topa Topino
|
||||
Toru Niina
|
||||
Tracy Narine
|
||||
Trung Nguyen
|
||||
Tudor Timi
|
||||
Tymoteusz Blazejczyk
|
||||
|
|
@ -291,3 +304,4 @@ em2machine
|
|||
emmettifelts
|
||||
Àlex Torregrosa
|
||||
Ícaro Lima
|
||||
Yogish Sekhar
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
.. comment: generated by t_lint_assigneqexpr_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 3,5
|
||||
|
||||
output logic c_o,
|
||||
output logic d_o
|
||||
);
|
||||
assign c_o = (a_i != 0) ? 1 : 0;
|
||||
assign d_o = // Note = not == below
|
||||
(
|
||||
e_o = 1 // <--- Warning: ASSIGNEQEXPR
|
||||
) ? 1 : (
|
||||
e_o = 0 // <--- Warning: ASSIGNEQEXPR
|
||||
) ? b_i : 0;
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
.. code-block::
|
||||
|
||||
-V{t#,#} 'stl' region trigger index 0 is active: @([hybrid] a)
|
||||
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 100 tries
|
||||
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 10000 tries
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
.. comment: generated by t_lint_didnotconverge_nodbg_bad
|
||||
.. code-block::
|
||||
|
||||
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 100 tries
|
||||
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 10000 tries
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
:emphasize-lines: 2
|
||||
|
||||
module t;
|
||||
sub sub(); // <--- Warning
|
||||
endmodule
|
||||
module sub
|
||||
(output port);
|
||||
sub sub (); // <--- Warning
|
||||
endmodule
|
||||
module sub (
|
||||
output port
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
.. comment: generated by t_lint_pinmissing_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-PINMISSING: example.v:1:8 Instance has missing pin: 'port'
|
||||
%Warning-PINMISSING: example.v:1:7 Instance has missing pin: 'port'
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ def setup(app):
|
|||
# -- Project information
|
||||
|
||||
project = 'Verilator'
|
||||
copyright = '2024 by Wilson Snyder, under LGPL-3.0 or Artistic-2.0'
|
||||
copyright = '2026 by Wilson Snyder, under LGPL-3.0 or Artistic-2.0'
|
||||
author = 'Wilson Snyder'
|
||||
|
||||
# The master toctree document.
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ Copyright
|
|||
|
||||
The latest version of Verilator is available from https://verilator.org.
|
||||
|
||||
Copyright 2003-2026 by Wilson Snyder. Verilator is free software; you
|
||||
can redistribute it and/or modify the Verilator internals under the terms
|
||||
of either the GNU Lesser General Public License Version 3 or the Perl
|
||||
Artistic License Version 2.0.
|
||||
Copyright 2003-2026 by Wilson Snyder. Verilator is free software; you can
|
||||
redistribute it and/or modify the Verilator internals under the terms of
|
||||
either the GNU Lesser General Public License Version 3 or the Perl Artistic
|
||||
License Version 2.0.
|
||||
|
||||
All Verilog and C++/SystemC code quoted within this documentation file is
|
||||
released as Creative Commons Public Domain (CC0). Many example files and
|
||||
|
|
|
|||
|
|
@ -16,3 +16,16 @@ C++14 compiler support
|
|||
(Although this date has expired, this change is currently on hold until
|
||||
the Ubuntu LTS versions of GCC and clang use C++20 by default, estimated
|
||||
May 2028.)
|
||||
|
||||
`--structs-packed` option
|
||||
The :vlopt:`--structs-packed` option was introduced when Verilator was
|
||||
first implementing unpacked structs. That feature has been stable now
|
||||
for multiple years, so :vlopt:`--structs-packed` should no longer be
|
||||
used. Thus :vlopt:`--structs-packed` will change to a no-operation flag
|
||||
and the related :option:`UNPACKED` warning will never be issued no
|
||||
sooner than September 2026.
|
||||
|
||||
tcmalloc support
|
||||
Verilator currently supports the default malloc, tcmalloc, or jemalloc.
|
||||
As jemalloc has better performance, support for tcmalloc may be removed
|
||||
no sooner than January 2027.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
.. _Environment:
|
||||
.. _environment:
|
||||
|
||||
Environment
|
||||
===========
|
||||
|
|
|
|||
|
|
@ -116,6 +116,12 @@ Options:
|
|||
simulation runtime random seed value. If zero or not specified picks a
|
||||
value from the system random number generator.
|
||||
|
||||
.. option:: +verilator+solver+file+<filename>
|
||||
|
||||
If specified, when the randomization solver is used, open the given
|
||||
filename for writing, and log all random solver commands and responses
|
||||
to it.
|
||||
|
||||
.. option:: +verilator+V
|
||||
|
||||
Shows the verbose version, including configuration information.
|
||||
|
|
|
|||
|
|
@ -262,15 +262,27 @@ Summary:
|
|||
limitation that allow only one precompiled header per compilation.
|
||||
Use this instead of ::vlopt:`-CFLAGS` with `-include <header-path>`.
|
||||
|
||||
.. option:: --constraint-array-limit <size>
|
||||
|
||||
Specifies the maximum array size for which array reduction methods
|
||||
(sum, product, and, or, xor) in constraint expressions will be
|
||||
expanded. Arrays larger than this limit will have their reduction
|
||||
constraints ignored with a `CONSTRAINTIGN` warning. This prevents
|
||||
excessive code generation for very large arrays.
|
||||
|
||||
Defaults to 64. Setting to 0 disables all array reduction constraint
|
||||
expansion.
|
||||
|
||||
.. option:: --converge-limit <loops>
|
||||
|
||||
Rarely needed. Specifies the maximum number of runtime iterations before
|
||||
creating a model failed to converge error. Defaults to 100.
|
||||
creating a model failed to converge error. Defaults to 10000.
|
||||
|
||||
.. option:: --coverage
|
||||
|
||||
Enables all forms of coverage, an alias for :vlopt:`--coverage-line`
|
||||
:vlopt:`--coverage-toggle` :vlopt:`--coverage-expr` :vlopt:`--coverage-user`.
|
||||
:vlopt:`--coverage-toggle` :vlopt:`--coverage-expr` :vlopt:`--coverage-fsm`
|
||||
:vlopt:`--coverage-user`.
|
||||
|
||||
.. option:: --coverage-expr
|
||||
|
||||
|
|
@ -282,6 +294,10 @@ Summary:
|
|||
covered for a given expression. Defaults to 32. Increasing may slow
|
||||
coverage simulations and make analyzing the results unwieldy.
|
||||
|
||||
.. option:: --coverage-fsm
|
||||
|
||||
Enables native FSM state and arc coverage. See :ref:`FSM Coverage`.
|
||||
|
||||
.. option:: --coverage-line
|
||||
|
||||
Enables basic block line coverage analysis. See :ref:`Line Coverage`.
|
||||
|
|
@ -467,6 +483,10 @@ Summary:
|
|||
Rarely needed. Enable dumping DfgGraph .dot debug files with dumping
|
||||
level 3.
|
||||
|
||||
.. option:: --dump-dfg-patterns
|
||||
|
||||
Rarely needed. Enable dumping DfgGraph pattern statistics.
|
||||
|
||||
.. option:: --dump-graph
|
||||
|
||||
Rarely needed. Enable dumping V3Graph .dot debug files with dumping
|
||||
|
|
@ -638,9 +658,12 @@ Summary:
|
|||
|
||||
.. option:: -fno-dfg
|
||||
|
||||
Rarely needed. Disable all use of the DFG-based combinational logic
|
||||
optimizer. Alias for :vlopt:`-fno-dfg-pre-inline`,
|
||||
:vlopt:`-fno-dfg-post-inline` and :vlopt:`-fno-dfg-scoped`.
|
||||
Rarely needed. Disable the DFG-based combinational logic optimizer.
|
||||
|
||||
In versions before 5.048:
|
||||
|
||||
Alias for :vlopt:`-fno-dfg-pre-inline`, :vlopt:`-fno-dfg-post-inline` and
|
||||
:vlopt:`-fno-dfg-scoped`.
|
||||
|
||||
.. option:: -fno-dfg-break-cycles
|
||||
|
||||
|
|
@ -656,11 +679,15 @@ Summary:
|
|||
|
||||
.. option:: -fno-dfg-post-inline
|
||||
|
||||
Rarely needed. Do not apply the DFG optimizer after inlining.
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions before 5.048: Do not apply the DFG optimizer after inlining.
|
||||
|
||||
.. option:: -fno-dfg-pre-inline
|
||||
|
||||
Rarely needed. Do not apply the DFG optimizer before inlining.
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions before 5.048: Do not apply the DFG optimizer before inlining.
|
||||
|
||||
.. option:: -fno-dfg-push-down-sels
|
||||
|
||||
|
|
@ -668,7 +695,10 @@ Summary:
|
|||
|
||||
.. option:: -fno-dfg-scoped
|
||||
|
||||
Rarely needed. Do not apply the DFG optimizer across module scopes.
|
||||
Deprecated; use :vlopt:`-fno-dfg` instead.
|
||||
|
||||
In versions before 5.048: Do not apply the DFG optimizer across module
|
||||
scopes.
|
||||
|
||||
.. option:: -fno-expand
|
||||
|
||||
|
|
@ -694,6 +724,8 @@ Summary:
|
|||
|
||||
.. option:: -fno-life-post
|
||||
|
||||
.. option:: -fno-lift-expr
|
||||
|
||||
.. option:: -fno-localize
|
||||
|
||||
.. option:: -fno-merge-cond
|
||||
|
|
@ -731,6 +763,14 @@ Summary:
|
|||
Rarely needed. Set the maximum array size (number of elements) for slice
|
||||
optimization to avoid excessive memory usage.
|
||||
|
||||
.. option:: --func-recursion-depth <value>
|
||||
|
||||
Specifies the maximum depth of recursive constant function evaluation.
|
||||
When a recursive function exceeds this depth during constant folding,
|
||||
Verilator gives up and treats the expression as non-constant.
|
||||
|
||||
Defaults to 1000.
|
||||
|
||||
.. option:: -future0 <option>
|
||||
|
||||
Rarely needed. Suppress an unknown Verilator option for an option that
|
||||
|
|
@ -1661,6 +1701,8 @@ Summary:
|
|||
|
||||
.. option:: --structs-packed
|
||||
|
||||
Deprecated; discontinue use of this option.
|
||||
|
||||
Converts all unpacked structures to packed structures, and issues an
|
||||
:option:`UNPACKED` warning. Specifying this option allows for backward
|
||||
compatibility with versions before Verilator 5.006, when Verilator would
|
||||
|
|
@ -1792,7 +1834,7 @@ Summary:
|
|||
.. option:: --trace-fst
|
||||
|
||||
Enable FST waveform tracing in the model. This overrides
|
||||
:vlopt:`--trace`. See also :vlopt:`--trace-threads` option.
|
||||
:vlopt:`--trace`.
|
||||
|
||||
.. option:: --trace-max-array <depth>
|
||||
|
||||
|
|
@ -1826,6 +1868,10 @@ Summary:
|
|||
|
||||
.. option:: --trace-threads <threads>
|
||||
|
||||
Deprecated and has no effect.
|
||||
|
||||
In versions before 5.048:
|
||||
|
||||
Enable waveform tracing using separate threads. This is typically faster
|
||||
in simulation runtime but uses more total compute. This option only
|
||||
applies to :vlopt:`--trace-fst`. FST tracing can utilize at most
|
||||
|
|
|
|||
|
|
@ -129,13 +129,20 @@ verilator_coverage Arguments
|
|||
.. option:: --filter-type <regex>
|
||||
|
||||
Skips records of coverage types that matches with <regex>
|
||||
Possible values are `toggle`, `line`, `branch`, `expr`, `user` and
|
||||
a wildcard with `\*` or `?`. The default value is `\*`.
|
||||
Possible values are `toggle`, `line`, `branch`, `expr`, `user`,
|
||||
`fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The default
|
||||
value is `\*`.
|
||||
|
||||
.. option:: --help
|
||||
|
||||
Displays a help summary, the program version, and exits.
|
||||
|
||||
.. option:: --include-reset-arcs
|
||||
|
||||
Includes FSM reset arcs in the printed summaries and annotated output.
|
||||
By default, reset arcs are tracked but summarized separately from the
|
||||
non-reset FSM arcs.
|
||||
|
||||
.. option:: --rank
|
||||
|
||||
Prints an experimental report listing the relative importance of each
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ To build using MSVC:
|
|||
.. code-block:: bash
|
||||
|
||||
cd verilator # directory containing source files of verilator
|
||||
mkdir build
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release --install-prefix $PWD/../install
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix $PWD/../install
|
||||
|
|
@ -50,7 +51,8 @@ To build using ninja:
|
|||
.. code-block:: bash
|
||||
|
||||
cd verilator
|
||||
mkdir build
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake -G Ninja .. -DCMAKE_BUILD_TYPE=Release --install-prefix $PWD/../install -DCMAKE_MAKE_PROGRAM=<path to ninja binary> -DBISON_EXECUTABLE=<path to bison> -DFLEX_EXECUTABLE=<path to flex>
|
||||
<path to ninja binary> #execute ninja
|
||||
cmake --install . --prefix $PWD/../install
|
||||
|
|
@ -71,7 +73,7 @@ Example
|
|||
|
||||
cd verilator/examples
|
||||
cd cmake_hello_c
|
||||
mkdir build
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. # cmake -G Ninja ..
|
||||
cmake --build . --config Release # ninja
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ In brief, to install from git:
|
|||
|
||||
# Prerequisites:
|
||||
#sudo apt-get install git help2man perl python3 make autoconf g++ flex bison ccache
|
||||
#sudo apt-get install libgoogle-perftools-dev numactl perl-doc
|
||||
#sudo apt-get install libgoogle-perftools-dev libjemalloc-dev numactl perl-doc
|
||||
#sudo apt-get install libfl2 # Ubuntu only (ignore if gives error)
|
||||
#sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error)
|
||||
#sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
|
||||
|
|
@ -144,7 +144,7 @@ installed for good performance:
|
|||
|
||||
sudo apt-get install ccache # If present at build, needed for run
|
||||
sudo apt-get install mold # If present at build, needed for run
|
||||
sudo apt-get install libgoogle-perftools-dev numactl
|
||||
sudo apt-get install libjemalloc-dev numactl
|
||||
|
||||
To build Verilator you will need to install these packages; these do not
|
||||
need to be present to run Verilator:
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ SystemVerilog code coverage. With :vlopt:`--coverage`, Verilator enables
|
|||
all forms of coverage:
|
||||
|
||||
- :ref:`User Coverage`
|
||||
- :ref:`FSM Coverage`
|
||||
- :ref:`Line Coverage`
|
||||
- :ref:`Toggle Coverage`
|
||||
|
||||
|
|
@ -208,6 +209,47 @@ point under the coverage name "DefaultClock":
|
|||
|
||||
DefaultClock: cover property (@(posedge clk) cyc==3);
|
||||
|
||||
.. _fsm coverage:
|
||||
|
||||
FSM Coverage
|
||||
------------
|
||||
|
||||
With :vlopt:`--coverage` or :vlopt:`--coverage-fsm`, Verilator can
|
||||
instrument a conservative subset of single-process FSMs and report both
|
||||
state coverage (`fsm_state`) and transition coverage (`fsm_arc`).
|
||||
|
||||
This feature is currently experimental and might change in subsequent
|
||||
releases. In particular, the native FSM coverage extraction heuristics,
|
||||
:vlopt:`--coverage-fsm`, and the Verilator-specific FSM metacomments below
|
||||
should be treated as subject to change while the interface settles.
|
||||
|
||||
FSM extraction is intentionally narrow. The current implementation targets
|
||||
clocked, enum-driven state machines that can be recovered directly from the
|
||||
RTL. It does not claim broad support for two-process FSMs, one-hot
|
||||
inference, helper-function next-state recovery, or deeply nested control
|
||||
recovery.
|
||||
|
||||
The following metacomments may be attached to the state variable to steer
|
||||
the extracted coverage model:
|
||||
|
||||
- ``/*verilator fsm_state*/`` forces the variable to be treated as
|
||||
FSM state.
|
||||
- ``/*verilator fsm_reset_arc*/`` marks reset transitions as
|
||||
user-visible reset arcs instead of defaulting to a hidden reset-only
|
||||
summary.
|
||||
- ``/*verilator fsm_arc_include_cond*/`` keeps conditional branch
|
||||
arcs that would otherwise be skipped by the conservative extractor.
|
||||
|
||||
Reset transitions are included in the collected data either way. By
|
||||
default, :command:`verilator_coverage` summarizes reset-only arcs rather
|
||||
than printing them alongside non-reset arcs. Use
|
||||
:option:`verilator_coverage --include-reset-arcs` to include those arcs in
|
||||
the printed summary and annotated output.
|
||||
|
||||
Annotated output produced by :command:`verilator_coverage --annotate` will
|
||||
label FSM points with `fsm_state` and `fsm_arc`, and synthetic fallback
|
||||
transitions with `SYNTHETIC DEFAULT ARC`.
|
||||
|
||||
|
||||
.. _line coverage:
|
||||
|
||||
|
|
|
|||
|
|
@ -262,23 +262,6 @@ construction is parallelized using the same number of threads as specified
|
|||
with :vlopt:`--threads`, and is executed on the same thread pool as the
|
||||
model.
|
||||
|
||||
The :vlopt:`--trace-threads` options can be used with :vlopt:`--trace-fst`
|
||||
to offload FST tracing using multiple threads. If :vlopt:`--trace-threads`
|
||||
is given without :vlopt:`--threads`, then :vlopt:`--trace-threads` will
|
||||
imply :vlopt:`--threads 1 <--threads>`, i.e., the support libraries will be
|
||||
thread safe.
|
||||
|
||||
With :vlopt:`--trace-threads 0 <--trace-threads>`, trace dumps are produced
|
||||
on the main thread. This again gives the highest single-thread performance.
|
||||
|
||||
With :vlopt:`--trace-threads {N} <--trace-threads>`, where N is at least 1,
|
||||
up to N additional threads will be created and managed by the trace files
|
||||
(e.g., VerilatedFstC), to offload construction of the trace dump. The main
|
||||
thread will be released to proceed with execution as soon as possible,
|
||||
though some main thread blocking is still necessary while capturing the
|
||||
trace. FST tracing can utilize up to 2 offload threads, so there is no use
|
||||
of setting :vlopt:`--trace-threads` higher than 2 at the moment.
|
||||
|
||||
When running a multithreaded model, the default Linux task scheduler often
|
||||
works against the model by assuming short-lived threads and thus it often
|
||||
schedules threads using multiple hyperthreads within the same physical
|
||||
|
|
@ -286,15 +269,14 @@ core. If there is no affinity already set, on Linux only, Verilator
|
|||
attempts to set thread-to-processor affinity in a reasonable way.
|
||||
|
||||
Some newer Linux kernels handle thread assignment well. If running
|
||||
Verilator on such a system, automatic thread affinity may not be
|
||||
beneficial and may even reduce performance. In this case, environment
|
||||
variable :vlopt:`VERILATOR_NUMA_STRATEGY` may be set to ``none`` to
|
||||
disable automatic thread affinity. For more information, refer to
|
||||
Verilator on such a system, automatic thread affinity may not be beneficial
|
||||
and may even reduce performance. In this case, environment variable
|
||||
:vlopt:`VERILATOR_NUMA_STRATEGY` may be set to ``none`` to disable
|
||||
automatic thread affinity. For more information, refer to
|
||||
:ref:`Environment`.
|
||||
|
||||
For best performance, use the :command:`numactl` program to (when the
|
||||
threading count fits) select unique physical cores on the same socket. The
|
||||
same applies for :vlopt:`--trace-threads` as well.
|
||||
threading count fits) select unique physical cores on the same socket.
|
||||
|
||||
As an example, if a model was Verilated with :vlopt:`--threads 4
|
||||
<--threads>`, we consult:
|
||||
|
|
@ -521,7 +503,10 @@ include directories and link to the SystemC libraries.
|
|||
|
||||
.. describe:: TRACE_THREADS
|
||||
|
||||
Optional. Enable multithreaded FST trace; see :vlopt:`--trace-threads`.
|
||||
Deprecated and has no effect.
|
||||
|
||||
In versions before 5.048: Optional. Enable multithreaded FST trace; see
|
||||
:vlopt:`--trace-threads`.
|
||||
|
||||
.. describe:: TRACE_VCD
|
||||
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ List Of Warnings
|
|||
else
|
||||
array[address] <= data;
|
||||
|
||||
While this is supported in typical synthesizeable code (including the
|
||||
While this is supported in typical synthesizable code (including the
|
||||
example above), some complicated cases are not supported. Namely:
|
||||
|
||||
1. If the above loop is inside a suspendable process or fork statement.
|
||||
|
|
@ -837,6 +837,18 @@ List Of Warnings
|
|||
with a newline."
|
||||
|
||||
|
||||
.. option:: FSMMULTI
|
||||
|
||||
Warns that the same always block contains multiple enum-typed case
|
||||
statements that look like FSM candidates for native FSM coverage when
|
||||
:vlopt:`--coverage-fsm` or :vlopt:`--coverage` is enabled.
|
||||
|
||||
Verilator's FSM coverage instruments only the first such candidate in
|
||||
source order. Split the FSMs into separate always blocks, or explicitly
|
||||
annotate the intended state variables and restructure the RTL for full
|
||||
coverage of such multiple state machines.
|
||||
|
||||
|
||||
.. option:: FUNCTIMECTL
|
||||
|
||||
Error that a function contains a time-controlling statement or call of a
|
||||
|
|
@ -856,6 +868,11 @@ List Of Warnings
|
|||
pass.
|
||||
|
||||
|
||||
.. option:: FUTURE
|
||||
|
||||
Warns that a feature is under development and not yet supported.
|
||||
|
||||
|
||||
.. option:: GENCLK
|
||||
|
||||
Historical, never issued since version 5.000.
|
||||
|
|
@ -1519,7 +1536,12 @@ List Of Warnings
|
|||
ANSI-style `#(...)` declarations. IEEE 1800-2023 6.20.1 requires this
|
||||
error, but some simulators accept this syntax.
|
||||
|
||||
Faulty example:
|
||||
Also issued with ANSI format where a parameter without default is
|
||||
present in the top-level module, and as such Verilator cannot know how
|
||||
to process that module. For such cases suggest adding a default so the
|
||||
module can lint cleanly.
|
||||
|
||||
Faulty non-ANSI example:
|
||||
|
||||
.. include:: ../../docs/gen/ex_PARAMNODEFAULT_faulty.rst
|
||||
|
||||
|
|
@ -1529,8 +1551,8 @@ List Of Warnings
|
|||
|
||||
To fix the issue, move to an ANSI-style declaration.
|
||||
|
||||
Suppressing this error will only suppress the IEEE-required check; it
|
||||
will simulate correctly.
|
||||
For the non-ANSI case, suppressing this error will only suppress the
|
||||
IEEE-required check; it will simulate correctly.
|
||||
|
||||
|
||||
.. option:: PINCONNECTEMPTY
|
||||
|
|
@ -1850,8 +1872,12 @@ List Of Warnings
|
|||
|
||||
and #(1,2,3) AND (out, a, b);
|
||||
|
||||
Warns that rising, falling, and turn-off delays are currently unsupported.
|
||||
The first (rising) delay is used for all cases.
|
||||
Warns that the third (turn-off) delay is currently unsupported and is
|
||||
ignored. Rising and falling delays are supported.
|
||||
|
||||
In versions before 5.048, warned that rising, falling, and turn-off
|
||||
delays were unsupported. The first (rising) delay was used for all
|
||||
cases.
|
||||
|
||||
|
||||
.. option:: SELRANGE
|
||||
|
|
@ -2592,11 +2618,11 @@ List Of Warnings
|
|||
Since version 5.046:
|
||||
|
||||
Issued if neither :vlopt:`--sched-zero-delay`, nor
|
||||
:vlopt:`--sched-zero-delay` is used on the command line, and the input does
|
||||
not contain a compile time known ``#0`` delay, but does contain a
|
||||
``#(expressin)`` where the delay value cannot be determined at compile time.
|
||||
Passing :vlopt:`--no-sched-zero-delay` can improve runtime performance if
|
||||
variable delays are all known to be non-zero at runtime.
|
||||
:vlopt:`--sched-zero-delay` is used on the command line, and the input
|
||||
does not contain a compile time known ``#0`` delay, but does contain a
|
||||
``#(expression)`` where the delay value cannot be determined at compile
|
||||
time. Passing :vlopt:`--no-sched-zero-delay` can improve runtime
|
||||
performance if variable delays are all known to be non-zero at runtime.
|
||||
|
||||
Also issued if :vlopt:`--no-sched-zero-delay` is used on the command line,
|
||||
but the input contains a compile time known ``#0`` delay. This is safe to
|
||||
|
|
|
|||
|
|
@ -594,16 +594,15 @@ object.
|
|||
This class manages processes that await events (triggers). There is one
|
||||
such object per each trigger awaited by coroutines. Coroutines ``co_await``
|
||||
this object's ``trigger`` function. They are stored in three stages -
|
||||
`awaiting`, `fired` and `toResume`. First, they land in the `awaiting` stage, and
|
||||
cannot be resumed. The ``ready`` function moves all coroutines from the
|
||||
`awaiting` stage into the `fired` stage. The ``moveToResumeQueue`` function moves
|
||||
`fired` coroutines into `toResume`. Finally, function `resume` resumes
|
||||
all coroutines from the `toResume` stage.
|
||||
`awaiting`, `fired` and `toResume`. First, they land in the `awaiting`
|
||||
stage, and cannot be resumed. The ``ready`` function moves all coroutines
|
||||
from the `awaiting` stage into the `fired` stage. The ``moveToResumeQueue``
|
||||
function moves `fired` coroutines into `toResume`. Finally, function
|
||||
`resume` resumes all coroutines from the `toResume` stage.
|
||||
|
||||
This split is done to avoid self-triggering, triggering coroutines
|
||||
multiple times and triggering coroutines in the same iteration
|
||||
they were suspended. See the `Scheduling with timing` section
|
||||
for details on how this is used.
|
||||
This split is done to avoid self-triggering, triggering coroutines multiple
|
||||
times and triggering coroutines in the same iteration they were suspended.
|
||||
See the `Scheduling with timing` section for details on how this is used.
|
||||
|
||||
``VlDynamicTriggerScheduler``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
@ -721,15 +720,15 @@ in that process. When ordering code using ``V3Order``, these triggers are
|
|||
provided as external domains of these variables. This ensures that the
|
||||
necessary combinational logic is triggered after a coroutine resumption.
|
||||
|
||||
Every call to a `VlTriggerScheduler`'s `trigger()` method is preempt by
|
||||
a call to a proper `__VbeforeTrig` function which evaluates all the necessary
|
||||
triggers so, the information about order of suspension/resumption is not lost.
|
||||
The triggers necessary to evaluate are ones dependent on the same events
|
||||
as the `trigger()` - e.g.: if `triggers()` awaits for event `a` or `b`, then
|
||||
every trigger that depends on any of those shall be evaluated. If they wouldn't
|
||||
be evaluated and next coroutine after resumption would fire the event `a` then
|
||||
it is impossible to get to know whether await or fire on event `a` was called
|
||||
first - which is necessary to know.
|
||||
Every call to a `VlTriggerScheduler`'s `trigger()` method is preempt by a
|
||||
call to a proper `__VbeforeTrig` function which evaluates all the necessary
|
||||
triggers so, the information about order of suspension/resumption is not
|
||||
lost. The triggers necessary to evaluate are ones dependent on the same
|
||||
events as the `trigger()` - e.g.: if `triggers()` awaits for event `a` or
|
||||
`b`, then every trigger that depends on any of those shall be evaluated. If
|
||||
they wouldn't be evaluated and next coroutine after resumption would fire
|
||||
the event `a` then it is impossible to get to know whether await or fire on
|
||||
event `a` was called first - which is necessary to know.
|
||||
|
||||
There are two functions for managing timing logic called by ``_eval()``:
|
||||
|
||||
|
|
@ -739,12 +738,13 @@ There are two functions for managing timing logic called by ``_eval()``:
|
|||
schedulers whose triggers were set in the current iteration.
|
||||
|
||||
Thanks to this separation a coroutine:
|
||||
|
||||
* awaiting a trigger cannot be suspended and resumed in the same iteration
|
||||
(``test_regress/t/t_timing_eval_act.v``) - which is necessary to make
|
||||
Verilator more predictable; this is the reason for introduction of 3rd stage
|
||||
in `VlTriggerScheduler` and thanks to this it is guaranteed that downstream
|
||||
logic will be evaluated before resumption (assuming that the coroutine wasn't
|
||||
already triggered in previous iteration);
|
||||
Verilator more predictable; this is the reason for introduction of 3rd
|
||||
stage in `VlTriggerScheduler` and thanks to this it is guaranteed that
|
||||
downstream logic will be evaluated before resumption (assuming that the
|
||||
coroutine wasn't already triggered in previous iteration);
|
||||
* cannot be resumed before it is suspended -
|
||||
``test_regress/t/t_event_control_double_excessive.v``;
|
||||
* firing cannot cannot be lost
|
||||
|
|
@ -1568,8 +1568,8 @@ For all tests to pass, you must install the following packages:
|
|||
|
||||
- SystemC to compile the SystemC outputs, see https://systemc.org
|
||||
|
||||
- vcddiff to find differences in VCD outputs. See the readme at
|
||||
https://github.com/veripool/vcddiff
|
||||
- wavediff to find differences in waveform outputs. See the readme at
|
||||
https://github.com/hudson-trading/wavetools
|
||||
|
||||
- Cmake for build paths that use it.
|
||||
|
||||
|
|
@ -1843,33 +1843,25 @@ algorithmic stage. An example:
|
|||
The following summarizes the above example dump, with more detail on each
|
||||
field in the section below.
|
||||
|
||||
+---------------+--------------------------------------------------------+
|
||||
| ``1:2:`` | The hierarchy of the ``VAR`` is the ``op2p`` |
|
||||
| | pointer under the ``MODULE``, which in turn is the |
|
||||
| | ``op1p`` pointer under the ``NETLIST``. |
|
||||
+---------------+--------------------------------------------------------+
|
||||
| ``VAR`` | The AstNodeType (e.g. ``AstVar``). |
|
||||
+---------------+--------------------------------------------------------+
|
||||
| ``0x91a780`` | Address of this node. |
|
||||
+---------------+--------------------------------------------------------+
|
||||
| ``<e74>`` | The 74th edit to the netlist was the last |
|
||||
| | modification to this node. |
|
||||
+---------------+--------------------------------------------------------+
|
||||
| ``{a22ah}`` | This node is related to the source filename |
|
||||
| | "a", where "a" is the first file read, "z" the 26th, |
|
||||
| | and "aa" the 27th. Then line 22 in that file, then |
|
||||
| | column 8 (aa=0, az=25, ba=26, ...). |
|
||||
+---------------+--------------------------------------------------------+
|
||||
| ``@dt=0x...`` | The address of the data type this node references. |
|
||||
+---------------+--------------------------------------------------------+
|
||||
| ``w32`` | The data-type width() is 32 bits. |
|
||||
+---------------+--------------------------------------------------------+
|
||||
| ``out_wide`` | The name() of the node, in this case, the name of the |
|
||||
| | variable. |
|
||||
+---------------+--------------------------------------------------------+
|
||||
| ``[O]`` | Flags which vary with the type of node, in this |
|
||||
| | case of a VAR, it means the variable is an output. |
|
||||
+---------------+--------------------------------------------------------+
|
||||
============= =============================================================
|
||||
``1:2:`` The hierarchy of the ``VAR`` is the ``op2p`` pointer under
|
||||
the ``MODULE``, which in turn is the ``op1p`` pointer under
|
||||
the ``NETLIST``.
|
||||
``VAR`` The AstNodeType (e.g. ``AstVar``).
|
||||
``0x91a780`` Address of this node.
|
||||
``<e74>`` The 74th edit to the netlist was the last modification to
|
||||
this node.
|
||||
``{a22ah}`` This node is related to the source filename "a", where "a" is
|
||||
the first file read, "z" the 26th, and "aa" the 27th. Then
|
||||
line 22 in that file, then column 8 (aa=0, az=25, ba=26,
|
||||
...).
|
||||
``@dt=0x...`` The address of the data type this node references.
|
||||
``w32`` The data-type width() is 32 bits.
|
||||
``out_wide`` The name() of the node, in this case, the name of the
|
||||
variable.
|
||||
``[O]`` Flags which vary with the type of node, in this case of a
|
||||
VAR, it means the variable is an output.
|
||||
============= =============================================================
|
||||
|
||||
In more detail, the following fields are dumped common to all nodes. They
|
||||
are produced by the ``AstNode::dump()`` method:
|
||||
|
|
@ -2141,6 +2133,49 @@ backtrace. You will typically see a frame sequence something like:
|
|||
visit()
|
||||
...
|
||||
|
||||
Bisecting bad transformations
|
||||
-----------------------------
|
||||
|
||||
If a bad transformation in the internals of Verilator causes a failure only
|
||||
at runtime, it can be found fairly automatically by only applying the
|
||||
transform a limited number of times, then performing a bisection search
|
||||
over the limit to pinpoint the exact transformation that introduces the
|
||||
failure.
|
||||
|
||||
To facilitate this an instance of the ``V3DebugBisect`` class can be used
|
||||
in conjunction with the ``verilator_bisect`` script.
|
||||
|
||||
In the offending algorithm, create a static instance of ``V3DebugBisect``:
|
||||
|
||||
::
|
||||
|
||||
static V3DebugBisect s_debugBisect{"TransformName"};
|
||||
|
||||
Call the ``stop`` method before applying a transformation, and do not
|
||||
proceed if it returns ``false``. Then use ``verilator_bisect`` to search an
|
||||
interval of values. You need to provide an arbitrary discriminator command,
|
||||
this should run Verilator, then any necessary checks (e.g.: simulation) to
|
||||
detect that the failure is still present. It should exit with a non-zero
|
||||
status if the failure is still present. The discriminator command can
|
||||
otherwise be arbitrarily complex, the actual search limit is passed via
|
||||
environment variables. E.g.:
|
||||
|
||||
::
|
||||
|
||||
bin/verilator_bisect DfgPeephole 0 1000 test_regress/t/t_myothertest.py
|
||||
|
||||
An additional command can be run before the discriminator command. E.g.
|
||||
this will run RTLMeter, but first removes its working directory so the
|
||||
models are recompiled on every step:
|
||||
|
||||
::
|
||||
|
||||
bin/verilator_bisect --pre "rm -rf work-bisect" DfgPeephole 0 10000000 \
|
||||
rtlmeter run --cases "..." --workRoot=work-bisect
|
||||
|
||||
When the bisection ends, the first value that makes the discriminator
|
||||
command fail is printed, which identifies the exact offending application
|
||||
of the transform.
|
||||
|
||||
Adding a New Feature
|
||||
====================
|
||||
|
|
@ -2662,6 +2697,7 @@ the terms of either the GNU Lesser General Public License Version 3 or the
|
|||
Perl Artistic License Version 2.0.
|
||||
|
||||
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
.. |Logo| image:: https://www.veripool.org/img/verilator_256_200_min.png
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@ Muhlestein
|
|||
Multithreaded
|
||||
Multithreading
|
||||
Mykyta
|
||||
NFA
|
||||
NOUNOPTFLAT
|
||||
NaN
|
||||
Nalbantis
|
||||
|
|
@ -589,6 +590,7 @@ al
|
|||
ala
|
||||
alejandro
|
||||
algrobman
|
||||
allocator
|
||||
andit
|
||||
ar
|
||||
architected
|
||||
|
|
@ -810,6 +812,7 @@ fs
|
|||
fscanf
|
||||
fseek
|
||||
fsiegle
|
||||
fsm
|
||||
fst
|
||||
fstrict
|
||||
ftell
|
||||
|
|
@ -834,8 +837,10 @@ glibc
|
|||
gmake
|
||||
gmon
|
||||
gotFinish
|
||||
goto
|
||||
gprof
|
||||
gtkwave
|
||||
hdl
|
||||
hdr
|
||||
hdzhangdoc
|
||||
hh
|
||||
|
|
@ -878,6 +883,7 @@ iostream
|
|||
ish
|
||||
isunbounded
|
||||
isunknown
|
||||
jemalloc
|
||||
jobserver
|
||||
json
|
||||
jwoutersymatra
|
||||
|
|
@ -891,6 +897,7 @@ len
|
|||
libc
|
||||
libext
|
||||
libgoogle
|
||||
libjemalloc
|
||||
liblist
|
||||
libsystemc
|
||||
libtcmalloc
|
||||
|
|
@ -917,6 +924,7 @@ lxt
|
|||
macromodule
|
||||
makefile
|
||||
makefiles
|
||||
malloc
|
||||
manpages
|
||||
metacomment
|
||||
metacomments
|
||||
|
|
@ -979,6 +987,7 @@ onehot
|
|||
ooo
|
||||
oprofile
|
||||
ortegon
|
||||
output
|
||||
oversized
|
||||
oversubscription
|
||||
parallelized
|
||||
|
|
@ -1067,12 +1076,14 @@ recrem
|
|||
recurse
|
||||
recurses
|
||||
redeclaring
|
||||
reentrant
|
||||
regs
|
||||
reloop
|
||||
replaceShiftOp
|
||||
reproducibility
|
||||
resetall
|
||||
respecified
|
||||
reusability
|
||||
rodata
|
||||
rolloverSize
|
||||
rr
|
||||
|
|
@ -1146,6 +1157,7 @@ sys
|
|||
systemc
|
||||
taskify
|
||||
tcmalloc
|
||||
tcmalloc
|
||||
tenghtt
|
||||
testbench
|
||||
threadsafe
|
||||
|
|
@ -1206,6 +1218,7 @@ upcasting
|
|||
urandom
|
||||
uselib
|
||||
utimes
|
||||
uvm
|
||||
uwire
|
||||
uwires
|
||||
valgrind
|
||||
|
|
@ -1239,6 +1252,7 @@ vpiConstType
|
|||
vpiDefName
|
||||
vpiInertialDelay
|
||||
vpiInstance
|
||||
vpiIntVal
|
||||
vpiLeftRange
|
||||
vpiModule
|
||||
vpiSigned
|
||||
|
|
@ -1251,6 +1265,7 @@ vpm
|
|||
vpp
|
||||
warmup
|
||||
wavealloca
|
||||
wavediff
|
||||
waveforms
|
||||
whitespace
|
||||
widthed
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2023 Tony Bybell.
|
||||
* Copyright (c) 2009-2026 Tony Bybell.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
|
|
@ -3203,7 +3203,8 @@ static const char *modtypes[] = {"module",
|
|||
"vhdl_for_generate",
|
||||
"vhdl_if_generate",
|
||||
"vhdl_generate",
|
||||
"vhdl_package"};
|
||||
"vhdl_package",
|
||||
"sv_array"};
|
||||
|
||||
static const char *attrtypes[] = {"misc", "array", "enum", "class"};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2018 Tony Bybell.
|
||||
* Copyright (c) 2009-2026 Tony Bybell.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
|
|
@ -108,7 +108,8 @@ enum fstScopeType
|
|||
FST_ST_VHDL_GENERATE = 20,
|
||||
FST_ST_VHDL_PACKAGE = 21,
|
||||
|
||||
FST_ST_MAX = 21,
|
||||
FST_ST_SV_ARRAY = 22,
|
||||
FST_ST_MAX = 22,
|
||||
|
||||
FST_ST_GEN_ATTRBEGIN = 252,
|
||||
FST_ST_GEN_ATTREND = 253,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -392,7 +392,7 @@ protected:
|
|||
int m_errorLimit = 1; // Stop on error number
|
||||
int m_randReset = 0; // Random reset: 0=all 0s, 1=all 1s, 2=random
|
||||
int m_randSeed = 0; // Random seed: 0=random
|
||||
enum { UNITS_NONE = 99 }; // Default based on precision
|
||||
static constexpr int UNITS_NONE = 99; // Default based on precision
|
||||
int m_timeFormatUnits = UNITS_NONE; // $timeformat units
|
||||
int m_timeFormatPrecision = 0; // $timeformat number of decimal places
|
||||
int m_timeFormatWidth = 20; // $timeformat character width
|
||||
|
|
@ -408,12 +408,14 @@ protected:
|
|||
struct NonSerialized final { // Non-serialized information
|
||||
// These are reloaded from on command-line settings, so do not need to persist
|
||||
// Fast path
|
||||
bool m_executingFinal = false; // Running generated final() code
|
||||
uint64_t m_profExecStart = 1; // +prof+exec+start time
|
||||
uint32_t m_profExecWindow = 2; // +prof+exec+window size
|
||||
// Slow path
|
||||
std::string m_coverageFilename; // +coverage+file filename
|
||||
std::string m_profExecFilename; // +prof+exec+file filename
|
||||
std::string m_profVltFilename; // +prof+vlt filename
|
||||
std::string m_solverLogFilename; // SMT solver log filename
|
||||
std::string m_solverProgram; // SMT solver program
|
||||
bool m_warnUnsatConstr = true; // Warn on unsatisfied constraints
|
||||
VlOs::DeltaCpuTime m_cpuTimeStart{false}; // CPU time, starts when create first model
|
||||
|
|
@ -530,12 +532,16 @@ public:
|
|||
bool gotFinish() const VL_MT_SAFE { return m_s.m_gotFinish; }
|
||||
/// Set if got a $finish or $stop/error
|
||||
void gotFinish(bool flag) VL_MT_SAFE;
|
||||
/// Check if generated final() code is executing
|
||||
bool executingFinal() const VL_MT_SAFE;
|
||||
/// Set if generated final() code is executing
|
||||
void executingFinal(bool flag) VL_MT_SAFE;
|
||||
/// Return if quiet enabled
|
||||
bool quiet() const VL_MT_SAFE { return m_s.m_quiet; }
|
||||
/// Enable quiet (also prevents need for OS calls to get CPU time)
|
||||
void quiet(bool flag) VL_MT_SAFE;
|
||||
/// Return randReset value
|
||||
int randReset() VL_MT_SAFE { return m_s.m_randReset; }
|
||||
int randReset() const VL_MT_SAFE { return m_s.m_randReset; }
|
||||
/// Select initial value of otherwise uninitialized signals.
|
||||
/// 0 = Set to zeros
|
||||
/// 1 = Set all bits to one
|
||||
|
|
@ -581,6 +587,8 @@ public:
|
|||
void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; }
|
||||
/// Advance current simulation time. See time() for side effect details
|
||||
void timeInc(uint64_t add) VL_MT_UNSAFE { m_s.m_time += add; }
|
||||
/// Return time as unit string
|
||||
std::string timeWithUnitString() const VL_MT_SAFE;
|
||||
/// Return time units as power-of-ten
|
||||
int timeunit() const VL_MT_SAFE { return -m_s.m_timeunit; }
|
||||
/// Set time units as power-of-ten
|
||||
|
|
@ -661,6 +669,9 @@ public:
|
|||
std::string profVltFilename() const VL_MT_SAFE;
|
||||
void profVltFilename(const std::string& flag) VL_MT_SAFE;
|
||||
|
||||
// Internal: Solver log filename
|
||||
std::string solverLogFilename() const VL_MT_SAFE;
|
||||
void solverLogFilename(const std::string& flag) VL_MT_SAFE;
|
||||
// Internal: SMT solver program
|
||||
std::string solverProgram() const VL_MT_SAFE;
|
||||
void solverProgram(const std::string& flag) VL_MT_SAFE;
|
||||
|
|
@ -731,8 +742,13 @@ public: // But internals only - called from verilated modules, VerilatedSyms
|
|||
~VerilatedScope();
|
||||
|
||||
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
|
||||
void varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype,
|
||||
int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE;
|
||||
VerilatedVar* varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype,
|
||||
int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE;
|
||||
VerilatedVar* forceableVarInsert(const char* namep, void* datap, bool isParam,
|
||||
VerilatedVarType vltype, int vlflags,
|
||||
void* forceReadSignalData, const char* forceReadSignalName,
|
||||
std::pair<VerilatedVar*, VerilatedVar*> forceControlSignals,
|
||||
int udims, int pdims...) VL_MT_UNSAFE;
|
||||
// ACCESSORS
|
||||
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
|
||||
const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ CPPFLAGS += $(OPT)
|
|||
# On macOS, specify all weak symbols as dynamic_lookup.
|
||||
# Otherwise, you get undefined symbol errors.
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
LDFLAGS += -Wl,-U,__Z15vl_time_stamp64v,-U,__Z13sc_time_stampv
|
||||
LDFLAGS += -Wl,-U,__Z15vl_time_stamp64v,-U,__Z13sc_time_stampv,-U,_vlog_startup_routines
|
||||
endif
|
||||
|
||||
# Allow upper level user makefiles to specify flags they want.
|
||||
|
|
@ -280,10 +280,10 @@ ifneq ($(VM_DEFAULT_RULES),0)
|
|||
$(OBJCACHE) $(CXX) $(OPT_FAST) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
$(VK_OBJS_FAST): %.o: %.cpp $(VK_PCH_H).fast.gch
|
||||
$(OBJCACHE) $(CXX) $(OPT_FAST) $(CXXFLAGS) $(CPPFLAGS) $(VK_PCH_I_FAST) -c -o $@ $<
|
||||
$(OBJCACHE) $(CXX) $(OPT_FAST) $(CXXFLAGS) $(CPPFLAGS) $(VK_PCH_I_FAST) -c $<
|
||||
|
||||
$(VK_OBJS_SLOW): %.o: %.cpp $(VK_PCH_H).slow.gch
|
||||
$(OBJCACHE) $(CXX) $(OPT_SLOW) $(CXXFLAGS) $(CPPFLAGS) $(VK_PCH_I_SLOW) -c -o $@ $<
|
||||
$(OBJCACHE) $(CXX) $(OPT_SLOW) $(CXXFLAGS) $(CPPFLAGS) $(VK_PCH_I_SLOW) -c $<
|
||||
|
||||
$(VK_GLOBAL_OBJS): %.o: %.cpp
|
||||
$(OBJCACHE) $(CXX) $(OPT_GLOBAL) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ private:
|
|||
if (!std::isprint(*pos) || *pos == '%' || *pos == '"') {
|
||||
constexpr size_t LEN_MAX_HEX = 20;
|
||||
char hex[LEN_MAX_HEX];
|
||||
VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]);
|
||||
(void)VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]);
|
||||
rtn += hex;
|
||||
} else {
|
||||
rtn += *pos;
|
||||
|
|
@ -344,9 +344,9 @@ public:
|
|||
// Insert the values
|
||||
int addKeynum = 0;
|
||||
for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
|
||||
const std::string key = keys[i];
|
||||
const std::string& key = keys[i];
|
||||
if (!keys[i].empty()) {
|
||||
const std::string val = valps[i];
|
||||
const std::string& val = valps[i];
|
||||
// std::cout << " " << __FUNCTION__ << " " << key << " = " << val << "\n";
|
||||
m_insertp->m_keys[addKeynum] = valueIndex(key);
|
||||
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
||||
|
|
@ -499,6 +499,26 @@ void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7
|
|||
C(13), C(14), C(15), C(16), C(17), C(18), C(19), N(20), N(21), N(22), N(23), N(24),
|
||||
N(25), N(26), N(27), N(28), N(29));
|
||||
}
|
||||
// Backward compatibility for mixed inserts with integer-valued
|
||||
// lineno/column pairs and C-string-valued metadata pairs.
|
||||
void VerilatedCovContext::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, A(4), A(5), A(6),
|
||||
A(7)) VL_MT_SAFE {
|
||||
const std::string val2str = std::to_string(val2);
|
||||
const std::string val3str = std::to_string(val3);
|
||||
_insertp(C(0), C(1), key2, val2str.c_str(), key3, val3str.c_str(), C(4), C(5), C(6), C(7),
|
||||
N(8), N(9), N(10), N(11), N(12), N(13), N(14), N(15), N(16), N(17), N(18), N(19),
|
||||
N(20), N(21), N(22), N(23), N(24), N(25), N(26), N(27), N(28), N(29));
|
||||
}
|
||||
// Backward compatibility for mixed inserts with integer-valued
|
||||
// lineno/column pairs and additional FSM metadata pairs.
|
||||
void VerilatedCovContext::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, A(4), A(5), A(6),
|
||||
A(7), A(8), A(9), A(10), A(11)) VL_MT_SAFE {
|
||||
const std::string val2str = std::to_string(val2);
|
||||
const std::string val3str = std::to_string(val3);
|
||||
_insertp(C(0), C(1), key2, val2str.c_str(), key3, val3str.c_str(), C(4), C(5), C(6), C(7),
|
||||
C(8), C(9), C(10), C(11), N(12), N(13), N(14), N(15), N(16), N(17), N(18), N(19),
|
||||
N(20), N(21), N(22), N(23), N(24), N(25), N(26), N(27), N(28), N(29));
|
||||
}
|
||||
// Backward compatibility for Verilator
|
||||
void VerilatedCovContext::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4),
|
||||
const std::string& val4, A(5), A(6), A(7)) VL_MT_SAFE {
|
||||
|
|
|
|||
|
|
@ -191,6 +191,13 @@ public:
|
|||
void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), A(11), A(12),
|
||||
A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), D(21), D(22), D(23),
|
||||
D(24), D(25), D(26), D(27), D(28), D(29)) VL_MT_SAFE;
|
||||
// Backward compatibility for mixed inserts with integer-valued
|
||||
// lineno/column pairs and C-string-valued metadata pairs.
|
||||
void _insertp(A(0), A(1), K(2), int val2, K(3), int val3, A(4), A(5), A(6), A(7)) VL_MT_SAFE;
|
||||
// Backward compatibility for mixed inserts with integer-valued
|
||||
// lineno/column pairs and additional FSM metadata pairs.
|
||||
void _insertp(A(0), A(1), K(2), int val2, K(3), int val3, A(4), A(5), A(6), A(7), A(8), A(9),
|
||||
A(10), A(11)) VL_MT_SAFE;
|
||||
// Backward compatibility for Verilator
|
||||
void _insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), const std::string& val4, A(5),
|
||||
A(6), A(7)) VL_MT_SAFE;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ VLCOVGEN_ITEM("'name':'thresh', 'short':'s', 'group':1, 'default':None, 'd
|
|||
VLCOVGEN_ITEM("'name':'type', 'short':'t', 'group':1, 'default':'', 'descr':'Type of coverage (block, line, fsm, etc)'")
|
||||
// Bin attributes
|
||||
VLCOVGEN_ITEM("'name':'comment', 'short':'o', 'group':0, 'default':'', 'descr':'Textual description for the item'")
|
||||
VLCOVGEN_ITEM("'name':'fsm_from', 'short':'Ff', 'group':0, 'default':'', 'descr':'FSM source state name for structured FSM coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'fsm_tag', 'short':'Fg', 'group':0, 'default':'', 'descr':'FSM point tag such as reset, reset_include, or default'")
|
||||
VLCOVGEN_ITEM("'name':'fsm_to', 'short':'Ft', 'group':0, 'default':'', 'descr':'FSM destination state name for structured FSM coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'fsm_var', 'short':'Fv', 'group':0, 'default':'', 'descr':'FSM state variable name for structured FSM coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'hier', 'short':'h', 'group':0, 'default':'', 'descr':'Hierarchy path name for the item'")
|
||||
VLCOVGEN_ITEM("'name':'lineno', 'short':'l', 'group':0, 'default':0, 'descr':'Line number for the item'")
|
||||
VLCOVGEN_ITEM("'name':'weight', 'short':'w', 'group':0, 'default':None, 'descr':'For totaling items, weight of this item'")
|
||||
|
|
@ -49,6 +53,10 @@ VLCOVGEN_ITEM("'name':'weight', 'short':'w', 'group':0, 'default':None, 'd
|
|||
#define VL_CIK_COLUMN "n"
|
||||
#define VL_CIK_COMMENT "o"
|
||||
#define VL_CIK_FILENAME "f"
|
||||
#define VL_CIK_FSM_FROM "Ff"
|
||||
#define VL_CIK_FSM_TAG "Fg"
|
||||
#define VL_CIK_FSM_TO "Ft"
|
||||
#define VL_CIK_FSM_VAR "Fv"
|
||||
#define VL_CIK_HIER "h"
|
||||
#define VL_CIK_LINENO "l"
|
||||
#define VL_CIK_LINESCOV "S"
|
||||
|
|
@ -70,6 +78,10 @@ public:
|
|||
if (key == "column") return VL_CIK_COLUMN;
|
||||
if (key == "comment") return VL_CIK_COMMENT;
|
||||
if (key == "filename") return VL_CIK_FILENAME;
|
||||
if (key == "fsm_from") return VL_CIK_FSM_FROM;
|
||||
if (key == "fsm_tag") return VL_CIK_FSM_TAG;
|
||||
if (key == "fsm_to") return VL_CIK_FSM_TO;
|
||||
if (key == "fsm_var") return VL_CIK_FSM_VAR;
|
||||
if (key == "hier") return VL_CIK_HIER;
|
||||
if (key == "lineno") return VL_CIK_LINENO;
|
||||
if (key == "linescov") return VL_CIK_LINESCOV;
|
||||
|
|
|
|||
|
|
@ -775,7 +775,7 @@ int svGetCallerInfo(const char** fileNamepp, int* lineNumberp) {
|
|||
//======================================================================
|
||||
// Time
|
||||
|
||||
int svGetTime(const svScope scope, svTimeVal* time) {
|
||||
int svGetTime(const svScope /*scope*/, svTimeVal* time) {
|
||||
if (VL_UNLIKELY(!time)) return -1;
|
||||
const QData qtime = VL_TIME_Q();
|
||||
VlWide<2> itime;
|
||||
|
|
@ -796,7 +796,7 @@ int svGetTimeUnit(const svScope scope, int32_t* time_unit) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int svGetTimePrecision(const svScope scope, int32_t* time_precision) {
|
||||
int svGetTimePrecision(const svScope /*scope*/, int32_t* time_precision) {
|
||||
if (VL_UNLIKELY(!time_precision)) return -1;
|
||||
*time_precision = Verilated::threadContextp()->timeprecision();
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,258 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2026-2026 by Wilson Snyder. 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
|
||||
//
|
||||
//*************************************************************************
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilator: Runtime support for force/release statements
|
||||
///
|
||||
/// This file provides runtime data structures for efficient dynamic
|
||||
/// resolution of force/release statements. A sorted list of active
|
||||
/// forces is maintained that can be efficiently queried and modified
|
||||
/// at runtime.
|
||||
///
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_VERILATED_FORCE_H_
|
||||
#define VERILATOR_VERILATED_FORCE_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
template <typename T>
|
||||
using VlForceBaseType = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
|
||||
// VlForceRead - Helper functions to read a forced value
|
||||
//
|
||||
// These functions combine original value with forced values based on
|
||||
// VlForceVec entries.
|
||||
// This achieves O(k) complexity where k = number of active forces.
|
||||
|
||||
template <typename T>
|
||||
struct VlForceTypeInfo final {
|
||||
using Type = VlForceBaseType<T>;
|
||||
static constexpr bool bitwise
|
||||
= std::is_integral<Type>::value || std::is_enum<Type>::value || VlIsVlWide<Type>::value;
|
||||
static constexpr bool unpackedArray = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct VlForceArrayIndexer final {
|
||||
static constexpr std::size_t size = 1;
|
||||
|
||||
static T& elem(T& value, std::size_t) { return value; }
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct VlForceArrayIndexer<VlUnpacked<T, N>> final {
|
||||
static constexpr std::size_t size = N * VlForceArrayIndexer<T>::size;
|
||||
|
||||
static auto& elem(VlUnpacked<T, N>& array, std::size_t index) {
|
||||
constexpr std::size_t subSize = VlForceArrayIndexer<T>::size;
|
||||
return VlForceArrayIndexer<T>::elem(array[index / subSize], index % subSize);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct VlForceTypeInfo<VlUnpacked<T, N>> final {
|
||||
using Type = VlUnpacked<T, N>;
|
||||
static constexpr bool bitwise = false;
|
||||
static constexpr bool unpackedArray = true;
|
||||
};
|
||||
|
||||
template <typename T, bool = std::is_enum<T>::value>
|
||||
struct VlForceStorageTypeOf final {
|
||||
using type = typename std::make_unsigned<T>::type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct VlForceStorageTypeOf<T, true> final {
|
||||
using type = typename std::make_unsigned<typename std::underlying_type<T>::type>::type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using VlForceStorageType = typename VlForceStorageTypeOf<VlForceBaseType<T>>::type;
|
||||
|
||||
//=============================================================================
|
||||
// VlForceVec - Vector of active force entries for a signal
|
||||
//
|
||||
// This class maintains a sorted vector of non-overlapping force entries.
|
||||
// When a new force is added, it removes or trims existing entries that
|
||||
// overlap with the new range.
|
||||
//
|
||||
// The generated code will:
|
||||
// 1. Use addForce/release to update the active forces
|
||||
// 2. Call a generated read function that iterates entries and evaluates RHS
|
||||
|
||||
class VlForceVec final {
|
||||
private:
|
||||
struct Entry final {
|
||||
int m_lsb; // Inclusive lower bit
|
||||
int m_msb; // Inclusive upper bit
|
||||
int m_rhsLsb; // Destination index that maps to RHS index 0
|
||||
const void* m_rhsDatap; // Pointer to RHS storage
|
||||
|
||||
bool operator<(const Entry& other) const { return m_msb < other.m_msb; }
|
||||
};
|
||||
|
||||
std::vector<Entry> m_entries; // Sorted by msb, non-overlapping
|
||||
|
||||
std::vector<Entry>::iterator trimEntries(int lsb, int msb) {
|
||||
auto it = std::lower_bound(m_entries.begin(), m_entries.end(), lsb,
|
||||
[](const Entry& e, int bit) { return e.m_msb < bit; });
|
||||
while (it != m_entries.end() && it->m_lsb <= msb) {
|
||||
if (it->m_lsb < lsb && it->m_msb > msb) {
|
||||
const Entry right{msb + 1, it->m_msb, it->m_rhsLsb, it->m_rhsDatap};
|
||||
it->m_msb = lsb - 1;
|
||||
return m_entries.insert(++it, right);
|
||||
}
|
||||
if (it->m_lsb < lsb) {
|
||||
it->m_msb = lsb - 1;
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
if (it->m_msb > msb) {
|
||||
it->m_lsb = msb + 1;
|
||||
return it;
|
||||
}
|
||||
it = m_entries.erase(it);
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
static QData extractRhsChunk(const Entry& entry, int rhsLsb, int width) {
|
||||
assert(width > 0 && width <= VL_QUADSIZE);
|
||||
assert(rhsLsb >= 0);
|
||||
|
||||
const QData mask = static_cast<QData>(VL_MASK_Q(width));
|
||||
const int rhsWidth = entry.m_msb - entry.m_rhsLsb + 1;
|
||||
if (rhsWidth <= VL_QUADSIZE) {
|
||||
const QData rhsVal = static_cast<QData>(*static_cast<const QData*>(entry.m_rhsDatap));
|
||||
return (rhsVal >> rhsLsb) & mask;
|
||||
}
|
||||
|
||||
const EData* const rhswp = static_cast<const EData*>(entry.m_rhsDatap);
|
||||
return VL_SEL_QWII(rhsWidth, rhswp, rhsLsb, width) & mask;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T applyBits(T cur, const Entry& entry, int lsb, int width, int rhsLsb) {
|
||||
const T lowMask = static_cast<T>(VL_MASK_Q(width));
|
||||
const T mask = static_cast<T>(lowMask << lsb);
|
||||
const T rhsBits = static_cast<T>(
|
||||
(static_cast<T>(extractRhsChunk(entry, rhsLsb, width)) & lowMask) << lsb);
|
||||
return static_cast<T>((cur & ~mask) | (rhsBits & mask));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static typename std::enable_if<VlIsVlWide<T>::value, T>::type applyEntry(T result,
|
||||
const Entry& entry) {
|
||||
EData* const reswp = result.data();
|
||||
const int lword = VL_BITWORD_E(entry.m_lsb);
|
||||
const int hword = VL_BITWORD_E(entry.m_msb);
|
||||
for (int word = lword; word <= hword; ++word) {
|
||||
const int wordLsb = word * VL_EDATASIZE;
|
||||
const int segLsb = std::max(entry.m_lsb, wordLsb);
|
||||
const int segMsb = std::min(entry.m_msb, wordLsb + VL_EDATASIZE - 1);
|
||||
const int segWidth = segMsb - segLsb + 1;
|
||||
const int bitOffset = segLsb - wordLsb;
|
||||
const int rhsLsb = segLsb - entry.m_rhsLsb;
|
||||
reswp[word] = applyBits(reswp[word], entry, bitOffset, segWidth, rhsLsb);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static typename std::enable_if<!VlIsVlWide<T>::value && VlForceTypeInfo<T>::bitwise, T>::type
|
||||
applyEntry(T result, const Entry& entry) {
|
||||
using U = VlForceStorageType<T>;
|
||||
const int width = entry.m_msb - entry.m_lsb + 1;
|
||||
const int bits = static_cast<int>(sizeof(U) * 8);
|
||||
const int rhsLsb = entry.m_lsb - entry.m_rhsLsb;
|
||||
const QData rhsChunk = extractRhsChunk(entry, rhsLsb, width);
|
||||
if (width >= bits) return static_cast<T>(static_cast<U>(rhsChunk));
|
||||
return static_cast<T>(
|
||||
applyBits(static_cast<U>(result), entry, entry.m_lsb, width, rhsLsb));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static typename std::enable_if<!VlForceTypeInfo<T>::bitwise, T>::type
|
||||
applyEntry(T result, const Entry& entry) {
|
||||
static_cast<void>(result);
|
||||
return *static_cast<const VlForceBaseType<T>*>(entry.m_rhsDatap);
|
||||
}
|
||||
|
||||
public:
|
||||
VlForceVec() = default;
|
||||
|
||||
template <typename T>
|
||||
T read(T val) const {
|
||||
if VL_CONSTEXPR_CXX17 (VlForceTypeInfo<T>::unpackedArray) {
|
||||
// Handling the case of a nested flattened array using recursion
|
||||
using ElemRef
|
||||
= decltype(VlForceArrayIndexer<T>::elem(val, static_cast<std::size_t>(0)));
|
||||
using Elem = VlForceBaseType<ElemRef>;
|
||||
const int total = static_cast<int>(VlForceArrayIndexer<T>::size);
|
||||
for (const auto& entry : m_entries) {
|
||||
const Elem* const rhsBasep = static_cast<const Elem*>(entry.m_rhsDatap);
|
||||
const int startIdx = entry.m_lsb;
|
||||
const int endIdx = entry.m_msb;
|
||||
for (int idx = startIdx; idx <= endIdx; idx++) {
|
||||
const int rhsIndex = idx - entry.m_rhsLsb;
|
||||
const std::size_t uidx = static_cast<std::size_t>(idx);
|
||||
VlForceArrayIndexer<T>::elem(val, uidx) = rhsBasep[rhsIndex];
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
for (const auto& entry : m_entries) { val = applyEntry(val, entry); }
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T readIndex(T origVal, int index) const {
|
||||
if (m_entries.empty()) return origVal;
|
||||
|
||||
const auto it = std::lower_bound(m_entries.begin(), m_entries.end(), index,
|
||||
[](const Entry& e, int idx) { return e.m_msb < idx; });
|
||||
if (it != m_entries.end() && it->m_lsb <= index) {
|
||||
const int rhsIndex = index - it->m_rhsLsb;
|
||||
const T* const rhsBasep = static_cast<const T*>(it->m_rhsDatap);
|
||||
return rhsBasep[rhsIndex];
|
||||
}
|
||||
return origVal;
|
||||
}
|
||||
|
||||
void addForce(int lsb, int msb, const void* rhsDatap, int rhsLsb) {
|
||||
assert(lsb <= msb);
|
||||
assert(rhsDatap);
|
||||
assert(rhsLsb <= lsb);
|
||||
|
||||
auto it = trimEntries(lsb, msb);
|
||||
m_entries.insert(it, {lsb, msb, rhsLsb, rhsDatap});
|
||||
}
|
||||
|
||||
void release(int lsb, int msb) {
|
||||
assert(lsb <= msb);
|
||||
trimEntries(lsb, msb);
|
||||
}
|
||||
|
||||
void touch() {}
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
|
@ -82,6 +82,7 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
|||
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
|
||||
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
|
||||
if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1);
|
||||
fstWriterSetVersion(m_fst, "Generated by VerilatedFst");
|
||||
constDump(true); // First dump must contain the const signals
|
||||
fullDump(true); // First dump must be full for fst
|
||||
|
||||
|
|
@ -138,19 +139,8 @@ void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, uint32_t elemen
|
|||
assert(newEntry);
|
||||
}
|
||||
|
||||
// TODO: should return std::optional<fstScopeType>, but I can't have C++17
|
||||
static std::pair<bool, fstScopeType> toFstScopeType(VerilatedTracePrefixType type) {
|
||||
switch (type) {
|
||||
case VerilatedTracePrefixType::SCOPE_MODULE: return {true, FST_ST_VCD_MODULE};
|
||||
case VerilatedTracePrefixType::SCOPE_INTERFACE: return {true, FST_ST_VCD_INTERFACE};
|
||||
case VerilatedTracePrefixType::STRUCT_PACKED:
|
||||
case VerilatedTracePrefixType::STRUCT_UNPACKED: return {true, FST_ST_VCD_STRUCT};
|
||||
case VerilatedTracePrefixType::UNION_PACKED: return {true, FST_ST_VCD_UNION};
|
||||
default: return {false, /* unused so whatever, just need a value */ FST_ST_VCD_SCOPE};
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedFst::pushPrefix(const char* namep, VerilatedTracePrefixType type) {
|
||||
void VerilatedFst::pushPrefix(const char* namep, VerilatedTracePrefixType type, int left,
|
||||
int right) {
|
||||
assert(!m_prefixStack.empty()); // Constructor makes an empty entry
|
||||
const std::string name{namep};
|
||||
// An empty name means this is the root of a model created with
|
||||
|
|
@ -162,28 +152,64 @@ void VerilatedFst::pushPrefix(const char* namep, VerilatedTracePrefixType type)
|
|||
// Upper has name, we can suppress inserting $rootio, but still push so popPrefix works
|
||||
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
|
||||
return;
|
||||
} else if (name.empty()) {
|
||||
}
|
||||
if (name.empty()) {
|
||||
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
|
||||
return;
|
||||
}
|
||||
|
||||
bool isProperScope = true;
|
||||
switch (type) {
|
||||
case VerilatedTracePrefixType::ARRAY_PACKED:
|
||||
case VerilatedTracePrefixType::ARRAY_UNPACKED: isProperScope = false; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// This code assumes a signal at a given prefix level is declared before
|
||||
// any pushPrefix are done at that same level.
|
||||
const std::string newPrefix = prevPrefix + name;
|
||||
const auto pair = toFstScopeType(type);
|
||||
const bool properScope = pair.first;
|
||||
const fstScopeType scopeType = pair.second;
|
||||
m_prefixStack.emplace_back(newPrefix + (properScope ? " " : ""), type);
|
||||
if (properScope) {
|
||||
const std::string scopeName = lastWord(newPrefix);
|
||||
fstWriterSetScope(m_fst, scopeType, scopeName.c_str(), nullptr);
|
||||
m_prefixStack.emplace_back(newPrefix + (isProperScope ? " " : ""), type);
|
||||
|
||||
const uint32_t l = static_cast<uint32_t>(left);
|
||||
const uint32_t r = static_cast<uint32_t>(right);
|
||||
const uint64_t lr = static_cast<uint64_t>(l) << 32 | static_cast<uint64_t>(r);
|
||||
|
||||
switch (type) {
|
||||
case VerilatedTracePrefixType::SCOPE_MODULE:
|
||||
fstWriterSetScope(m_fst, FST_ST_VCD_MODULE, namep, nullptr);
|
||||
break;
|
||||
case VerilatedTracePrefixType::SCOPE_INTERFACE:
|
||||
fstWriterSetScope(m_fst, FST_ST_VCD_INTERFACE, namep, nullptr);
|
||||
break;
|
||||
case VerilatedTracePrefixType::STRUCT_PACKED:
|
||||
fstWriterSetAttrBegin(m_fst, FST_AT_PACK, FST_PT_PACKED, "members", l);
|
||||
fstWriterSetScope(m_fst, FST_ST_VCD_STRUCT, namep, nullptr);
|
||||
break;
|
||||
case VerilatedTracePrefixType::STRUCT_UNPACKED:
|
||||
fstWriterSetAttrBegin(m_fst, FST_AT_PACK, FST_PT_UNPACKED, "members", l);
|
||||
fstWriterSetScope(m_fst, FST_ST_VCD_STRUCT, namep, nullptr);
|
||||
break;
|
||||
case VerilatedTracePrefixType::UNION_PACKED:
|
||||
fstWriterSetAttrBegin(m_fst, FST_AT_PACK, FST_PT_PACKED, "members", l);
|
||||
fstWriterSetScope(m_fst, FST_ST_VCD_UNION, namep, nullptr);
|
||||
break;
|
||||
case VerilatedTracePrefixType::ARRAY_PACKED:
|
||||
fstWriterSetAttrBegin(m_fst, FST_AT_ARRAY, FST_AR_PACKED, "bounds", lr);
|
||||
fstWriterSetScope(m_fst, FST_ST_SV_ARRAY, namep, nullptr);
|
||||
break;
|
||||
case VerilatedTracePrefixType::ARRAY_UNPACKED:
|
||||
fstWriterSetAttrBegin(m_fst, FST_AT_ARRAY, FST_AR_UNPACKED, "bounds", lr);
|
||||
fstWriterSetScope(m_fst, FST_ST_SV_ARRAY, namep, nullptr);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedFst::popPrefix() {
|
||||
assert(!m_prefixStack.empty());
|
||||
const bool properScope = toFstScopeType(m_prefixStack.back().second).first;
|
||||
if (properScope) fstWriterSetUpscope(m_fst);
|
||||
if (m_prefixStack.back().second != VerilatedTracePrefixType::ROOTIO_WRAPPER) {
|
||||
fstWriterSetUpscope(m_fst);
|
||||
}
|
||||
m_prefixStack.pop_back();
|
||||
assert(!m_prefixStack.empty()); // Always one left, the constructor's initial one
|
||||
}
|
||||
|
|
@ -260,66 +286,76 @@ void VerilatedFst::declare(uint32_t code, const char* name, int dtypenum,
|
|||
}
|
||||
}
|
||||
|
||||
void VerilatedFst::declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
// versions to call when the sig is not array member
|
||||
void VerilatedFst::declEvent(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, bool array, int arraynum) {
|
||||
declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 0, 0);
|
||||
VerilatedTraceSigType type) {
|
||||
declare(code, name, dtypenum, direction, kind, type, false, -1, false, 0, 0);
|
||||
}
|
||||
void VerilatedFst::declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
void VerilatedFst::declBit(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, bool array, int arraynum) {
|
||||
declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 0, 0);
|
||||
VerilatedTraceSigType type) {
|
||||
declare(code, name, dtypenum, direction, kind, type, false, -1, false, 0, 0);
|
||||
}
|
||||
void VerilatedFst::declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
void VerilatedFst::declBus(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb);
|
||||
VerilatedTraceSigType type, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, direction, kind, type, false, -1, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
void VerilatedFst::declQuad(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb);
|
||||
VerilatedTraceSigType type, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, direction, kind, type, false, -1, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb);
|
||||
void VerilatedFst::declWide(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, direction, kind, type, false, -1, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
void VerilatedFst::declDouble(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, bool array, int arraynum) {
|
||||
declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 63, 0);
|
||||
VerilatedTraceSigType type) {
|
||||
declare(code, name, dtypenum, direction, kind, type, false, -1, false, 63, 0);
|
||||
}
|
||||
|
||||
// versions to call when the sig is array member
|
||||
void VerilatedFst::declEventArray(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, int arraynum) {
|
||||
declare(code, name, dtypenum, direction, kind, type, true, arraynum, false, 0, 0);
|
||||
}
|
||||
void VerilatedFst::declBitArray(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, int arraynum) {
|
||||
declare(code, name, dtypenum, direction, kind, type, true, arraynum, false, 0, 0);
|
||||
}
|
||||
void VerilatedFst::declBusArray(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, direction, kind, type, true, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declQuadArray(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, direction, kind, type, true, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declWideArray(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, direction, kind, type, true, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declDoubleArray(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction,
|
||||
VerilatedTraceSigKind kind, VerilatedTraceSigType type,
|
||||
int arraynum) {
|
||||
declare(code, name, dtypenum, direction, kind, type, true, arraynum, false, 63, 0);
|
||||
}
|
||||
//=============================================================================
|
||||
// Get/commit trace buffer
|
||||
|
||||
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer(uint32_t fidx) {
|
||||
if (offload()) return new OffloadBuffer{*this};
|
||||
return new Buffer{*this};
|
||||
}
|
||||
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer(uint32_t /*fidx*/) { return new Buffer{*this}; }
|
||||
|
||||
void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) {
|
||||
if (offload()) {
|
||||
const OffloadBuffer* const offloadBufferp = static_cast<const OffloadBuffer*>(bufp);
|
||||
if (offloadBufferp->m_offloadBufferWritep) {
|
||||
m_offloadBufferWritep = offloadBufferp->m_offloadBufferWritep;
|
||||
return; // Buffer will be deleted by the offload thread
|
||||
}
|
||||
}
|
||||
delete bufp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Configure
|
||||
|
||||
void VerilatedFst::configure(const VerilatedTraceConfig& config) {
|
||||
// If at least one model requests the FST writer thread, then use it
|
||||
m_useFstWriterThread |= config.m_useFstWriterThread;
|
||||
}
|
||||
void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) { delete bufp; }
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstBuffer implementation
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ protected:
|
|||
void commitTraceBuffer(Buffer*) override;
|
||||
|
||||
// Configure sub-class
|
||||
void configure(const VerilatedTraceConfig&) override;
|
||||
void configure(const VerilatedTraceConfig&) override {}
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
|
|
@ -110,32 +110,78 @@ public:
|
|||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
void pushPrefix(const char*, VerilatedTracePrefixType);
|
||||
void pushPrefix(const char*, VerilatedTracePrefixType, int left = 0, int right = 0);
|
||||
void popPrefix();
|
||||
|
||||
void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum);
|
||||
void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum);
|
||||
void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum);
|
||||
// versions to call when the sig is not array member
|
||||
void declEvent(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType);
|
||||
void declBit(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType);
|
||||
void declBus(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType, int msb, int lsb);
|
||||
void declQuad(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType, int msb, int lsb);
|
||||
void declWide(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType, int msb, int lsb);
|
||||
void declDouble(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType);
|
||||
|
||||
// versions to call when the sig is array member
|
||||
void declEventArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum);
|
||||
void declBitArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum);
|
||||
void declBusArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum, int msb,
|
||||
int lsb);
|
||||
void declQuadArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum, int msb,
|
||||
int lsb);
|
||||
void declWideArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum, int msb,
|
||||
int lsb);
|
||||
void declDoubleArray(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType, int arraynum);
|
||||
|
||||
void declDTypeEnum(int dtypenum, const char* name, uint32_t elements, unsigned int minValbits,
|
||||
const char** itemNamesp, const char** itemValuesp);
|
||||
};
|
||||
|
||||
// We use macros to drop unused arguments at compile time. This saves code size.
|
||||
#define VL_TRACE_PUSH_PREFIX(tracep, name, type, left, right) \
|
||||
tracep->pushPrefix(name, type, left, right);
|
||||
#define VL_TRACE_POP_PREFIX(tracep) tracep->popPrefix();
|
||||
|
||||
#define VL_TRACE_DECL_EVENT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
|
||||
tracep->declEvent(code, name, dtypenum, dir, kind, type)
|
||||
#define VL_TRACE_DECL_BIT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
|
||||
tracep->declBit(code, name, dtypenum, dir, kind, type)
|
||||
#define VL_TRACE_DECL_BUS(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
|
||||
tracep->declBus(code, name, dtypenum, dir, kind, type, msb, lsb)
|
||||
#define VL_TRACE_DECL_QUAD(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
|
||||
tracep->declQuad(code, name, dtypenum, dir, kind, type, msb, lsb)
|
||||
#define VL_TRACE_DECL_WIDE(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
|
||||
tracep->declWide(code, name, dtypenum, dir, kind, type, msb, lsb)
|
||||
#define VL_TRACE_DECL_DOUBLE(tracep, code, fidx, name, dtypenum, dir, kind, type) \
|
||||
tracep->declDouble(code, name, dtypenum, dir, kind, type)
|
||||
|
||||
#define VL_TRACE_DECL_EVENT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
|
||||
tracep->declEventArray(code, name, dtypenum, dir, kind, type, arraynum)
|
||||
#define VL_TRACE_DECL_BIT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
|
||||
tracep->declBitArray(code, name, dtypenum, dir, kind, type, arraynum)
|
||||
#define VL_TRACE_DECL_BUS_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
|
||||
msb, lsb) \
|
||||
tracep->declBusArray(code, name, dtypenum, dir, kind, type, arraynum, msb, lsb)
|
||||
#define VL_TRACE_DECL_QUAD_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
|
||||
msb, lsb) \
|
||||
tracep->declQuadArray(code, name, dtypenum, dir, kind, type, arraynum, msb, lsb)
|
||||
#define VL_TRACE_DECL_WIDE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
|
||||
msb, lsb) \
|
||||
tracep->declWideArray(code, name, dtypenum, dir, kind, type, arraynum, msb, lsb)
|
||||
#define VL_TRACE_DECL_DOUBLE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
|
||||
tracep->declDoubleArray(code, name, dtypenum, dir, kind, type, arraynum)
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// Declare specialization here as it's used in VerilatedFstC just below
|
||||
template <>
|
||||
|
|
@ -160,7 +206,6 @@ class VerilatedFstBuffer VL_NOT_FINAL {
|
|||
friend VerilatedFst;
|
||||
friend VerilatedFst::Super;
|
||||
friend VerilatedFst::Buffer;
|
||||
friend VerilatedFst::OffloadBuffer;
|
||||
|
||||
VerilatedFst& m_owner; // Trace file owning this buffer. Required by subclasses.
|
||||
|
||||
|
|
|
|||
|
|
@ -98,10 +98,9 @@ inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) {
|
|||
if (VL_UNLIKELY(hi - lo + 1 == 0)) return rnd;
|
||||
// Modulus isn't very fast but it's common that hi-low is power-of-two
|
||||
return (rnd % (hi - lo + 1)) + lo;
|
||||
} else {
|
||||
if (VL_UNLIKELY(lo - hi + 1 == 0)) return rnd;
|
||||
return (rnd % (lo - hi + 1)) + hi;
|
||||
}
|
||||
if (VL_UNLIKELY(lo - hi + 1 == 0)) return rnd;
|
||||
return (rnd % (lo - hi + 1)) + hi;
|
||||
}
|
||||
|
||||
/// Random reset a signal of given width (init time only, var-specific PRNG)
|
||||
|
|
@ -306,7 +305,7 @@ extern void _vl_debug_print_w(int lbits, WDataInP const iwp) VL_MT_SAFE;
|
|||
|
||||
// clang-format off
|
||||
|
||||
#if defined(SYSTEMC_VERSION)
|
||||
#ifdef SYSTEMC_VERSION
|
||||
/// Return current simulation time
|
||||
// Already defined: extern sc_time sc_time_stamp();
|
||||
inline uint64_t vl_time_stamp64() VL_MT_SAFE { return sc_core::sc_time_stamp().value(); }
|
||||
|
|
@ -500,15 +499,15 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
|||
sc_dt::sc_biguint<(obits)> _butemp = (svar).read(); \
|
||||
uint32_t* chunkp = _butemp.get_raw(); \
|
||||
int32_t lsb = 0; \
|
||||
while (lsb < obits - BITS_PER_DIGIT) { \
|
||||
while (lsb < (obits) - BITS_PER_DIGIT) { \
|
||||
const uint32_t data = *chunkp; \
|
||||
++chunkp; \
|
||||
_vl_insert_WI(owp.data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \
|
||||
_vl_insert_WI((owp).data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \
|
||||
lsb += BITS_PER_DIGIT; \
|
||||
} \
|
||||
if (lsb < obits) { \
|
||||
if (lsb < (obits)) { \
|
||||
const uint32_t msb_data = *chunkp; \
|
||||
_vl_insert_WI(owp.data(), msb_data, obits - 1, lsb); \
|
||||
_vl_insert_WI((owp).data(), msb_data, (obits) - 1, lsb); \
|
||||
} \
|
||||
(owp)[words - 1] &= VL_MASK_E(obits); \
|
||||
}
|
||||
|
|
@ -762,14 +761,12 @@ static inline IData VL_COUNTONES_W(int words, WDataInP const lwp) VL_PURE {
|
|||
static inline IData VL_COUNTBITS_I(int lbits, IData lhs, IData ctrl0, IData ctrl1,
|
||||
IData ctrl2) VL_PURE {
|
||||
const int ctrlSum = (ctrl0 & 0x1) + (ctrl1 & 0x1) + (ctrl2 & 0x1);
|
||||
if (ctrlSum == 3) {
|
||||
return VL_COUNTONES_I(lhs);
|
||||
} else if (ctrlSum == 0) {
|
||||
if (ctrlSum == 3) return VL_COUNTONES_I(lhs);
|
||||
if (ctrlSum == 0) {
|
||||
const IData mask = (lbits == 32) ? -1 : ((1 << lbits) - 1);
|
||||
return VL_COUNTONES_I(~lhs & mask);
|
||||
} else {
|
||||
return (lbits == 32) ? 32 : lbits;
|
||||
}
|
||||
return (lbits == 32) ? 32 : lbits;
|
||||
}
|
||||
static inline IData VL_COUNTBITS_Q(int lbits, QData lhs, IData ctrl0, IData ctrl1,
|
||||
IData ctrl2) VL_PURE {
|
||||
|
|
@ -1018,17 +1015,17 @@ static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE
|
|||
// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean;
|
||||
// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean;
|
||||
// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean;
|
||||
static inline IData VL_DIV_III(int lbits, IData lhs, IData rhs) {
|
||||
static inline IData VL_DIV_III(int /*lbits*/, IData lhs, IData rhs) {
|
||||
return (rhs == 0) ? 0 : lhs / rhs;
|
||||
}
|
||||
static inline QData VL_DIV_QQQ(int lbits, QData lhs, QData rhs) {
|
||||
static inline QData VL_DIV_QQQ(int /*lbits*/, QData lhs, QData rhs) {
|
||||
return (rhs == 0) ? 0 : lhs / rhs;
|
||||
}
|
||||
#define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0))
|
||||
static inline IData VL_MODDIV_III(int lbits, IData lhs, IData rhs) {
|
||||
static inline IData VL_MODDIV_III(int /*lbits*/, IData lhs, IData rhs) {
|
||||
return (rhs == 0) ? 0 : lhs % rhs;
|
||||
}
|
||||
static inline QData VL_MODDIV_QQQ(int lbits, QData lhs, QData rhs) {
|
||||
static inline QData VL_MODDIV_QQQ(int /*lbits*/, QData lhs, QData rhs) {
|
||||
return (rhs == 0) ? 0 : lhs % rhs;
|
||||
}
|
||||
#define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1))
|
||||
|
|
@ -1176,9 +1173,8 @@ static inline WDataOutP VL_DIVS_WWW(int lbits, WDataOutP owp, WDataInP const lwp
|
|||
VL_DIV_WWW(lbits, qNoSign, ltup, rtup);
|
||||
_vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, owp, qNoSign));
|
||||
return owp;
|
||||
} else {
|
||||
return VL_DIV_WWW(lbits, owp, ltup, rtup);
|
||||
}
|
||||
return VL_DIV_WWW(lbits, owp, ltup, rtup);
|
||||
}
|
||||
static inline WDataOutP VL_MODDIVS_WWW(int lbits, WDataOutP owp, WDataInP const lwp,
|
||||
WDataInP const rwp) VL_MT_SAFE {
|
||||
|
|
@ -1199,9 +1195,8 @@ static inline WDataOutP VL_MODDIVS_WWW(int lbits, WDataOutP owp, WDataInP const
|
|||
VL_MODDIV_WWW(lbits, qNoSign, ltup, rtup);
|
||||
_vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, owp, qNoSign));
|
||||
return owp;
|
||||
} else {
|
||||
return VL_MODDIV_WWW(lbits, owp, ltup, rtup);
|
||||
}
|
||||
return VL_MODDIV_WWW(lbits, owp, ltup, rtup);
|
||||
}
|
||||
|
||||
#define VL_POW_IIQ(obits, lbits, rbits, lhs, rhs) VL_POW_QQQ(obits, lbits, rbits, lhs, rhs)
|
||||
|
|
@ -1255,14 +1250,11 @@ static inline IData VL_POWSS_III(int obits, int, int rbits, IData lhs, IData rhs
|
|||
if (rsign && VL_SIGN_I(rbits, rhs)) {
|
||||
if (lhs == 0) {
|
||||
return 0; // "X"
|
||||
} else if (lhs == 1) {
|
||||
return 1;
|
||||
} else if (lsign && lhs == VL_MASK_I(obits)) { // -1
|
||||
if (rhs & 1) {
|
||||
return VL_MASK_I(obits); // -1^odd=-1
|
||||
} else {
|
||||
return 1; // -1^even=1
|
||||
}
|
||||
}
|
||||
if (lhs == 1) { return 1; }
|
||||
if (lsign && lhs == VL_MASK_I(obits)) { // -1
|
||||
if (rhs & 1) return VL_MASK_I(obits); // -1^odd=-1
|
||||
return 1; // -1^even=1
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1272,16 +1264,12 @@ static inline QData VL_POWSS_QQQ(int obits, int, int rbits, QData lhs, QData rhs
|
|||
bool rsign) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(rhs == 0)) return 1;
|
||||
if (rsign && VL_SIGN_Q(rbits, rhs)) {
|
||||
if (lhs == 0) {
|
||||
return 0; // "X"
|
||||
} else if (lhs == 1) {
|
||||
return 1;
|
||||
} else if (lsign && lhs == VL_MASK_Q(obits)) { // -1
|
||||
if (rhs & 1) {
|
||||
return VL_MASK_Q(obits); // -1^odd=-1
|
||||
} else {
|
||||
return 1; // -1^even=1
|
||||
}
|
||||
if (lhs == 0) return 0; // "X"
|
||||
|
||||
if (lhs == 1) return 1;
|
||||
if (lsign && lhs == VL_MASK_Q(obits)) { // -1
|
||||
if (rhs & 1) return VL_MASK_Q(obits); // -1^odd=-1
|
||||
return 1; // -1^even=1
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1614,28 +1602,28 @@ static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const
|
|||
return owp;
|
||||
}
|
||||
|
||||
static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue<CData>& q) {
|
||||
static inline IData VL_PACK_I_RI(int /*obits*/, int lbits, const VlQueue<CData>& q) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i)
|
||||
ret |= static_cast<IData>(q.at(q.size() - 1 - i)) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue<SData>& q) {
|
||||
static inline IData VL_PACK_I_RI(int /*obits*/, int lbits, const VlQueue<SData>& q) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i)
|
||||
ret |= static_cast<IData>(q.at(q.size() - 1 - i)) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue<IData>& q) {
|
||||
static inline IData VL_PACK_I_RI(int /*obits*/, int lbits, const VlQueue<IData>& q) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i) ret |= q.at(q.size() - 1 - i) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked<CData, N_Depth>& q) {
|
||||
static inline IData VL_PACK_I_UI(int /*obits*/, int lbits, const VlUnpacked<CData, N_Depth>& q) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < N_Depth; ++i)
|
||||
ret |= static_cast<IData>(q[N_Depth - 1 - i]) << (i * lbits);
|
||||
|
|
@ -1643,7 +1631,7 @@ static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked<CData, N
|
|||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked<SData, N_Depth>& q) {
|
||||
static inline IData VL_PACK_I_UI(int /*obits*/, int lbits, const VlUnpacked<SData, N_Depth>& q) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < N_Depth; ++i)
|
||||
ret |= static_cast<IData>(q[N_Depth - 1 - i]) << (i * lbits);
|
||||
|
|
@ -1651,27 +1639,27 @@ static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked<SData, N
|
|||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked<IData, N_Depth>& q) {
|
||||
static inline IData VL_PACK_I_UI(int /*obits*/, int lbits, const VlUnpacked<IData, N_Depth>& q) {
|
||||
IData ret = 0;
|
||||
for (size_t i = 0; i < N_Depth; ++i) ret |= q[N_Depth - 1 - i] << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue<CData>& q) {
|
||||
static inline QData VL_PACK_Q_RI(int /*obits*/, int lbits, const VlQueue<CData>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i)
|
||||
ret |= static_cast<QData>(q.at(q.size() - 1 - i)) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue<SData>& q) {
|
||||
static inline QData VL_PACK_Q_RI(int /*obits*/, int lbits, const VlQueue<SData>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i)
|
||||
ret |= static_cast<QData>(q.at(q.size() - 1 - i)) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue<IData>& q) {
|
||||
static inline QData VL_PACK_Q_RI(int /*obits*/, int lbits, const VlQueue<IData>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i)
|
||||
ret |= static_cast<QData>(q.at(q.size() - 1 - i)) << (i * lbits);
|
||||
|
|
@ -1679,7 +1667,7 @@ static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue<IData>& q)
|
|||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked<CData, N_Depth>& q) {
|
||||
static inline QData VL_PACK_Q_UI(int /*obits*/, int lbits, const VlUnpacked<CData, N_Depth>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < N_Depth; ++i)
|
||||
ret |= static_cast<QData>(q[N_Depth - 1 - i]) << (i * lbits);
|
||||
|
|
@ -1687,7 +1675,7 @@ static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked<CData, N
|
|||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked<SData, N_Depth>& q) {
|
||||
static inline QData VL_PACK_Q_UI(int /*obits*/, int lbits, const VlUnpacked<SData, N_Depth>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < N_Depth; ++i)
|
||||
ret |= static_cast<QData>(q[N_Depth - 1 - i]) << (i * lbits);
|
||||
|
|
@ -1695,21 +1683,21 @@ static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked<SData, N
|
|||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked<IData, N_Depth>& q) {
|
||||
static inline QData VL_PACK_Q_UI(int /*obits*/, int lbits, const VlUnpacked<IData, N_Depth>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < N_Depth; ++i)
|
||||
ret |= static_cast<QData>(q[N_Depth - 1 - i]) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline QData VL_PACK_Q_RQ(int obits, int lbits, const VlQueue<QData>& q) {
|
||||
static inline QData VL_PACK_Q_RQ(int /*obits*/, int lbits, const VlQueue<QData>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < q.size(); ++i) ret |= q.at(q.size() - 1 - i) << (i * lbits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline QData VL_PACK_Q_UQ(int obits, int lbits, const VlUnpacked<QData, N_Depth>& q) {
|
||||
static inline QData VL_PACK_Q_UQ(int /*obits*/, int lbits, const VlUnpacked<QData, N_Depth>& q) {
|
||||
QData ret = 0;
|
||||
for (size_t i = 0; i < N_Depth; ++i) ret |= q[N_Depth - 1 - i] << (i * lbits);
|
||||
return ret;
|
||||
|
|
@ -1922,7 +1910,7 @@ static inline void _vl_shiftl_inplace_w(int obits, WDataOutP iowp,
|
|||
// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?)
|
||||
// If RHS (rd/rwp) is larger than the output, zeros (or all ones for >>>) must be returned
|
||||
// (This corresponds to AstShift*Ovr Ast nodes)
|
||||
static inline IData VL_SHIFTL_III(int obits, int, int, IData lhs, IData rhs) VL_MT_SAFE {
|
||||
static inline IData VL_SHIFTL_III(int /*obits*/, int, int, IData lhs, IData rhs) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
|
||||
return lhs << rhs; // Small is common so not clean return
|
||||
}
|
||||
|
|
@ -1930,7 +1918,7 @@ static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_
|
|||
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
|
||||
return VL_CLEAN_II(obits, obits, lhs << rhs);
|
||||
}
|
||||
static inline QData VL_SHIFTL_QQI(int obits, int, int, QData lhs, IData rhs) VL_MT_SAFE {
|
||||
static inline QData VL_SHIFTL_QQI(int /*obits*/, int, int, QData lhs, IData rhs) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
|
||||
return lhs << rhs; // Small is common so not clean return
|
||||
}
|
||||
|
|
@ -1991,19 +1979,19 @@ static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs,
|
|||
// EMIT_RULE: VL_SHIFTR: oclean=lclean; rclean==clean;
|
||||
// Important: Unlike most other funcs, the shift might well be a computed
|
||||
// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?)
|
||||
static inline IData VL_SHIFTR_III(int obits, int, int, IData lhs, IData rhs) VL_PURE {
|
||||
static inline IData VL_SHIFTR_III(int /*obits*/, int, int, IData lhs, IData rhs) VL_PURE {
|
||||
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
|
||||
return lhs >> rhs;
|
||||
}
|
||||
static inline IData VL_SHIFTR_IIQ(int obits, int, int, IData lhs, QData rhs) VL_PURE {
|
||||
static inline IData VL_SHIFTR_IIQ(int /*obits*/, int, int, IData lhs, QData rhs) VL_PURE {
|
||||
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
|
||||
return lhs >> rhs;
|
||||
}
|
||||
static inline QData VL_SHIFTR_QQI(int obits, int, int, QData lhs, IData rhs) VL_PURE {
|
||||
static inline QData VL_SHIFTR_QQI(int /*obits*/, int, int, QData lhs, IData rhs) VL_PURE {
|
||||
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
|
||||
return lhs >> rhs;
|
||||
}
|
||||
static inline QData VL_SHIFTR_QQQ(int obits, int, int, QData lhs, QData rhs) VL_PURE {
|
||||
static inline QData VL_SHIFTR_QQQ(int /*obits*/, int, int, QData lhs, QData rhs) VL_PURE {
|
||||
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
|
||||
return lhs >> rhs;
|
||||
}
|
||||
|
|
@ -2181,9 +2169,8 @@ static inline IData VL_BITSEL_IWII(int lbits, WDataInP const lwp, IData rd) VL_M
|
|||
if (VL_UNLIKELY(rd > static_cast<IData>(lbits))) {
|
||||
return ~0; // Spec says you can go outside the range of a array. Don't coredump if so.
|
||||
// We return all 1's as that's more likely to find bugs (?) than 0's.
|
||||
} else {
|
||||
return (lwp[word] >> VL_BITBIT_E(rd));
|
||||
}
|
||||
return (lwp[word] >> VL_BITBIT_E(rd));
|
||||
}
|
||||
|
||||
// EMIT_RULE: VL_RANGE: oclean=lclean; out=dirty
|
||||
|
|
@ -2196,34 +2183,35 @@ static inline IData VL_SEL_IWII(int lbits, WDataInP const lwp, IData lsb, IData
|
|||
const int msb = lsb + width - 1;
|
||||
if (VL_UNLIKELY(msb >= lbits)) {
|
||||
return ~0; // Spec says you can go outside the range of a array. Don't coredump if so.
|
||||
} else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast<int>(lsb))) {
|
||||
return VL_BITRSHIFT_W(lwp, lsb);
|
||||
} else {
|
||||
// 32 bit extraction may span two words
|
||||
const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); // bits that come from low word
|
||||
return ((lwp[VL_BITWORD_E(msb)] << nbitsfromlow) | VL_BITRSHIFT_W(lwp, lsb));
|
||||
}
|
||||
if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast<int>(lsb))) {
|
||||
return VL_BITRSHIFT_W(lwp, lsb);
|
||||
}
|
||||
// 32 bit extraction may span two words
|
||||
const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); // bits that come from low word
|
||||
return ((lwp[VL_BITWORD_E(msb)] << nbitsfromlow) | VL_BITRSHIFT_W(lwp, lsb));
|
||||
}
|
||||
|
||||
static inline QData VL_SEL_QWII(int lbits, WDataInP const lwp, IData lsb, IData width) VL_MT_SAFE {
|
||||
const int msb = lsb + width - 1;
|
||||
if (VL_UNLIKELY(msb > lbits)) {
|
||||
return ~0; // Spec says you can go outside the range of a array. Don't coredump if so.
|
||||
} else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast<int>(lsb))) {
|
||||
}
|
||||
if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast<int>(lsb))) {
|
||||
return VL_BITRSHIFT_W(lwp, lsb);
|
||||
} else if (VL_BITWORD_E(msb) == 1 + VL_BITWORD_E(static_cast<int>(lsb))) {
|
||||
}
|
||||
if (VL_BITWORD_E(msb) == 1 + VL_BITWORD_E(static_cast<int>(lsb))) {
|
||||
const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb);
|
||||
const QData hi = (lwp[VL_BITWORD_E(msb)]);
|
||||
const QData lo = VL_BITRSHIFT_W(lwp, lsb);
|
||||
return (hi << nbitsfromlow) | lo;
|
||||
} else {
|
||||
// 64 bit extraction may span three words
|
||||
const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb);
|
||||
const QData hi = (lwp[VL_BITWORD_E(msb)]);
|
||||
const QData mid = (lwp[VL_BITWORD_E(lsb) + 1]);
|
||||
const QData lo = VL_BITRSHIFT_W(lwp, lsb);
|
||||
return (hi << (nbitsfromlow + VL_EDATASIZE)) | (mid << nbitsfromlow) | lo;
|
||||
}
|
||||
// 64 bit extraction may span three words
|
||||
const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb);
|
||||
const QData hi = (lwp[VL_BITWORD_E(msb)]);
|
||||
const QData mid = (lwp[VL_BITWORD_E(lsb) + 1]);
|
||||
const QData lo = VL_BITRSHIFT_W(lwp, lsb);
|
||||
return (hi << (nbitsfromlow + VL_EDATASIZE)) | (mid << nbitsfromlow) | lo;
|
||||
}
|
||||
|
||||
static inline WDataOutP VL_SEL_WWII(int obits, int lbits, WDataOutP owp, WDataInP const lwp,
|
||||
|
|
@ -2277,13 +2265,11 @@ static inline bool VL_GET_QUEUE_BIT(const VlQueue<T>& queue, int srcElementBits,
|
|||
if (VL_UNLIKELY(elemIdx >= queue.size())) return false;
|
||||
|
||||
const T element = queue.at(elemIdx);
|
||||
if (srcElementBits == 1) {
|
||||
return element & 1;
|
||||
} else {
|
||||
const size_t bitInElem = bitIndex % srcElementBits;
|
||||
const size_t actualBitPos = srcElementBits - 1 - bitInElem;
|
||||
return (element >> actualBitPos) & 1;
|
||||
}
|
||||
if (srcElementBits == 1) return element & 1;
|
||||
|
||||
const size_t bitInElem = bitIndex % srcElementBits;
|
||||
const size_t actualBitPos = srcElementBits - 1 - bitInElem;
|
||||
return (element >> actualBitPos) & 1;
|
||||
}
|
||||
|
||||
// Helper function to set a bit in the destination queue
|
||||
|
|
@ -2351,8 +2337,8 @@ static inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide<N_Words>& elem) {
|
|||
// This specialization works for both VlQueue<CData> (and similar) as well
|
||||
// as VlQueue<VlWide<N>>.
|
||||
template <typename T>
|
||||
static inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int lbits, int srcElementBits,
|
||||
int dstElementBits) {
|
||||
static inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/,
|
||||
int srcElementBits, int dstElementBits) {
|
||||
if (srcElementBits == dstElementBits) {
|
||||
// Simple case: same element bit width, direct copy of each element
|
||||
if (VL_UNLIKELY(&q == &from)) return; // Skip self-assignment when it's truly a no-op
|
||||
|
|
@ -2417,6 +2403,46 @@ static inline void VL_REVCOPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int lbits
|
|||
}
|
||||
}
|
||||
|
||||
// Reverse element order of an unpacked array in-place.
|
||||
// Used by emitter for descending-range arrays after VL_UNPACK_*.
|
||||
template <typename T_Value, std::size_t N_Depth>
|
||||
static inline void VL_UNPACK_REVERSED(VlUnpacked<T_Value, N_Depth>& q) {
|
||||
for (size_t i = 0; i < N_Depth / 2; ++i) {
|
||||
const T_Value tmp = q[i];
|
||||
q[i] = q[N_Depth - 1 - i];
|
||||
q[N_Depth - 1 - i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// Return a reversed copy of an unpacked array.
|
||||
// Used by emitter for descending-range arrays before VL_PACK_*.
|
||||
template <typename T_Value, std::size_t N_Depth>
|
||||
static inline VlUnpacked<T_Value, N_Depth>
|
||||
VL_PACK_REVERSED(const VlUnpacked<T_Value, N_Depth>& q) {
|
||||
VlUnpacked<T_Value, N_Depth> ret;
|
||||
for (size_t i = 0; i < N_Depth; ++i) ret[i] = q[N_Depth - 1 - i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Overloads for VlUnpacked source -> VlQueue destination
|
||||
template <typename T, std::size_t N_Depth>
|
||||
static inline void VL_COPY_Q(VlQueue<T>& q, const VlUnpacked<T, N_Depth>& from, int lbits,
|
||||
int srcElementBits, int dstElementBits) {
|
||||
VlQueue<T> srcQ;
|
||||
srcQ.renew(N_Depth);
|
||||
for (size_t i = 0; i < N_Depth; ++i) srcQ.atWrite(i) = from[i];
|
||||
VL_COPY_Q(q, srcQ, lbits, srcElementBits, dstElementBits);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N_Depth>
|
||||
static inline void VL_REVCOPY_Q(VlQueue<T>& q, const VlUnpacked<T, N_Depth>& from, int lbits,
|
||||
int srcElementBits, int dstElementBits) {
|
||||
VlQueue<T> srcQ;
|
||||
srcQ.renew(N_Depth);
|
||||
for (size_t i = 0; i < N_Depth; ++i) srcQ.atWrite(i) = from[N_Depth - 1 - i];
|
||||
VL_COPY_Q(q, srcQ, lbits, srcElementBits, dstElementBits);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Expressions needing insert/select
|
||||
|
||||
|
|
@ -2536,49 +2562,49 @@ static inline void VL_UNPACK_RW_W(int lbits, int rbits, VlQueue<VlWide<N_Words>>
|
|||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked<CData, N_Depth>& q,
|
||||
static inline void VL_UNPACK_UI_I(int lbits, int /*rbits*/, VlUnpacked<CData, N_Depth>& q,
|
||||
IData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked<SData, N_Depth>& q,
|
||||
static inline void VL_UNPACK_UI_I(int lbits, int /*rbits*/, VlUnpacked<SData, N_Depth>& q,
|
||||
IData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked<IData, N_Depth>& q,
|
||||
static inline void VL_UNPACK_UI_I(int lbits, int /*rbits*/, VlUnpacked<IData, N_Depth>& q,
|
||||
IData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked<CData, N_Depth>& q,
|
||||
static inline void VL_UNPACK_UI_Q(int lbits, int /*rbits*/, VlUnpacked<CData, N_Depth>& q,
|
||||
QData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked<SData, N_Depth>& q,
|
||||
static inline void VL_UNPACK_UI_Q(int lbits, int /*rbits*/, VlUnpacked<SData, N_Depth>& q,
|
||||
QData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked<IData, N_Depth>& q,
|
||||
static inline void VL_UNPACK_UI_Q(int lbits, int /*rbits*/, VlUnpacked<IData, N_Depth>& q,
|
||||
QData from) {
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
|
||||
}
|
||||
|
||||
template <std::size_t N_Depth>
|
||||
static inline void VL_UNPACK_UQ_Q(int lbits, int rbits, VlUnpacked<QData, N_Depth>& q,
|
||||
static inline void VL_UNPACK_UQ_Q(int lbits, int /*rbits*/, VlUnpacked<QData, N_Depth>& q,
|
||||
QData from) {
|
||||
const QData mask = VL_MASK_Q(lbits);
|
||||
for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
|
||||
|
|
@ -2792,10 +2818,9 @@ static inline void VL_SELASSIGN_WW(int rbits, int obits, WDataOutP iowp, WDataIn
|
|||
//======================================================================
|
||||
// Triops
|
||||
|
||||
static inline WDataOutP VL_COND_WIWW(int obits, WDataOutP owp, int cond, WDataInP const w1p,
|
||||
WDataInP const w2p) VL_MT_SAFE {
|
||||
return VL_MEMCPY_W(owp, cond ? w1p : w2p, VL_WORDS_I(obits));
|
||||
}
|
||||
// This must be a macro in order for short-circuiting of the values to work.
|
||||
#define VL_COND_WIWW(obits, owp, cond, w1p, w2p) \
|
||||
VL_MEMCPY_W(owp, (cond) ? (w1p) : (w2p), VL_WORDS_I(obits))
|
||||
|
||||
//======================================================================
|
||||
// Constification
|
||||
|
|
@ -2936,17 +2961,15 @@ extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_
|
|||
inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE {
|
||||
// SystemVerilog does not allow a string variable to contain '\0'.
|
||||
// So C functions such as strcmp() can correctly compare strings.
|
||||
if (ignoreCase) {
|
||||
return VL_STRCASECMP(lhs.c_str(), rhs.c_str());
|
||||
} else {
|
||||
return std::strcmp(lhs.c_str(), rhs.c_str());
|
||||
}
|
||||
if (ignoreCase) { return VL_STRCASECMP(lhs.c_str(), rhs.c_str()); }
|
||||
return std::strcmp(lhs.c_str(), rhs.c_str());
|
||||
}
|
||||
|
||||
extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
|
||||
extern IData VL_NTOI_I(int obits, const std::string& str) VL_PURE;
|
||||
extern QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE;
|
||||
extern void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str) VL_PURE;
|
||||
extern void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str,
|
||||
int truncFront = 0) VL_PURE;
|
||||
|
||||
extern IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE;
|
||||
|
||||
|
|
|
|||
|
|
@ -378,7 +378,7 @@ private:
|
|||
// MCD Case
|
||||
if (fdi & 1) fp.push_back(stdout);
|
||||
fdi >>= 1;
|
||||
for (size_t i = 1; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
|
||||
for (size_t i = 1; (fdi != 0) && (i < VerilatedFpList::capacity()); ++i, fdi >>= 1) {
|
||||
if (fdi & VL_MASK_I(1)) fp.push_back(m_fdps[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -553,9 +553,8 @@ private:
|
|||
if (it == s().m_exportMap.end()) {
|
||||
s().m_exportMap.emplace(namep, s().m_exportNext++);
|
||||
return s().m_exportNext++;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -99,10 +99,11 @@ IData VL_DIST_CHI_SQUARE(IData& seedr, IData udf) VL_MT_SAFE {
|
|||
double r = _vl_dbase_chi_square(seedr, df);
|
||||
int32_t i;
|
||||
if (r >= 0) {
|
||||
i = static_cast<int32_t>(r + 0.5);
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
} else {
|
||||
r = -r; // LCOV_EXCL_LINE
|
||||
i = static_cast<int32_t>(r + 0.5); // LCOV_EXCL_LINE
|
||||
i = static_cast<int32_t>(
|
||||
r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding // LCOV_EXCL_LINE
|
||||
i = -i; // LCOV_EXCL_LINE
|
||||
}
|
||||
return static_cast<IData>(i);
|
||||
|
|
@ -122,10 +123,10 @@ IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE {
|
|||
double r = -a * log(x) / b;
|
||||
int32_t i;
|
||||
if (r >= 0) {
|
||||
i = static_cast<int32_t>(r + 0.5);
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
} else {
|
||||
r = -r;
|
||||
i = static_cast<int32_t>(r + 0.5);
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
i = -i;
|
||||
}
|
||||
return static_cast<IData>(i);
|
||||
|
|
@ -140,10 +141,11 @@ IData VL_DIST_EXPONENTIAL(IData& seedr, IData umean) VL_MT_SAFE {
|
|||
int32_t i;
|
||||
double r = _vl_dbase_exponential(seedr, mean);
|
||||
if (r >= 0) {
|
||||
i = static_cast<int32_t>(r + 0.5);
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
} else {
|
||||
r = -r; // LCOV_EXCL_LINE
|
||||
i = static_cast<int32_t>(r + 0.5); // LCOV_EXCL_LINE
|
||||
i = static_cast<int32_t>(
|
||||
r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding // LCOV_EXCL_LINE
|
||||
i = -i; // LCOV_EXCL_LINE
|
||||
}
|
||||
return static_cast<IData>(i);
|
||||
|
|
@ -155,10 +157,10 @@ IData VL_DIST_NORMAL(IData& seedr, IData umean, IData usd) VL_MT_SAFE {
|
|||
double r = _vl_dbase_normal(seedr, mean, sd);
|
||||
int32_t i;
|
||||
if (r >= 0) {
|
||||
i = static_cast<int32_t>(r + 0.5);
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
} else {
|
||||
r = -r;
|
||||
i = static_cast<int32_t>(r + 0.5);
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
i = -i;
|
||||
}
|
||||
return static_cast<IData>(i);
|
||||
|
|
@ -172,7 +174,7 @@ IData VL_DIST_POISSON(IData& seedr, IData umean) VL_MT_SAFE {
|
|||
}
|
||||
int32_t i = 0;
|
||||
double q = -static_cast<double>(mean);
|
||||
double p = exp(q);
|
||||
const double p = exp(q);
|
||||
q = _vl_dbase_uniform(seedr, 0, 1);
|
||||
while (p < q) {
|
||||
++i;
|
||||
|
|
@ -193,10 +195,10 @@ IData VL_DIST_T(IData& seedr, IData udf) VL_MT_SAFE {
|
|||
double r = _vl_dbase_normal(seedr, 0, 1) / root;
|
||||
int32_t i;
|
||||
if (r >= 0) {
|
||||
i = static_cast<int32_t>(r + 0.5);
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
} else {
|
||||
r = -r;
|
||||
i = static_cast<int32_t>(r + 0.5);
|
||||
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
|
||||
i = -i;
|
||||
}
|
||||
return static_cast<IData>(i);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ constexpr const char* const VlExecutionRecord::s_ascii[];
|
|||
template <size_t N>
|
||||
static size_t roundUptoMultipleOf(size_t value) {
|
||||
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
|
||||
size_t mask = N - 1;
|
||||
const size_t mask = N - 1;
|
||||
return (value + mask) & ~mask;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "verilated_random.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
|
@ -62,17 +62,20 @@ class VlRProcess final : private std::streambuf, public std::iostream {
|
|||
char m_readBuf[BUFFER_SIZE];
|
||||
char m_writeBuf[BUFFER_SIZE];
|
||||
|
||||
std::unique_ptr<std::ofstream> m_logfp; // Log file stream
|
||||
uint64_t m_logLastTime = ~0ULL; // Last timestamp for logfile
|
||||
|
||||
public:
|
||||
typedef std::streambuf::traits_type traits_type;
|
||||
|
||||
protected:
|
||||
int overflow(int c = traits_type::eof()) override {
|
||||
char c2 = static_cast<char>(c);
|
||||
const char c2 = static_cast<char>(c);
|
||||
if (pbase() == pptr()) return 0;
|
||||
size_t size = pptr() - pbase();
|
||||
ssize_t n = ::write(m_writeFd, pbase(), size);
|
||||
// VL_PRINTF_MT("solver-write '%s'\n", std::string(pbase(), size).c_str());
|
||||
if (n == -1) perror("write");
|
||||
const size_t size = pptr() - pbase();
|
||||
log(" ", std::string(pbase(), size));
|
||||
const ssize_t n = ::write(m_writeFd, pbase(), size);
|
||||
if (VL_UNLIKELY(n == -1)) perror("write");
|
||||
if (n <= 0) {
|
||||
wait_report();
|
||||
return traits_type::eof();
|
||||
|
|
@ -86,12 +89,13 @@ protected:
|
|||
}
|
||||
int underflow() override {
|
||||
sync();
|
||||
ssize_t n = ::read(m_readFd, m_readBuf, sizeof(m_readBuf));
|
||||
if (n == -1) perror("read");
|
||||
const ssize_t n = ::read(m_readFd, m_readBuf, sizeof(m_readBuf));
|
||||
if (VL_UNLIKELY(n == -1)) perror("read");
|
||||
if (n <= 0) {
|
||||
wait_report();
|
||||
return traits_type::eof();
|
||||
}
|
||||
log("< ", std::string(m_readBuf, n));
|
||||
setg(m_readBuf, m_readBuf, m_readBuf + n);
|
||||
return traits_type::to_int_type(m_readBuf[0]);
|
||||
}
|
||||
|
|
@ -105,6 +109,7 @@ public:
|
|||
: std::streambuf{}
|
||||
, std::iostream{this}
|
||||
, m_cmd{cmd} {
|
||||
logOpen();
|
||||
open(cmd);
|
||||
}
|
||||
|
||||
|
|
@ -153,11 +158,11 @@ public:
|
|||
constexpr int P_RD = 0;
|
||||
constexpr int P_WR = 1;
|
||||
|
||||
if (pipe(fd_stdin) != 0) {
|
||||
if (VL_UNLIKELY(pipe(fd_stdin) != 0)) {
|
||||
perror("VlRProcess::open: pipe");
|
||||
return false;
|
||||
}
|
||||
if (pipe(fd_stdout) != 0) {
|
||||
if (VL_UNLIKELY(pipe(fd_stdout) != 0)) {
|
||||
perror("VlRProcess::open: pipe");
|
||||
close(fd_stdin[P_RD]);
|
||||
close(fd_stdin[P_WR]);
|
||||
|
|
@ -176,8 +181,9 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
log("", "# Open: "s + cmd[0]);
|
||||
const pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
if (VL_UNLIKELY(pid < 0)) {
|
||||
perror("VlRProcess::open: fork");
|
||||
close(fd_stdin[P_RD]);
|
||||
close(fd_stdin[P_WR]);
|
||||
|
|
@ -213,6 +219,35 @@ public:
|
|||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
void logOpen() {
|
||||
const std::string filename = Verilated::threadContextp()->solverLogFilename();
|
||||
if (filename.empty()) return;
|
||||
m_logfp = std::make_unique<std::ofstream>(filename);
|
||||
if (m_logfp.get() && m_logfp.get()->fail()) m_logfp = nullptr;
|
||||
if (!m_logfp) {
|
||||
const std::string msg = "%Error: Can't write '"s + filename + "'";
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
return;
|
||||
}
|
||||
*m_logfp << "# Verilator solver log\n";
|
||||
}
|
||||
void log(const std::string& prefix, const std::string& text) {
|
||||
if (VL_LIKELY(!m_logfp.get()) || text.empty()) return;
|
||||
if (m_logLastTime != Verilated::threadContextp()->time()) {
|
||||
m_logLastTime = Verilated::threadContextp()->time();
|
||||
*m_logfp << "# [" << Verilated::threadContextp()->timeWithUnitString() << "]\n";
|
||||
}
|
||||
std::size_t startPos = 0;
|
||||
while (1) {
|
||||
const std::size_t endPos = text.find('\n', startPos);
|
||||
if (endPos == std::string::npos) break;
|
||||
*m_logfp << prefix << text.substr(startPos, endPos - startPos) << '\n';
|
||||
startPos = endPos + 1;
|
||||
}
|
||||
if (startPos < text.length()) *m_logfp << prefix << text.substr(startPos) << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
static VlRProcess& getSolver() {
|
||||
|
|
@ -275,7 +310,8 @@ static std::string readUntilBalanced(std::istream& stream) {
|
|||
static std::string parseNestedSelect(const std::string& nested_select_expr,
|
||||
std::vector<std::string>& indices) {
|
||||
std::istringstream nestedStream(nested_select_expr);
|
||||
std::string name, idx;
|
||||
std::string name;
|
||||
std::string idx;
|
||||
nestedStream >> name;
|
||||
if (name == "(select") {
|
||||
const std::string further_nested_expr = readUntilBalanced(nestedStream);
|
||||
|
|
@ -376,76 +412,33 @@ size_t VlRandomizer::hashConstraints() const {
|
|||
return h;
|
||||
}
|
||||
|
||||
void VlRandomizer::enumerateRandcValues(const std::string& varName, VlRNG& rngr) {
|
||||
std::vector<uint64_t> values;
|
||||
const auto varIt = m_vars.find(varName);
|
||||
if (varIt == m_vars.end()) return;
|
||||
const int width = varIt->second->width();
|
||||
|
||||
std::iostream& os = getSolver();
|
||||
if (!os) return;
|
||||
|
||||
// Set up a single incremental solver session for enumeration
|
||||
os << "(set-option :produce-models true)\n";
|
||||
os << "(set-logic QF_ABV)\n";
|
||||
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
|
||||
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
|
||||
|
||||
// Declare all variables (solver needs full context for cross-var constraints)
|
||||
for (const auto& var : m_vars) {
|
||||
if (var.second->dimension() > 0) {
|
||||
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
||||
var.second->setArrayInfo(arrVarsp);
|
||||
void VlRandomizer::emitRandcExclusions(std::ostream& os) const {
|
||||
for (const auto& name : m_randcVarNames) {
|
||||
const auto usedIt = m_randcUsedValues.find(name);
|
||||
if (usedIt != m_randcUsedValues.end()) {
|
||||
const int w = m_vars.at(name)->width();
|
||||
for (const uint64_t val : usedIt->second) {
|
||||
os << "(assert (not (= " << name << " (_ bv" << val << " " << w << "))))\n";
|
||||
}
|
||||
}
|
||||
os << "(declare-fun " << var.first << " () ";
|
||||
var.second->emitType(os);
|
||||
os << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Assert all user constraints
|
||||
for (const std::string& constraint : m_constraints) {
|
||||
os << "(assert (= #b1 " << constraint << "))\n";
|
||||
static uint64_t readVarValueU64(const void* datap, int width) {
|
||||
if (width <= VL_BYTESIZE) return *static_cast<const CData*>(datap);
|
||||
if (width <= VL_SHORTSIZE) return *static_cast<const SData*>(datap);
|
||||
if (width <= VL_IDATASIZE) return *static_cast<const IData*>(datap);
|
||||
if (width <= VL_QUADSIZE) return *static_cast<const QData*>(datap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VlRandomizer::recordRandcValues() {
|
||||
for (const auto& name : m_randcVarNames) {
|
||||
const auto varIt = m_vars.find(name);
|
||||
if (varIt == m_vars.end()) continue;
|
||||
const VlRandomVar& var = *varIt->second;
|
||||
m_randcUsedValues[name].insert(readVarValueU64(var.datap(0), var.width()));
|
||||
}
|
||||
|
||||
// Incrementally enumerate all valid values for this randc variable
|
||||
while (true) {
|
||||
os << "(check-sat)\n";
|
||||
std::string sat;
|
||||
do { std::getline(os, sat); } while (sat.empty());
|
||||
if (sat != "sat") break;
|
||||
|
||||
// Read just this variable's value
|
||||
os << "(get-value (" << varName << "))\n";
|
||||
char c;
|
||||
os >> c; // '('
|
||||
os >> c; // '('
|
||||
std::string name, value;
|
||||
os >> name; // Consume variable name token from solver output
|
||||
(void)name;
|
||||
std::getline(os, value, ')');
|
||||
os >> c; // ')'
|
||||
|
||||
// Parse the SMT value to uint64_t
|
||||
VlWide<VL_WQ_WORDS_E> qowp;
|
||||
VL_SET_WQ(qowp, 0ULL);
|
||||
if (!parseSMTNum(width, qowp, value)) break;
|
||||
const uint64_t numVal = (width <= 32) ? qowp[0] : VL_SET_QW(qowp);
|
||||
|
||||
values.push_back(numVal);
|
||||
|
||||
// Exclude this value for next iteration (incremental)
|
||||
os << "(assert (not (= " << varName << " (_ bv" << numVal << " " << width << "))))\n";
|
||||
}
|
||||
|
||||
os << "(reset)\n";
|
||||
|
||||
// Shuffle using Fisher-Yates
|
||||
for (size_t i = values.size(); i > 1; --i) {
|
||||
const size_t j = VL_RANDOM_RNG_I(rngr) % i;
|
||||
std::swap(values[i - 1], values[j]);
|
||||
}
|
||||
|
||||
m_randcValueQueues[varName] = std::deque<uint64_t>(values.begin(), values.end());
|
||||
}
|
||||
|
||||
bool VlRandomizer::next(VlRNG& rngr) {
|
||||
|
|
@ -466,67 +459,30 @@ bool VlRandomizer::next(VlRNG& rngr) {
|
|||
}
|
||||
}
|
||||
|
||||
// Randc queue-based cycling: enumerate valid values once, then pop per call
|
||||
// Randc exclusion-based cycling: exclude previously used values per randc var.
|
||||
// When solver returns unsat (all values exhausted), clear history for new cycle.
|
||||
if (!m_randcVarNames.empty()) {
|
||||
const size_t currentHash = hashConstraints();
|
||||
// Invalidate queues if constraints changed (e.g., constraint_mode toggled)
|
||||
// Invalidate history if constraints changed (e.g., constraint_mode toggled)
|
||||
if (currentHash != m_randcConstraintHash) {
|
||||
m_randcValueQueues.clear();
|
||||
m_randcUsedValues.clear();
|
||||
m_randcConstraintHash = currentHash;
|
||||
}
|
||||
// Refill empty queues (start of new cycle)
|
||||
for (const auto& name : m_randcVarNames) {
|
||||
auto& queue = m_randcValueQueues[name];
|
||||
if (queue.empty()) enumerateRandcValues(name, rngr);
|
||||
}
|
||||
}
|
||||
|
||||
// Pop randc values from queues (will be pinned in solver)
|
||||
std::map<std::string, uint64_t> randcPinned;
|
||||
for (const auto& name : m_randcVarNames) {
|
||||
auto& queue = m_randcValueQueues[name];
|
||||
if (queue.empty()) return false; // No valid values exist
|
||||
randcPinned[name] = queue.front();
|
||||
queue.pop_front();
|
||||
}
|
||||
|
||||
// If solve-before constraints are present, use phased solving
|
||||
if (!m_solveBefore.empty()) return nextPhased(rngr);
|
||||
|
||||
std::iostream& os = getSolver();
|
||||
if (!os) return false;
|
||||
// Randc retry: if unsat due to randc exhaustion, clear history and retry once
|
||||
const bool hasRandc = !m_randcVarNames.empty();
|
||||
for (int attempt = 0; attempt < (hasRandc ? 2 : 1); ++attempt) {
|
||||
std::iostream& os = getSolver();
|
||||
if (!os) return false;
|
||||
|
||||
os << "(set-option :produce-models true)\n";
|
||||
os << "(set-logic QF_ABV)\n";
|
||||
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
|
||||
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
|
||||
for (const auto& var : m_vars) {
|
||||
if (var.second->dimension() > 0) {
|
||||
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
||||
var.second->setArrayInfo(arrVarsp);
|
||||
}
|
||||
os << "(declare-fun " << var.first << " () ";
|
||||
var.second->emitType(os);
|
||||
os << ")\n";
|
||||
}
|
||||
|
||||
for (const std::string& constraint : m_constraints) {
|
||||
os << "(assert (= #b1 " << constraint << "))\n";
|
||||
}
|
||||
|
||||
// Pin randc values from pre-enumerated queues
|
||||
for (const auto& pair : randcPinned) {
|
||||
const int w = m_vars.at(pair.first)->width();
|
||||
os << "(assert (= " << pair.first << " (_ bv" << pair.second << " " << w << ")))\n";
|
||||
}
|
||||
|
||||
os << "(check-sat)\n";
|
||||
|
||||
bool sat = parseSolution(os, true);
|
||||
if (!sat) {
|
||||
// If unsat, use named assertions to get unsat-core
|
||||
os << "(reset)\n";
|
||||
os << "(set-option :produce-unsat-cores true)\n";
|
||||
// Soft constraint relaxation (IEEE 1800-2023 18.5.13, last-wins priority):
|
||||
// Try hard + soft[0..N-1], then hard + soft[1..N-1], ..., then hard only.
|
||||
// First SAT phase wins. If hard-only is UNSAT, report via unsat-core.
|
||||
os << "(set-option :produce-models true)\n";
|
||||
os << "(set-logic QF_ABV)\n";
|
||||
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
|
||||
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
|
||||
|
|
@ -539,28 +495,102 @@ bool VlRandomizer::next(VlRNG& rngr) {
|
|||
var.second->emitType(os);
|
||||
os << ")\n";
|
||||
}
|
||||
int j = 0;
|
||||
for (const std::string& constraint : m_constraints) {
|
||||
os << "(assert (! (= #b1 " << constraint << ") :named cons" << j << "))\n";
|
||||
j++;
|
||||
}
|
||||
os << "(check-sat)\n";
|
||||
sat = parseSolution(os, true);
|
||||
(void)sat;
|
||||
os << "(reset)\n";
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
|
||||
os << "(assert ";
|
||||
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
|
||||
os << ")\n";
|
||||
os << "\n(check-sat)\n";
|
||||
sat = parseSolution(os, false);
|
||||
(void)sat;
|
||||
}
|
||||
|
||||
os << "(reset)\n";
|
||||
return true;
|
||||
for (const std::string& constraint : m_constraints) {
|
||||
os << "(assert (= #b1 " << constraint << "))\n";
|
||||
}
|
||||
|
||||
// Randc: exclude previously used values to enforce cyclic non-repetition
|
||||
emitRandcExclusions(os);
|
||||
|
||||
const size_t nSoft = m_softConstraints.size();
|
||||
bool sat = false;
|
||||
if (nSoft > 0) {
|
||||
// Fast path: try all soft constraints at once
|
||||
os << "(push 1)\n";
|
||||
for (const auto& s : m_softConstraints) os << "(assert (= #b1 " << s << "))\n";
|
||||
os << "(check-sat)\n";
|
||||
sat = parseSolution(os, false);
|
||||
if (!sat) {
|
||||
// Some soft constraints conflict. Incrementally add from back
|
||||
// (highest priority first), keeping only compatible ones.
|
||||
// This preserves the maximum set of compatible soft constraints.
|
||||
os << "(pop 1)\n";
|
||||
for (int i = static_cast<int>(nSoft) - 1; i >= 0; --i) {
|
||||
os << "(push 1)\n";
|
||||
os << "(assert (= #b1 " << m_softConstraints[i] << "))\n";
|
||||
os << "(check-sat)\n";
|
||||
if (checkSat(os)) {
|
||||
// Compatible -- keep this push level
|
||||
} else {
|
||||
// Incompatible -- remove this soft constraint
|
||||
os << "(pop 1)\n";
|
||||
}
|
||||
}
|
||||
// Read solution with remaining compatible soft constraints
|
||||
os << "(check-sat)\n";
|
||||
sat = parseSolution(os, false);
|
||||
}
|
||||
} else {
|
||||
// No soft constraints -- hard-only
|
||||
os << "(check-sat)\n";
|
||||
sat = parseSolution(os, false);
|
||||
}
|
||||
|
||||
if (!sat) {
|
||||
os << "(reset)\n";
|
||||
// If randc vars have used values, this may be cycle exhaustion - retry
|
||||
if (hasRandc && !m_randcUsedValues.empty() && attempt == 0) {
|
||||
m_randcUsedValues.clear();
|
||||
continue; // Retry without exclusions
|
||||
}
|
||||
// Genuine unsat: report via unsat-core
|
||||
os << "(set-option :produce-unsat-cores true)\n";
|
||||
os << "(set-logic QF_ABV)\n";
|
||||
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
|
||||
os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
|
||||
for (const auto& var : m_vars) {
|
||||
if (var.second->dimension() > 0) {
|
||||
auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
|
||||
var.second->setArrayInfo(arrVarsp);
|
||||
}
|
||||
os << "(declare-fun " << var.first << " () ";
|
||||
var.second->emitType(os);
|
||||
os << ")\n";
|
||||
}
|
||||
int j = 0;
|
||||
for (const std::string& constraint : m_constraints) {
|
||||
os << "(assert (! (= #b1 " << constraint << ") :named cons" << j++ << "))\n";
|
||||
}
|
||||
os << "(check-sat)\n";
|
||||
sat = parseSolution(os, true);
|
||||
(void)sat;
|
||||
os << "(reset)\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
|
||||
os << "(assert ";
|
||||
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
|
||||
os << ")\n";
|
||||
os << "\n(check-sat)\n";
|
||||
sat = parseSolution(os, false);
|
||||
(void)sat;
|
||||
}
|
||||
|
||||
// Record solved randc values for future exclusion
|
||||
recordRandcValues();
|
||||
|
||||
os << "(reset)\n";
|
||||
return true;
|
||||
}
|
||||
return false; // Should not reach here
|
||||
}
|
||||
|
||||
bool VlRandomizer::checkSat(std::iostream& os) {
|
||||
std::string result;
|
||||
do { std::getline(os, result); } while (result.empty());
|
||||
return result == "sat";
|
||||
}
|
||||
|
||||
bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
|
||||
|
|
@ -573,7 +603,7 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
|
|||
std::getline(os, sat);
|
||||
std::vector<int> numbers;
|
||||
std::string currentNum;
|
||||
for (char c : sat) {
|
||||
for (const char c : sat) {
|
||||
if (std::isdigit(c)) {
|
||||
currentNum += c;
|
||||
numbers.push_back(std::stoi(currentNum));
|
||||
|
|
@ -581,14 +611,14 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
|
|||
}
|
||||
}
|
||||
if (Verilated::threadContextp()->warnUnsatConstr()) {
|
||||
for (int n : numbers) {
|
||||
for (const int n : numbers) {
|
||||
if (n < m_constraints_line.size()) {
|
||||
const std::string& constraint_info = m_constraints_line[n];
|
||||
// Parse "filename:linenum source" format
|
||||
size_t colon_pos = constraint_info.find(':');
|
||||
const size_t colon_pos = constraint_info.find(':');
|
||||
if (colon_pos != std::string::npos) {
|
||||
std::string filename = constraint_info.substr(0, colon_pos);
|
||||
size_t space_pos = constraint_info.find(" ", colon_pos);
|
||||
const std::string filename = constraint_info.substr(0, colon_pos);
|
||||
const size_t space_pos = constraint_info.find(" ", colon_pos);
|
||||
std::string linenum_str;
|
||||
std::string source;
|
||||
if (space_pos != std::string::npos) {
|
||||
|
|
@ -602,7 +632,7 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
|
|||
std::string msg = "UNSATCONSTR: Unsatisfied constraint";
|
||||
if (!source.empty()) {
|
||||
// Trim leading whitespace and add quotes
|
||||
size_t start = source.find_first_not_of(" \t");
|
||||
const size_t start = source.find_first_not_of(" \t");
|
||||
if (start != std::string::npos) {
|
||||
msg += ": '" + source.substr(start) + "'";
|
||||
}
|
||||
|
|
@ -650,7 +680,9 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
|
|||
"Internal: Unable to parse solver's response: invalid S-expression");
|
||||
return false;
|
||||
}
|
||||
std::string name, idx, value;
|
||||
std::string name;
|
||||
std::string idx;
|
||||
std::string value;
|
||||
std::vector<std::string> indices;
|
||||
os >> name;
|
||||
indices.clear();
|
||||
|
|
@ -665,6 +697,7 @@ bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
|
|||
if (m_randmodep && !varr.randModeIdxNone()) {
|
||||
if (!m_randmodep->at(varr.randModeIdx())) continue;
|
||||
}
|
||||
if (m_disabledVars.count(name)) continue;
|
||||
if (!indices.empty()) {
|
||||
std::ostringstream oss;
|
||||
oss << varr.name();
|
||||
|
|
@ -723,25 +756,40 @@ void VlRandomizer::hard(std::string&& constraint, const char* filename, uint32_t
|
|||
}
|
||||
}
|
||||
|
||||
void VlRandomizer::soft(std::string&& constraint, const char* /*filename*/, uint32_t /*linenum*/,
|
||||
const char* /*source*/) {
|
||||
m_softConstraints.emplace_back(std::move(constraint));
|
||||
}
|
||||
|
||||
void VlRandomizer::disable_soft(const std::string& varName) {
|
||||
// IEEE 1800-2023 18.5.13: Remove all soft constraints referencing the variable
|
||||
m_softConstraints.erase(
|
||||
std::remove_if(m_softConstraints.begin(), m_softConstraints.end(),
|
||||
[&](const std::string& c) { return c.find(varName) != std::string::npos; }),
|
||||
m_softConstraints.end());
|
||||
}
|
||||
|
||||
void VlRandomizer::clearConstraints() {
|
||||
m_constraints.clear();
|
||||
m_constraints_line.clear();
|
||||
m_solveBefore.clear();
|
||||
m_softConstraints.clear();
|
||||
// Keep m_vars for class member randomization
|
||||
}
|
||||
|
||||
void VlRandomizer::clearAll() {
|
||||
m_constraints.clear();
|
||||
m_softConstraints.clear();
|
||||
m_vars.clear();
|
||||
m_randcVarNames.clear();
|
||||
m_randcValueQueues.clear();
|
||||
m_randcUsedValues.clear();
|
||||
m_randcConstraintHash = 0;
|
||||
}
|
||||
|
||||
void VlRandomizer::markRandc(const char* name) { m_randcVarNames.insert(name); }
|
||||
|
||||
void VlRandomizer::solveBefore(const char* beforeName, const char* afterName) {
|
||||
m_solveBefore.emplace_back(std::string(beforeName), std::string(afterName));
|
||||
void VlRandomizer::solveBefore(const std::string& beforeName, const std::string& afterName) {
|
||||
m_solveBefore.emplace_back(beforeName, afterName);
|
||||
}
|
||||
|
||||
bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
||||
|
|
@ -845,6 +893,9 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
|||
os << "(assert (= #b1 " << constraint << "))\n";
|
||||
}
|
||||
|
||||
// Randc: exclude previously used values
|
||||
emitRandcExclusions(os);
|
||||
|
||||
// Initial check-sat WITHOUT diversity (guaranteed sat if constraints are consistent)
|
||||
os << "(check-sat)\n";
|
||||
|
||||
|
|
@ -852,9 +903,12 @@ bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
|||
// Final phase: use parseSolution to write ALL values to memory
|
||||
bool sat = parseSolution(os, true);
|
||||
if (!sat) {
|
||||
if (!m_randcVarNames.empty()) m_randcUsedValues.clear();
|
||||
os << "(reset)\n";
|
||||
return false;
|
||||
}
|
||||
// Record solved randc values for future exclusion
|
||||
recordRandcValues();
|
||||
// Diversity loop (same as normal next())
|
||||
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
|
||||
os << "(assert ";
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@
|
|||
|
||||
#include "verilated.h"
|
||||
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
//=============================================================================
|
||||
|
|
@ -75,7 +75,7 @@ public:
|
|||
std::string name() const { return m_name; }
|
||||
int width() const { return m_width; }
|
||||
int dimension() const { return m_dimension; }
|
||||
virtual void* datap(int idx) const { return m_datap; }
|
||||
virtual void* datap(int /*idx*/) const { return m_datap; }
|
||||
std::uint32_t randModeIdx() const { return m_randModeIdx; }
|
||||
bool randModeIdxNone() const { return randModeIdx() == std::numeric_limits<unsigned>::max(); }
|
||||
bool set(const std::string& idx, const std::string& val) const;
|
||||
|
|
@ -88,6 +88,7 @@ public:
|
|||
m_arrVarsRefp = arrVarsRefp;
|
||||
}
|
||||
mutable std::map<std::string, int> count_cache;
|
||||
void clearCountCache() const { count_cache.clear(); }
|
||||
int countMatchingElements(const ArrayInfoMap& arr_vars, const std::string& base_name) const {
|
||||
if (VL_LIKELY(count_cache.find(base_name) != count_cache.end()))
|
||||
return count_cache[base_name];
|
||||
|
|
@ -109,12 +110,9 @@ public:
|
|||
void* datap(int idx) const override {
|
||||
const std::string indexed_name = name() + std::to_string(idx);
|
||||
const auto it = m_arrVarsRefp->find(indexed_name);
|
||||
if (it != m_arrVarsRefp->end()) {
|
||||
return it->second->m_datap;
|
||||
} else {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
|
||||
return nullptr; // LCOV_EXCL_BR_LINE
|
||||
}
|
||||
if (VL_LIKELY(it != m_arrVarsRefp->end())) return it->second->m_datap;
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
|
||||
return nullptr; // LCOV_EXCL_BR_LINE
|
||||
}
|
||||
void emitHexs(std::ostream& s, const std::vector<IData>& indices, const size_t bit_width,
|
||||
size_t idx) const {
|
||||
|
|
@ -172,7 +170,14 @@ public:
|
|||
for (int i = 0; i < dimension(); ++i) s << ")";
|
||||
}
|
||||
} else {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
|
||||
if (dimension() > 0) {
|
||||
for (int i = 0; i < dimension(); ++i) s << "(Array (_ BitVec 32) ";
|
||||
s << "(_ BitVec " << width() << ")";
|
||||
for (int i = 0; i < dimension(); ++i) s << ")";
|
||||
} else {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "randomize",
|
||||
"indexed_name not found in m_arr_vars");
|
||||
}
|
||||
}
|
||||
}
|
||||
int totalWidth() const override {
|
||||
|
|
@ -200,27 +205,31 @@ public:
|
|||
// Object holding constraints and variable references.
|
||||
class VlRandomizer VL_NOT_FINAL {
|
||||
// MEMBERS
|
||||
std::vector<std::string> m_constraints; // Solver-dependent constraints
|
||||
std::vector<std::string> m_constraints; // Solver-dependent hard constraints
|
||||
std::vector<std::string>
|
||||
m_constraints_line; // fileline content of the constraint for unsat constraints
|
||||
std::vector<std::string> m_softConstraints; // Soft constraints
|
||||
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
|
||||
// variables
|
||||
std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back)
|
||||
// variables
|
||||
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
|
||||
std::vector<std::string> m_unique_arrays;
|
||||
std::map<std::string, uint32_t> m_unique_array_sizes;
|
||||
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
|
||||
int m_index = 0; // Internal counter for key generation
|
||||
std::set<std::string> m_randcVarNames; // Names of randc variables for cyclic tracking
|
||||
std::map<std::string, std::deque<uint64_t>>
|
||||
m_randcValueQueues; // Remaining values per randc var (queue-based cycling)
|
||||
size_t m_randcConstraintHash = 0; // Hash of constraints when queues were built
|
||||
std::map<std::string, std::set<uint64_t>>
|
||||
m_randcUsedValues; // Previously used values per randc var (exclusion-based cycling)
|
||||
size_t m_randcConstraintHash = 0; // Hash of constraints when history was valid
|
||||
std::vector<std::pair<std::string, std::string>>
|
||||
m_solveBefore; // Solve-before ordering pairs (beforeVar, afterVar)
|
||||
|
||||
// PRIVATE METHODS
|
||||
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
|
||||
bool parseSolution(std::iostream& file, bool log = false);
|
||||
void enumerateRandcValues(const std::string& varName, VlRNG& rngr);
|
||||
bool parseSolution(std::iostream& os, bool log = false);
|
||||
bool checkSat(std::iostream& os);
|
||||
void emitRandcExclusions(std::ostream& os) const; // Emit randc exclusion constraints
|
||||
void recordRandcValues(); // Record solved randc values for future exclusion
|
||||
size_t hashConstraints() const;
|
||||
bool nextPhased(VlRNG& rngr); // Phased solving for solve...before
|
||||
|
||||
|
|
@ -328,6 +337,12 @@ public:
|
|||
"supported currently.");
|
||||
}
|
||||
|
||||
// Mark a variable as rand_mode-disabled: solver keeps it in m_vars
|
||||
// (so constraints still reference it) but skips write-back after solving.
|
||||
void set_var_disabled(const char* name) { m_disabledVars.insert(name); }
|
||||
// Clear disabled state for a variable
|
||||
void clear_var_disabled(const char* name) { m_disabledVars.erase(name); }
|
||||
|
||||
// --- write_var to register variables ---
|
||||
// Register scalar variable (non-struct, basic type)
|
||||
template <typename T>
|
||||
|
|
@ -354,12 +369,15 @@ public:
|
|||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlQueue<T>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (m_vars.find(name) != m_vars.end()) return;
|
||||
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
if (m_vars.find(name) == m_vars.end()) {
|
||||
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
}
|
||||
if (dimension > 0) {
|
||||
m_index = 0;
|
||||
clear_arr_table(name);
|
||||
record_arr_table(var, name, dimension, {}, {});
|
||||
m_vars[name]->clearCountCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -376,21 +394,23 @@ public:
|
|||
write_var(VlUnpacked<T, N_Depth>& var, uint32_t width, const std::string& name,
|
||||
uint32_t dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
|
||||
if (m_vars.find(name) != m_vars.end()) return;
|
||||
|
||||
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
if (m_vars.find(name) == m_vars.end()) {
|
||||
m_vars[name]
|
||||
= std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
}
|
||||
|
||||
if (dimension > 0) {
|
||||
m_index = 0;
|
||||
clear_arr_table(name);
|
||||
record_arr_table(var, name, dimension, {}, {});
|
||||
m_vars[name]->clearCountCache();
|
||||
}
|
||||
}
|
||||
// Register unpacked array of structs
|
||||
template <typename T, std::size_t N_Depth>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlUnpacked<T, N_Depth>& var, int width, const char* name, int dimension,
|
||||
write_var(VlUnpacked<T, N_Depth>& var, int /*width*/, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
}
|
||||
|
|
@ -400,20 +420,23 @@ public:
|
|||
typename std::enable_if<!VlContainsCustomStruct<T_Value>::value, void>::type
|
||||
write_var(VlAssocArray<T_Key, T_Value>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (m_vars.find(name) != m_vars.end()) return;
|
||||
m_vars[name]
|
||||
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
if (m_vars.find(name) == m_vars.end()) {
|
||||
m_vars[name]
|
||||
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
}
|
||||
if (dimension > 0) {
|
||||
m_index = 0;
|
||||
clear_arr_table(name);
|
||||
record_arr_table(var, name, dimension, {}, {});
|
||||
m_vars[name]->clearCountCache();
|
||||
}
|
||||
}
|
||||
|
||||
// Register associative array of structs
|
||||
template <typename T_Key, typename T_Value>
|
||||
typename std::enable_if<VlContainsCustomStruct<T_Value>::value, void>::type
|
||||
write_var(VlAssocArray<T_Key, T_Value>& var, int width, const char* name, int dimension,
|
||||
write_var(VlAssocArray<T_Key, T_Value>& var, int /*width*/, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
}
|
||||
|
|
@ -423,8 +446,8 @@ public:
|
|||
// Record a flat (non-class) element into the array variable table
|
||||
template <typename T>
|
||||
typename std::enable_if<!std::is_class<T>::value || VlIsVlWide<T>::value, void>::type
|
||||
record_arr_table(T& var, const std::string& name, int dimension, std::vector<IData> indices,
|
||||
std::vector<size_t> idxWidths) {
|
||||
record_arr_table(T& var, const std::string& name, int /*dimension*/,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
const std::string key = generateKey(name, m_index);
|
||||
m_arr_vars[key] = std::make_shared<ArrayInfo>(name, &var, m_index, indices, idxWidths);
|
||||
++m_index;
|
||||
|
|
@ -499,8 +522,8 @@ public:
|
|||
// Register a single structArray element via write_var
|
||||
template <typename T>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
record_struct_arr(T& var, const std::string& name, int dimension, std::vector<IData> indices,
|
||||
std::vector<size_t> idxWidths) {
|
||||
record_struct_arr(T& var, const std::string& name, int /*dimension*/,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
std::ostringstream oss;
|
||||
for (size_t i = 0; i < indices.size(); ++i) {
|
||||
oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0')
|
||||
|
|
@ -580,7 +603,7 @@ public:
|
|||
}
|
||||
|
||||
// Helper: Generate unique variable key from name and index
|
||||
std::string generateKey(const std::string& name, int idx) {
|
||||
static std::string generateKey(const std::string& name, int idx) {
|
||||
if (!name.empty() && name[0] == '\\') {
|
||||
const size_t space_pos = name.find(' ');
|
||||
return (space_pos != std::string::npos ? name.substr(0, space_pos) : name)
|
||||
|
|
@ -591,13 +614,31 @@ public:
|
|||
+ std::to_string(idx);
|
||||
}
|
||||
|
||||
// Helper: Clear existing array element entries for a base name
|
||||
void clear_arr_table(const std::string& name) {
|
||||
for (int index = 0;; ++index) {
|
||||
const std::string key = generateKey(name, index);
|
||||
const auto it = m_arr_vars.find(key);
|
||||
if (it == m_arr_vars.end()) break;
|
||||
m_arr_vars.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void hard(std::string&& constraint, const char* filename = "", uint32_t linenum = 0,
|
||||
const char* source = "");
|
||||
void soft(std::string&& constraint, const char* filename = "", uint32_t linenum = 0,
|
||||
const char* source = "");
|
||||
void pin_var(const char* name, int width, uint64_t value) {
|
||||
std::string constraint = "(__Vbv (= "s + name + " (_ bv" + std::to_string(value) + " "
|
||||
+ std::to_string(width) + ")))";
|
||||
hard(std::move(constraint));
|
||||
}
|
||||
void disable_soft(const std::string& varName);
|
||||
void clearConstraints();
|
||||
void clearAll(); // Clear both constraints and variables
|
||||
void markRandc(const char* name); // Mark variable as randc for cyclic tracking
|
||||
void solveBefore(const char* beforeName,
|
||||
const char* afterName); // Register solve-before ordering
|
||||
void solveBefore(const std::string& beforeName,
|
||||
const std::string& afterName); // Register solve-before ordering
|
||||
void set_randmode(const VlQueue<CData>& randmode) { m_randmodep = &randmode; }
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
|
|
|
|||
|
|
@ -88,14 +88,15 @@ public:
|
|||
|
||||
class VerilatedSaifActivityVar final {
|
||||
// MEMBERS
|
||||
uint64_t m_lastTime = 0; // Last time when variable value was updated
|
||||
uint64_t m_lastTime; // Last time when variable value was updated
|
||||
VerilatedSaifActivityBit* m_bits; // Pointer to variable bits objects
|
||||
uint32_t m_width; // Width of variable (in bits)
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedSaifActivityVar(uint32_t width, VerilatedSaifActivityBit* bits)
|
||||
: m_bits{bits}
|
||||
VerilatedSaifActivityVar(uint64_t startTime, uint32_t width, VerilatedSaifActivityBit* bits)
|
||||
: m_lastTime{startTime}
|
||||
, m_bits{bits}
|
||||
, m_width{width} {}
|
||||
|
||||
VerilatedSaifActivityVar(VerilatedSaifActivityVar&&) = default;
|
||||
|
|
@ -139,9 +140,9 @@ class VerilatedSaifActivityScope final {
|
|||
// Name of the activity scope
|
||||
std::string m_scopeName;
|
||||
// Array indices of child scopes
|
||||
std::vector<std::unique_ptr<VerilatedSaifActivityScope>> m_childScopes{};
|
||||
std::vector<std::unique_ptr<VerilatedSaifActivityScope>> m_childScopes;
|
||||
// Children signals codes mapped to their names in the current scope
|
||||
std::vector<std::pair<uint32_t, std::string>> m_childActivities{};
|
||||
std::vector<std::pair<uint32_t, std::string>> m_childActivities;
|
||||
// Parent scope pointer
|
||||
VerilatedSaifActivityScope* m_parentScope = nullptr;
|
||||
|
||||
|
|
@ -203,7 +204,7 @@ class VerilatedSaifActivityAccumulator final {
|
|||
public:
|
||||
// METHODS
|
||||
void declare(uint32_t code, const std::string& absoluteScopePath, std::string variableName,
|
||||
int bits, bool array, int arraynum);
|
||||
int bits, bool array, int arraynum, uint64_t startTime);
|
||||
|
||||
// CONSTRUCTORS
|
||||
VerilatedSaifActivityAccumulator() = default;
|
||||
|
|
@ -252,7 +253,7 @@ VerilatedSaifActivityBit& VerilatedSaifActivityVar::bit(const std::size_t index)
|
|||
|
||||
void VerilatedSaifActivityAccumulator::declare(uint32_t code, const std::string& absoluteScopePath,
|
||||
std::string variableName, int bits, bool array,
|
||||
int arraynum) {
|
||||
int arraynum, uint64_t startTime) {
|
||||
const size_t block_size = 1024;
|
||||
if (m_activityArena.empty()
|
||||
|| m_activityArena.back().size() + bits > m_activityArena.back().capacity()) {
|
||||
|
|
@ -268,7 +269,7 @@ void VerilatedSaifActivityAccumulator::declare(uint32_t code, const std::string&
|
|||
variableName += ']';
|
||||
}
|
||||
m_scopeToActivities[absoluteScopePath].emplace_back(code, variableName);
|
||||
m_activity.emplace(code, VerilatedSaifActivityVar{static_cast<uint32_t>(bits),
|
||||
m_activity.emplace(code, VerilatedSaifActivityVar{startTime, static_cast<uint32_t>(bits),
|
||||
m_activityArena.back().data() + bitsIdx});
|
||||
}
|
||||
|
||||
|
|
@ -277,18 +278,18 @@ void VerilatedSaifActivityAccumulator::declare(uint32_t code, const std::string&
|
|||
//=============================================================================
|
||||
// VerilatedSaif implementation
|
||||
|
||||
VerilatedSaif::VerilatedSaif(void* filep) {
|
||||
m_activityAccumulators.emplace_back(std::make_unique<VerilatedSaifActivityAccumulator>());
|
||||
}
|
||||
VerilatedSaif::VerilatedSaif(void* /*filep*/) {}
|
||||
|
||||
void VerilatedSaif::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (isOpen()) return;
|
||||
|
||||
m_startTime = currentTime();
|
||||
m_filename = filename; // "" is ok, as someone may overload open
|
||||
m_filep = ::open(m_filename.c_str(),
|
||||
O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE | O_NONBLOCK | O_CLOEXEC, 0666);
|
||||
m_isOpen = true;
|
||||
m_activityAccumulators.emplace_back(std::make_unique<VerilatedSaifActivityAccumulator>());
|
||||
|
||||
initializeSaifFileContents();
|
||||
|
||||
|
|
@ -328,7 +329,7 @@ void VerilatedSaif::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
|||
|
||||
void VerilatedSaif::finalizeSaifFileContents() {
|
||||
printStr("(DURATION ");
|
||||
printStr(std::to_string(currentTime()));
|
||||
printStr(std::to_string(currentTime() - m_startTime));
|
||||
printStr(")\n");
|
||||
|
||||
incrementIndent();
|
||||
|
|
@ -377,8 +378,7 @@ bool VerilatedSaif::printScopeActivitiesFromAccumulatorIfPresent(
|
|||
|
||||
for (const auto& childSignal : accumulator.m_scopeToActivities.at(absoluteScopePath)) {
|
||||
VerilatedSaifActivityVar& activityVariable = accumulator.m_activity.at(childSignal.first);
|
||||
anyNetWritten
|
||||
= printActivityStats(activityVariable, childSignal.second.c_str(), anyNetWritten);
|
||||
anyNetWritten = printActivityStats(activityVariable, childSignal.second, anyNetWritten);
|
||||
}
|
||||
|
||||
return anyNetWritten;
|
||||
|
|
@ -419,7 +419,7 @@ bool VerilatedSaif::printActivityStats(VerilatedSaifActivityVar& activity,
|
|||
|
||||
// We only have two-value logic so TZ, TX and TB will always be 0
|
||||
printStr(" (T0 ");
|
||||
printStr(std::to_string(currentTime() - bit.highTime()));
|
||||
printStr(std::to_string(currentTime() - m_startTime - bit.highTime()));
|
||||
printStr(") (T1 ");
|
||||
printStr(std::to_string(bit.highTime()));
|
||||
printStr(") (TZ 0) (TX 0) (TB 0) (TC ");
|
||||
|
|
@ -451,7 +451,8 @@ void VerilatedSaif::printStr(const std::string& str) {
|
|||
void VerilatedSaif::writeBuffered(bool force) {
|
||||
if (VL_UNLIKELY(m_buffer.size() >= WRITE_BUFFER_SIZE || force)) {
|
||||
if (VL_UNLIKELY(!m_buffer.empty())) {
|
||||
::write(m_filep, m_buffer.data(), m_buffer.size());
|
||||
const ssize_t n = ::write(m_filep, m_buffer.data(), m_buffer.size());
|
||||
assert(n == static_cast<ssize_t>(m_buffer.size()));
|
||||
m_buffer = "";
|
||||
m_buffer.reserve(WRITE_BUFFER_SIZE * 2);
|
||||
}
|
||||
|
|
@ -486,7 +487,8 @@ void VerilatedSaif::pushPrefix(const char* namep, VerilatedTracePrefixType type)
|
|||
// Upper has name, we can suppress inserting $rootio, but still push so popPrefix works
|
||||
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
|
||||
return;
|
||||
} else if (name.empty()) {
|
||||
}
|
||||
if (name.empty()) {
|
||||
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
|
||||
return;
|
||||
}
|
||||
|
|
@ -511,9 +513,9 @@ void VerilatedSaif::pushPrefix(const char* namep, VerilatedTracePrefixType type)
|
|||
}
|
||||
|
||||
const std::string newPrefix = prevPrefix + name;
|
||||
bool properScope = (type != VerilatedTracePrefixType::ARRAY_UNPACKED
|
||||
&& type != VerilatedTracePrefixType::ARRAY_PACKED
|
||||
&& type != VerilatedTracePrefixType::ROOTIO_WRAPPER);
|
||||
const bool properScope = (type != VerilatedTracePrefixType::ARRAY_UNPACKED
|
||||
&& type != VerilatedTracePrefixType::ARRAY_PACKED
|
||||
&& type != VerilatedTracePrefixType::ROOTIO_WRAPPER);
|
||||
m_prefixStack.emplace_back(newPrefix + (properScope ? " " : ""), type);
|
||||
}
|
||||
|
||||
|
|
@ -529,8 +531,8 @@ void VerilatedSaif::popPrefix() {
|
|||
}
|
||||
|
||||
void VerilatedSaif::declare(const uint32_t code, uint32_t fidx, const char* name,
|
||||
const char* wirep, const bool array, const int arraynum,
|
||||
const bool bussed, const int msb, const int lsb) {
|
||||
const char* /*wirep*/, const bool array, const int arraynum,
|
||||
const bool /*bussed*/, const int msb, const int lsb) {
|
||||
assert(m_activityAccumulators.size() > fidx);
|
||||
VerilatedSaifActivityAccumulator& accumulator = *m_activityAccumulators.at(fidx);
|
||||
|
||||
|
|
@ -544,51 +546,64 @@ void VerilatedSaif::declare(const uint32_t code, uint32_t fidx, const char* name
|
|||
m_currentScope->addActivityVar(code, variableName);
|
||||
|
||||
accumulator.declare(code, m_currentScope->path(), std::move(variableName), bits, array,
|
||||
arraynum);
|
||||
arraynum, m_startTime);
|
||||
}
|
||||
|
||||
void VerilatedSaif::declEvent(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int dtypenum, const VerilatedTraceSigDirection,
|
||||
const VerilatedTraceSigKind, const VerilatedTraceSigType,
|
||||
const bool array, const int arraynum) {
|
||||
declare(code, fidx, name, "event", array, arraynum, false, 0, 0);
|
||||
// versions to call when the sig is not array member
|
||||
void VerilatedSaif::declEvent(const uint32_t code, const uint32_t fidx, const char* name) {
|
||||
declare(code, fidx, name, "event", false, -1, false, 0, 0);
|
||||
}
|
||||
|
||||
void VerilatedSaif::declBit(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int dtypenum, const VerilatedTraceSigDirection,
|
||||
const VerilatedTraceSigKind, const VerilatedTraceSigType,
|
||||
const bool array, const int arraynum) {
|
||||
declare(code, fidx, name, "wire", array, arraynum, false, 0, 0);
|
||||
void VerilatedSaif::declBit(const uint32_t code, const uint32_t fidx, const char* name) {
|
||||
declare(code, fidx, name, "wire", false, -1, false, 0, 0);
|
||||
}
|
||||
void VerilatedSaif::declBus(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int dtypenum, const VerilatedTraceSigDirection,
|
||||
const VerilatedTraceSigKind, const VerilatedTraceSigType,
|
||||
const bool array, const int arraynum, const int msb, const int lsb) {
|
||||
declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb);
|
||||
const int msb, const int lsb) {
|
||||
declare(code, fidx, name, "wire", false, -1, true, msb, lsb);
|
||||
}
|
||||
void VerilatedSaif::declQuad(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int dtypenum, const VerilatedTraceSigDirection,
|
||||
const VerilatedTraceSigKind, const VerilatedTraceSigType,
|
||||
const bool array, const int arraynum, const int msb, const int lsb) {
|
||||
declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb);
|
||||
const int msb, const int lsb) {
|
||||
declare(code, fidx, name, "wire", false, -1, true, msb, lsb);
|
||||
}
|
||||
void VerilatedSaif::declArray(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int dtypenum, const VerilatedTraceSigDirection,
|
||||
const VerilatedTraceSigKind, const VerilatedTraceSigType,
|
||||
const bool array, const int arraynum, const int msb, const int lsb) {
|
||||
declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb);
|
||||
void VerilatedSaif::declWide(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int msb, const int lsb) {
|
||||
declare(code, fidx, name, "wire", false, -1, true, msb, lsb);
|
||||
}
|
||||
void VerilatedSaif::declDouble(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int dtypenum, const VerilatedTraceSigDirection,
|
||||
const VerilatedTraceSigKind, const VerilatedTraceSigType,
|
||||
const bool array, const int arraynum) {
|
||||
declare(code, fidx, name, "real", array, arraynum, false, 63, 0);
|
||||
void VerilatedSaif::declDouble(const uint32_t code, const uint32_t fidx, const char* name) {
|
||||
declare(code, fidx, name, "real", false, -1, false, 63, 0);
|
||||
}
|
||||
|
||||
// versions to call when the sig is array member
|
||||
void VerilatedSaif::declEventArray(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int arraynum) {
|
||||
declare(code, fidx, name, "event", true, arraynum, false, 0, 0);
|
||||
}
|
||||
void VerilatedSaif::declBitArray(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int arraynum) {
|
||||
declare(code, fidx, name, "wire", true, arraynum, false, 0, 0);
|
||||
}
|
||||
void VerilatedSaif::declBusArray(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int arraynum, const int msb, const int lsb) {
|
||||
declare(code, fidx, name, "wire", true, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedSaif::declQuadArray(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int arraynum, const int msb, const int lsb) {
|
||||
declare(code, fidx, name, "wire", true, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedSaif::declWideArray(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int arraynum, const int msb, const int lsb) {
|
||||
declare(code, fidx, name, "wire", true, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedSaif::declDoubleArray(const uint32_t code, const uint32_t fidx, const char* name,
|
||||
const int arraynum) {
|
||||
declare(code, fidx, name, "real", true, arraynum, false, 63, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Get/commit trace buffer
|
||||
|
||||
VerilatedSaif::Buffer* VerilatedSaif::getTraceBuffer(uint32_t fidx) { return new Buffer{*this}; }
|
||||
VerilatedSaif::Buffer* VerilatedSaif::getTraceBuffer(uint32_t /*fidx*/) {
|
||||
return new Buffer{*this};
|
||||
}
|
||||
|
||||
void VerilatedSaif::commitTraceBuffer(VerilatedSaif::Buffer* bufp) { delete bufp; }
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ private:
|
|||
|
||||
int m_filep = 0; // File we're writing to
|
||||
bool m_isOpen = false; // True indicates open file
|
||||
uint64_t m_startTime = 0; // Time file was opened
|
||||
std::string m_filename; // Filename we're writing to (if open)
|
||||
std::string m_buffer; // Write data buffer
|
||||
|
||||
|
|
@ -60,9 +61,9 @@ private:
|
|||
// Currently active scope
|
||||
VerilatedSaifActivityScope* m_currentScope = nullptr;
|
||||
// Array of declared scopes
|
||||
std::vector<std::unique_ptr<VerilatedSaifActivityScope>> m_scopes{};
|
||||
std::vector<std::unique_ptr<VerilatedSaifActivityScope>> m_scopes;
|
||||
// Activity accumulators used to store variables statistics over simulation time
|
||||
std::vector<std::unique_ptr<VerilatedSaifActivityAccumulator>> m_activityAccumulators{};
|
||||
std::vector<std::unique_ptr<VerilatedSaifActivityAccumulator>> m_activityAccumulators;
|
||||
// Total time of the currently traced simulation
|
||||
uint64_t m_time = 0;
|
||||
|
||||
|
|
@ -146,26 +147,59 @@ public:
|
|||
void pushPrefix(const char*, VerilatedTracePrefixType);
|
||||
void popPrefix();
|
||||
|
||||
void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum);
|
||||
void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum);
|
||||
void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum);
|
||||
// versions to call when the sig is not array member
|
||||
void declEvent(uint32_t code, uint32_t fidx, const char* name);
|
||||
void declBit(uint32_t code, uint32_t fidx, const char* name);
|
||||
void declBus(uint32_t code, uint32_t fidx, const char* name, int msb, int lsb);
|
||||
void declQuad(uint32_t code, uint32_t fidx, const char* name, int msb, int lsb);
|
||||
void declWide(uint32_t code, uint32_t fidx, const char* name, int msb, int lsb);
|
||||
void declDouble(uint32_t code, uint32_t fidx, const char* name);
|
||||
|
||||
// versions to call when the sig is array member
|
||||
void declEventArray(uint32_t code, uint32_t fidx, const char* name, int arraynum);
|
||||
void declBitArray(uint32_t code, uint32_t fidx, const char* name, int arraynum);
|
||||
void declBusArray(uint32_t code, uint32_t fidx, const char* name, int arraynum, int msb,
|
||||
int lsb);
|
||||
void declQuadArray(uint32_t code, uint32_t fidx, const char* name, int arraynum, int msb,
|
||||
int lsb);
|
||||
void declWideArray(uint32_t code, uint32_t fidx, const char* name, int arraynum, int msb,
|
||||
int lsb);
|
||||
void declDoubleArray(uint32_t code, uint32_t fidx, const char* name, int arraynum);
|
||||
};
|
||||
|
||||
// We use macros to drop unused arguments at compile time. This saves code size.
|
||||
#define VL_TRACE_PUSH_PREFIX(tracep, name, type, left, right) tracep->pushPrefix(name, type);
|
||||
#define VL_TRACE_POP_PREFIX(tracep) tracep->popPrefix();
|
||||
|
||||
#define VL_TRACE_DECL_EVENT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
|
||||
tracep->declEvent(code, fidx, name)
|
||||
#define VL_TRACE_DECL_BIT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
|
||||
tracep->declBit(code, fidx, name)
|
||||
#define VL_TRACE_DECL_BUS(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
|
||||
tracep->declBus(code, fidx, name, msb, lsb)
|
||||
#define VL_TRACE_DECL_QUAD(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
|
||||
tracep->declQuad(code, fidx, name, msb, lsb)
|
||||
#define VL_TRACE_DECL_WIDE(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
|
||||
tracep->declWide(code, fidx, name, msb, lsb)
|
||||
#define VL_TRACE_DECL_DOUBLE(tracep, code, fidx, name, dtypenum, dir, kind, type) \
|
||||
tracep->declDouble(code, fidx, name)
|
||||
|
||||
#define VL_TRACE_DECL_EVENT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
|
||||
tracep->declEventArray(code, fidx, name, arraynum)
|
||||
#define VL_TRACE_DECL_BIT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
|
||||
tracep->declBitArray(code, fidx, name, arraynum)
|
||||
#define VL_TRACE_DECL_BUS_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
|
||||
msb, lsb) \
|
||||
tracep->declBusArray(code, fidx, name, arraynum, msb, lsb)
|
||||
#define VL_TRACE_DECL_QUAD_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
|
||||
msb, lsb) \
|
||||
tracep->declQuadArray(code, fidx, name, arraynum, msb, lsb)
|
||||
#define VL_TRACE_DECL_WIDE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
|
||||
msb, lsb) \
|
||||
tracep->declWideArray(code, fidx, name, arraynum, msb, lsb)
|
||||
#define VL_TRACE_DECL_DOUBLE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
|
||||
tracep->declDoubleArray(code, fidx, name, arraynum)
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// Declare specialization here as it's used in VerilatedSaifC just below
|
||||
template <>
|
||||
|
|
@ -190,7 +224,6 @@ class VerilatedSaifBuffer VL_NOT_FINAL {
|
|||
friend VerilatedSaif;
|
||||
friend VerilatedSaif::Super;
|
||||
friend VerilatedSaif::Buffer;
|
||||
friend VerilatedSaif::OffloadBuffer;
|
||||
|
||||
VerilatedSaif& m_owner; // Trace file owning this buffer. Required by subclasses.
|
||||
uint32_t m_fidx; // Index of target activity accumulator
|
||||
|
|
|
|||
|
|
@ -254,13 +254,13 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
|
|||
// Serialization of types
|
||||
|
||||
VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp) {
|
||||
os.write(rhsp->serialized1Ptr(), rhsp->serialized1Size());
|
||||
os.write(rhsp->serialized1Ptr(), VerilatedContext::serialized1Size());
|
||||
os << rhsp->impp()->timeFormatSuffix();
|
||||
os << rhsp->dumpfile();
|
||||
return os;
|
||||
}
|
||||
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp) {
|
||||
os.read(rhsp->serialized1Ptr(), rhsp->serialized1Size());
|
||||
os.read(rhsp->serialized1Ptr(), VerilatedContext::serialized1Size());
|
||||
std::string s;
|
||||
os >> s;
|
||||
rhsp->impp()->timeFormatSuffix(s);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@
|
|||
// The following keywords from this file are hardcoded for detection in the parser:
|
||||
// "mailbox", "process", "randomize", "semaphore", "std"
|
||||
|
||||
`ifndef VERILATOR_STD_SV_
|
||||
`define VERILATOR_STD_SV_
|
||||
|
||||
// verilator lint_off DECLFILENAME
|
||||
// verilator lint_off TIMESCALEMOD
|
||||
// verilator lint_off UNUSEDSIGNAL
|
||||
|
|
@ -181,8 +184,8 @@ package std;
|
|||
|
||||
static task killQueue(ref process processQueue[$]);
|
||||
`ifdef VERILATOR_TIMING
|
||||
while (processQueue.size() > 0) begin
|
||||
processQueue.pop_back().kill();
|
||||
repeat (processQueue.size()) begin
|
||||
processQueue.pop_front().kill();
|
||||
end
|
||||
`endif
|
||||
endtask
|
||||
|
|
@ -215,17 +218,6 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
|||
`endif
|
||||
// verilog_format: on
|
||||
|
||||
// When really implemented, srandom must operate on the process, but for
|
||||
// now rely on the srandom() that is automatically generated for all
|
||||
// classes.
|
||||
//
|
||||
// function void srandom(int seed);
|
||||
// endfunction
|
||||
|
||||
// The methods below access the common RNG, full support
|
||||
// of get_randstate/set_randstate requires accessing the RNG state
|
||||
// of the specified process (see IEEE 1800-2023, 18.14.), but as for
|
||||
// now processes do not have their own RNGs.
|
||||
function string get_randstate();
|
||||
// Initialize with $c to ensure it won't be constified
|
||||
string s = string'($c("0"));
|
||||
|
|
@ -303,3 +295,5 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
|
|||
} vl_cross_type_options_t;
|
||||
|
||||
endpackage
|
||||
|
||||
`endif // Guard
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
//===========================================================================
|
||||
|
|
@ -168,12 +170,9 @@ public:
|
|||
const std::vector<VerilatedRange>& packedRanges() const VL_MT_SAFE { return m_packed; }
|
||||
const std::vector<VerilatedRange>& unpackedRanges() const VL_MT_SAFE { return m_unpacked; }
|
||||
const VerilatedRange* range(int dim) const VL_MT_SAFE {
|
||||
if (dim < udims())
|
||||
return &m_unpacked[dim];
|
||||
else if (dim < dims())
|
||||
return &m_packed[dim - udims()];
|
||||
else
|
||||
return nullptr;
|
||||
if (dim < udims()) return &m_unpacked[dim];
|
||||
if (dim < dims()) return &m_packed[dim - udims()];
|
||||
return nullptr;
|
||||
}
|
||||
// DPI accessors (with packed dimensions flattened!)
|
||||
int left(int dim) const VL_MT_SAFE {
|
||||
|
|
@ -252,10 +251,14 @@ public:
|
|||
// Verilator variable
|
||||
// Thread safety: Assume is constructed only with model, then any number of readers
|
||||
|
||||
struct VerilatedForceControlSignals;
|
||||
class VerilatedVar final : public VerilatedVarProps {
|
||||
// MEMBERS
|
||||
void* const m_datap; // Location of data
|
||||
const char* const m_namep; // Name - slowpath
|
||||
std::unique_ptr<const VerilatedForceControlSignals>
|
||||
m_forceControlSignals; // Force control signals
|
||||
|
||||
protected:
|
||||
const bool m_isParam;
|
||||
friend class VerilatedScope;
|
||||
|
|
@ -266,13 +269,34 @@ protected:
|
|||
, m_datap{datap}
|
||||
, m_namep{namep}
|
||||
, m_isParam{isParam} {}
|
||||
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
||||
VerilatedVarFlags vlflags, int udims, int pdims, bool isParam,
|
||||
std::unique_ptr<const VerilatedForceControlSignals> forceControlSignals)
|
||||
: VerilatedVarProps{vltype, vlflags, udims, pdims}
|
||||
, m_datap{datap}
|
||||
, m_namep{namep}
|
||||
, m_forceControlSignals{std::move(forceControlSignals)}
|
||||
, m_isParam{isParam} {}
|
||||
|
||||
public:
|
||||
~VerilatedVar() = default;
|
||||
VerilatedVar(VerilatedVar&&) = default;
|
||||
// ACCESSORS
|
||||
void* datap() const { return m_datap; }
|
||||
const char* name() const { return m_namep; }
|
||||
bool isParam() const { return m_isParam; }
|
||||
const VerilatedForceControlSignals* forceControlSignals() const {
|
||||
return m_forceControlSignals.get();
|
||||
}
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
// Force control signals of a VerilatedVar
|
||||
|
||||
struct VerilatedForceControlSignals final {
|
||||
const VerilatedVar* forceEnableSignalp{nullptr}; // __VforceEn signal
|
||||
const VerilatedVar* forceValueSignalp{nullptr}; // __VforceVal signal
|
||||
const VerilatedVar forceReadSignal; // __VforceRd signal
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -148,12 +148,11 @@ VlThreadPool::~VlThreadPool() {
|
|||
std::string VlThreadPool::numaAssign(VerilatedContext* contextp) {
|
||||
#if defined(__linux) || defined(CPU_ZERO) || defined(VL_CPPCHECK) // Linux-like pthreads
|
||||
if (contextp && !contextp->useNumaAssign()) { return "NUMA assignment not requested"; }
|
||||
std::string numa_strategy = VlOs::getenvStr("VERILATOR_NUMA_STRATEGY", "default");
|
||||
if (numa_strategy == "none") {
|
||||
return "no NUMA assignment requested";
|
||||
} else if (numa_strategy != "default" && numa_strategy != "") {
|
||||
const std::string numa_strategy = VlOs::getenvStr("VERILATOR_NUMA_STRATEGY", "default");
|
||||
if (numa_strategy == "none") return "no NUMA assignment requested";
|
||||
if (numa_strategy != "default" && numa_strategy != "")
|
||||
return "%Warning: unknown VERILATOR_NUMA_STRATEGY value '" + numa_strategy + "'";
|
||||
}
|
||||
|
||||
// Get number of processor available to the current process
|
||||
const unsigned num_proc = VlOs::getProcessAvailableParallelism();
|
||||
if (!num_proc) return "Can't determine number of available threads";
|
||||
|
|
@ -184,7 +183,7 @@ std::string VlThreadPool::numaAssign(VerilatedContext* contextp) {
|
|||
while (!is.eof()) {
|
||||
std::string line;
|
||||
std::getline(is, line);
|
||||
std::string::size_type pos = line.find(":");
|
||||
const std::string::size_type pos = line.find(':');
|
||||
int number = -1;
|
||||
if (pos != std::string::npos) number = atoi(line.c_str() + pos + 1);
|
||||
if (line.compare(0, std::strlen("processor"), "processor") == 0) {
|
||||
|
|
|
|||
|
|
@ -101,12 +101,11 @@ public:
|
|||
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
|
||||
assert(upstreamDepsDone <= m_upstreamDepCount);
|
||||
return (upstreamDepsDone == m_upstreamDepCount);
|
||||
} else {
|
||||
const uint32_t upstreamDepsDone_prev
|
||||
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
|
||||
assert(upstreamDepsDone_prev > 0);
|
||||
return (upstreamDepsDone_prev == 1);
|
||||
}
|
||||
const uint32_t upstreamDepsDone_prev
|
||||
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
|
||||
assert(upstreamDepsDone_prev > 0);
|
||||
return (upstreamDepsDone_prev == 1);
|
||||
}
|
||||
bool areUpstreamDepsDone(bool evenCycle) const {
|
||||
const uint32_t target = evenCycle ? m_upstreamDepCount : 0;
|
||||
|
|
@ -158,7 +157,7 @@ class VlWorkerThread final {
|
|||
#ifdef VL_USE_PTHREADS
|
||||
pthread_t m_pthread{};
|
||||
#else
|
||||
std::thread m_cthread{};
|
||||
std::thread m_cthread;
|
||||
#endif
|
||||
|
||||
// METHDOS
|
||||
|
|
@ -182,7 +181,7 @@ public:
|
|||
VL_CPU_RELAX();
|
||||
}
|
||||
}
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
while (m_ready.empty()) {
|
||||
m_waiting = true;
|
||||
m_cv.wait(m_mutex);
|
||||
|
|
@ -239,7 +238,7 @@ public:
|
|||
}
|
||||
void freeWorkerIndexes(std::vector<size_t>& indexes) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
for (size_t index : indexes) m_unassignedWorkers.push(index);
|
||||
for (const size_t index : indexes) m_unassignedWorkers.push(index);
|
||||
indexes.clear();
|
||||
}
|
||||
unsigned assignTaskIndex() { return m_assignedTasks++; }
|
||||
|
|
|
|||
|
|
@ -36,9 +36,12 @@ void VlCoroutineHandle::resume() {
|
|||
m_coro.destroy();
|
||||
} else {
|
||||
m_process->state(VlProcess::RUNNING);
|
||||
VlProcess::currentp(m_process.get());
|
||||
m_coro();
|
||||
VlProcess::currentp(nullptr);
|
||||
}
|
||||
} else {
|
||||
VlProcess::currentp(nullptr);
|
||||
m_coro();
|
||||
}
|
||||
m_coro = nullptr;
|
||||
|
|
@ -58,6 +61,12 @@ void VlDelayScheduler::resume() {
|
|||
#ifdef VL_DEBUG
|
||||
VL_DEBUG_IF(dump(); VL_DBG_MSGF(" Resuming delayed processes\n"););
|
||||
#endif
|
||||
if (VL_UNLIKELY(m_context.gotFinish())) {
|
||||
m_queue.clear();
|
||||
m_zeroDelayed.clear();
|
||||
m_zeroDelayesSwap.clear();
|
||||
return;
|
||||
}
|
||||
bool resumed = false;
|
||||
|
||||
while (!m_queue.empty() && (m_queue.cbegin()->first == m_context.time())) {
|
||||
|
|
@ -80,6 +89,11 @@ void VlDelayScheduler::resume() {
|
|||
}
|
||||
|
||||
void VlDelayScheduler::resumeZeroDelay() {
|
||||
if (VL_UNLIKELY(m_context.gotFinish())) {
|
||||
m_zeroDelayed.clear();
|
||||
m_zeroDelayesSwap.clear();
|
||||
return;
|
||||
}
|
||||
m_zeroDelayesSwap.swap(m_zeroDelayed);
|
||||
for (VlCoroutineHandle& handle : m_zeroDelayesSwap) handle.resume();
|
||||
m_zeroDelayesSwap.clear();
|
||||
|
|
@ -120,6 +134,12 @@ void VlTriggerScheduler::resume(const char* eventDescription) {
|
|||
VL_DEBUG_IF(dump(eventDescription);
|
||||
VL_DBG_MSGF(" Resuming processes waiting for %s\n", eventDescription););
|
||||
#endif
|
||||
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
|
||||
m_toResume.clear();
|
||||
m_fired.clear();
|
||||
m_awaiting.clear();
|
||||
return;
|
||||
}
|
||||
for (VlCoroutineHandle& coro : m_toResume) coro.resume();
|
||||
m_toResume.clear();
|
||||
}
|
||||
|
|
@ -136,6 +156,11 @@ void VlTriggerScheduler::moveToResumeQueue(const char* eventDescription) {
|
|||
});
|
||||
}
|
||||
#endif
|
||||
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
|
||||
m_toResume.clear();
|
||||
m_fired.clear();
|
||||
return;
|
||||
}
|
||||
std::swap(m_fired, m_toResume);
|
||||
}
|
||||
|
||||
|
|
@ -151,6 +176,11 @@ void VlTriggerScheduler::ready(const char* eventDescription) {
|
|||
});
|
||||
}
|
||||
#endif
|
||||
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
|
||||
m_fired.clear();
|
||||
m_awaiting.clear();
|
||||
return;
|
||||
}
|
||||
const size_t expectedSize = m_fired.size() + m_awaiting.size();
|
||||
if (m_fired.capacity() < expectedSize) m_fired.reserve(expectedSize * 2);
|
||||
m_fired.insert(m_fired.end(), std::make_move_iterator(m_awaiting.begin()),
|
||||
|
|
@ -190,6 +220,14 @@ void VlTriggerScheduler::dump(const char* eventDescription) const {
|
|||
// VlDynamicTriggerScheduler:: Methods
|
||||
|
||||
bool VlDynamicTriggerScheduler::evaluate() {
|
||||
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
|
||||
m_anyTriggered = false;
|
||||
m_suspended.clear();
|
||||
m_evaluated.clear();
|
||||
m_triggered.clear();
|
||||
m_post.clear();
|
||||
return false;
|
||||
}
|
||||
m_anyTriggered = false;
|
||||
VL_DEBUG_IF(dump(););
|
||||
std::swap(m_suspended, m_evaluated);
|
||||
|
|
@ -206,6 +244,10 @@ void VlDynamicTriggerScheduler::doPostUpdates() {
|
|||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
});
|
||||
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
|
||||
m_post.clear();
|
||||
return;
|
||||
}
|
||||
for (auto& coro : m_post) coro.resume();
|
||||
m_post.clear();
|
||||
}
|
||||
|
|
@ -217,6 +259,10 @@ void VlDynamicTriggerScheduler::resume() {
|
|||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
});
|
||||
if (VL_UNLIKELY(Verilated::threadContextp()->gotFinish())) {
|
||||
m_triggered.clear();
|
||||
return;
|
||||
}
|
||||
for (auto& coro : m_triggered) coro.resume();
|
||||
m_triggered.clear();
|
||||
}
|
||||
|
|
@ -238,10 +284,56 @@ void VlDynamicTriggerScheduler::dump() const {
|
|||
//======================================================================
|
||||
// VlForkSync:: Methods
|
||||
|
||||
void VlForkSync::done(const char* filename, int lineno) {
|
||||
void VlProcess::forkSyncOnKill(VlForkSyncState* forkSyncp) {
|
||||
m_forkSyncOnKillp = forkSyncp;
|
||||
m_forkSyncOnKillDone = false;
|
||||
}
|
||||
|
||||
void VlProcess::forkSyncOnKillClear(VlForkSyncState* forkSyncp) {
|
||||
if (m_forkSyncOnKillp != forkSyncp) return;
|
||||
m_forkSyncOnKillp = nullptr;
|
||||
m_forkSyncOnKillDone = false;
|
||||
}
|
||||
|
||||
void VlProcess::state(int s) {
|
||||
if (s == KILLED && m_state != KILLED && m_state != FINISHED && m_forkSyncOnKillp
|
||||
&& !m_forkSyncOnKillDone) {
|
||||
m_forkSyncOnKillDone = true;
|
||||
m_state = s;
|
||||
m_forkSyncOnKillp->done();
|
||||
return;
|
||||
}
|
||||
m_state = s;
|
||||
}
|
||||
|
||||
VlForkSyncState::~VlForkSyncState() {
|
||||
for (const VlProcessRef& processp : m_onKillProcessps) processp->forkSyncOnKillClear(this);
|
||||
}
|
||||
|
||||
void VlForkSync::onKill(VlProcessRef process) {
|
||||
if (!process) return;
|
||||
m_state->m_onKillProcessps.emplace_back(process);
|
||||
process->forkSyncOnKill(m_state.get());
|
||||
}
|
||||
|
||||
void VlForkSyncState::done(const char* filename, int lineno) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Process forked at %s:%d finished\n", filename, lineno););
|
||||
if (m_join->m_counter > 0) m_join->m_counter--;
|
||||
if (m_join->m_counter == 0) m_join->m_susp.resume();
|
||||
if (!m_inited) {
|
||||
++m_pendingDones;
|
||||
return;
|
||||
}
|
||||
if (m_counter > 0) m_counter--;
|
||||
if (m_counter != 0) return;
|
||||
if (m_inDone) {
|
||||
m_resumePending = true;
|
||||
return;
|
||||
}
|
||||
m_inDone = true;
|
||||
do {
|
||||
m_resumePending = false;
|
||||
m_susp.resume();
|
||||
} while (m_resumePending && m_inited && m_counter == 0);
|
||||
m_inDone = false;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
// clang-format off
|
||||
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
|
||||
#if defined _LIBCPP_VERSION // libc++
|
||||
#ifdef _LIBCPP_VERSION // libc++
|
||||
# if defined(__has_include) && !__has_include(<coroutine>) && __has_include(<experimental/coroutine>)
|
||||
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
|
||||
# pragma clang diagnostic push
|
||||
|
|
@ -190,10 +190,11 @@ public:
|
|||
bool empty() const { return m_queue.empty() && m_zeroDelayed.empty(); }
|
||||
// Are there coroutines to resume at the current simulation time?
|
||||
bool awaitingCurrentTime() const {
|
||||
return (!m_queue.empty() && (m_queue.cbegin()->first <= m_context.time()));
|
||||
return !m_context.gotFinish()
|
||||
&& (!m_queue.empty() && (m_queue.cbegin()->first <= m_context.time()));
|
||||
}
|
||||
// Are there coroutines to resume in the inactive region after a #0 delay?
|
||||
bool awaitingZeroDelay() const { return !m_zeroDelayed.empty(); }
|
||||
bool awaitingZeroDelay() const { return !m_context.gotFinish() && !m_zeroDelayed.empty(); }
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
|
|
@ -383,40 +384,59 @@ struct VlForever final {
|
|||
//=============================================================================
|
||||
// VlForkSync is used to manage fork..join and fork..join_any constructs.
|
||||
|
||||
class VlForkSync final {
|
||||
// VlJoin stores the handle of a suspended coroutine that did a fork..join or fork..join_any.
|
||||
// If the counter reaches 0, the suspended coroutine shall be resumed.
|
||||
struct VlJoin final {
|
||||
size_t m_counter = 0; // When reaches 0, resume suspended coroutine
|
||||
VlCoroutineHandle m_susp; // Coroutine to resume
|
||||
};
|
||||
// Shared fork..join state, because VlForkSync is copied into generated coroutine frames.
|
||||
class VlForkSyncState final {
|
||||
public:
|
||||
size_t m_counter = 0; // When reaches 0, resume suspended coroutine
|
||||
VlCoroutineHandle m_susp; // Coroutine to resume
|
||||
bool m_inited = false;
|
||||
size_t m_pendingDones = 0; // done() calls seen before init() (e.g. early killed branch)
|
||||
bool m_inDone = false; // Guard against re-entrant resume recursion from nested kills
|
||||
bool m_resumePending = false; // Join reached zero again while inside done()
|
||||
std::vector<VlProcessRef> m_onKillProcessps; // Branches registered for kill hooks
|
||||
|
||||
// The join info is shared among all forked processes
|
||||
std::shared_ptr<VlJoin> m_join;
|
||||
VlForkSyncState() // Construct with a null coroutine handle
|
||||
: m_susp{VlProcessRef{}} {}
|
||||
~VlForkSyncState();
|
||||
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
|
||||
};
|
||||
|
||||
class VlForkSync final {
|
||||
std::shared_ptr<VlForkSyncState> m_state{std::make_shared<VlForkSyncState>()};
|
||||
|
||||
public:
|
||||
// Create the join object and set the counter to the specified number
|
||||
void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); }
|
||||
void init(size_t count, VlProcessRef process) {
|
||||
const size_t pendingDones = m_state->m_pendingDones;
|
||||
m_state->m_pendingDones = 0;
|
||||
count = (pendingDones >= count) ? 0 : (count - pendingDones);
|
||||
m_state->m_counter = count;
|
||||
m_state->m_susp = {process};
|
||||
m_state->m_inited = true;
|
||||
}
|
||||
// Register process kill callback so killed fork branches still decrement join counter
|
||||
void onKill(VlProcessRef process);
|
||||
// Called whenever any of the forked processes finishes. If the join counter reaches 0, the
|
||||
// main process gets resumed
|
||||
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
|
||||
void done(const char* filename = VL_UNKNOWN, int lineno = 0) {
|
||||
m_state->done(filename, lineno);
|
||||
}
|
||||
// Used by coroutines for co_awaiting a join
|
||||
auto join(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) {
|
||||
assert(m_join);
|
||||
VL_DEBUG_IF(
|
||||
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
|
||||
struct Awaitable final {
|
||||
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||
const std::shared_ptr<VlJoin> join; // Join to await on
|
||||
const std::shared_ptr<VlForkSyncState> state; // Join to await on
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() { return join->m_counter == 0; } // Suspend if join still exists
|
||||
bool await_ready() { return state->m_counter == 0; } // Suspend if join still exists
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
join->m_susp = {coro, process, fileline};
|
||||
state->m_susp = {coro, process, fileline};
|
||||
}
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{process, m_join, VlFileLineDebug{filename, lineno}};
|
||||
return Awaitable{process, m_state, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -43,8 +43,6 @@
|
|||
class VlThreadPool;
|
||||
template <typename T_Buffer>
|
||||
class VerilatedTraceBuffer;
|
||||
template <typename T_Buffer>
|
||||
class VerilatedTraceOffloadBuffer;
|
||||
|
||||
//=============================================================================
|
||||
// Common enumerations
|
||||
|
|
@ -98,76 +96,6 @@ enum class VerilatedTraceSigType : uint8_t {
|
|||
TIME,
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// Offloaded tracing
|
||||
|
||||
// A simple synchronized first in first out queue
|
||||
template <typename T>
|
||||
class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug
|
||||
private:
|
||||
mutable VerilatedMutex m_mutex; // Protects m_queue
|
||||
std::condition_variable_any m_cv;
|
||||
std::deque<T> m_queue VL_GUARDED_BY(m_mutex);
|
||||
|
||||
public:
|
||||
// Put an element at the back of the queue
|
||||
void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_queue.push_back(value);
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
// Put an element at the front of the queue
|
||||
void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_queue.push_front(value);
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
// Get an element from the front of the queue. Blocks if none available
|
||||
T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
m_cv.wait(m_mutex, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
|
||||
assert(!m_queue.empty());
|
||||
T value = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
return value;
|
||||
}
|
||||
|
||||
// Non blocking get
|
||||
bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lockGuard{m_mutex};
|
||||
if (m_queue.empty()) return false;
|
||||
result = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Commands used by thread tracing. Anonymous enum in class, as we want
|
||||
// it scoped, but we also want the automatic conversion to integer types.
|
||||
class VerilatedTraceOffloadCommand final {
|
||||
public:
|
||||
// These must all fit in 4 bit at the moment, as the tracing routines
|
||||
// pack parameters in the top bits.
|
||||
enum : uint8_t {
|
||||
CHG_BIT_0 = 0x0,
|
||||
CHG_BIT_1 = 0x1,
|
||||
CHG_CDATA = 0x2,
|
||||
CHG_SDATA = 0x3,
|
||||
CHG_IDATA = 0x4,
|
||||
CHG_QDATA = 0x5,
|
||||
CHG_WDATA = 0x6,
|
||||
CHG_DOUBLE = 0x8,
|
||||
CHG_EVENT = 0x9,
|
||||
// TODO: full..
|
||||
TIME_CHANGE = 0xc,
|
||||
TRACE_BUFFER = 0xd,
|
||||
END = 0xe, // End of buffer
|
||||
SHUTDOWN = 0xf // Shutdown worker thread, also marks end of buffer
|
||||
};
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTraceConfig
|
||||
|
||||
|
|
@ -175,13 +103,9 @@ public:
|
|||
class VerilatedTraceConfig final {
|
||||
public:
|
||||
const bool m_useParallel; // Use parallel tracing
|
||||
const bool m_useOffloading; // Offloading trace rendering
|
||||
const bool m_useFstWriterThread; // Use the separate FST writer thread
|
||||
|
||||
VerilatedTraceConfig(bool useParallel, bool useOffloading, bool useFstWriterThread)
|
||||
: m_useParallel{useParallel}
|
||||
, m_useOffloading{useOffloading}
|
||||
, m_useFstWriterThread{useFstWriterThread} {}
|
||||
VerilatedTraceConfig(bool useParallel)
|
||||
: m_useParallel{useParallel} {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
|
@ -208,27 +132,23 @@ template <typename T_Trace, typename T_Buffer>
|
|||
class VerilatedTrace VL_NOT_FINAL {
|
||||
public:
|
||||
using Buffer = VerilatedTraceBuffer<T_Buffer>;
|
||||
using OffloadBuffer = VerilatedTraceOffloadBuffer<T_Buffer>;
|
||||
|
||||
//=========================================================================
|
||||
// Generic tracing internals
|
||||
|
||||
using initCb_t = void (*)(void*, T_Trace*, uint32_t); // Type of init callbacks
|
||||
using dumpCb_t = void (*)(void*, Buffer*); // Type of dump callbacks
|
||||
using dumpOffloadCb_t = void (*)(void*, OffloadBuffer*); // Type of offload dump callbacks
|
||||
using cleanupCb_t = void (*)(void*, T_Trace*); // Type of cleanup callbacks
|
||||
|
||||
private:
|
||||
// Give the buffer (both base and derived) access to the private bits
|
||||
friend T_Buffer;
|
||||
friend Buffer;
|
||||
friend OffloadBuffer;
|
||||
|
||||
struct CallbackRecord final {
|
||||
union { // The callback
|
||||
const initCb_t m_initCb;
|
||||
const dumpCb_t m_dumpCb;
|
||||
const dumpOffloadCb_t m_dumpOffloadCb;
|
||||
const cleanupCb_t m_cleanupCb;
|
||||
};
|
||||
const uint32_t m_fidx; // The index of the tracing function
|
||||
|
|
@ -252,14 +172,6 @@ private:
|
|||
, m_name{} // Don't care
|
||||
, m_nTraceCodes{0} // Don't care
|
||||
{}
|
||||
CallbackRecord(dumpOffloadCb_t cb, uint32_t fidx, void* userp)
|
||||
: m_dumpOffloadCb{cb}
|
||||
, m_fidx{fidx}
|
||||
, m_userp{userp}
|
||||
, m_isLibInstance{false} // Don't care
|
||||
, m_name{} // Don't care
|
||||
, m_nTraceCodes{0} // Don't care
|
||||
{}
|
||||
CallbackRecord(cleanupCb_t cb, void* userp)
|
||||
: m_cleanupCb{cb}
|
||||
, m_fidx{0}
|
||||
|
|
@ -270,7 +182,6 @@ private:
|
|||
{}
|
||||
};
|
||||
|
||||
bool m_offload = false; // Use the offload thread
|
||||
bool m_parallel = false; // Use parallel tracing
|
||||
|
||||
struct ParallelWorkerData final {
|
||||
|
|
@ -300,11 +211,8 @@ private:
|
|||
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
|
||||
std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing
|
||||
std::vector<CallbackRecord> m_constCbs; // Routines to perform const dump
|
||||
std::vector<CallbackRecord> m_constOffloadCbs; // Routines to perform offloaded const dump
|
||||
std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
|
||||
std::vector<CallbackRecord> m_fullOffloadCbs; // Routines to perform offloaded full dump
|
||||
std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
|
||||
std::vector<CallbackRecord> m_chgOffloadCbs; // Routines to perform offloaded incremental dump
|
||||
std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump
|
||||
bool m_constDump = true; // Whether a const dump is required on the next call to 'dump'
|
||||
bool m_fullDump = true; // Whether a full dump is required on the next call to 'dump'
|
||||
|
|
@ -329,44 +237,13 @@ private:
|
|||
T_Trace* self() { return static_cast<T_Trace*>(this); }
|
||||
|
||||
void runCallbacks(const std::vector<CallbackRecord>& cbVec);
|
||||
void runOffloadedCallbacks(const std::vector<CallbackRecord>& cbVec);
|
||||
|
||||
// Flush any remaining data for this file
|
||||
static void onFlush(void* selfp) VL_MT_UNSAFE_ONE;
|
||||
// Close the file on termination
|
||||
static void onExit(void* selfp) VL_MT_UNSAFE_ONE;
|
||||
|
||||
// Number of total offload buffers that have been allocated
|
||||
uint32_t m_numOffloadBuffers = 0;
|
||||
// Size of offload buffers
|
||||
size_t m_offloadBufferSize = 0;
|
||||
// Buffers handed to worker for processing
|
||||
VerilatedThreadQueue<uint32_t*> m_offloadBuffersToWorker;
|
||||
// Buffers returned from worker after processing
|
||||
VerilatedThreadQueue<uint32_t*> m_offloadBuffersFromWorker;
|
||||
|
||||
protected:
|
||||
// Write pointer into current buffer
|
||||
uint32_t* m_offloadBufferWritep = nullptr;
|
||||
// End of offload buffer
|
||||
uint32_t* m_offloadBufferEndp = nullptr;
|
||||
|
||||
private:
|
||||
// The offload worker thread itself
|
||||
std::unique_ptr<std::thread> m_workerThread;
|
||||
|
||||
// Get a new offload buffer that can be populated. May block if none available
|
||||
uint32_t* getOffloadBuffer();
|
||||
|
||||
// The function executed by the offload worker thread
|
||||
void offloadWorkerThreadMain();
|
||||
|
||||
// Wait until given offload buffer is placed in m_offloadBuffersFromWorker
|
||||
void waitForOffloadBuffer(const uint32_t* bufferp);
|
||||
|
||||
// Shut down and join worker, if it's running, otherwise do nothing
|
||||
void shutdownOffloadWorker();
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedTrace);
|
||||
|
||||
|
|
@ -395,7 +272,6 @@ protected:
|
|||
void closeBase();
|
||||
void flushBase();
|
||||
|
||||
bool offload() const { return m_offload; }
|
||||
bool parallel() const { return m_parallel; }
|
||||
|
||||
// Return last ' ' separated word. Assumes string does not end in ' '.
|
||||
|
|
@ -453,11 +329,8 @@ public:
|
|||
void addInitCb(initCb_t cb, void* userp, const std::string& name, bool isLibInstance,
|
||||
uint32_t nTraceCodes) VL_MT_SAFE;
|
||||
void addConstCb(dumpCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
|
||||
void addConstCb(dumpOffloadCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
|
||||
void addFullCb(dumpCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
|
||||
void addFullCb(dumpOffloadCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
|
||||
void addChgCb(dumpCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
|
||||
void addChgCb(dumpOffloadCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
|
||||
void addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void initLib(const std::string& name) VL_MT_UNSAFE;
|
||||
};
|
||||
|
|
@ -517,10 +390,6 @@ public:
|
|||
void fullEvent(uint32_t* oldp, const VlEventBase* newvalp);
|
||||
void fullEventTriggered(uint32_t* oldp);
|
||||
|
||||
// In non-offload mode, these are called directly by the trace callbacks,
|
||||
// and are called chg*. In offload mode, they are called by the worker
|
||||
// thread and are called chg*Impl
|
||||
|
||||
// Check previous dumped value of signal. If changed, then emit trace entry
|
||||
VL_ATTR_ALWINLINE void chgBit(uint32_t* oldp, CData newval) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
|
|
@ -563,86 +432,4 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTraceOffloadBuffer
|
||||
|
||||
// T_Buffer is the format-specific base class of VerilatedTraceBuffer.
|
||||
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
|
||||
template <typename T_Buffer>
|
||||
class VerilatedTraceOffloadBuffer final : public VerilatedTraceBuffer<T_Buffer> {
|
||||
using typename VerilatedTraceBuffer<T_Buffer>::Trace;
|
||||
|
||||
friend Trace; // Give the trace file access to the private bits
|
||||
|
||||
uint32_t* m_offloadBufferWritep; // Write pointer into current buffer
|
||||
uint32_t* const m_offloadBufferEndp; // End of offload buffer
|
||||
|
||||
explicit VerilatedTraceOffloadBuffer(Trace& owner);
|
||||
~VerilatedTraceOffloadBuffer() override = default;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// Hot path internal interface to Verilator generated code
|
||||
|
||||
// Offloaded tracing. Just dump everything in the offload buffer
|
||||
void chgBit(uint32_t code, CData newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgCData(uint32_t code, CData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgSData(uint32_t code, SData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgIData(uint32_t code, IData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgQData(uint32_t code, QData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
*reinterpret_cast<QData*>(m_offloadBufferWritep + 2) = newval;
|
||||
m_offloadBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) *m_offloadBufferWritep++ = newvalp[i];
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgDouble(uint32_t code, double newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(m_offloadBufferWritep + 2) = newval;
|
||||
m_offloadBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgEvent(uint32_t code, const VlEventBase* newvalp) {
|
||||
if (newvalp->isTriggered()) chgEventTriggered(code);
|
||||
}
|
||||
void chgEventTriggered(uint32_t code) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_EVENT;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
|
|
|||
|
|
@ -29,13 +29,6 @@
|
|||
#include "verilated_threads.h"
|
||||
#include <list>
|
||||
|
||||
#if 0
|
||||
# include <iostream>
|
||||
# define VL_TRACE_OFFLOAD_DEBUG(msg) std::cout << "TRACE OFFLOAD THREAD: " << msg << "\n"
|
||||
#else
|
||||
# define VL_TRACE_OFFLOAD_DEBUG(msg)
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
//=============================================================================
|
||||
|
|
@ -60,210 +53,14 @@ static double timescaleToDouble(const char* unitp) VL_PURE {
|
|||
return value;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Buffer management
|
||||
|
||||
template <>
|
||||
uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
|
||||
uint32_t* bufferp;
|
||||
// Some jitter is expected, so some number of alternative offload buffers are
|
||||
// required, but don't allocate more than 8 buffers.
|
||||
if (m_numOffloadBuffers < 8) {
|
||||
// Allocate a new buffer if none is available
|
||||
if (!m_offloadBuffersFromWorker.tryGet(bufferp)) {
|
||||
++m_numOffloadBuffers;
|
||||
// Note: over allocate a bit so pointer comparison is well defined
|
||||
// if we overflow only by a small amount
|
||||
bufferp = new uint32_t[m_offloadBufferSize + 16];
|
||||
}
|
||||
} else {
|
||||
// Block until a buffer becomes available
|
||||
bufferp = m_offloadBuffersFromWorker.get();
|
||||
}
|
||||
return bufferp;
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const uint32_t* buffp) {
|
||||
// Slow path code only called on flush/shutdown, so use a simple algorithm.
|
||||
// Collect buffers from worker and stash them until we get the one we want.
|
||||
std::deque<uint32_t*> stash;
|
||||
do { stash.push_back(m_offloadBuffersFromWorker.get()); } while (stash.back() != buffp);
|
||||
// Now put them back in the queue, in the original order.
|
||||
while (!stash.empty()) {
|
||||
m_offloadBuffersFromWorker.put_front(stash.back());
|
||||
stash.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Worker thread
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
bool shutdown = false;
|
||||
|
||||
do {
|
||||
uint32_t* const bufferp = m_offloadBuffersToWorker.get();
|
||||
|
||||
VL_TRACE_OFFLOAD_DEBUG("");
|
||||
VL_TRACE_OFFLOAD_DEBUG("Got buffer: " << bufferp);
|
||||
|
||||
const uint32_t* readp = bufferp;
|
||||
|
||||
std::unique_ptr<Buffer> traceBufp; // We own the passed tracebuffer
|
||||
|
||||
while (true) {
|
||||
const uint32_t cmd = readp[0];
|
||||
const uint32_t top = cmd >> 4;
|
||||
// Always set this up, as it is almost always needed
|
||||
uint32_t* const oldp = m_sigs_oldvalp + readp[1];
|
||||
// Note this increment needs to be undone on commands which do not
|
||||
// actually contain a code, but those are the rare cases.
|
||||
readp += 2;
|
||||
|
||||
switch (cmd & 0xF) {
|
||||
//===
|
||||
// CHG_* commands
|
||||
case VerilatedTraceOffloadCommand::CHG_BIT_0:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_0 " << top);
|
||||
traceBufp->chgBit(oldp, 0);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_BIT_1:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_1 " << top);
|
||||
traceBufp->chgBit(oldp, 1);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_CDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_CDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgCData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_SDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_SDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgSData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_IDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_IDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgIData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_QDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_QDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgQData(oldp, *reinterpret_cast<const QData*>(readp), top);
|
||||
readp += 2;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_WDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_WDATA " << top);
|
||||
traceBufp->chgWData(oldp, readp, top);
|
||||
readp += VL_WORDS_I(top);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_DOUBLE:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_DOUBLE " << top);
|
||||
traceBufp->chgDouble(oldp, *reinterpret_cast<const double*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_EVENT:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_EVENT " << top);
|
||||
traceBufp->chgEventTriggered(oldp);
|
||||
continue;
|
||||
|
||||
//===
|
||||
// Rare commands
|
||||
case VerilatedTraceOffloadCommand::TIME_CHANGE: {
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command TIME_CHANGE " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
const uint64_t timeui
|
||||
= static_cast<uint64_t>(*reinterpret_cast<const uint32_t*>(readp)) << 32ULL
|
||||
| static_cast<uint64_t>(*reinterpret_cast<const uint32_t*>(readp + 1));
|
||||
emitTimeChange(timeui);
|
||||
readp += 2;
|
||||
continue;
|
||||
}
|
||||
case VerilatedTraceOffloadCommand::TRACE_BUFFER:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
traceBufp.reset(*reinterpret_cast<Buffer* const*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
//===
|
||||
// Commands ending this buffer
|
||||
case VerilatedTraceOffloadCommand::END: //
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command END");
|
||||
break;
|
||||
case VerilatedTraceOffloadCommand::SHUTDOWN:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command SHUTDOWN");
|
||||
shutdown = true;
|
||||
break;
|
||||
|
||||
//===
|
||||
// Unknown command
|
||||
default: { // LCOV_EXCL_START
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command UNKNOWN " << cmd);
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unknown trace command");
|
||||
break;
|
||||
} // LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// The above switch will execute 'continue' when necessary,
|
||||
// so if we ever reach here, we are done with the buffer.
|
||||
break;
|
||||
}
|
||||
|
||||
VL_TRACE_OFFLOAD_DEBUG("Returning buffer");
|
||||
|
||||
// Return buffer
|
||||
m_offloadBuffersFromWorker.put(bufferp);
|
||||
} while (VL_LIKELY(!shutdown));
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
|
||||
// If the worker thread is not running, done..
|
||||
if (!m_workerThread) return;
|
||||
|
||||
// Hand an buffer with a shutdown command to the worker thread
|
||||
uint32_t* const bufferp = getOffloadBuffer();
|
||||
bufferp[0] = VerilatedTraceOffloadCommand::SHUTDOWN;
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
// Wait for it to return
|
||||
waitForOffloadBuffer(bufferp);
|
||||
// Join the thread and delete it
|
||||
m_workerThread->join();
|
||||
m_workerThread.reset(nullptr);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Life cycle
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
|
||||
if (offload()) {
|
||||
shutdownOffloadWorker();
|
||||
while (m_numOffloadBuffers) {
|
||||
delete[] m_offloadBuffersFromWorker.get();
|
||||
--m_numOffloadBuffers;
|
||||
}
|
||||
}
|
||||
}
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
|
||||
if (offload()) {
|
||||
// Hand an empty buffer to the worker thread
|
||||
uint32_t* const bufferp = getOffloadBuffer();
|
||||
*bufferp = VerilatedTraceOffloadCommand::END;
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
// Wait for it to be returned. As the processing is in-order,
|
||||
// this ensures all previous buffers have been processed.
|
||||
waitForOffloadBuffer(bufferp);
|
||||
}
|
||||
}
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {}
|
||||
|
||||
//=============================================================================
|
||||
// Callbacks to run on global events
|
||||
|
|
@ -295,7 +92,6 @@ VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
|
|||
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
|
||||
Verilated::removeFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
|
||||
Verilated::removeExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
|
||||
if (offload()) closeBase();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
|
@ -353,19 +149,6 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
|
|||
// Set callback so flush/abort will flush this file
|
||||
Verilated::addFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
|
||||
Verilated::addExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
|
||||
|
||||
if (offload()) {
|
||||
// Compute offload buffer size. we need to be able to store a new value for
|
||||
// each signal, which is 'nextCode()' entries after the init callbacks
|
||||
// above have been run, plus up to 2 more words of metadata per signal,
|
||||
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
|
||||
// update and 2 for the buffer address.
|
||||
m_offloadBufferSize = nextCode() + numSignals() * 2 + 6;
|
||||
|
||||
// Start the worker thread
|
||||
m_workerThread.reset(
|
||||
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
|
|
@ -520,17 +303,6 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runCallbacks(const std::vector<Callback
|
|||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runOffloadedCallbacks(
|
||||
const std::vector<CallbackRecord>& cbVec) {
|
||||
// Fall back on sequential execution
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
Buffer* traceBufferp = getTraceBuffer(cbr.m_fidx);
|
||||
cbr.m_dumpOffloadCb(cbr.m_userp, static_cast<OffloadBuffer*>(traceBufferp));
|
||||
commitTraceBuffer(traceBufferp);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
|
||||
|
|
@ -556,72 +328,23 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
|||
}
|
||||
|
||||
uint32_t* bufferp = nullptr;
|
||||
if (offload()) {
|
||||
// Currently only incremental dumps run on the worker thread
|
||||
if (VL_LIKELY(!m_fullDump)) {
|
||||
// Get the offload buffer we are about to fill
|
||||
bufferp = getOffloadBuffer();
|
||||
m_offloadBufferWritep = bufferp;
|
||||
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
|
||||
|
||||
// Tell worker to update time point
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
|
||||
*reinterpret_cast<uint32_t*>(m_offloadBufferWritep + 1)
|
||||
= static_cast<uint32_t>(timeui >> 32ULL);
|
||||
*reinterpret_cast<uint32_t*>(m_offloadBufferWritep + 2)
|
||||
= static_cast<uint32_t>(timeui);
|
||||
m_offloadBufferWritep += 3;
|
||||
} else {
|
||||
// Update time point
|
||||
flushBase();
|
||||
emitTimeChange(timeui);
|
||||
}
|
||||
} else {
|
||||
// Update time point
|
||||
emitTimeChange(timeui);
|
||||
}
|
||||
// Update time point
|
||||
emitTimeChange(timeui);
|
||||
|
||||
// Run the callbacks
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No more need for next dump to be full
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_fullOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_fullCbs);
|
||||
}
|
||||
runCallbacks(m_fullCbs);
|
||||
} else {
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_chgOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_chgCbs);
|
||||
}
|
||||
runCallbacks(m_chgCbs);
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(m_constDump)) {
|
||||
m_constDump = false;
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_constOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_constCbs);
|
||||
}
|
||||
runCallbacks(m_constCbs);
|
||||
}
|
||||
|
||||
for (const CallbackRecord& cbr : m_cleanupCbs) cbr.m_cleanupCb(cbr.m_userp, self());
|
||||
|
||||
if (offload() && VL_LIKELY(bufferp)) {
|
||||
// Mark end of the offload buffer we just filled
|
||||
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::END;
|
||||
|
||||
// Assert no buffer overflow
|
||||
assert(static_cast<size_t>(m_offloadBufferWritep - bufferp) <= m_offloadBufferSize);
|
||||
|
||||
// Reset our pointers as we are giving up the buffer
|
||||
m_offloadBufferWritep = nullptr;
|
||||
m_offloadBufferEndp = nullptr;
|
||||
|
||||
// Pass it to the worker thread
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
|
@ -658,20 +381,9 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
|
|||
const std::unique_ptr<VerilatedTraceConfig> configp = modelp->traceConfig();
|
||||
|
||||
// Configure trace base class
|
||||
if (!firstModel) {
|
||||
if (m_offload != configp->m_useOffloading) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Either all or no models using the same trace file must use offloading");
|
||||
}
|
||||
}
|
||||
m_offload = configp->m_useOffloading;
|
||||
// If at least one model requests parallel tracing, then use it
|
||||
m_parallel |= configp->m_useParallel;
|
||||
|
||||
if (VL_UNCOVERABLE(m_parallel && m_offload)) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use parallel tracing with offloading");
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
// Configure format-specific sub class
|
||||
configure(*(configp.get()));
|
||||
}
|
||||
|
|
@ -696,31 +408,16 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addConstCb(dumpCb_t cb, uint32_t fidx,
|
|||
addCallbackRecord(m_constCbs, CallbackRecord{cb, fidx, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addConstCb(dumpOffloadCb_t cb, uint32_t fidx,
|
||||
void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_constOffloadCbs, CallbackRecord{cb, fidx, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, uint32_t fidx,
|
||||
void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_fullCbs, CallbackRecord{cb, fidx, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpOffloadCb_t cb, uint32_t fidx,
|
||||
void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_fullOffloadCbs, CallbackRecord{cb, fidx, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, uint32_t fidx,
|
||||
void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_chgCbs, CallbackRecord{cb, fidx, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpOffloadCb_t cb, uint32_t fidx,
|
||||
void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_chgOffloadCbs, CallbackRecord{cb, fidx, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
|
|
@ -914,23 +611,4 @@ void VerilatedTraceBuffer<VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
|
|||
emitDouble(code, newval);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// VerilatedTraceOffloadBuffer
|
||||
|
||||
template <>
|
||||
VerilatedTraceOffloadBuffer<VL_BUF_T>::VerilatedTraceOffloadBuffer(VL_SUB_T& owner)
|
||||
: VerilatedTraceBuffer<VL_BUF_T>{owner}
|
||||
, m_offloadBufferWritep{owner.m_offloadBufferWritep}
|
||||
, m_offloadBufferEndp{owner.m_offloadBufferEndp} {
|
||||
if (m_offloadBufferWritep) {
|
||||
using This = VerilatedTraceBuffer<VL_BUF_T>*;
|
||||
// Tack on the buffer address
|
||||
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
|
||||
"This should be enough on all platforms");
|
||||
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
|
||||
*reinterpret_cast<This*>(m_offloadBufferWritep) = static_cast<This>(this);
|
||||
m_offloadBufferWritep += 2;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // VL_CPPCHECK
|
||||
|
|
|
|||
|
|
@ -78,11 +78,14 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
|
|||
//=========================================================================
|
||||
// Declare net data types
|
||||
|
||||
#ifndef VL_NO_LEGACY
|
||||
#define VL_SIG8(name, msb, lsb) CData name ///< Declare signal, 1-8 bits
|
||||
#define VL_SIG16(name, msb, lsb) SData name ///< Declare signal, 9-16 bits
|
||||
#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits
|
||||
#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits
|
||||
#define VL_SIGW(name, msb, lsb, words) VlWide<words> name ///< Declare signal, 65+ bits
|
||||
#endif
|
||||
|
||||
#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits
|
||||
#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits
|
||||
#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits
|
||||
|
|
@ -107,14 +110,47 @@ constexpr IData VL_CLOG2_CE_Q(QData lhs) VL_PURE {
|
|||
return lhs <= 1 ? 0 : VL_CLOG2_CE_Q((lhs + 1) >> 1ULL) + 1;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Random
|
||||
|
||||
// Random Number Generator with internal state
|
||||
class VlRNG final {
|
||||
std::array<uint64_t, 2> m_state;
|
||||
|
||||
public:
|
||||
// The default constructor simply sets state, to avoid vl_rand64()
|
||||
// having to check for construction at each call
|
||||
// Alternative: seed with zero and check on rand64() call
|
||||
VlRNG() VL_MT_SAFE;
|
||||
explicit VlRNG(uint64_t seed) VL_PURE;
|
||||
void srandom(uint64_t n) VL_MT_UNSAFE;
|
||||
std::string get_randstate() const VL_MT_UNSAFE;
|
||||
void set_randstate(const std::string& state) VL_MT_UNSAFE;
|
||||
uint64_t rand64() VL_MT_UNSAFE;
|
||||
// Threadsafe, but requires use on vl_thread_rng or vl_current_rng
|
||||
static uint64_t vl_thread_rng_rand64() VL_MT_SAFE;
|
||||
static uint64_t vl_current_rng_rand64() VL_MT_SAFE;
|
||||
static VlRNG& vl_thread_rng() VL_MT_SAFE;
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Metadata of processes
|
||||
using VlProcessRef = std::shared_ptr<VlProcess>;
|
||||
class VlForkSync;
|
||||
class VlForkSyncState;
|
||||
|
||||
class VlProcess final {
|
||||
// MEMBERS
|
||||
int m_state; // Current state of the process
|
||||
VlProcessRef m_parentp = nullptr; // Parent process, if exists
|
||||
std::set<VlProcess*> m_children; // Active child processes
|
||||
VlForkSyncState* m_forkSyncOnKillp
|
||||
= nullptr; // Optional fork..join counter to decrement on kill
|
||||
bool m_forkSyncOnKillDone = false; // Ensure on-kill callback fires only once
|
||||
VlRNG m_rng; // Per-process RNG (IEEE 1800-2023 18.14)
|
||||
|
||||
// Thread-local current process pointer for hierarchical object seeding
|
||||
static thread_local VlProcess* t_currentp;
|
||||
|
||||
public:
|
||||
// TYPES
|
||||
|
|
@ -145,14 +181,18 @@ public:
|
|||
void detach(VlProcess* childp) { m_children.erase(childp); }
|
||||
|
||||
int state() const { return m_state; }
|
||||
void state(int s) { m_state = s; }
|
||||
void state(int s);
|
||||
void disable() {
|
||||
state(KILLED);
|
||||
disableFork();
|
||||
}
|
||||
void disableFork() {
|
||||
for (VlProcess* childp : m_children) childp->disable();
|
||||
// childp->disable() may resume coroutines and mutate m_children
|
||||
const std::set<VlProcess*> children = m_children;
|
||||
for (VlProcess* childp : children) childp->disable();
|
||||
}
|
||||
void forkSyncOnKill(VlForkSyncState* forkSyncp);
|
||||
void forkSyncOnKillClear(VlForkSyncState* forkSyncp);
|
||||
bool completed() const { return state() == FINISHED || state() == KILLED; }
|
||||
bool completedFork() const {
|
||||
for (const VlProcess* const childp : m_children)
|
||||
|
|
@ -160,11 +200,22 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
// Random state (IEEE 1800-2023 9.7, 18.14)
|
||||
void srandom(uint64_t seed) VL_MT_UNSAFE { m_rng.srandom(seed); }
|
||||
std::string randstate() const VL_MT_UNSAFE;
|
||||
void randstate(const std::string& state) VL_MT_UNSAFE;
|
||||
|
||||
// Current process tracking for hierarchical object seeding
|
||||
static VlProcess* currentp() VL_MT_UNSAFE { return t_currentp; }
|
||||
static void currentp(VlProcess* processp) VL_MT_UNSAFE { t_currentp = processp; }
|
||||
// Return process RNG if in a process, else thread RNG
|
||||
static VlRNG& currentRng() VL_MT_SAFE;
|
||||
};
|
||||
|
||||
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
|
||||
inline std::string VL_TO_STRING(const VlProcessRef&) { return std::string("process"); }
|
||||
|
||||
// Use process RNG if in a process, else thread RNG (IEEE 1800-2023 18.14)
|
||||
inline uint64_t vl_rand64() VL_MT_SAFE { return VlRNG::vl_current_rng_rand64(); }
|
||||
|
||||
//===================================================================
|
||||
// SystemVerilog event type
|
||||
|
|
@ -232,30 +283,6 @@ inline std::string VL_TO_STRING(const VlEventBase& e) {
|
|||
return "triggered="s + (e.isTriggered() ? "true" : "false");
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Random
|
||||
|
||||
// Random Number Generator with internal state
|
||||
class VlRNG final {
|
||||
std::array<uint64_t, 2> m_state;
|
||||
|
||||
public:
|
||||
// The default constructor simply sets state, to avoid vl_rand64()
|
||||
// having to check for construction at each call
|
||||
// Alternative: seed with zero and check on rand64() call
|
||||
VlRNG() VL_MT_SAFE;
|
||||
explicit VlRNG(uint64_t seed) VL_PURE;
|
||||
void srandom(uint64_t n) VL_MT_UNSAFE;
|
||||
std::string get_randstate() const VL_MT_UNSAFE;
|
||||
void set_randstate(const std::string& state) VL_MT_UNSAFE;
|
||||
uint64_t rand64() VL_MT_UNSAFE;
|
||||
// Threadsafe, but requires use on vl_thread_rng
|
||||
static uint64_t vl_thread_rng_rand64() VL_MT_SAFE;
|
||||
static VlRNG& vl_thread_rng() VL_MT_SAFE;
|
||||
};
|
||||
|
||||
inline uint64_t vl_rand64() VL_MT_SAFE { return VlRNG::vl_thread_rng_rand64(); }
|
||||
|
||||
// RNG for shuffle()
|
||||
class VlURNG final {
|
||||
public:
|
||||
|
|
@ -428,6 +455,9 @@ std::string VL_TO_STRING(const VlWide<N_Words>& obj) {
|
|||
return VL_TO_STRING_W(N_Words, obj.data());
|
||||
}
|
||||
|
||||
template <typename T_Class>
|
||||
class VlClassRef;
|
||||
|
||||
//===================================================================
|
||||
// Verilog queue and dynamic array container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
|
|
@ -438,6 +468,9 @@ std::string VL_TO_STRING(const VlWide<N_Words>& obj) {
|
|||
template <typename T_Value, size_t N_MaxSize = 0>
|
||||
class VlQueue final {
|
||||
private:
|
||||
template <typename U_Value, size_t M_MaxSize>
|
||||
friend class VlQueue;
|
||||
|
||||
// TYPES
|
||||
using Deque = std::deque<T_Value>;
|
||||
|
||||
|
|
@ -460,6 +493,13 @@ public:
|
|||
VlQueue(VlQueue&&) = default;
|
||||
VlQueue& operator=(const VlQueue&) = default;
|
||||
VlQueue& operator=(VlQueue&&) = default;
|
||||
|
||||
// Template constuctors that construct from containers holding sub-classes
|
||||
template <typename T_Subclass>
|
||||
inline VlQueue(const VlQueue<VlClassRef<T_Subclass>>&);
|
||||
template <typename T_Subclass>
|
||||
inline VlQueue(VlQueue<VlClassRef<T_Subclass>>&&);
|
||||
|
||||
bool operator==(const VlQueue& rhs) const { return m_deque == rhs.m_deque; }
|
||||
bool operator!=(const VlQueue& rhs) const { return m_deque != rhs.m_deque; }
|
||||
bool operator<(const VlQueue& rhs) const {
|
||||
|
|
@ -589,11 +629,8 @@ public:
|
|||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(int32_t index) const {
|
||||
// Needs to work for dynamic arrays, so does not use N_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
return atDefault();
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return atDefault();
|
||||
return m_deque[index];
|
||||
}
|
||||
// Access with an index counted from end (e.g. q[$])
|
||||
T_Value& atWriteAppendBack(int32_t index) { return atWriteAppend(m_deque.size() - 1 - index); }
|
||||
|
|
@ -625,6 +662,24 @@ public:
|
|||
VlQueue sliceBackBack(int32_t lsb, int32_t msb) const {
|
||||
return slice(m_deque.size() - 1 - lsb, m_deque.size() - 1 - msb);
|
||||
}
|
||||
// Assign src elements to q[lsb:msb]
|
||||
void sliceAssign(int32_t lsb, int32_t msb, const VlQueue& src) {
|
||||
const int32_t sz = static_cast<int32_t>(m_deque.size());
|
||||
const int32_t srcSz = static_cast<int32_t>(src.m_deque.size());
|
||||
if (VL_UNLIKELY(sz <= 0 || srcSz <= 0)) return;
|
||||
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
|
||||
if (VL_UNLIKELY(lsb >= sz)) lsb = sz - 1;
|
||||
if (VL_UNLIKELY(msb >= sz)) msb = sz - 1;
|
||||
const int32_t count = std::min(msb - lsb + 1, srcSz);
|
||||
if (VL_UNLIKELY(count <= 0)) return;
|
||||
std::copy_n(src.m_deque.begin(), count, m_deque.begin() + lsb);
|
||||
}
|
||||
void sliceAssignFrontBack(int32_t lsb, int32_t msb, const VlQueue& src) {
|
||||
sliceAssign(lsb, m_deque.size() - 1 - msb, src);
|
||||
}
|
||||
void sliceAssignBackBack(int32_t lsb, int32_t msb, const VlQueue& src) {
|
||||
sliceAssign(m_deque.size() - 1 - lsb, m_deque.size() - 1 - msb, src);
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_deque.begin(); }
|
||||
|
|
@ -764,6 +819,17 @@ public:
|
|||
}
|
||||
return VlQueue<IData>{};
|
||||
}
|
||||
// Map method (IEEE 1800-2023 7.12.5)
|
||||
template <typename T_Func>
|
||||
VlQueue<WithFuncReturnType<T_Func>> map(T_Func with_func) const {
|
||||
VlQueue<WithFuncReturnType<T_Func>> out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
out.push_back(with_func(index, i));
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue min() const {
|
||||
|
|
@ -976,11 +1042,8 @@ public:
|
|||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(const T_Key& index) const {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
return m_defaultValue;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
if (it == m_map.end()) return m_defaultValue;
|
||||
return it->second;
|
||||
}
|
||||
// Setting as a chained operation
|
||||
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
||||
|
|
@ -1100,6 +1163,13 @@ public:
|
|||
if (it == m_map.rend()) return VlQueue<T_Key>{};
|
||||
return VlQueue<T_Key>::consV(it->first);
|
||||
}
|
||||
// Map method (IEEE 1800-2023 7.12.5)
|
||||
template <typename T_Func>
|
||||
VlQueue<WithFuncReturnType<T_Func>> map(T_Func with_func) const {
|
||||
VlQueue<WithFuncReturnType<T_Func>> out;
|
||||
for (const auto& i : m_map) out.push_back(with_func(i.first, i.second));
|
||||
return out;
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue<T_Value> min() const {
|
||||
|
|
@ -1284,7 +1354,6 @@ public:
|
|||
// Default copy assignment operators are used.
|
||||
|
||||
// METHODS
|
||||
public:
|
||||
// Raw access
|
||||
WData* data() { return &m_storage[0]; }
|
||||
const WData* data() const { return &m_storage[0]; }
|
||||
|
|
@ -1303,11 +1372,8 @@ public:
|
|||
|
||||
template <std::size_t N_CurrentDimension = 0, typename U = T_Value>
|
||||
int find_length(int dimension, std::true_type) const {
|
||||
if (dimension == N_CurrentDimension) {
|
||||
return size();
|
||||
} else {
|
||||
return m_storage[0].template find_length<N_CurrentDimension + 1>(dimension);
|
||||
}
|
||||
if (dimension == N_CurrentDimension) return size();
|
||||
return m_storage[0].template find_length<N_CurrentDimension + 1>(dimension);
|
||||
}
|
||||
|
||||
template <std::size_t N_CurrentDimension = 0>
|
||||
|
|
@ -1492,6 +1558,13 @@ public:
|
|||
}
|
||||
return VlQueue<T_Key>{};
|
||||
}
|
||||
// Map method (IEEE 1800-2023 7.12.5)
|
||||
template <typename T_Func>
|
||||
VlQueue<WithFuncReturnType<T_Func>> map(T_Func with_func) const {
|
||||
VlQueue<WithFuncReturnType<T_Func>> out;
|
||||
for (IData i = 0; i < N_Depth; ++i) out.push_back(with_func(i, m_storage[i]));
|
||||
return out;
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue<T_Value> min() const {
|
||||
|
|
@ -1883,7 +1956,7 @@ class VlClass VL_NOT_FINAL : public VlDeletable {
|
|||
public:
|
||||
// CONSTRUCTORS
|
||||
VlClass() {}
|
||||
VlClass(const VlClass& copied) {}
|
||||
VlClass(const VlClass& /*copied*/) {}
|
||||
~VlClass() override = default;
|
||||
// Polymorphic shallow clone. Overridden in each generated concrete class.
|
||||
virtual VlClass* clone() const { return nullptr; }
|
||||
|
|
@ -1900,7 +1973,10 @@ public:
|
|||
|
||||
struct VlNull final {
|
||||
operator bool() const { return false; }
|
||||
bool operator==(const void* ptr) const { return !ptr; }
|
||||
template <class T>
|
||||
operator T*() const {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
inline bool operator==(const void* ptr, VlNull) { return !ptr; }
|
||||
|
||||
|
|
@ -1937,11 +2013,26 @@ public:
|
|||
VlClassRef(VlNull){};
|
||||
template <typename... T_Args>
|
||||
VlClassRef(VlDeleter& deleter, T_Args&&... args)
|
||||
// () required here to avoid narrowing conversion warnings,
|
||||
// when a new() has an e.g. CData type and passed a 1U.
|
||||
: m_objp{new T_Class(std::forward<T_Args>(args)...)} {
|
||||
// refCountInc was moved to the constructor of T_Class
|
||||
// to fix self references in constructor.
|
||||
: m_objp{new T_Class} {
|
||||
// Instantly init the object to presevrve RAII
|
||||
m_objp->init(std::forward<T_Args>(args)...);
|
||||
m_objp->m_deleterp = &deleter;
|
||||
}
|
||||
VlClassRef(VlDeleter& deleter, T_Class&& args)
|
||||
// Move constructor
|
||||
: m_objp{new T_Class{std::forward<T_Class>(args)}} {
|
||||
m_objp->m_deleterp = &deleter;
|
||||
}
|
||||
VlClassRef(VlDeleter& deleter, const T_Class& args)
|
||||
// Copy constructor
|
||||
: m_objp{new T_Class{args}} {
|
||||
m_objp->m_deleterp = &deleter;
|
||||
}
|
||||
VlClassRef(VlDeleter& deleter, T_Class& args)
|
||||
// Copy constructor - this is required since if `T_Class&`
|
||||
// will be provided a compiler will match it to the constructor
|
||||
// with variadic template instead of `T_Class&&`
|
||||
: m_objp{new T_Class{args}} {
|
||||
m_objp->m_deleterp = &deleter;
|
||||
}
|
||||
// Explicit to avoid implicit conversion from 0
|
||||
|
|
@ -2010,7 +2101,7 @@ public:
|
|||
VlClassRef<T_OtherClass> dynamicCast() const {
|
||||
return VlClassRef<T_OtherClass>{dynamic_cast<T_OtherClass*>(m_objp)};
|
||||
}
|
||||
// Polymorphic shallow clone (IEEE 1800-2017 8.7: new <handle> preserves runtime type)
|
||||
// Polymorphic shallow clone (IEEE 1800-2023 8.7: new <handle> preserves runtime type)
|
||||
VlClassRef clone(VlDeleter& deleter) const {
|
||||
VlClass* clonedp = m_objp->clone();
|
||||
if (VL_UNLIKELY(!clonedp)) return {};
|
||||
|
|
@ -2049,13 +2140,12 @@ static inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr
|
|||
if (VL_LIKELY(casted)) {
|
||||
outr = casted;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T_Lhs>
|
||||
static inline bool VL_CAST_DYNAMIC(VlNull in, VlClassRef<T_Lhs>& outr) {
|
||||
static inline bool VL_CAST_DYNAMIC(VlNull, VlClassRef<T_Lhs>& outr) {
|
||||
outr = VlNull{};
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2136,4 +2226,17 @@ inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
|
|||
|
||||
//======================================================================
|
||||
|
||||
template <typename T_Value, size_t N_MaxSize>
|
||||
template <typename T_Subclass>
|
||||
VlQueue<T_Value, N_MaxSize>::VlQueue(const VlQueue<VlClassRef<T_Subclass>>& that)
|
||||
: m_deque{that.m_deque.begin(), that.m_deque.end()}
|
||||
, m_defaultValue{that.m_defaultValue} {}
|
||||
|
||||
template <typename T_Value, size_t N_MaxSize>
|
||||
template <typename T_Subclass>
|
||||
VlQueue<T_Value, N_MaxSize>::VlQueue(VlQueue<VlClassRef<T_Subclass>>&& that)
|
||||
: m_deque{std::make_move_iterator(that.m_deque.begin()),
|
||||
std::make_move_iterator(that.m_deque.end())}
|
||||
, m_defaultValue{std::move(that.m_defaultValue)} {}
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -278,7 +278,6 @@ void VerilatedVcd::bufferResize(size_t minsize) {
|
|||
}
|
||||
|
||||
void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
// This function can be called from the trace offload thread
|
||||
// This function is on the flush() call path
|
||||
// We add output data to m_writep.
|
||||
// When it gets nearly full we dump it using this routine which calls write()
|
||||
|
|
@ -333,7 +332,8 @@ void VerilatedVcd::pushPrefix(const char* namep, VerilatedTracePrefixType type)
|
|||
// Upper has name, we can suppress inserting $rootio, but still push so popPrefix works
|
||||
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
|
||||
return;
|
||||
} else if (name.empty()) {
|
||||
}
|
||||
if (name.empty()) {
|
||||
m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
|
||||
return;
|
||||
}
|
||||
|
|
@ -404,7 +404,7 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b
|
|||
char* vcdCodeWritep = vcdCode;
|
||||
uint32_t codeEnc = code;
|
||||
do {
|
||||
*vcdCodeWritep++ = static_cast<char>('!' + codeEnc % 94);
|
||||
*vcdCodeWritep++ = static_cast<char>('!' + (codeEnc % 94));
|
||||
codeEnc /= 94;
|
||||
} while (codeEnc--);
|
||||
*vcdCodeWritep = '\0';
|
||||
|
|
@ -451,41 +451,50 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b
|
|||
printStr(decl.c_str());
|
||||
}
|
||||
|
||||
void VerilatedVcd::declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind,
|
||||
VerilatedTraceSigType, bool array, int arraynum) {
|
||||
declare(code, name, "event", array, arraynum, false, 0, 0);
|
||||
// versions to call when the sig is not array member
|
||||
void VerilatedVcd::declEvent(uint32_t code, const char* name) {
|
||||
declare(code, name, "event", false, -1, false, 0, 0);
|
||||
}
|
||||
void VerilatedVcd::declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind,
|
||||
VerilatedTraceSigType, bool array, int arraynum) {
|
||||
declare(code, name, "wire", array, arraynum, false, 0, 0);
|
||||
void VerilatedVcd::declBit(uint32_t code, const char* name) {
|
||||
declare(code, name, "wire", false, -1, false, 0, 0);
|
||||
}
|
||||
void VerilatedVcd::declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind,
|
||||
VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, true, msb, lsb);
|
||||
void VerilatedVcd::declBus(uint32_t code, const char* name, int msb, int lsb) {
|
||||
declare(code, name, "wire", false, -1, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind,
|
||||
VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, true, msb, lsb);
|
||||
void VerilatedVcd::declQuad(uint32_t code, const char* name, int msb, int lsb) {
|
||||
declare(code, name, "wire", false, -1, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind,
|
||||
VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, true, msb, lsb);
|
||||
void VerilatedVcd::declWide(uint32_t code, const char* name, int msb, int lsb) {
|
||||
declare(code, name, "wire", false, -1, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind,
|
||||
VerilatedTraceSigType, bool array, int arraynum) {
|
||||
declare(code, name, "real", array, arraynum, false, 63, 0);
|
||||
void VerilatedVcd::declDouble(uint32_t code, const char* name) {
|
||||
declare(code, name, "real", false, -1, false, 63, 0);
|
||||
}
|
||||
|
||||
// versions to call when the sig is array member
|
||||
void VerilatedVcd::declEventArray(uint32_t code, const char* name, int arraynum) {
|
||||
declare(code, name, "event", true, arraynum, false, 0, 0);
|
||||
}
|
||||
void VerilatedVcd::declBitArray(uint32_t code, const char* name, int arraynum) {
|
||||
declare(code, name, "wire", true, arraynum, false, 0, 0);
|
||||
}
|
||||
void VerilatedVcd::declBusArray(uint32_t code, const char* name, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, "wire", true, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declQuadArray(uint32_t code, const char* name, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, "wire", true, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declWideArray(uint32_t code, const char* name, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, "wire", true, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declDoubleArray(uint32_t code, const char* name, int arraynum) {
|
||||
declare(code, name, "real", true, arraynum, false, 63, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Get/commit trace buffer
|
||||
|
||||
VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer(uint32_t fidx) {
|
||||
VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer(uint32_t /*fidx*/) {
|
||||
VerilatedVcd::Buffer* const bufp = new Buffer{*this};
|
||||
if (parallel()) {
|
||||
// Note: This is called from VerilatedVcd::dump, which already holds the lock
|
||||
|
|
@ -494,7 +503,7 @@ VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer(uint32_t fidx) {
|
|||
// cppcheck-suppress unreadVariable // cppcheck bug, used below
|
||||
constexpr size_t pageSize = 4096;
|
||||
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
|
||||
size_t startingSize = vlstd::roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
|
||||
const size_t startingSize = vlstd::roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
|
||||
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
|
||||
++m_numBuffers;
|
||||
}
|
||||
|
|
@ -679,7 +688,7 @@ VL_ATTR_ALWINLINE
|
|||
void VerilatedVcdBuffer::emitDouble(uint32_t code, double newval) {
|
||||
char* wp = m_writep;
|
||||
// Buffer can't overflow before VL_SNPRINTF; we sized during declaration
|
||||
VL_SNPRINTF(wp, m_maxSignalBytes, "r%.16g", newval);
|
||||
(void)VL_SNPRINTF(wp, m_maxSignalBytes, "r%.16g", newval);
|
||||
wp += std::strlen(wp);
|
||||
finishLine(code, wp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,26 +137,56 @@ public:
|
|||
void pushPrefix(const char*, VerilatedTracePrefixType);
|
||||
void popPrefix();
|
||||
|
||||
void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum);
|
||||
void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum);
|
||||
void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
|
||||
bool array, int arraynum);
|
||||
// versions to call when the sig is not array member
|
||||
void declEvent(uint32_t code, const char* name);
|
||||
void declBit(uint32_t code, const char* name);
|
||||
void declBus(uint32_t code, const char* name, int msb, int lsb);
|
||||
void declQuad(uint32_t code, const char* name, int msb, int lsb);
|
||||
void declWide(uint32_t code, const char* name, int msb, int lsb);
|
||||
void declDouble(uint32_t code, const char* name);
|
||||
|
||||
// versions to call when the sig is array member
|
||||
void declEventArray(uint32_t code, const char* name, int arraynum);
|
||||
void declBitArray(uint32_t code, const char* name, int arraynum);
|
||||
void declBusArray(uint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void declQuadArray(uint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void declWideArray(uint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void declDoubleArray(uint32_t code, const char* name, int arraynum);
|
||||
};
|
||||
|
||||
// We use macros to drop unused arguments at compile time. This saves code size.
|
||||
#define VL_TRACE_PUSH_PREFIX(tracep, name, type, left, right) tracep->pushPrefix(name, type);
|
||||
#define VL_TRACE_POP_PREFIX(tracep) tracep->popPrefix();
|
||||
|
||||
#define VL_TRACE_DECL_EVENT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
|
||||
tracep->declEvent(code, name)
|
||||
#define VL_TRACE_DECL_BIT(tracep, code, fidx, name, dtypenum, dir, kind, type) \
|
||||
tracep->declBit(code, name)
|
||||
#define VL_TRACE_DECL_BUS(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
|
||||
tracep->declBus(code, name, msb, lsb)
|
||||
#define VL_TRACE_DECL_QUAD(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
|
||||
tracep->declQuad(code, name, msb, lsb)
|
||||
#define VL_TRACE_DECL_WIDE(tracep, code, fidx, name, dtypenum, dir, kind, type, msb, lsb) \
|
||||
tracep->declWide(code, name, msb, lsb)
|
||||
#define VL_TRACE_DECL_DOUBLE(tracep, code, fidx, name, dtypenum, dir, kind, type) \
|
||||
tracep->declDouble(code, name)
|
||||
|
||||
#define VL_TRACE_DECL_EVENT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
|
||||
tracep->declEventArray(code, name, arraynum)
|
||||
#define VL_TRACE_DECL_BIT_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
|
||||
tracep->declBitArray(code, name, arraynum)
|
||||
#define VL_TRACE_DECL_BUS_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
|
||||
msb, lsb) \
|
||||
tracep->declBusArray(code, name, arraynum, msb, lsb)
|
||||
#define VL_TRACE_DECL_QUAD_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
|
||||
msb, lsb) \
|
||||
tracep->declQuadArray(code, name, arraynum, msb, lsb)
|
||||
#define VL_TRACE_DECL_WIDE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum, \
|
||||
msb, lsb) \
|
||||
tracep->declWideArray(code, name, arraynum, msb, lsb)
|
||||
#define VL_TRACE_DECL_DOUBLE_ARRAY(tracep, code, fidx, name, dtypenum, dir, kind, type, arraynum) \
|
||||
tracep->declDoubleArray(code, name, arraynum)
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// Declare specialization here as it's used in VerilatedFstC just below
|
||||
template <>
|
||||
|
|
@ -181,7 +211,6 @@ class VerilatedVcdBuffer VL_NOT_FINAL {
|
|||
friend VerilatedVcd;
|
||||
friend VerilatedVcd::Super;
|
||||
friend VerilatedVcd::Buffer;
|
||||
friend VerilatedVcd::OffloadBuffer;
|
||||
|
||||
VerilatedVcd& m_owner; // Trace file owning this buffer. Required by subclasses.
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -37,7 +37,7 @@
|
|||
//=========================================================================
|
||||
// Compiler pragma abstraction
|
||||
|
||||
#if defined(__clang__)
|
||||
#ifdef __clang__
|
||||
# define VL_CLANG_ATTR(attr) __attribute__(( attr ))
|
||||
#else
|
||||
# define VL_CLANG_ATTR(attr)
|
||||
|
|
@ -127,7 +127,7 @@
|
|||
VL_CLANG_ATTR(no_thread_safety_analysis)
|
||||
|
||||
// Require mutex locks only in code units which work with enabled multi-threading.
|
||||
#if !defined(VL_MT_DISABLED_CODE_UNIT)
|
||||
#ifndef VL_MT_DISABLED_CODE_UNIT
|
||||
// Function requires not having a capability inbound (-fthread-safety)
|
||||
# define VL_REQUIRES(x) \
|
||||
VL_CLANG_ATTR(annotate("REQUIRES")) \
|
||||
|
|
@ -318,7 +318,6 @@
|
|||
# define VL_CONSTEXPR_CXX17
|
||||
#endif
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// Optimization
|
||||
|
||||
|
|
@ -351,6 +350,8 @@ extern "C" void __gcov_dump();
|
|||
# pragma warning(disable:4189) // C4189: local variable is initialized but not referenced (L4)
|
||||
# pragma warning(disable:4244) // C4244: conversion from 'uint64_t' to 'uint_32_t', possible loss of data
|
||||
# pragma warning(disable:4245) // C4245: conversion from 'int' to 'unsigned', signed/unsigned mismatch
|
||||
# pragma warning(disable:4267) // C4267: conversion from 'size_t' to 'int', possible loss of data
|
||||
# pragma warning(disable:4316) // C4316: 'Vtop': object allocated on the heap may not be aligned 64
|
||||
# pragma warning(disable:4996) // C4996: sscanf/fopen/etc may be unsafe
|
||||
# endif
|
||||
#endif
|
||||
|
|
@ -383,7 +384,7 @@ using vlsint32_t = int32_t; ///< 32-bit signed type (backward compatibility)
|
|||
using vlsint64_t = int64_t; ///< 64-bit signed type (backward compatibility)
|
||||
#endif
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
#ifdef __CYGWIN__
|
||||
|
||||
# include <sys/types.h> // __WORDSIZE
|
||||
# include <unistd.h> // ssize_t
|
||||
|
|
@ -434,6 +435,17 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
|||
# define VL_VSNPRINTF vsnprintf
|
||||
#endif
|
||||
|
||||
// Constants for VL_SFORMATF; see V3Number.h VFormatAttr
|
||||
// Character codes are upper case so harder to confuse with format %codes.
|
||||
// (...) indicates what is passed as arguments in emitted code
|
||||
#define VL_VFORMATATTR_UNSIGNED '#' // (int widthMin, IData/WData/etc) Use standard format
|
||||
#define VL_VFORMATATTR_SIGNED '~' // (int widthMin, IData/WData/etc) Signed number; for %d showing sign
|
||||
#define VL_VFORMATATTR_COMPLEX '!' // (std::string*); for non-POD; e.g. struct, requires %p typically
|
||||
#define VL_VFORMATATTR_DOUBLE 'D' // (double); promote %p to %f
|
||||
#define VL_VFORMATATTR_SCOPE 'M' // (char* name, char* scope); for scopes
|
||||
#define VL_VFORMATATTR_STRING 'S' // (char* name, char* scope); for scopes // (std::string*); for %p/%s
|
||||
#define VL_VFORMATATTR_TIMEUNIT 'T' // (int timeunit); timeunits passed from V3Emit to runtime
|
||||
|
||||
//=========================================================================
|
||||
// File system functions
|
||||
|
||||
|
|
@ -473,12 +485,12 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
|||
|
||||
// Declare a class as uncopyable; put after a private:
|
||||
#define VL_UNCOPYABLE(Type) \
|
||||
Type(const Type& other) = delete; \
|
||||
Type(const Type& other) = delete; \
|
||||
Type& operator=(const Type&) = delete
|
||||
|
||||
// Declare a class as unmovable; put after a private:
|
||||
#define VL_UNMOVABLE(Type) \
|
||||
Type(Type&& other) = delete; \
|
||||
Type(Type&& other) = delete; \
|
||||
Type& operator=(Type&&) = delete
|
||||
|
||||
//=========================================================================
|
||||
|
|
@ -486,11 +498,6 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
|||
|
||||
#define VL_MULS_MAX_WORDS 128 ///< Max size in words of MULS operation
|
||||
|
||||
#ifndef VL_VALUE_STRING_MAX_WORDS
|
||||
#define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
|
||||
#endif
|
||||
#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS) * 4
|
||||
|
||||
//=========================================================================
|
||||
// Base macros
|
||||
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@
|
|||
# include <psapi.h> // GetProcessMemoryInfo
|
||||
#endif
|
||||
|
||||
#if defined(__linux)
|
||||
#ifdef __linux
|
||||
# include <sched.h> // For sched_getcpu()
|
||||
#endif
|
||||
#if defined(__APPLE__) && !defined(__arm64__) && !defined(__POWERPC__)
|
||||
# include <cpuid.h> // For __cpuid_count()
|
||||
#endif
|
||||
#if defined(__FreeBSD__)
|
||||
#ifdef __FreeBSD__
|
||||
# include <pthread_np.h> // For pthread_getaffinity_np()
|
||||
#endif
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ double DeltaWallTime::gettime() VL_MT_SAFE {
|
|||
// Vlos::getcpu implementation
|
||||
|
||||
uint16_t getcpu() VL_MT_SAFE {
|
||||
#if defined(__linux)
|
||||
#ifdef __linux
|
||||
return sched_getcpu(); // TODO: this is a system call. Not exactly cheap.
|
||||
#elif defined(__APPLE__) && !defined(__arm64__) && !defined(__POWERPC__)
|
||||
uint32_t info[4];
|
||||
|
|
@ -194,7 +194,7 @@ void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
|
|||
|
||||
std::string getenvStr(const std::string& envvar, const std::string& defaultValue) VL_MT_SAFE {
|
||||
std::string ret;
|
||||
#if defined(_MSC_VER)
|
||||
#ifdef _MSC_VER
|
||||
// Note: MinGW does not offer _dupenv_s
|
||||
const char* envvalue = nullptr;
|
||||
_dupenv_s((char**)&envvalue, nullptr, envvar.c_str());
|
||||
|
|
|
|||
|
|
@ -13,14 +13,20 @@ import re
|
|||
|
||||
def message_section(msg: str) -> int:
|
||||
"""Return sorting-section number for given commit message"""
|
||||
if re.match(r'^Support', msg, flags=re.IGNORECASE):
|
||||
return 10
|
||||
if re.match(r'^Add', msg, flags=re.IGNORECASE):
|
||||
return 10
|
||||
if re.match(r'^Deprecate', msg, flags=re.IGNORECASE):
|
||||
return 20
|
||||
if re.match(r'^Improve', msg, flags=re.IGNORECASE):
|
||||
if re.match(r'^Remove', msg, flags=re.IGNORECASE):
|
||||
return 30
|
||||
if re.match(r'^Fix', msg, flags=re.IGNORECASE):
|
||||
if re.match(r'^Improve', msg, flags=re.IGNORECASE):
|
||||
return 40
|
||||
if re.match(r'^Change', msg, flags=re.IGNORECASE):
|
||||
return 50
|
||||
if re.match(r'^Support', msg, flags=re.IGNORECASE):
|
||||
return 60
|
||||
if re.match(r'^Fix', msg, flags=re.IGNORECASE):
|
||||
return 70
|
||||
if re.match(r'^(Internals|CI|Tests)', msg, flags=re.IGNORECASE):
|
||||
return -1
|
||||
if re.match(r'^Bump.* from .* to .*', msg, flags=re.IGNORECASE): # dependabot
|
||||
|
|
@ -58,6 +64,8 @@ def process() -> None:
|
|||
author = am.group(1)
|
||||
if re.search(r'antmicro', email):
|
||||
author += ", Antmicro Ltd."
|
||||
if re.search(r'stud\.uni-heidelberg', email):
|
||||
author += ", Heidelberg University"
|
||||
if re.search(r'tenstorrent', email):
|
||||
author += ", Testorrent USA, Inc."
|
||||
if re.search(r'github action', author):
|
||||
|
|
@ -87,8 +95,10 @@ def process() -> None:
|
|||
line,
|
||||
flags=re.IGNORECASE)
|
||||
if m:
|
||||
# print("K %s" % line)
|
||||
msgs[key] += ' (#' + m.group(2) + ')'
|
||||
mid = m.group(2)
|
||||
if ("#" + mid) not in msgs[key]:
|
||||
# print("K %s" % line)
|
||||
msgs[key] += ' (#' + mid + ')'
|
||||
|
||||
if not msgs:
|
||||
print("No Changes need to be inserted.")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0103,C0114,C0116,W0613
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify the
|
||||
# Verilator internals under the terms of either the GNU Lesser General
|
||||
# Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
######################################################################
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
try:
|
||||
from termcolor import colored
|
||||
except ModuleNotFoundError:
|
||||
|
||||
def colored(msg, **kwargs):
|
||||
return msg
|
||||
|
||||
|
||||
def cprint(msg="", *, color=None, attrs=None, **kwargs):
|
||||
print(colored(msg, color=color, attrs=attrs), **kwargs)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Binary search utility for debugging Verilator with V3DebugBisect',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog='''
|
||||
example:
|
||||
%(prog)s DfgPeephole 0 1000 test_regress/t/t_foo.py --no-skip-identical
|
||||
''')
|
||||
|
||||
parser.add_argument("--pre", type=str, help='Command to run before each execution')
|
||||
parser.add_argument('name', help='Name of V3DebugBisect instance')
|
||||
parser.add_argument('low', type=int, help='Bisection range low value, use 0 by default')
|
||||
parser.add_argument('high',
|
||||
type=int,
|
||||
help='Bisection range high value, use a sufficiently high number')
|
||||
parser.add_argument('cmd',
|
||||
nargs=argparse.REMAINDER,
|
||||
help='Discriminator command that should exit non-zero on failure')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
var = f"VERILATOR_DEBUG_BISECT_{args.name}"
|
||||
passing = args.low - 1
|
||||
failing = args.high + 1
|
||||
|
||||
cprint()
|
||||
cprint(f"Starting bisection serach for {var} in interval [{args.low}, {args.high}]",
|
||||
attrs=["bold"])
|
||||
|
||||
while True:
|
||||
cprint()
|
||||
|
||||
passStr = str(passing) if passing >= args.low else '?'
|
||||
failStr = str(failing) if failing < args.high else '?'
|
||||
cprint(f"Current step Pass: {passStr} Fail: {failStr}", attrs=["bold"])
|
||||
|
||||
# Stop if found, or exhausted interval without finding both a pass and a fail
|
||||
if failing == args.low:
|
||||
cprint(f"The low endpoint of the search interval ({args.low}) fails. Suggest rerun with:",
|
||||
color="yellow")
|
||||
cprint(f" {sys.argv[0]} {args.name} 0 {args.low} ...", color="yellow")
|
||||
sys.exit(1)
|
||||
if passing == args.high:
|
||||
cprint(
|
||||
f"The high endpoint of the search interval ({args.high}) passes. Suggest rerun with:",
|
||||
color="yellow")
|
||||
cprint(f" {sys.argv[0]} {args.name} {args.high} {10*args.high} ...", color="yellow")
|
||||
sys.exit(1)
|
||||
if failing == passing + 1:
|
||||
cprint(f"First faling value: {var}={failing}", attrs=["bold"])
|
||||
sys.exit(0)
|
||||
|
||||
# Compute middle of interval to evaluate
|
||||
mid = (failing + passing) // 2
|
||||
|
||||
# Run pre command if given:
|
||||
if args.pre:
|
||||
cprint("Running --pre command", attrs=["bold"])
|
||||
preResult = subprocess.run(args.pre, shell=True, check=False)
|
||||
if preResult.returncode != 0:
|
||||
cprint("Pre command failed", color="red")
|
||||
sys.exit(2)
|
||||
|
||||
# Set up environment variable
|
||||
env = os.environ.copy()
|
||||
env[var] = str(mid)
|
||||
|
||||
# Run the discriminator command
|
||||
cprint(f"Running with {var}={mid}", attrs=["bold"])
|
||||
result = subprocess.run(args.cmd, env=env, check=False)
|
||||
|
||||
# Check status, update interval
|
||||
if result.returncode != 0:
|
||||
cprint(f"Run with {var}={mid}: Fail", color="red")
|
||||
failing = mid
|
||||
else:
|
||||
cprint(f"Run with {var}={mid}: Pass", color="green")
|
||||
passing = mid
|
||||
|
|
@ -42,8 +42,8 @@ set(HEADERS
|
|||
V3Active.h
|
||||
V3ActiveTop.h
|
||||
V3Assert.h
|
||||
V3AssertNfa.h
|
||||
V3AssertPre.h
|
||||
V3AssertProp.h
|
||||
V3Ast.h
|
||||
V3AstAttr.h
|
||||
V3AstInlines.h
|
||||
|
|
@ -65,11 +65,13 @@ set(HEADERS
|
|||
V3Clock.h
|
||||
V3Combine.h
|
||||
V3Common.h
|
||||
V3Control.h
|
||||
V3Const.h
|
||||
V3Container.h
|
||||
V3Control.h
|
||||
V3Coverage.h
|
||||
V3CoverageJoin.h
|
||||
V3Dead.h
|
||||
V3DebugBisect.h
|
||||
V3Delayed.h
|
||||
V3Depth.h
|
||||
V3DepthBlock.h
|
||||
|
|
@ -80,7 +82,6 @@ set(HEADERS
|
|||
V3DfgDataType.h
|
||||
V3DfgOptimizer.h
|
||||
V3DfgPasses.h
|
||||
V3DfgPatternStats.h
|
||||
V3DfgPeepholePatterns.h
|
||||
V3DfgVertices.h
|
||||
V3DiagSarif.h
|
||||
|
|
@ -99,6 +100,7 @@ set(HEADERS
|
|||
V3File.h
|
||||
V3FileLine.h
|
||||
V3Force.h
|
||||
V3FsmDetect.h
|
||||
V3Fork.h
|
||||
V3FuncOpt.h
|
||||
V3FunctionTraits.h
|
||||
|
|
@ -120,6 +122,7 @@ set(HEADERS
|
|||
V3LanguageWords.h
|
||||
V3Life.h
|
||||
V3LifePost.h
|
||||
V3LiftExpr.h
|
||||
V3LinkCells.h
|
||||
V3LinkDot.h
|
||||
V3LinkDotIfaceCapture.h
|
||||
|
|
@ -129,6 +132,7 @@ set(HEADERS
|
|||
V3LinkLevel.h
|
||||
V3LinkParse.h
|
||||
V3LinkResolve.h
|
||||
V3LinkWith.h
|
||||
V3List.h
|
||||
V3Localize.h
|
||||
V3MemberMap.h
|
||||
|
|
@ -210,8 +214,8 @@ set(COMMON_SOURCES
|
|||
V3Active.cpp
|
||||
V3ActiveTop.cpp
|
||||
V3Assert.cpp
|
||||
V3AssertNfa.cpp
|
||||
V3AssertPre.cpp
|
||||
V3AssertProp.cpp
|
||||
V3Ast.cpp
|
||||
V3AstNodes.cpp
|
||||
V3Begin.cpp
|
||||
|
|
@ -241,12 +245,12 @@ set(COMMON_SOURCES
|
|||
V3Dfg.cpp
|
||||
V3DfgAstToDfg.cpp
|
||||
V3DfgBreakCycles.cpp
|
||||
V3DfgCache.cpp
|
||||
V3DfgColorSCCs.cpp
|
||||
V3DfgCse.cpp
|
||||
V3DfgDataType.cpp
|
||||
V3DfgDecomposition.cpp
|
||||
V3DfgDfgToAst.cpp
|
||||
V3DfgDumpPatterns.cpp
|
||||
V3DfgOptimizer.cpp
|
||||
V3DfgPasses.cpp
|
||||
V3DfgPeephole.cpp
|
||||
|
|
@ -274,6 +278,7 @@ set(COMMON_SOURCES
|
|||
V3File.cpp
|
||||
V3FileLine.cpp
|
||||
V3Force.cpp
|
||||
V3FsmDetect.cpp
|
||||
V3Fork.cpp
|
||||
V3FuncOpt.cpp
|
||||
V3Gate.cpp
|
||||
|
|
@ -294,6 +299,7 @@ set(COMMON_SOURCES
|
|||
V3LibMap.cpp
|
||||
V3Life.cpp
|
||||
V3LifePost.cpp
|
||||
V3LiftExpr.cpp
|
||||
V3LinkCells.cpp
|
||||
V3LinkDot.cpp
|
||||
V3LinkDotIfaceCapture.cpp
|
||||
|
|
@ -303,6 +309,7 @@ set(COMMON_SOURCES
|
|||
V3LinkLevel.cpp
|
||||
V3LinkParse.cpp
|
||||
V3LinkResolve.cpp
|
||||
V3LinkWith.cpp
|
||||
V3Localize.cpp
|
||||
V3MergeCond.cpp
|
||||
V3Name.cpp
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ LIBS = $(CFG_LIBS) -lm
|
|||
CPPFLAGS += -DVERILATOR_INTERNAL_
|
||||
CPPFLAGS += -MMD
|
||||
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
|
||||
#CPPFLAGS += -DVL_ALLOC_RANDOM_CHECKS # To allow --debug-new-random
|
||||
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
|
||||
CPPFLAGS += -MP # Only works on recent GCC versions
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
|
|
@ -231,8 +232,8 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3Active.o \
|
||||
V3ActiveTop.o \
|
||||
V3Assert.o \
|
||||
V3AssertNfa.o \
|
||||
V3AssertPre.o \
|
||||
V3AssertProp.o \
|
||||
V3Begin.o \
|
||||
V3Branch.o \
|
||||
V3CCtors.o \
|
||||
|
|
@ -257,12 +258,12 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3Dfg.o \
|
||||
V3DfgAstToDfg.o \
|
||||
V3DfgBreakCycles.o \
|
||||
V3DfgCache.o \
|
||||
V3DfgColorSCCs.o \
|
||||
V3DfgCse.o \
|
||||
V3DfgDataType.o \
|
||||
V3DfgDecomposition.o \
|
||||
V3DfgDfgToAst.o \
|
||||
V3DfgDumpPatterns.o \
|
||||
V3DfgOptimizer.o \
|
||||
V3DfgPasses.o \
|
||||
V3DfgPeephole.o \
|
||||
|
|
@ -279,6 +280,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3ExecGraph.o \
|
||||
V3Expand.o \
|
||||
V3Force.o \
|
||||
V3FsmDetect.o \
|
||||
V3Fork.o \
|
||||
V3Gate.o \
|
||||
V3HierBlock.o \
|
||||
|
|
@ -290,6 +292,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3LibMap.o \
|
||||
V3Life.o \
|
||||
V3LifePost.o \
|
||||
V3LiftExpr.o \
|
||||
V3LinkCells.o \
|
||||
V3LinkDot.o \
|
||||
V3LinkDotIfaceCapture.o \
|
||||
|
|
@ -299,6 +302,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3LinkLevel.o \
|
||||
V3LinkParse.o \
|
||||
V3LinkResolve.o \
|
||||
V3LinkWith.o \
|
||||
V3Localize.o \
|
||||
V3MergeCond.o \
|
||||
V3Name.o \
|
||||
|
|
@ -360,6 +364,7 @@ NON_STANDALONE_HEADERS = \
|
|||
V3AstNodeExpr.h \
|
||||
V3AstNodeOther.h \
|
||||
V3AstNodeStmt.h \
|
||||
V3DebugBisect.h \
|
||||
V3DfgVertices.h \
|
||||
V3ThreadPool.h \
|
||||
V3WidthRemove.h \
|
||||
|
|
|
|||
112
src/V3Assert.cpp
112
src/V3Assert.cpp
|
|
@ -127,6 +127,7 @@ class AssertVisitor final : public VNVisitor {
|
|||
// Cleared on netlist
|
||||
// AstNode::user1() -> bool. True if processed
|
||||
// AstAlways::user2p() -> std::vector<AstVar*>. Delayed variables via 'm_delayed'
|
||||
// AstNodeVarRef::user2() -> bool. True if shouldn't be sampled
|
||||
const VNUser1InUse m_user1InUse;
|
||||
const VNUser2InUse m_user2InUse;
|
||||
AstUser2Allocator<AstAlways, std::vector<AstVar*>> m_delayed;
|
||||
|
|
@ -139,7 +140,7 @@ class AssertVisitor final : public VNVisitor {
|
|||
AstVar* m_monitorOffVarp = nullptr; // $monitoroff variable
|
||||
unsigned m_modPastNum = 0; // Module past numbering
|
||||
unsigned m_modStrobeNum = 0; // Module $strobe numbering
|
||||
const AstNodeProcedure* m_procedurep = nullptr; // Current procedure
|
||||
AstNodeProcedure* m_procedurep = nullptr; // Current procedure
|
||||
VDouble0 m_statCover; // Statistic tracking
|
||||
VDouble0 m_statAsNotImm; // Statistic tracking
|
||||
VDouble0 m_statAsImm; // Statistic tracking
|
||||
|
|
@ -149,7 +150,6 @@ class AssertVisitor final : public VNVisitor {
|
|||
bool m_inRestrict = false; // True inside restrict assertion
|
||||
AstNode* m_passsp = nullptr; // Current pass statement
|
||||
AstNode* m_failsp = nullptr; // Current fail statement
|
||||
bool m_underAssert = false; // Visited from assert
|
||||
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
|
||||
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
|
||||
m_modExpr2Sen2DelayedAlwaysp;
|
||||
|
|
@ -198,11 +198,10 @@ class AssertVisitor final : public VNVisitor {
|
|||
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
|
||||
+ cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m"
|
||||
+ ((message != "") ? ": " : "") + message + "\n");
|
||||
} else {
|
||||
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
|
||||
+ cvtToStr(nodep->fileline()->lineno()) + ": %m"
|
||||
+ ((message != "") ? ": " : "") + message + "\n");
|
||||
}
|
||||
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
|
||||
+ cvtToStr(nodep->fileline()->lineno()) + ": %m" + ((message != "") ? ": " : "")
|
||||
+ message + "\n");
|
||||
}
|
||||
static bool resolveAssertType(AstAssertCtl* nodep) {
|
||||
if (!nodep->assertTypesp()) {
|
||||
|
|
@ -308,7 +307,7 @@ class AssertVisitor final : public VNVisitor {
|
|||
} else {
|
||||
bodyp = assertCond(nodep, VN_AS(propp, NodeExpr), passsp, failsp);
|
||||
}
|
||||
return newIfAssertOn(bodyp, nodep->directive(), nodep->type());
|
||||
return newIfAssertOn(bodyp, nodep->directive(), nodep->userType());
|
||||
}
|
||||
|
||||
AstNodeStmt* newFireAssertUnchecked(const AstNodeStmt* nodep, const string& message,
|
||||
|
|
@ -395,27 +394,43 @@ class AssertVisitor final : public VNVisitor {
|
|||
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
|
||||
|
||||
{ AssertDeFutureVisitor{nodep->propp(), m_modp, m_modPastNum++}; }
|
||||
iterateChildren(nodep);
|
||||
|
||||
iterateAndNextNull(nodep->sentreep());
|
||||
if (AstAssert* const assertp = VN_CAST(nodep, Assert)) {
|
||||
iterateAndNextNull(assertp->failsp());
|
||||
} else if (AstAssertIntrinsic* const assertp = VN_CAST(nodep, AssertIntrinsic)) {
|
||||
iterateAndNextNull(assertp->failsp());
|
||||
} else if (AstCover* const coverp = VN_CAST(nodep, Cover)) {
|
||||
iterateAndNextNull(coverp->coverincsp());
|
||||
} else if (!VN_IS(nodep, Restrict)) {
|
||||
nodep->v3fatalSrc("Unhandled assert type");
|
||||
}
|
||||
iterateAndNextNull(nodep->passsp());
|
||||
AstSenTree* const sentreep = nodep->sentreep();
|
||||
if (nodep->immediate()) {
|
||||
UASSERT_OBJ(!sentreep, nodep, "Immediate assertions don't have sensitivity");
|
||||
} else {
|
||||
UASSERT_OBJ(sentreep, nodep, "Concurrent assertions must have sensitivity");
|
||||
if (m_procedurep) {
|
||||
if (!nodep->senFromAlways()) {
|
||||
// To support this need queue of asserts to activate
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Procedural concurrent assertion with"
|
||||
" clocking event inside always (IEEE 1800-2023 16.14.6)");
|
||||
}
|
||||
// Change type to concurrent and relink after process
|
||||
nodep->immediate(false);
|
||||
static_cast<AstNode*>(m_procedurep)->addNext(nodep->unlinkFrBack());
|
||||
return; // Later iterate will pick up
|
||||
}
|
||||
sentreep->unlinkFrBack();
|
||||
}
|
||||
//
|
||||
const string& message = nodep->name();
|
||||
AstNode* passsp = nodep->passsp();
|
||||
if (passsp) passsp->unlinkFrBackWithNext();
|
||||
if (failsp) failsp->unlinkFrBackWithNext();
|
||||
|
||||
if (nodep->immediate()) {
|
||||
UASSERT_OBJ(!sentreep, nodep, "Immediate assertions don't have sensitivity");
|
||||
} else {
|
||||
UASSERT_OBJ(sentreep, nodep, "Concurrent assertions must have sensitivity");
|
||||
sentreep->unlinkFrBack();
|
||||
if (m_procedurep) {
|
||||
// To support this need queue of asserts to activate
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Procedural concurrent assertion with"
|
||||
" clocking event inside always (IEEE 1800-2023 16.14.6)");
|
||||
}
|
||||
}
|
||||
//
|
||||
bool selfDestruct = false;
|
||||
if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
|
||||
++m_statCover;
|
||||
|
|
@ -448,10 +463,8 @@ class AssertVisitor final : public VNVisitor {
|
|||
|
||||
VL_RESTORER(m_passsp);
|
||||
VL_RESTORER(m_failsp);
|
||||
VL_RESTORER(m_underAssert);
|
||||
m_passsp = passsp;
|
||||
m_failsp = failsp;
|
||||
m_underAssert = true;
|
||||
iterate(nodep->propp());
|
||||
|
||||
AstNode* propExprp;
|
||||
|
|
@ -704,9 +717,8 @@ class AssertVisitor final : public VNVisitor {
|
|||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_inSampled && !VString::startsWith(nodep->name(), "__VcycleDly")) {
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
if (m_inSampled && !nodep->user2() && !nodep->varp()->isTemp()) {
|
||||
if (!nodep->access().isReadOnly()) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Write to variable in sampled expression");
|
||||
|
|
@ -727,27 +739,31 @@ class AssertVisitor final : public VNVisitor {
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstPExprClause* nodep) override {
|
||||
if (m_underAssert) {
|
||||
if (nodep->pass() && m_passsp) {
|
||||
// Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
|
||||
nodep->replaceWith(m_passsp->cloneTree(true));
|
||||
} else if (!nodep->pass() && m_failsp) {
|
||||
// Asserts with multiple statements are wrapped in implicit begin/end blocks so no
|
||||
// need to clone next.
|
||||
nodep->replaceWith(m_failsp->cloneTree(false));
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
AstNode* stmtsp = nullptr;
|
||||
if (nodep->pass() && m_passsp) {
|
||||
// Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
|
||||
stmtsp = m_passsp->cloneTree(true);
|
||||
} else if (!nodep->pass() && m_failsp) {
|
||||
stmtsp = m_failsp->cloneTree(true);
|
||||
}
|
||||
if (stmtsp) {
|
||||
stmtsp->foreachAndNext([](AstNodeVarRef* const refp) {
|
||||
// References inside action blocks shouldn't be implicitly sampled
|
||||
// m_passsp/m_failsp have been already visited once and refs explicitly sampled
|
||||
// are handled already
|
||||
refp->user2(1);
|
||||
});
|
||||
nodep->replaceWith(stmtsp);
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstPExpr* nodep) override {
|
||||
if (m_underAssert) {
|
||||
VL_RESTORER(m_inSampled);
|
||||
m_inSampled = false;
|
||||
iterateChildren(nodep);
|
||||
} else if (m_inRestrict) {
|
||||
if (m_inRestrict) {
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -766,10 +782,10 @@ class AssertVisitor final : public VNVisitor {
|
|||
} else if (nodep->displayType() == VDisplayType::DT_MONITOR) {
|
||||
nodep->displayType(VDisplayType::DT_DISPLAY);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNode* monExprsp = nodep->fmtp()->exprsp();
|
||||
|
||||
AstSenItem* monSenItemsp = nullptr;
|
||||
while (monExprsp) {
|
||||
if (AstNodeVarRef* varrefp = VN_CAST(monExprsp, NodeVarRef)) {
|
||||
if (AstNode* const monExprsp = nodep->fmtp()->exprsp()) {
|
||||
monExprsp->foreachAndNext([&](AstVarRef* varrefp) {
|
||||
AstSenItem* const senItemp
|
||||
= new AstSenItem{fl, VEdgeType::ET_CHANGED,
|
||||
// Clone so get VarRef or VarXRef as needed
|
||||
|
|
@ -779,9 +795,9 @@ class AssertVisitor final : public VNVisitor {
|
|||
} else {
|
||||
monSenItemsp->addNext(senItemp);
|
||||
}
|
||||
}
|
||||
monExprsp = monExprsp->nextp();
|
||||
});
|
||||
}
|
||||
|
||||
AstSenTree* const monSenTree = new AstSenTree{fl, monSenItemsp};
|
||||
const auto monNum = ++m_monitorNum;
|
||||
// Where $monitor was we do "__VmonitorNum = N;"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Implementation of assertion properties
|
||||
// DESCRIPTION: Verilator: NFA-based multi-cycle SVA assertion evaluation
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
|
|
@ -14,8 +14,8 @@
|
|||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3ASSERTPROP_H_
|
||||
#define VERILATOR_V3ASSERTPROP_H_
|
||||
#ifndef VERILATOR_V3ASSERTNFA_H_
|
||||
#define VERILATOR_V3ASSERTNFA_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
|
@ -24,9 +24,9 @@ class AstNetlist;
|
|||
|
||||
//============================================================================
|
||||
|
||||
class V3AssertProp final {
|
||||
class V3AssertNfa final {
|
||||
public:
|
||||
static void assertPropAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
static void assertNfaAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
//*************************************************************************
|
||||
// Pre steps:
|
||||
// Attach clocks to each assertion
|
||||
// Substitute property references by property body (IEEE 1800-2012 16.12.1).
|
||||
// Substitute property references by property body (IEEE 1800-2023 16.12.1).
|
||||
// Transform clocking blocks into imperative logic
|
||||
//*************************************************************************
|
||||
|
||||
|
|
@ -27,6 +27,8 @@
|
|||
#include "V3Task.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -39,6 +41,7 @@ class AssertPreVisitor final : public VNVisitor {
|
|||
private:
|
||||
// NODE STATE
|
||||
// AstClockingItem::user1p() // AstVar*. varp() of ClockingItem after unlink
|
||||
// AstPExpr::user1() // bool. Created from AstUntil
|
||||
const VNUser1InUse m_inuser1;
|
||||
// STATE
|
||||
// Current context:
|
||||
|
|
@ -47,6 +50,7 @@ private:
|
|||
AstClocking* m_clockingp = nullptr; // Current clocking block
|
||||
// Reset each module:
|
||||
AstClocking* m_defaultClockingp = nullptr; // Default clocking for current module
|
||||
AstVar* m_defaultClkEvtVarp = nullptr; // Event var for default clocking (for ##0)
|
||||
AstDefaultDisable* m_defaultDisablep = nullptr; // Default disable for current module
|
||||
// Reset each assertion:
|
||||
AstSenItem* m_senip = nullptr; // Last sensitivity
|
||||
|
|
@ -55,40 +59,91 @@ private:
|
|||
// Reset each assertion:
|
||||
AstNodeExpr* m_disablep = nullptr; // Last disable
|
||||
AstIf* m_disableSeqIfp = nullptr; // Used for handling disable iff in sequences
|
||||
AstPExpr* m_pexprp = nullptr; // Last AstPExpr
|
||||
// Other:
|
||||
V3UniqueNames m_cycleDlyNames{"__VcycleDly"}; // Cycle delay counter name generator
|
||||
V3UniqueNames m_consRepNames{"__VconsRep"}; // Consecutive repetition counter name generator
|
||||
V3UniqueNames m_gotoRepNames{"__VgotoRep"}; // Goto repetition counter name generator
|
||||
V3UniqueNames m_nonConsRepNames{"__VnonConsRep"}; // Nonconsecutive rep name generator
|
||||
V3UniqueNames m_disableCntNames{"__VdisableCnt"}; // Disable condition counter name generator
|
||||
V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable name generator
|
||||
bool m_inAssign = false; // True if in an AssignNode
|
||||
bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS
|
||||
bool m_inSynchDrive = false; // True if in synchronous drive
|
||||
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
|
||||
bool m_inPExpr = false; // True if in AstPExpr
|
||||
std::vector<AstSequence*> m_seqsToCleanup; // Sequences to clean up after traversal
|
||||
|
||||
// METHODS
|
||||
|
||||
AstSenTree* newSenTree(AstNode* nodep, AstSenTree* useTreep = nullptr) {
|
||||
AstSenTree* newSenTree(AstNode* nodep, AstSenTree* useTreep = nullptr,
|
||||
AstNodeCoverOrAssert* cassertp = nullptr) {
|
||||
// Create sentree based on clocked or default clock
|
||||
// Return nullptr for always
|
||||
if (useTreep) return useTreep;
|
||||
AstSenTree* newp = nullptr;
|
||||
AstSenItem* senip = m_senip;
|
||||
bool fromAlways = false;
|
||||
if (!senip && m_defaultClockingp) senip = m_defaultClockingp->sensesp();
|
||||
if (!senip) senip = m_seniAlwaysp;
|
||||
if (!senip) {
|
||||
senip = m_seniAlwaysp;
|
||||
fromAlways = true;
|
||||
}
|
||||
if (!senip) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Unclocked assertion");
|
||||
newp = new AstSenTree{nodep->fileline(), nullptr};
|
||||
} else {
|
||||
if (cassertp && fromAlways) cassertp->senFromAlways(true);
|
||||
newp = new AstSenTree{nodep->fileline(), senip->cloneTree(true)};
|
||||
}
|
||||
return newp;
|
||||
}
|
||||
AstNodeExpr* getSequenceBodyExprp(const AstSequence* const seqp) const {
|
||||
// The statements in AstSequence are optional AstVar (ports) followed by body expr.
|
||||
AstNode* bodyp = seqp->stmtsp();
|
||||
while (bodyp && VN_IS(bodyp, Var)) bodyp = bodyp->nextp();
|
||||
return VN_CAST(bodyp, NodeExpr);
|
||||
}
|
||||
AstPropSpec* getPropertyExprp(const AstProperty* const propp) {
|
||||
// The only statements possible in AstProperty are AstPropSpec (body)
|
||||
// and AstVar (arguments).
|
||||
// Statements in AstProperty: AstVar (ports/local vars),
|
||||
// AstInitialStaticStmt/AstInitialAutomaticStmt (var init), AstPropSpec (body).
|
||||
AstNode* propExprp = propp->stmtsp();
|
||||
while (VN_IS(propExprp, Var)) propExprp = propExprp->nextp();
|
||||
while (propExprp
|
||||
&& (VN_IS(propExprp, Var) || VN_IS(propExprp, InitialStaticStmt)
|
||||
|| VN_IS(propExprp, InitialAutomaticStmt))) {
|
||||
propExprp = propExprp->nextp();
|
||||
}
|
||||
return VN_CAST(propExprp, PropSpec);
|
||||
}
|
||||
void substituteSequenceCall(AstFuncRef* funcrefp, AstSequence* seqp) {
|
||||
// IEEE 1800-2023 16.7 (sequence declarations), 16.8 (sequence instances)
|
||||
// Inline the sequence body at the call site, replacing the FuncRef
|
||||
AstNodeExpr* bodyExprp = getSequenceBodyExprp(seqp);
|
||||
UASSERT_OBJ(bodyExprp, funcrefp, "Sequence has no body expression");
|
||||
// Clone the body expression since the sequence may be referenced multiple times
|
||||
AstNodeExpr* clonedp = bodyExprp->cloneTree(false);
|
||||
// Build substitution map, then do a single traversal to replace all formals
|
||||
// (textual substitution per IEEE 16.8.2).
|
||||
const V3TaskConnects tconnects = V3Task::taskConnects(funcrefp, seqp->stmtsp());
|
||||
std::unordered_map<const AstVar*, AstNodeExpr*> portMap;
|
||||
for (const auto& tconnect : tconnects) {
|
||||
portMap[tconnect.first] = tconnect.second->exprp();
|
||||
}
|
||||
clonedp->foreach([&](AstVarRef* refp) {
|
||||
const auto it = portMap.find(refp->varp());
|
||||
if (it != portMap.end()) {
|
||||
refp->replaceWith(it->second->cloneTree(false));
|
||||
VL_DO_DANGLING(pushDeletep(refp), refp);
|
||||
}
|
||||
});
|
||||
for (const auto& tconnect : tconnects) {
|
||||
pushDeletep(tconnect.second->exprp()->unlinkFrBack());
|
||||
}
|
||||
// Replace the FuncRef with the inlined body
|
||||
funcrefp->replaceWith(clonedp);
|
||||
VL_DO_DANGLING(pushDeletep(funcrefp), funcrefp);
|
||||
// Clear referenced flag; sequences with isReferenced==false are deleted in assertPreAll
|
||||
seqp->isReferenced(false);
|
||||
}
|
||||
AstPropSpec* substitutePropertyCall(AstPropSpec* nodep) {
|
||||
if (AstFuncRef* const funcrefp = VN_CAST(nodep->propp(), FuncRef)) {
|
||||
if (const AstProperty* const propp = VN_CAST(funcrefp->taskp(), Property)) {
|
||||
|
|
@ -99,20 +154,53 @@ private:
|
|||
// Clone subtree after substitution. It is needed, because property might be called
|
||||
// multiple times with different arguments.
|
||||
propExprp = propExprp->cloneTree(false);
|
||||
// Substitute formal arguments with actual arguments
|
||||
// Build substitution maps for formal arguments and property-local
|
||||
// variables, then perform a single foreach to apply all replacements.
|
||||
// Map port vars to their actual argument expressions
|
||||
const V3TaskConnects tconnects = V3Task::taskConnects(funcrefp, propp->stmtsp());
|
||||
std::unordered_map<const AstVar*, AstNodeExpr*> portMap;
|
||||
for (const auto& tconnect : tconnects) {
|
||||
const AstVar* const portp = tconnect.first;
|
||||
// cppcheck-suppress constVariablePointer // 'exprp' unlinked below
|
||||
AstArg* const argp = tconnect.second;
|
||||
propExprp->foreach([&](AstVarRef* refp) {
|
||||
if (refp->varp() == portp) {
|
||||
refp->replaceWith(argp->exprp()->cloneTree(false));
|
||||
VL_DO_DANGLING(pushDeletep(refp), refp);
|
||||
}
|
||||
});
|
||||
pushDeletep(argp->exprp()->unlinkFrBack());
|
||||
portMap[tconnect.first] = tconnect.second->exprp();
|
||||
}
|
||||
|
||||
// Promote property-local variables (non-port vars, IEEE 16.10) to
|
||||
// module-level __Vpropvar temps. Cross-cycle persistence is handled
|
||||
// by the match item lowering in visit(AstImplication*).
|
||||
std::unordered_map<const AstVar*, AstVar*> localVarMap;
|
||||
for (AstNode* stmtp = propp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
|
||||
if (!varp->isIO()) {
|
||||
const string newName = m_propVarNames.get(varp);
|
||||
AstVar* const newVarp = new AstVar{
|
||||
varp->fileline(), VVarType::MODULETEMP, newName, varp->dtypep()};
|
||||
newVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
m_modp->addStmtsp(newVarp);
|
||||
localVarMap[varp] = newVarp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Single traversal: substitute ports and update local var references
|
||||
propExprp->foreach([&](AstVarRef* refp) {
|
||||
{
|
||||
const auto portIt = portMap.find(refp->varp());
|
||||
if (portIt != portMap.end()) {
|
||||
refp->replaceWith(portIt->second->cloneTree(false));
|
||||
VL_DO_DANGLING(pushDeletep(refp), refp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
{
|
||||
const auto localIt = localVarMap.find(refp->varp());
|
||||
if (localIt != localVarMap.end()) { refp->varp(localIt->second); }
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up argument expressions
|
||||
for (const auto& tconnect : tconnects) {
|
||||
pushDeletep(tconnect.second->exprp()->unlinkFrBack());
|
||||
}
|
||||
|
||||
// Handle case with 2 disable iff statement (IEEE 1800-2023 16.12.1)
|
||||
if (nodep->disablep() && propExprp->disablep()) {
|
||||
nodep->v3error("disable iff expression before property call and in its "
|
||||
|
|
@ -334,17 +422,49 @@ private:
|
|||
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
|
||||
const AstConst* const constp = VN_CAST(valuep, Const);
|
||||
if (!constp) {
|
||||
nodep->v3error(
|
||||
"Delay value is not an elaboration-time constant (IEEE 1800-2023 16.7)");
|
||||
// V3AssertNfa handles non-const delays before this pass and
|
||||
// replaces the property; this branch should never be reached.
|
||||
nodep->v3fatalSrc("Non-constant cycle delay in assertion: "
|
||||
"should have been caught by V3AssertNfa");
|
||||
} else if (constp->isZero()) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: ##0 delays");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(valuep->deleteTree(), valuep);
|
||||
VL_DO_DANGLING(pushDeletep(valuep), valuep);
|
||||
if (m_inSynchDrive) {
|
||||
// ##0 has no effect in synchronous drives (IEEE 1800-2023 14.11)
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
if (m_pexprp) {
|
||||
// ##0 in sequence context = zero delay = same clock tick
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
// Procedural ##0: synchronize with default clocking event (IEEE 1800-2023 14.11)
|
||||
// If the clocking event has not yet occurred this timestep, wait for it;
|
||||
// otherwise continue without suspension.
|
||||
if (!m_defaultClockingp) {
|
||||
nodep->v3error("Usage of cycle delays requires default clocking"
|
||||
" (IEEE 1800-2023 14.11)");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
AstVar* const evtVarp = m_defaultClkEvtVarp;
|
||||
UASSERT_OBJ(evtVarp, nodep, "Default clocking event var not pre-created");
|
||||
AstCMethodHard* const isTriggeredp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, evtVarp, VAccess::READ}, VCMethod::EVENT_IS_TRIGGERED};
|
||||
isTriggeredp->dtypeSetBit();
|
||||
AstEventControl* const waitp = new AstEventControl{
|
||||
flp,
|
||||
new AstSenTree{flp, new AstSenItem{flp, VEdgeType::ET_EVENT,
|
||||
new AstVarRef{flp, evtVarp, VAccess::READ}}},
|
||||
nullptr};
|
||||
AstIf* const ifp = new AstIf{flp, new AstNot{flp, isTriggeredp}, waitp};
|
||||
nodep->replaceWith(ifp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
AstSenItem* sensesp = nullptr;
|
||||
if (!m_defaultClockingp) {
|
||||
if (!m_inPExpr) {
|
||||
if (!m_pexprp) {
|
||||
nodep->v3error("Usage of cycle delays requires default clocking"
|
||||
" (IEEE 1800-2023 14.11)");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
|
|
@ -358,28 +478,75 @@ private:
|
|||
AstEventControl* const controlp = new AstEventControl{
|
||||
nodep->fileline(), new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr};
|
||||
const std::string delayName = m_cycleDlyNames.get(nodep);
|
||||
AstNodeExpr* throughoutp
|
||||
= nodep->throughoutp() ? nodep->throughoutp()->unlinkFrBack() : nullptr;
|
||||
|
||||
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true};
|
||||
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
|
||||
|
||||
// Throughout: create flag tracking whether condition held every tick
|
||||
AstVar* throughoutOkp = nullptr;
|
||||
if (throughoutp) {
|
||||
throughoutOkp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__throughoutOk",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::LOGIC_IMPLICIT)};
|
||||
throughoutOkp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
beginp->addStmtsp(throughoutOkp);
|
||||
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitTrue{}}});
|
||||
// Check condition at tick 0 (sequence start, before entering loop)
|
||||
beginp->addStmtsp(
|
||||
new AstIf{flp, new AstLogNot{flp, throughoutp->cloneTreePure(false)},
|
||||
new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitFalse{}}}});
|
||||
}
|
||||
|
||||
{
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(new AstLoopTest{
|
||||
flp, loopp,
|
||||
new AstGt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}}});
|
||||
// When throughout is present, exit loop early if condition fails
|
||||
AstNodeExpr* loopCondp
|
||||
= new AstGt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}};
|
||||
if (throughoutOkp) {
|
||||
loopCondp = new AstLogAnd{flp, loopCondp,
|
||||
new AstVarRef{flp, throughoutOkp, VAccess::READ}};
|
||||
}
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, loopCondp});
|
||||
loopp->addStmtsp(controlp);
|
||||
loopp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
|
||||
new AstSub{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, 1}}});
|
||||
// Check throughout condition at each tick during delay (IEEE 1800-2023 16.9.9)
|
||||
if (throughoutp) {
|
||||
loopp->addStmtsp(
|
||||
new AstIf{flp, new AstLogNot{flp, throughoutp},
|
||||
new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitFalse{}}}});
|
||||
}
|
||||
beginp->addStmtsp(loopp);
|
||||
}
|
||||
if (m_disableSeqIfp) {
|
||||
// Compose wrappers on remaining sequence: throughout gate (inner), disable iff (outer)
|
||||
AstNode* remainp = nodep->nextp() ? nodep->nextp()->unlinkFrBackWithNext() : nullptr;
|
||||
if (throughoutOkp) {
|
||||
// If condition failed during delay, fail assertion
|
||||
remainp = new AstIf{flp, new AstVarRef{flp, throughoutOkp, VAccess::READ}, remainp,
|
||||
new AstPExprClause{flp, /*pass=*/false}};
|
||||
}
|
||||
if (m_disableSeqIfp && remainp) {
|
||||
AstIf* const disableSeqIfp = m_disableSeqIfp->cloneTree(false);
|
||||
disableSeqIfp->addThensp(nodep->nextp()->unlinkFrBackWithNext());
|
||||
nodep->addNextHere(disableSeqIfp);
|
||||
// Keep continuation statements in a proper statement-list container.
|
||||
disableSeqIfp->addThensp(new AstBegin{flp, "", remainp, true});
|
||||
remainp = disableSeqIfp;
|
||||
}
|
||||
if (remainp) {
|
||||
if (throughoutOkp) {
|
||||
// throughoutOkp is declared in beginp scope -- check must be inside it
|
||||
beginp->addStmtsp(remainp);
|
||||
} else {
|
||||
nodep->addNextHere(remainp);
|
||||
}
|
||||
}
|
||||
nodep->replaceWith(beginp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
|
|
@ -494,7 +661,7 @@ private:
|
|||
|
||||
// Find Clocking's buried under nodep->exprsp
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep));
|
||||
if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep, nullptr, nodep));
|
||||
}
|
||||
void visit(AstFalling* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
|
|
@ -537,6 +704,56 @@ private:
|
|||
iterateChildren(nodep);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
}
|
||||
void visit(AstSConsRep* nodep) override {
|
||||
// IEEE 1800-2023 16.9.2 -- Lower standalone exact [*N] (N >= 2) via saturating counter.
|
||||
// Range/unbounded forms and SExpr-contained forms are lowered by V3AssertNfa.
|
||||
iterateChildren(nodep);
|
||||
// V3AssertNfa handles unbounded/ranged forms upstream, so this fast-path
|
||||
// is effectively unreachable when NFA is enabled.
|
||||
if (nodep->unbounded() || nodep->maxCountp()) return; // LCOV_EXCL_LINE
|
||||
const AstConst* const constp = VN_CAST(nodep->countp(), Const);
|
||||
if (VL_UNLIKELY(!constp || constp->toSInt() < 1)) {
|
||||
nodep->v3fatalSrc("Consecutive repetition count must be a positive constant"
|
||||
" (should have been caught by V3Width)");
|
||||
return;
|
||||
}
|
||||
const int n = constp->toSInt();
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* const exprp = nodep->exprp()->unlinkFrBack();
|
||||
if (n == 1) {
|
||||
nodep->replaceWith(exprp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
// Saturating counter: if (expr) cnt <= min(cnt+1, N); else cnt <= 0;
|
||||
AstVar* const cntVarp = new AstVar{flp, VVarType::MODULETEMP, m_consRepNames.get(""),
|
||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
cntVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
m_modp->addStmtsp(cntVarp);
|
||||
AstNodeExpr* const exprClonep = exprp->cloneTreePure(false);
|
||||
AstNodeExpr* const saturatingIncrp = new AstCond{
|
||||
flp,
|
||||
new AstLt{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, static_cast<uint32_t>(n)}},
|
||||
new AstAdd{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 1u}},
|
||||
new AstConst{flp, static_cast<uint32_t>(n)}};
|
||||
AstAssignDly* const incrAssignp
|
||||
= new AstAssignDly{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, saturatingIncrp};
|
||||
AstAssignDly* const resetAssignp = new AstAssignDly{
|
||||
flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, new AstConst{flp, 0u}};
|
||||
AstIf* const ifp = new AstIf{flp, exprClonep, incrAssignp, resetAssignp};
|
||||
AstSenTree* const senTreep = newSenTree(nodep);
|
||||
AstAlways* const alwaysp = new AstAlways{flp, VAlwaysKwd::ALWAYS, senTreep, ifp};
|
||||
cntVarp->addNextHere(alwaysp);
|
||||
// Match: cnt >= N-1 (previous cycles via NBA) && expr (current cycle)
|
||||
AstNodeExpr* const cntCheckp = new AstGte{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, static_cast<uint32_t>(n - 1)}};
|
||||
cntCheckp->dtypeSetBit();
|
||||
AstNodeExpr* const matchp = new AstAnd{flp, cntCheckp, exprp};
|
||||
matchp->dtypeSetBit();
|
||||
nodep->replaceWith(matchp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstRising* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -595,15 +812,323 @@ private:
|
|||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
// Validate repetition count: must be a non-negative elaboration-time constant.
|
||||
// Shared by goto [->N] and nonconsecutive [=N] repetition.
|
||||
// On error, replaces nodep with BitFalse placeholder and returns nullptr.
|
||||
const AstConst* validateRepCount(AstNode* nodep, AstNodeExpr*& countp) {
|
||||
countp = V3Const::constifyEdit(countp);
|
||||
const AstConst* const constp = VN_CAST(countp, Const);
|
||||
if (!constp) {
|
||||
nodep->v3error("Repetition count is not an elaboration-time constant"
|
||||
" (IEEE 1800-2023 16.9.2)");
|
||||
VL_DO_DANGLING(pushDeletep(countp), countp);
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return nullptr;
|
||||
}
|
||||
if (constp->toSInt() < 0) {
|
||||
nodep->v3error("Repetition count must be non-negative"
|
||||
" (IEEE 1800-2023 16.9.2)");
|
||||
VL_DO_DANGLING(pushDeletep(countp), countp);
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return nullptr;
|
||||
}
|
||||
if (constp->isZero()) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: zero repetition count"
|
||||
" (IEEE 1800-2023 16.9.2)");
|
||||
VL_DO_DANGLING(pushDeletep(countp), countp);
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return nullptr;
|
||||
}
|
||||
return constp;
|
||||
}
|
||||
|
||||
// Lower goto/nonconsecutive repetition to a counter-based PExpr loop.
|
||||
// IEEE 1800-2023 16.9.2:
|
||||
// Goto [->N]: count N occurrences, then check consequent
|
||||
// Nonconsec [=N] = [->N] ##1 !b[*0:$]: count N, then scan trailing !b window
|
||||
// Generated structure for goto [->N]:
|
||||
// begin
|
||||
// automatic uint32 cnt = 0;
|
||||
// loop { test(cnt < N); if (sampled(expr)) cnt++; if (cnt < N) @(clk); }
|
||||
// // consequent check or pass clause
|
||||
// end
|
||||
// Generated structure for nonconsec [=N] with implication:
|
||||
// begin
|
||||
// automatic uint32 cnt = 0;
|
||||
// loop { test(cnt < N); if (sampled(expr)) cnt++; if (cnt < N) @(clk); }
|
||||
// @(clk); // ##1
|
||||
// if (!isOverlapped) @(clk); // |=> delay
|
||||
// loop { if (sampled(expr)) fail; if (sampled(rhs)) pass; @(clk); }
|
||||
// end
|
||||
AstPExpr* createRepPExpr(FileLine* flp, AstNodeExpr* exprp, AstNodeExpr* countp,
|
||||
AstNodeExpr* rhsp, bool isOverlapped, bool isNonConsec) {
|
||||
AstSenItem* const sensesp = m_senip;
|
||||
UASSERT_OBJ(sensesp, exprp, "Repetition requires a clock");
|
||||
|
||||
const std::string name
|
||||
= isNonConsec ? m_nonConsRepNames.get(exprp) : m_gotoRepNames.get(exprp);
|
||||
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, name + "__counter",
|
||||
exprp->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
|
||||
AstBegin* const beginp = new AstBegin{flp, name + "__block", cntVarp, true};
|
||||
beginp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, new AstConst{flp, 0}});
|
||||
|
||||
// Counting loop: find N occurrences of expr (shared by goto and nonconsec)
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp,
|
||||
new AstLt{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
countp->cloneTreePure(false)}});
|
||||
// if (expr) cnt++ -- sampled is applied to whole property expr by V3Assert
|
||||
loopp->addStmtsp(
|
||||
new AstIf{flp, exprp,
|
||||
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
|
||||
new AstAdd{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, 1}}}});
|
||||
loopp->addStmtsp(new AstIf{
|
||||
flp, new AstLt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, countp},
|
||||
new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr}});
|
||||
|
||||
beginp->addStmtsp(loopp);
|
||||
|
||||
if (isNonConsec && rhsp) {
|
||||
// IEEE 16.9.2: b[=N] = b[->N] ##1 !b[*0:$]
|
||||
// Trailing window: ##1 advance, then scan !expr cycles checking rhs.
|
||||
// Window closes when expr becomes true again (fail if rhs was never true).
|
||||
beginp->addStmtsp(new AstEventControl{
|
||||
flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr}); // ##1
|
||||
if (!isOverlapped) {
|
||||
beginp->addStmtsp(new AstEventControl{
|
||||
flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr}); // |=>
|
||||
}
|
||||
// Window loop: check rhs at each !expr cycle (done variable for termination)
|
||||
AstVar* const doneVarp
|
||||
= new AstVar{flp, VVarType::BLOCKTEMP, name + "__done", exprp->findBitDType()};
|
||||
doneVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
beginp->addStmtsp(doneVarp);
|
||||
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, doneVarp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitFalse{}}});
|
||||
auto setDone = [&]() {
|
||||
return new AstAssign{flp, new AstVarRef{flp, doneVarp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitTrue{}}};
|
||||
};
|
||||
AstLoop* const windowp = new AstLoop{flp};
|
||||
// LoopTest: continue while !done
|
||||
windowp->addStmtsp(new AstLoopTest{
|
||||
flp, windowp, new AstNot{flp, new AstVarRef{flp, doneVarp, VAccess::READ}}});
|
||||
// if (expr) { fail; done = 1; } -- window closed, expr true again
|
||||
AstBegin* const failBlockp = new AstBegin{flp, "", nullptr, true};
|
||||
failBlockp->addStmtsp(new AstPExprClause{flp, false});
|
||||
failBlockp->addStmtsp(setDone());
|
||||
windowp->addStmtsp(new AstIf{flp, exprp->cloneTreePure(false), failBlockp});
|
||||
// if (rhs) { pass; done = 1; } -- consequent true at this !expr endpoint
|
||||
AstBegin* const passBlockp = new AstBegin{flp, "", nullptr, true};
|
||||
passBlockp->addStmtsp(new AstPExprClause{flp, true});
|
||||
passBlockp->addStmtsp(setDone());
|
||||
windowp->addStmtsp(new AstIf{flp, rhsp, passBlockp});
|
||||
// @(clk) -- advance to next cycle in window
|
||||
windowp->addStmtsp(
|
||||
new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
|
||||
beginp->addStmtsp(windowp);
|
||||
} else if (isNonConsec) {
|
||||
// Standalone nonconsec: ##1 into window, then pass
|
||||
beginp->addStmtsp(
|
||||
new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
|
||||
beginp->addStmtsp(new AstPExprClause{flp, true});
|
||||
} else if (rhsp) {
|
||||
// Goto rep: check consequent once at match endpoint
|
||||
if (!isOverlapped) {
|
||||
beginp->addStmtsp(new AstEventControl{
|
||||
flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
|
||||
}
|
||||
beginp->addStmtsp(new AstIf{flp, rhsp, new AstPExprClause{flp, true},
|
||||
new AstPExprClause{flp, false}});
|
||||
} else {
|
||||
// Standalone goto: pass after counting
|
||||
beginp->addStmtsp(new AstPExprClause{flp, true});
|
||||
}
|
||||
|
||||
return new AstPExpr{flp, beginp, exprp->findBitDType()};
|
||||
}
|
||||
|
||||
void visit(AstSGotoRep* nodep) override {
|
||||
// Standalone goto rep (not inside implication antecedent)
|
||||
iterateChildren(nodep);
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* countp = nodep->countp()->unlinkFrBack();
|
||||
if (!validateRepCount(nodep, countp)) return;
|
||||
AstNodeExpr* const exprp = nodep->exprp()->unlinkFrBack();
|
||||
AstPExpr* const pexprp = createRepPExpr(flp, exprp, countp, nullptr, true, false);
|
||||
nodep->replaceWith(pexprp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
void visit(AstSNonConsRep* nodep) override {
|
||||
// Standalone nonconsecutive rep (not inside implication antecedent)
|
||||
iterateChildren(nodep);
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* countp = nodep->countp()->unlinkFrBack();
|
||||
if (!validateRepCount(nodep, countp)) return;
|
||||
AstNodeExpr* const exprp = nodep->exprp()->unlinkFrBack();
|
||||
AstPExpr* const pexprp = createRepPExpr(flp, exprp, countp, nullptr, true, true);
|
||||
nodep->replaceWith(pexprp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
void visit(AstFuncRef* nodep) override {
|
||||
// IEEE 1800-2023 16.8: Inline sequence instances wherever they appear
|
||||
// in the expression tree (inside implications, boolean ops, nested refs, etc.)
|
||||
if (AstSequence* const seqp = VN_CAST(nodep->taskp(), Sequence)) {
|
||||
substituteSequenceCall(nodep, seqp);
|
||||
// The FuncRef has been replaced; do not access nodep after this point.
|
||||
// The replacement node will be visited by the parent's iterateChildren.
|
||||
return;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstImplication* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
|
||||
// Handle goto repetition as antecedent before iterateChildren,
|
||||
// so the standalone AstSGotoRep visitor doesn't process it
|
||||
if (AstSGotoRep* const gotop = VN_CAST(nodep->lhsp(), SGotoRep)) {
|
||||
iterateChildren(gotop);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* countp = gotop->countp()->unlinkFrBack();
|
||||
if (!validateRepCount(nodep, countp)) return;
|
||||
AstNodeExpr* const exprp = gotop->exprp()->unlinkFrBack();
|
||||
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstPExpr* const pexprp
|
||||
= createRepPExpr(flp, exprp, countp, rhsp, nodep->isOverlapped(), false);
|
||||
nodep->replaceWith(pexprp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle nonconsecutive repetition as antecedent before iterateChildren,
|
||||
// so the standalone AstSNonConsRep visitor doesn't process it
|
||||
if (AstSNonConsRep* const ncrp = VN_CAST(nodep->lhsp(), SNonConsRep)) {
|
||||
iterateChildren(ncrp);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* countp = ncrp->countp()->unlinkFrBack();
|
||||
if (!validateRepCount(nodep, countp)) return;
|
||||
AstNodeExpr* const exprp = ncrp->exprp()->unlinkFrBack();
|
||||
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstPExpr* const pexprp
|
||||
= createRepPExpr(flp, exprp, countp, rhsp, nodep->isOverlapped(), true);
|
||||
nodep->replaceWith(pexprp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
iterateChildren(nodep);
|
||||
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack();
|
||||
if (nodep->isOverlapped()) {
|
||||
|
||||
// Lower sequence match items (IEEE 16.11): (expr, var = val, ...) |-> / |=>
|
||||
if (AstExprStmt* const exprStmtp = VN_CAST(lhsp, ExprStmt)) {
|
||||
AstNodeExpr* const antExprp = exprStmtp->resultp()->unlinkFrBack();
|
||||
|
||||
if (nodep->isOverlapped()) {
|
||||
// |-> : assign to __Vpropvar via always_comb (continuous).
|
||||
// The assign evaluates RHS once; V3Sampled snapshots the
|
||||
// result so all consequent refs read the same value.
|
||||
AstNode* matchAssignsp = nullptr;
|
||||
for (AstNode* stmtp = exprStmtp->stmtsp(); stmtp;) {
|
||||
AstNode* const nextp = stmtp->nextp();
|
||||
if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) {
|
||||
assignp->unlinkFrBack();
|
||||
if (!matchAssignsp) {
|
||||
matchAssignsp = assignp;
|
||||
} else {
|
||||
matchAssignsp->addNext(assignp);
|
||||
}
|
||||
}
|
||||
stmtp = nextp;
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(lhsp), lhsp);
|
||||
lhsp = antExprp;
|
||||
|
||||
if (matchAssignsp) {
|
||||
AstAlways* const alwaysp
|
||||
= new AstAlways{flp, VAlwaysKwd::ALWAYS_COMB, nullptr, matchAssignsp};
|
||||
m_modp->addStmtsp(alwaysp);
|
||||
}
|
||||
} else {
|
||||
// |=> : assign to __Vpropvar via NBA in a clocked always block.
|
||||
// The NBA commits before the next cycle's sampled snapshot,
|
||||
// so the consequent (which already references __Vpropvar)
|
||||
// sees the captured value.
|
||||
AstNode* matchAssignsp = nullptr;
|
||||
for (AstNode* stmtp = exprStmtp->stmtsp(); stmtp;) {
|
||||
AstNode* const nextp = stmtp->nextp();
|
||||
if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) {
|
||||
assignp->unlinkFrBack();
|
||||
AstNodeExpr* const assignLhsp = assignp->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const assignRhsp = assignp->rhsp()->unlinkFrBack();
|
||||
AstAssignDly* const dlyp = new AstAssignDly{flp, assignLhsp, assignRhsp};
|
||||
VL_DO_DANGLING(pushDeletep(assignp), assignp);
|
||||
if (!matchAssignsp) {
|
||||
matchAssignsp = dlyp;
|
||||
} else {
|
||||
matchAssignsp->addNext(dlyp);
|
||||
}
|
||||
}
|
||||
stmtp = nextp;
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(lhsp), lhsp);
|
||||
lhsp = antExprp;
|
||||
|
||||
if (matchAssignsp) {
|
||||
AstIf* const condp
|
||||
= new AstIf{flp, antExprp->cloneTreePure(false), matchAssignsp};
|
||||
AstAlways* const alwaysp
|
||||
= new AstAlways{flp, VAlwaysKwd::ALWAYS, newSenTree(nodep), condp};
|
||||
m_modp->addStmtsp(alwaysp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AstPExpr* const pexprp = VN_CAST(rhsp, PExpr)) {
|
||||
// Implication with sequence expression on RHS (IEEE 1800-2023 16.11, 16.12.7).
|
||||
// The PExpr was lowered from the property expression earlier in this pass.
|
||||
// Wrap the PExpr body with the antecedent check so the sequence only
|
||||
// starts when the antecedent holds.
|
||||
AstNodeExpr* condp;
|
||||
if (nodep->isOverlapped()) {
|
||||
// Overlapped implication (|->): check antecedent on same cycle.
|
||||
// disable iff is applied at the assertion level, not at the
|
||||
// antecedent gate, matching the existing non-PExpr overlapped path.
|
||||
condp = lhsp;
|
||||
} else {
|
||||
// Non-overlapped implication (|=>): check antecedent from previous cycle
|
||||
if (m_disablep) {
|
||||
lhsp
|
||||
= new AstAnd{flp, new AstNot{flp, m_disablep->cloneTreePure(false)}, lhsp};
|
||||
}
|
||||
AstPast* const pastp = new AstPast{flp, lhsp};
|
||||
pastp->dtypeFrom(lhsp);
|
||||
pastp->sentreep(newSenTree(nodep));
|
||||
condp = pastp;
|
||||
}
|
||||
// Wrap existing PExpr body: if (antecedent) { <original body> } else { /* vacuous pass
|
||||
// */ }
|
||||
AstBegin* const bodyp = pexprp->bodyp();
|
||||
AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext();
|
||||
AstIf* const guardp = new AstIf{flp, condp, new AstBegin{flp, "", origStmtsp, true}};
|
||||
bodyp->addStmtsp(guardp);
|
||||
nodep->replaceWith(pexprp);
|
||||
// Don't iterate pexprp here -- it was already iterated when created
|
||||
// (in visit(AstSExpr*)), so delays and disable iff are already processed.
|
||||
} else if (nodep->isOverlapped()) {
|
||||
nodep->replaceWith(new AstLogOr{flp, new AstLogNot{flp, lhsp}, rhsp});
|
||||
} else {
|
||||
if (m_disablep) {
|
||||
|
|
@ -619,6 +1144,42 @@ private:
|
|||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (nodep->isStrong()) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: s_until"
|
||||
<< (nodep->isOverlapping() ? "_with" : "")
|
||||
<< " (in property expresion)");
|
||||
nodep->replaceWith(new AstConst{flp, AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
|
||||
AstLogAnd* const loopCondp = new AstLogAnd{flp, lhsp, new AstLogNot{flp, rhsp}};
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, loopCondp});
|
||||
loopp->addStmtsp(new AstEventControl{flp, newSenTree(nodep), nullptr});
|
||||
|
||||
AstNodeExpr* const rhsCopyp = rhsp->cloneTreePure(false);
|
||||
AstNodeExpr* const passCondp
|
||||
= nodep->isOverlapping() ? new AstLogAnd{flp, lhsp->cloneTreePure(false), rhsCopyp}
|
||||
: rhsCopyp;
|
||||
AstBegin* const beginp = new AstBegin{flp, "", loopp, true};
|
||||
beginp->addStmtsp(
|
||||
new AstIf{flp, passCondp, new AstPExprClause{flp}, new AstPExprClause{flp, false}});
|
||||
|
||||
AstPExpr* const pexprp = new AstPExpr{flp, beginp, nodep->dtypep()};
|
||||
pexprp->user1(1);
|
||||
nodep->replaceWith(pexprp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
void visit(AstDefaultDisable* nodep) override {
|
||||
// Done with these
|
||||
|
|
@ -647,7 +1208,7 @@ private:
|
|||
// Unlink and just keep a pointer to it, convert to sentree as needed
|
||||
m_senip = nodep->sensesp();
|
||||
iterateNull(nodep->disablep());
|
||||
if (VN_AS(nodep->backp(), NodeCoverOrAssert)->type() == VAssertType::CONCURRENT) {
|
||||
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);
|
||||
|
|
@ -655,28 +1216,36 @@ private:
|
|||
iterate(nodep->propp());
|
||||
}
|
||||
void visit(AstPExpr* nodep) override {
|
||||
// V3AssertNfa handles multi-cycle property expressions before this pass,
|
||||
// so the following unsupported paths are defensive and typically unreached.
|
||||
if (m_pexprp && m_pexprp->user1()) { // LCOV_EXCL_START
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Complex property expression inside 'until''");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
} // LCOV_EXCL_STOP
|
||||
if (AstLogNot* const notp = VN_CAST(nodep->backp(), LogNot)) {
|
||||
notp->replaceWith(nodep->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(notp), notp);
|
||||
iterate(nodep);
|
||||
return;
|
||||
}
|
||||
VL_RESTORER(m_inPExpr);
|
||||
// Sequence expression as antecedent of implication is not yet supported
|
||||
if (AstImplication* const implp = VN_CAST(nodep->backp(), Implication)) {
|
||||
if (implp->lhsp() == nodep) { // LCOV_EXCL_START
|
||||
implp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Implication with sequence expression as antecedent");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
} // LCOV_EXCL_STOP
|
||||
}
|
||||
VL_RESTORER(m_pexprp);
|
||||
VL_RESTORER(m_disableSeqIfp);
|
||||
m_inPExpr = true;
|
||||
m_pexprp = nodep;
|
||||
|
||||
if (m_disablep) {
|
||||
const AstSampled* sampledp;
|
||||
if (m_disablep->exists([&sampledp](const AstSampled* const sp) {
|
||||
sampledp = sp;
|
||||
return true;
|
||||
})) {
|
||||
sampledp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: $sampled inside disabled condition of a sequence");
|
||||
m_disablep = new AstConst{m_disablep->fileline(), AstConst::BitFalse{}};
|
||||
// always a copy is used, so remove it now
|
||||
pushDeletep(m_disablep);
|
||||
}
|
||||
FileLine* const flp = nodep->fileline();
|
||||
// Add counter which counts times the condition turned true
|
||||
AstVar* const disableCntp
|
||||
|
|
@ -700,11 +1269,14 @@ private:
|
|||
AstVar* const initialCntp = new AstVar{flp, VVarType::BLOCKTEMP, "__VinitialCnt",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
initialCntp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
bodyp->stmtsp()->addHereThisAsNext(initialCntp);
|
||||
AstAssign* const assignp
|
||||
= new AstAssign{flp, new AstVarRef{flp, initialCntp, VAccess::WRITE},
|
||||
readCntRefp->cloneTree(false)};
|
||||
// Prepend to the sequence body to keep statement list structure valid.
|
||||
AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext();
|
||||
bodyp->addStmtsp(initialCntp);
|
||||
initialCntp->addNextHere(assignp);
|
||||
assignp->addNextHere(origStmtsp);
|
||||
|
||||
m_disableSeqIfp
|
||||
= new AstIf{flp, new AstEq{flp, new AstVarRef{flp, initialCntp, VAccess::READ},
|
||||
|
|
@ -716,9 +1288,11 @@ private:
|
|||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_defaultClockingp);
|
||||
VL_RESTORER(m_defaultClkEvtVarp);
|
||||
VL_RESTORER(m_defaultDisablep);
|
||||
VL_RESTORER(m_modp);
|
||||
m_defaultClockingp = nullptr;
|
||||
m_defaultClkEvtVarp = nullptr;
|
||||
nodep->foreach([&](AstClocking* const clockingp) {
|
||||
if (clockingp->isDefault()) {
|
||||
if (m_defaultClockingp) {
|
||||
|
|
@ -737,6 +1311,11 @@ private:
|
|||
m_defaultDisablep = disablep;
|
||||
});
|
||||
m_modp = nodep;
|
||||
// Pre-create and cache the clocking event var before iterating children.
|
||||
// visit(AstClocking) will unlink the event from the clocking node and place it
|
||||
// in the module tree, then delete the clocking. After that, ensureEventp() would
|
||||
// create an orphaned var. Caching here avoids this.
|
||||
m_defaultClkEvtVarp = m_defaultClockingp ? m_defaultClockingp->ensureEventp() : nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstProperty* nodep) override {
|
||||
|
|
@ -744,6 +1323,11 @@ private:
|
|||
// (AstFuncRef)
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstSequence* nodep) override {
|
||||
// Sequence declarations are not visited directly; their bodies are inlined
|
||||
// at call sites by visit(AstFuncRef*). Collect for post-traversal cleanup.
|
||||
m_seqsToCleanup.push_back(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
|
|
@ -754,6 +1338,17 @@ public:
|
|||
iterate(nodep);
|
||||
// Fix up varref names
|
||||
for (AstVarXRef* xrefp : m_xrefsp) xrefp->name(xrefp->varp()->name());
|
||||
// Clean up sequence declarations after inlining.
|
||||
// Referenced sequences that were inlined have isReferenced cleared.
|
||||
// Remaining referenced sequences are in unsupported contexts (e.g. @seq event).
|
||||
for (AstSequence* seqp : m_seqsToCleanup) {
|
||||
if (seqp->isReferenced()) {
|
||||
seqp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: sequence referenced outside assertion property");
|
||||
} else {
|
||||
VL_DO_DANGLING(seqp->unlinkFrBack()->deleteTree(), seqp);
|
||||
}
|
||||
}
|
||||
}
|
||||
~AssertPreVisitor() override = default;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,264 +0,0 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Implementation of assertion properties
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2005-2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
// Each sequence is translated into a decision tree in form of deterministic
|
||||
// finite automaton (DFA) with bipartite structure. Each cycle delay is connected
|
||||
// with an expression that depending on an evaluation result, proceeds to the next
|
||||
// evaluation state. The structure is rooted with original sequence expression for
|
||||
// simplifying further transformation back to AST.
|
||||
//
|
||||
// The graph consists of the following nodes:
|
||||
//
|
||||
// DfaStmtVertex: Statements to be executed to traverse from one state to another
|
||||
// DfaExprVertex: Property expression that is checked and based on that a branch
|
||||
// is taken.
|
||||
// DfaConditionEdge: Branch edge that connects statements and expressions.
|
||||
//
|
||||
// Properties steps:
|
||||
// Ensemble a property decision tree from sequence expressions.
|
||||
// Transform property decision tree into AST, remove source sequence expression
|
||||
// Property blocks are wrapped with AstPExpr that are transformed
|
||||
// further by V3AssertPre and V3Assert.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3AssertProp.h"
|
||||
|
||||
#include "V3Graph.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Data structures (graph types)
|
||||
|
||||
class DfaVertex VL_NOT_FINAL : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(DfaVertex, V3GraphVertex)
|
||||
// STATE
|
||||
AstNode* const m_nodep; // Underlying node
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit DfaVertex(V3Graph* graphp, AstNode* nodep) VL_MT_DISABLED : V3GraphVertex{graphp},
|
||||
m_nodep{nodep} {}
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
string name() const override VL_MT_STABLE {
|
||||
return cvtToHex(m_nodep) + "\\n " + cvtToStr(m_nodep->typeName()) + "\\n"s
|
||||
+ m_nodep->fileline()->ascii();
|
||||
};
|
||||
string dotShape() const override {
|
||||
if (inEmpty()) return "tripleoctagon";
|
||||
if (outEmpty()) return "doubleoctagon";
|
||||
return "oval";
|
||||
}
|
||||
bool isStart() const { return inEmpty(); }
|
||||
};
|
||||
|
||||
class DfaStmtVertex final : public DfaVertex {
|
||||
VL_RTTI_IMPL(DfaStmtVertex, V3GraphEdge)
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit DfaStmtVertex(V3Graph* graphp, AstNodeStmt* stmtp) VL_MT_DISABLED
|
||||
: DfaVertex{graphp, stmtp} {}
|
||||
string dotColor() const override { return "green"; }
|
||||
};
|
||||
|
||||
class DfaExprVertex final : public DfaVertex {
|
||||
VL_RTTI_IMPL(DfaExprVertex, V3GraphEdge)
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit DfaExprVertex(V3Graph* graphp, AstNodeExpr* exprp) VL_MT_DISABLED
|
||||
: DfaVertex{graphp, exprp} {}
|
||||
string dotColor() const override { return "blue"; }
|
||||
};
|
||||
|
||||
class DfaConditionEdge final : public V3GraphEdge {
|
||||
VL_RTTI_IMPL(DfaConditionEdge, V3GraphEdge)
|
||||
// STATE
|
||||
const bool m_ifBranch; // Whether this branch is taken for fulfilled condition
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit DfaConditionEdge(V3Graph* graphp, DfaVertex* fromp, DfaVertex* top,
|
||||
bool ifBranch) VL_MT_DISABLED : V3GraphEdge{graphp, fromp, top, 1},
|
||||
m_ifBranch{ifBranch} {}
|
||||
~DfaConditionEdge() override = default;
|
||||
|
||||
bool ifBranch() const { return m_ifBranch; }
|
||||
string dotColor() const override { return m_ifBranch ? "green" : "red"; }
|
||||
};
|
||||
|
||||
// Parse properties and ensemble a property tree graph
|
||||
class AssertPropBuildVisitor final : public VNVisitorConst {
|
||||
// STATE
|
||||
V3Graph& m_graph; // Property tree
|
||||
DfaVertex* m_lastVtxp = nullptr; // Last encountered vertex
|
||||
bool m_underSExpr = false; // Is under sequence expression, for creating a start node
|
||||
size_t m_underLogNots = 0; // Number of 'not' operators before sequence
|
||||
|
||||
DfaStmtVertex* makeClause(const AstSExpr* nodep, bool pass) {
|
||||
return new DfaStmtVertex{
|
||||
&m_graph,
|
||||
new AstPExprClause{nodep->fileline(), m_underLogNots % 2 == 0 ? pass : !pass}};
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeCoverOrAssert* nodep) override { iterateChildrenConst(nodep); }
|
||||
void visit(AstLogNot* nodep) override {
|
||||
VL_RESTORER(m_underLogNots);
|
||||
++m_underLogNots;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstSExpr* nodep) override {
|
||||
|
||||
if (VN_IS(nodep->exprp(), SExpr)) {
|
||||
VL_RESTORER(m_underSExpr);
|
||||
m_underSExpr = true;
|
||||
iterateConst(nodep->exprp());
|
||||
} else {
|
||||
DfaExprVertex* const exprVtxp
|
||||
= new DfaExprVertex{&m_graph, nodep->exprp()->unlinkFrBack()};
|
||||
new DfaConditionEdge{&m_graph, exprVtxp, makeClause(nodep, true), true};
|
||||
new DfaConditionEdge{&m_graph, exprVtxp, makeClause(nodep, false), false};
|
||||
m_lastVtxp = exprVtxp;
|
||||
}
|
||||
|
||||
DfaExprVertex* const startVtxp
|
||||
= m_underSExpr ? nullptr : new DfaExprVertex{&m_graph, nodep};
|
||||
|
||||
DfaStmtVertex* const dlyVtxp
|
||||
= new DfaStmtVertex{&m_graph, nodep->delayp()->unlinkFrBack()};
|
||||
|
||||
if (AstSExpr* const sexprp = VN_CAST(nodep->preExprp(), SExpr)) {
|
||||
UASSERT_OBJ(!sexprp->preExprp() && !VN_IS(sexprp->exprp(), SExpr), sexprp,
|
||||
"Incorrect sexpr tree");
|
||||
DfaStmtVertex* const sdlyVtxp
|
||||
= new DfaStmtVertex{&m_graph, sexprp->delayp()->unlinkFrBack()};
|
||||
DfaExprVertex* const exprVtxp
|
||||
= new DfaExprVertex{&m_graph, sexprp->exprp()->unlinkFrBack()};
|
||||
|
||||
if (startVtxp) new DfaConditionEdge{&m_graph, startVtxp, sdlyVtxp, true};
|
||||
new DfaConditionEdge{&m_graph, sdlyVtxp, exprVtxp, true};
|
||||
new DfaConditionEdge{&m_graph, exprVtxp, dlyVtxp, true};
|
||||
new DfaConditionEdge{&m_graph, dlyVtxp, m_lastVtxp, true};
|
||||
new DfaConditionEdge{&m_graph, exprVtxp, makeClause(nodep, false), false};
|
||||
|
||||
// This case only occurs when multi-delay sequence starts with an expression,
|
||||
// don't set last as this is never a last expression.
|
||||
} else if (nodep->preExprp()) {
|
||||
DfaExprVertex* const preVtxp
|
||||
= new DfaExprVertex{&m_graph, nodep->preExprp()->unlinkFrBack()};
|
||||
if (startVtxp) new DfaConditionEdge{&m_graph, startVtxp, preVtxp, true};
|
||||
new DfaConditionEdge{&m_graph, preVtxp, dlyVtxp, true};
|
||||
new DfaConditionEdge{&m_graph, dlyVtxp, m_lastVtxp, true};
|
||||
new DfaConditionEdge{&m_graph, preVtxp, makeClause(nodep, false), false};
|
||||
m_lastVtxp = preVtxp;
|
||||
} else {
|
||||
if (startVtxp) new DfaConditionEdge{&m_graph, startVtxp, dlyVtxp, true};
|
||||
new DfaConditionEdge{&m_graph, dlyVtxp, m_lastVtxp, true};
|
||||
m_lastVtxp = dlyVtxp;
|
||||
}
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
void visit(AstConstPool* nodep) override {}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit AssertPropBuildVisitor(AstNetlist* nodep, V3Graph& graph)
|
||||
: m_graph{graph} {
|
||||
iterateConst(nodep);
|
||||
if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixedAlways("properties", true);
|
||||
}
|
||||
~AssertPropBuildVisitor() override = default;
|
||||
};
|
||||
|
||||
// Transform property graph into AST
|
||||
class AssertPropTransformer final {
|
||||
// STATE
|
||||
V3Graph& m_graph; // Property tree
|
||||
AstPExpr* m_pexprp = nullptr; // Currently built property sequence
|
||||
AstBegin* m_current = nullptr; // Currently built block
|
||||
|
||||
V3GraphVertex* processVtx(V3GraphVertex* vtxp) {
|
||||
if (DfaStmtVertex* const stmtp = vtxp->cast<DfaStmtVertex>()) return processVtx(stmtp);
|
||||
if (DfaExprVertex* const exprp = vtxp->cast<DfaExprVertex>()) return processVtx(exprp);
|
||||
// TODO use C++17 std::variant and std::visit
|
||||
v3fatalSrc("Unexpected vertex type");
|
||||
return nullptr;
|
||||
}
|
||||
V3GraphVertex* processVtx(DfaStmtVertex* vtxp) {
|
||||
UASSERT_OBJ(!vtxp->isStart(), vtxp->nodep(),
|
||||
"Starting node should be a property expression");
|
||||
UASSERT_OBJ(m_current, vtxp->nodep(), "Should be under a block");
|
||||
m_current->addStmtsp(vtxp->nodep());
|
||||
return processEdge(vtxp->outEdges().frontp());
|
||||
}
|
||||
V3GraphVertex* processVtx(DfaExprVertex* vtxp) {
|
||||
AstNode* const nodep = vtxp->nodep();
|
||||
if (vtxp->isStart()) {
|
||||
AstBegin* const bodyp = new AstBegin{nodep->fileline(), "", nullptr, true};
|
||||
m_pexprp = new AstPExpr{nodep->fileline(), bodyp, nodep->dtypep()};
|
||||
UASSERT_OBJ(vtxp->outSize1(), nodep, "Starting node must have one out edge");
|
||||
m_current = m_pexprp->bodyp();
|
||||
return processEdge(vtxp->outEdges().frontp());
|
||||
}
|
||||
UASSERT_OBJ(vtxp->outEdges().size() == 2, nodep, "Each expression must have two branches");
|
||||
AstBegin* const passsp = new AstBegin{nodep->fileline(), "", nullptr, true};
|
||||
AstNode* const failsp = vtxp->outEdges().backp()->top()->as<DfaStmtVertex>()->nodep();
|
||||
|
||||
AstSampled* const sampledp
|
||||
= new AstSampled{nodep->fileline(), VN_AS(vtxp->nodep(), NodeExpr)};
|
||||
sampledp->dtypeFrom(vtxp->nodep());
|
||||
AstIf* const ifp = new AstIf{nodep->fileline(), sampledp, passsp, failsp};
|
||||
m_current->addStmtsp(ifp);
|
||||
m_current = passsp;
|
||||
return processEdge(vtxp->outEdges().frontp());
|
||||
}
|
||||
V3GraphVertex* processEdge(const V3GraphEdge* edgep) {
|
||||
if (edgep) return processVtx(edgep->top());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit AssertPropTransformer(V3Graph& graph)
|
||||
: m_graph{graph} {
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
if (DfaVertex* const dVtxp = vtx.cast<DfaExprVertex>()) {
|
||||
if (dVtxp->isStart()) {
|
||||
VL_RESTORER(m_pexprp);
|
||||
processVtx(&vtx);
|
||||
AstSExpr* const propp = VN_AS(dVtxp->nodep(), SExpr);
|
||||
propp->replaceWith(m_pexprp);
|
||||
VL_DO_DANGLING(propp->deleteTree(), propp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Top AssertProp class
|
||||
|
||||
void V3AssertProp::assertPropAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{
|
||||
V3Graph graph;
|
||||
{ AssertPropBuildVisitor{nodep, graph}; }
|
||||
AssertPropTransformer{graph};
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("assertproperties", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
151
src/V3Ast.cpp
151
src/V3Ast.cpp
|
|
@ -63,7 +63,7 @@ VCMethod VCMethod::arrayMethod(const string& name) {
|
|||
void VCMethod::selfTest() {
|
||||
int i = 0;
|
||||
for (const auto& it : s_itemData) {
|
||||
VCMethod exp{i};
|
||||
const VCMethod exp{i};
|
||||
UASSERT_STATIC(it.m_e == exp,
|
||||
"VCMethod::s_itemData table rows are out-of-order, starting at row "s
|
||||
+ cvtToStr(i) + " '" + +it.m_name + '\'');
|
||||
|
|
@ -181,11 +181,8 @@ string AstNode::encodeName(const string& namein) {
|
|||
}
|
||||
|
||||
string AstNode::encodeNumber(int64_t num) {
|
||||
if (num < 0) {
|
||||
return "__02D" + cvtToStr(-num); // 2D=-
|
||||
} else {
|
||||
return cvtToStr(num);
|
||||
}
|
||||
if (num < 0) return "__02D" + cvtToStr(-num); // 2D=-
|
||||
return cvtToStr(num);
|
||||
}
|
||||
|
||||
string AstNode::nameProtect() const { return VIdProtect::protectIf(name(), protect()); }
|
||||
|
|
@ -394,36 +391,36 @@ AstNode* AstNode::addNext<AstNode, AstNode>(AstNode* nodep, AstNode* newp) {
|
|||
UDEBUGONLY(UASSERT_OBJ(newp, nodep, "Null item passed to addNext"););
|
||||
debugTreeChange(nodep, "-addNextThs: ", __LINE__, false);
|
||||
debugTreeChange(newp, "-addNextNew: ", __LINE__, true);
|
||||
if (!nodep) { // verilog.y and lots of other places assume this
|
||||
if (!nodep) // verilog.y and lots of other places assume this
|
||||
return newp;
|
||||
} else {
|
||||
// Find end of old list
|
||||
AstNode* oldtailp = nodep;
|
||||
if (oldtailp->m_nextp) {
|
||||
if (oldtailp->m_headtailp) {
|
||||
oldtailp = oldtailp->m_headtailp; // This=beginning of list, jump to end
|
||||
UDEBUGONLY(UASSERT_OBJ(!oldtailp->m_nextp, nodep,
|
||||
"Node had next, but headtail says it shouldn't"););
|
||||
} else {
|
||||
// Though inefficient, we are occasionally passed an
|
||||
// addNext in the middle of a list.
|
||||
while (oldtailp->m_nextp) oldtailp = oldtailp->m_nextp;
|
||||
}
|
||||
|
||||
// Find end of old list
|
||||
AstNode* oldtailp = nodep;
|
||||
if (oldtailp->m_nextp) {
|
||||
if (oldtailp->m_headtailp) {
|
||||
oldtailp = oldtailp->m_headtailp; // This=beginning of list, jump to end
|
||||
UDEBUGONLY(UASSERT_OBJ(!oldtailp->m_nextp, nodep,
|
||||
"Node had next, but headtail says it shouldn't"););
|
||||
} else {
|
||||
// Though inefficient, we are occasionally passed an
|
||||
// addNext in the middle of a list.
|
||||
while (oldtailp->m_nextp) oldtailp = oldtailp->m_nextp;
|
||||
}
|
||||
// Link it in
|
||||
oldtailp->m_nextp = newp;
|
||||
newp->m_backp = oldtailp;
|
||||
// New tail needs the head
|
||||
AstNode* const newtailp = newp->m_headtailp;
|
||||
AstNode* const headp = oldtailp->m_headtailp;
|
||||
oldtailp->m_headtailp = nullptr; // May be written again as new head
|
||||
newp->m_headtailp = nullptr; // May be written again as new tail
|
||||
newtailp->m_headtailp = headp;
|
||||
headp->m_headtailp = newtailp;
|
||||
newp->editCountInc();
|
||||
// No change of m_iterpp, as only changing m_nextp of current node;
|
||||
// the current node is still the one at the iteration point
|
||||
}
|
||||
// Link it in
|
||||
oldtailp->m_nextp = newp;
|
||||
newp->m_backp = oldtailp;
|
||||
// New tail needs the head
|
||||
AstNode* const newtailp = newp->m_headtailp;
|
||||
AstNode* const headp = oldtailp->m_headtailp;
|
||||
oldtailp->m_headtailp = nullptr; // May be written again as new head
|
||||
newp->m_headtailp = nullptr; // May be written again as new tail
|
||||
newtailp->m_headtailp = headp;
|
||||
headp->m_headtailp = newtailp;
|
||||
newp->editCountInc();
|
||||
// No change of m_iterpp, as only changing m_nextp of current node;
|
||||
// the current node is still the one at the iteration point
|
||||
|
||||
debugTreeChange(nodep, "-addNextOut:", __LINE__, true);
|
||||
return nodep;
|
||||
}
|
||||
|
|
@ -992,6 +989,54 @@ void AstNode::operator delete(void* objp, size_t size) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef VL_ALLOC_RANDOM_CHECKS
|
||||
void* AstNode::operator new(size_t size) { // VL_MT_SAFE
|
||||
// Compute the maximum node size once and cache it
|
||||
static const size_t ASTGEN_MAX_NODE_SIZE = []() {
|
||||
size_t maxSize = 0;
|
||||
for (size_t t = 0; t < VNType::NUM_TYPES(); ++t) {
|
||||
maxSize = std::max(maxSize, VNType::typeInfo(static_cast<VNType::en>(t)).m_sizeof);
|
||||
}
|
||||
return maxSize;
|
||||
}();
|
||||
|
||||
// Make the following small to debug this routine, larger for performance and better random
|
||||
constexpr size_t POOL_SIZE = 65536; // Ideally large enough to fit all nodes used in tests
|
||||
// Randomly select from a large pool (POOL_SIZE) of max-node sized (MAX_NODE_SIZE) pointers
|
||||
static uint64_t s_lfsr = 0; // LFSR, 0 = didn't initialize yet
|
||||
if (int seed = v3Global.opt.debugAllocRandom()) {
|
||||
static V3Mutex s_mutex;
|
||||
const V3LockGuard lock{s_mutex};
|
||||
static std::array<void*, POOL_SIZE> s_nodePool VL_GUARDED_BY(s_mutex);
|
||||
constexpr uint64_t POLYNOMIAL = 0x80000000000019e2ULL;
|
||||
UASSERT_STATIC(size <= ASTGEN_MAX_NODE_SIZE, "fix ASTGEN_MAX_NODE_SIZE");
|
||||
if (!s_lfsr) {
|
||||
s_lfsr = seed;
|
||||
if (!s_lfsr) s_lfsr = ~s_lfsr;
|
||||
for (size_t i = 0; i < POOL_SIZE; ++i) {
|
||||
s_nodePool[i] = ::operator new(ASTGEN_MAX_NODE_SIZE + 64);
|
||||
}
|
||||
// Sort, just to make it more obvious we are properly randomizing
|
||||
std::sort(std::begin(s_nodePool), std::end(s_nodePool));
|
||||
}
|
||||
// Xoroshiro128+ algorithm
|
||||
s_lfsr = (s_lfsr & 1ULL) ? ((s_lfsr >> 1ULL) ^ POLYNOMIAL) : (s_lfsr >> 1ULL);
|
||||
const size_t index = s_lfsr % POOL_SIZE;
|
||||
AstNode* const objp = static_cast<AstNode*>(s_nodePool[index]);
|
||||
s_nodePool[index] = ::operator new(ASTGEN_MAX_NODE_SIZE + 64); // For later new()
|
||||
return objp;
|
||||
} else {
|
||||
AstNode* const objp = static_cast<AstNode*>(::operator new(size));
|
||||
return objp;
|
||||
}
|
||||
}
|
||||
void AstNode::operator delete(void* objp, size_t size) {
|
||||
// Leak due to size difference between true node and MAX_NODE_SIZE
|
||||
(void)objp;
|
||||
(void)size;
|
||||
}
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// Iterators
|
||||
|
||||
|
|
@ -1201,10 +1246,10 @@ void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE {
|
|||
break;
|
||||
case VNTypeInfo::OP_USED:
|
||||
UASSERT_OBJ(nodep, this,
|
||||
typeInfo.m_namep << " must have non nullptr " << opName << "()");
|
||||
typeInfo.m_namep << " must have non-nullptr " << opName << "()");
|
||||
UASSERT_OBJ(!nodep->nextp(), this,
|
||||
typeInfo.m_namep << "::" << opName
|
||||
<< "() cannot have a non nullptr nextp()");
|
||||
<< "() cannot have a non-nullptr nextp()");
|
||||
nodep->checkTreeIter(this);
|
||||
break;
|
||||
case VNTypeInfo::OP_LIST:
|
||||
|
|
@ -1546,11 +1591,12 @@ void AstNode::dtypeChgWidthSigned(int width, int widthMin, VSigning numeric) {
|
|||
// Enums need to become direct sizes to avoid later ENUMVALUE errors
|
||||
&& !VN_IS(dtypep()->skipRefToEnump(), EnumDType))
|
||||
return; // Correct already
|
||||
// FUTURE: We may be pointing at a two state data type, and this may
|
||||
// convert it to logic. Since the AstVar remains correct, we
|
||||
// work OK but this assumption may break in the future.
|
||||
// Note we can't just clone and do a widthForce, as if it's a BasicDType
|
||||
// the msb() indications etc will be incorrect.
|
||||
if (AstBasicDType* const basicp = VN_CAST(dtypep(), BasicDType)) {
|
||||
if (basicp->keyword() == VBasicDTypeKwd::BIT) {
|
||||
dtypeSetBitUnsized(width, widthMin, numeric);
|
||||
return;
|
||||
}
|
||||
}
|
||||
dtypeSetLogicUnsized(width, widthMin, numeric);
|
||||
}
|
||||
}
|
||||
|
|
@ -1602,7 +1648,8 @@ static const AstNodeDType* computeCastableBase(const AstNodeDType* nodep) {
|
|||
if (const AstPackArrayDType* const packp = VN_CAST(nodep, PackArrayDType)) {
|
||||
nodep = packp->subDTypep();
|
||||
continue;
|
||||
} else if (const AstNodeDType* const refp = nodep->skipRefToEnump()) {
|
||||
}
|
||||
if (const AstNodeDType* const refp = nodep->skipRefToEnump()) {
|
||||
if (refp != nodep) {
|
||||
nodep = refp;
|
||||
continue;
|
||||
|
|
@ -1630,9 +1677,9 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp
|
|||
const bool toNumericable
|
||||
= VN_IS(toBaseDtp, BasicDType) || VN_IS(toBaseDtp, NodeUOrStructDType);
|
||||
|
||||
if (toBaseDtp == fromBaseDtp) {
|
||||
return VCastable::COMPATIBLE;
|
||||
} else if (toNumericable) {
|
||||
if (toBaseDtp == fromBaseDtp) return VCastable::COMPATIBLE;
|
||||
|
||||
if (toNumericable) {
|
||||
if (fromNumericable) return VCastable::COMPATIBLE;
|
||||
} else if (VN_IS(toBaseDtp, EnumDType)) {
|
||||
if (VN_IS(fromBaseDtp, EnumDType) && toDtp->sameTree(fromDtp))
|
||||
|
|
@ -1648,13 +1695,9 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp
|
|||
const AstClass* const fromClassp = VN_AS(fromDtp, ClassRefDType)->classp();
|
||||
const bool downcast = AstClass::isClassExtendedFrom(toClassp, fromClassp);
|
||||
const bool upcast = AstClass::isClassExtendedFrom(fromClassp, toClassp);
|
||||
if (upcast) {
|
||||
return VCastable::COMPATIBLE;
|
||||
} else if (downcast) {
|
||||
return VCastable::DYNAMIC_CLASS;
|
||||
} else {
|
||||
return VCastable::INCOMPATIBLE;
|
||||
}
|
||||
if (upcast) return VCastable::COMPATIBLE;
|
||||
if (downcast) return VCastable::DYNAMIC_CLASS;
|
||||
return VCastable::INCOMPATIBLE;
|
||||
}
|
||||
return castable;
|
||||
}
|
||||
|
|
@ -1678,11 +1721,9 @@ AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) {
|
|||
if (VN_IS(node1p, Const)) std::swap(node1p, node2p);
|
||||
{
|
||||
const VCastable castable = computeCastable(node1p->dtypep(), node2p->dtypep(), node2p);
|
||||
if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) {
|
||||
if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE)
|
||||
return node1p->dtypep();
|
||||
} else if (castable == VCastable::DYNAMIC_CLASS) {
|
||||
return node2p->dtypep();
|
||||
}
|
||||
if (castable == VCastable::DYNAMIC_CLASS) return node2p->dtypep();
|
||||
}
|
||||
|
||||
AstClassRefDType* classDtypep1 = VN_CAST(node1p->dtypep(), ClassRefDType);
|
||||
|
|
|
|||
24
src/V3Ast.h
24
src/V3Ast.h
|
|
@ -95,7 +95,7 @@ class ExecMTask;
|
|||
|
||||
struct VNTypeInfo final {
|
||||
const char* m_namep;
|
||||
enum uint8_t {
|
||||
enum OpEn : uint8_t {
|
||||
OP_UNUSED,
|
||||
OP_USED,
|
||||
OP_LIST,
|
||||
|
|
@ -147,7 +147,7 @@ public:
|
|||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VNUser(int i) {
|
||||
m_u.up = 0;
|
||||
m_u.up = nullptr;
|
||||
m_u.ui = i;
|
||||
}
|
||||
explicit VNUser(void* p) { m_u.up = p; }
|
||||
|
|
@ -560,7 +560,7 @@ public:
|
|||
// Perform a function on every link in a node
|
||||
virtual void foreachLink(std::function<void(AstNode** linkpp, const char* namep)> f) = 0;
|
||||
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
#if defined(VL_LEAK_CHECKS) || defined(VL_ALLOC_RANDOM_CHECKS)
|
||||
static void* operator new(size_t size);
|
||||
static void operator delete(void* obj, size_t size);
|
||||
#endif
|
||||
|
|
@ -766,7 +766,7 @@ public:
|
|||
void dtypeSetVoid() { dtypep(findVoidDType()); }
|
||||
|
||||
// Data type locators
|
||||
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); }
|
||||
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::BIT); }
|
||||
AstNodeDType* findDoubleDType() const { return findBasicDType(VBasicDTypeKwd::DOUBLE); }
|
||||
AstNodeDType* findIntDType() const { return findBasicDType(VBasicDTypeKwd::INT); }
|
||||
AstNodeDType* findStringDType() const { return findBasicDType(VBasicDTypeKwd::STRING); }
|
||||
|
|
@ -932,7 +932,7 @@ public:
|
|||
virtual bool isSame(const AstNode* samep) const {
|
||||
return type() == samep->type() && sameNode(samep);
|
||||
}
|
||||
// Iff has a data type; dtype() must be non null
|
||||
// Iff has a data type; dtype() must be non-null
|
||||
virtual bool hasDType() const VL_MT_SAFE { return false; }
|
||||
// Iff has a non-null childDTypep(), as generic node function
|
||||
virtual AstNodeDType* getChildDTypep() const { return nullptr; }
|
||||
|
|
@ -1005,7 +1005,7 @@ public:
|
|||
// For use via the VN_IS macro only, or in templated code
|
||||
template <typename T_TargetType, typename T_Node>
|
||||
static bool is(const T_Node* nodep) VL_MT_SAFE {
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_IS called on non AstNode");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_IS called on non-AstNode");
|
||||
static_assert(!uselessCast<T_TargetType, T_Node>(),
|
||||
"Unnecessary VN_IS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T_TargetType, T_Node>(),
|
||||
|
|
@ -1016,7 +1016,7 @@ public:
|
|||
// For use via the VN_CAST macro only, or in templated code
|
||||
template <typename T_TargetType, typename T_Node>
|
||||
static T_TargetType* cast(T_Node* nodep) VL_MT_SAFE {
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_CAST called on non AstNode");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_CAST called on non-AstNode");
|
||||
static_assert(!uselessCast<T_TargetType, T_Node>(),
|
||||
"Unnecessary VN_CAST, node known to have target type.");
|
||||
static_assert(!impossibleCast<T_TargetType, T_Node>(),
|
||||
|
|
@ -1027,7 +1027,7 @@ public:
|
|||
}
|
||||
template <typename T_TargetType, typename T_Node>
|
||||
static const T_TargetType* cast(const T_Node* nodep) VL_MT_SAFE {
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_CAST called on non AstNode");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_CAST called on non-AstNode");
|
||||
static_assert(!uselessCast<T_TargetType, T_Node>(),
|
||||
"Unnecessary VN_CAST, node known to have target type.");
|
||||
static_assert(!impossibleCast<T_TargetType, T_Node>(),
|
||||
|
|
@ -1040,7 +1040,7 @@ public:
|
|||
// For use via the VN_AS macro only, or in templated code
|
||||
template <typename T_TargetType, typename T_Node>
|
||||
static T_TargetType* as(T_Node* nodep) VL_PURE {
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_AS called on non AstNode");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_AS called on non-AstNode");
|
||||
static_assert(!uselessCast<T_TargetType, T_Node>(),
|
||||
"Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T_TargetType, T_Node>(),
|
||||
|
|
@ -1052,7 +1052,7 @@ public:
|
|||
}
|
||||
template <typename T_TargetType, typename T_Node>
|
||||
static const T_TargetType* as(const T_Node* nodep) VL_PURE {
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_AS called on non AstNode");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_AS called on non-AstNode");
|
||||
static_assert(!uselessCast<T_TargetType, T_Node>(),
|
||||
"Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T_TargetType, T_Node>(),
|
||||
|
|
@ -1066,7 +1066,7 @@ public:
|
|||
// For use via privateAs or the VN_DBG_AS macro only
|
||||
template <typename T_TargetType, typename T_Node>
|
||||
static T_TargetType* unsafePrivateAs(T_Node* nodep) VL_PURE {
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_DBG_AS called on non AstNode");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_DBG_AS called on non-AstNode");
|
||||
static_assert(!uselessCast<T_TargetType, T_Node>(),
|
||||
"Unnecessary VN_DBG_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T_TargetType, T_Node>(),
|
||||
|
|
@ -1075,7 +1075,7 @@ public:
|
|||
}
|
||||
template <typename T_TargetType, typename T_Node>
|
||||
static const T_TargetType* unsafePrivateAs(const T_Node* nodep) VL_PURE {
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_DBG_AS called on non AstNode");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "VN_DBG_AS called on non-AstNode");
|
||||
static_assert(!uselessCast<T_TargetType, T_Node>(),
|
||||
"Unnecessary VN_DBG_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T_TargetType, T_Node>(),
|
||||
|
|
|
|||
122
src/V3AstAttr.h
122
src/V3AstAttr.h
|
|
@ -311,6 +311,9 @@ public:
|
|||
//
|
||||
VAR_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes
|
||||
VAR_FORCEABLE, // V3LinkParse moves to AstVar::isForceable
|
||||
VAR_FSM_ARC_INCLUDE_COND, // V3LinkParse moves to AstVar::attrFsmArcInclCond
|
||||
VAR_FSM_RESET_ARC, // V3LinkParse moves to AstVar::attrFsmResetArc
|
||||
VAR_FSM_STATE, // V3LinkParse moves to AstVar::attrFsmState
|
||||
VAR_PORT_DTYPE, // V3LinkDot for V3Width to check port dtype
|
||||
VAR_PUBLIC, // V3LinkParse moves to AstVar::sigPublic
|
||||
VAR_PUBLIC_FLAT, // V3LinkParse moves to AstVar::sigPublic
|
||||
|
|
@ -336,10 +339,10 @@ public:
|
|||
"ENUM_NEXT", "ENUM_PREV", "ENUM_NAME", "ENUM_VALID",
|
||||
"FUNC_ARG_PROTO", "FUNC_RETURN_PROTO",
|
||||
"TYPEID", "TYPENAME",
|
||||
"VAR_BASE", "VAR_FORCEABLE", "VAR_PORT_DTYPE", "VAR_PUBLIC",
|
||||
"VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD", "VAR_PUBLIC_FLAT_RW",
|
||||
"VAR_ISOLATE_ASSIGNMENTS", "VAR_SC_BIGUINT", "VAR_SC_BV", "VAR_SFORMAT",
|
||||
"VAR_SPLIT_VAR"
|
||||
"VAR_BASE", "VAR_FORCEABLE", "VAR_FSM_ARC_INCLUDE_COND", "VAR_FSM_RESET_ARC",
|
||||
"VAR_FSM_STATE", "VAR_PORT_DTYPE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT",
|
||||
"VAR_PUBLIC_FLAT_RD", "VAR_PUBLIC_FLAT_RW", "VAR_ISOLATE_ASSIGNMENTS",
|
||||
"VAR_SC_BIGUINT", "VAR_SC_BV", "VAR_SFORMAT", "VAR_SPLIT_VAR"
|
||||
};
|
||||
// clang-format on
|
||||
return names[m_e];
|
||||
|
|
@ -574,6 +577,7 @@ public:
|
|||
|| m_e == LONGINT || m_e == SHORTINT || m_e == UINT32 || m_e == UINT64
|
||||
|| m_e == TIME);
|
||||
}
|
||||
bool isBit() const { return m_e == BIT; }
|
||||
bool isBitLogic() const { // Bit/logic vector types; can form a packed array
|
||||
return (m_e == LOGIC || m_e == BIT);
|
||||
}
|
||||
|
|
@ -715,13 +719,9 @@ public:
|
|||
bool likely() const { return m_e == BP_LIKELY; }
|
||||
bool unlikely() const { return m_e == BP_UNLIKELY; }
|
||||
VBranchPred invert() const {
|
||||
if (m_e == BP_UNLIKELY) {
|
||||
return BP_LIKELY;
|
||||
} else if (m_e == BP_LIKELY) {
|
||||
return BP_UNLIKELY;
|
||||
} else {
|
||||
return m_e;
|
||||
}
|
||||
if (m_e == BP_UNLIKELY) return BP_LIKELY;
|
||||
if (m_e == BP_LIKELY) return BP_UNLIKELY;
|
||||
return m_e;
|
||||
}
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"", "VL_LIKELY", "VL_UNLIKELY"};
|
||||
|
|
@ -757,6 +757,7 @@ public:
|
|||
ARRAY_FIRST,
|
||||
ARRAY_INSIDE,
|
||||
ARRAY_LAST,
|
||||
ARRAY_MAP,
|
||||
ARRAY_MAX,
|
||||
ARRAY_MIN,
|
||||
ARRAY_NEXT,
|
||||
|
|
@ -801,6 +802,9 @@ public:
|
|||
DYN_RESIZE,
|
||||
DYN_SIZE,
|
||||
DYN_SLICE,
|
||||
DYN_SLICE_ASSIGN,
|
||||
DYN_SLICE_ASSIGN_BACK_BACK,
|
||||
DYN_SLICE_ASSIGN_FRONT_BACK,
|
||||
DYN_SLICE_BACK_BACK,
|
||||
DYN_SLICE_FRONT_BACK,
|
||||
EVENT_CLEAR_FIRED,
|
||||
|
|
@ -808,17 +812,28 @@ public:
|
|||
EVENT_FIRE,
|
||||
EVENT_IS_FIRED,
|
||||
EVENT_IS_TRIGGERED,
|
||||
FORCE_ADD,
|
||||
FORCE_READ,
|
||||
FORCE_READ_INDEX,
|
||||
FORCE_RELEASE,
|
||||
FORCE_TOUCH,
|
||||
FORK_DONE,
|
||||
FORK_INIT,
|
||||
FORK_JOIN,
|
||||
FORK_ON_KILL,
|
||||
RANDOMIZER_BASIC_STD_RANDOMIZATION,
|
||||
RANDOMIZER_CLEARCONSTRAINTS,
|
||||
RANDOMIZER_CLEARALL,
|
||||
RANDOMIZER_DISABLE_SOFT,
|
||||
RANDOMIZER_HARD,
|
||||
RANDOMIZER_SOFT,
|
||||
RANDOMIZER_UNIQUE,
|
||||
RANDOMIZER_MARK_RANDC,
|
||||
RANDOMIZER_SOLVE_BEFORE,
|
||||
RANDOMIZER_PIN_VAR,
|
||||
RANDOMIZER_WRITE_VAR,
|
||||
RANDOMIZER_SET_VAR_DISABLED,
|
||||
RANDOMIZER_CLEAR_VAR_DISABLED,
|
||||
RNG_GET_RANDSTATE,
|
||||
RNG_SET_RANDSTATE,
|
||||
SCHED_ANY_TRIGGERED,
|
||||
|
|
@ -893,6 +908,7 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
|
|||
{ARRAY_FIRST, "first", false}, \
|
||||
{ARRAY_INSIDE, "inside", true}, \
|
||||
{ARRAY_LAST, "last", false}, \
|
||||
{ARRAY_MAP, "map", true}, \
|
||||
{ARRAY_MAX, "max", true}, \
|
||||
{ARRAY_MIN, "min", true}, \
|
||||
{ARRAY_NEXT, "next", false}, \
|
||||
|
|
@ -937,6 +953,9 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
|
|||
{DYN_RESIZE, "resize", false}, \
|
||||
{DYN_SIZE, "size", true}, \
|
||||
{DYN_SLICE, "slice", true}, \
|
||||
{DYN_SLICE_ASSIGN, "sliceAssign", false}, \
|
||||
{DYN_SLICE_ASSIGN_BACK_BACK, "sliceAssignBackBack", false}, \
|
||||
{DYN_SLICE_ASSIGN_FRONT_BACK, "sliceAssignFrontBack", false}, \
|
||||
{DYN_SLICE_BACK_BACK, "sliceBackBack", true}, \
|
||||
{DYN_SLICE_FRONT_BACK, "sliceFrontBack", true}, \
|
||||
{EVENT_CLEAR_FIRED, "clearFired", false}, \
|
||||
|
|
@ -944,17 +963,28 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
|
|||
{EVENT_FIRE, "fire", false}, \
|
||||
{EVENT_IS_FIRED, "isFired", true}, \
|
||||
{EVENT_IS_TRIGGERED, "isTriggered", true}, \
|
||||
{FORCE_ADD, "addForce", false}, \
|
||||
{FORCE_READ, "read", true}, \
|
||||
{FORCE_READ_INDEX, "readIndex", true}, \
|
||||
{FORCE_RELEASE, "release", false}, \
|
||||
{FORCE_TOUCH, "touch", false}, \
|
||||
{FORK_DONE, "done", false}, \
|
||||
{FORK_INIT, "init", false}, \
|
||||
{FORK_JOIN, "join", false}, \
|
||||
{FORK_ON_KILL, "onKill", false}, \
|
||||
{RANDOMIZER_BASIC_STD_RANDOMIZATION, "basicStdRandomization", false}, \
|
||||
{RANDOMIZER_CLEARCONSTRAINTS, "clearConstraints", false}, \
|
||||
{RANDOMIZER_CLEARALL, "clearAll", false}, \
|
||||
{RANDOMIZER_DISABLE_SOFT, "disable_soft", false}, \
|
||||
{RANDOMIZER_HARD, "hard", false}, \
|
||||
{RANDOMIZER_SOFT, "soft", false}, \
|
||||
{RANDOMIZER_UNIQUE, "rand_unique", false}, \
|
||||
{RANDOMIZER_MARK_RANDC, "markRandc", false}, \
|
||||
{RANDOMIZER_SOLVE_BEFORE, "solveBefore", false}, \
|
||||
{RANDOMIZER_PIN_VAR, "pin_var", false}, \
|
||||
{RANDOMIZER_WRITE_VAR, "write_var", false}, \
|
||||
{RANDOMIZER_SET_VAR_DISABLED, "set_var_disabled", false}, \
|
||||
{RANDOMIZER_CLEAR_VAR_DISABLED, "clear_var_disabled", false}, \
|
||||
{RNG_GET_RANDSTATE, "__Vm_rng.get_randstate", true}, \
|
||||
{RNG_SET_RANDSTATE, "__Vm_rng.set_randstate", false}, \
|
||||
{SCHED_ANY_TRIGGERED, "anyTriggered", false}, \
|
||||
|
|
@ -980,6 +1010,48 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
|
|||
|
||||
// ######################################################################
|
||||
|
||||
class VCStmtType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
NONE, // Unknown or not applicable
|
||||
CTOR_VAR_RESET_CALL,
|
||||
_ENUM_MAX // Leave last
|
||||
};
|
||||
|
||||
private:
|
||||
struct Item final {
|
||||
enum en m_e; // Statement's enum mnemonic, for checking
|
||||
const char* m_name; // Statements name, for debugging
|
||||
};
|
||||
static Item s_itemData[];
|
||||
|
||||
public:
|
||||
enum en m_e;
|
||||
VCStmtType()
|
||||
: m_e{NONE} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VCStmtType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VCStmtType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const VL_PURE {
|
||||
static const char* const names[] = {"none", "ctor_var_reset_call"};
|
||||
return names[m_e];
|
||||
}
|
||||
bool isNone() const { return m_e == NONE; }
|
||||
};
|
||||
constexpr bool operator==(const VCStmtType& lhs, const VCStmtType& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
constexpr bool operator==(const VCStmtType& lhs, VCStmtType::en rhs) { return lhs.m_e == rhs; }
|
||||
constexpr bool operator==(VCStmtType::en lhs, const VCStmtType& rhs) { return lhs == rhs.m_e; }
|
||||
inline std::ostream& operator<<(std::ostream& os, const VCStmtType& rhs) {
|
||||
return os << rhs.ascii();
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VCaseType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
|
|
@ -1080,6 +1152,16 @@ public:
|
|||
bool isWritable() const VL_MT_SAFE { return m_e == OUTPUT || m_e == INOUT || m_e == REF; }
|
||||
bool isRef() const VL_MT_SAFE { return m_e == REF; }
|
||||
bool isConstRef() const VL_MT_SAFE { return m_e == CONSTREF; }
|
||||
string traceSigDirection() const {
|
||||
if (isInout()) {
|
||||
return "VerilatedTraceSigDirection::INOUT";
|
||||
} else if (isWritable()) {
|
||||
return "VerilatedTraceSigDirection::OUTPUT";
|
||||
} else if (isNonOutput()) {
|
||||
return "VerilatedTraceSigDirection::INPUT";
|
||||
}
|
||||
return "VerilatedTraceSigDirection::NONE";
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VDirection& lhs, const VDirection& rhs) VL_MT_SAFE {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
|
|
@ -1375,7 +1457,7 @@ public:
|
|||
bool isAutomatic() const { return m_e == AUTOMATIC_EXPLICIT || m_e == AUTOMATIC_IMPLICIT; }
|
||||
bool isStatic() const { return m_e == STATIC_EXPLICIT || m_e == STATIC_IMPLICIT; }
|
||||
bool isStaticExplicit() const { return m_e == STATIC_EXPLICIT; }
|
||||
VLifetime makeImplicit() {
|
||||
VLifetime makeImplicit() const {
|
||||
switch (m_e) {
|
||||
case AUTOMATIC_EXPLICIT: return AUTOMATIC_IMPLICIT;
|
||||
case STATIC_EXPLICIT: return STATIC_IMPLICIT;
|
||||
|
|
@ -1423,11 +1505,7 @@ public:
|
|||
~VNumRange() = default;
|
||||
// MEMBERS
|
||||
void init(int hi, int lo, bool ascending) {
|
||||
if (lo > hi) {
|
||||
const int t = hi;
|
||||
hi = lo;
|
||||
lo = t;
|
||||
}
|
||||
if (lo > hi) std::swap(hi, lo);
|
||||
m_left = ascending ? lo : hi;
|
||||
m_right = ascending ? hi : lo;
|
||||
m_ranged = true;
|
||||
|
|
@ -1743,6 +1821,10 @@ public:
|
|||
static const char* const names[] = {"CONSTANT", "FULL", "CHANGE"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* func_prefix() const {
|
||||
static const char* const names[] = {"trace_const", "trace_full", "trace_chg"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VTraceType& lhs, const VTraceType& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
|
|
@ -1771,7 +1853,7 @@ public:
|
|||
explicit VUseType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
bool containsAny(VUseType other) { return m_e & other.m_e; }
|
||||
bool containsAny(VUseType other) const { return m_e & other.m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"INT_FWD", "INT_INC", "INT_FWD_INC"};
|
||||
return names[m_e - 1];
|
||||
|
|
@ -1781,10 +1863,10 @@ constexpr bool operator==(const VUseType& lhs, const VUseType& rhs) { return lhs
|
|||
constexpr bool operator==(const VUseType& lhs, VUseType::en rhs) { return lhs.m_e == rhs; }
|
||||
constexpr bool operator==(VUseType::en lhs, const VUseType& rhs) { return lhs == rhs.m_e; }
|
||||
constexpr VUseType::en operator|(VUseType::en lhs, VUseType::en rhs) {
|
||||
return VUseType::en((uint8_t)lhs | (uint8_t)rhs);
|
||||
return VUseType::en(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
||||
}
|
||||
constexpr VUseType::en operator&(VUseType::en lhs, VUseType::en rhs) {
|
||||
return VUseType::en((uint8_t)lhs & (uint8_t)rhs);
|
||||
return VUseType::en(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
|
||||
}
|
||||
inline std::ostream& operator<<(std::ostream& os, const VUseType& rhs) {
|
||||
return os << rhs.ascii();
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ AstAlways::AstAlways(AstAssignW* assignp)
|
|||
|
||||
AstElabDisplay::AstElabDisplay(FileLine* fl, VDisplayType dispType, AstNodeExpr* exprsp)
|
||||
: ASTGEN_SUPER_ElabDisplay(fl) {
|
||||
addFmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp});
|
||||
addFmtp(new AstSFormatF{fl, "", true, exprsp});
|
||||
m_displayType = dispType;
|
||||
}
|
||||
|
||||
|
|
@ -183,23 +183,17 @@ AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
|
|||
string AstVarRef::name() const { return varp() ? varp()->name() : nameThis(); }
|
||||
|
||||
bool AstVarRef::sameNode(const AstVarRef* samep) const {
|
||||
if (varScopep()) {
|
||||
return (varScopep() == samep->varScopep() && access() == samep->access());
|
||||
} else {
|
||||
return (selfPointer() == samep->selfPointer()
|
||||
&& classOrPackagep() == samep->classOrPackagep() && access() == samep->access()
|
||||
&& (varp() && samep->varp() && varp()->sameNode(samep->varp())));
|
||||
}
|
||||
if (varScopep()) return (varScopep() == samep->varScopep() && access() == samep->access());
|
||||
return (selfPointer() == samep->selfPointer() && classOrPackagep() == samep->classOrPackagep()
|
||||
&& access() == samep->access()
|
||||
&& (varp() && samep->varp() && varp()->sameNode(samep->varp())));
|
||||
}
|
||||
bool AstVarRef::sameNoLvalue(const AstVarRef* samep) const {
|
||||
if (varScopep()) {
|
||||
return (varScopep() == samep->varScopep());
|
||||
} else {
|
||||
return (selfPointer() == samep->selfPointer()
|
||||
&& classOrPackagep() == samep->classOrPackagep()
|
||||
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
|
||||
&& varp()->sameNode(samep->varp()));
|
||||
}
|
||||
if (varScopep()) return (varScopep() == samep->varScopep());
|
||||
|
||||
return (selfPointer() == samep->selfPointer() && classOrPackagep() == samep->classOrPackagep()
|
||||
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
|
||||
&& varp()->sameNode(samep->varp()));
|
||||
}
|
||||
|
||||
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access)
|
||||
|
|
@ -211,4 +205,13 @@ AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const V
|
|||
|
||||
AstStmtExpr* AstNodeExpr::makeStmt() { return new AstStmtExpr{fileline(), this}; }
|
||||
|
||||
// Walk up the AST via backp() to find the containing AstNodeModule.
|
||||
// Returns nullptr if not found.
|
||||
inline AstNodeModule* findParentModule(AstNode* nodep) {
|
||||
for (AstNode* curp = nodep; curp; curp = curp->backp()) {
|
||||
if (AstNodeModule* const modp = VN_CAST(curp, NodeModule)) return modp;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -125,10 +125,13 @@ public:
|
|||
virtual AstNodeDType* subDTypep() const VL_MT_STABLE { return nullptr; }
|
||||
virtual AstNodeDType* subDType2p() const VL_MT_STABLE { return nullptr; }
|
||||
virtual bool isAggregateType() const { return false; }
|
||||
// True for unpacked, dynamic, queue, and associative arrays (not packed arrays)
|
||||
bool isNonPackedArray() const;
|
||||
virtual bool isFourstate() const;
|
||||
// Ideally an IEEE $typename
|
||||
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
|
||||
string prettyDTypeNameQ() const { return "'" + prettyDTypeName(false) + "'"; }
|
||||
string stateDTypeName() const { return this->isFourstate() ? "(4-state)" : "(2-state)"; }
|
||||
//
|
||||
// Changing the width may confuse the data type resolution, so must clear
|
||||
// TypeTable cache after use.
|
||||
|
|
@ -250,8 +253,7 @@ protected:
|
|||
, m_name{other.m_name}
|
||||
, m_uniqueNum{uniqueNumInc()}
|
||||
, m_packed{other.m_packed}
|
||||
, m_isFourstate{other.m_isFourstate}
|
||||
, m_constrainedRand{false} {}
|
||||
, m_isFourstate{other.m_isFourstate} {}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstNodeUOrStructDType;
|
||||
|
|
@ -458,6 +460,7 @@ public:
|
|||
return m.m_keyword;
|
||||
}
|
||||
bool isBitLogic() const { return keyword().isBitLogic(); }
|
||||
bool isBit() const { return keyword().isBit(); }
|
||||
bool isCHandle() const VL_MT_STABLE { return keyword().isCHandle(); }
|
||||
bool isDouble() const VL_MT_STABLE { return keyword().isDouble(); }
|
||||
bool isEvent() const VL_MT_STABLE { return keyword() == VBasicDTypeKwd::EVENT; }
|
||||
|
|
@ -540,7 +543,6 @@ public:
|
|||
dtypep(this);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstCDType;
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstCDType* const asamep = VN_DBG_AS(samep, CDType);
|
||||
|
|
@ -555,16 +557,12 @@ public:
|
|||
int widthTotalBytes() const override { return 8; } // Assume
|
||||
bool isCompound() const override { return true; }
|
||||
static string typeToHold(int width) {
|
||||
if (width <= 8)
|
||||
return "CData";
|
||||
else if (width <= 16)
|
||||
return "SData";
|
||||
else if (width <= VL_IDATASIZE)
|
||||
return "IData";
|
||||
else if (width <= VL_QUADSIZE)
|
||||
return "QData";
|
||||
else
|
||||
return "VlWide<" + std::to_string(VL_WORDS_I(width)) + ">";
|
||||
if (width <= 8) return "CData";
|
||||
if (width <= 16) return "SData";
|
||||
if (width <= VL_IDATASIZE) return "IData";
|
||||
if (width <= VL_QUADSIZE) return "QData";
|
||||
|
||||
return "VlWide<" + std::to_string(VL_WORDS_I(width)) + ">";
|
||||
}
|
||||
};
|
||||
class AstClassRefDType final : public AstNodeDType {
|
||||
|
|
@ -586,11 +584,7 @@ public:
|
|||
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
|
||||
return (m_classp == asamep->m_classp && m_classOrPackagep == asamep->m_classOrPackagep);
|
||||
}
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override {
|
||||
// Doesn't need to compare m_classOrPackagep
|
||||
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
|
||||
return m_classp == asamep->m_classp;
|
||||
}
|
||||
bool similarDTypeNode(const AstNodeDType* samep) const override;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
|
|
@ -966,8 +960,7 @@ public:
|
|||
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
|
||||
AstNode* valuep)
|
||||
: ASTGEN_SUPER_MemberDType(fl)
|
||||
, m_name{name}
|
||||
, m_constrainedRand{false} {
|
||||
, m_name{name} {
|
||||
childDTypep(dtp); // Only for parser
|
||||
this->valuep(valuep);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
|
|
@ -975,8 +968,7 @@ public:
|
|||
}
|
||||
AstMemberDType(FileLine* fl, const string& name, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_MemberDType(fl)
|
||||
, m_name{name}
|
||||
, m_constrainedRand{false} {
|
||||
, m_name{name} {
|
||||
UASSERT(dtp, "AstMember created with no dtype");
|
||||
refDTypep(dtp);
|
||||
dtypep(this);
|
||||
|
|
@ -1251,10 +1243,9 @@ public:
|
|||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
// Used for recursive definition checking
|
||||
if (AstNodeDType* const dtp = VN_CAST(lhsp(), NodeDType))
|
||||
return dtp;
|
||||
else
|
||||
return nullptr;
|
||||
if (AstNodeDType* const dtp = VN_CAST(lhsp(), NodeDType)) return dtp;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
int widthAlignBytes() const override { V3ERROR_NA_RETURN(1); }
|
||||
int widthTotalBytes() const override { V3ERROR_NA_RETURN(1); }
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ public:
|
|||
// Someday we will generically support data types on every expr node
|
||||
// Until then isOpaque indicates we shouldn't constant optimize this node type
|
||||
bool isOpaque() const { return VN_IS(this, CvtPackString); }
|
||||
// True for SVA multi-cycle sequence nodes (SExpr, SConsRep, etc.)
|
||||
virtual bool isMultiCycleSva() const { return false; }
|
||||
bool isLValue() const;
|
||||
|
||||
// Wrap This expression into an AstStmtExpr to denote it occurs in statement position
|
||||
inline AstStmtExpr* makeStmt();
|
||||
|
|
@ -232,6 +235,7 @@ private:
|
|||
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
|
||||
string m_inlinedDots; // Dotted hierarchy flattened out
|
||||
bool m_pli = false; // Pli system call ($name)
|
||||
bool m_containsGenBlock = false; // Contains gen block reference
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
protected:
|
||||
|
|
@ -258,6 +262,8 @@ public:
|
|||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
bool pli() const { return m_pli; }
|
||||
void pli(bool flag) { m_pli = flag; }
|
||||
bool containsGenBlock() const { return m_containsGenBlock; }
|
||||
void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
|
||||
bool isPure() override;
|
||||
|
||||
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
|
||||
|
|
@ -561,7 +567,6 @@ public:
|
|||
dtypep(findCHandleDType());
|
||||
}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstAddrOfCFunc;
|
||||
void dump(std::ostream& str) const override;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
|
|
@ -601,7 +606,7 @@ class AstCExpr final : public AstNodeExpr {
|
|||
void init(const string& text, int setwidth) {
|
||||
if (!text.empty()) add(text);
|
||||
if (setwidth) {
|
||||
dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
|
||||
dtypeSetBitSized(setwidth, VSigning::UNSIGNED);
|
||||
} else {
|
||||
dtypeSetVoid(); // Caller to override if necessary
|
||||
}
|
||||
|
|
@ -620,6 +625,8 @@ public:
|
|||
init(text, setwidth);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCExpr;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
// METHODS
|
||||
bool cleanOut() const override { return true; }
|
||||
std::string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
|
|
@ -936,10 +943,7 @@ public:
|
|||
addMembersp(membersp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConsPackUOrStruct;
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(dtypep() && !VN_IS(dtypep(), NodeUOrStructDType));
|
||||
return nullptr;
|
||||
}
|
||||
const char* broken() const override { return nullptr; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
|
|
@ -1005,9 +1009,12 @@ class AstConst final : public AstNodeExpr {
|
|||
dtypeSetDouble();
|
||||
} else if (m_num.isString()) {
|
||||
dtypeSetString();
|
||||
} else {
|
||||
} else if (m_num.isAnyXZ()) {
|
||||
dtypeSetLogicUnsized(m_num.width(), (m_num.sized() ? 0 : m_num.widthToFit()),
|
||||
VSigning::fromBool(m_num.isSigned()));
|
||||
} else {
|
||||
dtypeSetBitUnsized(m_num.width(), (m_num.sized() ? 0 : m_num.widthToFit()),
|
||||
VSigning::fromBool(m_num.isSigned()));
|
||||
}
|
||||
m_num.nodep(this);
|
||||
}
|
||||
|
|
@ -1046,35 +1053,35 @@ public:
|
|||
AstConst(FileLine* fl, uint32_t num)
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num(this, 32, num) { // Need () constructor
|
||||
dtypeSetLogicUnsized(m_num.width(), 0, VSigning::UNSIGNED);
|
||||
dtypeSetBitUnsized(m_num.width(), 0, VSigning::UNSIGNED);
|
||||
}
|
||||
class Unsized32 {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, Unsized32, uint32_t num) // Unsized 32-bit integer of specified value
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num(this, 32, num) { // Need () constructor
|
||||
m_num.width(32, false);
|
||||
dtypeSetLogicUnsized(32, m_num.widthToFit(), VSigning::UNSIGNED);
|
||||
dtypeSetBitUnsized(32, m_num.widthToFit(), VSigning::UNSIGNED);
|
||||
}
|
||||
class Signed32 {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, Signed32, int32_t num) // Signed 32-bit integer of specified value
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num(this, 32, num) { // Need () constructor
|
||||
m_num.width(32, true);
|
||||
dtypeSetLogicUnsized(32, m_num.widthToFit(), VSigning::SIGNED);
|
||||
dtypeSetBitUnsized(32, m_num.widthToFit(), VSigning::SIGNED);
|
||||
}
|
||||
class Unsized64 {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, Unsized64, uint64_t num)
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num(this, 64, 0) { // Need () constructor
|
||||
m_num.setQuad(num);
|
||||
dtypeSetLogicSized(64, VSigning::UNSIGNED);
|
||||
dtypeSetBitSized(64, VSigning::UNSIGNED);
|
||||
}
|
||||
class SizedEData {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, SizedEData, uint64_t num)
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num(this, VL_EDATASIZE, 0) { // Need () constructor
|
||||
m_num.setQuad(num);
|
||||
dtypeSetLogicSized(VL_EDATASIZE, VSigning::UNSIGNED);
|
||||
dtypeSetBitSized(VL_EDATASIZE, VSigning::UNSIGNED);
|
||||
}
|
||||
class RealDouble {}; // for creator type-overload selection
|
||||
AstConst(FileLine* fl, RealDouble, double num)
|
||||
|
|
@ -1090,12 +1097,12 @@ public:
|
|||
dtypeSetString();
|
||||
}
|
||||
class BitFalse {};
|
||||
AstConst(FileLine* fl, BitFalse) // Shorthand const 0, dtype should be a logic of size 1
|
||||
AstConst(FileLine* fl, BitFalse) // Shorthand const 0, dtype should be a bit of size 1
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num(this, 1, 0) { // Need () constructor
|
||||
dtypeSetBit();
|
||||
}
|
||||
// Shorthand const 1 (or with argument 0/1), dtype should be a logic of size 1
|
||||
// Shorthand const 1 (or with argument 0/1), dtype should be a bit of size 1
|
||||
class BitTrue {};
|
||||
AstConst(FileLine* fl, BitTrue, bool on = true)
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
|
|
@ -1127,7 +1134,7 @@ public:
|
|||
AstConst(FileLine* fl, OneStep)
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num{this, V3Number::OneStep{}} {
|
||||
dtypeSetLogicSized(64, VSigning::UNSIGNED);
|
||||
dtypeSetBitSized(64, VSigning::UNSIGNED);
|
||||
initWithNumber();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConst;
|
||||
|
|
@ -1268,7 +1275,7 @@ class AstDist final : public AstNodeExpr {
|
|||
// @astgen op2 := itemsp : List[AstDistItem]
|
||||
public:
|
||||
AstDist(FileLine* fl, AstNodeExpr* exprp, AstDistItem* itemsp)
|
||||
: ASTGEN_SUPER_Inside(fl) {
|
||||
: ASTGEN_SUPER_Dist(fl) {
|
||||
this->exprp(exprp);
|
||||
addItemsp(itemsp);
|
||||
dtypeSetBit();
|
||||
|
|
@ -2045,17 +2052,11 @@ public:
|
|||
}
|
||||
string emitC() override {
|
||||
if (seedp()) {
|
||||
if (urandom()) {
|
||||
return "VL_URANDOM_SEEDED_%nq%lq(%li)";
|
||||
} else {
|
||||
return "VL_RANDOM_SEEDED_%nq%lq(%li)";
|
||||
}
|
||||
}
|
||||
if (isWide()) {
|
||||
return "VL_RANDOM_%nq(%nw, %P)";
|
||||
} else {
|
||||
return "VL_RANDOM_%nq()";
|
||||
if (urandom()) return "VL_URANDOM_SEEDED_%nq%lq(%li)";
|
||||
return "VL_RANDOM_SEEDED_%nq%lq(%li)";
|
||||
}
|
||||
if (isWide()) return "VL_RANDOM_%nq(%nw, %P)";
|
||||
return "VL_RANDOM_%nq()";
|
||||
}
|
||||
bool cleanOut() const override { return false; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
|
|
@ -2126,6 +2127,55 @@ public:
|
|||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
bool isSystemFunc() const override { return true; }
|
||||
};
|
||||
class AstSConsRep final : public AstNodeExpr {
|
||||
// Consecutive repetition [*N], [*N:M], [+], [*] (IEEE 1800-2023 16.9.2)
|
||||
// op1 := exprp -- the repeated expression
|
||||
// op2 := countp -- min repetition count (N); always a positive constant after V3Width
|
||||
// op3 := maxCountp -- max repetition count (M); nullptr when exact or unbounded
|
||||
//
|
||||
// Encoding:
|
||||
// [*N]: countp=N, maxCountp=nullptr, unbounded=false
|
||||
// [*N:M]: countp=N, maxCountp=M, unbounded=false
|
||||
// [+]: countp=1, maxCountp=nullptr, unbounded=true (= [*1:$])
|
||||
// [*]: countp=0, maxCountp=nullptr, unbounded=true (= [*0:$])
|
||||
//
|
||||
// Lowering:
|
||||
// Exact [*N] standalone: V3AssertPre saturating counter
|
||||
// All other forms and all SExpr-contained forms: V3AssertProp PExpr loop
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := countp : AstNodeExpr
|
||||
// @astgen op3 := maxCountp : Optional[AstNodeExpr]
|
||||
const bool m_unbounded = false; // True for [+] and [*] (upper bound is $)
|
||||
public:
|
||||
// Exact [*N]
|
||||
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
|
||||
: ASTGEN_SUPER_SConsRep(fl) {
|
||||
this->exprp(exprp);
|
||||
this->countp(countp);
|
||||
}
|
||||
// Range [*N:M] or unbounded [+]/[*]
|
||||
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp, AstNodeExpr* maxCountp,
|
||||
bool unbounded)
|
||||
: ASTGEN_SUPER_SConsRep(fl)
|
||||
, m_unbounded{unbounded} {
|
||||
this->exprp(exprp);
|
||||
this->countp(countp);
|
||||
this->maxCountp(maxCountp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSConsRep;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
int instrCount() const override { V3ERROR_NA_RETURN(0); }
|
||||
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_LINE
|
||||
return m_unbounded == VN_DBG_AS(samep, SConsRep)->m_unbounded; // LCOV_EXCL_LINE
|
||||
}
|
||||
bool unbounded() const { return m_unbounded; }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSExpr final : public AstNodeExpr {
|
||||
// Sequence expression
|
||||
// @astgen op1 := preExprp: Optional[AstNodeExpr]
|
||||
|
|
@ -2149,40 +2199,79 @@ public:
|
|||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSFormatArg final : public AstNodeExpr {
|
||||
// Information for formatting each argument to AstSFormat,
|
||||
// used to pass to (potentially) runtime decoding of format arguments
|
||||
// PARENT: SFormatF (or next list of expressions)
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
VFormatAttr m_formatAttr; // How to format expression
|
||||
|
||||
public:
|
||||
AstSFormatArg(FileLine* fl, VFormatAttr formatAttr, AstNodeExpr* exprp)
|
||||
: ASTGEN_SUPER_SFormatArg(fl)
|
||||
, m_formatAttr{formatAttr} {
|
||||
dtypeFrom(exprp);
|
||||
this->exprp(exprp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSFormatArg;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
int instrCount() const override { return 0; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
return formatAttr() == VN_DBG_AS(samep, SFormatArg)->formatAttr();
|
||||
}
|
||||
string verilogKwd() const override { return "$sformatarg"; }
|
||||
string emitVerilog() override { return "%l"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!VN_IS(backp(), SFormatF) && firstAbovep()); // In list under SFormatF
|
||||
return nullptr;
|
||||
}
|
||||
VFormatAttr formatAttr() const { return m_formatAttr; }
|
||||
void formatAttr(const VFormatAttr& formatAttr) { m_formatAttr = formatAttr; }
|
||||
static VFormatAttr formatAttrDefauled(const AstSFormatArg* nodep, const AstNodeDType* dtypep);
|
||||
};
|
||||
class AstSFormatF final : public AstNodeExpr {
|
||||
// Convert format to string, generally under an AstDisplay or AstSFormat
|
||||
// Also used as "real" function for /*verilator sformat*/ functions
|
||||
// exprsp() once past parsing may be AstSFormatArgs
|
||||
// @astgen op1 := exprsp : List[AstNodeExpr]
|
||||
// @astgen op2 := scopeNamep : Optional[AstScopeName]
|
||||
string m_text;
|
||||
const bool m_hidden; // Under display, etc
|
||||
bool m_hasFormat; // Has format code
|
||||
bool m_exprFormat
|
||||
= false; // Runtime Node* format, false = text() format code, false = possibly r
|
||||
bool m_optionalFormat
|
||||
= false; // With exprFormat, first argument is either format or format-implied
|
||||
const char m_missingArgChar; // Format code when argument without format, 'h'/'o'/'b'
|
||||
VTimescale m_timeunit; // Parent module time unit
|
||||
public:
|
||||
class NoFormat {};
|
||||
class ExprFormat {};
|
||||
AstSFormatF(FileLine* fl, const string& text, bool hidden, AstNodeExpr* exprsp,
|
||||
char missingArgChar = 'd')
|
||||
: ASTGEN_SUPER_SFormatF(fl)
|
||||
, m_text{text}
|
||||
, m_hidden{hidden}
|
||||
, m_hasFormat{true}
|
||||
, m_missingArgChar{missingArgChar} {
|
||||
dtypeSetString();
|
||||
addExprsp(exprsp);
|
||||
}
|
||||
AstSFormatF(FileLine* fl, NoFormat, AstNodeExpr* exprsp, char missingArgChar = 'd',
|
||||
AstSFormatF(FileLine* fl, ExprFormat, AstNodeExpr* exprsp, char missingArgChar = 'd',
|
||||
bool hidden = true)
|
||||
: ASTGEN_SUPER_SFormatF(fl)
|
||||
, m_text{""}
|
||||
, m_hidden{hidden}
|
||||
, m_hasFormat{false}
|
||||
, m_exprFormat{true}
|
||||
, m_missingArgChar{missingArgChar} {
|
||||
dtypeSetString();
|
||||
addExprsp(exprsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSFormatF;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
string name() const override VL_MT_STABLE { return m_text; }
|
||||
void name(const string& name) override { m_text = name; }
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
|
|
@ -2192,12 +2281,19 @@ public:
|
|||
string verilogKwd() const override { return "$sformatf"; }
|
||||
string text() const { return m_text; } // * = Text to display
|
||||
void text(const string& text) { m_text = text; }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(text() != "" && exprFormat()); // Expr format means no literal format
|
||||
return nullptr;
|
||||
}
|
||||
bool formatScopeTracking() const { // Track scopeNamep(); Ok if false positive
|
||||
return (name().find("%m") != string::npos || name().find("%M") != string::npos);
|
||||
return exprFormat() || name().find("%m") != string::npos
|
||||
|| name().find("%M") != string::npos;
|
||||
}
|
||||
bool hidden() const { return m_hidden; }
|
||||
bool hasFormat() const { return m_hasFormat; }
|
||||
void hasFormat(bool flag) { m_hasFormat = flag; }
|
||||
bool exprFormat() const { return m_exprFormat; }
|
||||
void exprFormat(bool flag) { m_exprFormat = flag; }
|
||||
bool optionalFormat() const { return m_optionalFormat; }
|
||||
void optionalFormat(bool flag) { m_optionalFormat = flag; }
|
||||
char missingArgChar() const { return m_missingArgChar; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
|
|
@ -2207,6 +2303,40 @@ public:
|
|||
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
|
||||
bool isSystemFunc() const override { return true; }
|
||||
};
|
||||
class AstSGotoRep final : public AstNodeExpr {
|
||||
// Goto repetition: expr [-> count]
|
||||
// IEEE 1800-2023 16.9.2
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := countp : AstNodeExpr
|
||||
public:
|
||||
explicit AstSGotoRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
|
||||
: ASTGEN_SUPER_SGotoRep(fl) {
|
||||
this->exprp(exprp);
|
||||
this->countp(countp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSGotoRep;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSNonConsRep final : public AstNodeExpr {
|
||||
// Nonconsecutive repetition: expr [= count]
|
||||
// IEEE 1800-2023 16.9.2
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := countp : AstNodeExpr
|
||||
public:
|
||||
explicit AstSNonConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
|
||||
: ASTGEN_SUPER_SNonConsRep(fl) {
|
||||
this->exprp(exprp);
|
||||
this->countp(countp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSNonConsRep;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSScanF final : public AstNodeExpr {
|
||||
// @astgen op1 := exprsp : List[AstNodeExpr] // VarRefs for results
|
||||
// @astgen op2 := fromp : AstNodeExpr
|
||||
|
|
@ -2609,6 +2739,33 @@ public:
|
|||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
|
||||
};
|
||||
class AstUntil final : public AstNodeExpr {
|
||||
// The until property expression
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
// @astgen op2 := rhsp : AstNodeExpr
|
||||
|
||||
const bool m_strong; // 's_' preffix
|
||||
const bool m_overlapping; // '_with` suffix
|
||||
public:
|
||||
AstUntil(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, bool strong, bool overlapping)
|
||||
: ASTGEN_SUPER_Until(fl)
|
||||
, m_strong{strong}
|
||||
, m_overlapping{overlapping} {
|
||||
this->lhsp(lhsp);
|
||||
this->rhsp(rhsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstUntil;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
bool isStrong() const { return m_strong; }
|
||||
bool isOverlapping() const { return m_overlapping; }
|
||||
};
|
||||
class AstValuePlusArgs final : public AstNodeExpr {
|
||||
// Search expression. If nullptr then this is a $test$plusargs instead of $value$plusargs.
|
||||
// @astgen op1 := searchp : Optional[AstNodeExpr]
|
||||
|
|
@ -2732,12 +2889,18 @@ public:
|
|||
};
|
||||
class AstConcat final : public AstNodeBiop {
|
||||
// If you're looking for {#{}}, see AstReplicate
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstConcat(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Concat(fl, lhsp, rhsp) {
|
||||
if (lhsp->dtypep() && rhsp->dtypep()) {
|
||||
dtypeSetLogicSized(lhsp->dtypep()->width() + rhsp->dtypep()->width(),
|
||||
VSigning::UNSIGNED);
|
||||
if (lhsp->dtypep()->isFourstate() || rhsp->dtypep()->isFourstate()) {
|
||||
dtypeSetLogicSized(lhsp->dtypep()->width() + rhsp->dtypep()->width(),
|
||||
VSigning::UNSIGNED);
|
||||
} else {
|
||||
dtypeSetBitSized(lhsp->dtypep()->width() + rhsp->dtypep()->width(),
|
||||
VSigning::UNSIGNED);
|
||||
}
|
||||
}
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConcat;
|
||||
|
|
@ -2776,6 +2939,7 @@ public:
|
|||
bool stringFlavor() const override { return true; }
|
||||
};
|
||||
class AstDiv final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstDiv(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Div(fl, lhsp, rhsp) {
|
||||
|
|
@ -2818,6 +2982,7 @@ public:
|
|||
bool doubleFlavor() const override { return true; }
|
||||
};
|
||||
class AstDivS final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstDivS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_DivS(fl, lhsp, rhsp) {
|
||||
|
|
@ -2841,6 +3006,7 @@ public:
|
|||
};
|
||||
class AstEqWild final : public AstNodeBiop {
|
||||
// Note wildcard operator rhs differs from lhs
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstEqWild(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_EqWild(fl, lhsp, rhsp) {
|
||||
|
|
@ -2947,6 +3113,7 @@ public:
|
|||
bool sizeMattersRhs() const override { return false; }
|
||||
};
|
||||
class AstGt final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstGt(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Gt(fl, lhsp, rhsp) {
|
||||
|
|
@ -3009,6 +3176,7 @@ public:
|
|||
bool stringFlavor() const override { return true; }
|
||||
};
|
||||
class AstGtS final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstGtS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_GtS(fl, lhsp, rhsp) {
|
||||
|
|
@ -3030,6 +3198,7 @@ public:
|
|||
bool signedFlavor() const override { return true; }
|
||||
};
|
||||
class AstGte final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstGte(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Gte(fl, lhsp, rhsp) {
|
||||
|
|
@ -3092,6 +3261,7 @@ public:
|
|||
bool stringFlavor() const override { return true; }
|
||||
};
|
||||
class AstGteS final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstGteS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_GteS(fl, lhsp, rhsp) {
|
||||
|
|
@ -3113,6 +3283,7 @@ public:
|
|||
bool signedFlavor() const override { return true; }
|
||||
};
|
||||
class AstLogAnd final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstLogAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_LogAnd(fl, lhsp, rhsp) {
|
||||
|
|
@ -3134,6 +3305,7 @@ public:
|
|||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
};
|
||||
class AstLogIf final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstLogIf(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_LogIf(fl, lhsp, rhsp) {
|
||||
|
|
@ -3155,6 +3327,7 @@ public:
|
|||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
};
|
||||
class AstLogOr final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstLogOr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_LogOr(fl, lhsp, rhsp) {
|
||||
|
|
@ -3176,6 +3349,7 @@ public:
|
|||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
};
|
||||
class AstLt final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstLt(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Lt(fl, lhsp, rhsp) {
|
||||
|
|
@ -3238,6 +3412,7 @@ public:
|
|||
bool stringFlavor() const override { return true; }
|
||||
};
|
||||
class AstLtS final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstLtS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_LtS(fl, lhsp, rhsp) {
|
||||
|
|
@ -3259,6 +3434,7 @@ public:
|
|||
bool signedFlavor() const override { return true; }
|
||||
};
|
||||
class AstLte final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstLte(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Lte(fl, lhsp, rhsp) {
|
||||
|
|
@ -3321,6 +3497,7 @@ public:
|
|||
bool stringFlavor() const override { return true; }
|
||||
};
|
||||
class AstLteS final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstLteS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_LteS(fl, lhsp, rhsp) {
|
||||
|
|
@ -3342,6 +3519,7 @@ public:
|
|||
bool signedFlavor() const override { return true; }
|
||||
};
|
||||
class AstModDiv final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstModDiv(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_ModDiv(fl, lhsp, rhsp) {
|
||||
|
|
@ -3363,6 +3541,7 @@ public:
|
|||
int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; }
|
||||
};
|
||||
class AstModDivS final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstModDivS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_ModDivS(fl, lhsp, rhsp) {
|
||||
|
|
@ -3385,6 +3564,7 @@ public:
|
|||
bool signedFlavor() const override { return true; }
|
||||
};
|
||||
class AstNeqWild final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstNeqWild(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_NeqWild(fl, lhsp, rhsp) {
|
||||
|
|
@ -3404,6 +3584,7 @@ public:
|
|||
bool sizeMattersRhs() const override { return false; }
|
||||
};
|
||||
class AstPow final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstPow(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Pow(fl, lhsp, rhsp) {
|
||||
|
|
@ -3444,6 +3625,7 @@ public:
|
|||
bool doubleFlavor() const override { return true; }
|
||||
};
|
||||
class AstPowSS final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstPowSS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_PowSS(fl, lhsp, rhsp) {
|
||||
|
|
@ -3465,6 +3647,7 @@ public:
|
|||
bool signedFlavor() const override { return true; }
|
||||
};
|
||||
class AstPowSU final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstPowSU(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_PowSU(fl, lhsp, rhsp) {
|
||||
|
|
@ -3486,6 +3669,7 @@ public:
|
|||
bool signedFlavor() const override { return true; }
|
||||
};
|
||||
class AstPowUS final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstPowUS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_PowUS(fl, lhsp, rhsp) {
|
||||
|
|
@ -3564,6 +3748,123 @@ public:
|
|||
int instrCount() const override { return widthInstrs() * 2; }
|
||||
bool stringFlavor() const override { return true; }
|
||||
};
|
||||
class AstSAnd final : public AstNodeBiop {
|
||||
// Sequence 'and' (IEEE 1800-2023 16.9.5): both operand sequences must match.
|
||||
// Operates on match sets, not values. For boolean operands, lowered to AstLogAnd.
|
||||
public:
|
||||
AstSAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_SAnd(fl, lhsp, rhsp) {
|
||||
dtypeSetBit();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSAnd;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opLogAnd(lhs, rhs);
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %fand %r)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSIntersect final : public AstNodeBiop {
|
||||
// Sequence 'intersect' (IEEE 1800-2023 16.9.6): both operands match with equal length.
|
||||
// AND with a length restriction. For boolean operands, lowered to AstLogAnd.
|
||||
public:
|
||||
AstSIntersect(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_SIntersect(fl, lhsp, rhsp) {
|
||||
dtypeSetBit();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSIntersect;
|
||||
// LCOV_EXCL_START // Lowered before these are ever called
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opLogAnd(lhs, rhs);
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %fintersect %r)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
// LCOV_EXCL_STOP
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSOr final : public AstNodeBiop {
|
||||
// Sequence 'or' (IEEE 1800-2023 16.9.7): at least one operand sequence must match.
|
||||
// Operates on match sets, not values. For boolean operands, lowered to AstLogOr.
|
||||
public:
|
||||
AstSOr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_SOr(fl, lhsp, rhsp) {
|
||||
dtypeSetBit();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSOr;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opLogOr(lhs, rhs);
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %for %r)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSThroughout final : public AstNodeBiop {
|
||||
// expr throughout seq (IEEE 1800-2023 16.9.9)
|
||||
public:
|
||||
AstSThroughout(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_SThroughout(fl, lhsp, rhsp) {
|
||||
dtypeSetBit();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSThroughout;
|
||||
// LCOV_EXCL_START // Lowered in V3AssertProp before these are called
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opLogAnd(lhs, rhs);
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %fthroughout %r)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
// LCOV_EXCL_STOP
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSWithin final : public AstNodeBiop {
|
||||
// seq1 within seq2 (IEEE 1800-2023 16.9.10)
|
||||
public:
|
||||
AstSWithin(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_SWithin(fl, lhsp, rhsp) {
|
||||
dtypeSetBit();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSWithin;
|
||||
// LCOV_EXCL_START // Lowered in V3AssertNfa before these are called
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opLogAnd(lhs, rhs);
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %fwithin %r)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
// LCOV_EXCL_STOP
|
||||
bool isMultiCycleSva() const override { return true; }
|
||||
};
|
||||
class AstSel final : public AstNodeBiop {
|
||||
// *Resolved* (tyep checked) multiple bit range extraction. Always const width
|
||||
// @astgen alias op1 := fromp
|
||||
|
|
@ -3621,6 +3922,7 @@ public:
|
|||
void declElWidth(int flag) { m_declElWidth = flag; }
|
||||
};
|
||||
class AstShiftL final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstShiftL(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
|
||||
: ASTGEN_SUPER_ShiftL(fl, lhsp, rhsp) {
|
||||
|
|
@ -3664,6 +3966,7 @@ public:
|
|||
bool sizeMattersRhs() const override { return false; }
|
||||
};
|
||||
class AstShiftR final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstShiftR(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
|
||||
: ASTGEN_SUPER_ShiftR(fl, lhsp, rhsp) {
|
||||
|
|
@ -3711,6 +4014,7 @@ public:
|
|||
class AstShiftRS final : public AstNodeBiop {
|
||||
// Shift right with sign extension, >>> operator
|
||||
// Output data type's width determines which bit is used for sign extension
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstShiftRS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
|
||||
: ASTGEN_SUPER_ShiftRS(fl, lhsp, rhsp) {
|
||||
|
|
@ -3757,6 +4061,7 @@ public:
|
|||
bool signedFlavor() const override { return true; }
|
||||
};
|
||||
class AstSub final : public AstNodeBiop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstSub(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Sub(fl, lhsp, rhsp) {
|
||||
|
|
@ -3822,6 +4127,7 @@ public:
|
|||
|
||||
// === AstNodeBiCom ===
|
||||
class AstEq final : public AstNodeBiCom {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstEq(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Eq(fl, lhsp, rhsp) {
|
||||
|
|
@ -3844,6 +4150,7 @@ public:
|
|||
bool sizeMattersRhs() const override { return false; }
|
||||
};
|
||||
class AstEqCase final : public AstNodeBiCom {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstEqCase(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_EqCase(fl, lhsp, rhsp) {
|
||||
|
|
@ -3924,6 +4231,7 @@ public:
|
|||
int instrCount() const override { return INSTR_COUNT_STR; }
|
||||
};
|
||||
class AstLogEq final : public AstNodeBiCom {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstLogEq(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_LogEq(fl, lhsp, rhsp) {
|
||||
|
|
@ -3945,6 +4253,7 @@ public:
|
|||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
};
|
||||
class AstNeq final : public AstNodeBiCom {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstNeq(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Neq(fl, lhsp, rhsp) {
|
||||
|
|
@ -3966,6 +4275,7 @@ public:
|
|||
bool sizeMattersRhs() const override { return false; }
|
||||
};
|
||||
class AstNeqCase final : public AstNodeBiCom {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstNeqCase(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_NeqCase(fl, lhsp, rhsp) {
|
||||
|
|
@ -4048,6 +4358,7 @@ public:
|
|||
|
||||
// === AstNodeBiComAsv ===
|
||||
class AstAdd final : public AstNodeBiComAsv {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstAdd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Add(fl, lhsp, rhsp) {
|
||||
|
|
@ -4089,6 +4400,7 @@ public:
|
|||
bool doubleFlavor() const override { return true; }
|
||||
};
|
||||
class AstAnd final : public AstNodeBiComAsv {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_And(fl, lhsp, rhsp) {
|
||||
|
|
@ -4110,6 +4422,7 @@ public:
|
|||
const char* widthMismatch() const override VL_MT_STABLE;
|
||||
};
|
||||
class AstMul final : public AstNodeBiComAsv {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstMul(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Mul(fl, lhsp, rhsp) {
|
||||
|
|
@ -4152,6 +4465,7 @@ public:
|
|||
bool doubleFlavor() const override { return true; }
|
||||
};
|
||||
class AstMulS final : public AstNodeBiComAsv {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstMulS(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_MulS(fl, lhsp, rhsp) {
|
||||
|
|
@ -4175,6 +4489,7 @@ public:
|
|||
bool signedFlavor() const override { return true; }
|
||||
};
|
||||
class AstOr final : public AstNodeBiComAsv {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstOr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Or(fl, lhsp, rhsp) {
|
||||
|
|
@ -4196,6 +4511,7 @@ public:
|
|||
const char* widthMismatch() const override VL_MT_STABLE;
|
||||
};
|
||||
class AstXor final : public AstNodeBiComAsv {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstXor(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_Xor(fl, lhsp, rhsp) {
|
||||
|
|
@ -4253,6 +4569,7 @@ public:
|
|||
|
||||
// === AstNodeSel ===
|
||||
class AstArraySel final : public AstNodeSel {
|
||||
// @astgen makeDfgVertex
|
||||
void init(const AstNode* fromp) {
|
||||
if (fromp && VN_IS(fromp->dtypep()->skipRefp(), NodeArrayDType)) {
|
||||
// Strip off array to find what array references
|
||||
|
|
@ -4365,6 +4682,7 @@ public:
|
|||
// === AstNodeStream ===
|
||||
class AstStreamL final : public AstNodeStream {
|
||||
// Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstStreamL(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_StreamL(fl, lhsp, rhsp) {}
|
||||
|
|
@ -4383,6 +4701,7 @@ public:
|
|||
};
|
||||
class AstStreamR final : public AstNodeStream {
|
||||
// Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstStreamR(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_StreamR(fl, lhsp, rhsp) {}
|
||||
|
|
@ -4442,6 +4761,7 @@ public:
|
|||
string selfPointerProtect(bool useSelfForThis) const {
|
||||
return selfPointer().protect(useSelfForThis, protect());
|
||||
}
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
};
|
||||
class AstCMethodCall final : public AstNodeCCall {
|
||||
// C++ method call
|
||||
|
|
@ -4670,6 +4990,7 @@ class AstCond final : public AstNodeTriop {
|
|||
// @astgen alias op1 := condp
|
||||
// @astgen alias op2 := thenp
|
||||
// @astgen alias op3 := elsep
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstCond(FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep);
|
||||
ASTGEN_MEMBERS_AstCond;
|
||||
|
|
@ -4960,7 +5281,7 @@ public:
|
|||
m_size = setwidth;
|
||||
if (setwidth) {
|
||||
if (minwidth == -1) minwidth = setwidth;
|
||||
dtypeSetLogicUnsized(setwidth, minwidth, VSigning::UNSIGNED);
|
||||
dtypeSetBitUnsized(setwidth, minwidth, VSigning::UNSIGNED);
|
||||
}
|
||||
}
|
||||
// cppcheck-suppress constParameterPointer
|
||||
|
|
@ -5018,6 +5339,7 @@ public:
|
|||
};
|
||||
class AstCountOnes final : public AstNodeUniop {
|
||||
// Number of bits set in vector
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstCountOnes(FileLine* fl, AstNodeExpr* lhsp)
|
||||
: ASTGEN_SUPER_CountOnes(fl, lhsp) {}
|
||||
|
|
@ -5049,6 +5371,7 @@ public:
|
|||
};
|
||||
class AstExtend final : public AstNodeUniop {
|
||||
// Expand a value into a wider entity by 0 extension. Width is implied from nodep->width()
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstExtend(FileLine* fl, AstNodeExpr* lhsp)
|
||||
: ASTGEN_SUPER_Extend(fl, lhsp) {}
|
||||
|
|
@ -5072,6 +5395,7 @@ public:
|
|||
};
|
||||
class AstExtendS final : public AstNodeUniop {
|
||||
// Expand a value into a wider entity by sign extension. Width is implied from nodep->width()
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstExtendS(FileLine* fl, AstNodeExpr* lhsp)
|
||||
: ASTGEN_SUPER_ExtendS(fl, lhsp) {}
|
||||
|
|
@ -5218,6 +5542,7 @@ public:
|
|||
bool sizeMattersLhs() const override { return false; }
|
||||
};
|
||||
class AstLogNot final : public AstNodeUniop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstLogNot(FileLine* fl, AstNodeExpr* lhsp)
|
||||
: ASTGEN_SUPER_LogNot(fl, lhsp) {
|
||||
|
|
@ -5249,6 +5574,7 @@ public:
|
|||
bool sizeMattersLhs() const override { return false; }
|
||||
};
|
||||
class AstNegate final : public AstNodeUniop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstNegate(FileLine* fl, AstNodeExpr* lhsp)
|
||||
: ASTGEN_SUPER_Negate(fl, lhsp) {
|
||||
|
|
@ -5282,6 +5608,7 @@ public:
|
|||
bool doubleFlavor() const override { return true; }
|
||||
};
|
||||
class AstNot final : public AstNodeUniop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstNot(FileLine* fl, AstNodeExpr* lhsp)
|
||||
: ASTGEN_SUPER_Not(fl, lhsp) {
|
||||
|
|
@ -5403,6 +5730,7 @@ public:
|
|||
bool isSystemFunc() const override { return true; }
|
||||
};
|
||||
class AstRedAnd final : public AstNodeUniop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstRedAnd(FileLine* fl, AstNodeExpr* lhsp)
|
||||
: ASTGEN_SUPER_RedAnd(fl, lhsp) {
|
||||
|
|
@ -5417,6 +5745,7 @@ public:
|
|||
bool sizeMattersLhs() const override { return false; }
|
||||
};
|
||||
class AstRedOr final : public AstNodeUniop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstRedOr(FileLine* fl, AstNodeExpr* lhsp)
|
||||
: ASTGEN_SUPER_RedOr(fl, lhsp) {
|
||||
|
|
@ -5431,6 +5760,7 @@ public:
|
|||
bool sizeMattersLhs() const override { return false; }
|
||||
};
|
||||
class AstRedXor final : public AstNodeUniop {
|
||||
// @astgen makeDfgVertex
|
||||
public:
|
||||
AstRedXor(FileLine* fl, AstNodeExpr* lhsp)
|
||||
: ASTGEN_SUPER_RedXor(fl, lhsp) {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
|
|||
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
||||
string m_name; // Name of task
|
||||
string m_cname; // Name of task if DPI import
|
||||
string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8)
|
||||
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
|
||||
bool m_taskPublic : 1; // Public task
|
||||
bool m_attrIsolateAssign : 1; // User isolate_assignments attribute
|
||||
|
|
@ -179,6 +180,8 @@ public:
|
|||
void isExternProto(bool flag) { m_isExternProto = flag; }
|
||||
bool isExternDef() const { return m_isExternDef; }
|
||||
void isExternDef(bool flag) { m_isExternDef = flag; }
|
||||
const string& ifacePortName() const { return m_ifacePortName; }
|
||||
void ifacePortName(const string& name) { m_ifacePortName = name; }
|
||||
bool prototype() const { return m_prototype; }
|
||||
void prototype(bool flag) { m_prototype = flag; }
|
||||
bool dpiExport() const { return m_dpiExport; }
|
||||
|
|
@ -277,6 +280,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
|
|||
bool m_modPublic : 1; // Module has public references
|
||||
bool m_modTrace : 1; // Tracing this module
|
||||
bool m_inLibrary : 1; // From a library, no error if not used, never top level
|
||||
bool m_ctorVarReset : 1; // Ctor needs to call ctor_var_reset
|
||||
bool m_dead : 1; // LinkDot believes is dead; will remove in Dead visitors
|
||||
bool m_hasGParam : 1; // Has global parameter (for link)
|
||||
bool m_hasParameterList : 1; // Has #() for parameter declaration
|
||||
|
|
@ -285,6 +289,9 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
|
|||
bool m_internal : 1; // Internally created
|
||||
bool m_recursive : 1; // Recursive module
|
||||
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
|
||||
bool m_parameterizedTemplate : 1; // True when at least one specialized clone exists;
|
||||
// set by V3Param::deepCloneModule. Suppresses
|
||||
// width/type errors on the unresolved template.
|
||||
bool m_verilatorLib : 1; // Module is a stub for a Verilator produced --lib-create
|
||||
protected:
|
||||
AstNodeModule(VNType t, FileLine* fl, const string& name, const string& libname)
|
||||
|
|
@ -295,6 +302,7 @@ protected:
|
|||
, m_modPublic{false}
|
||||
, m_modTrace{false}
|
||||
, m_inLibrary{false}
|
||||
, m_ctorVarReset{false}
|
||||
, m_dead{false}
|
||||
, m_hasGParam{false}
|
||||
, m_hasParameterList{false}
|
||||
|
|
@ -303,6 +311,7 @@ protected:
|
|||
, m_internal{false}
|
||||
, m_recursive{false}
|
||||
, m_recursiveClone{false}
|
||||
, m_parameterizedTemplate{false}
|
||||
, m_verilatorLib{false} {}
|
||||
|
||||
public:
|
||||
|
|
@ -329,6 +338,8 @@ public:
|
|||
void modPublic(bool flag) { m_modPublic = flag; }
|
||||
bool modTrace() const { return m_modTrace; }
|
||||
void modTrace(bool flag) { m_modTrace = flag; }
|
||||
bool ctorVarReset() const { return m_ctorVarReset; }
|
||||
void ctorVarReset(bool flag) { m_ctorVarReset = flag; }
|
||||
bool dead() const { return m_dead; }
|
||||
void dead(bool flag) { m_dead = flag; }
|
||||
bool hasGParam() const { return m_hasGParam; }
|
||||
|
|
@ -345,6 +356,8 @@ public:
|
|||
void recursive(bool flag) { m_recursive = flag; }
|
||||
void recursiveClone(bool flag) { m_recursiveClone = flag; }
|
||||
bool recursiveClone() const { return m_recursiveClone; }
|
||||
bool parameterizedTemplate() const { return m_parameterizedTemplate; }
|
||||
void parameterizedTemplate(bool flag) { m_parameterizedTemplate = flag; }
|
||||
void verilatorLib(bool flag) { m_verilatorLib = flag; }
|
||||
bool verilatorLib() const { return m_verilatorLib; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
|
|
@ -643,7 +656,8 @@ class AstCell final : public AstNode {
|
|||
// A instantiation cell or interface call (don't know which until link)
|
||||
// @astgen op1 := pinsp : List[AstPin] // List of port assignments
|
||||
// @astgen op2 := paramsp : List[AstPin] // List of parameter assignments
|
||||
// @astgen op3 := rangep : Optional[AstRange] // Range for arrayed instances
|
||||
// @astgen op3 := rangep : List[AstRange] // Range(s) for arrayed instances; multi-dim chains
|
||||
// via nextp()
|
||||
// @astgen op4 := intfRefsp : List[AstIntfRef] // List of interface references, for tracing
|
||||
//
|
||||
// @astgen ptr := m_modp : Optional[AstNodeModule] // [AfterLink] Pointer to module instanced
|
||||
|
|
@ -667,7 +681,7 @@ public:
|
|||
, m_trace{true} {
|
||||
addPinsp(pinsp);
|
||||
addParamsp(paramsp);
|
||||
this->rangep(rangep);
|
||||
addRangep(rangep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCell;
|
||||
// No cloneRelink, we presume cloneee's want the same module linkages
|
||||
|
|
@ -1011,19 +1025,18 @@ class AstDefParam final : public AstNode {
|
|||
// A defparam assignment
|
||||
// Parents: MODULE
|
||||
// @astgen op1 := rhsp : AstNodeExpr
|
||||
// @astgen op2 := pathp : AstNodeExpr
|
||||
string m_name; // Name of variable getting set
|
||||
string m_path; // Dotted cellname to set parameter of
|
||||
public:
|
||||
AstDefParam(FileLine* fl, const string& path, const string& name, AstNodeExpr* rhsp)
|
||||
AstDefParam(FileLine* fl, AstNodeExpr* pathp, const string& name, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_DefParam(fl)
|
||||
, m_name{name}
|
||||
, m_path{path} {
|
||||
, m_name{name} {
|
||||
this->rhsp(rhsp);
|
||||
this->pathp(pathp);
|
||||
}
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
|
||||
ASTGEN_MEMBERS_AstDefParam;
|
||||
bool sameNode(const AstNode*) const override { return true; }
|
||||
string path() const { return m_path; }
|
||||
};
|
||||
class AstDefaultDisable final : public AstNode {
|
||||
// @astgen op1 := condp : AstNodeExpr
|
||||
|
|
@ -1377,6 +1390,7 @@ class AstPin final : public AstNode {
|
|||
// @astgen ptr := m_modPTypep : Optional[AstParamTypeDType] // Param type connects to on sub
|
||||
int m_pinNum; // Pin number
|
||||
string m_name; // Pin name, or "" for number based interconnect
|
||||
string m_paramPath; // Original defparam cell path, if this pin came from a defparam
|
||||
bool m_param = false; // Pin connects to parameter
|
||||
bool m_svDotName = false; // Pin is SystemVerilog .name'ed
|
||||
bool m_svImplicit = false; // Pin is SystemVerilog .name'ed, allow implicit
|
||||
|
|
@ -1403,6 +1417,8 @@ public:
|
|||
void modPTypep(AstParamTypeDType* nodep) { m_modPTypep = nodep; }
|
||||
bool param() const { return m_param; }
|
||||
void param(bool flag) { m_param = flag; }
|
||||
const string& paramPath() const { return m_paramPath; }
|
||||
void paramPath(const string& path) { m_paramPath = path; }
|
||||
bool svDotName() const { return m_svDotName; }
|
||||
void svDotName(bool flag) { m_svDotName = flag; }
|
||||
bool svImplicit() const { return m_svImplicit; }
|
||||
|
|
@ -1481,7 +1497,7 @@ public:
|
|||
bool sameNode(const AstNode* samep) const override {
|
||||
return direction() == VN_DBG_AS(samep, Pull)->direction();
|
||||
}
|
||||
uint32_t direction() const { return (uint32_t)m_direction; }
|
||||
uint32_t direction() const { return static_cast<uint32_t>(m_direction); }
|
||||
};
|
||||
class AstScope final : public AstNode {
|
||||
// A particular usage of a cell
|
||||
|
|
@ -1772,6 +1788,15 @@ public:
|
|||
addAttrsp(attrsp);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
AstTypedef(FileLine* fl, const string& name, AstNodeDType* dtp, bool underClass)
|
||||
: ASTGEN_SUPER_Typedef(fl)
|
||||
, m_name{name}
|
||||
, m_declTokenNum{fl->tokenNum()}
|
||||
, m_isHideLocal{false}
|
||||
, m_isHideProtected{false}
|
||||
, m_isUnderClass{underClass} {
|
||||
dtypep(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstTypedef;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
|
|
@ -1909,6 +1934,9 @@ class AstVar final : public AstNode {
|
|||
bool m_attrIsolateAssign : 1; // User isolate_assignments attribute
|
||||
bool m_attrSFormat : 1; // User sformat attribute
|
||||
bool m_attrSplitVar : 1; // declared with split_var metacomment
|
||||
bool m_attrFsmState : 1; // declared with fsm_state metacomment
|
||||
bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment
|
||||
bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment
|
||||
bool m_fileDescr : 1; // File descriptor
|
||||
bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration
|
||||
bool m_isConst : 1; // Table contains constant data
|
||||
|
|
@ -1927,20 +1955,24 @@ class AstVar final : public AstNode {
|
|||
bool m_noCReset : 1; // Do not do automated CReset creation
|
||||
bool m_noReset : 1; // Do not do automated reset/randomization
|
||||
bool m_noSubst : 1; // Do not substitute out references
|
||||
bool m_sampled : 1; // Sampled timing region
|
||||
bool m_substConstOnly : 1; // Only substitute if constant
|
||||
bool m_overridenParam : 1; // Overridden parameter by #(...) or defparam
|
||||
bool m_trace : 1; // Trace this variable
|
||||
bool m_isLatched : 1; // Not assigned in all control paths of combo always
|
||||
bool m_isForceable : 1; // May be forced/released externally from user C code
|
||||
bool m_isForcedByCode : 1; // May be forced/released from AstAssignForce/AstRelease
|
||||
bool m_isReadByDpi : 1; // This variable can be read by a DPI Export
|
||||
bool m_isWrittenByDpi : 1; // This variable can be written by a DPI Export
|
||||
bool m_isWrittenBySuspendable : 1; // This variable can be written by a suspendable process
|
||||
bool m_ignorePostRead : 1; // Ignore reads in 'Post' blocks during ordering
|
||||
bool m_ignorePostWrite : 1; // Ignore writes in 'Post' blocks during ordering
|
||||
bool m_ignoreSchedWrite : 1; // Ignore writes in scheduling (for special optimizations)
|
||||
bool m_dfgMultidriven : 1; // Singal is multidriven, used by DFG to avoid repeat processing
|
||||
bool m_dfgTriLowered : 1; // Signal/temporary introduced by tristate lowering
|
||||
bool m_dfgAllowMultidriveTri : 1; // Allow DFG MULTIDRIVEN warning for intentional tri nets
|
||||
bool m_globalConstrained : 1; // Global constraint per IEEE 1800-2023 18.5.8
|
||||
bool m_isStdRandomizeArg : 1; // Argument variable created for std::randomize (__Varg*)
|
||||
bool m_processQueue : 1; // Process queue variable
|
||||
void init() {
|
||||
m_ansi = false;
|
||||
m_declTyped = false;
|
||||
|
|
@ -1963,6 +1995,9 @@ class AstVar final : public AstNode {
|
|||
m_attrIsolateAssign = false;
|
||||
m_attrSFormat = false;
|
||||
m_attrSplitVar = false;
|
||||
m_attrFsmState = false;
|
||||
m_attrFsmResetArc = false;
|
||||
m_attrFsmArcInclCond = false;
|
||||
m_fileDescr = false;
|
||||
m_gotNansiType = false;
|
||||
m_isConst = false;
|
||||
|
|
@ -1981,20 +2016,24 @@ class AstVar final : public AstNode {
|
|||
m_noCReset = false;
|
||||
m_noReset = false;
|
||||
m_noSubst = false;
|
||||
m_sampled = false;
|
||||
m_substConstOnly = false;
|
||||
m_overridenParam = false;
|
||||
m_trace = false;
|
||||
m_isLatched = false;
|
||||
m_isForceable = false;
|
||||
m_isForcedByCode = false;
|
||||
m_isReadByDpi = false;
|
||||
m_isWrittenByDpi = false;
|
||||
m_isWrittenBySuspendable = false;
|
||||
m_ignorePostRead = false;
|
||||
m_ignorePostWrite = false;
|
||||
m_ignoreSchedWrite = false;
|
||||
m_dfgMultidriven = false;
|
||||
m_dfgTriLowered = false;
|
||||
m_dfgAllowMultidriveTri = false;
|
||||
m_globalConstrained = false;
|
||||
m_isStdRandomizeArg = false;
|
||||
m_processQueue = false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -2057,6 +2096,12 @@ public:
|
|||
}
|
||||
VDirection direction() const VL_MT_SAFE { return m_direction; }
|
||||
bool isIO() const VL_MT_SAFE { return m_direction != VDirection::NONE; }
|
||||
bool isVLIO() const {
|
||||
const AstBasicDType* const bdtypep = basicp();
|
||||
return isPrimaryIO() && bdtypep && !bdtypep->isOpaque()
|
||||
&& !dtypep()->skipRefp()->isCompound()
|
||||
&& !VN_IS(dtypep()->skipRefp(), UnpackArrayDType);
|
||||
}
|
||||
void declDirection(const VDirection& flag) { m_declDirection = flag; }
|
||||
VDirection declDirection() const { return m_declDirection; }
|
||||
void varType(VVarType type) { m_varType = type; }
|
||||
|
|
@ -2075,7 +2120,7 @@ public:
|
|||
string dpiTmpVarType(const string& varName) const;
|
||||
// Return Verilator internal type for argument: CData, SData, IData, WData
|
||||
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
|
||||
bool asRef = false) const;
|
||||
bool asRef = false, bool constRef = false) const;
|
||||
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
|
||||
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
||||
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
|
||||
|
|
@ -2097,6 +2142,9 @@ public:
|
|||
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
|
||||
void attrSFormat(bool flag) { m_attrSFormat = flag; }
|
||||
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
|
||||
void attrFsmState(bool flag) { m_attrFsmState = flag; }
|
||||
void attrFsmResetArc(bool flag) { m_attrFsmResetArc = flag; }
|
||||
void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; }
|
||||
void rand(const VRandAttr flag) { m_rand = flag; }
|
||||
void usedParam(bool flag) { m_usedParam = flag; }
|
||||
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
|
||||
|
|
@ -2142,6 +2190,10 @@ public:
|
|||
void noReset(bool flag) { m_noReset = flag; }
|
||||
bool noSubst() const { return m_noSubst; }
|
||||
void noSubst(bool flag) { m_noSubst = flag; }
|
||||
bool processQueue() const { return m_processQueue; }
|
||||
void processQueue(bool flag) { m_processQueue = flag; }
|
||||
bool sampled() const { return m_sampled; }
|
||||
void sampled(bool flag) { m_sampled = flag; }
|
||||
bool substConstOnly() const { return m_substConstOnly; }
|
||||
void substConstOnly(bool flag) { m_substConstOnly = flag; }
|
||||
bool overriddenParam() const { return m_overridenParam; }
|
||||
|
|
@ -2152,6 +2204,8 @@ public:
|
|||
void setForceable() { m_isForceable = true; }
|
||||
void setForcedByCode() { m_isForcedByCode = true; }
|
||||
bool isForced() const { return m_isForceable || m_isForcedByCode; }
|
||||
bool isReadByDpi() const { return m_isReadByDpi; }
|
||||
void setReadByDpi() { m_isReadByDpi = true; }
|
||||
bool isWrittenByDpi() const { return m_isWrittenByDpi; }
|
||||
void setWrittenByDpi() { m_isWrittenByDpi = true; }
|
||||
bool isWrittenBySuspendable() const { return m_isWrittenBySuspendable; }
|
||||
|
|
@ -2162,8 +2216,10 @@ public:
|
|||
void setIgnorePostWrite() { m_ignorePostWrite = true; }
|
||||
bool ignoreSchedWrite() const { return m_ignoreSchedWrite; }
|
||||
void setIgnoreSchedWrite() { m_ignoreSchedWrite = true; }
|
||||
bool dfgMultidriven() const { return m_dfgMultidriven; }
|
||||
void setDfgMultidriven() { m_dfgMultidriven = true; }
|
||||
bool dfgTriLowered() const { return m_dfgTriLowered; }
|
||||
void setDfgTriLowered() { m_dfgTriLowered = true; }
|
||||
bool dfgAllowMultidriveTri() const { return m_dfgAllowMultidriveTri; }
|
||||
void setDfgAllowMultidriveTri() { m_dfgAllowMultidriveTri = true; }
|
||||
void globalConstrained(bool flag) { m_globalConstrained = flag; }
|
||||
bool globalConstrained() const { return m_globalConstrained; }
|
||||
bool isStdRandomizeArg() const { return m_isStdRandomizeArg; }
|
||||
|
|
@ -2252,6 +2308,9 @@ public:
|
|||
bool attrFileDescr() const { return m_fileDescr; }
|
||||
bool attrSFormat() const { return m_attrSFormat; }
|
||||
bool attrSplitVar() const { return m_attrSplitVar; }
|
||||
bool attrFsmState() const { return m_attrFsmState; }
|
||||
bool attrFsmResetArc() const { return m_attrFsmResetArc; }
|
||||
bool attrFsmArcInclCond() const { return m_attrFsmArcInclCond; }
|
||||
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
|
||||
AstIface* sensIfacep() const { return m_sensIfacep; }
|
||||
VRandAttr rand() const { return m_rand; }
|
||||
|
|
@ -2337,12 +2396,22 @@ class AstCoverOtherDecl final : public AstNodeCoverDecl {
|
|||
// Coverage analysis point declaration
|
||||
// Used for other than toggle types of coverage
|
||||
string m_linescov;
|
||||
string m_fsmVar;
|
||||
string m_fsmFrom;
|
||||
string m_fsmTo;
|
||||
string m_fsmTag;
|
||||
int m_offset; // Offset column numbers to uniq-ify IFs
|
||||
public:
|
||||
AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment,
|
||||
const string& linescov, int offset)
|
||||
const string& linescov, int offset, const string& fsmVar = "",
|
||||
const string& fsmFrom = "", const string& fsmTo = "",
|
||||
const string& fsmTag = "")
|
||||
: ASTGEN_SUPER_CoverOtherDecl(fl, page, comment)
|
||||
, m_linescov{linescov}
|
||||
, m_fsmVar{fsmVar}
|
||||
, m_fsmFrom{fsmFrom}
|
||||
, m_fsmTo{fsmTo}
|
||||
, m_fsmTag{fsmTag}
|
||||
, m_offset{offset} {}
|
||||
ASTGEN_MEMBERS_AstCoverOtherDecl;
|
||||
void dump(std::ostream& str) const override;
|
||||
|
|
@ -2350,6 +2419,10 @@ public:
|
|||
int offset() const { return m_offset; }
|
||||
int size() const override { return 1; }
|
||||
const string& linescov() const { return m_linescov; }
|
||||
const string& fsmVar() const { return m_fsmVar; }
|
||||
const string& fsmFrom() const { return m_fsmFrom; }
|
||||
const string& fsmTo() const { return m_fsmTo; }
|
||||
const string& fsmTag() const { return m_fsmTag; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl);
|
||||
return AstNodeCoverDecl::sameNode(samep) && linescov() == asamep->linescov();
|
||||
|
|
@ -2424,7 +2497,9 @@ public:
|
|||
class AstSequence final : public AstNodeFTask {
|
||||
// A sequence inside a module
|
||||
// TODO when supported might not want to be a NodeFTask
|
||||
bool m_referenced = false; // Ever referenced (for unsupported check)
|
||||
bool m_referenced = false; // Set by V3LinkResolve when referenced; cleared by
|
||||
// V3AssertPre after inlining; if still set after
|
||||
// V3AssertPre, the reference is in an unsupported context
|
||||
public:
|
||||
AstSequence(FileLine* fl, const string& name, AstNode* stmtp)
|
||||
: ASTGEN_SUPER_Sequence(fl, name, stmtp) {}
|
||||
|
|
|
|||
|
|
@ -97,18 +97,25 @@ class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt {
|
|||
// op3 used by some sub-types only
|
||||
// @astgen op4 := passsp: List[AstNode] // Statements when propp is passing/truthly
|
||||
string m_name; // Name to report
|
||||
const VAssertType m_type; // Assertion/cover type
|
||||
const VAssertType m_userType; // Assertion/cover type for user enable/disable
|
||||
const VAssertDirectiveType m_directive; // Assertion directive type
|
||||
bool m_senFromAlways = false; // Sensitivity list copied from upper always
|
||||
bool m_immediate = false; // Immediate assert (may differ from userType being immediate)
|
||||
|
||||
public:
|
||||
AstNodeCoverOrAssert(VNType t, FileLine* fl, AstNode* propp, AstNode* passsp, VAssertType type,
|
||||
VAssertDirectiveType directive, const string& name = "")
|
||||
AstNodeCoverOrAssert(VNType t, FileLine* fl, AstNode* propp, AstNode* passsp,
|
||||
VAssertType userType, VAssertDirectiveType directive,
|
||||
const string& name = "")
|
||||
: AstNodeStmt{t, fl}
|
||||
, m_name{name}
|
||||
, m_type{type}
|
||||
, m_userType{userType}
|
||||
, m_directive{directive} {
|
||||
this->propp(propp);
|
||||
addPasssp(passsp);
|
||||
m_immediate = m_userType.containsAny(VAssertType::SIMPLE_IMMEDIATE
|
||||
| VAssertType::OBSERVED_DEFERRED_IMMEDIATE
|
||||
| VAssertType::FINAL_DEFERRED_IMMEDIATE)
|
||||
|| m_userType == VAssertType::INTERNAL;
|
||||
}
|
||||
ASTGEN_MEMBERS_AstNodeCoverOrAssert;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
|
|
@ -116,14 +123,13 @@ public:
|
|||
void name(const string& name) override { m_name = name; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpJson(std::ostream& str = std::cout) const override;
|
||||
VAssertType type() const VL_MT_SAFE { return m_type; }
|
||||
string type() { return ""; }
|
||||
VAssertType userType() const VL_MT_SAFE { return m_userType; }
|
||||
VAssertDirectiveType directive() const { return m_directive; }
|
||||
bool immediate() const {
|
||||
return this->type().containsAny(VAssertType::SIMPLE_IMMEDIATE
|
||||
| VAssertType::OBSERVED_DEFERRED_IMMEDIATE
|
||||
| VAssertType::FINAL_DEFERRED_IMMEDIATE)
|
||||
|| this->type() == VAssertType::INTERNAL;
|
||||
}
|
||||
bool immediate() const { return m_immediate; }
|
||||
void immediate(bool flag) { m_immediate = flag; }
|
||||
bool senFromAlways() const VL_MT_STABLE { return m_senFromAlways; }
|
||||
void senFromAlways(bool flag) { m_senFromAlways = flag; }
|
||||
};
|
||||
class AstNodeForeach VL_NOT_FINAL : public AstNodeStmt {
|
||||
// @astgen op1 := headerp : AstForeachHeader
|
||||
|
|
@ -305,6 +311,7 @@ public:
|
|||
class AstCStmt final : public AstNodeStmt {
|
||||
// C statement emitted into output, with some arbitrary nodes interspersed
|
||||
// @astgen op1 := nodesp : List[AstNode<AstNodeStmt|AstNodeExpr|AstText>]
|
||||
const VCStmtType m_stmtType; // Special statement (instead of comparing name())
|
||||
|
||||
static AstCStmt* profExecSection(FileLine* flp, const std::string& section, bool push) {
|
||||
// Compute the label
|
||||
|
|
@ -331,8 +338,10 @@ class AstCStmt final : public AstNodeStmt {
|
|||
}
|
||||
|
||||
public:
|
||||
explicit AstCStmt(FileLine* fl, const std::string& text = "")
|
||||
: ASTGEN_SUPER_CStmt(fl) {
|
||||
explicit AstCStmt(FileLine* fl, const std::string& text = "",
|
||||
const VCStmtType& stmtType = VCStmtType::NONE)
|
||||
: ASTGEN_SUPER_CStmt(fl)
|
||||
, m_stmtType{stmtType} {
|
||||
if (!text.empty()) add(text);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCStmt;
|
||||
|
|
@ -341,6 +350,9 @@ public:
|
|||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() override { return false; }
|
||||
bool sameNode(const AstNode*) const override { return true; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
VCStmtType stmtType() const { return m_stmtType; }
|
||||
// Add some text, or a node to this statement
|
||||
void add(const std::string& text) { addNodesp(new AstText{fileline(), text}); }
|
||||
void add(AstNode* nodep) { addNodesp(nodep); }
|
||||
|
|
@ -529,11 +541,12 @@ public:
|
|||
};
|
||||
class AstDelay final : public AstNodeStmt {
|
||||
// Delay statement
|
||||
// @astgen op1 := lhsp : AstNodeExpr // Delay value
|
||||
// @astgen op1 := lhsp : AstNodeExpr // Delay value (or min for range)
|
||||
// @astgen op2 := stmtsp : List[AstNode] // Statements under delay
|
||||
// @astgen op3 := rhsp : Optional[AstNodeExpr] // Max bound for cycle range or fall delay
|
||||
// @astgen op4 := throughoutp : Optional[AstNodeExpr] // Throughout condition (IEEE 16.9.9)
|
||||
VTimescale m_timeunit; // Delay's time unit
|
||||
const bool m_isCycle; // True if it is a cycle delay
|
||||
|
||||
public:
|
||||
AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycle)
|
||||
: ASTGEN_SUPER_Delay(fl)
|
||||
|
|
@ -548,6 +561,10 @@ public:
|
|||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
bool isCycleDelay() const { return m_isCycle; }
|
||||
bool isRangeDelay() const { return m_isCycle && rhsp() != nullptr; }
|
||||
bool isUnbounded() const { return isRangeDelay() && VN_IS(rhsp(), Unbounded); }
|
||||
void fallDelay(AstNodeExpr* const fallDelayp) { rhsp(fallDelayp); }
|
||||
AstNodeExpr* fallDelay() const { return m_isCycle ? nullptr : rhsp(); }
|
||||
};
|
||||
class AstDisable final : public AstNodeStmt {
|
||||
// @astgen op1 := targetRefp : Optional[AstNodeExpr] // Reference to link in V3LinkDot
|
||||
|
|
@ -589,7 +606,10 @@ public:
|
|||
char missingArgChar = 'd')
|
||||
: ASTGEN_SUPER_Display(fl)
|
||||
, m_displayType{dispType} {
|
||||
fmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp, missingArgChar});
|
||||
AstSFormatF* const newp
|
||||
= new AstSFormatF{fl, AstSFormatF::ExprFormat{}, exprsp, missingArgChar};
|
||||
newp->optionalFormat(true);
|
||||
fmtp(newp);
|
||||
this->filep(filep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstDisplay;
|
||||
|
|
@ -1098,15 +1118,13 @@ class AstSFormat final : public AstNodeStmt {
|
|||
// @astgen op1 := fmtp : AstSFormatF
|
||||
// @astgen op2 := lhsp : AstNodeExpr
|
||||
public:
|
||||
AstSFormat(FileLine* fl, AstNodeExpr* lhsp, const string& text, AstNodeExpr* exprsp,
|
||||
AstSFormat(FileLine* fl, bool optionalFormat, AstNodeExpr* lhsp, AstNodeExpr* exprsp,
|
||||
char missingArgChar = 'd')
|
||||
: ASTGEN_SUPER_SFormat(fl) {
|
||||
fmtp(new AstSFormatF{fl, text, true, exprsp, missingArgChar});
|
||||
this->lhsp(lhsp);
|
||||
}
|
||||
AstSFormat(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* exprsp, char missingArgChar = 'd')
|
||||
: ASTGEN_SUPER_SFormat(fl) {
|
||||
fmtp(new AstSFormatF{fl, AstSFormatF::NoFormat{}, exprsp, missingArgChar});
|
||||
AstSFormatF* const newp
|
||||
= new AstSFormatF{fl, AstSFormatF::ExprFormat{}, exprsp, missingArgChar};
|
||||
newp->optionalFormat(optionalFormat);
|
||||
fmtp(newp);
|
||||
this->lhsp(lhsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSFormat;
|
||||
|
|
@ -1249,6 +1267,9 @@ class AstTraceDecl final : public AstNodeStmt {
|
|||
// Parents: {statement list}
|
||||
// Expression being traced - Moved to AstTraceInc by V3Trace
|
||||
// @astgen op1 := valuep : Optional[AstNodeExpr]
|
||||
//
|
||||
// @astgen ptr := m_dtypeCallp: Optional[AstCCall] // Type init function call
|
||||
// @astgen ptr := m_dtypeDeclp: Optional[AstTraceDecl] // CCall TraceDecl which replaces this
|
||||
uint32_t m_code{std::numeric_limits<uint32_t>::max()}; // Trace identifier code
|
||||
uint32_t m_fidx{0}; // Trace function index
|
||||
const string m_showname; // Name of variable
|
||||
|
|
@ -1256,18 +1277,23 @@ class AstTraceDecl final : public AstNodeStmt {
|
|||
const VNumRange m_arrayRange; // Property of var the trace details
|
||||
const VVarType m_varType; // Type of variable (for localparam vs. param)
|
||||
const VDirection m_declDirection; // Declared direction input/output etc
|
||||
const bool m_inDtypeFunc; // Trace decl inside type init function
|
||||
int m_codeInc{0}; // Code increment for type
|
||||
public:
|
||||
AstTraceDecl(FileLine* fl, const string& showname,
|
||||
AstVar* varp, // For input/output state etc
|
||||
AstNodeExpr* valuep, const VNumRange& bitRange, const VNumRange& arrayRange)
|
||||
AstNodeExpr* valuep, const VNumRange& bitRange, const VNumRange& arrayRange,
|
||||
AstCCall* const dtypeCallp, const bool inDtypeFunc)
|
||||
: ASTGEN_SUPER_TraceDecl(fl)
|
||||
, m_showname{showname}
|
||||
, m_bitRange{bitRange}
|
||||
, m_arrayRange{arrayRange}
|
||||
, m_varType{varp->varType()}
|
||||
, m_declDirection{varp->declDirection()} {
|
||||
, m_declDirection{varp->declDirection()}
|
||||
, m_inDtypeFunc{inDtypeFunc} {
|
||||
dtypeFrom(valuep);
|
||||
this->valuep(valuep);
|
||||
this->dtypeCallp(dtypeCallp);
|
||||
}
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
|
|
@ -1276,7 +1302,7 @@ public:
|
|||
string name() const override VL_MT_STABLE { return m_showname; }
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
bool hasDType() const override VL_MT_SAFE { return true; }
|
||||
bool sameNode(const AstNode* samep) const override { return false; }
|
||||
bool sameNode(const AstNode* samep) const override { return true; }
|
||||
string showname() const { return m_showname; } // * = Var name
|
||||
// Details on what we're tracing
|
||||
uint32_t code() const { return m_code; }
|
||||
|
|
@ -1284,7 +1310,9 @@ public:
|
|||
bool codeAssigned() const { return m_code != std::numeric_limits<uint32_t>::max(); }
|
||||
uint32_t fidx() const { return m_fidx; }
|
||||
void fidx(uint32_t fidx) { m_fidx = fidx; }
|
||||
void codeInc(uint32_t codeInc) { m_codeInc = codeInc; }
|
||||
uint32_t codeInc() const {
|
||||
if (m_codeInc) { return m_codeInc; }
|
||||
return (m_arrayRange.ranged() ? m_arrayRange.elements() : 1)
|
||||
* valuep()->dtypep()->widthWords()
|
||||
* (VL_EDATASIZE / 32); // A code is always 32-bits
|
||||
|
|
@ -1293,6 +1321,11 @@ public:
|
|||
const VNumRange& arrayRange() const { return m_arrayRange; }
|
||||
VVarType varType() const { return m_varType; }
|
||||
VDirection declDirection() const { return m_declDirection; }
|
||||
AstCCall* dtypeCallp() const { return m_dtypeCallp; }
|
||||
void dtypeCallp(AstCCall* const callp) { m_dtypeCallp = callp; }
|
||||
AstTraceDecl* dtypeDeclp() const { return m_dtypeDeclp; }
|
||||
void dtypeDeclp(AstTraceDecl* const declp) { m_dtypeDeclp = declp; }
|
||||
bool inDtypeFunc() const { return m_inDtypeFunc; }
|
||||
};
|
||||
class AstTraceInc final : public AstNodeStmt {
|
||||
// Trace point dump
|
||||
|
|
@ -1339,15 +1372,25 @@ public:
|
|||
class AstTracePushPrefix final : public AstNodeStmt {
|
||||
const string m_prefix; // Prefix to add to signal names
|
||||
const VTracePrefixType m_prefixType; // Type of prefix being pushed
|
||||
const int m_left; // Array left index, or struct/union member count
|
||||
const int m_right; // Array right index
|
||||
const bool m_quotedPrefix; // Quote prefix name
|
||||
public:
|
||||
AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType)
|
||||
AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType,
|
||||
int left = 0, int right = 0, bool quotedPrefix = true)
|
||||
: ASTGEN_SUPER_TracePushPrefix(fl)
|
||||
, m_prefix{prefix}
|
||||
, m_prefixType{prefixType} {}
|
||||
, m_prefixType{prefixType}
|
||||
, m_left{left}
|
||||
, m_right{right}
|
||||
, m_quotedPrefix{quotedPrefix} {}
|
||||
ASTGEN_MEMBERS_AstTracePushPrefix;
|
||||
bool sameNode(const AstNode* samep) const override { return false; }
|
||||
string prefix() const { return m_prefix; }
|
||||
VTracePrefixType prefixType() const { return m_prefixType; }
|
||||
int left() const { return m_left; }
|
||||
int right() const { return m_right; }
|
||||
bool quotedPrefix() const { return m_quotedPrefix; }
|
||||
};
|
||||
class AstWait final : public AstNodeStmt {
|
||||
// @astgen op1 := condp : AstNodeExpr
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ void AstBasicDType::init(VBasicDTypeKwd kwd, VSigning numer, int wantwidth, int
|
|||
AstRange* rangep) {
|
||||
// wantwidth=0 means figure it out, but if a widthmin is >=0
|
||||
// we allow width 0 so that {{0{x}},y} works properly
|
||||
// wantwidthmin=-1: default, use wantwidth if it is non zero
|
||||
// wantwidthmin=-1: default, use wantwidth if it is non-zero
|
||||
m.m_keyword = kwd;
|
||||
// Implicitness: // "parameter X" is implicit and sized from initial
|
||||
// value, "parameter reg x" not
|
||||
|
|
@ -448,6 +448,14 @@ void AstConsDynArray::dumpJson(std::ostream& str) const {
|
|||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
void AstSConsRep::dump(std::ostream& str) const { // LCOV_EXCL_START
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (unbounded()) str << " [unbounded]";
|
||||
}
|
||||
void AstSConsRep::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, unbounded);
|
||||
dumpJsonGen(str);
|
||||
} // LCOV_EXCL_STOP
|
||||
void AstConsQueue::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (lhsIsValue()) str << " [LVAL]";
|
||||
|
|
@ -614,7 +622,7 @@ string AstVar::verilogKwd() const {
|
|||
}
|
||||
|
||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
|
||||
bool asRef) const {
|
||||
bool asRef, bool constRef) const {
|
||||
UASSERT_OBJ(!forReturn, this,
|
||||
"Internal data is never passed as return, but as first argument");
|
||||
string ostatic;
|
||||
|
|
@ -622,7 +630,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
|||
|
||||
asRef = asRef || isDpiOpenArray() || (forFunc && (isWritable() || isRef() || isConstRef()));
|
||||
|
||||
if (forFunc && isReadOnly() && asRef) ostatic = ostatic + "const ";
|
||||
if (forFunc && (isReadOnly() || constRef) && asRef) ostatic = ostatic + "const ";
|
||||
|
||||
string oname;
|
||||
if (named) {
|
||||
|
|
@ -682,6 +690,12 @@ string AstVar::vlEnumDir() const {
|
|||
if (const AstBasicDType* const bdtypep = basicp()) {
|
||||
if (bdtypep->keyword().isDpiCLayout()) out += "|VLVF_DPI_CLAY";
|
||||
}
|
||||
//
|
||||
if (dtypep()->skipRefp()->isSigned()) out += "|VLVF_SIGNED";
|
||||
//
|
||||
if (AstBasicDType* const basicp = dtypep()->skipRefp()->basicp()) {
|
||||
if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -1007,6 +1021,11 @@ bool AstNodeDType::similarDType(const AstNodeDType* samep) const {
|
|||
|
||||
bool AstNodeDType::isFourstate() const { return basicp() && basicp()->isFourstate(); }
|
||||
|
||||
bool AstNodeDType::isNonPackedArray() const {
|
||||
return VN_IS(this, UnpackArrayDType) || VN_IS(this, DynArrayDType) || VN_IS(this, QueueDType)
|
||||
|| VN_IS(this, AssocArrayDType);
|
||||
}
|
||||
|
||||
class AstNodeDType::CTypeRecursed final {
|
||||
public:
|
||||
string m_type; // The base type, e.g.: "Foo_t"s
|
||||
|
|
@ -1791,6 +1810,26 @@ string AstBasicDType::prettyDTypeName(bool) const {
|
|||
|
||||
void AstNodeExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
void AstNodeExpr::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
|
||||
bool AstNodeExpr::isLValue() const {
|
||||
if (const AstNodeVarRef* const varrefp = VN_CAST(this, NodeVarRef)) {
|
||||
return varrefp->access().isWriteOrRW();
|
||||
} else if (const AstMemberSel* const memberselp = VN_CAST(this, MemberSel)) {
|
||||
return memberselp->access().isWriteOrRW();
|
||||
} else if (const AstSel* const selp = VN_CAST(this, Sel)) {
|
||||
return selp->fromp()->isLValue();
|
||||
} else if (const AstNodeSel* const nodeSelp = VN_CAST(this, NodeSel)) {
|
||||
return nodeSelp->fromp()->isLValue();
|
||||
} else if (const AstConcat* const concatp = VN_CAST(this, Concat)) {
|
||||
// Enough to check only one side, as both must be same otherwise malformed
|
||||
return concatp->lhsp()->isLValue();
|
||||
} else if (const AstCMethodHard* const cMethodHardp = VN_CAST(this, CMethodHard)) {
|
||||
// Used for things like Queue/AssocArray/DynArray
|
||||
return cMethodHardp->fromp()->isLValue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AstNodeUniop::dump(std::ostream& str) const { this->AstNodeExpr::dump(str); }
|
||||
void AstNodeUniop::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
|
||||
|
|
@ -1849,6 +1888,14 @@ void AstCellInlineScope::dumpJson(std::ostream& str) const {
|
|||
dumpJsonStrFunc(str, origModName);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstCExpr::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (m_pure) str << " [PURE]";
|
||||
}
|
||||
void AstCExpr::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolIf(str, "pure", m_pure);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
bool AstClass::isCacheableChild(const AstNode* nodep) {
|
||||
return VN_IS(nodep, Var) || VN_IS(nodep, Typedef)
|
||||
|| (VN_IS(nodep, Constraint) && !VN_AS(nodep, Constraint)->isExternProto())
|
||||
|
|
@ -1901,9 +1948,8 @@ AstClass* AstClassExtends::classOrNullp() const {
|
|||
if (refp && !refp->paramsp()) {
|
||||
// Class already resolved
|
||||
return refp->classp();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
AstClass* AstClassExtends::classp() const {
|
||||
AstClass* const clsp = classOrNullp();
|
||||
|
|
@ -1927,13 +1973,39 @@ void AstClassRefDType::dumpSmall(std::ostream& str) const {
|
|||
}
|
||||
string AstClassRefDType::prettyDTypeName(bool) const { return "class{}"s + prettyName(); }
|
||||
string AstClassRefDType::name() const { return classp() ? classp()->name() : "<unlinked>"; }
|
||||
bool AstClassRefDType::similarDTypeNode(const AstNodeDType* samep) const {
|
||||
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
|
||||
if (m_classp != asamep->m_classp) return false;
|
||||
// Compare type parameters so C#(int) != C#(string)
|
||||
const AstPin* lp = paramsp();
|
||||
const AstPin* rp = asamep->paramsp();
|
||||
while (lp && rp) {
|
||||
if (!lp->exprp() != !rp->exprp()) return false;
|
||||
if (lp->exprp()) {
|
||||
const AstNodeDType* const lDtp = VN_CAST(lp->exprp(), NodeDType);
|
||||
const AstNodeDType* const rDtp = VN_CAST(rp->exprp(), NodeDType);
|
||||
if (lDtp && rDtp) {
|
||||
if (!lDtp->similarDType(rDtp)) return false;
|
||||
} else {
|
||||
if (!lp->exprp()->sameTree(rp->exprp())) return false;
|
||||
}
|
||||
}
|
||||
lp = VN_CAST(lp->nextp(), Pin);
|
||||
rp = VN_CAST(rp->nextp(), Pin);
|
||||
}
|
||||
return !lp && !rp;
|
||||
}
|
||||
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " ["s + this->type().ascii() + "]";
|
||||
str << " ["s + this->userType().ascii() + "]";
|
||||
if (immediate()) str << " [IMMEDIATE]";
|
||||
if (senFromAlways()) str << " [SENALW]";
|
||||
}
|
||||
void AstNodeCoverOrAssert::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStr(str, "type", "["s + this->type().ascii() + "]");
|
||||
dumpJsonStr(str, "type", "["s + this->userType().ascii() + "]");
|
||||
dumpJsonGen(str);
|
||||
dumpJsonBoolFuncIf(str, immediate);
|
||||
dumpJsonBoolFuncIf(str, senFromAlways);
|
||||
}
|
||||
void AstClocking::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
|
|
@ -1981,7 +2053,7 @@ void AstEnumDType::dump(std::ostream& str) const {
|
|||
str << " enum";
|
||||
}
|
||||
void AstEnumDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolIf(str, "enum", 1);
|
||||
dumpJsonBoolIf(str, "enum", true);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstEnumDType::dumpSmall(std::ostream& str) const {
|
||||
|
|
@ -2114,11 +2186,8 @@ void AstInitArray::addIndexValuep(uint64_t index, AstNodeExpr* newp) {
|
|||
}
|
||||
AstNodeExpr* AstInitArray::getIndexValuep(uint64_t index) const {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return it->second->valuep();
|
||||
}
|
||||
if (it == m_map.end()) { return nullptr; }
|
||||
return it->second->valuep();
|
||||
}
|
||||
AstNodeExpr* AstInitArray::getIndexDefaultedValuep(uint64_t index) const {
|
||||
AstNodeExpr* valuep = getIndexValuep(index);
|
||||
|
|
@ -2274,12 +2343,14 @@ void AstPin::dump(std::ostream& str) const {
|
|||
} else {
|
||||
str << " ->UNLINKED";
|
||||
}
|
||||
if (!paramPath().empty()) str << " paramPath=" << paramPath();
|
||||
if (svDotName()) str << " [.n]";
|
||||
if (svImplicit()) str << " [.SV]";
|
||||
}
|
||||
void AstPin::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, svDotName);
|
||||
dumpJsonBoolFuncIf(str, svImplicit);
|
||||
if (!paramPath().empty()) dumpJsonStr(str, "paramPath", paramPath());
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
string AstPin::prettyOperatorName() const {
|
||||
|
|
@ -2453,6 +2524,16 @@ bool AstUnionDType::sameNode(const AstNode* samep) const {
|
|||
const AstUnionDType* const asamep = VN_DBG_AS(samep, UnionDType);
|
||||
return m_isSoft == asamep->m_isSoft && m_isTagged == asamep->m_isTagged;
|
||||
}
|
||||
void AstUntil::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (isStrong()) str << " [strong]";
|
||||
if (isOverlapping()) str << " [overlapping]";
|
||||
}
|
||||
void AstUntil::dumpJson(std::ostream& str) const {
|
||||
this->AstNodeExpr::dumpJson(str);
|
||||
dumpJsonBoolFuncIf(str, isStrong);
|
||||
dumpJsonBoolFuncIf(str, isOverlapping);
|
||||
}
|
||||
string AstNodeUOrStructDType::prettyDTypeName(bool full) const {
|
||||
string result = verilogKwd() + "{";
|
||||
if (full) { // else shorten for errors
|
||||
|
|
@ -2475,7 +2556,7 @@ void AstNodeDType::dump(std::ostream& str) const {
|
|||
}
|
||||
void AstNodeDType::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFuncIf(str, generic);
|
||||
if (isSigned() && !isDouble()) dumpJsonBoolIf(str, "signed", 1);
|
||||
if (isSigned() && !isDouble()) dumpJsonBoolIf(str, "signed", true);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstNodeDType::dumpSmall(std::ostream& str) const VL_MT_STABLE {
|
||||
|
|
@ -2595,12 +2676,14 @@ void AstNodeModule::dump(std::ostream& str) const {
|
|||
str << " D" << depth();
|
||||
if (modPublic()) str << " [P]";
|
||||
if (inLibrary()) str << " [LIB]";
|
||||
if (ctorVarReset()) str << " [CVRESET]";
|
||||
if (dead()) str << " [DEAD]";
|
||||
if (recursiveClone()) {
|
||||
str << " [RECURSIVE-CLONE]";
|
||||
} else if (recursive()) {
|
||||
str << " [RECURSIVE]";
|
||||
}
|
||||
if (parameterizedTemplate()) str << " [PAR-TEMPL]";
|
||||
if (verilatorLib()) str << " [VERILATOR-LIB]";
|
||||
str << " [" << timeunit() << "]";
|
||||
if (libname() != "work") str << " libname=" << libname();
|
||||
|
|
@ -2611,6 +2694,7 @@ void AstNodeModule::dumpJson(std::ostream& str) const {
|
|||
dumpJsonNumFunc(str, level);
|
||||
dumpJsonBoolFuncIf(str, modPublic);
|
||||
dumpJsonBoolFuncIf(str, inLibrary);
|
||||
dumpJsonBoolFuncIf(str, ctorVarReset);
|
||||
dumpJsonBoolFuncIf(str, dead);
|
||||
dumpJsonBoolFuncIf(str, recursiveClone);
|
||||
dumpJsonBoolFuncIf(str, recursive);
|
||||
|
|
@ -2652,6 +2736,35 @@ void AstPatMember::dumpJson(std::ostream& str) const {
|
|||
}
|
||||
void AstNodeTriop::dump(std::ostream& str) const { this->AstNodeExpr::dump(str); }
|
||||
void AstNodeTriop::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
void AstSFormatArg::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
str << " [" << formatAttr().ascii() << "]";
|
||||
}
|
||||
void AstSFormatArg::dumpJson(std::ostream& str) const {
|
||||
dumpJsonGen(str);
|
||||
dumpJsonStr(str, "formatAttr", std::string{formatAttr().ascii()});
|
||||
}
|
||||
VFormatAttr AstSFormatArg::formatAttrDefauled(const AstSFormatArg* nodep,
|
||||
const AstNodeDType* dtypep) {
|
||||
if (nodep) return nodep->formatAttr();
|
||||
// Used to initially assign the formatArg
|
||||
// Later, V3Randomize creates raw %s's without SFormatArg's that have string arguments
|
||||
if (!dtypep) return VFormatAttr{};
|
||||
const AstNodeDType* skipDtypep = dtypep->skipRefp();
|
||||
if (skipDtypep->isDouble()) return VFormatAttr{VFormatAttr::DOUBLE};
|
||||
if (skipDtypep->isString()) return VFormatAttr{VFormatAttr::STRING};
|
||||
return VFormatAttr{};
|
||||
}
|
||||
void AstSFormatF::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (exprFormat()) str << " [EXPRFMT]";
|
||||
if (optionalFormat()) str << " [OPTFMT]";
|
||||
}
|
||||
void AstSFormatF::dumpJson(std::ostream& str) const {
|
||||
dumpJsonGen(str);
|
||||
dumpJsonBoolFuncIf(str, exprFormat);
|
||||
dumpJsonBoolFuncIf(str, optionalFormat);
|
||||
}
|
||||
void AstSel::dump(std::ostream& str) const {
|
||||
this->AstNodeBiop::dump(str);
|
||||
str << " widthConst=" << this->widthConst();
|
||||
|
|
@ -2880,13 +2993,20 @@ void AstVar::dump(std::ostream& str) const {
|
|||
if (isSigPublic()) str << " [P]";
|
||||
if (isSigUserRdPublic()) str << " [PRD]";
|
||||
if (isSigUserRWPublic()) str << " [PWR]";
|
||||
if (isReadByDpi()) str << " [DPIRD]";
|
||||
if (isWrittenByDpi()) str << " [DPIWR]";
|
||||
if (isInternal()) str << " [INTERNAL]";
|
||||
if (isLatched()) str << " [LATCHED]";
|
||||
if (isUsedLoopIdx()) str << " [LOOPIDX]";
|
||||
if (rand().isRandomizable()) str << " [" << rand() << "]";
|
||||
if (noCReset()) str << " [!CRST]";
|
||||
if (noReset()) str << " [!RST]";
|
||||
if (processQueue()) str << " [PROCQ]";
|
||||
if (sampled()) str << " [SAMPLED]";
|
||||
if (attrIsolateAssign()) str << " [aISO]";
|
||||
if (attrFsmState()) str << " [aFSMSTATE]";
|
||||
if (attrFsmResetArc()) str << " [aFSMRESETARC]";
|
||||
if (attrFsmArcInclCond()) str << " [aFSMARCCOND]";
|
||||
if (attrFileDescr()) str << " [aFD]";
|
||||
if (isFuncReturn()) {
|
||||
str << " [FUNCRTN]";
|
||||
|
|
@ -2916,7 +3036,12 @@ void AstVar::dumpJson(std::ostream& str) const {
|
|||
dumpJsonBoolFuncIf(str, isUsedLoopIdx);
|
||||
dumpJsonBoolFuncIf(str, noCReset);
|
||||
dumpJsonBoolFuncIf(str, noReset);
|
||||
dumpJsonBoolFuncIf(str, processQueue);
|
||||
dumpJsonBoolFuncIf(str, sampled);
|
||||
dumpJsonBoolFuncIf(str, attrIsolateAssign);
|
||||
dumpJsonBoolFuncIf(str, attrFsmState);
|
||||
dumpJsonBoolFuncIf(str, attrFsmResetArc);
|
||||
dumpJsonBoolFuncIf(str, attrFsmArcInclCond);
|
||||
dumpJsonBoolFuncIf(str, attrFileDescr);
|
||||
dumpJsonBoolFuncIf(str, isDpiOpenArray);
|
||||
dumpJsonBoolFuncIf(str, isFuncReturn);
|
||||
|
|
@ -2927,6 +3052,8 @@ void AstVar::dumpJson(std::ostream& str) const {
|
|||
if (dtypep()) dumpJsonStr(str, "dtypeName", dtypep()->name());
|
||||
dumpJsonBoolFuncIf(str, isSigUserRdPublic);
|
||||
dumpJsonBoolFuncIf(str, isSigUserRWPublic);
|
||||
dumpJsonBoolFuncIf(str, isReadByDpi);
|
||||
dumpJsonBoolFuncIf(str, isWrittenByDpi);
|
||||
dumpJsonBoolFuncIf(str, isGParam);
|
||||
dumpJsonBoolFuncIf(str, isParam);
|
||||
dumpJsonBoolFuncIf(str, attrScBv);
|
||||
|
|
@ -3039,6 +3166,7 @@ void AstActive::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
|||
void AstNodeFTaskRef::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
|
||||
if (containsGenBlock()) str << " [GENBLK]";
|
||||
str << " -> ";
|
||||
if (dotted() != "") str << ".=" << dotted() << " ";
|
||||
if (taskp()) {
|
||||
|
|
@ -3049,6 +3177,7 @@ void AstNodeFTaskRef::dump(std::ostream& str) const {
|
|||
}
|
||||
void AstNodeFTaskRef::dumpJson(std::ostream& str) const {
|
||||
dumpJsonStrFunc(str, dotted);
|
||||
dumpJsonBoolFuncIf(str, containsGenBlock);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstNodeFTask::dump(std::ostream& str) const {
|
||||
|
|
@ -3160,10 +3289,18 @@ void AstNodeCoverDecl::dumpJson(std::ostream& str) const {
|
|||
void AstCoverOtherDecl::dump(std::ostream& str) const {
|
||||
this->AstNodeCoverDecl::dump(str);
|
||||
if (!linescov().empty()) str << " lc=" << linescov();
|
||||
if (!fsmVar().empty()) str << " fv=" << fsmVar();
|
||||
if (!fsmFrom().empty()) str << " ff=" << fsmFrom();
|
||||
if (!fsmTo().empty()) str << " ft=" << fsmTo();
|
||||
if (!fsmTag().empty()) str << " fg=" << fsmTag();
|
||||
}
|
||||
void AstCoverOtherDecl::dumpJson(std::ostream& str) const {
|
||||
this->AstNodeCoverDecl::dumpJson(str);
|
||||
dumpJsonStrFunc(str, linescov);
|
||||
dumpJsonStrFunc(str, fsmVar);
|
||||
dumpJsonStrFunc(str, fsmFrom);
|
||||
dumpJsonStrFunc(str, fsmTo);
|
||||
dumpJsonStrFunc(str, fsmTag);
|
||||
}
|
||||
void AstCoverToggleDecl::dump(std::ostream& str) const {
|
||||
this->AstNodeCoverDecl::dump(str);
|
||||
|
|
@ -3204,7 +3341,8 @@ void AstStop::dumpJson(std::ostream& str) const {
|
|||
}
|
||||
void AstTraceDecl::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (code()) str << " [code=" << code() << "]";
|
||||
if (codeAssigned()) str << " [code=" << code() << "]";
|
||||
if (dtypeCallp()) str << " [dtypeCallp=" << dtypeCallp() << "]";
|
||||
}
|
||||
void AstTraceDecl::dumpJson(std::ostream& str) const {
|
||||
dumpJsonNumFunc(str, code);
|
||||
|
|
@ -3326,6 +3464,14 @@ void AstCMethodHard::setPurity() {
|
|||
}
|
||||
}
|
||||
|
||||
void AstCStmt::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (!stmtType().isNone()) str << " [" << stmtType().ascii() << "]";
|
||||
}
|
||||
void AstCStmt::dumpJson(std::ostream& str) const {
|
||||
dumpJsonGen(str);
|
||||
if (!stmtType().isNone()) dumpJsonStr(str, "stmtType", stmtType().ascii());
|
||||
}
|
||||
void AstCUse::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << useType() << "]";
|
||||
|
|
@ -3346,18 +3492,12 @@ static AstDelay* getLhsNetDelayRecurse(const AstNodeExpr* const nodep) {
|
|||
AstDelay* AstAssignW::getLhsNetDelay() const { return getLhsNetDelayRecurse(lhsp()); }
|
||||
|
||||
string AstCase::pragmaString() const {
|
||||
if (fullPragma() && parallelPragma())
|
||||
return "synthesis full_case parallel_case";
|
||||
else if (fullPragma())
|
||||
return "synthesis full_case";
|
||||
else if (parallelPragma())
|
||||
return "synthesis parallel_case";
|
||||
else if (uniquePragma())
|
||||
return "unique case";
|
||||
else if (unique0Pragma())
|
||||
return "unique0 case";
|
||||
else if (priorityPragma())
|
||||
return "priority case";
|
||||
if (fullPragma() && parallelPragma()) return "synthesis full_case parallel_case";
|
||||
if (fullPragma()) return "synthesis full_case";
|
||||
if (parallelPragma()) return "synthesis parallel_case";
|
||||
if (uniquePragma()) return "unique case";
|
||||
if (unique0Pragma()) return "unique0 case";
|
||||
if (priorityPragma()) return "priority case";
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
|||
156
src/V3Begin.cpp
156
src/V3Begin.cpp
|
|
@ -499,8 +499,9 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
|
|||
V3Global::dumpCheckGlobalTree("begin", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
static AstNode* createForeachLoop(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
|
||||
AstNodeExpr* leftp, AstNodeExpr* rightp, VNType nodeType) {
|
||||
static AstNode* createForeachLoop(AstNodeForeach* /*nodep*/, AstNode* bodysp, bool arrayMayResize,
|
||||
AstNodeExpr* subfromp, AstVar* varp, AstNodeExpr* leftp,
|
||||
AstNodeExpr* rightp, VNType nodeType) {
|
||||
FileLine* const fl = varp->fileline();
|
||||
AstNodeExpr* varRefp = new AstVarRef{fl, varp, VAccess::READ};
|
||||
AstNodeExpr* condp;
|
||||
|
|
@ -522,32 +523,128 @@ static AstNode* createForeachLoop(AstNodeForeach* nodep, AstNode* bodysp, AstVar
|
|||
|
||||
AstLoop* const loopp = new AstLoop{fl};
|
||||
loopp->addStmtsp(new AstLoopTest{fl, loopp, condp});
|
||||
AstVar* sizeVarp = nullptr;
|
||||
AstNodeExpr* sizeGetp = nullptr;
|
||||
if (arrayMayResize) {
|
||||
sizeVarp = new AstVar{fl, VVarType::BLOCKTEMP, varp->name() + "__Vloopsize",
|
||||
varp->findUInt32DType()};
|
||||
sizeVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
sizeVarp->usedLoopIdx(true); // Not technically an index, but used only inside loop
|
||||
varp->addNext(sizeVarp);
|
||||
sizeGetp
|
||||
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), VCMethod::DYN_SIZE, nullptr};
|
||||
sizeGetp->dtypeSetUInt32();
|
||||
loopp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, sizeVarp, VAccess::WRITE}, sizeGetp});
|
||||
}
|
||||
loopp->addStmtsp(bodysp);
|
||||
loopp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp});
|
||||
AstNodeStmt* incStmtp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp};
|
||||
if (arrayMayResize) {
|
||||
incStmtp = new AstIf{fl,
|
||||
new AstLte{fl, new AstVarRef{fl, sizeVarp, VAccess::READ},
|
||||
sizeGetp->cloneTreePure(false)},
|
||||
incStmtp};
|
||||
}
|
||||
loopp->addStmtsp(incStmtp);
|
||||
AstNode* const stmtsp = varp; // New statements for outer loop
|
||||
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, leftp});
|
||||
stmtsp->addNext(loopp);
|
||||
return stmtsp;
|
||||
}
|
||||
static AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
|
||||
static AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp,
|
||||
AstNodeExpr* subfromp, AstVar* varp,
|
||||
const VNumRange& declRange) {
|
||||
FileLine* const fl = varp->fileline();
|
||||
V3Number left{nodep, 32}, right{nodep, 32};
|
||||
V3Number left{nodep, 32};
|
||||
V3Number right{nodep, 32};
|
||||
left.isSigned(true);
|
||||
right.isSigned(true);
|
||||
left.setLongS(declRange.left());
|
||||
right.setLongS(declRange.right());
|
||||
AstNodeExpr* const leftp = new AstConst{fl, left};
|
||||
AstNodeExpr* const rightp = new AstConst{fl, right};
|
||||
return createForeachLoop(nodep, bodysp, varp, leftp, rightp,
|
||||
return createForeachLoop(nodep, bodysp, false /*fixed array size*/, subfromp, varp, leftp,
|
||||
rightp,
|
||||
declRange.left() <= declRange.right() ? VNType::LteS : VNType::GteS);
|
||||
}
|
||||
static AstNode* createForeachAssoc(FileLine* fl, AstVar* varp, AstNodeExpr* subfromp,
|
||||
AstNodeDType* fromDtp, AstNode* bodyPointp) {
|
||||
AstNode* loopp = varp;
|
||||
AstVar* const next_varp // Iterator containing next element (to handle mid-array delete)
|
||||
= new AstVar{fl, VVarType::BLOCKTEMP, varp->name() + "__Vnext", varp};
|
||||
next_varp->usedLoopIdx(true);
|
||||
next_varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
loopp->addNext(next_varp);
|
||||
|
||||
AstVar* const more_varp // bool var. 0 = loop empty/done, 1 = continue with loop
|
||||
= new AstVar{fl, VVarType::BLOCKTEMP, varp->name() + "__Vmore", VFlagBitPacked{}, 1};
|
||||
more_varp->usedLoopIdx(true); // Not technically an index, but used only inside loop
|
||||
more_varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
loopp->addNext(more_varp);
|
||||
|
||||
AstNodeExpr* const firstp
|
||||
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), VCMethod::ASSOC_FIRST,
|
||||
new AstVarRef{fl, next_varp, VAccess::READWRITE}};
|
||||
firstp->dtypeSetSigned32();
|
||||
AstNodeExpr* const nextp
|
||||
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), VCMethod::ASSOC_NEXT,
|
||||
new AstVarRef{fl, next_varp, VAccess::READWRITE}};
|
||||
nextp->dtypeSetSigned32();
|
||||
|
||||
// _Vmore = array.first(__Vnext)
|
||||
loopp->addNext(new AstAssign{fl, new AstVarRef{fl, more_varp, VAccess::WRITE},
|
||||
new AstNeq{fl, new AstConst{fl, 0}, firstp}});
|
||||
|
||||
// LOOP(if (!_Vmore) break; ...)
|
||||
AstLoop* const lp = new AstLoop{fl};
|
||||
loopp->addNext(lp);
|
||||
lp->addStmtsp(new AstLoopTest{fl, lp, new AstVarRef{fl, more_varp, VAccess::READ}});
|
||||
|
||||
// index = __Vnext
|
||||
lp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
||||
new AstVarRef{fl, next_varp, VAccess::READ}});
|
||||
// _Vmore = array.next(__Vnext)
|
||||
lp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, more_varp, VAccess::WRITE},
|
||||
new AstNeq{fl, new AstConst{fl, 0}, nextp}});
|
||||
lp->addStmtsp(bodyPointp);
|
||||
return loopp;
|
||||
}
|
||||
|
||||
static bool arrayMayResizeCheck(AstNode* nodesp, AstVar* fromVarp) {
|
||||
// IEEE 1800-2023 12.7.3 "If the dimensions of a dynamically sized
|
||||
// array are changed while iterating over a foreach-loop construct, th
|
||||
// results are undefined". However UVM calls delete within a foreach
|
||||
// in the uvm_reg code and in the tests, and expects the foreach to see
|
||||
// all elements post-delete.
|
||||
//
|
||||
// Cannot just check body for delete() because the body may call a
|
||||
// function that does a delete. Cannot easily have the verilated_types
|
||||
// code set a flag on delete, because might be multiple foreach in
|
||||
// flight. Could use generation counter and each loop checks, but adds
|
||||
// time to every array modification. So, we assume if the array
|
||||
// shrinks, we should repeat the loop.
|
||||
if (!nodesp) return false;
|
||||
return nodesp->existsAndNext([&](const AstNode* nodep) -> bool {
|
||||
if (const AstNodeVarRef* varrefp = VN_CAST(nodep, NodeVarRef)) {
|
||||
// Any variable written, as might have indirect ref to array
|
||||
if (varrefp->access().isWriteOrRW()) {
|
||||
if (!fromVarp) return true;
|
||||
if (varrefp->varp() == fromVarp) return true;
|
||||
}
|
||||
}
|
||||
// No idea what a function might be doing
|
||||
if (VN_IS(nodep, MethodCall) || VN_IS(nodep, NodeFTaskRef)) return true;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
|
||||
// UINFOTREE(1, nodep, "", "foreach-old");
|
||||
const AstForeachHeader* const headerp = nodep->headerp();
|
||||
AstNodeExpr* const fromp = headerp->fromp();
|
||||
UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type");
|
||||
AstVar* const fromVarp = VN_IS(fromp, VarRef) ? VN_CAST(fromp, VarRef)->varp() : nullptr;
|
||||
AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
|
||||
const bool arrayMayResize = arrayMayResizeCheck(nodep->bodyp(), fromVarp);
|
||||
// Split into for loop
|
||||
// We record where the body needs to eventually go with bodyPointp
|
||||
AstNode* bodyPointp = new AstBegin{nodep->fileline(), "[EditWrapper]", nullptr, false};
|
||||
|
|
@ -573,15 +670,18 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
|
|||
VNRelinker handle;
|
||||
lastp->unlinkFrBack(&handle);
|
||||
if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) {
|
||||
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
|
||||
loopp = createForeachLoopRanged(nodep, bodyPointp, subfromp, varp,
|
||||
adtypep->declRange());
|
||||
} else if (const AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) {
|
||||
if (adtypep->isString()) {
|
||||
AstConst* const leftp = new AstConst{fl, 0};
|
||||
AstNodeExpr* const rightp = new AstLenN{fl, fromp->cloneTreePure(false)};
|
||||
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::Lt);
|
||||
loopp = createForeachLoop(nodep, bodyPointp, arrayMayResize, subfromp, varp,
|
||||
leftp, rightp, VNType::Lt);
|
||||
} else {
|
||||
UASSERT_OBJ(adtypep->isRanged(), varp, "foreach on basic " << adtypep);
|
||||
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
|
||||
loopp = createForeachLoopRanged(nodep, bodyPointp, subfromp, varp,
|
||||
adtypep->declRange());
|
||||
}
|
||||
} else if (VN_IS(fromDtp, DynArrayDType) || VN_IS(fromDtp, QueueDType)) {
|
||||
AstConst* const leftp = new AstConst{fl, 0};
|
||||
|
|
@ -593,50 +693,22 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
|
|||
: subfromp->cloneTreePure(false),
|
||||
VCMethod::DYN_SIZE};
|
||||
AstVarRef* varRefp = new AstVarRef{fl, varp, VAccess::READ};
|
||||
subfromp = new AstCMethodHard{fl, subfromp, VCMethod::ARRAY_AT, varRefp};
|
||||
subfromp->dtypep(fromDtp);
|
||||
rightp->dtypeSetSigned32();
|
||||
rightp->protect(false);
|
||||
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::Lt);
|
||||
loopp = createForeachLoop(nodep, bodyPointp, arrayMayResize, subfromp, varp, leftp,
|
||||
rightp, VNType::Lt);
|
||||
subfromp = new AstCMethodHard{fl, subfromp, VCMethod::ARRAY_AT, varRefp};
|
||||
subfromp->dtypep(fromDtp);
|
||||
} else if (VN_IS(fromDtp, AssocArrayDType)) {
|
||||
// Make this: var KEY_TYPE index;
|
||||
// bit index__Vfirst;
|
||||
// index__Vfirst = 0;
|
||||
// if (0 != array.first(index))
|
||||
// do body while (index__Vfirst || 0 != array.next(index))
|
||||
AstVar* const first_varp = new AstVar{
|
||||
fl, VVarType::BLOCKTEMP, varp->name() + "__Vfirst", VFlagBitPacked{}, 1};
|
||||
first_varp->usedLoopIdx(true);
|
||||
first_varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
AstNodeExpr* const firstp
|
||||
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), VCMethod::ASSOC_FIRST,
|
||||
new AstVarRef{fl, varp, VAccess::READWRITE}};
|
||||
firstp->dtypeSetSigned32();
|
||||
AstNodeExpr* const nextp
|
||||
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), VCMethod::ASSOC_NEXT,
|
||||
new AstVarRef{fl, varp, VAccess::READWRITE}};
|
||||
nextp->dtypeSetSigned32();
|
||||
loopp = createForeachAssoc(fl, varp, subfromp, fromDtp, bodyPointp);
|
||||
AstVarRef* varRefp = new AstVarRef{fl, varp, VAccess::READ};
|
||||
subfromp = new AstCMethodHard{fl, subfromp, VCMethod::ARRAY_AT, varRefp};
|
||||
subfromp->dtypep(fromDtp);
|
||||
AstNode* const first_clearp
|
||||
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitFalse{}}};
|
||||
AstLogOr* const orp
|
||||
= new AstLogOr{fl, new AstVarRef{fl, first_varp, VAccess::READ},
|
||||
new AstNeq{fl, new AstConst{fl, 0}, nextp}};
|
||||
AstLoop* const lp = new AstLoop{fl};
|
||||
lp->addStmtsp(new AstLoopTest{fl, lp, orp});
|
||||
lp->addStmtsp(first_clearp);
|
||||
first_clearp->addNext(bodyPointp);
|
||||
AstNode* const ifbodyp
|
||||
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitTrue{}}};
|
||||
ifbodyp->addNext(lp);
|
||||
loopp = varp;
|
||||
loopp->addNext(first_varp);
|
||||
loopp->addNext(
|
||||
new AstIf{fl, new AstNeq{fl, new AstConst{fl, 0}, firstp}, ifbodyp});
|
||||
}
|
||||
UASSERT_OBJ(loopp, argsp, "unable to foreach " << fromDtp);
|
||||
// New loop goes UNDER previous loop
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ public:
|
|||
void addNewed(const AstNode* nodep) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Called by operator new on any node - only if VL_LEAK_CHECKS
|
||||
// LCOV_EXCL_START
|
||||
V3LockGuard lock{m_mutex};
|
||||
const V3LockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(!m_allocated.emplace(nodep).second)) {
|
||||
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
|
||||
}
|
||||
|
|
@ -80,7 +80,7 @@ public:
|
|||
void deleted(const AstNode* nodep) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Called by operator delete on any node - only if VL_LEAK_CHECKS
|
||||
// LCOV_EXCL_START
|
||||
V3LockGuard lock{m_mutex};
|
||||
const V3LockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(m_allocated.erase(nodep) == 0)) {
|
||||
nodep->v3fatalSrc("Deleting AstNode object that was not allocated or already freed");
|
||||
}
|
||||
|
|
@ -190,8 +190,6 @@ private:
|
|||
}
|
||||
if (v3Global.assertDTypesResolved()) {
|
||||
if (nodep->hasDType()) {
|
||||
UASSERT_OBJ(nodep->dtypep(), nodep,
|
||||
"No dtype on node with hasDType(): " << nodep->prettyTypeName());
|
||||
} else {
|
||||
UASSERT_OBJ(!VN_IS(nodep, NodeExpr), nodep,
|
||||
"All AstNodeExpr must have a dtype post V3WidthCommit");
|
||||
|
|
@ -379,7 +377,7 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
|
|||
} else {
|
||||
s_inBroken = true;
|
||||
|
||||
V3LockGuard lock{s_allocTable.m_mutex};
|
||||
const V3LockGuard lock{s_allocTable.m_mutex};
|
||||
|
||||
// Mark every node in the tree
|
||||
const uint8_t brokenCntCurrent = s_brokenCntGlobal.get();
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ class V3CCtorsBuilder final {
|
|||
funcp->keepIfEmpty(true); // TODO relax
|
||||
funcp->declPrivate(true);
|
||||
funcp->slow(!m_type.isClass()); // Only classes construct on fast path
|
||||
if (!m_type.isCoverage()) m_modp->ctorVarReset(true);
|
||||
string preventUnusedStmt;
|
||||
if (m_type.isClass()) {
|
||||
funcp->argTypes(EmitCUtil::symClassVar());
|
||||
|
|
@ -83,6 +84,7 @@ class V3CCtorsBuilder final {
|
|||
|
||||
public:
|
||||
void add(AstNode* nodep) {
|
||||
if (m_newFunctions.empty()) m_newFunctions.push_back(makeNewFunc());
|
||||
if (v3Global.opt.outputSplitCFuncs() && m_numStmts > v3Global.opt.outputSplitCFuncs()) {
|
||||
m_newFunctions.push_back(makeNewFunc());
|
||||
}
|
||||
|
|
@ -94,13 +96,13 @@ public:
|
|||
: m_modp{nodep}
|
||||
, m_basename{basename}
|
||||
, m_type{type} {
|
||||
// Note: The constructor is always called, even if empty, so we must always create at least
|
||||
// one.
|
||||
m_newFunctions.push_back(makeNewFunc());
|
||||
// Expect coverage function to always exist, so must always create at least one.
|
||||
if (m_type.isCoverage()) m_newFunctions.push_back(makeNewFunc());
|
||||
}
|
||||
|
||||
~V3CCtorsBuilder() {
|
||||
if (m_newFunctions.size() == 1) {
|
||||
if (m_newFunctions.size() == 0) {
|
||||
} else if (m_newFunctions.size() == 1) {
|
||||
// No split was necessary, rename the one function to the basename
|
||||
m_newFunctions.front()->name(m_basename);
|
||||
} else {
|
||||
|
|
@ -136,6 +138,7 @@ class CCtorsVisitor final : public VNVisitor {
|
|||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstCFunc* m_cfuncp = nullptr; // Current function
|
||||
V3CCtorsBuilder* m_varResetp = nullptr; // Builder of _ctor_var_reset
|
||||
std::map<AstCStmt*, const AstNodeModule*> m_ctorCalls; // Calls to _ctor_var_reset
|
||||
|
||||
// METHODS
|
||||
static void insertSc(AstCFunc* cfuncp, const AstNodeModule* modp, VSystemCSectionType type) {
|
||||
|
|
@ -149,6 +152,10 @@ class CCtorsVisitor final : public VNVisitor {
|
|||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
if (const AstClass* const classp = VN_CAST(nodep, Class)) {
|
||||
// Interface class may only have pure virtuals and params which do not need cctor reset
|
||||
if (classp->isInterfaceClass()) return;
|
||||
}
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_varResetp);
|
||||
m_modp = nodep;
|
||||
|
|
@ -190,6 +197,13 @@ class CCtorsVisitor final : public VNVisitor {
|
|||
iterateChildren(nodep);
|
||||
if (nodep->name() == "new") insertSc(nodep, m_modp, VSystemCSectionType::CTOR);
|
||||
}
|
||||
void visit(AstCStmt* nodep) override {
|
||||
if (nodep->stmtType() == VCStmtType::CTOR_VAR_RESET_CALL) {
|
||||
UASSERT_OBJ(m_modp, nodep, "ctor_var_reset call not under module");
|
||||
m_ctorCalls.emplace(nodep, m_modp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstVar* nodep) override {
|
||||
if (nodep->needsCReset()) {
|
||||
AstNode* const crstp = new AstAssign{
|
||||
|
|
@ -199,6 +213,8 @@ class CCtorsVisitor final : public VNVisitor {
|
|||
m_varResetp->add(crstp);
|
||||
} else if (m_cfuncp) {
|
||||
nodep->addNextHere(crstp);
|
||||
} else {
|
||||
nodep->v3fatalSrc("Var needs CReset but nowhere to place it");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -209,7 +225,14 @@ class CCtorsVisitor final : public VNVisitor {
|
|||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CCtorsVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
~CCtorsVisitor() override = default;
|
||||
~CCtorsVisitor() override {
|
||||
// Remove CStmts to ctor_var_resets that are no longer needed
|
||||
for (auto& itr : m_ctorCalls) {
|
||||
AstCStmt* const nodep = itr.first;
|
||||
const AstNodeModule* const modp = itr.second;
|
||||
if (!modp->ctorVarReset()) VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class CUseVisitor final : public VNVisitorConst {
|
|||
iterateAndNextConstNull(nodep->argsp());
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
}
|
||||
void visit(AstCCall* nodep) override { return; }
|
||||
void visit(AstCCall* /*nodep*/) override {}
|
||||
void visit(AstCReturn* nodep) override {
|
||||
UASSERT_OBJ(!nodep->user1SetOnce(), nodep, "Visited same return twice");
|
||||
iterateConst(nodep->lhsp()->dtypep());
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue