Merge from master for release.

This commit is contained in:
Wilson Snyder 2026-04-26 01:57:12 -04:00
commit c233a39052
4069 changed files with 283291 additions and 160250 deletions

View File

@ -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

View File

@ -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 }}

View File

@ -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 }}

15
.github/workflows/no-pulls.yml vendored Normal file
View File

@ -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

View File

@ -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 }}

View File

@ -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 }}

View File

@ -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 }}

View File

@ -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 }}

View File

@ -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

View File

@ -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 }}

View File

@ -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 }}

View File

@ -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
View File

@ -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]

View File

@ -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:

View File

@ -15,6 +15,7 @@ path = [
".style.yapf",
"CITATION.cff",
"CPPLINT.cfg",
"ci/docker/buildenv/wavetools.conf",
"docs/CONTRIBUTORS",
"docs/spelling.txt",
"docs/verilated.dox",

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
WAVETOOLS_VERSION=v0.1.2

View File

@ -22,7 +22,7 @@ RUN apt-get update \
help2man \
libfl2 \
libfl-dev \
libgoogle-perftools-dev \
libjemalloc-dev \
numactl \
perl \
perl-doc \

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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
);

View File

@ -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'

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -1,7 +1,7 @@
.. SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
.. _Environment:
.. _environment:
Environment
===========

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"};

View File

@ -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

View File

@ -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; }

View File

@ -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 $@ $<

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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;

258
include/verilated_force.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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:

View File

@ -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);

View File

@ -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;
}

View File

@ -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 ";

View File

@ -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;

View File

@ -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; }

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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++; }

View File

@ -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;
}
//======================================================================

View File

@ -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}};
}
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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());

View File

@ -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.")

105
nodist/verilator_bisect Executable file
View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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;"

1888
src/V3AssertNfa.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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);

View File

@ -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>(),

View File

@ -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();

View File

@ -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

View File

@ -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); }

View File

@ -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) {

View File

@ -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) {}

View File

@ -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

View File

@ -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 "";
}

View File

@ -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

View File

@ -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();

View File

@ -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);
}
}
};
//######################################################################

View File

@ -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