mirror of
https://github.com/verilator/verilator.git
synced 2026-10-03 16:44:11 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67dfa37c56 | ||
|
|
590188820d | ||
|
|
a764c70b4a | ||
|
|
a27abab65f | ||
|
|
c241ec90ec | ||
|
|
f789d28277 | ||
|
|
c1c8b30a35 | ||
|
|
15046c6c59 | ||
|
|
1da15181da | ||
|
|
3c6b755352 | ||
|
|
3c144ada53 | ||
|
|
7ba6647c4f | ||
|
|
e708670f9a | ||
|
|
de4c6065dc | ||
|
|
d60f180f43 | ||
|
|
30318a6654 | ||
|
|
d8420413b7 | ||
|
|
a5951babfc | ||
|
|
2cba167634 | ||
|
|
89743aae5d | ||
|
|
e6135981a5 | ||
|
|
739521333c | ||
|
|
64af83161a | ||
|
|
98252634fc | ||
|
|
a87fb57656 | ||
|
|
34708bbba1 | ||
|
|
f91259f46d | ||
|
|
cf6e362972 | ||
|
|
95c4ade718 | ||
|
|
84125d7c92 | ||
|
|
17721aff55 | ||
|
|
d1b6224c2b | ||
|
|
1bd31742b9 | ||
|
|
165a2ef1b8 | ||
|
|
774c10396c | ||
|
|
33c5b5feb8 | ||
|
|
b83b9974ee | ||
|
|
bcbe5059a9 | ||
|
|
5bda901146 | ||
|
|
82565690c7 | ||
|
|
978d900e36 | ||
|
|
d330100542 | ||
|
|
4c0edd2efb | ||
|
|
146cdc020d | ||
|
|
52f2b9ef58 | ||
|
|
a09506a0ad | ||
|
|
10d33238b9 | ||
|
|
b78ea06829 | ||
|
|
eae942b91c | ||
|
|
1075b006b5 | ||
|
|
afaa02709c | ||
|
|
332a687aef | ||
|
|
d1c7875406 | ||
|
|
cbc2f9eb32 | ||
|
|
26e3785963 | ||
|
|
f8b7fb72b8 | ||
|
|
7b12f6a1dd | ||
|
|
c7a0613c57 | ||
|
|
83a0085c4d | ||
|
|
05bb7fa821 | ||
|
|
4cc42133b5 | ||
|
|
a773a52559 | ||
|
|
5af271cf3a | ||
|
|
0c2bab1f69 | ||
|
|
b7233d063f | ||
|
|
8720841c48 | ||
|
|
263697e491 | ||
|
|
493f1da266 | ||
|
|
8b44a54bb2 | ||
|
|
b5828a7ce9 | ||
|
|
51a18225ad | ||
|
|
28625451dd | ||
|
|
3914176bc4 | ||
|
|
13989b965d | ||
|
|
770cd24f27 | ||
|
|
431bb1ed16 | ||
|
|
3bb9c7ee92 | ||
|
|
45edcbb03e | ||
|
|
e99fd2892b | ||
|
|
99ea16d7fd | ||
|
|
bc9ff6d1bf | ||
|
|
ad3bcbb1bb | ||
|
|
25dde58297 | ||
|
|
bba3487dc4 | ||
|
|
4fdaa46328 | ||
|
|
4427f03b08 | ||
|
|
4e2c63c8cb | ||
|
|
afecde87d8 | ||
|
|
c14eae6d56 | ||
|
|
684aba0e90 | ||
|
|
46f8a659b3 | ||
|
|
3940a214d0 | ||
|
|
411f563cbe | ||
|
|
b5a6b34968 | ||
|
|
fa8eaf7355 | ||
|
|
7eb09c3445 | ||
|
|
edfd3d7206 | ||
|
|
d699caf255 | ||
|
|
bd4eede6b4 | ||
|
|
1fd8e772b0 | ||
|
|
7ce0bd1181 | ||
|
|
a4b7c39a9a | ||
|
|
a5ee8b39ef | ||
|
|
5b06c60318 | ||
|
|
b306715b4a | ||
|
|
8d99bdac25 | ||
|
|
e114dfda6d | ||
|
|
472ad90d83 | ||
|
|
622f0c047c | ||
|
|
7f38414276 | ||
|
|
1d3d59243c | ||
|
|
e2343c03e0 | ||
|
|
0e0df1fb3b | ||
|
|
800a789f50 | ||
|
|
106664b942 | ||
|
|
1c738c6b83 | ||
|
|
25fa1d7c6a | ||
|
|
45162652de | ||
|
|
537650a2cd | ||
|
|
f849073137 | ||
|
|
a3c154dcd3 | ||
|
|
1b8228b642 | ||
|
|
732d03f4e5 | ||
|
|
33787c6add | ||
|
|
44e7d2ebe6 | ||
|
|
e49ae663a6 | ||
|
|
2385ab8294 | ||
|
|
84ccb23098 | ||
|
|
ad343f8260 | ||
|
|
018d766179 | ||
|
|
a948be184c | ||
|
|
72b4a74245 | ||
|
|
36c824d973 | ||
|
|
229ce1aecf | ||
|
|
be45a9b7d5 | ||
|
|
d2c72a7f21 | ||
|
|
60e9827ffe | ||
|
|
a9256d4758 | ||
|
|
750b5be5d3 | ||
|
|
28bd7e5b19 | ||
|
|
d64c023099 | ||
|
|
429cb1f7f7 | ||
|
|
9f75068059 | ||
|
|
4636e9cffb | ||
|
|
4b2bf556d8 | ||
|
|
a16ea94836 | ||
|
|
b9ea249f81 | ||
|
|
470f480bc1 | ||
|
|
24ff3155ae | ||
|
|
47b3f464a9 | ||
|
|
cd201f8eb8 | ||
|
|
016278a759 | ||
|
|
b68f101e81 | ||
|
|
ab13548018 | ||
|
|
f507ee56b0 | ||
|
|
761adf1cf0 | ||
|
|
cd1fe64e6c | ||
|
|
8f43e74288 | ||
|
|
cef3960a4e | ||
|
|
783b7ecdab | ||
|
|
8bd1c63b32 | ||
|
|
e6fb7e970d | ||
|
|
891cc0f9b6 | ||
|
|
10fbe74cef | ||
|
|
05d04a3959 | ||
|
|
19f7279542 | ||
|
|
aa608472ae | ||
|
|
26ed2a06a0 | ||
|
|
07013da13d | ||
|
|
d840c612d4 | ||
|
|
188cf85b3f | ||
|
|
c52ba28dd0 | ||
|
|
10c1653e72 | ||
|
|
2fbd41e13b | ||
|
|
96857c5f95 | ||
|
|
131eb319e0 | ||
|
|
6e589377f4 | ||
|
|
2c84b37bf7 | ||
|
|
0d67caff77 | ||
|
|
3dde57d539 | ||
|
|
ec2e3ec0e4 | ||
|
|
9fe459c820 | ||
|
|
823e0723fb | ||
|
|
8bd6d7c5b1 | ||
|
|
839a8fa4d9 | ||
|
|
24917a187a | ||
|
|
6c6f03cf7c | ||
|
|
5676443139 | ||
|
|
e77d847671 | ||
|
|
0baee84e96 | ||
|
|
c446cc2596 | ||
|
|
b66c4153b1 | ||
|
|
d72f1b89fc | ||
|
|
b5b278d072 | ||
|
|
cb5466a90b | ||
|
|
4de25369da | ||
|
|
cdd8953085 | ||
|
|
2f2f0164ff | ||
|
|
6e977e1024 | ||
|
|
70b11f91b4 | ||
|
|
014301587f | ||
|
|
139e93d371 | ||
|
|
fb1fc46b06 | ||
|
|
9882ab6c67 | ||
|
|
1a1f919882 | ||
|
|
91227d26bb | ||
|
|
11b5dae88d | ||
|
|
0c7d485417 | ||
|
|
c40e34b134 | ||
|
|
6cdf8da3f7 | ||
|
|
c65265477f | ||
|
|
de77f1ee5c | ||
|
|
c8c980c49d | ||
|
|
ad329612fd | ||
|
|
c324895cbc | ||
|
|
679a4314dc | ||
|
|
36932d31c4 | ||
|
|
ed25aaaad9 | ||
|
|
147aa41ba5 | ||
|
|
5b7e32581b | ||
|
|
2f90fb2bec | ||
|
|
8c480fd39e | ||
|
|
ffbbd438ae | ||
|
|
96ee81fa3f | ||
|
|
3d2399ea0f | ||
|
|
6d5dde8645 | ||
|
|
048c9f3fe2 | ||
|
|
373265752f | ||
|
|
5e6519c1b0 | ||
|
|
ccdb52c1d9 | ||
|
|
b4b74d72f0 | ||
|
|
ca6ab584d0 | ||
|
|
799a502cac | ||
|
|
8d1570db28 | ||
|
|
2a30bbe493 | ||
|
|
cf6566b9bc | ||
|
|
c3e19f2821 | ||
|
|
e24197fd16 | ||
|
|
63db60f646 | ||
|
|
e66b28823d | ||
|
|
77bd565ef6 | ||
|
|
00d63883f8 | ||
|
|
10dd675371 | ||
|
|
2daa32b98b | ||
|
|
e8e7912871 | ||
|
|
90079c2974 | ||
|
|
967a8530dd | ||
|
|
ef4794e36d | ||
|
|
7c7c92d2dd | ||
|
|
95f3dd0535 | ||
|
|
768b78e7d0 | ||
|
|
4370254e73 | ||
|
|
436f72582b | ||
|
|
af3aba7821 | ||
|
|
7479db6ffc | ||
|
|
e9cc2786b7 | ||
|
|
cbdee5a804 | ||
|
|
0f66262fa1 | ||
|
|
6c80457262 | ||
|
|
16008acdf1 | ||
|
|
5447ed2629 | ||
|
|
9eba61018a | ||
|
|
bc97319402 | ||
|
|
709db5c648 | ||
|
|
b752faa107 | ||
|
|
8d512c3187 | ||
|
|
2d9bc73709 | ||
|
|
b0942ed8c7 | ||
|
|
b1c1aa53a9 |
@@ -63,6 +63,8 @@ ForEachMacros:
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '"V3Pch.*\.h"'
|
||||
Priority: -2 # Precompiled headers
|
||||
- Regex: '"(config_build|verilated_config|verilatedos)\.h"'
|
||||
Priority: -1 # Sepecials before main header
|
||||
- Regex: '(<|")verilated.*'
|
||||
@@ -112,6 +114,9 @@ SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 0
|
||||
Maximum: -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp11
|
||||
|
||||
@@ -23,6 +23,10 @@ defaults:
|
||||
shell: bash
|
||||
working-directory: repo
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
@@ -33,7 +37,8 @@ jobs:
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
m32: [0, 1]
|
||||
# m32 1 is deprecated, not here to speed up regressions
|
||||
m32: [0]
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-22.04 and without m32
|
||||
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
@@ -58,7 +63,7 @@ jobs:
|
||||
CC: ${{ matrix.compiler.cc }}
|
||||
CXX: ${{ matrix.compiler.cxx }}
|
||||
CACHE_BASE_KEY: build-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}
|
||||
CCACHE_MAXSIZE: 250M # Per build matrix entry (2000M in total)
|
||||
CCACHE_MAXSIZE: 1000M # Per build matrix entry (* 5 = 5000M in total)
|
||||
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}${{ matrix.m32 && '-m32' || '' }}.tar.gz
|
||||
steps:
|
||||
|
||||
@@ -103,7 +108,7 @@ jobs:
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
m32: [0, 1]
|
||||
m32: [0]
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-22.04 and without m32
|
||||
@@ -131,7 +136,7 @@ jobs:
|
||||
CC: ${{ matrix.compiler.cc }}
|
||||
CXX: ${{ matrix.compiler.cxx }}
|
||||
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}-${{ matrix.suite }}
|
||||
CCACHE_MAXSIZE: 64M # Per build matrix entry (2160M in total)
|
||||
CCACHE_MAXSIZE: 100M # Per build per suite (* 5 * 5 = 2500M in total)
|
||||
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}${{ matrix.m32 && '-m32' || '' }}.tar.gz
|
||||
steps:
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Build and push verilator docker image when tags are pushed to the repository.
|
||||
# The following secrets must be configured in the github repository:
|
||||
# The following variable(s) must be configured in the github repository:
|
||||
# DOCKER_HUB_NAMESPACE: docker hub namespace.
|
||||
# The following secrets must be configured in the github repository:
|
||||
# DOCKER_HUB_USER: user name for logging into docker hub
|
||||
# DOCKER_HUB_ACCESS_TOKEN: docker hub access token.
|
||||
name: Build Verilator Container
|
||||
@@ -52,7 +53,7 @@ jobs:
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
${{ secrets.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
||||
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
||||
tags: |
|
||||
type=match,pattern=(v.*),group=1,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
type=raw,value=${{ inputs.manual_tag }},enable=${{ inputs.manual_tag != '' }}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# DESCRIPTION: Github actions config
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: lint
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # weekly
|
||||
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CI_COMMIT: ${{ github.sha }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: repo
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
lint-py:
|
||||
runs-on: ubuntu-22.04
|
||||
name: Lint Python
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
CI_RUNS_ON: ubuntu-22.04
|
||||
CI_M32: 0
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: repo
|
||||
|
||||
- name: Install packages for build
|
||||
run: ./ci/ci-install.bash
|
||||
|
||||
# We use specific version numbers, otherwise a Python package
|
||||
# update may add a warning and break our build
|
||||
- name: Install packages for lint
|
||||
run: sudo pip3 install pylint==3.0.2 ruff==0.1.3 clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
|
||||
|
||||
- name: Configure
|
||||
run: autoconf && ./configure --enable-longtests --enable-ccwarn
|
||||
|
||||
- name: Lint
|
||||
run: make -k lint-py
|
||||
@@ -22,6 +22,10 @@ defaults:
|
||||
run:
|
||||
working-directory: repo
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
windows:
|
||||
|
||||
@@ -41,3 +41,6 @@ verilator-config-version.cmake
|
||||
**/_build/*
|
||||
**/obj_dir/*
|
||||
/.vscode/
|
||||
/.idea/
|
||||
/cmake-build-*/
|
||||
/test_regress/snapshot/
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||
project(Verilator
|
||||
VERSION 5.014
|
||||
VERSION 5.018
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
@@ -8,6 +8,133 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.018 2023-10-30
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* Support compilation with precompiled headers with Make and GCC or CLang.
|
||||
* Change include of systemc instead of systemc.h (#4622) (#4623). [Chih-Mao Chen]
|
||||
This may require that SystemC programs add 'using namespace sc_core', 'using namespace sc_dt'.
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add SIDEEFFECT warning on mishandled side effect cases.
|
||||
* Add trace() API even when Verilated without --trace (#4462). [phelter]
|
||||
* Add warning on interface instantiation without parens (#4094). [Gökçe Aydos]
|
||||
* Add sv_vpi_user.h from IEEE 1800-2017 Annex M (#4606). [Marlon James]
|
||||
* Support 'disable fork' (#4125) (#4569). [Aleksander Kiryk, Antmicro Ltd.]
|
||||
* Support 'wait fork' (#4586). [Aleksander Kiryk, Antmicro Ltd.]
|
||||
* Support 'randc' (#4349).
|
||||
* Support assigning events (#4403). [Krzysztof Boroński]
|
||||
* Support resizing function call inout arguments (#4467).
|
||||
* Support NBAs in non-inlined functions/tasks (#4496) (#4572). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Support converting parameters inside modules to localparams (#4511). [Anthony Donlon]
|
||||
* Support concatenation of unpacked arrays (#4558). [Yutetsu TAKATSUKASA]
|
||||
* Support Clang 16 (#4592). [Mariusz Glebocki]
|
||||
* Support VPI variables of real and string data types (#4594). [Marlon James]
|
||||
* Support making VL_LOCK_SPINS configurable (#4599). [Geza Lore]
|
||||
* Change code --stats output (#4597). [Geza Lore]
|
||||
* Change --prof-exec infrastructure and report (#4602). [Geza Lore]
|
||||
* Change lint_off to not propagate upwards to files including where the lint_off is.
|
||||
* Optimize empty expression statements (#4544).
|
||||
* Optimize trace internals (#4610) (#4612). [Geza Lore]
|
||||
* Optimize internal performance issues (#4638). [Geza Lore]
|
||||
* Fix conversion of impure logical expressions to bit expressions (#487 partial) (#4437). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix enum functions in localparams (#3999). [Andrew Nolte]
|
||||
* Fix passing arguments by reference (#3385 partial) (#4489). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix multithreading handling to separate by code units that use/never use it (#4228). [Mariusz Glebocki, Antmicro Ltd.]
|
||||
* Fix usage of annotation options (#4486) (#4504). [Michal Czyz]
|
||||
* Fix detecting local vars in nested forks (#4493) (#4506). [Kamil Rakoczy]
|
||||
* Fix handling input file path separator (#4515) (#4516). [Anthony Donlon]
|
||||
* Fix mis-support for parameterized UDPs (#4518). [Anthony Donlon]
|
||||
* Fix constant conversion of $realtobits, $bitstoreal (#4522). [Andrew Nolte]
|
||||
* Fix conversion of integers in $display '%e' (#4528). [muzafferkal]
|
||||
* Fix non-inlined interface tracing (#3984) (#4530). [Todd Strader]
|
||||
* Fix stream operations with operands of struct type (#4531) (#4532). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix 'this' in a constructor (#4533). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix stream shift operator of 32 bits (#4536). [Julien Faucher]
|
||||
* Fix object destruction after a copy constructor (#4540) (#4541). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix inlining of real functions miscasting (#4543). [Andrew Nolte]
|
||||
* Fix broken link error for enum references (#4551). [Anthony Donlon]
|
||||
* Fix logical expressions with class objects - caching in v3Const (#4552). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix using functions/tasks following class definition inside module (#4553). [Anthony Donlon]
|
||||
* Fix large constant buffer overflow (#4556). [Varun Koyyalagunta]
|
||||
* Fix instance arrays connecting to array of structs (#4557). [raphmaster]
|
||||
* Fix error message for invalid parameter overrides (#4559). [Anthony Donlon]
|
||||
* Fix shift to remove operation side effects (#4563).
|
||||
* Fix compile warning on unused member function variable (#4567).
|
||||
* Fix method narrowing conversion compiler error (#4568).
|
||||
* Fix interface comparison (#4570). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix dynamic triggers for named events (#4571). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix dictionaries with keys of class types (#4576). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix to not remap local assign intervals in forks (#4583). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix display optimization ignoring side effects (#4585).
|
||||
* Fix PLI/DPI user defined system task/function grammar (#4587) (#4588). [Quentin Corradi]
|
||||
* Fix fault on empty clocking block (#4593). [Alex Mykyta]
|
||||
* Fix creating implicit nets for inputs of gate primitives (#4603). [Geza Lore]
|
||||
* Fix try_put method of unbounded mailbox (#4608). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix stable name generation in V3Fork (#4615) (#4624). [Krzysztof Boroński]
|
||||
* Fix virtual methods (#4616). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix insertion at queue end (#4619). [Krzysztof Boroński]
|
||||
* Fix rand fields of reference types (#4627). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix dynamic casts of null values (#4631). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix signals read via virtual interfaces being misoptimized (#4645). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
* Fix handling of static keyword in methods (#4649). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Fix preprocessor to show `line 2 on resumed file.
|
||||
|
||||
|
||||
Verilator 5.016 2023-09-16
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add prepareClone and atClone APIs for Verilated models (#3503) (#4444). [Yinan Xu]
|
||||
* Add check for conflicting options e.g. binary and lint-only (#4409). [Ethan Sifferman]
|
||||
* Add --no-trace-top to not trace top signals (#4412) (#4422). [Frans Skarman]
|
||||
* Support recursive function calls (#3267).
|
||||
* Support assignments of packed values to stream expressions on queues (#4401). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support no-parentheses calls to static methods (#4432). [Krzysztof Boroński]
|
||||
* Support block_item_declaration in forks (#4455). [Krzysztof Boroński]
|
||||
* Support assignments of stream expressions on queues to packed values (#4458). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support function non-constant default arguments (#4470).
|
||||
* Support 'let'.
|
||||
* Optimize Verilator executable size by refactoring error reporting routines (#4446). [Anthony Donlon]
|
||||
* Optimize Verilation runtime pointers and graphs (#4396) (#4397) (#4398). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Optimize preparations towards multithreaded Verilation (#4291) (#4463) (#4476) (#4477) (#4479). [Kamil Rakoczy, Antmicro Ltd]
|
||||
* Fix Windows filename format, etc (#3873) (#4421). [Anthony Donlon].
|
||||
* Fix t_dist_cppstyle Perl performance issue (#4085). [Srinivasan Venkataramanan]
|
||||
* Fix using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon]
|
||||
* Fix false INFINITELOOP on forever..mailbox.get() (#4323). [Srinivasan Venkataramanan]
|
||||
* Fix data type of condition operation on class objects (#4345) (#4352). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix variables mutated under fork..join_none/join_any blocks into anonymous objects (#4356). [Krzysztof Boroński]
|
||||
* Fix V3CUse, do not consider implementations (.cpp) at all (#4386). [Krzysztof Boroński]
|
||||
* Fix ++/-- under statements (#4399). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix detection of mixed blocking and nonblocking assignment in nested assignments (#4404). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix jumping over object initialization (#4411). [Krzysztof Boroński]
|
||||
* Fix multiple issues towards short circuit support (#4413) (#4460). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix variable lifetimes in extern methods (#4414). [Krzysztof Boroński]
|
||||
* Fix multiple function definitions in V3Sched (#4416). [Hennadii Chernyshchyk]
|
||||
* Fix false UNUSEDPARAM on generate localparam (#4427). [Bill Pringlemeir]
|
||||
* Fix checking for parameter and port connections in the wrong place (#4428). [Anthony Donlon]
|
||||
* Fix coroutine handle movement during queue manipulation (#4431). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix nested assignments on the LHS (#4435). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix false MULTITOP on bound interfaces (#4438). [Alex Solomatnikov]
|
||||
* Fix internal error on real conversion (#4447). [vdhotre-ventana]
|
||||
* Fix lifetime unknown error on enum.name (#4448). [jwoutersymatra]
|
||||
* Fix unstable output of VHashSha256 (#4453). [Anthony Donlon]
|
||||
* Fix static cast from a stream type (#4469) (#4485). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix error on enum with VARHIDDEN of cell (#4482). [Michail Rontionov]
|
||||
* Fix lint of case statements with enum and wildcard bits (#4464) (#4487). [Anthony Donlon]
|
||||
* Fix reference to extended class in parameterized class (#4466).
|
||||
* Fix recursive display causing segfault (#4480). [Kuoping Hsu]
|
||||
* Fix the error message when the type of ref argument is wrong (#4490). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix display %x formatting of real.
|
||||
* Fix mis-warning on #() in classes' own functions.
|
||||
* Fix IGNOREDRETURN to not warn on void-cast static function calls.
|
||||
* Fix ZERODLY to not warn on 'wait(0)'.
|
||||
|
||||
|
||||
Verilator 5.014 2023-08-06
|
||||
==========================
|
||||
|
||||
|
||||
+12
-2
@@ -173,6 +173,10 @@ smoke-test: all_nomsg
|
||||
test_regress: all_nomsg
|
||||
$(MAKE) -C test_regress
|
||||
|
||||
.PHONY: test-snap test-diff
|
||||
test-snap test-diff:
|
||||
$(MAKE) -C test_regress $@
|
||||
|
||||
examples: all_nomsg
|
||||
for p in $(EXAMPLES) ; do \
|
||||
$(MAKE) -C $$p VERILATOR_ROOT=`pwd` || exit 10; \
|
||||
@@ -430,9 +434,15 @@ PYLINT_FLAGS = --score=n --disable=R0801
|
||||
RUFF = ruff
|
||||
RUFF_FLAGS = check --ignore=E402,E501,E701
|
||||
|
||||
# "make -k" so can see all tool result errors
|
||||
lint-py:
|
||||
-$(PYLINT) $(PYLINT_FLAGS) $(PY_PROGRAMS)
|
||||
-$(RUFF) $(RUFF_FLAGS) $(PY_PROGRAMS)
|
||||
$(MAKE) -k lint-py-pylint lint-py-ruff
|
||||
|
||||
lint-py-pylint:
|
||||
$(PYLINT) $(PYLINT_FLAGS) $(PY_PROGRAMS)
|
||||
|
||||
lint-py-ruff:
|
||||
$(RUFF) $(RUFF_FLAGS) $(PY_PROGRAMS)
|
||||
|
||||
format-pl-exec:
|
||||
-chmod a+x test_regress/t/*.pl
|
||||
|
||||
+1
-1
@@ -150,7 +150,7 @@ the terms of either the GNU Lesser General Public License Version 3 or the
|
||||
Perl Artistic License Version 2.0. See the documentation for more details.
|
||||
|
||||
.. _CHIPS Alliance: https://chipsalliance.org
|
||||
.. _Icarus Verilog: http://iverilog.icarus.com
|
||||
.. _Icarus Verilog: https://steveicarus.github.io/iverilog
|
||||
.. _Linux Foundation: https://www.linuxfoundation.org
|
||||
.. |Logo| image:: https://www.veripool.org/img/verilator_256_200_min.png
|
||||
.. |verilator multithreaded performance| image:: https://www.veripool.org/img/verilator_multithreaded_performance_bg-min.png
|
||||
|
||||
@@ -440,6 +440,7 @@ detailed descriptions of these arguments.
|
||||
--trace-params Enable tracing of parameters
|
||||
--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
|
||||
-U<var> Undefine preprocessor define
|
||||
--no-unlimited-stack Don't disable stack size limit
|
||||
|
||||
+339
-276
@@ -1,33 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0103,C0114,C0116,C0209,C0301,R0914,R0912,R0915,W0511,eval-used
|
||||
# pylint: disable=C0103,C0114,C0116,C0209,C0301,R0914,R0912,R0915,W0511,W0603,eval-used
|
||||
######################################################################
|
||||
|
||||
import argparse
|
||||
import bisect
|
||||
import collections
|
||||
import math
|
||||
import re
|
||||
import statistics
|
||||
# from pprint import pprint
|
||||
|
||||
Threads = collections.defaultdict(lambda: collections.defaultdict(lambda: {}))
|
||||
Mtasks = collections.defaultdict(lambda: {})
|
||||
Evals = collections.defaultdict(lambda: {})
|
||||
EvalLoops = collections.defaultdict(lambda: {})
|
||||
Sections = []
|
||||
LongestVcdStrValueLength = 0
|
||||
Threads = collections.defaultdict(lambda: []) # List of records per thread id
|
||||
Mtasks = collections.defaultdict(lambda: {'elapsed': 0, 'end': 0})
|
||||
Cpus = collections.defaultdict(lambda: {'mtask_time': 0})
|
||||
Global = {
|
||||
'args': {},
|
||||
'cpuinfo': collections.defaultdict(lambda: {}),
|
||||
'rdtsc_cycle_time': 0,
|
||||
'stats': {}
|
||||
}
|
||||
ElapsedTime = None # total elapsed time
|
||||
ExecGraphTime = 0 # total elapsed time excuting an exec graph
|
||||
ExecGraphIntervals = [] # list of (start, end) pairs
|
||||
|
||||
######################################################################
|
||||
|
||||
|
||||
def process(filename):
|
||||
read_data(filename)
|
||||
report()
|
||||
|
||||
|
||||
def read_data(filename):
|
||||
with open(filename, "r", encoding="utf8") as fh:
|
||||
re_thread = re.compile(r'^VLPROFTHREAD (\d+)$')
|
||||
@@ -39,14 +38,17 @@ def read_data(filename):
|
||||
re_arg1 = re.compile(r'VLPROF arg\s+(\S+)\+([0-9.]*)\s*')
|
||||
re_arg2 = re.compile(r'VLPROF arg\s+(\S+)\s+([0-9.]*)\s*$')
|
||||
re_stat = re.compile(r'VLPROF stat\s+(\S+)\s+([0-9.]+)')
|
||||
re_time = re.compile(r'rdtsc time = (\d+) ticks')
|
||||
re_proc_cpu = re.compile(r'VLPROFPROC processor\s*:\s*(\d+)\s*$')
|
||||
re_proc_dat = re.compile(r'VLPROFPROC ([a-z_ ]+)\s*:\s*(.*)$')
|
||||
cpu = None
|
||||
thread = None
|
||||
execGraphStart = None
|
||||
|
||||
lastEvalBeginTick = None
|
||||
lastEvalLoopBeginTick = None
|
||||
global LongestVcdStrValueLength
|
||||
global ExecGraphTime
|
||||
|
||||
SectionStack = []
|
||||
mTaskThread = {}
|
||||
|
||||
for line in fh:
|
||||
recordMatch = re_record.match(line)
|
||||
@@ -54,29 +56,31 @@ def read_data(filename):
|
||||
kind, tick, payload = recordMatch.groups()
|
||||
tick = int(tick)
|
||||
payload = payload.strip()
|
||||
if kind == "EVAL_BEGIN":
|
||||
Evals[tick]['start'] = tick
|
||||
lastEvalBeginTick = tick
|
||||
elif kind == "EVAL_END":
|
||||
Evals[lastEvalBeginTick]['end'] = tick
|
||||
lastEvalBeginTick = None
|
||||
elif kind == "EVAL_LOOP_BEGIN":
|
||||
EvalLoops[tick]['start'] = tick
|
||||
lastEvalLoopBeginTick = tick
|
||||
elif kind == "EVAL_LOOP_END":
|
||||
EvalLoops[lastEvalLoopBeginTick]['end'] = tick
|
||||
lastEvalLoopBeginTick = None
|
||||
if kind == "SECTION_PUSH":
|
||||
LongestVcdStrValueLength = max(LongestVcdStrValueLength,
|
||||
len(payload))
|
||||
SectionStack.append(payload)
|
||||
Sections.append((tick, tuple(SectionStack)))
|
||||
elif kind == "SECTION_POP":
|
||||
assert SectionStack, "SECTION_POP without SECTION_PUSH"
|
||||
SectionStack.pop()
|
||||
Sections.append((tick, tuple(SectionStack)))
|
||||
elif kind == "MTASK_BEGIN":
|
||||
mtask, predict_start, ecpu = re_payload_mtaskBegin.match(
|
||||
payload).groups()
|
||||
mtask = int(mtask)
|
||||
predict_start = int(predict_start)
|
||||
ecpu = int(ecpu)
|
||||
Threads[thread][tick]['mtask'] = mtask
|
||||
Threads[thread][tick]['predict_start'] = predict_start
|
||||
Threads[thread][tick]['cpu'] = ecpu
|
||||
if 'elapsed' not in Mtasks[mtask]:
|
||||
Mtasks[mtask] = {'end': 0, 'elapsed': 0}
|
||||
mTaskThread[mtask] = thread
|
||||
records = Threads[thread]
|
||||
assert not records or records[-1]['start'] <= records[-1][
|
||||
'end'] <= tick
|
||||
records.append({
|
||||
'start': tick,
|
||||
'mtask': mtask,
|
||||
'predict_start': predict_start,
|
||||
'cpu': ecpu
|
||||
})
|
||||
Mtasks[mtask]['begin'] = tick
|
||||
Mtasks[mtask]['thread'] = thread
|
||||
Mtasks[mtask]['predict_start'] = predict_start
|
||||
@@ -86,11 +90,18 @@ def read_data(filename):
|
||||
mtask = int(mtask)
|
||||
predict_cost = int(predict_cost)
|
||||
begin = Mtasks[mtask]['begin']
|
||||
Threads[thread][begin]['end'] = tick
|
||||
Threads[thread][begin]['predict_cost'] = predict_cost
|
||||
record = Threads[mTaskThread[mtask]][-1]
|
||||
record['end'] = tick
|
||||
record['predict_cost'] = predict_cost
|
||||
Mtasks[mtask]['elapsed'] += tick - begin
|
||||
Mtasks[mtask]['predict_cost'] = predict_cost
|
||||
Mtasks[mtask]['end'] = max(Mtasks[mtask]['end'], tick)
|
||||
elif kind == "EXEC_GRAPH_BEGIN":
|
||||
execGraphStart = tick
|
||||
elif kind == "EXEC_GRAPH_END":
|
||||
ExecGraphTime += tick - execGraphStart
|
||||
ExecGraphIntervals.append((execGraphStart, tick))
|
||||
execGraphStart = None
|
||||
elif Args.debug:
|
||||
print("-Unknown execution trace record: %s" % line)
|
||||
elif re_thread.match(line):
|
||||
@@ -109,7 +120,7 @@ def read_data(filename):
|
||||
elif re_proc_cpu.match(line):
|
||||
match = re_proc_cpu.match(line)
|
||||
cpu = int(match.group(1))
|
||||
elif cpu and re_proc_dat.match(line):
|
||||
elif cpu is not None and re_proc_dat.match(line):
|
||||
match = re_proc_dat.match(line)
|
||||
term = match.group(1)
|
||||
value = match.group(2)
|
||||
@@ -121,11 +132,6 @@ def read_data(filename):
|
||||
pass
|
||||
elif Args.debug:
|
||||
print("-Unk: %s" % line)
|
||||
# TODO -- this is parsing text printed by a client.
|
||||
# Really, verilator proper should generate this
|
||||
# if it's useful...
|
||||
if re_time.match(line):
|
||||
Global['rdtsc_cycle_time'] = re_time.group(1)
|
||||
|
||||
|
||||
def re_match_result(regexp, line, result_to):
|
||||
@@ -144,125 +150,33 @@ def report():
|
||||
plus = "+" if re.match(r'^\+', arg) else " "
|
||||
print(" %s%s%s" % (arg, plus, Global['args'][arg]))
|
||||
|
||||
for records in Threads.values():
|
||||
for record in records:
|
||||
cpu = record['cpu']
|
||||
elapsed = record['end'] - record['start']
|
||||
Cpus[cpu]['mtask_time'] += elapsed
|
||||
|
||||
global ElapsedTime
|
||||
ElapsedTime = int(Global['stats']['ticks'])
|
||||
nthreads = int(Global['stats']['threads'])
|
||||
Global['cpus'] = {}
|
||||
for thread in Threads:
|
||||
# Make potentially multiple characters per column
|
||||
for start in Threads[thread]:
|
||||
if not Threads[thread][start]:
|
||||
continue
|
||||
cpu = Threads[thread][start]['cpu']
|
||||
elapsed = Threads[thread][start]['end'] - start
|
||||
if cpu not in Global['cpus']:
|
||||
Global['cpus'][cpu] = {'cpu_time': 0}
|
||||
Global['cpus'][cpu]['cpu_time'] += elapsed
|
||||
ncpus = max(len(Cpus), 1)
|
||||
|
||||
measured_mt_mtask_time = 0
|
||||
predict_mt_mtask_time = 0
|
||||
long_mtask_time = 0
|
||||
measured_last_end = 0
|
||||
predict_last_end = 0
|
||||
for mtask in Mtasks:
|
||||
measured_mt_mtask_time += Mtasks[mtask]['elapsed']
|
||||
predict_mt_mtask_time += Mtasks[mtask]['predict_cost']
|
||||
measured_last_end = max(measured_last_end, Mtasks[mtask]['end'])
|
||||
predict_last_end = max(
|
||||
predict_last_end,
|
||||
Mtasks[mtask]['predict_start'] + Mtasks[mtask]['predict_cost'])
|
||||
long_mtask_time = max(long_mtask_time, Mtasks[mtask]['elapsed'])
|
||||
Global['measured_last_end'] = measured_last_end
|
||||
Global['predict_last_end'] = predict_last_end
|
||||
|
||||
# If we know cycle time in the same (rdtsc) units,
|
||||
# this will give us an actual utilization number,
|
||||
# (how effectively we keep the cores busy.)
|
||||
#
|
||||
# It also gives us a number we can compare against
|
||||
# serial mode, to estimate the overhead of data sharing,
|
||||
# which will show up in the total elapsed time. (Overhead
|
||||
# of synchronization and scheduling should not.)
|
||||
print("\nAnalysis:")
|
||||
print(" Total threads = %d" % nthreads)
|
||||
print(" Total mtasks = %d" % len(Mtasks))
|
||||
ncpus = max(len(Global['cpus']), 1)
|
||||
print(" Total cpus used = %d" % ncpus)
|
||||
print(" Total yields = %d" %
|
||||
int(Global['stats'].get('yields', 0)))
|
||||
print(" Total evals = %d" % len(Evals))
|
||||
print(" Total eval loops = %d" % len(EvalLoops))
|
||||
if Mtasks:
|
||||
print(" Total eval time = %d rdtsc ticks" %
|
||||
Global['measured_last_end'])
|
||||
print(" Longest mtask time = %d rdtsc ticks" % long_mtask_time)
|
||||
print(" All-thread mtask time = %d rdtsc ticks" %
|
||||
measured_mt_mtask_time)
|
||||
long_efficiency = long_mtask_time / (Global.get(
|
||||
'measured_last_end', 1) or 1)
|
||||
print(" Longest-thread efficiency = %0.1f%%" %
|
||||
(long_efficiency * 100.0))
|
||||
mt_efficiency = measured_mt_mtask_time / (
|
||||
Global.get('measured_last_end', 1) * nthreads or 1)
|
||||
print(" All-thread efficiency = %0.1f%%" %
|
||||
(mt_efficiency * 100.0))
|
||||
print(" All-thread speedup = %0.1f" %
|
||||
(mt_efficiency * nthreads))
|
||||
if Global['rdtsc_cycle_time'] > 0:
|
||||
ut = measured_mt_mtask_time / Global['rdtsc_cycle_time']
|
||||
print("tot_mtask_cpu=" + measured_mt_mtask_time + " cyc=" +
|
||||
Global['rdtsc_cycle_time'] + " ut=" + ut)
|
||||
|
||||
predict_mt_efficiency = predict_mt_mtask_time / (
|
||||
Global.get('predict_last_end', 1) * nthreads or 1)
|
||||
print("\nPrediction (what Verilator used for scheduling):")
|
||||
print(" All-thread efficiency = %0.1f%%" %
|
||||
(predict_mt_efficiency * 100.0))
|
||||
print(" All-thread speedup = %0.1f" %
|
||||
(predict_mt_efficiency * nthreads))
|
||||
|
||||
p2e_ratios = []
|
||||
min_p2e = 1000000
|
||||
min_mtask = None
|
||||
max_p2e = -1000000
|
||||
max_mtask = None
|
||||
|
||||
for mtask in sorted(Mtasks.keys()):
|
||||
if Mtasks[mtask]['elapsed'] > 0:
|
||||
if Mtasks[mtask]['predict_cost'] == 0:
|
||||
Mtasks[mtask]['predict_cost'] = 1 # don't log(0) below
|
||||
p2e_ratio = math.log(Mtasks[mtask]['predict_cost'] /
|
||||
Mtasks[mtask]['elapsed'])
|
||||
p2e_ratios.append(p2e_ratio)
|
||||
|
||||
if p2e_ratio > max_p2e:
|
||||
max_p2e = p2e_ratio
|
||||
max_mtask = mtask
|
||||
if p2e_ratio < min_p2e:
|
||||
min_p2e = p2e_ratio
|
||||
min_mtask = mtask
|
||||
|
||||
print("\nMTask statistics:")
|
||||
print(" min log(p2e) = %0.3f" % min_p2e, end="")
|
||||
print(" from mtask %d (predict %d," %
|
||||
(min_mtask, Mtasks[min_mtask]['predict_cost']),
|
||||
end="")
|
||||
print(" elapsed %d)" % Mtasks[min_mtask]['elapsed'])
|
||||
print(" max log(p2e) = %0.3f" % max_p2e, end="")
|
||||
print(" from mtask %d (predict %d," %
|
||||
(max_mtask, Mtasks[max_mtask]['predict_cost']),
|
||||
end="")
|
||||
print(" elapsed %d)" % Mtasks[max_mtask]['elapsed'])
|
||||
|
||||
stddev = statistics.pstdev(p2e_ratios)
|
||||
mean = statistics.mean(p2e_ratios)
|
||||
print(" mean = %0.3f" % mean)
|
||||
print(" stddev = %0.3f" % stddev)
|
||||
print(" e ^ stddev = %0.3f" % math.exp(stddev))
|
||||
print("\nSummary:")
|
||||
print(" Total elapsed time = {} rdtsc ticks".format(ElapsedTime))
|
||||
print(" Parallelized code = {:.2%} of elapsed time".format(
|
||||
ExecGraphTime / ElapsedTime))
|
||||
print(" Total threads = %d" % nthreads)
|
||||
print(" Total CPUs used = %d" % ncpus)
|
||||
print(" Total mtasks = %d" % len(Mtasks))
|
||||
print(" Total yields = %d" % int(Global['stats'].get('yields', 0)))
|
||||
|
||||
report_mtasks()
|
||||
report_cpus()
|
||||
report_sections()
|
||||
|
||||
if nthreads > ncpus:
|
||||
print()
|
||||
print("%%Warning: There were fewer CPUs (%d) then threads (%d)." %
|
||||
print("%%Warning: There were fewer CPUs (%d) than threads (%d)." %
|
||||
(ncpus, nthreads))
|
||||
print(" : See docs on use of numactl.")
|
||||
else:
|
||||
@@ -279,32 +193,130 @@ def report():
|
||||
print()
|
||||
|
||||
|
||||
def report_mtasks():
|
||||
if not Mtasks:
|
||||
return
|
||||
|
||||
nthreads = int(Global['stats']['threads'])
|
||||
|
||||
# If we know cycle time in the same (rdtsc) units,
|
||||
# this will give us an actual utilization number,
|
||||
# (how effectively we keep the cores busy.)
|
||||
#
|
||||
# It also gives us a number we can compare against
|
||||
# serial mode, to estimate the overhead of data sharing,
|
||||
# which will show up in the total elapsed time. (Overhead
|
||||
# of synchronization and scheduling should not.)
|
||||
total_mtask_time = 0
|
||||
thread_mtask_time = collections.defaultdict(lambda: 0)
|
||||
long_mtask_time = 0
|
||||
long_mtask = None
|
||||
predict_mtask_time = 0
|
||||
predict_elapsed = 0
|
||||
for mtaskId in Mtasks:
|
||||
record = Mtasks[mtaskId]
|
||||
predict_mtask_time += record['predict_cost']
|
||||
total_mtask_time += record['elapsed']
|
||||
thread_mtask_time[record['thread']] += record['elapsed']
|
||||
predict_end = record['predict_start'] + record['predict_cost']
|
||||
predict_elapsed = max(predict_elapsed, predict_end)
|
||||
if record['elapsed'] > long_mtask_time:
|
||||
long_mtask_time = record['elapsed']
|
||||
long_mtask = mtaskId
|
||||
Global['predict_last_end'] = predict_elapsed
|
||||
|
||||
serialTime = ElapsedTime - ExecGraphTime
|
||||
|
||||
def subReport(elapsed, work):
|
||||
print(" Thread utilization = {:7.2%}".format(work /
|
||||
(elapsed * nthreads)))
|
||||
print(" Speedup = {:6.3}x".format(work / elapsed))
|
||||
|
||||
print("\nParallelized code, measured:")
|
||||
subReport(ExecGraphTime, total_mtask_time)
|
||||
|
||||
print("\nParallelized code, predicted during static scheduling:")
|
||||
subReport(predict_elapsed, predict_mtask_time)
|
||||
|
||||
print("\nAll code, measured:")
|
||||
subReport(ElapsedTime, serialTime + total_mtask_time)
|
||||
|
||||
print("\nAll code, measured, scaled by predicted speedup:")
|
||||
expectedParallelSpeedup = predict_mtask_time / predict_elapsed
|
||||
scaledElapsed = serialTime + total_mtask_time / expectedParallelSpeedup
|
||||
subReport(scaledElapsed, serialTime + total_mtask_time)
|
||||
|
||||
p2e_ratios = []
|
||||
min_p2e = 1000000
|
||||
min_mtask = None
|
||||
max_p2e = -1000000
|
||||
max_mtask = None
|
||||
|
||||
for mtask in sorted(Mtasks.keys()):
|
||||
if Mtasks[mtask]['elapsed'] > 0:
|
||||
if Mtasks[mtask]['predict_cost'] == 0:
|
||||
Mtasks[mtask]['predict_cost'] = 1 # don't log(0) below
|
||||
p2e_ratio = math.log(Mtasks[mtask]['predict_cost'] /
|
||||
Mtasks[mtask]['elapsed'])
|
||||
p2e_ratios.append(p2e_ratio)
|
||||
|
||||
if p2e_ratio > max_p2e:
|
||||
max_p2e = p2e_ratio
|
||||
max_mtask = mtask
|
||||
if p2e_ratio < min_p2e:
|
||||
min_p2e = p2e_ratio
|
||||
min_mtask = mtask
|
||||
|
||||
print("\nMTask statistics:")
|
||||
print(" Longest mtask id = {}".format(long_mtask))
|
||||
print(" Longest mtask time = {:.2%} of time elapsed in parallelized code".
|
||||
format(long_mtask_time / ExecGraphTime))
|
||||
print(" min log(p2e) = %0.3f" % min_p2e, end="")
|
||||
|
||||
print(" from mtask %d (predict %d," %
|
||||
(min_mtask, Mtasks[min_mtask]['predict_cost']),
|
||||
end="")
|
||||
print(" elapsed %d)" % Mtasks[min_mtask]['elapsed'])
|
||||
print(" max log(p2e) = %0.3f" % max_p2e, end="")
|
||||
print(" from mtask %d (predict %d," %
|
||||
(max_mtask, Mtasks[max_mtask]['predict_cost']),
|
||||
end="")
|
||||
print(" elapsed %d)" % Mtasks[max_mtask]['elapsed'])
|
||||
|
||||
stddev = statistics.pstdev(p2e_ratios)
|
||||
mean = statistics.mean(p2e_ratios)
|
||||
print(" mean = %0.3f" % mean)
|
||||
print(" stddev = %0.3f" % stddev)
|
||||
print(" e ^ stddev = %0.3f" % math.exp(stddev))
|
||||
|
||||
|
||||
def report_cpus():
|
||||
print("\nCPUs:")
|
||||
print("\nCPU info:")
|
||||
|
||||
Global['cpu_sockets'] = collections.defaultdict(lambda: 0)
|
||||
Global['cpu_socket_cores'] = collections.defaultdict(lambda: 0)
|
||||
|
||||
for cpu in sorted(Global['cpus'].keys()):
|
||||
print(" cpu %d: " % cpu, end='')
|
||||
print("cpu_time=%d" % Global['cpus'][cpu]['cpu_time'], end='')
|
||||
|
||||
socket = None
|
||||
print(" Id | Time spent executing MTask | Socket | Core | Model")
|
||||
print(" | % of elapsed ticks / ticks | | |")
|
||||
print(" ====|============================|========|======|======")
|
||||
for cpu in sorted(Cpus):
|
||||
socket = ""
|
||||
core = ""
|
||||
model = ""
|
||||
if cpu in Global['cpuinfo']:
|
||||
cpuinfo = Global['cpuinfo'][cpu]
|
||||
if 'physical_id' in cpuinfo and 'core_id' in cpuinfo:
|
||||
socket = int(cpuinfo['physical_id'])
|
||||
socket = cpuinfo['physical_id']
|
||||
Global['cpu_sockets'][socket] += 1
|
||||
print(" socket=%d" % socket, end='')
|
||||
|
||||
core = int(cpuinfo['core_id'])
|
||||
Global['cpu_socket_cores'][str(socket) + "__" + str(core)] += 1
|
||||
print(" core=%d" % core, end='')
|
||||
core = cpuinfo['core_id']
|
||||
Global['cpu_socket_cores'][socket + "__" + core] += 1
|
||||
|
||||
if 'model_name' in cpuinfo:
|
||||
model = cpuinfo['model_name']
|
||||
print(" %s" % model, end='')
|
||||
print()
|
||||
|
||||
print(" {:3d} | {:7.2%} / {:16d} | {:>6s} | {:>4s} | {}".format(
|
||||
cpu, Cpus[cpu]['mtask_time'] / ElapsedTime,
|
||||
Cpus[cpu]['mtask_time'], socket, core, model))
|
||||
|
||||
if len(Global['cpu_sockets']) > 1:
|
||||
Global['cpu_sockets_warning'] = True
|
||||
@@ -313,25 +325,68 @@ def report_cpus():
|
||||
Global['cpu_socket_cores_warning'] = True
|
||||
|
||||
|
||||
def report_sections():
|
||||
if not Sections:
|
||||
return
|
||||
print("\nSection profile:")
|
||||
|
||||
totalTime = collections.defaultdict(lambda: 0)
|
||||
selfTime = collections.defaultdict(lambda: 0)
|
||||
|
||||
sectionTree = [0, {}, 1] # [selfTime, childTrees, numberOfTimesEntered]
|
||||
prevTime = 0
|
||||
prevStack = ()
|
||||
for time, stack in Sections:
|
||||
if len(stack) > len(prevStack):
|
||||
scope = sectionTree
|
||||
for item in stack:
|
||||
scope = scope[1].setdefault(item, [0, {}, 0])
|
||||
scope[2] += 1
|
||||
dt = time - prevTime
|
||||
scope = sectionTree
|
||||
for item in prevStack:
|
||||
scope = scope[1].setdefault(item, [0, {}, 0])
|
||||
scope[0] += dt
|
||||
|
||||
if prevStack:
|
||||
for name in prevStack:
|
||||
totalTime[name] += dt
|
||||
selfTime[prevStack[-1]] += dt
|
||||
prevTime = time
|
||||
prevStack = stack
|
||||
|
||||
def treeSum(tree):
|
||||
n = tree[0]
|
||||
for subTree in tree[1].values():
|
||||
n += treeSum(subTree)
|
||||
return n
|
||||
|
||||
# Make sure the tree sums to the elapsed time
|
||||
sectionTree[0] += ElapsedTime - treeSum(sectionTree)
|
||||
|
||||
def printTree(prefix, name, entries, tree):
|
||||
print(" {:7.2%} | {:7.2%} | {:8} | {:10.2f} | {}".format(
|
||||
treeSum(tree) / ElapsedTime, tree[0] / ElapsedTime, tree[2],
|
||||
tree[2] / entries, prefix + name))
|
||||
for k in sorted(tree[1], key=lambda _: -treeSum(tree[1][_])):
|
||||
printTree(prefix + " ", k, tree[2], tree[1][k])
|
||||
|
||||
print(" Total | Self | Total | Relative | Section")
|
||||
print(" time | time | entries | entries | name ")
|
||||
print("==========|=========|==========|============|========")
|
||||
printTree("", "*TOTAL*", 1, sectionTree)
|
||||
|
||||
|
||||
######################################################################
|
||||
|
||||
|
||||
def write_vcd(filename):
|
||||
print("Writing %s" % filename)
|
||||
with open(filename, "w", encoding="utf8") as fh:
|
||||
vcd = {
|
||||
'values':
|
||||
collections.defaultdict(lambda: {}), # {<time>}{<code>} = value
|
||||
'sigs': {
|
||||
'predicted_threads': {},
|
||||
'measured_threads': {},
|
||||
'cpus': {},
|
||||
'evals': {},
|
||||
'mtasks': {},
|
||||
'Stats': {}
|
||||
} # {<module>}{<sig}} = code
|
||||
}
|
||||
code = 0
|
||||
# dict of dicts of hierarchy elements/signal name -> (code, width)
|
||||
scopeSigs = {}
|
||||
# map from time -> code -> value
|
||||
values = collections.defaultdict(lambda: {})
|
||||
|
||||
parallelism = {
|
||||
'measured': collections.defaultdict(lambda: 0),
|
||||
@@ -340,132 +395,139 @@ def write_vcd(filename):
|
||||
parallelism['measured'][0] = 0
|
||||
parallelism['predicted'][0] = 0
|
||||
|
||||
# Measured graph
|
||||
for thread in sorted(Threads.keys()):
|
||||
sig = "thread%d_mtask" % thread
|
||||
if sig not in vcd['sigs']['measured_threads']:
|
||||
vcd['sigs']['measured_threads'][sig] = code
|
||||
code += 1
|
||||
mcode = vcd['sigs']['measured_threads'][sig]
|
||||
nextFreeCode = 0
|
||||
|
||||
for start in sorted(Threads[thread]):
|
||||
mtask = Threads[thread][start]['mtask']
|
||||
end = Threads[thread][start]['end']
|
||||
cpu = Threads[thread][start]['cpu']
|
||||
vcd['values'][start][mcode] = mtask
|
||||
vcd['values'][end][mcode] = None
|
||||
def getCode(width, *names):
|
||||
nonlocal nextFreeCode
|
||||
scope = scopeSigs
|
||||
*path, name = names
|
||||
for item in path:
|
||||
scope = scope.setdefault(item, {})
|
||||
code, oldWidth = scope.setdefault(name, (nextFreeCode, width))
|
||||
assert oldWidth == width
|
||||
if code == nextFreeCode:
|
||||
nextFreeCode += 1
|
||||
return code
|
||||
|
||||
def addValue(code, time, val):
|
||||
if isinstance(val, str):
|
||||
buf = ""
|
||||
for c in val:
|
||||
buf += bin(ord(c))[2:].rjust(8, "0")
|
||||
val = buf.ljust(LongestVcdStrValueLength * 8, "0")
|
||||
values[time][code] = val
|
||||
|
||||
# Measured graph
|
||||
for thread in sorted(Threads):
|
||||
mcode = getCode(32, 'measured', 't%d_mtask' % thread)
|
||||
for record in Threads[thread]:
|
||||
start = record['start']
|
||||
mtask = record['mtask']
|
||||
end = record['end']
|
||||
cpu = record['cpu']
|
||||
addValue(mcode, start, mtask)
|
||||
addValue(mcode, end, None)
|
||||
parallelism['measured'][start] += 1
|
||||
parallelism['measured'][end] -= 1
|
||||
|
||||
sig = "cpu%d_thread" % cpu
|
||||
if sig not in vcd['sigs']['cpus']:
|
||||
vcd['sigs']['cpus'][sig] = code
|
||||
code += 1
|
||||
ccode = vcd['sigs']['cpus'][sig]
|
||||
vcd['values'][start][ccode] = thread
|
||||
vcd['values'][end][ccode] = None
|
||||
code = getCode(32, 'measured', 'cpu%d_thread' % cpu)
|
||||
addValue(code, start, thread)
|
||||
addValue(code, end, None)
|
||||
|
||||
sig = "mtask%d_cpu" % mtask
|
||||
if sig not in vcd['sigs']['mtasks']:
|
||||
vcd['sigs']['mtasks'][sig] = code
|
||||
code += 1
|
||||
ccode = vcd['sigs']['mtasks'][sig]
|
||||
vcd['values'][start][ccode] = cpu
|
||||
vcd['values'][end][ccode] = None
|
||||
code = getCode(32, 'measured', 'cpu%d_mtask' % cpu)
|
||||
addValue(code, start, mtask)
|
||||
addValue(code, end, None)
|
||||
|
||||
# Eval graph
|
||||
vcd['sigs']['evals']["eval"] = code
|
||||
elcode = code
|
||||
code += 1
|
||||
n = 0
|
||||
for eval_start in Evals:
|
||||
eval_end = Evals[eval_start]['end']
|
||||
n += 1
|
||||
vcd['values'][eval_start][elcode] = n
|
||||
vcd['values'][eval_end][elcode] = None
|
||||
tStart = sorted(_['start'] for records in Threads.values()
|
||||
for _ in records)
|
||||
tEnd = sorted(_['end'] for records in Threads.values()
|
||||
for _ in records)
|
||||
|
||||
# Eval_loop graph
|
||||
vcd['sigs']['evals']["eval_loop"] = code
|
||||
elcode = code
|
||||
code += 1
|
||||
n = 0
|
||||
for eval_start in EvalLoops:
|
||||
eval_end = EvalLoops[eval_start]['end']
|
||||
n += 1
|
||||
vcd['values'][eval_start][elcode] = n
|
||||
vcd['values'][eval_end][elcode] = None
|
||||
# Predicted graph
|
||||
for start, end in ExecGraphIntervals:
|
||||
# Find the earliest MTask start after the start point, and the
|
||||
# latest MTask end before the end point, so we can scale to the
|
||||
# same range
|
||||
start = tStart[bisect.bisect_left(tStart, start)]
|
||||
end = tEnd[bisect.bisect_right(tEnd, end) - 1]
|
||||
# Compute scale so predicted graph is of same width as interval
|
||||
measured_scaling = (end - start) / Global['predict_last_end']
|
||||
# Predict mtasks that fill the time the execution occupied
|
||||
for mtask in Mtasks:
|
||||
thread = Mtasks[mtask]['thread']
|
||||
pred_scaled_start = start + int(
|
||||
Mtasks[mtask]['predict_start'] * measured_scaling)
|
||||
pred_scaled_end = start + int(
|
||||
(Mtasks[mtask]['predict_start'] +
|
||||
Mtasks[mtask]['predict_cost']) * measured_scaling)
|
||||
if pred_scaled_start == pred_scaled_end:
|
||||
continue
|
||||
|
||||
if Mtasks:
|
||||
# Predicted graph
|
||||
for eval_start in EvalLoops:
|
||||
eval_end = EvalLoops[eval_start]['end']
|
||||
# Compute scale so predicted graph is of same width as eval
|
||||
measured_scaling = (eval_end -
|
||||
eval_start) / Global['predict_last_end']
|
||||
# Predict mtasks that fill the time the eval occupied
|
||||
for mtask in Mtasks:
|
||||
thread = Mtasks[mtask]['thread']
|
||||
pred_scaled_start = eval_start + int(
|
||||
Mtasks[mtask]['predict_start'] * measured_scaling)
|
||||
pred_scaled_end = eval_start + int(
|
||||
(Mtasks[mtask]['predict_start'] +
|
||||
Mtasks[mtask]['predict_cost']) * measured_scaling)
|
||||
if pred_scaled_start == pred_scaled_end:
|
||||
continue
|
||||
mcode = getCode(32, 'predicted', 't%d_mtask' % thread)
|
||||
addValue(mcode, pred_scaled_start, mtask)
|
||||
addValue(mcode, pred_scaled_end, None)
|
||||
|
||||
sig = "predicted_thread%d_mtask" % thread
|
||||
if sig not in vcd['sigs']['predicted_threads']:
|
||||
vcd['sigs']['predicted_threads'][sig] = code
|
||||
code += 1
|
||||
mcode = vcd['sigs']['predicted_threads'][sig]
|
||||
parallelism['predicted'][pred_scaled_start] += 1
|
||||
parallelism['predicted'][pred_scaled_end] -= 1
|
||||
|
||||
vcd['values'][pred_scaled_start][mcode] = mtask
|
||||
vcd['values'][pred_scaled_end][mcode] = None
|
||||
# Parallelism graph
|
||||
for measpred in ('measured', 'predicted'):
|
||||
pcode = getCode(32, 'stats', '%s_parallelism' % measpred)
|
||||
value = 0
|
||||
for time in sorted(parallelism[measpred].keys()):
|
||||
value += parallelism[measpred][time]
|
||||
addValue(pcode, time, value)
|
||||
|
||||
parallelism['predicted'][pred_scaled_start] += 1
|
||||
parallelism['predicted'][pred_scaled_end] -= 1
|
||||
|
||||
# Parallelism graph
|
||||
for measpred in ('measured', 'predicted'):
|
||||
vcd['sigs']['Stats']["%s_parallelism" % measpred] = code
|
||||
pcode = code
|
||||
code += 1
|
||||
value = 0
|
||||
for time in sorted(parallelism[measpred].keys()):
|
||||
value += parallelism[measpred][time]
|
||||
vcd['values'][time][pcode] = value
|
||||
# Section graph
|
||||
if Sections:
|
||||
scode = getCode(LongestVcdStrValueLength * 8, "section", "trace")
|
||||
dcode = getCode(32, "section", "depth")
|
||||
for time, stack in Sections:
|
||||
addValue(scode, time, stack[-1] if stack else None)
|
||||
addValue(dcode, time, len(stack))
|
||||
|
||||
# Create output file
|
||||
fh.write("$version Generated by verilator_gantt $end\n")
|
||||
fh.write("$timescale 1ns $end\n")
|
||||
fh.write("\n")
|
||||
|
||||
all_codes = {}
|
||||
all_codes = set()
|
||||
|
||||
def writeScope(scope):
|
||||
assert isinstance(scope, dict)
|
||||
for key in sorted(scope):
|
||||
val = scope[key]
|
||||
if isinstance(val, dict):
|
||||
fh.write(" $scope module %s $end\n" % key)
|
||||
writeScope(val)
|
||||
fh.write(" $upscope $end\n")
|
||||
else:
|
||||
(code, width) = val
|
||||
all_codes.add(code)
|
||||
fh.write(" $var wire %d v%x %s [%d:0] $end\n" %
|
||||
(width, code, key, width - 1))
|
||||
|
||||
fh.write(" $scope module gantt $end\n")
|
||||
for module in sorted(vcd['sigs'].keys()):
|
||||
fh.write(" $scope module %s $end\n" % module)
|
||||
for sig in sorted(vcd['sigs'][module].keys()):
|
||||
code = vcd['sigs'][module][sig]
|
||||
fh.write(" $var wire 32 v%x %s [31:0] $end\n" % (code, sig))
|
||||
all_codes[code] = 1
|
||||
fh.write(" $upscope $end\n")
|
||||
writeScope(scopeSigs)
|
||||
fh.write(" $upscope $end\n")
|
||||
fh.write("$enddefinitions $end\n")
|
||||
fh.write("\n")
|
||||
|
||||
first = True
|
||||
for time in sorted(vcd['values']):
|
||||
for time in sorted(values):
|
||||
if first:
|
||||
first = False
|
||||
# Start with Z for any signals without time zero data
|
||||
for code in sorted(all_codes.keys()):
|
||||
if code not in vcd['values'][time]:
|
||||
vcd['values'][time][code] = None
|
||||
for code in sorted(all_codes):
|
||||
if code not in values[time]:
|
||||
addValue(code, time, None)
|
||||
fh.write("#%d\n" % time)
|
||||
for code in sorted(vcd['values'][time].keys()):
|
||||
value = vcd['values'][time][code]
|
||||
for code in sorted(values[time]):
|
||||
value = values[time][code]
|
||||
if value is None:
|
||||
fh.write("bz v%x\n" % code)
|
||||
elif isinstance(value, str):
|
||||
fh.write("b%s v%x\n" % (value, code))
|
||||
else:
|
||||
fh.write("b%s v%x\n" % (format(value, 'b'), code))
|
||||
|
||||
@@ -504,7 +566,8 @@ parser.add_argument('filename',
|
||||
|
||||
Args = parser.parse_args()
|
||||
|
||||
process(Args.filename)
|
||||
read_data(Args.filename)
|
||||
report()
|
||||
if not Args.no_vcd:
|
||||
write_vcd(Args.vcd)
|
||||
|
||||
|
||||
@@ -26,4 +26,5 @@ if git log --format=%B -n 1 "$COMMIT" | grep -q -i '\[CI\s\+ccache\s\+clear\]';
|
||||
fi
|
||||
|
||||
# Dump stats, then zero stats
|
||||
# 22.04: ccache -s -v -z
|
||||
ccache -s -z
|
||||
|
||||
+2
-2
@@ -71,8 +71,8 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
sudo apt-get install libsystemc libsystemc-dev
|
||||
fi
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install mold ||
|
||||
sudo apt-get install mold
|
||||
sudo apt-get install bear mold ||
|
||||
sudo apt-get install bear mold
|
||||
fi
|
||||
if [ "$COVERAGE" = 1 ]; then
|
||||
yes yes | sudo cpan -fi Parallel::Forker
|
||||
|
||||
@@ -49,6 +49,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
./configure --enable-longtests --enable-ccwarn ${CI_M32:+--enable-m32}
|
||||
ccache -z
|
||||
"$MAKE" -j "$NPROC" -k
|
||||
# 22.04: ccache -s -v
|
||||
ccache -s
|
||||
if [ "$CI_OS_NAME" = "osx" ]; then
|
||||
file bin/verilator_bin
|
||||
@@ -180,6 +181,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
fatal "Unknown test: $TESTS"
|
||||
;;
|
||||
esac
|
||||
# 22.04: ccache -s -v
|
||||
ccache -s
|
||||
|
||||
# Upload coverage data
|
||||
|
||||
+32
-9
@@ -10,7 +10,7 @@
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.014 2023-08-06],
|
||||
AC_INIT([Verilator],[5.018 2023-10-30],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
@@ -24,6 +24,10 @@ AC_SUBST(PACKAGE_VERSION_NUMBER)
|
||||
AC_DEFINE_UNQUOTED([PACKAGE_VERSION_NUMBER_STRING],["$PACKAGE_VERSION_NUMBER"],[Package version as a number])
|
||||
VERILATOR_VERSION_INTEGER=`AS_ECHO("$PACKAGE_VERSION") | [sed 's/\([0-9]\)\.\([0-9][0-9][0-9]\) .*/\1\2000/g']`
|
||||
AC_SUBST(VERILATOR_VERSION_INTEGER)
|
||||
AC_DEFINE_UNQUOTED([PACKAGE_VERSION_STRING_CHAR],
|
||||
[static const char* const PACKAGE_STRING_UNUSED = "$PACKAGE_STRING";],
|
||||
[Package version as a number])
|
||||
AC_SUBST(PACKAGE_VERSION_STRING_CHAR)
|
||||
|
||||
# Ignore automake flags passed by Ubuntu builds
|
||||
AC_ARG_ENABLE([dependency-tracking],
|
||||
@@ -371,10 +375,9 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wthread-safety)
|
||||
AC_SUBST(CFG_CXXFLAGS_WEXTRA)
|
||||
|
||||
# Flags for coroutine support for dynamic scheduling
|
||||
_MY_CXX_CHECK_IFELSE(
|
||||
-fcoroutines-ts,
|
||||
[CFG_CXXFLAGS_COROUTINES="-fcoroutines-ts"],
|
||||
[CFG_CXXFLAGS_COROUTINES="-fcoroutines"])
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines-ts)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines-ts -Wno-deprecated-experimental-coroutine)
|
||||
AC_SUBST(CFG_CXXFLAGS_COROUTINES)
|
||||
|
||||
# HAVE_COROUTINES
|
||||
@@ -442,14 +445,19 @@ m4_foreach([cflag],[
|
||||
[-mno-cet],
|
||||
[-Qunused-arguments],
|
||||
[-Wno-bool-operation],
|
||||
[-Wno-tautological-bitwise-compare],
|
||||
[-Wno-c++11-narrowing],
|
||||
[-Wno-constant-logical-operand],
|
||||
[-Wno-non-pod-varargs],
|
||||
[-Wno-overloaded-virtual],
|
||||
[-Wno-parentheses-equality],
|
||||
[-Wno-shadow],
|
||||
[-Wno-sign-compare],
|
||||
[-Wno-tautological-bitwise-compare],
|
||||
[-Wno-uninitialized],
|
||||
[-Wno-unused-but-set-parameter],
|
||||
[-Wno-unused-but-set-variable],
|
||||
[-Wno-unused-parameter],
|
||||
[-Wno-unused-variable],
|
||||
[-Wno-shadow]],[
|
||||
[-Wno-unused-variable]],[
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_NO_UNUSED,cflag)
|
||||
# CMake will test what flags work itself, so pass all flags through to it
|
||||
CFG_CXX_FLAGS_CMAKE="$CFG_CXX_FLAGS_CMAKE cflag"
|
||||
@@ -559,6 +567,21 @@ Verilator requires a C++11 or newer compiler.]])
|
||||
fi
|
||||
AC_SUBST(CFG_CXXFLAGS_STD)
|
||||
|
||||
# Compiler precompiled header options (assumes either gcc or clang++)
|
||||
AC_MSG_CHECKING([for $CXX precompile header include option])
|
||||
if $CXX --help | grep include-pch >/dev/null 2>/dev/null ; then
|
||||
# clang
|
||||
CFG_CXXFLAGS_PCH_I=-include-pch
|
||||
CFG_GCH_IF_CLANG=.gch
|
||||
else
|
||||
# GCC
|
||||
CFG_CXXFLAGS_PCH_I=-include
|
||||
CFG_GCH_IF_CLANG=
|
||||
fi
|
||||
AC_MSG_RESULT($CFG_CXXFLAGS_PCH_I)
|
||||
AC_SUBST(CFG_CXXFLAGS_PCH_I)
|
||||
AC_SUBST(CFG_GCH_IF_CLANG)
|
||||
|
||||
# Checks for library functions.
|
||||
AC_CHECK_MEMBER([struct stat.st_mtim.tv_nsec],
|
||||
[AC_DEFINE([HAVE_STAT_NSEC],[1],[Defined if struct stat has st_mtim.tv_nsec])],
|
||||
@@ -573,7 +596,7 @@ ACO_SAVE_LIBS="$LIBS"
|
||||
LIBS="$LIBS -lsystemc"
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([[#include <systemc.h>
|
||||
extern "C" int sc_main(int argc, char* argv[]) {}
|
||||
extern "C" int sc_main(int argc, char* argv[]) { return 0; }
|
||||
]],[[sc_version()]])],
|
||||
[_my_result=yes
|
||||
AC_DEFINE([HAVE_SYSTEMC],[1],[Defined if have SystemC library])],
|
||||
|
||||
@@ -19,6 +19,7 @@ Anthony Donlon
|
||||
Arkadiusz Kozdra
|
||||
Aylon Chaim Porat
|
||||
Cameron Kirk
|
||||
Chih-Mao Chen
|
||||
Chris Randall
|
||||
Chuxuan Wang
|
||||
Conor McCullough
|
||||
@@ -40,6 +41,7 @@ Fan Shupei
|
||||
february cozzocrea
|
||||
Felix Neumärker
|
||||
Felix Yan
|
||||
Frans Skarman
|
||||
G-A. Kamendje
|
||||
Garrett Smith
|
||||
Geza Lore
|
||||
@@ -48,6 +50,7 @@ Glen Gibb
|
||||
Gökçe Aydos
|
||||
Graham Rushton
|
||||
Guokai Chen
|
||||
Gus Smith
|
||||
Gustav Svensk
|
||||
Harald Heckmann
|
||||
Hennadii Chernyshchyk
|
||||
@@ -56,6 +59,7 @@ Huang Rui
|
||||
Huanghuang Zhou
|
||||
HungMingWu
|
||||
HyungKi Jeong
|
||||
Ícaro Lima
|
||||
Ilya Barkov
|
||||
Iru Cai
|
||||
Ivan Vnučec
|
||||
@@ -87,6 +91,7 @@ Josh Redford
|
||||
Julian Daube
|
||||
Julie Schwartz
|
||||
Julien Margetts
|
||||
Justin Thiel
|
||||
Kaleb Barrett
|
||||
Kamil Rakoczy
|
||||
Kanad Kanhere
|
||||
@@ -113,6 +118,7 @@ Martin Stadler
|
||||
Matthew Ballance
|
||||
Michael Killough
|
||||
Michaël Lefebvre
|
||||
Michal Czyz
|
||||
Mike Popoloski
|
||||
Miodrag Milanović
|
||||
Mladen Slijepcevic
|
||||
@@ -133,6 +139,7 @@ Pierre-Henri Horrein
|
||||
Pieter Kapsenberg
|
||||
Piotr Binkowski
|
||||
Qingyao Sun
|
||||
Quentin Corradi
|
||||
Rafal Kapuscik
|
||||
Raynard Qiao
|
||||
Richard Myers
|
||||
@@ -178,5 +185,6 @@ Yuri Victorovich
|
||||
Yutetsu TAKATSUKASA
|
||||
Yves Mathieu
|
||||
Zhanglei Wang
|
||||
Zhou Shen
|
||||
Zixi Li
|
||||
أحمد المحمودي
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0112,C0114,C0115,C0116,C0209,C0301,R0201,R0903
|
||||
# pylint: disable=C0112,C0114,C0115,C0116,C0209,C0301,R0903
|
||||
# -*- Python -*- See copyright, etc below
|
||||
######################################################################
|
||||
|
||||
|
||||
+1
-1
@@ -230,4 +230,4 @@ spelling_ignore_contributor_names = True
|
||||
|
||||
breathe_projects = {"verilated": "_build/doxygen/verilated/xml/"}
|
||||
breathe_default_project = "verilated"
|
||||
breathe_default_members = ('members')
|
||||
breathe_default_members = 'members'
|
||||
|
||||
@@ -128,6 +128,43 @@ in the distribution. These headers use Doxygen comments, `///` and `//<`,
|
||||
to indicate and document those functions that are part of the Verilated
|
||||
public API.
|
||||
|
||||
Process-Level Clone APIs
|
||||
--------------------------
|
||||
|
||||
Modern operating systems support process-level clone (a.k.a copying, forking)
|
||||
with system call interfaces in C/C++, e.g., :code:`fork()` in Linux.
|
||||
|
||||
However, after cloning a parent process, some resources cannot be inherited
|
||||
in the child process. For example, in POSIX systems, when you fork a process,
|
||||
the child process inherits all the memory of the parent process. However,
|
||||
only the thread that called fork is replicated in the child process. Other
|
||||
threads are not.
|
||||
|
||||
Therefore, to support the process-level clone mechanisms, Verilator supports
|
||||
:code:`prepareClone()` and :code:`atClone()` APIs to allow the user to manually
|
||||
re-construct the model in the child process. The two APIs handle all necessary
|
||||
resources required for releasing and re-initializing before and after cloning.
|
||||
|
||||
The two APIs are supported in the verilated models. Here is an example of usage
|
||||
with Linux :code:`fork()` and :code:`pthread_atfork` APIs:
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
// static function pointers to fit pthread_atfork
|
||||
static auto prepareClone = [](){ topp->prepareClone(); };
|
||||
static auto atClone = [](){ topp->atClone(); };
|
||||
|
||||
// in main function, register the handlers:
|
||||
pthread_atfork(prepareClone, atClone, atClone);
|
||||
|
||||
For better flexibility, you can also manually call the handlers before and
|
||||
after :code:`fork()`.
|
||||
|
||||
With the process-level clone APIs, users can create process-level snapshots
|
||||
for the verilated models. While the Verilator save/restore option provides
|
||||
persistent and circuit-dependent snapshots, the process-level clone APIs
|
||||
enable in-memory, circuit-transparent, and highly efficient snapshots.
|
||||
|
||||
|
||||
Direct Programming Interface (DPI)
|
||||
==================================
|
||||
|
||||
@@ -7,6 +7,21 @@ Environment
|
||||
This section describes the environment variables used by Verilator and
|
||||
associated programs.
|
||||
|
||||
.. option:: AR
|
||||
|
||||
Optionally overrides the default :command:`ar` (archive) binary used by
|
||||
the Verilated makefiles. If AR is not set, the version found at
|
||||
configure time is used.
|
||||
|
||||
.. option:: CXX
|
||||
|
||||
Optionally overrides the default compiler binary used by the Verilated
|
||||
makefiles. If CXX is not set, the version found at configure time is
|
||||
used. Note the default flags passed to the compiler are determined at
|
||||
configuration time, so changing the CXX compiler version using this
|
||||
variable, as opposed to passing it at configuration time, may not give
|
||||
desired results.
|
||||
|
||||
.. option:: LD_LIBRARY_PATH
|
||||
|
||||
A generic Linux/OS variable specifying what directories have shared
|
||||
@@ -35,6 +50,12 @@ associated programs.
|
||||
those programs. If OBJCACHE is not set, and at configure time ccache
|
||||
was present, ccache will be used as a default.
|
||||
|
||||
.. option:: PYTHON3
|
||||
|
||||
Optionally overrides the default :command:`python3` binary used by the
|
||||
Verilated makefiles. If PYTHON3 is not set, the version found at
|
||||
configure time is used.
|
||||
|
||||
.. option:: SYSTEMC
|
||||
|
||||
Deprecated. Used only if :option:`SYSTEMC_INCLUDE` or
|
||||
|
||||
@@ -125,9 +125,12 @@ Summary:
|
||||
|
||||
After generating the SystemC/C++ code, Verilator will invoke the
|
||||
toolchain to build the model library (and executable when :vlopt:`--exe`
|
||||
is also used). Verilator manages the build itself, and for this --build
|
||||
is also used). Verilator manages the build itself, and for this --build
|
||||
requires GNU Make to be available on the platform.
|
||||
|
||||
:vlopt:`--build` cannot be specified when using :vlopt:`-E`,
|
||||
:vlopt:`--dpi-hdr-only`, :vlopt:`--lint-only`, or :vlopt:`--xml-only`.
|
||||
|
||||
.. option:: --build-dep-bin <filename>
|
||||
|
||||
Rarely needed. When a dependency (.d) file is created, this filename
|
||||
@@ -1314,6 +1317,7 @@ Summary:
|
||||
This is not needed with standard designs with only one top. See also
|
||||
:option:`MULTITOP` warning.
|
||||
|
||||
|
||||
.. option:: --trace
|
||||
|
||||
Adds waveform tracing code to the model using VCD format. This overrides
|
||||
@@ -1392,6 +1396,17 @@ Summary:
|
||||
This option is accepted, but has absolutely no effect with
|
||||
:vlopt:`--trace`, which respects :vlopt:`--threads` instead.
|
||||
|
||||
.. option:: --no-trace-top
|
||||
|
||||
Disables tracing for the input and output signals in the top wrapper which
|
||||
Verilator adds to the design. The signals are still traced in the original
|
||||
verilog top modules.
|
||||
|
||||
When combined with :option:`--main-top-name` set to "-" or when the name of
|
||||
the top module is set to "" in its constructor, the generated trace file
|
||||
will have the verilog top module as its root, rather than another module
|
||||
added by Verilator.
|
||||
|
||||
.. option:: --trace-underscore
|
||||
|
||||
Enable tracing of signals or modules that start with an
|
||||
@@ -1523,13 +1538,15 @@ Summary:
|
||||
|
||||
.. option:: -Wno-lint
|
||||
|
||||
Disable all lint-related warning messages, and all style warnings. This is
|
||||
equivalent to ``-Wno-ALWCOMBORDER -Wno-ASCRANGE -Wno-BSSPACE -Wno-CASEINCOMPLETE
|
||||
-Wno-CASEOVERLAP -Wno-CASEX -Wno-CASTCONST -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS
|
||||
-Wno-IMPLICIT -Wno-IMPLICITSTATIC -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINMISSING -Wno-STATICVAR -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED
|
||||
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
|
||||
-Wno-WIDTH`` plus the list shown for Wno-style.
|
||||
Disable all lint-related warning messages, and all style warnings. This
|
||||
is equivalent to ``-Wno-ALWCOMBORDER`` ``-Wno-ASCRANGE``
|
||||
``-Wno-BSSPACE`` ``-Wno-CASEINCOMPLETE`` ``-Wno-CASEOVERLAP``
|
||||
``-Wno-CASEX`` ``-Wno-CASTCONST`` ``-Wno-CASEWITHX`` ``-Wno-CMPCONST``
|
||||
``-Wno-COLONPLUS`` ``-Wno-IMPLICIT`` ``-Wno-IMPLICITSTATIC``
|
||||
``-Wno-PINCONNECTEMPTY`` ``-Wno-PINMISSING`` ``-Wno-STATICVAR``
|
||||
``-Wno-SYNCASYNCNET`` ``-Wno-UNDRIVEN`` ``-Wno-UNSIGNED``
|
||||
``-Wno-UNUSEDGENVAR`` ``-Wno-UNUSEDPARAM`` ``-Wno-UNUSEDSIGNAL``
|
||||
``-Wno-WIDTH``, plus the list shown for :vlopt:`-Wno-style`.
|
||||
|
||||
It is strongly recommended that you clean up your code rather than using this
|
||||
option; it is only intended to be used when running test-cases of code
|
||||
@@ -1537,12 +1554,13 @@ Summary:
|
||||
|
||||
.. option:: -Wno-style
|
||||
|
||||
Disable all code style related warning messages (note that by default, they are
|
||||
already disabled). This is equivalent to ``-Wno-DECLFILENAME -Wno-DEFPARAM
|
||||
-Wno-EOFNEWLINE -Wno-GENUNNAMED -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN
|
||||
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
|
||||
-Wno-VARHIDDEN``.
|
||||
Disable all code style related warning messages (note that by default,
|
||||
they are already disabled). This is equivalent to ``-Wno-DECLFILENAME``
|
||||
``-Wno-DEFPARAM`` ``-Wno-EOFNEWLINE`` ``-Wno-GENUNNAMED``
|
||||
``-Wno-IMPORTSTAR`` ``-Wno-INCABSPATH`` ``-Wno-PINCONNECTEMPTY``
|
||||
``-Wno-PINNOCONNECT`` ``-Wno-SYNCASYNCNET`` ``-Wno-UNDRIVEN``
|
||||
``-Wno-UNUSEDGENVAR`` ``-Wno-UNUSEDPARAM`` ``-Wno-UNUSEDSIGNAL``
|
||||
``-Wno-VARHIDDEN``.
|
||||
|
||||
.. option:: -Wpedantic
|
||||
|
||||
@@ -1560,20 +1578,25 @@ Summary:
|
||||
|
||||
.. option:: -Wwarn-lint
|
||||
|
||||
Enable all lint-related warning messages (note that by default, they are already
|
||||
enabled), but do not affect style messages. This is equivalent to
|
||||
``-Wwarn-ALWCOMBORDER -Wwarn-ASCRANGE -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE
|
||||
-Wwarn-CASEOVERLAP -Wwarn-CASEWITHX -Wwarn-CASEX -Wwarn-CASTCONST -Wwarn-CMPCONST
|
||||
-Wwarn-COLONPLUS -Wwarn-IMPLICIT -Wwarn-IMPLICITSTATIC -Wwarn-LATCH -Wwarn-MISINDENT
|
||||
-Wwarn-NEWERSTD -Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-STATICVAR -Wwarn-UNSIGNED
|
||||
-Wwarn-WIDTHTRUNC -Wwarn-WIDTHEXPAND -Wwarn-WIDTHXZEXPAND``.
|
||||
Enable all lint-related warning messages (note that by default, they are
|
||||
already enabled), but do not affect style messages. This is equivalent
|
||||
to ``-Wwarn-ALWCOMBORDER`` ``-Wwarn-ASCRANGE`` ``-Wwarn-BSSPACE``
|
||||
``-Wwarn-CASEINCOMPLETE`` ``-Wwarn-CASEOVERLAP`` ``-Wwarn-CASEWITHX``
|
||||
``-Wwarn-CASEX`` ``-Wwarn-CASTCONST`` ``-Wwarn-CMPCONST``
|
||||
``-Wwarn-COLONPLUS`` ``-Wwarn-IMPLICIT`` ``-Wwarn-IMPLICITSTATIC``
|
||||
``-Wwarn-LATCH`` ``-Wwarn-MISINDENT`` ``-Wwarn-NEWERSTD``
|
||||
``-Wwarn-PINMISSING`` ``-Wwarn-REALCVT`` ``-Wwarn-STATICVAR``
|
||||
``-Wwarn-UNSIGNED`` ``-Wwarn-WIDTHTRUNC`` ``-Wwarn-WIDTHEXPAND``
|
||||
``-Wwarn-WIDTHXZEXPAND``.
|
||||
|
||||
.. option:: -Wwarn-style
|
||||
|
||||
Enable all code style-related warning messages. This is equivalent to
|
||||
``-Wwarn ASSIGNDLY -Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-EOFNEWLINE
|
||||
-Wwarn-GENUNNAMED -Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN
|
||||
-Wwarn-UNUSEDGENVAR -Wwarn-UNUSEDPARAM -Wwarn-UNUSEDSIGNAL -Wwarn-VARHIDDEN``.
|
||||
``-Wwarn-ASSIGNDLY`` ``-Wwarn-DECLFILENAME`` ``-Wwarn-DEFPARAM``
|
||||
``-Wwarn-EOFNEWLINE`` ``-Wwarn-GENUNNAMED`` ``-Wwarn-INCABSPATH``
|
||||
``-Wwarn-PINNOCONNECT`` ``-Wwarn-SYNCASYNCNET`` ``-Wwarn-UNDRIVEN``
|
||||
``-Wwarn-UNUSEDGENVAR`` ``-Wwarn-UNUSEDPARAM`` ``-Wwarn-UNUSEDSIGNAL``
|
||||
``-Wwarn-VARHIDDEN``.
|
||||
|
||||
.. option:: --x-assign 0
|
||||
|
||||
|
||||
@@ -64,30 +64,59 @@ if there are multiple coverage points on a single line they will merge.
|
||||
The minimum coverage across all merged points will be used to report
|
||||
coverage of the line.
|
||||
|
||||
The first character of the line shows a summary of the coverage; this
|
||||
allows use of grep to filter the report. `%` indicates at least one point
|
||||
on the line was below the coverage limit. `+` indicates an
|
||||
:option:`--annotate-points` point was at or above the limit, and `-` that
|
||||
the point was below the limit.
|
||||
Coverage data is annotated at the beginning of the line and is formatted
|
||||
as a special character followed by the number of coverage hits. The special
|
||||
characters " ,%,+,-" indicate summary of the coverage, and allow use of grep
|
||||
to filter the report.
|
||||
|
||||
* " " (whitespace) indicates that all points on the line are above the coverage limit.
|
||||
* "%" indicates at least one point on the line was below the coverage limit.
|
||||
* "+" coverage point was at or above the limit. Only used with :option:`--annotate-points`.
|
||||
* "-" coverage point was below the limit. Only used with :option:`--annotate-points`.
|
||||
|
||||
.. code-block::
|
||||
|
||||
100000 input logic a; // Begins with whitespace, because
|
||||
// number of hits (100000) is above the limit.
|
||||
+100000 point: comment=a // Begins with +, because
|
||||
// number of hits (100000) is above the limit.
|
||||
%000000 input logic b; // Begins with %, because
|
||||
// number of hits (0) is below the limit.
|
||||
-000000 point: comment=b // Begins with -, because
|
||||
// number of hits (0) is below the limit.
|
||||
|
||||
.. option:: --annotate-all
|
||||
|
||||
Specifies all files should be shown. By default, only those source files
|
||||
with low coverage are written to the output directory.
|
||||
|
||||
This option should be used together with :option:`--annotate`.
|
||||
|
||||
.. option:: --annotate-min <count>
|
||||
|
||||
Specifies if the coverage point does not include the count number of
|
||||
coverage hits, then the coverage point will be considered above the
|
||||
threshold, and the coverage report will put a "%" to indicate the coverage
|
||||
is insufficient. Defaults to 10.
|
||||
Specifies the threshold (<count>) below which coverage point is considered
|
||||
sufficient. If the threshold is not exceeded, then the annotation will begin
|
||||
with a "%" symbol to indicate the coverage is insufficient.
|
||||
|
||||
The <count> threshold defaults to 10.
|
||||
|
||||
This option should be used together with :option:`--annotate`.
|
||||
|
||||
|
||||
.. option:: --annotate-points
|
||||
|
||||
Specifies all coverage points should be shown after each line of text. By
|
||||
default, only source lines are shown.
|
||||
|
||||
with low coverage are written to the output directory.
|
||||
.. code-block::
|
||||
|
||||
100000 input logic a, b, c;
|
||||
+100000 point: comment=a // These lines are only shown
|
||||
+200000 point: comment=b // with option --annotate-points
|
||||
+300000 point: comment=c // enabled.
|
||||
|
||||
|
||||
This option should be used together with :option:`--annotate`.
|
||||
|
||||
.. option:: --help
|
||||
|
||||
|
||||
@@ -30,15 +30,13 @@ Gantt Chart VCD Signals
|
||||
In waveforms, there are the following signals. In GTKWave, use "decimal"
|
||||
data format to remove the leading zeros and make the traces easier to read.
|
||||
|
||||
evals
|
||||
Increments each time when eval_step was measured to be active. This
|
||||
allow visualization of how much time eval_step was active.
|
||||
trace/section
|
||||
Shows the name of the current top of the execution section stack.
|
||||
Set GTKWave data format to "ASCII".
|
||||
|
||||
eval_loop
|
||||
Increments each time when the evaluation loop within eval_step was
|
||||
measured to be active. For best performance, there is only a single
|
||||
evaluation loop within each eval_step call; that is, the eval_loop
|
||||
waveform looks identical to the evals waveform.
|
||||
trace/depth
|
||||
Shows the depth of the execution section stack.
|
||||
Set GTKWave data format to "Analog".
|
||||
|
||||
measured_parallelism
|
||||
The number of mtasks active at this time, for best performance, this will
|
||||
|
||||
@@ -17,14 +17,21 @@ or "`ifdef`"'s may break other tools.
|
||||
|
||||
.. option:: `__LINE__
|
||||
|
||||
The :option:`\`__LINE__` define expands to the current filename as a
|
||||
string, like C++'s __LINE__. This Verilator feature added in 2006 was
|
||||
incorporated into IEEE 1800-2009.
|
||||
The :option:`\`__LINE__` define expands to the current line number like
|
||||
C++'s __LINE__. This Verilator feature added in 2006 was incorporated
|
||||
into IEEE 1800-2009.
|
||||
|
||||
.. option:: `error [string]
|
||||
|
||||
This will report an error when encountered, like C++'s #error.
|
||||
|
||||
.. option:: `line
|
||||
|
||||
As a special case `\`line \`__LINE__ "filename"` allows setting the
|
||||
filename, without changing the line number. This is used for some
|
||||
internal tests, so that debugging can leave the line numbers correctly
|
||||
referring to the test file's line numbers.
|
||||
|
||||
.. option:: """ [string] """
|
||||
|
||||
A triple-quoted block specifies a string that may include newlines and
|
||||
|
||||
@@ -126,7 +126,7 @@ Those developing Verilator itself may also want these (see internals.rst):
|
||||
::
|
||||
|
||||
sudo apt-get install clang clang-format-14 cmake gdb gprof graphviz lcov
|
||||
sudo apt-get install libclang-dev yapf3
|
||||
sudo apt-get install libclang-dev yapf3 bear
|
||||
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
|
||||
cpan install Pod::Perldoc
|
||||
cpan install Parallel::Forker
|
||||
|
||||
+18
-37
@@ -237,11 +237,9 @@ optimizations will be disabled around the latch.
|
||||
Structures and Unions
|
||||
---------------------
|
||||
|
||||
Presently Verilator only supports packed structs and packed unions. Rand
|
||||
and randc tags on members are ignored. All structures and unions are
|
||||
represented as a single vector, which means that generating one member of a
|
||||
structure from blocking, and another from non-blocking assignments is
|
||||
unsupported.
|
||||
All structures and unions are scheduled together, which means that
|
||||
generating one member of a structure from blocking, and another from
|
||||
non-blocking assignments is unsupported.
|
||||
|
||||
|
||||
.. _Unknown States:
|
||||
@@ -320,20 +318,6 @@ there will be considered a two-state variable that is read and written
|
||||
instead of a four-state variable.
|
||||
|
||||
|
||||
Functions & Tasks
|
||||
-----------------
|
||||
|
||||
All functions and tasks will be inlined (will not become functions in C.)
|
||||
The only support provided is simple statements in tasks (which may
|
||||
affect global variables).
|
||||
|
||||
Recursive functions and tasks are not supported. All inputs and outputs
|
||||
are automatic as if they had the Verilog 2001 "automatic" keyword
|
||||
prepended. (If you don't know what this means, Verilator will do what you
|
||||
probably expect, what C does. The default behavior of Verilog is
|
||||
different.)
|
||||
|
||||
|
||||
Gate Primitives
|
||||
---------------
|
||||
|
||||
@@ -383,24 +367,6 @@ Verilator does not support SEREs yet. All assertion and coverage
|
||||
statements must be simple expressions that complete in one cycle.
|
||||
|
||||
|
||||
Force statement
|
||||
---------------
|
||||
|
||||
Verilator supports the procedural `force` (and corresponding `release`)
|
||||
statement. However, the behavior of the `force` statement does not entirely
|
||||
comply with IEEE 1800. According to the standard,
|
||||
when a procedural statement of the form `force a = b;` is executed, the
|
||||
simulation should behave as if, from that point forwards, a continuous
|
||||
assignment `assign a = b;` has been added to override the drivers of `a`.
|
||||
More specifically: the value of `a` should be updated whenever the value of
|
||||
`b` changes, until a `release a;` statement is executed.
|
||||
Verilator instead evaluates the current value of `b` when the `force`
|
||||
statement is executed, and forces `a` to that value, without updating it
|
||||
until a new `force` or `release` statement is encountered that applies to
|
||||
`a`. This non-standard behavior is nevertheless consistent with some other
|
||||
simulators.
|
||||
|
||||
|
||||
Encrypted Verilog
|
||||
-----------------
|
||||
|
||||
@@ -471,6 +437,21 @@ disable
|
||||
provide loop break and continue functionality before SystemVerilog added
|
||||
the break and continue keywords.
|
||||
|
||||
force, release
|
||||
Verilator supports the procedural `force` (and corresponding `release`)
|
||||
statement. However, the behavior of the `force` statement does not
|
||||
entirely comply with IEEE 1800. According to the standard, when a
|
||||
procedural statement of the form `force a = b;` is executed, the
|
||||
simulation should behave as if, from that point forwards, a continuous
|
||||
assignment `assign a = b;` has been added to override the drivers of `a`.
|
||||
More specifically: the value of `a` should be updated whenever the value
|
||||
of `b` changes, until a `release a;` statement is executed. Verilator
|
||||
instead evaluates the current value of `b` when the `force` statement is
|
||||
executed, and forces `a` to that value, without updating it until a new
|
||||
`force` or `release` statement is encountered that applies to `a`. This
|
||||
non-standard behavior is nevertheless consistent with some other
|
||||
simulators.
|
||||
|
||||
inside
|
||||
Inside expressions may not include unpacked array traversal or $ as an
|
||||
upper bound. Case inside and case matches are also unsupported.
|
||||
|
||||
@@ -44,7 +44,7 @@ The best place to get started is to try the :ref:`Examples`.
|
||||
.. [#] Verilog is defined by the `Institute of Electrical and Electronics
|
||||
Engineers (IEEE) Standard for Verilog Hardware Description
|
||||
Language`, Std. 1364, released in 1995, 2001, and 2005. The
|
||||
Verilator documentation uses the shorthand, e.g., "IEEE 1394-2005",
|
||||
Verilator documentation uses the shorthand, e.g., "IEEE 1364-2005",
|
||||
to refer to the, e.g., 2005 version of this standard.
|
||||
|
||||
.. [#] SystemVerilog is defined by the `Institute of Electrical and
|
||||
|
||||
+65
-16
@@ -12,10 +12,18 @@ Disabling Warnings
|
||||
|
||||
Warnings may be disabled in multiple ways:
|
||||
|
||||
#. Disable the warning in the source code. When the warning is printed, it
|
||||
will include a warning code. Surround the offending line with a
|
||||
:code:`/*verilator&32;lint_off*/` and :code:`/*verilator&32;lint_on*/`
|
||||
metacomment pair:
|
||||
#. Disable the warning globally by invoking Verilator with the
|
||||
:code:`-Wno-{warning-code}` option.
|
||||
|
||||
Global disables should be avoided, as they removes all checking across
|
||||
the source files, and prevents other users from compiling the sources
|
||||
without knowing the magic set of disables needed to compile those
|
||||
sources successfully.
|
||||
|
||||
#. Disable the warning in the design source code. When the warning is
|
||||
printed, it will include a warning code. Surround the offending line
|
||||
with a :code:`/*verilator&32;lint_off*/` and
|
||||
:code:`/*verilator&32;lint_on*/` metacomment pair:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
@@ -23,6 +31,12 @@ Warnings may be disabled in multiple ways:
|
||||
if (`DEF_THAT_IS_EQ_ZERO <= 3) $stop;
|
||||
// verilator lint_on UNSIGNED
|
||||
|
||||
|
||||
A lint_off in the design source code will propagate down to any child
|
||||
files (files later included by the file with the lint_off), but will not
|
||||
propagate upwards to any parent file (file that included the file with
|
||||
the lint_off).
|
||||
|
||||
#. Disable the warning using :ref:`Configuration Files` with a
|
||||
:option:`lint_off` command. This is useful when a script suppresses
|
||||
warnings, and the Verilog source should not be changed. This method also
|
||||
@@ -32,12 +46,6 @@ Warnings may be disabled in multiple ways:
|
||||
|
||||
lint_off -rule UNSIGNED -file "*/example.v" -line 1
|
||||
|
||||
#. Disable the warning globally invoking Verilator with the
|
||||
:code:`-Wno-{warning-code}` option. This should be avoided, as it
|
||||
removes all checking across the designs, and prevents other users from
|
||||
compiling your code without knowing the magic set of disables needed to
|
||||
compile your design successfully.
|
||||
|
||||
|
||||
Error And Warning Format
|
||||
========================
|
||||
@@ -1166,7 +1174,7 @@ List Of Warnings
|
||||
|
||||
Warns that a feature requires a newer standard of Verilog or SystemVerilog
|
||||
than the one specified by the :vlopt:`--language` option. For example, unsized
|
||||
unbased literals (`'0`, `'1`, `'z`, `'x`) require 1800-2005 or later.
|
||||
unbased literals (`'0`, `'1`, `'z`, `'x`) require IEEE 1800-2005 or later.
|
||||
|
||||
To avoid this warning, use a Verilog or SystemVerilog standard that
|
||||
supports the feature. Alternatively, modify your code to use a different
|
||||
@@ -1360,7 +1368,10 @@ List Of Warnings
|
||||
|
||||
.. option:: RANDC
|
||||
|
||||
Warns that the :code:`randc` keyword is unsupported and being converted
|
||||
Historical, never issued since version 5.018, when :code:`randc` became
|
||||
fully supported.
|
||||
|
||||
Warned that the :code:`randc` keyword was unsupported and was converted
|
||||
to :code:`rand`.
|
||||
|
||||
|
||||
@@ -1503,6 +1514,39 @@ List Of Warnings
|
||||
modeled values, or DPI calls.
|
||||
|
||||
|
||||
.. option:: SIDEEFFECT
|
||||
|
||||
Warns that an expression has a side effect that might not properly be
|
||||
executed by Verilator.
|
||||
|
||||
This often represents a bug in Verilator, as opposed to a bad code
|
||||
construct, however the Verilog code can typically be changed to avoid
|
||||
the warning.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 1
|
||||
|
||||
x = y[a++];
|
||||
|
||||
This example warns because Verilator does not currently handle side
|
||||
effects inside array subscripts; the a++ may be executed multiple times.
|
||||
|
||||
Rewrite the code to avoid expression side effects, typically by using a
|
||||
temporary:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
|
||||
temp = a++;
|
||||
x = y[temp];
|
||||
|
||||
Ignoring this warning may make Verilator simulations differ from other
|
||||
simulators.
|
||||
|
||||
|
||||
.. option:: SPLITVAR
|
||||
|
||||
Warns that a variable with a :option:`/*verilator&32;split_var*/`
|
||||
@@ -1990,11 +2034,15 @@ List Of Warnings
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
wait(0); // Blocks forever
|
||||
wait(1); // Blocks forever
|
||||
|
||||
Warns that a `wait` statement awaits a constant condition, which means it
|
||||
either blocks forever or never blocks.
|
||||
|
||||
As a special case `wait(0)` with the literal constant `0` (as opposed to
|
||||
something that elaborates to zero), does not warn, as it is presumed the
|
||||
code is making the intent clear.
|
||||
|
||||
|
||||
.. option:: WIDTH
|
||||
|
||||
@@ -2039,17 +2087,18 @@ List Of Warnings
|
||||
|
||||
.. include:: ../../docs/gen/ex_WIDTHEXPAND_1_fixed.rst
|
||||
|
||||
|
||||
.. option:: WIDTHTRUNC
|
||||
|
||||
A more granular WIDTH warning, for when a value is truncated
|
||||
A more granular WIDTH warning, for when a value is truncated.
|
||||
|
||||
.. option:: WIDTHEXPAND
|
||||
|
||||
A more granular WIDTH warning, for when a value is zero expanded
|
||||
A more granular WIDTH warning, for when a value is zero expanded.
|
||||
|
||||
.. option:: WIDTHXZEXPAND
|
||||
|
||||
A more granular WIDTH warning, for when a value is X/Z expanded
|
||||
A more granular WIDTH warning, for when a value is X/Z expanded.
|
||||
|
||||
.. option:: WIDTHCONCAT
|
||||
|
||||
|
||||
+18
-5
@@ -1222,9 +1222,9 @@ There are three ways data is passed between visitor functions.
|
||||
|
||||
2. User attributes. Each ``AstNode`` (**Note.** The AST node, not the
|
||||
visitor) has five user attributes, which may be accessed as an
|
||||
integer using the ``user1()`` through ``user5()`` methods, or as a
|
||||
integer using the ``user1()`` through ``user4()`` methods, or as a
|
||||
pointer (of type ``AstNUser``) using the ``user1p()`` through
|
||||
``user5p()`` methods (a common technique lifted from graph traversal
|
||||
``user4p()`` methods (a common technique lifted from graph traversal
|
||||
packages).
|
||||
|
||||
A visitor first clears the one it wants to use by calling
|
||||
@@ -1400,7 +1400,7 @@ Controlling the Test Driver
|
||||
---------------------------
|
||||
|
||||
The test driver script `driver.pl` runs tests; see the `Test Driver`
|
||||
section. The individual test drivers are written in PERL; see `Test
|
||||
section. The individual test drivers are written in Perl; see `Test
|
||||
Language`.
|
||||
|
||||
|
||||
@@ -1432,7 +1432,7 @@ Developers will also want to call ./configure with two extra flags:
|
||||
disabled by default, as SystemC installation problems would otherwise
|
||||
falsely indicate a Verilator problem.
|
||||
|
||||
When enabling the long tests, some additional PERL modules are needed,
|
||||
When enabling the long tests, some additional Perl modules are needed,
|
||||
which you can install using cpan.
|
||||
|
||||
::
|
||||
@@ -1462,6 +1462,19 @@ There are some traps to avoid when running regression tests
|
||||
system-dependent, but on Fedora Linux it would require editing the
|
||||
``/etc/security/limits.conf`` file as root.
|
||||
|
||||
Diffing generated code after changes
|
||||
------------------------------------
|
||||
|
||||
When making a change in the code generation area that should not change the
|
||||
actual emitted code, it is useful to perform a diff to make sure the emitted
|
||||
code really did not change. To do this, the top level Makefile provides the
|
||||
*test-snap* and *test-diff* targets:
|
||||
|
||||
- Run the test suite with ``make test``
|
||||
- Take a snapshot with ``make test-snap``
|
||||
- Apply your changes
|
||||
- Run the test suite again with ``make test``
|
||||
- See the changes in the output with ``make test-diff``
|
||||
|
||||
Continuous Integration
|
||||
----------------------
|
||||
@@ -1640,7 +1653,7 @@ Source file and line
|
||||
file is ``a``, the 26th is ``z``, the 27th is ``aa``, and so on.
|
||||
|
||||
User pointers
|
||||
Shows the value of the node's user1p...user5p, if non-NULL.
|
||||
Shows the value of the node's user1p...user4p, if non-NULL.
|
||||
|
||||
Data type
|
||||
Many nodes have an explicit data type. "@dt=0x..." indicates the
|
||||
|
||||
@@ -35,11 +35,13 @@ Briquet
|
||||
Brownlee
|
||||
Buckenmaier
|
||||
Bybell
|
||||
CLang
|
||||
CMake
|
||||
Cai
|
||||
Cavium
|
||||
Cfuncs
|
||||
Chandan
|
||||
Chih
|
||||
Chitlesh
|
||||
Christophe
|
||||
Cochrane
|
||||
@@ -89,6 +91,7 @@ Engstrom
|
||||
Enzo
|
||||
Eugen
|
||||
Fabrizio
|
||||
Faucher
|
||||
Fekete
|
||||
Ferrandi
|
||||
Flachs
|
||||
@@ -214,6 +217,7 @@ Maupin
|
||||
Mdir
|
||||
Mednick
|
||||
Menküc
|
||||
Michail
|
||||
Michiels
|
||||
Microsystems
|
||||
Milanovic
|
||||
@@ -229,6 +233,7 @@ Mong
|
||||
Muhlestein
|
||||
Multithreaded
|
||||
Multithreading
|
||||
Mykyta
|
||||
NOUNOPTFLAT
|
||||
NaN
|
||||
Nalbantis
|
||||
@@ -263,6 +268,7 @@ Pre
|
||||
Preprocess
|
||||
Pretet
|
||||
Pretl
|
||||
Pringlemeir
|
||||
Priyadharshini
|
||||
Pullup
|
||||
Pulver
|
||||
@@ -277,6 +283,7 @@ Renga
|
||||
Requin
|
||||
Rodionov
|
||||
Rolfe
|
||||
Rontionov
|
||||
Roodselaar
|
||||
Runtime
|
||||
Ruud
|
||||
@@ -415,6 +422,7 @@ arrarys
|
||||
assertOn
|
||||
astgen
|
||||
async
|
||||
atClone
|
||||
ato
|
||||
atoi
|
||||
autoconf
|
||||
@@ -482,6 +490,7 @@ countbits
|
||||
countones
|
||||
cout
|
||||
cpp
|
||||
cppstyle
|
||||
cpu
|
||||
ctor
|
||||
ctrace
|
||||
@@ -516,6 +525,7 @@ doxygen
|
||||
dpiGetContext
|
||||
dpic
|
||||
dsvf
|
||||
dt
|
||||
dtor
|
||||
dumpall
|
||||
dumpfile
|
||||
@@ -653,6 +663,7 @@ ish
|
||||
isunbounded
|
||||
isunknown
|
||||
jobserver
|
||||
jwoutersymatra
|
||||
killua
|
||||
lang
|
||||
lcov
|
||||
@@ -716,6 +727,7 @@ multithreading
|
||||
musl
|
||||
mutexes
|
||||
mux
|
||||
muzafferkal
|
||||
myftptoyman
|
||||
mysignal
|
||||
namespace
|
||||
@@ -754,6 +766,7 @@ pdf
|
||||
perf
|
||||
perftools
|
||||
pgo
|
||||
phelter
|
||||
picoChip
|
||||
pinIndex
|
||||
pinout
|
||||
@@ -771,7 +784,9 @@ pragma
|
||||
pragmas
|
||||
pre
|
||||
precisions
|
||||
precompiled
|
||||
predefines
|
||||
prepareClone
|
||||
prepend
|
||||
prepended
|
||||
preprocess
|
||||
@@ -799,6 +814,7 @@ radix
|
||||
randc
|
||||
randcase
|
||||
randstate
|
||||
raphmaster
|
||||
rarr
|
||||
rdtsc
|
||||
reStructuredText
|
||||
@@ -808,6 +824,7 @@ readmemb
|
||||
readmemh
|
||||
realpath
|
||||
realtime
|
||||
realtobits
|
||||
recoding
|
||||
recrem
|
||||
redeclaring
|
||||
@@ -930,7 +947,9 @@ vc
|
||||
vcd
|
||||
vcddiff
|
||||
vcoverage
|
||||
vdhotre
|
||||
vec
|
||||
ventana
|
||||
ver
|
||||
verFiles
|
||||
verible
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "Vtop.h"
|
||||
|
||||
using namespace sc_core;
|
||||
using namespace sc_dt;
|
||||
|
||||
int sc_main(int argc, char* argv[]) {
|
||||
// This is a more complicated example, please also see the simpler examples/make_hello_c.
|
||||
@@ -52,7 +53,7 @@ int sc_main(int argc, char* argv[]) {
|
||||
Verilated::commandArgs(argc, argv);
|
||||
|
||||
// General logfile
|
||||
ios::sync_with_stdio();
|
||||
std::ios::sync_with_stdio();
|
||||
|
||||
// Define clocks
|
||||
sc_clock clk{"clk", 10, SC_NS, 0.5, 3, SC_NS, true};
|
||||
|
||||
@@ -60,8 +60,8 @@ class VlFileCopy:
|
||||
print("\t%s " % command)
|
||||
status = subprocess.call(command, shell=True)
|
||||
if status != 0:
|
||||
raise Exception("Command failed running Verilator with '" +
|
||||
command + "', stopped")
|
||||
raise RuntimeError("Command failed running Verilator with '" +
|
||||
command + "', stopped")
|
||||
|
||||
|
||||
#######################################################################
|
||||
|
||||
@@ -83,8 +83,8 @@ class VlHierGraph:
|
||||
print("\t%s " % command)
|
||||
status = subprocess.call(command, shell=True)
|
||||
if status != 0:
|
||||
raise Exception("Command failed running Verilator with '" +
|
||||
command + "', stopped")
|
||||
raise RuntimeError("Command failed running Verilator with '" +
|
||||
command + "', stopped")
|
||||
|
||||
|
||||
#######################################################################
|
||||
|
||||
@@ -4181,7 +4181,7 @@ if(!(isfeof=feof(xc->fh)))
|
||||
if((xc->hier.u.attr.subtype == FST_MT_SOURCESTEM)||(xc->hier.u.attr.subtype == FST_MT_SOURCEISTEM))
|
||||
{
|
||||
int sidx_skiplen_dummy = 0;
|
||||
xc->hier.u.attr.arg_from_name = fstGetVarint64((unsigned char *)xc->str_scope_nam, &sidx_skiplen_dummy);
|
||||
xc->hier.u.attr.arg_from_name = fstGetVarint64((unsigned char *)xc->str_scope_attr, &sidx_skiplen_dummy);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
+12
-3
@@ -320,7 +320,7 @@ std::string VlRNG::get_randstate() const VL_MT_UNSAFE {
|
||||
const char* const stateCharsp = reinterpret_cast<const char*>(&m_state);
|
||||
static_assert(sizeof(m_state) == 16, "");
|
||||
std::string result{"R00112233445566770011223344556677"};
|
||||
for (int i = 0; i < sizeof(m_state); ++i) {
|
||||
for (size_t i = 0; i < sizeof(m_state); ++i) {
|
||||
result[1 + i * 2] = 'a' + ((stateCharsp[i] >> 4) & 15);
|
||||
result[1 + i * 2 + 1] = 'a' + (stateCharsp[i] & 15);
|
||||
}
|
||||
@@ -332,7 +332,7 @@ void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE {
|
||||
return;
|
||||
}
|
||||
char* const stateCharsp = reinterpret_cast<char*>(&m_state);
|
||||
for (int i = 0; i < sizeof(m_state); ++i) {
|
||||
for (size_t i = 0; i < sizeof(m_state); ++i) {
|
||||
stateCharsp[i]
|
||||
= (((state[1 + i * 2] - 'a') & 15) << 4) | ((state[1 + i * 2 + 1] - 'a') & 15);
|
||||
}
|
||||
@@ -2094,7 +2094,7 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
||||
|
||||
if (VL_UNLIKELY(m_end != ~0ULL && m_addr <= m_end && !m_anyAddr)) {
|
||||
VL_WARN_MT(m_filename.c_str(), m_linenum, "",
|
||||
"$readmem file ended before specified final address (IEEE 2017 21.4)");
|
||||
"$readmem file ended before specified final address (IEEE 1800-2017 21.4)");
|
||||
}
|
||||
|
||||
return false; // EOF
|
||||
@@ -2608,6 +2608,14 @@ VerilatedVirtualBase* VerilatedContext::threadPoolp() {
|
||||
return m_threadPool.get();
|
||||
}
|
||||
|
||||
void VerilatedContext::prepareClone() { delete m_threadPool.release(); }
|
||||
|
||||
VerilatedVirtualBase* VerilatedContext::threadPoolpOnClone() {
|
||||
if (VL_UNLIKELY(m_threadPool)) m_threadPool.release();
|
||||
m_threadPool = std::unique_ptr<VlThreadPool>(new VlThreadPool{this, m_threads - 1});
|
||||
return m_threadPool.get();
|
||||
}
|
||||
|
||||
VerilatedVirtualBase*
|
||||
VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
|
||||
if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
|
||||
@@ -3016,6 +3024,7 @@ void Verilated::endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
||||
// It should be ok to call into endOfEvalGuts, it returns immediately
|
||||
// if there are no transactions.
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("End-of-eval cleanup\n"););
|
||||
VerilatedThreadMsgQueue::flush(evalMsgQp);
|
||||
evalMsgQp->process();
|
||||
}
|
||||
|
||||
|
||||
+7
-1
@@ -133,7 +133,8 @@ enum VerilatedVarType : uint8_t {
|
||||
VLVT_UINT32, // AKA IData
|
||||
VLVT_UINT64, // AKA QData
|
||||
VLVT_WDATA, // AKA WData
|
||||
VLVT_STRING // C++ string
|
||||
VLVT_STRING, // C++ string
|
||||
VLVT_REAL // AKA double
|
||||
};
|
||||
|
||||
enum VerilatedVarFlags {
|
||||
@@ -164,7 +165,9 @@ inline constexpr size_t roundUpToMultipleOf(size_t value) {
|
||||
// Return current thread ID (or 0), not super fast, cache if needed
|
||||
extern uint32_t VL_THREAD_ID() VL_MT_SAFE;
|
||||
|
||||
#ifndef VL_LOCK_SPINS
|
||||
#define VL_LOCK_SPINS 50000 /// Number of times to spin for a mutex before yielding
|
||||
#endif
|
||||
|
||||
/// Mutex, wrapped to allow -fthread_safety checks
|
||||
class VL_CAPABILITY("mutex") VerilatedMutex final {
|
||||
@@ -568,6 +571,8 @@ public:
|
||||
void addModel(VerilatedModel*);
|
||||
|
||||
VerilatedVirtualBase* threadPoolp();
|
||||
void prepareClone();
|
||||
VerilatedVirtualBase* threadPoolpOnClone();
|
||||
VerilatedVirtualBase*
|
||||
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
|
||||
|
||||
@@ -917,6 +922,7 @@ public:
|
||||
|
||||
// Internal: Called at end of each thread mtask, before finishing eval
|
||||
static void endOfThreadMTask(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
|
||||
mtaskId(0);
|
||||
if (VL_UNLIKELY(t_s.t_endOfEvalReqd)) endOfThreadMTaskGuts(evalMsgQp);
|
||||
}
|
||||
// Internal: Called at end of eval loop
|
||||
|
||||
+52
-14
@@ -9,6 +9,9 @@
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
######################################################################
|
||||
|
||||
# Tool names.
|
||||
# These are computed at configuration time, and most are not ?=
|
||||
# to avoid picking up potentilly incorrect Make implicit variables
|
||||
AR = @AR@
|
||||
CXX = @CXX@
|
||||
LINK = @CXX@
|
||||
@@ -16,6 +19,7 @@ OBJCACHE ?= @OBJCACHE@
|
||||
PERL = @PERL@
|
||||
PYTHON3 = @PYTHON3@
|
||||
|
||||
# Configuration time options
|
||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
|
||||
|
||||
@@ -31,6 +35,12 @@ CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
|
||||
CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
|
||||
# Compiler flags that enable coroutine support
|
||||
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
|
||||
# Compiler flags when creating a precompiled header
|
||||
CFG_CXXFLAGS_PCH = -x c++-header
|
||||
# Compiler option to put in front of filename to read precompiled header
|
||||
CFG_CXXFLAGS_PCH_I = @CFG_CXXFLAGS_PCH_I@
|
||||
# Compiler's filename prefix for precompiled headers, .gch if clang, empty if GCC
|
||||
CFG_GCH_IF_CLANG = @CFG_GCH_IF_CLANG@
|
||||
# Linker flags
|
||||
CFG_LDFLAGS_VERILATED = @CFG_LDFLAGS_VERILATED@
|
||||
# Linker libraries for multithreading
|
||||
@@ -43,6 +53,12 @@ VERILATOR_COVERAGE = $(PERL) $(VERILATOR_ROOT)/bin/verilator_coverage
|
||||
VERILATOR_INCLUDER = $(PYTHON3) $(VERILATOR_ROOT)/bin/verilator_includer
|
||||
VERILATOR_CCACHE_REPORT = $(PYTHON3) $(VERILATOR_ROOT)/bin/verilator_ccache_report
|
||||
|
||||
######################################################################
|
||||
# CCACHE flags (via environment as no command line option available)
|
||||
|
||||
CCACHE_SLOPPINESS ?= pch_defines,time_macros
|
||||
export CCACHE_SLOPPINESS
|
||||
|
||||
######################################################################
|
||||
# Make checks
|
||||
|
||||
@@ -162,11 +178,17 @@ endif
|
||||
VM_FAST += $(VM_CLASSES_FAST) $(VM_SUPPORT_FAST)
|
||||
VM_SLOW += $(VM_CLASSES_SLOW) $(VM_SUPPORT_SLOW)
|
||||
|
||||
# Precompiled header filename
|
||||
VK_PCH_H = $(VM_PREFIX)__pch.h
|
||||
# Compiler read-a-precompiled-header option for precompiled header filename
|
||||
VK_PCH_I_FAST = $(CFG_CXXFLAGS_PCH_I) $(VM_PREFIX)__pch.h.fast$(CFG_GCH_IF_CLANG)
|
||||
VK_PCH_I_SLOW = $(CFG_CXXFLAGS_PCH_I) $(VM_PREFIX)__pch.h.slow$(CFG_GCH_IF_CLANG)
|
||||
|
||||
#######################################################################
|
||||
### Overall Objects Linking
|
||||
|
||||
VK_FAST_OBJS = $(addsuffix .o, $(VM_FAST))
|
||||
VK_SLOW_OBJS = $(addsuffix .o, $(VM_SLOW))
|
||||
VK_OBJS_FAST = $(addsuffix .o, $(VM_FAST))
|
||||
VK_OBJS_SLOW = $(addsuffix .o, $(VM_SLOW))
|
||||
|
||||
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
|
||||
|
||||
@@ -193,7 +215,7 @@ else
|
||||
# Parallel build: Each .cpp file by itself. This can be somewhat slower for
|
||||
# very small designs and examples, but is a lot faster for large designs.
|
||||
|
||||
VK_OBJS += $(VK_FAST_OBJS) $(VK_SLOW_OBJS)
|
||||
VK_OBJS += $(VK_OBJS_FAST) $(VK_OBJS_SLOW)
|
||||
endif
|
||||
|
||||
# When archiving just objects (.o), use single $(AR) run
|
||||
@@ -237,18 +259,32 @@ $(VM_PREFIX)__ALL.a: $(VK_OBJS) $(VM_HIER_LIBS)
|
||||
### Compile rules
|
||||
|
||||
ifneq ($(VM_DEFAULT_RULES),0)
|
||||
# Anything not in $(VK_SLOW_OBJS) or $(VK_GLOBAL_OBJS), including verilated.o
|
||||
# and user files passed on the Verilator command line use this rule.
|
||||
# We put OPT_FAST/OPT_SLOW/OPT_GLOBAL before the other flags to
|
||||
# Compilation rule for anything not in $(VK_OBJS_FAST), $(VK_OBJS_SLOW), or
|
||||
# $(VK_GLOBAL_OBJS) including verilated.o. This typically means user files
|
||||
# passed on the Verilator command line.
|
||||
#
|
||||
# These rules put OPT_FAST/OPT_SLOW/OPT_GLOBAL before the other flags to
|
||||
# allow USER_CPPFLAGS to override them
|
||||
%.o: %.cpp
|
||||
$(OBJCACHE) $(CXX) $(OPT_FAST) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
$(VK_SLOW_OBJS): %.o: %.cpp
|
||||
$(OBJCACHE) $(CXX) $(OPT_SLOW) $(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 $@ $<
|
||||
|
||||
$(VK_OBJS_SLOW): %.o: %.cpp $(VK_PCH_H).slow.gch
|
||||
$(OBJCACHE) $(CXX) $(OPT_SLOW) $(CXXFLAGS) $(CPPFLAGS) $(VK_PCH_I_SLOW) -c -o $@ $<
|
||||
|
||||
$(VK_GLOBAL_OBJS): %.o: %.cpp
|
||||
$(OBJCACHE) $(CXX) $(OPT_GLOBAL) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
# Precompile a header file
|
||||
# PCH's compiler flags must match exactly the rules' above FAST/SLOW
|
||||
# arguments used for the .cpp files, or the PCH file won't be used.
|
||||
%.fast.gch: %
|
||||
$(OBJCACHE) $(CXX) $(OPT_FAST) $(CXXFLAGS) $(CPPFLAGS) $(CFG_CXXFLAGS_PCH) $< -o $@
|
||||
%.slow.gch: %
|
||||
$(OBJCACHE) $(CXX) $(OPT_SLOW) $(CXXFLAGS) $(CPPFLAGS) $(CFG_CXXFLAGS_PCH) $< -o $@
|
||||
|
||||
endif
|
||||
|
||||
#Default rule embedded in make:
|
||||
@@ -296,19 +332,21 @@ endif
|
||||
|
||||
debug-make::
|
||||
@echo
|
||||
@echo CXXFLAGS: $(CXXFLAGS)
|
||||
@echo CPPFLAGS: $(CPPFLAGS)
|
||||
@echo CXXFLAGS: $(CXXFLAGS)
|
||||
@echo OPT_FAST: $(OPT_FAST)
|
||||
@echo OPT_SLOW: $(OPT_SLOW)
|
||||
@echo VM_PREFIX: $(VM_PREFIX)
|
||||
@echo VM_PARALLEL_BUILDS: $(VM_PARALLEL_BUILDS)
|
||||
@echo VK_OBJS: $(VK_OBJS)
|
||||
@echo VK_OBJS_FAST: $(VK_OBJS_FAST)
|
||||
@echo VK_OBJS_SLOW: $(VK_OBJS_SLOW)
|
||||
@echo VM_CLASSES_FAST: $(VM_CLASSES_FAST)
|
||||
@echo VM_CLASSES_SLOW: $(VM_CLASSES_SLOW)
|
||||
@echo VM_SUPPORT_FAST: $(VM_SUPPORT_FAST)
|
||||
@echo VM_SUPPORT_SLOW: $(VM_SUPPORT_SLOW)
|
||||
@echo VM_GLOBAL_FAST: $(VM_GLOBAL_FAST)
|
||||
@echo VM_GLOBAL_SLOW: $(VM_GLOBAL_SLOW)
|
||||
@echo VK_OBJS: $(VK_OBJS)
|
||||
@echo VM_PARALLEL_BUILDS: $(VM_PARALLEL_BUILDS)
|
||||
@echo VM_PREFIX: $(VM_PREFIX)
|
||||
@echo VM_SUPPORT_FAST: $(VM_SUPPORT_FAST)
|
||||
@echo VM_SUPPORT_SLOW: $(VM_SUPPORT_SLOW)
|
||||
@echo
|
||||
|
||||
######################################################################
|
||||
|
||||
+115
-106
@@ -23,7 +23,6 @@
|
||||
|
||||
// clang-format off
|
||||
|
||||
#define __STDC_LIMIT_MACROS // UINT64_MAX
|
||||
#include "verilated.h"
|
||||
#include "verilated_fst_c.h"
|
||||
|
||||
@@ -40,6 +39,7 @@
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
|
||||
# include <io.h>
|
||||
@@ -50,33 +50,10 @@
|
||||
// clang-format on
|
||||
|
||||
//=============================================================================
|
||||
// Check that vltscope_t matches fstScopeType
|
||||
// Check that forward declared types matches the FST API types
|
||||
|
||||
static_assert(static_cast<int>(FST_ST_VCD_MODULE) == static_cast<int>(VLT_TRACE_SCOPE_MODULE),
|
||||
"VLT_TRACE_SCOPE_MODULE mismatches");
|
||||
static_assert(static_cast<int>(FST_ST_VCD_TASK) == static_cast<int>(VLT_TRACE_SCOPE_TASK),
|
||||
"VLT_TRACE_SCOPE_TASK mismatches");
|
||||
static_assert(static_cast<int>(FST_ST_VCD_FUNCTION) == static_cast<int>(VLT_TRACE_SCOPE_FUNCTION),
|
||||
"VLT_TRACE_SCOPE_FUNCTION mismatches");
|
||||
static_assert(static_cast<int>(FST_ST_VCD_BEGIN) == static_cast<int>(VLT_TRACE_SCOPE_BEGIN),
|
||||
"VLT_TRACE_SCOPE_BEGIN mismatches");
|
||||
static_assert(static_cast<int>(FST_ST_VCD_FORK) == static_cast<int>(VLT_TRACE_SCOPE_FORK),
|
||||
"VLT_TRACE_SCOPE_FORK mismatches");
|
||||
static_assert(static_cast<int>(FST_ST_VCD_GENERATE) == static_cast<int>(VLT_TRACE_SCOPE_GENERATE),
|
||||
"VLT_TRACE_SCOPE_GENERATE mismatches");
|
||||
static_assert(static_cast<int>(FST_ST_VCD_STRUCT) == static_cast<int>(VLT_TRACE_SCOPE_STRUCT),
|
||||
"VLT_TRACE_SCOPE_STRUCT mismatches");
|
||||
static_assert(static_cast<int>(FST_ST_VCD_UNION) == static_cast<int>(VLT_TRACE_SCOPE_UNION),
|
||||
"VLT_TRACE_SCOPE_UNION mismatches");
|
||||
static_assert(static_cast<int>(FST_ST_VCD_CLASS) == static_cast<int>(VLT_TRACE_SCOPE_CLASS),
|
||||
"VLT_TRACE_SCOPE_CLASS mismatches");
|
||||
static_assert(static_cast<int>(FST_ST_VCD_INTERFACE)
|
||||
== static_cast<int>(VLT_TRACE_SCOPE_INTERFACE),
|
||||
"VLT_TRACE_SCOPE_INTERFACE mismatches");
|
||||
static_assert(static_cast<int>(FST_ST_VCD_PACKAGE) == static_cast<int>(VLT_TRACE_SCOPE_PACKAGE),
|
||||
"VLT_TRACE_SCOPE_PACKAGE mismatches");
|
||||
static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE_SCOPE_PROGRAM),
|
||||
"VLT_TRACE_SCOPE_PROGRAM mismatches");
|
||||
static_assert(std::is_same<vlFstHandle, fstHandle>::value, "vlFstHandle mismatch");
|
||||
static_assert(std::is_same<vlFstEnumHandle, fstEnumHandle>::value, "vlFstHandle mismatch");
|
||||
|
||||
//=============================================================================
|
||||
// Specialization of the generics for this trace format
|
||||
@@ -104,19 +81,11 @@ 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);
|
||||
constDump(true); // First dump must contain the const signals
|
||||
fullDump(true); // First dump must be full for fst
|
||||
|
||||
m_curScope.clear();
|
||||
|
||||
Super::traceInit();
|
||||
|
||||
// Clear the scope stack
|
||||
auto it = m_curScope.begin();
|
||||
while (it != m_curScope.end()) {
|
||||
fstWriterSetUpscope(m_fst);
|
||||
it = m_curScope.erase(it);
|
||||
}
|
||||
|
||||
// convert m_code2symbol into an array for fast lookup
|
||||
if (!m_symbolp) {
|
||||
m_symbolp = new fstHandle[nextCode()]{0};
|
||||
@@ -154,102 +123,142 @@ void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, uint32_t elemen
|
||||
m_local2fstdtype[dtypenum] = enumNum;
|
||||
}
|
||||
|
||||
void VerilatedFst::declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, bool bussed, int msb,
|
||||
int lsb) {
|
||||
// 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 std::string& name, VerilatedTracePrefixType type) {
|
||||
const std::string newPrefix = m_prefixStack.back().first + 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);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedFst::popPrefix() {
|
||||
const bool properScope = toFstScopeType(m_prefixStack.back().second).first;
|
||||
if (properScope) fstWriterSetUpscope(m_fst);
|
||||
m_prefixStack.pop_back();
|
||||
assert(!m_prefixStack.empty());
|
||||
}
|
||||
|
||||
void VerilatedFst::declare(uint32_t code, const char* name, int dtypenum,
|
||||
VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
|
||||
VerilatedTraceSigType type, bool array, int arraynum, bool bussed,
|
||||
int msb, int lsb) {
|
||||
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
|
||||
|
||||
const bool enabled = Super::declCode(code, name, bits, false);
|
||||
const std::string hierarchicalName = m_prefixStack.back().first + name;
|
||||
|
||||
const bool enabled = Super::declCode(code, hierarchicalName, bits);
|
||||
if (!enabled) return;
|
||||
|
||||
std::string nameasstr = namePrefix() + name;
|
||||
std::istringstream nameiss{nameasstr};
|
||||
std::istream_iterator<std::string> beg(nameiss);
|
||||
std::istream_iterator<std::string> end;
|
||||
std::list<std::string> tokens(beg, end); // Split name
|
||||
std::string symbol_name{tokens.back()};
|
||||
tokens.pop_back(); // Remove symbol name from hierarchy
|
||||
std::string tmpModName;
|
||||
|
||||
// Find point where current and new scope diverge
|
||||
auto cur_it = m_curScope.begin();
|
||||
auto new_it = tokens.begin();
|
||||
while (cur_it != m_curScope.end() && new_it != tokens.end()) {
|
||||
if (*cur_it != *new_it) break;
|
||||
++cur_it;
|
||||
++new_it;
|
||||
}
|
||||
|
||||
// Go back to the common point
|
||||
while (cur_it != m_curScope.end()) {
|
||||
fstWriterSetUpscope(m_fst);
|
||||
cur_it = m_curScope.erase(cur_it);
|
||||
}
|
||||
|
||||
// Follow the hierarchy of the new variable from the common scope point
|
||||
while (new_it != tokens.end()) {
|
||||
if ((new_it->back() & 0x80)) {
|
||||
tmpModName = *new_it;
|
||||
tmpModName.pop_back();
|
||||
// If the scope ends with a non-ASCII character, it will be 0x80 + fstScopeType
|
||||
fstWriterSetScope(m_fst, static_cast<fstScopeType>(new_it->back() & 0x7f),
|
||||
tmpModName.c_str(), nullptr);
|
||||
} else {
|
||||
fstWriterSetScope(m_fst, FST_ST_VCD_SCOPE, new_it->c_str(), nullptr);
|
||||
}
|
||||
m_curScope.push_back(*new_it);
|
||||
new_it = tokens.erase(new_it);
|
||||
}
|
||||
|
||||
assert(hierarchicalName.rfind(' ') != std::string::npos);
|
||||
std::stringstream name_ss;
|
||||
name_ss << symbol_name;
|
||||
name_ss << lastWord(hierarchicalName);
|
||||
if (array) name_ss << "[" << arraynum << "]";
|
||||
if (bussed) name_ss << " [" << msb << ":" << lsb << "]";
|
||||
std::string name_str = name_ss.str();
|
||||
const std::string name_str = name_ss.str();
|
||||
|
||||
if (dtypenum > 0) {
|
||||
const fstEnumHandle enumNum = m_local2fstdtype[dtypenum];
|
||||
fstWriterEmitEnumTableRef(m_fst, enumNum);
|
||||
if (dtypenum > 0) fstWriterEmitEnumTableRef(m_fst, m_local2fstdtype[dtypenum]);
|
||||
|
||||
fstVarDir varDir;
|
||||
switch (direction) {
|
||||
case VerilatedTraceSigDirection::INOUT: varDir = FST_VD_INOUT; break;
|
||||
case VerilatedTraceSigDirection::OUTPUT: varDir = FST_VD_OUTPUT; break;
|
||||
case VerilatedTraceSigDirection::INPUT: varDir = FST_VD_INPUT; break;
|
||||
case VerilatedTraceSigDirection::NONE: varDir = FST_VD_IMPLICIT; break;
|
||||
}
|
||||
|
||||
fstVarType varType;
|
||||
// Doubles have special decoding properties, so must indicate if a double
|
||||
if (type == VerilatedTraceSigType::DOUBLE) {
|
||||
if (kind == VerilatedTraceSigKind::PARAMETER) {
|
||||
varType = FST_VT_VCD_REAL_PARAMETER;
|
||||
} else {
|
||||
varType = FST_VT_VCD_REAL;
|
||||
}
|
||||
}
|
||||
// clang-format off
|
||||
else if (kind == VerilatedTraceSigKind::PARAMETER) varType = FST_VT_VCD_PARAMETER;
|
||||
else if (kind == VerilatedTraceSigKind::SUPPLY0) varType = FST_VT_VCD_SUPPLY0;
|
||||
else if (kind == VerilatedTraceSigKind::SUPPLY1) varType = FST_VT_VCD_SUPPLY1;
|
||||
else if (kind == VerilatedTraceSigKind::TRI) varType = FST_VT_VCD_TRI;
|
||||
else if (kind == VerilatedTraceSigKind::TRI0) varType = FST_VT_VCD_TRI0;
|
||||
else if (kind == VerilatedTraceSigKind::TRI1) varType = FST_VT_VCD_TRI1;
|
||||
else if (kind == VerilatedTraceSigKind::WIRE) varType = FST_VT_VCD_WIRE;
|
||||
//
|
||||
else if (type == VerilatedTraceSigType::INTEGER) varType = FST_VT_VCD_INTEGER;
|
||||
else if (type == VerilatedTraceSigType::BIT) varType = FST_VT_SV_BIT;
|
||||
else if (type == VerilatedTraceSigType::LOGIC) varType = FST_VT_SV_LOGIC;
|
||||
else if (type == VerilatedTraceSigType::INT) varType = FST_VT_SV_INT;
|
||||
else if (type == VerilatedTraceSigType::SHORTINT) varType = FST_VT_SV_SHORTINT;
|
||||
else if (type == VerilatedTraceSigType::LONGINT) varType = FST_VT_SV_LONGINT;
|
||||
else if (type == VerilatedTraceSigType::BYTE) varType = FST_VT_SV_BYTE;
|
||||
else if (type == VerilatedTraceSigType::EVENT) varType = FST_VT_VCD_EVENT;
|
||||
else if (type == VerilatedTraceSigType::TIME) varType = FST_VT_VCD_TIME;
|
||||
else { assert(0); /* Unreachable */ }
|
||||
// clang-format on
|
||||
|
||||
const auto it = vlstd::as_const(m_code2symbol).find(code);
|
||||
if (it == m_code2symbol.end()) { // New
|
||||
m_code2symbol[code]
|
||||
= fstWriterCreateVar(m_fst, vartype, vardir, bits, name_str.c_str(), 0);
|
||||
= fstWriterCreateVar(m_fst, varType, varDir, bits, name_str.c_str(), 0);
|
||||
} else { // Alias
|
||||
fstWriterCreateVar(m_fst, vartype, vardir, bits, name_str.c_str(), it->second);
|
||||
fstWriterCreateVar(m_fst, varType, varDir, bits, name_str.c_str(), it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedFst::declEvent(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 0, 0);
|
||||
void VerilatedFst::declEvent(uint32_t code, uint32_t fidx, 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);
|
||||
}
|
||||
void VerilatedFst::declBit(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 0, 0);
|
||||
void VerilatedFst::declBit(uint32_t code, uint32_t fidx, 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);
|
||||
}
|
||||
void VerilatedFst::declBus(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
|
||||
void VerilatedFst::declBus(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::declQuad(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
|
||||
void VerilatedFst::declQuad(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::declArray(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, 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::declDouble(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 63, 0);
|
||||
void VerilatedFst::declDouble(uint32_t code, uint32_t fidx, 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);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Get/commit trace buffer
|
||||
|
||||
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer() {
|
||||
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer(uint32_t fidx) {
|
||||
if (offload()) return new OffloadBuffer{*this};
|
||||
return new Buffer{*this};
|
||||
}
|
||||
@@ -284,7 +293,7 @@ void VerilatedFst::configure(const VerilatedTraceConfig& config) {
|
||||
// so always inline them.
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFstBuffer::emitEvent(uint32_t code, VlEvent newval) {
|
||||
void VerilatedFstBuffer::emitEvent(uint32_t code, const VlEventBase* newval) {
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], "1");
|
||||
}
|
||||
|
||||
+37
-23
@@ -24,13 +24,14 @@
|
||||
#include "verilated.h"
|
||||
#include "verilated_trace.h"
|
||||
|
||||
#include "gtkwave/fstapi.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
typedef uint32_t vlFstHandle;
|
||||
typedef uint32_t vlFstEnumHandle;
|
||||
|
||||
class VerilatedFstBuffer;
|
||||
|
||||
//=============================================================================
|
||||
@@ -49,18 +50,22 @@ private:
|
||||
// FST specific internals
|
||||
|
||||
void* m_fst = nullptr;
|
||||
std::map<uint32_t, fstHandle> m_code2symbol;
|
||||
std::map<int, fstEnumHandle> m_local2fstdtype;
|
||||
std::list<std::string> m_curScope;
|
||||
fstHandle* m_symbolp = nullptr; // same as m_code2symbol, but as an array
|
||||
std::map<uint32_t, vlFstHandle> m_code2symbol;
|
||||
std::map<int, vlFstEnumHandle> m_local2fstdtype;
|
||||
vlFstHandle* m_symbolp = nullptr; // same as m_code2symbol, but as an array
|
||||
char* m_strbufp = nullptr; // String buffer long enough to hold maxBits() chars
|
||||
|
||||
bool m_useFstWriterThread = false; // Whether to use the separate FST writer thread
|
||||
|
||||
// Prefixes to add to signal names/scope types
|
||||
std::vector<std::pair<std::string, VerilatedTracePrefixType>> m_prefixStack{
|
||||
{"", VerilatedTracePrefixType::SCOPE_MODULE}};
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedFst);
|
||||
void declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, bool bussed, int msb, int lsb);
|
||||
void declare(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
|
||||
VerilatedTraceSigKind, VerilatedTraceSigType, bool array, int arraynum,
|
||||
bool bussed, int msb, int lsb);
|
||||
|
||||
protected:
|
||||
//=========================================================================
|
||||
@@ -74,7 +79,7 @@ protected:
|
||||
bool preChangeDump() override { return isOpen(); }
|
||||
|
||||
// Trace buffer management
|
||||
Buffer* getTraceBuffer() override;
|
||||
Buffer* getTraceBuffer(uint32_t fidx) override;
|
||||
void commitTraceBuffer(Buffer*) override;
|
||||
|
||||
// Configure sub-class
|
||||
@@ -101,18 +106,27 @@ public:
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
void declEvent(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
void declBit(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
void declBus(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
void pushPrefix(const std::string&, 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);
|
||||
|
||||
void declDTypeEnum(int dtypenum, const char* name, uint32_t elements, unsigned int minValbits,
|
||||
const char** itemNamesp, const char** itemValuesp);
|
||||
@@ -149,7 +163,7 @@ class VerilatedFstBuffer VL_NOT_FINAL {
|
||||
// The FST file handle
|
||||
void* const m_fst = m_owner.m_fst;
|
||||
// code to fstHande map, as an array
|
||||
const fstHandle* const m_symbolp = m_owner.m_symbolp;
|
||||
const vlFstHandle* const m_symbolp = m_owner.m_symbolp;
|
||||
// String buffer long enough to hold maxBits() chars
|
||||
char* const m_strbufp = m_owner.m_strbufp;
|
||||
|
||||
@@ -163,7 +177,7 @@ class VerilatedFstBuffer VL_NOT_FINAL {
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
|
||||
// called from only one place (the full* methods), so always inline them.
|
||||
VL_ATTR_ALWINLINE void emitEvent(uint32_t code, VlEvent newval);
|
||||
VL_ATTR_ALWINLINE void emitEvent(uint32_t code, const VlEventBase* newval);
|
||||
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
|
||||
|
||||
+100
-63
@@ -344,8 +344,8 @@ uint64_t vl_time_pow10(int n) VL_PURE;
|
||||
|
||||
// Output clean
|
||||
// EMIT_RULE: VL_CLEAN: oclean=clean; obits=lbits;
|
||||
#define VL_CLEAN_II(obits, lbits, lhs) ((lhs)&VL_MASK_I(obits))
|
||||
#define VL_CLEAN_QQ(obits, lbits, lhs) ((lhs)&VL_MASK_Q(obits))
|
||||
#define VL_CLEAN_II(obits, lbits, lhs) ((lhs) & (VL_MASK_I(obits)))
|
||||
#define VL_CLEAN_QQ(obits, lbits, lhs) ((lhs) & (VL_MASK_Q(obits)))
|
||||
|
||||
// EMIT_RULE: VL_ASSIGNCLEAN: oclean=clean; obits==lbits;
|
||||
#define VL_ASSIGNCLEAN_W(obits, owp, lwp) VL_CLEAN_WW((obits), (owp), (lwp))
|
||||
@@ -449,7 +449,7 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
||||
#define VL_ASSIGN_WSB(obits, owp, svar) \
|
||||
{ \
|
||||
const int words = VL_WORDS_I(obits); \
|
||||
sc_biguint<(obits)> _butemp = (svar).read(); \
|
||||
sc_dt::sc_biguint<(obits)> _butemp = (svar).read(); \
|
||||
uint32_t* chunkp = _butemp.get_raw(); \
|
||||
int32_t lsb = 0; \
|
||||
while (lsb < obits - BITS_PER_DIGIT) { \
|
||||
@@ -475,20 +475,20 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
||||
|
||||
#define VL_ASSIGN_SWI(obits, svar, rd) \
|
||||
{ \
|
||||
sc_bv<(obits)> _bvtemp; \
|
||||
sc_dt::sc_bv<(obits)> _bvtemp; \
|
||||
_bvtemp.set_word(0, (rd)); \
|
||||
(svar).write(_bvtemp); \
|
||||
}
|
||||
#define VL_ASSIGN_SWQ(obits, svar, rd) \
|
||||
{ \
|
||||
sc_bv<(obits)> _bvtemp; \
|
||||
sc_dt::sc_bv<(obits)> _bvtemp; \
|
||||
_bvtemp.set_word(0, static_cast<IData>(rd)); \
|
||||
_bvtemp.set_word(1, static_cast<IData>((rd) >> VL_IDATASIZE)); \
|
||||
(svar).write(_bvtemp); \
|
||||
}
|
||||
#define VL_ASSIGN_SWW(obits, svar, rwp) \
|
||||
{ \
|
||||
sc_bv<(obits)> _bvtemp; \
|
||||
sc_dt::sc_bv<(obits)> _bvtemp; \
|
||||
for (int i = 0; i < VL_WORDS_I(obits); ++i) _bvtemp.set_word(i, (rwp)[i]); \
|
||||
(svar).write(_bvtemp); \
|
||||
}
|
||||
@@ -504,7 +504,7 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
||||
#define VL_SC_BITS_PER_DIGIT 30 // This comes from sc_nbdefs.h BITS_PER_DIGIT
|
||||
#define VL_ASSIGN_SBW(obits, svar, rwp) \
|
||||
{ \
|
||||
sc_biguint<(obits)> _butemp; \
|
||||
sc_dt::sc_biguint<(obits)> _butemp; \
|
||||
int32_t lsb = 0; \
|
||||
uint32_t* chunkp = _butemp.get_raw(); \
|
||||
while (lsb + VL_SC_BITS_PER_DIGIT < (obits)) { \
|
||||
@@ -1437,7 +1437,7 @@ static inline IData VL_STREAML_FAST_III(int lbits, IData ld, IData rd_log2) VL_P
|
||||
if (rd_log2) {
|
||||
const uint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits
|
||||
const uint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS)
|
||||
const IData msbMask = VL_MASK_I(lbitsRem) << lbitsFloor; // mask to sel only bits in MSS
|
||||
const IData msbMask = lbitsFloor == 32 ? 0UL : VL_MASK_I(lbitsRem) << lbitsFloor;
|
||||
ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem));
|
||||
}
|
||||
switch (rd_log2) {
|
||||
@@ -1526,6 +1526,28 @@ static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const
|
||||
return owp;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void VL_ASSIGN_DYN_Q(VlQueue<T>& q, int elem_size, int lbits, QData from) {
|
||||
const int size = (lbits + elem_size - 1) / elem_size;
|
||||
q.renew(size);
|
||||
const QData mask = VL_MASK_Q(elem_size);
|
||||
for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline IData VL_DYN_TO_I(const VlQueue<T>& q, int elem_size) {
|
||||
IData ret = 0;
|
||||
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline QData VL_DYN_TO_Q(const VlQueue<T>& q, int elem_size) {
|
||||
QData ret = 0;
|
||||
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Because concats are common and wide, it's valuable to always have a clean output.
|
||||
// Thus we specify inputs must be clean, so we don't need to clean the output.
|
||||
// Note the bit shifts are always constants, so the adds in these constify out.
|
||||
@@ -1625,6 +1647,24 @@ static inline void _vl_shiftl_inplace_w(int obits, WDataOutP iowp,
|
||||
// EMIT_RULE: VL_SHIFTL: 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?)
|
||||
// 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 {
|
||||
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
|
||||
return lhs << rhs; // Small is common so not clean return
|
||||
}
|
||||
static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE {
|
||||
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 {
|
||||
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
|
||||
return lhs << rhs; // Small is common so not clean return
|
||||
}
|
||||
static inline QData VL_SHIFTL_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
|
||||
return VL_CLEAN_QQ(obits, obits, lhs << rhs);
|
||||
}
|
||||
static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp,
|
||||
IData rd) VL_MT_SAFE {
|
||||
const int word_shift = VL_BITWORD_E(rd);
|
||||
@@ -1662,11 +1702,7 @@ static inline IData VL_SHIFTL_IIW(int obits, int, int rbits, IData lhs,
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return VL_CLEAN_II(obits, obits, lhs << rwp[0]);
|
||||
}
|
||||
static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
|
||||
return VL_CLEAN_II(obits, obits, lhs << rhs);
|
||||
return VL_SHIFTL_III(obits, obits, 32, lhs, rwp[0]);
|
||||
}
|
||||
static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs,
|
||||
WDataInP const rwp) VL_MT_SAFE {
|
||||
@@ -1676,16 +1712,28 @@ static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs,
|
||||
}
|
||||
}
|
||||
// Above checks rwp[1]==0 so not needed in below shift
|
||||
return VL_CLEAN_QQ(obits, obits, lhs << (static_cast<QData>(rwp[0])));
|
||||
}
|
||||
static inline QData VL_SHIFTL_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
|
||||
return VL_CLEAN_QQ(obits, obits, lhs << rhs);
|
||||
return VL_SHIFTL_QQI(obits, obits, 32, lhs, rwp[0]);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
|
||||
return lhs >> rhs; // Small is common so assumed not clean
|
||||
}
|
||||
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 VL_CLEAN_QQ(obits, obits, lhs >> rhs);
|
||||
}
|
||||
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; // Small is common so assumed not clean
|
||||
}
|
||||
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 VL_CLEAN_QQ(obits, obits, lhs >> rhs);
|
||||
}
|
||||
static inline WDataOutP VL_SHIFTR_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp,
|
||||
IData rd) VL_MT_SAFE {
|
||||
const int word_shift = VL_BITWORD_E(rd); // Maybe 0
|
||||
@@ -1729,29 +1777,16 @@ static inline WDataOutP VL_SHIFTR_WWQ(int obits, int lbits, int rbits, WDataOutP
|
||||
static inline IData VL_SHIFTR_IIW(int obits, int, int rbits, IData lhs,
|
||||
WDataInP const rwp) VL_PURE {
|
||||
for (int i = 1; i < VL_WORDS_I(rbits); ++i) {
|
||||
if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more
|
||||
return 0;
|
||||
}
|
||||
if (VL_UNLIKELY(rwp[i])) return 0; // Huge shift 1>>32 or more
|
||||
}
|
||||
return VL_CLEAN_II(obits, obits, lhs >> rwp[0]);
|
||||
return VL_SHIFTR_III(obits, obits, 32, lhs, rwp[0]);
|
||||
}
|
||||
static inline QData VL_SHIFTR_QQW(int obits, int, int rbits, QData lhs,
|
||||
WDataInP const rwp) VL_PURE {
|
||||
for (int i = 1; i < VL_WORDS_I(rbits); ++i) {
|
||||
if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more
|
||||
return 0;
|
||||
}
|
||||
if (VL_UNLIKELY(rwp[i])) return 0; // Huge shift 1>>32 or more
|
||||
}
|
||||
// Above checks rwp[1]==0 so not needed in below shift
|
||||
return VL_CLEAN_QQ(obits, obits, lhs >> (static_cast<QData>(rwp[0])));
|
||||
}
|
||||
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 VL_CLEAN_QQ(obits, obits, lhs >> rhs);
|
||||
}
|
||||
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 VL_CLEAN_QQ(obits, obits, lhs >> rhs);
|
||||
return VL_SHIFTR_QQI(obits, obits, 32, lhs, rwp[0]);
|
||||
}
|
||||
|
||||
// EMIT_RULE: VL_SHIFTRS: oclean=false; lclean=clean, rclean==clean;
|
||||
@@ -1761,11 +1796,13 @@ static inline IData VL_SHIFTRS_III(int obits, int lbits, int, IData lhs, IData r
|
||||
// must use lbits for sign; lbits might != obits,
|
||||
// an EXTEND(SHIFTRS(...)) can became a SHIFTRS(...) within same 32/64 bit word length
|
||||
const IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative
|
||||
if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return sign & VL_MASK_I(obits);
|
||||
const IData signext = ~(VL_MASK_I(lbits) >> rhs); // One with bits where we've shifted "past"
|
||||
return (lhs >> rhs) | (sign & VL_CLEAN_II(obits, obits, signext));
|
||||
}
|
||||
static inline QData VL_SHIFTRS_QQI(int obits, int lbits, int, QData lhs, IData rhs) VL_PURE {
|
||||
const QData sign = -(lhs >> (lbits - 1));
|
||||
if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return sign & VL_MASK_Q(obits);
|
||||
const QData signext = ~(VL_MASK_Q(lbits) >> rhs);
|
||||
return (lhs >> rhs) | (sign & VL_CLEAN_QQ(obits, obits, signext));
|
||||
}
|
||||
@@ -2089,60 +2126,60 @@ static inline WDataOutP VL_CONST_W_8X(int obits, WDataOutP o,
|
||||
VL_C_END_(obits, 8);
|
||||
}
|
||||
//
|
||||
static inline WDataOutP VL_CONSTHI_W_1X(int obits, int lsb, WDataOutP obase,
|
||||
static inline WDataOutP VL_CONSTHI_W_1X(int obits, int lsb, WDataOutP o,
|
||||
EData d0) VL_MT_SAFE {
|
||||
WDataOutP o = obase + VL_WORDS_I(lsb);
|
||||
o[0] = d0;
|
||||
WDataOutP ohi = o + VL_WORDS_I(lsb);
|
||||
ohi[0] = d0;
|
||||
VL_C_END_(obits, VL_WORDS_I(lsb) + 1);
|
||||
}
|
||||
static inline WDataOutP VL_CONSTHI_W_2X(int obits, int lsb, WDataOutP obase,
|
||||
static inline WDataOutP VL_CONSTHI_W_2X(int obits, int lsb, WDataOutP o,
|
||||
EData d1, EData d0) VL_MT_SAFE {
|
||||
WDataOutP o = obase + VL_WORDS_I(lsb);
|
||||
o[0] = d0; o[1] = d1;
|
||||
WDataOutP ohi = o + VL_WORDS_I(lsb);
|
||||
ohi[0] = d0; ohi[1] = d1;
|
||||
VL_C_END_(obits, VL_WORDS_I(lsb) + 2);
|
||||
}
|
||||
static inline WDataOutP VL_CONSTHI_W_3X(int obits, int lsb, WDataOutP obase,
|
||||
static inline WDataOutP VL_CONSTHI_W_3X(int obits, int lsb, WDataOutP o,
|
||||
EData d2, EData d1, EData d0) VL_MT_SAFE {
|
||||
WDataOutP o = obase + VL_WORDS_I(lsb);
|
||||
o[0] = d0; o[1] = d1; o[2] = d2;
|
||||
WDataOutP ohi = o + VL_WORDS_I(lsb);
|
||||
ohi[0] = d0; ohi[1] = d1; ohi[2] = d2;
|
||||
VL_C_END_(obits, VL_WORDS_I(lsb) + 3);
|
||||
}
|
||||
static inline WDataOutP VL_CONSTHI_W_4X(int obits, int lsb, WDataOutP obase,
|
||||
static inline WDataOutP VL_CONSTHI_W_4X(int obits, int lsb, WDataOutP o,
|
||||
EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
|
||||
WDataOutP o = obase + VL_WORDS_I(lsb);
|
||||
o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3;
|
||||
WDataOutP ohi = o + VL_WORDS_I(lsb);
|
||||
ohi[0] = d0; ohi[1] = d1; ohi[2] = d2; ohi[3] = d3;
|
||||
VL_C_END_(obits, VL_WORDS_I(lsb) + 4);
|
||||
}
|
||||
static inline WDataOutP VL_CONSTHI_W_5X(int obits, int lsb, WDataOutP obase,
|
||||
static inline WDataOutP VL_CONSTHI_W_5X(int obits, int lsb, WDataOutP o,
|
||||
EData d4,
|
||||
EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
|
||||
WDataOutP o = obase + VL_WORDS_I(lsb);
|
||||
o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3;
|
||||
o[4] = d4;
|
||||
WDataOutP ohi = o + VL_WORDS_I(lsb);
|
||||
ohi[0] = d0; ohi[1] = d1; ohi[2] = d2; ohi[3] = d3;
|
||||
ohi[4] = d4;
|
||||
VL_C_END_(obits, VL_WORDS_I(lsb) + 5);
|
||||
}
|
||||
static inline WDataOutP VL_CONSTHI_W_6X(int obits, int lsb, WDataOutP obase,
|
||||
static inline WDataOutP VL_CONSTHI_W_6X(int obits, int lsb, WDataOutP o,
|
||||
EData d5, EData d4,
|
||||
EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
|
||||
WDataOutP o = obase + VL_WORDS_I(lsb);
|
||||
o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3;
|
||||
o[4] = d4; o[5] = d5;
|
||||
WDataOutP ohi = o + VL_WORDS_I(lsb);
|
||||
ohi[0] = d0; ohi[1] = d1; ohi[2] = d2; ohi[3] = d3;
|
||||
ohi[4] = d4; ohi[5] = d5;
|
||||
VL_C_END_(obits, VL_WORDS_I(lsb) + 6);
|
||||
}
|
||||
static inline WDataOutP VL_CONSTHI_W_7X(int obits, int lsb, WDataOutP obase,
|
||||
static inline WDataOutP VL_CONSTHI_W_7X(int obits, int lsb, WDataOutP o,
|
||||
EData d6, EData d5, EData d4,
|
||||
EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
|
||||
WDataOutP o = obase + VL_WORDS_I(lsb);
|
||||
o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3;
|
||||
o[4] = d4; o[5] = d5; o[6] = d6;
|
||||
WDataOutP ohi = o + VL_WORDS_I(lsb);
|
||||
ohi[0] = d0; ohi[1] = d1; ohi[2] = d2; ohi[3] = d3;
|
||||
ohi[4] = d4; ohi[5] = d5; ohi[6] = d6;
|
||||
VL_C_END_(obits, VL_WORDS_I(lsb) + 7);
|
||||
}
|
||||
static inline WDataOutP VL_CONSTHI_W_8X(int obits, int lsb, WDataOutP obase,
|
||||
static inline WDataOutP VL_CONSTHI_W_8X(int obits, int lsb, WDataOutP o,
|
||||
EData d7, EData d6, EData d5, EData d4,
|
||||
EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
|
||||
WDataOutP o = obase + VL_WORDS_I(lsb);
|
||||
o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3;
|
||||
o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7;
|
||||
WDataOutP ohi = o + VL_WORDS_I(lsb);
|
||||
ohi[0] = d0; ohi[1] = d1; ohi[2] = d2; ohi[3] = d3;
|
||||
ohi[4] = d4; ohi[5] = d5; ohi[6] = d6; ohi[7] = d7;
|
||||
VL_C_END_(obits, VL_WORDS_I(lsb) + 8);
|
||||
}
|
||||
|
||||
|
||||
@@ -161,12 +161,18 @@ void VlExecutionProfiler::dump(const char* filenamep, uint64_t tickEnd)
|
||||
|
||||
// TODO Perhaps merge with verilated_coverage output format, so can
|
||||
// have a common merging and reporting tool, etc.
|
||||
fprintf(fp, "VLPROFVERSION 2.0 # Verilator execution profile version 2.0\n");
|
||||
fprintf(fp, "VLPROFVERSION 2.1 # Verilator execution profile version 2.1\n");
|
||||
fprintf(fp, "VLPROF arg +verilator+prof+exec+start+%" PRIu64 "\n",
|
||||
Verilated::threadContextp()->profExecStart());
|
||||
fprintf(fp, "VLPROF arg +verilator+prof+exec+window+%u\n",
|
||||
Verilated::threadContextp()->profExecWindow());
|
||||
const unsigned threads = static_cast<unsigned>(m_traceps.size());
|
||||
// Note that VerilatedContext will by default create as many threads as there are hardware
|
||||
// processors, but not all of them might be utilized. Report the actual number that has trace
|
||||
// entries to avoid over-counting.
|
||||
unsigned threads = 0;
|
||||
for (const auto& pair : m_traceps) {
|
||||
if (!pair.second->empty()) ++threads;
|
||||
}
|
||||
fprintf(fp, "VLPROF stat threads %u\n", threads);
|
||||
fprintf(fp, "VLPROF stat yields %" PRIu64 "\n", VlMTaskVertex::yields());
|
||||
|
||||
@@ -183,6 +189,7 @@ void VlExecutionProfiler::dump(const char* filenamep, uint64_t tickEnd)
|
||||
for (const auto& pair : m_traceps) {
|
||||
const uint32_t threadId = pair.first;
|
||||
ExecutionTrace* const tracep = pair.second;
|
||||
if (tracep->empty()) continue;
|
||||
fprintf(fp, "VLPROFTHREAD %" PRIu32 "\n", threadId);
|
||||
|
||||
for (const VlExecutionRecord& er : *tracep) {
|
||||
@@ -191,10 +198,9 @@ void VlExecutionProfiler::dump(const char* filenamep, uint64_t tickEnd)
|
||||
fprintf(fp, "VLPROFEXEC %s %" PRIu64, name, time);
|
||||
|
||||
switch (er.m_type) {
|
||||
case VlExecutionRecord::Type::EVAL_BEGIN:
|
||||
case VlExecutionRecord::Type::EVAL_END:
|
||||
case VlExecutionRecord::Type::EVAL_LOOP_BEGIN:
|
||||
case VlExecutionRecord::Type::EVAL_LOOP_END:
|
||||
case VlExecutionRecord::Type::SECTION_POP:
|
||||
case VlExecutionRecord::Type::EXEC_GRAPH_BEGIN:
|
||||
case VlExecutionRecord::Type::EXEC_GRAPH_END:
|
||||
// No payload
|
||||
fprintf(fp, "\n");
|
||||
break;
|
||||
@@ -209,6 +215,11 @@ void VlExecutionProfiler::dump(const char* filenamep, uint64_t tickEnd)
|
||||
fprintf(fp, " id %u predictCost %u\n", payload.m_id, payload.m_predictCost);
|
||||
break;
|
||||
}
|
||||
case VlExecutionRecord::Type::SECTION_PUSH: {
|
||||
const auto& payload = er.m_payload.sectionPush;
|
||||
fprintf(fp, " %s\n", payload.m_name);
|
||||
break;
|
||||
}
|
||||
default: abort(); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,12 +58,12 @@ VL_ATTR_ALWINLINE QData VL_CPU_TICK() {
|
||||
|
||||
// clang-format off
|
||||
#define FOREACH_VlExecutionRecord_TYPE(macro) \
|
||||
_VL_FOREACH_APPLY(macro, EVAL_BEGIN) \
|
||||
_VL_FOREACH_APPLY(macro, EVAL_END) \
|
||||
_VL_FOREACH_APPLY(macro, EVAL_LOOP_BEGIN) \
|
||||
_VL_FOREACH_APPLY(macro, EVAL_LOOP_END) \
|
||||
_VL_FOREACH_APPLY(macro, SECTION_PUSH) \
|
||||
_VL_FOREACH_APPLY(macro, SECTION_POP) \
|
||||
_VL_FOREACH_APPLY(macro, MTASK_BEGIN) \
|
||||
_VL_FOREACH_APPLY(macro, MTASK_END)
|
||||
_VL_FOREACH_APPLY(macro, MTASK_END) \
|
||||
_VL_FOREACH_APPLY(macro, EXEC_GRAPH_BEGIN) \
|
||||
_VL_FOREACH_APPLY(macro, EXEC_GRAPH_END)
|
||||
// clang-format on
|
||||
|
||||
class VlExecutionRecord final {
|
||||
@@ -83,6 +83,9 @@ class VlExecutionRecord final {
|
||||
};
|
||||
|
||||
union Payload {
|
||||
struct {
|
||||
const char* m_name; // Name of section being entered
|
||||
} sectionPush;
|
||||
struct {
|
||||
uint32_t m_id; // MTask id
|
||||
uint32_t m_predictStart; // Time scheduler predicted would start
|
||||
@@ -109,10 +112,11 @@ public:
|
||||
VlExecutionRecord() = default;
|
||||
|
||||
// METHODS
|
||||
void evalBegin() { m_type = Type::EVAL_BEGIN; }
|
||||
void evalEnd() { m_type = Type::EVAL_END; }
|
||||
void evalLoopBegin() { m_type = Type::EVAL_LOOP_BEGIN; }
|
||||
void evalLoopEnd() { m_type = Type::EVAL_LOOP_END; }
|
||||
void sectionPush(const char* name) {
|
||||
m_payload.sectionPush.m_name = name;
|
||||
m_type = Type::SECTION_PUSH;
|
||||
}
|
||||
void sectionPop() { m_type = Type::SECTION_POP; }
|
||||
void mtaskBegin(uint32_t id, uint32_t predictStart) {
|
||||
m_payload.mtaskBegin.m_id = id;
|
||||
m_payload.mtaskBegin.m_predictStart = predictStart;
|
||||
@@ -124,6 +128,8 @@ public:
|
||||
m_payload.mtaskEnd.m_predictCost = predictCost;
|
||||
m_type = Type::MTASK_END;
|
||||
}
|
||||
void execGraphBegin() { m_type = Type::EXEC_GRAPH_BEGIN; }
|
||||
void execGraphEnd() { m_type = Type::EXEC_GRAPH_END; }
|
||||
};
|
||||
|
||||
static_assert(std::is_trivially_destructible<VlExecutionRecord>::value,
|
||||
|
||||
@@ -47,7 +47,7 @@ package std;
|
||||
endtask
|
||||
|
||||
function int try_put(T message);
|
||||
if (num() < m_bound) begin
|
||||
if (m_bound == 0 || num() < m_bound) begin
|
||||
m_queue.push_back(message);
|
||||
return 1;
|
||||
end
|
||||
|
||||
@@ -153,11 +153,12 @@ void VlTriggerScheduler::dump(const char* eventDescription) const {
|
||||
// VlDynamicTriggerScheduler:: Methods
|
||||
|
||||
bool VlDynamicTriggerScheduler::evaluate() {
|
||||
m_anyTriggered = false;
|
||||
VL_DEBUG_IF(dump(););
|
||||
std::swap(m_suspended, m_evaluated);
|
||||
for (auto& coro : m_evaluated) coro.resume();
|
||||
m_evaluated.clear();
|
||||
return !m_triggered.empty();
|
||||
return m_anyTriggered;
|
||||
}
|
||||
|
||||
void VlDynamicTriggerScheduler::doPostUpdates() {
|
||||
|
||||
@@ -102,6 +102,8 @@ class VlCoroutineHandle final {
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// Construct
|
||||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlCoroutineHandle(VlProcessRef process)
|
||||
: m_coro{nullptr}
|
||||
, m_process{process} {
|
||||
@@ -114,9 +116,11 @@ public:
|
||||
if (m_process) m_process->state(VlProcess::WAITING);
|
||||
}
|
||||
// Move the handle, leaving a nullptr
|
||||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlCoroutineHandle(VlCoroutineHandle&& moved)
|
||||
: m_coro{std::exchange(moved.m_coro, nullptr)}
|
||||
, m_process{moved.m_process}
|
||||
, m_process{std::exchange(moved.m_process, nullptr)}
|
||||
, m_fileline{moved.m_fileline} {}
|
||||
// Destroy if the handle isn't null
|
||||
~VlCoroutineHandle() {
|
||||
@@ -133,6 +137,8 @@ public:
|
||||
// Move the handle, leaving a null handle
|
||||
auto& operator=(VlCoroutineHandle&& moved) {
|
||||
m_coro = std::exchange(moved.m_coro, nullptr);
|
||||
m_process = std::exchange(moved.m_process, nullptr);
|
||||
m_fileline = moved.m_fileline;
|
||||
return *this;
|
||||
}
|
||||
// Resume the coroutine if the handle isn't null and the process isn't killed
|
||||
@@ -269,6 +275,7 @@ public:
|
||||
// co_await __VdynSched.evaluation();
|
||||
// <pre updates>;
|
||||
// __Vtrigger = <trigger eval>;
|
||||
// __VdynShed.anyTriggered(__Vtrigger);
|
||||
// [optionally] co_await __VdynSched.postUpdate();
|
||||
// <post updates>;
|
||||
// }
|
||||
@@ -282,6 +289,7 @@ class VlDynamicTriggerScheduler final {
|
||||
using VlCoroutineVec = std::vector<VlCoroutineHandle>;
|
||||
|
||||
// MEMBERS
|
||||
bool m_anyTriggered = false; // If true, at least one trigger was set
|
||||
VlCoroutineVec m_suspended; // Suspended coroutines awaiting trigger evaluation
|
||||
VlCoroutineVec m_evaluated; // Coroutines currently being evaluated (for evaluate())
|
||||
VlCoroutineVec m_triggered; // Coroutines whose triggers were set, and are awaiting resumption
|
||||
@@ -307,6 +315,8 @@ class VlDynamicTriggerScheduler final {
|
||||
public:
|
||||
// Evaluates all dynamic triggers (resumed coroutines that co_await evaluation())
|
||||
bool evaluate();
|
||||
// Called by coroutines that evaluate triggers to notify the scheduler if any triggers were set
|
||||
void anyTriggered(bool triggered) { m_anyTriggered = m_anyTriggered || triggered; }
|
||||
// Runs post updates for all dynamic triggers (resumes coroutines that co_await postUpdate())
|
||||
void doPostUpdates();
|
||||
// Resumes all coroutines whose triggers are set (those that co_await resumption())
|
||||
|
||||
+82
-28
@@ -25,7 +25,6 @@
|
||||
// clang-format off
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_trace_defs.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <condition_variable>
|
||||
@@ -46,6 +45,54 @@ class VerilatedTraceBuffer;
|
||||
template <class T_Buffer>
|
||||
class VerilatedTraceOffloadBuffer;
|
||||
|
||||
//=============================================================================
|
||||
// Common enumerations
|
||||
|
||||
enum class VerilatedTracePrefixType : uint32_t {
|
||||
// Note: Entries must match VTracePrefixType (by name, not necessarily by value)
|
||||
ARRAY_PACKED,
|
||||
ARRAY_UNPACKED,
|
||||
SCOPE_MODULE,
|
||||
SCOPE_INTERFACE,
|
||||
STRUCT_PACKED,
|
||||
STRUCT_UNPACKED,
|
||||
UNION_PACKED
|
||||
};
|
||||
|
||||
// Direction attribute for ports
|
||||
enum class VerilatedTraceSigDirection : uint32_t {
|
||||
NONE,
|
||||
INPUT,
|
||||
OUTPUT,
|
||||
INOUT,
|
||||
};
|
||||
|
||||
// Kind of signal. Similar to nettype but with a few more alternatives
|
||||
enum class VerilatedTraceSigKind : uint32_t {
|
||||
PARAMETER,
|
||||
SUPPLY0,
|
||||
SUPPLY1,
|
||||
TRI,
|
||||
TRI0,
|
||||
TRI1,
|
||||
WIRE,
|
||||
VAR,
|
||||
};
|
||||
|
||||
// Base data type of signal
|
||||
enum class VerilatedTraceSigType : uint32_t {
|
||||
DOUBLE,
|
||||
INTEGER,
|
||||
BIT,
|
||||
LOGIC,
|
||||
INT,
|
||||
SHORTINT,
|
||||
LONGINT,
|
||||
BYTE,
|
||||
EVENT,
|
||||
TIME,
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// Offloaded tracing
|
||||
|
||||
@@ -162,24 +209,29 @@ private:
|
||||
// (the one in Ubuntu 14.04 with GCC 4.8.4 in particular) use the
|
||||
// assignment operator on inserting into collections, so they don't work
|
||||
// with const fields...
|
||||
union { // The callback
|
||||
const union { // The callback
|
||||
initCb_t m_initCb;
|
||||
dumpCb_t m_dumpCb;
|
||||
dumpOffloadCb_t m_dumpOffloadCb;
|
||||
cleanupCb_t m_cleanupCb;
|
||||
};
|
||||
void* m_userp; // The user pointer to pass to the callback (the symbol table)
|
||||
const uint32_t m_fidx; // The index of the tracing function
|
||||
void* const m_userp; // The user pointer to pass to the callback (the symbol table)
|
||||
CallbackRecord(initCb_t cb, void* userp)
|
||||
: m_initCb{cb}
|
||||
, m_fidx{0}
|
||||
, m_userp{userp} {}
|
||||
CallbackRecord(dumpCb_t cb, void* userp)
|
||||
CallbackRecord(dumpCb_t cb, uint32_t fidx, void* userp)
|
||||
: m_dumpCb{cb}
|
||||
, m_fidx{fidx}
|
||||
, m_userp{userp} {}
|
||||
CallbackRecord(dumpOffloadCb_t cb, void* userp)
|
||||
CallbackRecord(dumpOffloadCb_t cb, uint32_t fidx, void* userp)
|
||||
: m_dumpOffloadCb{cb}
|
||||
, m_fidx{fidx}
|
||||
, m_userp{userp} {}
|
||||
CallbackRecord(cleanupCb_t cb, void* userp)
|
||||
: m_cleanupCb{cb}
|
||||
, m_fidx{0}
|
||||
, m_userp{userp} {}
|
||||
};
|
||||
|
||||
@@ -212,18 +264,20 @@ protected:
|
||||
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'
|
||||
uint32_t m_nextCode = 0; // Next code number to assign
|
||||
uint32_t m_numSignals = 0; // Number of distinct signals
|
||||
uint32_t m_maxBits = 0; // Number of bits in the widest signal
|
||||
std::vector<std::string> m_namePrefixStack{""}; // Path prefixes to add to signal names
|
||||
// TODO: Should keep this as a Trie, that is how it's accessed all the time.
|
||||
std::vector<std::pair<int, std::string>> m_dumpvars; // dumpvar() entries
|
||||
char m_scopeEscape = '.';
|
||||
double m_timeRes = 1e-9; // Time resolution (ns/ms etc)
|
||||
double m_timeUnit = 1e-0; // Time units (ns/ms etc)
|
||||
uint64_t m_timeLastDump = 0; // Last time we did a dump
|
||||
@@ -289,6 +343,7 @@ protected:
|
||||
uint32_t nextCode() const { return m_nextCode; }
|
||||
uint32_t numSignals() const { return m_numSignals; }
|
||||
uint32_t maxBits() const { return m_maxBits; }
|
||||
void constDump(bool value) { m_constDump = value; }
|
||||
void fullDump(bool value) { m_fullDump = value; }
|
||||
|
||||
double timeRes() const { return m_timeRes; }
|
||||
@@ -298,14 +353,7 @@ protected:
|
||||
void traceInit() VL_MT_UNSAFE;
|
||||
|
||||
// Declare new signal and return true if enabled
|
||||
bool declCode(uint32_t code, const char* namep, uint32_t bits, bool tri);
|
||||
|
||||
// Is this an escape?
|
||||
bool isScopeEscape(char c) { return std::isspace(c) || c == m_scopeEscape; }
|
||||
// Character that splits scopes. Note whitespace are ALWAYS escapes.
|
||||
char scopeEscape() { return m_scopeEscape; }
|
||||
// Prefix to assume in signal declarations
|
||||
const std::string& namePrefix() const { return m_namePrefixStack.back(); }
|
||||
bool declCode(uint32_t code, const std::string& declName, uint32_t bits);
|
||||
|
||||
void closeBase();
|
||||
void flushBase();
|
||||
@@ -313,6 +361,13 @@ protected:
|
||||
bool offload() const { return m_offload; }
|
||||
bool parallel() const { return m_parallel; }
|
||||
|
||||
// Return last ' ' separated word. Assumes string does not end in ' '.
|
||||
static std::string lastWord(const std::string& str) {
|
||||
const size_t idx = str.rfind(' ');
|
||||
if (idx == std::string::npos) return str;
|
||||
return str.substr(idx + 1);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Virtual functions to be provided by the format specific implementation
|
||||
|
||||
@@ -325,7 +380,7 @@ protected:
|
||||
virtual bool preChangeDump() = 0;
|
||||
|
||||
// Trace buffer management
|
||||
virtual Buffer* getTraceBuffer() = 0;
|
||||
virtual Buffer* getTraceBuffer(uint32_t fidx) = 0;
|
||||
virtual void commitTraceBuffer(Buffer*) = 0;
|
||||
|
||||
// Configure sub-class
|
||||
@@ -359,16 +414,13 @@ public:
|
||||
|
||||
void addModel(VerilatedModel*) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addChgCb(dumpOffloadCb_t cb, void* userp) 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 scopeEscape(char flag) { m_scopeEscape = flag; }
|
||||
|
||||
void pushNamePrefix(const std::string&);
|
||||
void popNamePrefix(unsigned count = 1);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -423,7 +475,7 @@ public:
|
||||
void fullQData(uint32_t* oldp, QData newval, int bits);
|
||||
void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
|
||||
void fullDouble(uint32_t* oldp, double newval);
|
||||
void fullEvent(uint32_t* oldp, VlEvent newval);
|
||||
void fullEvent(uint32_t* oldp, const VlEventBase* newval);
|
||||
|
||||
// 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
|
||||
@@ -460,7 +512,9 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
VL_ATTR_ALWINLINE void chgEvent(uint32_t* oldp, VlEvent newval) { fullEvent(oldp, newval); }
|
||||
VL_ATTR_ALWINLINE void chgEvent(uint32_t* oldp, const VlEventBase* newval) {
|
||||
fullEvent(oldp, newval);
|
||||
}
|
||||
VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) {
|
||||
double old;
|
||||
std::memcpy(&old, oldp, sizeof(old));
|
||||
@@ -539,7 +593,7 @@ public:
|
||||
m_offloadBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgEvent(uint32_t code, VlEvent newval) {
|
||||
void chgEvent(uint32_t code, const VlEventBase* newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_EVENT;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2001-2023 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated tracing types
|
||||
///
|
||||
/// This file is not part of the Verilated public-facing API.
|
||||
/// It is only for internal use by Verilated tracing routines.
|
||||
///
|
||||
//=============================================================================
|
||||
#ifndef VERILATOR_VERILATED_TRACE_DEFS_H_
|
||||
#define VERILATOR_VERILATED_TRACE_DEFS_H_
|
||||
|
||||
// Verilator tracing scope types:
|
||||
// The values should match FST_ST_VCD_* from fstScopeType in gtkwave/fstapi.h
|
||||
// verilated_fst_c.cpp contains assertions to enforce this
|
||||
enum VltTraceScope {
|
||||
VLT_TRACE_SCOPE_MODULE = 0,
|
||||
VLT_TRACE_SCOPE_TASK = 1,
|
||||
VLT_TRACE_SCOPE_FUNCTION = 2,
|
||||
VLT_TRACE_SCOPE_BEGIN = 3,
|
||||
VLT_TRACE_SCOPE_FORK = 4,
|
||||
VLT_TRACE_SCOPE_GENERATE = 5,
|
||||
VLT_TRACE_SCOPE_STRUCT = 6,
|
||||
VLT_TRACE_SCOPE_UNION = 7,
|
||||
VLT_TRACE_SCOPE_CLASS = 8,
|
||||
VLT_TRACE_SCOPE_INTERFACE = 9,
|
||||
VLT_TRACE_SCOPE_PACKAGE = 10,
|
||||
VLT_TRACE_SCOPE_PROGRAM = 11
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
@@ -185,7 +185,7 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_EVENT:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_EVENT " << top);
|
||||
traceBufp->chgEvent(oldp, *reinterpret_cast<const VlEvent*>(readp));
|
||||
traceBufp->chgEvent(oldp, reinterpret_cast<const VlEventBase*>(readp));
|
||||
continue;
|
||||
|
||||
//===
|
||||
@@ -378,13 +378,12 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
|
||||
}
|
||||
|
||||
template <>
|
||||
bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const char* namep, uint32_t bits,
|
||||
bool tri) {
|
||||
bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const std::string& declName,
|
||||
uint32_t bits) {
|
||||
if (VL_UNCOVERABLE(!code)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
|
||||
}
|
||||
// To keep it simple, this is O(enables * signals), but we expect few enables
|
||||
std::string declName = namePrefix() + namep;
|
||||
bool enabled = false;
|
||||
if (m_dumpvars.empty()) enabled = true;
|
||||
for (const auto& item : m_dumpvars) {
|
||||
@@ -410,10 +409,7 @@ bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const char* nam
|
||||
break;
|
||||
}
|
||||
|
||||
// Note: The tri-state flag is not used by Verilator, but is here for
|
||||
// compatibility with some foreign code.
|
||||
int codesNeeded = VL_WORDS_I(bits);
|
||||
if (tri) codesNeeded *= 2;
|
||||
m_nextCode = std::max(m_nextCode, code + codesNeeded);
|
||||
++m_numSignals;
|
||||
m_maxBits = std::max(m_maxBits, bits);
|
||||
@@ -457,7 +453,7 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dumpvars(int level, const std::string&
|
||||
for (auto& i : hierSpaced) {
|
||||
if (i == '.') i = ' ';
|
||||
}
|
||||
m_dumpvars.push_back(std::make_pair(level, hierSpaced));
|
||||
m_dumpvars.emplace_back(level, hierSpaced);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,16 +494,15 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runCallbacks(const std::vector<Callback
|
||||
// Main thread executes all jobs with index % threads == 0
|
||||
std::vector<ParallelWorkerData*> mainThreadWorkerData;
|
||||
// Enqueue all the jobs
|
||||
for (unsigned i = 0; i < cbVec.size(); ++i) {
|
||||
const CallbackRecord& cbr = cbVec[i];
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
// Always get the trace buffer on the main thread
|
||||
Buffer* const bufp = getTraceBuffer();
|
||||
Buffer* const bufp = getTraceBuffer(cbr.m_fidx);
|
||||
// Create new work item
|
||||
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
|
||||
// Grab the new work item
|
||||
ParallelWorkerData* const itemp = &workerData.back();
|
||||
// Enqueue task to thread pool, or main thread
|
||||
if (unsigned rem = i % threads) {
|
||||
if (unsigned rem = cbr.m_fidx % threads) {
|
||||
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp);
|
||||
} else {
|
||||
mainThreadWorkerData.push_back(itemp);
|
||||
@@ -530,7 +525,7 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runCallbacks(const std::vector<Callback
|
||||
}
|
||||
// Fall back on sequential execution
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
Buffer* const traceBufferp = getTraceBuffer();
|
||||
Buffer* const traceBufferp = getTraceBuffer(cbr.m_fidx);
|
||||
cbr.m_dumpCb(cbr.m_userp, traceBufferp);
|
||||
commitTraceBuffer(traceBufferp);
|
||||
}
|
||||
@@ -541,7 +536,7 @@ 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();
|
||||
Buffer* traceBufferp = getTraceBuffer(cbr.m_fidx);
|
||||
cbr.m_dumpOffloadCb(cbr.m_userp, static_cast<OffloadBuffer*>(traceBufferp));
|
||||
commitTraceBuffer(traceBufferp);
|
||||
}
|
||||
@@ -610,6 +605,15 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
||||
}
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(m_constDump)) {
|
||||
m_constDump = false;
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_constOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_constCbs);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_cleanupCbs[i];
|
||||
cbr.m_cleanupCb(cbr.m_userp, self());
|
||||
@@ -695,37 +699,40 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp) VL_
|
||||
addCallbackRecord(m_initCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_fullCbs, CallbackRecord{cb, userp});
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addConstCb(dumpCb_t cb, uint32_t fidx,
|
||||
void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_constCbs, CallbackRecord{cb, fidx, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_fullOffloadCbs, CallbackRecord{cb, userp});
|
||||
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>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_chgCbs, CallbackRecord{cb, userp});
|
||||
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>::addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_chgOffloadCbs, CallbackRecord{cb, userp});
|
||||
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});
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {
|
||||
m_namePrefixStack.push_back(m_namePrefixStack.back() + prefix);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::popNamePrefix(unsigned count) {
|
||||
while (count--) m_namePrefixStack.pop_back();
|
||||
assert(!m_namePrefixStack.empty());
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Primitives converting binary values to strings...
|
||||
|
||||
@@ -839,7 +846,7 @@ void VerilatedTraceBuffer<VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullEvent(uint32_t* oldp, VlEvent newval) {
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullEvent(uint32_t* oldp, const VlEventBase* newval) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = 1; // Do we really store an "event" ?
|
||||
emitEvent(code, newval);
|
||||
|
||||
+162
-17
@@ -33,6 +33,7 @@
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
@@ -69,16 +70,28 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
|
||||
#define VL_INOUTW(name, msb, lsb, words) VlWide<words> name ///< Declare bidir signal, 65+ bits
|
||||
#define VL_OUT8(name, msb, lsb) CData name ///< Declare output signal, 1-8 bits
|
||||
#define VL_OUT16(name, msb, lsb) SData name ///< Declare output signal, 9-16 bits
|
||||
#define VL_OUT64(name, msb, lsb) QData name ///< Declare output signal, 33-64bits
|
||||
#define VL_OUT64(name, msb, lsb) QData name ///< Declare output signal, 33-64 bits
|
||||
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
|
||||
#define VL_OUTW(name, msb, lsb, words) VlWide<words> name ///< Declare output signal, 65+ bits
|
||||
|
||||
//===================================================================
|
||||
// VlProcess stores metadata of running processes
|
||||
// Functions needed here
|
||||
|
||||
constexpr IData VL_CLOG2_CE_Q(QData lhs) VL_PURE {
|
||||
// constexpr usage only! Recuses to meet C++11 constexpr func limitations
|
||||
return lhs <= 1 ? 0 : VL_CLOG2_CE_Q((lhs + 1) >> 1ULL) + 1;
|
||||
}
|
||||
|
||||
// Metadata of processes
|
||||
class VlProcess;
|
||||
|
||||
using VlProcessRef = std::shared_ptr<VlProcess>;
|
||||
|
||||
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
|
||||
|
||||
public:
|
||||
// TYPES
|
||||
@@ -91,16 +104,40 @@ public:
|
||||
};
|
||||
|
||||
// CONSTRUCTORS
|
||||
// Construct independent process
|
||||
VlProcess()
|
||||
: m_state{RUNNING} {}
|
||||
// Construct child process of parent
|
||||
VlProcess(VlProcessRef parentp)
|
||||
: m_state{RUNNING}
|
||||
, m_parentp{parentp} {
|
||||
m_parentp->attach(this);
|
||||
}
|
||||
|
||||
// METHODS
|
||||
int state() { return m_state; }
|
||||
~VlProcess() {
|
||||
if (m_parentp) m_parentp->detach(this);
|
||||
}
|
||||
|
||||
void attach(VlProcess* childp) { m_children.insert(childp); }
|
||||
void detach(VlProcess* childp) { m_children.erase(childp); }
|
||||
|
||||
int state() const { return m_state; }
|
||||
void state(int s) { m_state = s; }
|
||||
void disable() {
|
||||
state(KILLED);
|
||||
disableFork();
|
||||
}
|
||||
void disableFork() {
|
||||
for (VlProcess* childp : m_children) childp->disable();
|
||||
}
|
||||
bool completed() const { return state() == FINISHED || state() == KILLED; }
|
||||
bool completedFork() const {
|
||||
for (const VlProcess* const childp : m_children)
|
||||
if (!childp->completed()) return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
using VlProcessRef = std::shared_ptr<VlProcess>;
|
||||
|
||||
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
|
||||
|
||||
//===================================================================
|
||||
@@ -155,7 +192,18 @@ public:
|
||||
//===================================================================
|
||||
// SystemVerilog event type
|
||||
|
||||
class VlEvent final {
|
||||
class VlEventBase VL_NOT_FINAL {
|
||||
public:
|
||||
virtual ~VlEventBase() = default;
|
||||
|
||||
virtual void fire() = 0;
|
||||
virtual bool isFired() const = 0;
|
||||
virtual bool isTriggered() const = 0;
|
||||
virtual void clearFired() = 0;
|
||||
virtual void clearTriggered() = 0;
|
||||
};
|
||||
|
||||
class VlEvent final : public VlEventBase {
|
||||
// MEMBERS
|
||||
bool m_fired = false; // Fired on this scheduling iteration
|
||||
bool m_triggered = false; // Triggered state of event persisting until next time step
|
||||
@@ -163,20 +211,50 @@ class VlEvent final {
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlEvent() = default;
|
||||
~VlEvent() = default;
|
||||
~VlEvent() override = default;
|
||||
|
||||
friend std::string VL_TO_STRING(const VlEvent& e);
|
||||
friend class VlAssignableEvent;
|
||||
// METHODS
|
||||
void fire() { m_fired = m_triggered = true; }
|
||||
bool isFired() const { return m_fired; }
|
||||
bool isTriggered() const { return m_triggered; }
|
||||
void clearFired() { m_fired = false; }
|
||||
void clearTriggered() { m_triggered = false; }
|
||||
void fire() override { m_fired = m_triggered = true; }
|
||||
bool isFired() const override { return m_fired; }
|
||||
bool isTriggered() const override { return m_triggered; }
|
||||
void clearFired() override { m_fired = false; }
|
||||
void clearTriggered() override { m_triggered = false; }
|
||||
};
|
||||
|
||||
class VlAssignableEvent final : public std::shared_ptr<VlEvent>, public VlEventBase {
|
||||
public:
|
||||
// Constructor
|
||||
VlAssignableEvent()
|
||||
: std::shared_ptr<VlEvent>(new VlEvent) {}
|
||||
~VlAssignableEvent() override = default;
|
||||
|
||||
// METHODS
|
||||
void fire() override { (*this)->m_fired = (*this)->m_triggered = true; }
|
||||
bool isFired() const override { return (*this)->m_fired; }
|
||||
bool isTriggered() const override { return (*this)->m_triggered; }
|
||||
void clearFired() override { (*this)->m_fired = false; }
|
||||
void clearTriggered() override { (*this)->m_triggered = false; }
|
||||
};
|
||||
|
||||
inline std::string VL_TO_STRING(const VlEventBase& e);
|
||||
|
||||
inline std::string VL_TO_STRING(const VlEvent& e) {
|
||||
return std::string{"triggered="} + (e.isTriggered() ? "true" : "false");
|
||||
}
|
||||
|
||||
inline std::string VL_TO_STRING(const VlAssignableEvent& e) {
|
||||
return "&{ " + VL_TO_STRING(*e) + " }";
|
||||
}
|
||||
|
||||
inline std::string VL_TO_STRING(const VlEventBase& e) {
|
||||
if (const VlAssignableEvent& assignable = dynamic_cast<const VlAssignableEvent&>(e)) {
|
||||
return VL_TO_STRING(assignable);
|
||||
}
|
||||
return std::string{"triggered="} + (e.isTriggered() ? "true" : "false");
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Random
|
||||
|
||||
@@ -210,6 +288,58 @@ public:
|
||||
size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
|
||||
};
|
||||
|
||||
template <class T_Value, uint64_t T_numValues>
|
||||
class VlRandC final {
|
||||
T_Value m_remaining = 0; // Number of values to pull before re-randomize
|
||||
T_Value m_lfsr = 1; // LFSR state
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlRandC() {
|
||||
static_assert(T_numValues >= 1, "");
|
||||
static_assert(sizeof(T_Value) == 8 || (T_numValues < (1ULL << (8 * sizeof(T_Value)))), "");
|
||||
}
|
||||
// METHODS
|
||||
T_Value randomize(VlRNG& rngr) {
|
||||
if (VL_UNLIKELY(!m_remaining)) reseed(rngr);
|
||||
// Polynomials are first listed at https://users.ece.cmu.edu/~koopman/lfsr/
|
||||
static constexpr uint64_t s_polynomials[] = {
|
||||
0x0ULL, // 0 never used (constant, no randomization)
|
||||
0x0ULL, // 1
|
||||
0x3ULL, 0x5ULL, 0x9ULL, 0x12ULL, 0x21ULL,
|
||||
0x41ULL, 0x8eULL, 0x108ULL, 0x204ULL, 0x402ULL,
|
||||
0x829ULL, 0x100dULL, 0x2015ULL, 0x4001ULL,
|
||||
0x8016ULL, // 16
|
||||
0x10004ULL, 0x20040ULL, 0x40013ULL, 0x80004ULL, 0x100002ULL,
|
||||
0x200001ULL, 0x400010ULL, 0x80000dULL, 0x1000004ULL, 0x2000023ULL,
|
||||
0x4000013ULL, 0x8000004ULL, 0x10000002ULL, 0x20000029ULL, 0x40000004ULL,
|
||||
0x80000057ULL, // 32
|
||||
0x100000029ULL // 33
|
||||
};
|
||||
constexpr uint32_t clogWidth = VL_CLOG2_CE_Q(T_numValues) + 1;
|
||||
constexpr uint32_t lfsrWidth = (clogWidth < 2) ? 2 : clogWidth;
|
||||
constexpr T_Value polynomial = static_cast<T_Value>(s_polynomials[lfsrWidth]);
|
||||
// printf(" numV=%ld w=%d poly=%x\n", T_numValues, lfsrWidth, polynomial);
|
||||
// Loop until get reasonable value. Because we picked a LFSR of at most one
|
||||
// extra bit in width, this will only require at most on average 1.5 loops
|
||||
do {
|
||||
m_lfsr = (m_lfsr & 1ULL) ? ((m_lfsr >> 1ULL) ^ polynomial) : (m_lfsr >> 1ULL);
|
||||
} while (m_lfsr > T_numValues); // Note if == then output value 0
|
||||
--m_remaining;
|
||||
T_Value result = (m_lfsr == T_numValues) ? 0 : m_lfsr;
|
||||
// printf(" result=%x (numv=%ld, rem=%d)\n", result, T_numValues, m_remaining);
|
||||
return result;
|
||||
}
|
||||
void reseed(VlRNG& rngr) {
|
||||
constexpr uint32_t lfsrWidth = VL_CLOG2_CE_Q(T_numValues) + 1;
|
||||
m_remaining = T_numValues;
|
||||
do {
|
||||
m_lfsr = rngr.rand64() & VL_MASK_Q(lfsrWidth);
|
||||
// printf(" lfsr.reseed=%x\n", m_lfsr);
|
||||
} while (!m_lfsr); // 0 not a legal seed
|
||||
}
|
||||
};
|
||||
|
||||
// These require the class object to have the thread safety lock
|
||||
inline IData VL_RANDOM_RNG_I(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); }
|
||||
inline QData VL_RANDOM_RNG_Q(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); }
|
||||
@@ -457,7 +587,7 @@ public:
|
||||
|
||||
// function void q.insert(index, value);
|
||||
void insert(int32_t index, const T_Value& value) {
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
|
||||
if (VL_UNLIKELY(index < 0 || index > m_deque.size())) return;
|
||||
m_deque.insert(m_deque.begin() + index, value);
|
||||
}
|
||||
|
||||
@@ -1418,8 +1548,8 @@ class VlClass VL_NOT_FINAL : public VlDeletable {
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlClass() = default;
|
||||
VlClass(const VlClass& copied) {}
|
||||
VlClass() { refCountInc(); }
|
||||
VlClass(const VlClass& copied) { refCountInc(); }
|
||||
~VlClass() override = default;
|
||||
};
|
||||
|
||||
@@ -1467,8 +1597,9 @@ public:
|
||||
// () 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->m_deleterp = &deleter;
|
||||
refCountInc();
|
||||
}
|
||||
// Explicit to avoid implicit conversion from 0
|
||||
explicit VlClassRef(T_Class* objp)
|
||||
@@ -1546,10 +1677,18 @@ public:
|
||||
bool operator!=(const VlClassRef<T_OtherClass>& rhs) const {
|
||||
return m_objp != rhs.m_objp;
|
||||
};
|
||||
template <typename T_OtherClass>
|
||||
bool operator<(const VlClassRef<T_OtherClass>& rhs) const {
|
||||
return m_objp < rhs.m_objp;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
|
||||
if (!in) {
|
||||
outr = VlNull{};
|
||||
return true;
|
||||
}
|
||||
VlClassRef<U> casted = in.template dynamicCast<U>();
|
||||
if (VL_LIKELY(casted)) {
|
||||
outr = casted;
|
||||
@@ -1559,6 +1698,12 @@ static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline bool VL_CAST_DYNAMIC(VlNull in, VlClassRef<T>& outr) {
|
||||
outr = VlNull{};
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlSampleQueue stores samples for input clockvars in clocking blocks. At a clocking event,
|
||||
// samples from this queue should be written to the correct input clockvar.
|
||||
|
||||
+124
-219
@@ -112,7 +112,19 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
openNextImp(m_rolloverSize != 0);
|
||||
if (!isOpen()) return;
|
||||
|
||||
dumpHeader();
|
||||
printStr("$version Generated by VerilatedVcd $end\n");
|
||||
printStr("$timescale ");
|
||||
printStr(timeResStr().c_str()); // lintok-begin-on-ref
|
||||
printStr(" $end\n");
|
||||
|
||||
// Scope and signal definitions
|
||||
assert(m_indent == 0);
|
||||
++m_indent;
|
||||
Super::traceInit();
|
||||
--m_indent;
|
||||
assert(m_indent == 0);
|
||||
|
||||
printStr("$enddefinitions $end\n\n\n");
|
||||
|
||||
// When using rollover, the first chunk contains the header only.
|
||||
if (m_rolloverSize) openNextImp(true);
|
||||
@@ -164,6 +176,7 @@ void VerilatedVcd::openNextImp(bool incFilename) {
|
||||
}
|
||||
}
|
||||
m_isOpen = true;
|
||||
constDump(true); // First dump must containt the const signals
|
||||
fullDump(true); // First dump must be full
|
||||
m_wroteBytes = 0;
|
||||
}
|
||||
@@ -175,49 +188,14 @@ bool VerilatedVcd::preChangeDump() {
|
||||
|
||||
void VerilatedVcd::emitTimeChange(uint64_t timeui) {
|
||||
printStr("#");
|
||||
printQuad(timeui);
|
||||
const std::string str = std::to_string(timeui);
|
||||
printStr(str.c_str());
|
||||
printStr("\n");
|
||||
}
|
||||
|
||||
void VerilatedVcd::makeNameMap() {
|
||||
// Take signal information from each module and build m_namemapp
|
||||
deleteNameMap();
|
||||
m_namemapp = new NameMap;
|
||||
|
||||
Super::traceInit();
|
||||
|
||||
// Though not speced, it's illegal to generate a vcd with signals
|
||||
// not under any module - it crashes at least two viewers.
|
||||
// If no scope was specified, prefix everything with a "top"
|
||||
// This comes from user instantiations with no name - IE Vtop("").
|
||||
bool nullScope = false;
|
||||
for (const auto& i : *m_namemapp) {
|
||||
const std::string& hiername = i.first;
|
||||
if (!hiername.empty() && hiername[0] == '\t') nullScope = true;
|
||||
}
|
||||
if (nullScope) {
|
||||
NameMap* const newmapp = new NameMap;
|
||||
for (const auto& i : *m_namemapp) {
|
||||
const std::string& hiername = i.first;
|
||||
const std::string& decl = i.second;
|
||||
std::string newname{"top"};
|
||||
if (hiername[0] != '\t') newname += ' ';
|
||||
newname += hiername;
|
||||
newmapp->emplace(newname, decl);
|
||||
}
|
||||
deleteNameMap();
|
||||
m_namemapp = newmapp;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::deleteNameMap() {
|
||||
if (m_namemapp) VL_DO_CLEAR(delete m_namemapp, m_namemapp = nullptr);
|
||||
}
|
||||
|
||||
VerilatedVcd::~VerilatedVcd() {
|
||||
close();
|
||||
if (m_wrBufp) VL_DO_CLEAR(delete[] m_wrBufp, m_wrBufp = nullptr);
|
||||
deleteNameMap();
|
||||
if (m_filep && m_fileNewed) VL_DO_CLEAR(delete m_filep, m_filep = nullptr);
|
||||
if (parallel()) {
|
||||
assert(m_numBuffers == m_freeBuffers.size());
|
||||
@@ -270,13 +248,6 @@ void VerilatedVcd::printStr(const char* str) {
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::printQuad(uint64_t n) {
|
||||
constexpr size_t LEN_STR_QUAD = 40;
|
||||
char buf[LEN_STR_QUAD];
|
||||
VL_SNPRINTF(buf, LEN_STR_QUAD, "%" PRIu64, n);
|
||||
printStr(buf);
|
||||
}
|
||||
|
||||
void VerilatedVcd::bufferResize(size_t minsize) {
|
||||
// minsize is size of largest write. We buffer at least 8 times as much data,
|
||||
// writing when we are 3/4 full (with thus 2*minsize remaining free)
|
||||
@@ -325,127 +296,59 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
m_writep = m_wrBufp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VCD string code
|
||||
|
||||
char* VerilatedVcd::writeCode(char* writep, uint32_t code) {
|
||||
*writep++ = static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
while (code) {
|
||||
--code;
|
||||
*writep++ = static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
}
|
||||
return writep;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Definitions
|
||||
|
||||
void VerilatedVcd::printIndent(int level_change) {
|
||||
if (level_change < 0) m_modDepth += level_change;
|
||||
assert(m_modDepth >= 0);
|
||||
for (int i = 0; i < m_modDepth; i++) printStr(" ");
|
||||
if (level_change > 0) m_modDepth += level_change;
|
||||
if (level_change < 0) m_indent += level_change;
|
||||
for (int i = 0; i < m_indent; i++) printStr(" ");
|
||||
if (level_change > 0) m_indent += level_change;
|
||||
}
|
||||
|
||||
void VerilatedVcd::dumpHeader() {
|
||||
printStr("$version Generated by VerilatedVcd $end\n");
|
||||
|
||||
// Verilator used to put in a $date here. Although $date is shown in
|
||||
// IEEE examples, and it is common in VCD writers, VCD readers don't
|
||||
// seem to care about it. Thus, we omit the $date so artifacts are
|
||||
// more likely to be reproducible. If use cases show up that require
|
||||
// the $date command to be present, it could be re-added with support
|
||||
// for the SOURCE_DATE_EPOCH hook.
|
||||
|
||||
printStr("$timescale ");
|
||||
printStr(timeResStr().c_str()); // lintok-begin-on-ref
|
||||
printStr(" $end\n");
|
||||
|
||||
makeNameMap();
|
||||
|
||||
// Signal header
|
||||
assert(m_modDepth == 0);
|
||||
printIndent(1);
|
||||
printStr("\n");
|
||||
|
||||
// We detect the spaces in module names to determine hierarchy. This
|
||||
// allows signals to be declared without fixed ordering, which is
|
||||
// required as Verilog signals might be separately declared from
|
||||
// SC module signals.
|
||||
|
||||
// Print the signal names
|
||||
const char* lastName = "";
|
||||
for (const auto& i : *m_namemapp) {
|
||||
const std::string& hiernamestr = i.first;
|
||||
const std::string& decl = i.second;
|
||||
|
||||
// Determine difference between the old and new names
|
||||
const char* const hiername = hiernamestr.c_str();
|
||||
const char* lp = lastName;
|
||||
const char* np = hiername;
|
||||
lastName = hiername;
|
||||
|
||||
// Skip common prefix, it must break at a space or tab
|
||||
for (; *np && (*np == *lp); np++, lp++) {}
|
||||
while (np != hiername && *np && *np != ' ' && *np != '\t') {
|
||||
--np;
|
||||
--lp;
|
||||
}
|
||||
// printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np);
|
||||
|
||||
// Any extra spaces in last name are scope ups we need to do
|
||||
bool first = true;
|
||||
for (; *lp; lp++) {
|
||||
if (*lp == ' ' || (first && *lp != '\t')) {
|
||||
printIndent(-1);
|
||||
printStr("$upscope $end\n");
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
// Any new spaces are scope downs we need to do
|
||||
while (*np) {
|
||||
if (*np == ' ') np++;
|
||||
if (*np == '\t') break; // tab means signal name starts
|
||||
printIndent(1);
|
||||
printStr("$scope module ");
|
||||
|
||||
for (; *np && *np != ' ' && *np != '\t'; np++) {
|
||||
if (*np == '[') {
|
||||
printStr("[");
|
||||
} else if (*np == ']') {
|
||||
printStr("]");
|
||||
} else {
|
||||
*m_writep++ = *np;
|
||||
}
|
||||
}
|
||||
printStr(" $end\n");
|
||||
}
|
||||
|
||||
printIndent(0);
|
||||
printStr(decl.c_str());
|
||||
void VerilatedVcd::pushPrefix(const std::string& name, VerilatedTracePrefixType type) {
|
||||
std::string newPrefix = m_prefixStack.back().first + name;
|
||||
switch (type) {
|
||||
case VerilatedTracePrefixType::SCOPE_MODULE:
|
||||
case VerilatedTracePrefixType::SCOPE_INTERFACE:
|
||||
case VerilatedTracePrefixType::STRUCT_PACKED:
|
||||
case VerilatedTracePrefixType::STRUCT_UNPACKED:
|
||||
case VerilatedTracePrefixType::UNION_PACKED: {
|
||||
printIndent(1);
|
||||
printStr("$scope module ");
|
||||
const std::string n = lastWord(newPrefix);
|
||||
printStr(n.c_str());
|
||||
printStr(" $end\n");
|
||||
newPrefix += ' ';
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
m_prefixStack.emplace_back(newPrefix, type);
|
||||
}
|
||||
|
||||
while (m_modDepth > 1) {
|
||||
void VerilatedVcd::popPrefix() {
|
||||
switch (m_prefixStack.back().second) {
|
||||
case VerilatedTracePrefixType::SCOPE_MODULE:
|
||||
case VerilatedTracePrefixType::SCOPE_INTERFACE:
|
||||
case VerilatedTracePrefixType::STRUCT_PACKED:
|
||||
case VerilatedTracePrefixType::STRUCT_UNPACKED:
|
||||
case VerilatedTracePrefixType::UNION_PACKED:
|
||||
printIndent(-1);
|
||||
printStr("$upscope $end\n");
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
printIndent(-1);
|
||||
printStr("$enddefinitions $end\n\n\n");
|
||||
assert(m_modDepth == 0);
|
||||
|
||||
// Reclaim storage
|
||||
deleteNameMap();
|
||||
m_prefixStack.pop_back();
|
||||
assert(!m_prefixStack.empty());
|
||||
}
|
||||
|
||||
void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, bool array,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb) {
|
||||
int arraynum, bool bussed, int msb, int lsb) {
|
||||
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
|
||||
|
||||
const bool enabled = Super::declCode(code, name, bits, tri);
|
||||
const std::string hierarchicalName = m_prefixStack.back().first + name;
|
||||
|
||||
const bool enabled = Super::declCode(code, hierarchicalName, bits);
|
||||
|
||||
if (m_suffixes.size() <= nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE) {
|
||||
m_suffixes.resize(nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE * 2, 0);
|
||||
@@ -458,93 +361,95 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b
|
||||
|
||||
if (!enabled) return;
|
||||
|
||||
// Split name into basename
|
||||
// Spaces and tabs aren't legal in VCD signal names, so:
|
||||
// Space separates each level of scope
|
||||
// Tab separates final scope from signal name
|
||||
// Tab sorts before spaces, so signals nicely will print before scopes
|
||||
// Note the hiername may be nothing, if so we'll add "\t{name}"
|
||||
std::string nameasstr = namePrefix() + name;
|
||||
std::string hiername;
|
||||
std::string basename;
|
||||
for (const char* cp = nameasstr.c_str(); *cp; cp++) {
|
||||
if (isScopeEscape(*cp)) {
|
||||
// Ahh, we've just read a scope, not a basename
|
||||
if (!hiername.empty()) hiername += " ";
|
||||
hiername += basename;
|
||||
basename = "";
|
||||
} else {
|
||||
basename += *cp;
|
||||
}
|
||||
// Create the VCD code and build the suffix array entry
|
||||
char vcdCode[VL_TRACE_SUFFIX_ENTRY_SIZE];
|
||||
{
|
||||
// Render the VCD code
|
||||
char* vcdCodeWritep = vcdCode;
|
||||
uint32_t codeEnc = code;
|
||||
do {
|
||||
*vcdCodeWritep++ = static_cast<char>('!' + codeEnc % 94);
|
||||
codeEnc /= 94;
|
||||
} while (codeEnc--);
|
||||
*vcdCodeWritep = '\0';
|
||||
const size_t vcdCodeLength = vcdCodeWritep - vcdCode;
|
||||
assert(vcdCodeLength <= VL_TRACE_MAX_VCD_CODE_SIZE);
|
||||
// Build suffix array entry
|
||||
char* const entryBeginp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
|
||||
entryBeginp[0] = ' '; // Separator
|
||||
// 1 bit values don't have a ' ' separator between value and string code
|
||||
char* entryWritep = bits == 1 ? entryBeginp : entryBeginp + 1;
|
||||
// Use memcpy as we know the size, and strcpy is flagged unsafe
|
||||
std::memcpy(entryWritep, vcdCode, vcdCodeLength);
|
||||
entryWritep += vcdCodeLength;
|
||||
// Line terminator
|
||||
*entryWritep++ = '\n';
|
||||
// Set length of suffix (used to increment write pointer)
|
||||
assert(entryWritep <= entryBeginp + VL_TRACE_SUFFIX_ENTRY_SIZE - 1);
|
||||
entryBeginp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = static_cast<char>(entryWritep - entryBeginp);
|
||||
}
|
||||
hiername += "\t" + basename;
|
||||
|
||||
// Print reference
|
||||
// Assemble the declaration
|
||||
std::string decl = "$var ";
|
||||
decl += wirep; // usually "wire"
|
||||
|
||||
constexpr size_t bufsize = 1000;
|
||||
char buf[bufsize];
|
||||
VL_SNPRINTF(buf, bufsize, " %2d ", bits);
|
||||
decl += buf;
|
||||
// Add string code to decl
|
||||
char* const endp = writeCode(buf, code);
|
||||
*endp = '\0';
|
||||
decl += buf;
|
||||
// Build suffix array entry
|
||||
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
|
||||
const size_t length = endp - buf;
|
||||
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
|
||||
// 1 bit values don't have a ' ' separator between value and string code
|
||||
const bool isBit = bits == 1;
|
||||
entryp[0] = ' '; // Separator
|
||||
// Use memcpy as we checked size above, and strcpy is flagged unsafe
|
||||
std::memcpy(entryp + !isBit, buf,
|
||||
std::strlen(buf)); // Code (overwrite separator if isBit)
|
||||
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
|
||||
// Set length of suffix (used to increment write pointer)
|
||||
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = static_cast<char>(length + !isBit + 1);
|
||||
decl += " ";
|
||||
decl += basename;
|
||||
decl += wirep;
|
||||
decl += ' ';
|
||||
decl += std::to_string(bits);
|
||||
decl += ' ';
|
||||
decl += vcdCode;
|
||||
decl += ' ';
|
||||
decl += lastWord(hierarchicalName);
|
||||
if (array) {
|
||||
VL_SNPRINTF(buf, bufsize, "[%d]", arraynum);
|
||||
decl += buf;
|
||||
hiername += buf;
|
||||
decl += '[';
|
||||
decl += std::to_string(arraynum);
|
||||
decl += ']';
|
||||
}
|
||||
if (bussed) {
|
||||
VL_SNPRINTF(buf, bufsize, " [%d:%d]", msb, lsb);
|
||||
decl += buf;
|
||||
decl += " [";
|
||||
decl += std::to_string(msb);
|
||||
decl += ':';
|
||||
decl += std::to_string(lsb);
|
||||
decl += ']';
|
||||
}
|
||||
decl += " $end\n";
|
||||
m_namemapp->emplace(hiername, decl);
|
||||
printIndent(0);
|
||||
printStr(decl.c_str());
|
||||
}
|
||||
|
||||
void VerilatedVcd::declEvent(uint32_t code, const char* name, bool array, int arraynum) {
|
||||
declare(code, name, "event", array, arraynum, false, false, 0, 0);
|
||||
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);
|
||||
}
|
||||
void VerilatedVcd::declBit(uint32_t code, const char* name, bool array, int arraynum) {
|
||||
declare(code, name, "wire", array, arraynum, false, 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::declBus(uint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, false, true, msb, lsb);
|
||||
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::declQuad(uint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, false, 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::declArray(uint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, false, 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::declDouble(uint32_t code, const char* name, bool array, int arraynum) {
|
||||
declare(code, name, "real", array, arraynum, false, false, 63, 0);
|
||||
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);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Get/commit trace buffer
|
||||
|
||||
VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer() {
|
||||
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
|
||||
@@ -669,8 +574,8 @@ void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) {
|
||||
// so always inline them.
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcdBuffer::emitEvent(uint32_t code, VlEvent newval) {
|
||||
const bool triggered = newval.isTriggered();
|
||||
void VerilatedVcdBuffer::emitEvent(uint32_t code, const VlEventBase* newval) {
|
||||
const bool triggered = newval->isTriggered();
|
||||
// TODO : It seems that untriggered events are not filtered
|
||||
// should be tested before this last step
|
||||
if (triggered) {
|
||||
|
||||
+28
-21
@@ -24,7 +24,6 @@
|
||||
#include "verilated.h"
|
||||
#include "verilated_trace.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -51,7 +50,7 @@ private:
|
||||
bool m_isOpen = false; // True indicates open file
|
||||
std::string m_filename; // Filename we're writing to (if open)
|
||||
uint64_t m_rolloverSize = 0; // File size to rollover at
|
||||
int m_modDepth = 0; // Depth of module hierarchy
|
||||
unsigned m_indent = 0; // Indentation depth
|
||||
|
||||
char* m_wrBufp; // Output buffer
|
||||
char* m_wrFlushp; // Output buffer flush trigger location
|
||||
@@ -62,8 +61,9 @@ private:
|
||||
|
||||
std::vector<char> m_suffixes; // VCD line end string codes + metadata
|
||||
|
||||
using NameMap = std::map<const std::string, const std::string>;
|
||||
NameMap* m_namemapp = nullptr; // List of names for the header
|
||||
// Prefixes to add to signal names/scope types
|
||||
std::vector<std::pair<std::string, VerilatedTracePrefixType>> m_prefixStack{
|
||||
{"", VerilatedTracePrefixType::SCOPE_MODULE}};
|
||||
|
||||
// Vector of free trace buffers as (pointer, size) pairs.
|
||||
std::vector<std::pair<char*, size_t>> m_freeBuffers;
|
||||
@@ -79,18 +79,10 @@ private:
|
||||
void openNextImp(bool incFilename);
|
||||
void closePrev();
|
||||
void closeErr();
|
||||
void makeNameMap();
|
||||
void deleteNameMap();
|
||||
void printIndent(int level_change);
|
||||
void printStr(const char* str);
|
||||
void printQuad(uint64_t n);
|
||||
void printTime(uint64_t timeui);
|
||||
void declare(uint32_t code, const char* name, const char* wirep, bool array, int arraynum,
|
||||
bool tri, bool bussed, int msb, int lsb);
|
||||
|
||||
void dumpHeader();
|
||||
|
||||
static char* writeCode(char* writep, uint32_t code);
|
||||
bool bussed, int msb, int lsb);
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcd);
|
||||
@@ -107,7 +99,7 @@ protected:
|
||||
bool preChangeDump() override;
|
||||
|
||||
// Trace buffer management
|
||||
Buffer* getTraceBuffer() override;
|
||||
Buffer* getTraceBuffer(uint32_t fidx) override;
|
||||
void commitTraceBuffer(Buffer*) override;
|
||||
|
||||
// Configure sub-class
|
||||
@@ -140,12 +132,27 @@ public:
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
void declEvent(uint32_t code, const char* name, bool array, int arraynum);
|
||||
void declBit(uint32_t code, const char* name, bool array, int arraynum);
|
||||
void declBus(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble(uint32_t code, const char* name, bool array, int arraynum);
|
||||
void pushPrefix(const std::string&, 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);
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
@@ -207,7 +214,7 @@ class VerilatedVcdBuffer VL_NOT_FINAL {
|
||||
// Implementation of VerilatedTraceBuffer interface
|
||||
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
|
||||
// called from only one place (the full* methods), so always inline them.
|
||||
VL_ATTR_ALWINLINE void emitEvent(uint32_t code, VlEvent newval);
|
||||
VL_ATTR_ALWINLINE void emitEvent(uint32_t code, const VlEventBase* newval);
|
||||
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
|
||||
|
||||
+254
-17
@@ -175,7 +175,7 @@ public:
|
||||
VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: m_varp{varp}
|
||||
, m_scopep{scopep}
|
||||
, m_fullname{std::string{m_scopep->name()} + '.' + name()} {}
|
||||
, m_fullname{std::string{m_scopep->name()} + '.' + m_varp->name()} {}
|
||||
explicit VerilatedVpioVarBase(const VerilatedVpioVarBase* varp) {
|
||||
if (varp) {
|
||||
m_varp = varp->m_varp;
|
||||
@@ -303,7 +303,13 @@ public:
|
||||
uint32_t entSize() const { return m_entSize; }
|
||||
uint32_t index() const { return m_index; }
|
||||
uint32_t type() const override {
|
||||
return (varp()->dims() > 1) ? vpiMemory : vpiReg; // but might be wire, logic
|
||||
uint32_t type = vpiReg;
|
||||
switch (varp()->vltype()) {
|
||||
case VLVT_REAL: type = vpiRealVar; break;
|
||||
case VLVT_STRING: type = vpiStringVar; break;
|
||||
default: break;
|
||||
}
|
||||
return (varp()->dims() > 1) ? vpiMemory : type; // but might be wire, logic
|
||||
}
|
||||
void* prevDatap() const { return m_prevDatap; }
|
||||
void* varDatap() const { return m_varDatap; }
|
||||
@@ -536,8 +542,7 @@ public:
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64 " time=%" PRIu64
|
||||
" obj=%p\n",
|
||||
cb_data_p->reason, id, time, cb_data_p->obj););
|
||||
s().m_futureCbs.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::make_pair(time, id)),
|
||||
s().m_futureCbs.emplace(std::piecewise_construct, std::forward_as_tuple(time, id),
|
||||
std::forward_as_tuple(id, cb_data_p, nullptr));
|
||||
}
|
||||
static void cbNextAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
|
||||
@@ -545,8 +550,7 @@ public:
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d(NEXT) id=%" PRId64
|
||||
" time=%" PRIu64 " obj=%p\n",
|
||||
cb_data_p->reason, id, time, cb_data_p->obj););
|
||||
s().m_nextCbs.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::make_pair(time, id)),
|
||||
s().m_nextCbs.emplace(std::piecewise_construct, std::forward_as_tuple(time, id),
|
||||
std::forward_as_tuple(id, cb_data_p, nullptr));
|
||||
}
|
||||
static void cbReasonRemove(uint64_t id, uint32_t reason, QData time) {
|
||||
@@ -985,9 +989,117 @@ const char* VerilatedVpiError::strFromVpiObjType(PLI_INT32 vpiVal) VL_PURE {
|
||||
"vpiGenVar",
|
||||
"vpiAutomatics"
|
||||
};
|
||||
static const char* const sv_names1[] = {
|
||||
"vpiPackage",
|
||||
"vpiInterface",
|
||||
"vpiProgram",
|
||||
"vpiInterfaceArray",
|
||||
"vpiProgramArray",
|
||||
"vpiTypespec",
|
||||
"vpiModport",
|
||||
"vpiInterfaceTfDecl",
|
||||
"vpiRefObj",
|
||||
"vpiTypeParameter",
|
||||
"vpiLongIntVar",
|
||||
"vpiShortIntVar",
|
||||
"vpiIntVar",
|
||||
"vpiShortRealVar",
|
||||
"vpiByteVar",
|
||||
"vpiClassVar",
|
||||
"vpiStringVar",
|
||||
"vpiEnumVar",
|
||||
"vpiStructVar",
|
||||
"vpiUnionVar",
|
||||
"vpiBitVar",
|
||||
"vpiClassObj",
|
||||
"vpiChandleVar",
|
||||
"vpiPackedArrayVar",
|
||||
"*undefined*", // 624 is not defined for object types
|
||||
"vpiLongIntTypespec",
|
||||
"vpiShortRealTypespec",
|
||||
"vpiByteTypespec",
|
||||
"vpiShortIntTypespec",
|
||||
"vpiIntTypespec",
|
||||
"vpiClassTypespec",
|
||||
"vpiStringTypespec",
|
||||
"vpiChandleTypespec",
|
||||
"vpiEnumTypespec",
|
||||
"vpiEnumConst",
|
||||
"vpiIntegerTypespec",
|
||||
"vpiTimeTypespec",
|
||||
"vpiRealTypespec",
|
||||
"vpiStructTypespec",
|
||||
"vpiUnionTypespec",
|
||||
"vpiBitTypespec",
|
||||
"vpiLogicTypespec",
|
||||
"vpiArrayTypespec",
|
||||
"vpiVoidTypespec",
|
||||
"vpiTypespecMember",
|
||||
"vpiDistItem",
|
||||
"vpiAliasStmt",
|
||||
"vpiThread",
|
||||
"vpiMethodFuncCall",
|
||||
"vpiMethodTaskCall",
|
||||
"vpiClockingBlock",
|
||||
"vpiClockingIODecl",
|
||||
"vpiClassDefn",
|
||||
"vpiConstraint",
|
||||
"vpiConstraintOrdering",
|
||||
"vpiPropertyDecl",
|
||||
"vpiPropertySpec",
|
||||
"vpiPropertyExpr",
|
||||
"vpiMulticlockSequenceExpr",
|
||||
"vpiClockedSeq",
|
||||
"vpiPropertyInst",
|
||||
"vpiSequenceDecl",
|
||||
"vpiCaseProperty",
|
||||
"*undefined*", // 663 is not defined for object types
|
||||
"vpiSequenceInst",
|
||||
"vpiImmediateAssert",
|
||||
"vpiReturn",
|
||||
"vpiAnyPattern",
|
||||
"vpiTaggedPattern",
|
||||
"vpiStructPattern",
|
||||
"vpiDoWhile",
|
||||
"vpiOrderedWait",
|
||||
"vpiWaitFork",
|
||||
"vpiDisableFork",
|
||||
"vpiExpectStmt",
|
||||
"vpiForeachStmt",
|
||||
"vpiFinal",
|
||||
"vpiExtends",
|
||||
"vpiDistribution",
|
||||
"vpiSeqFormalDecl",
|
||||
"vpiEnumNet",
|
||||
"vpiIntegerNet",
|
||||
"vpiTimeNet",
|
||||
"vpiStructNet",
|
||||
"vpiBreak",
|
||||
"vpiContinue",
|
||||
"vpiAssert",
|
||||
"vpiAssume",
|
||||
"vpiCover",
|
||||
"vpiDisableCondition",
|
||||
"vpiClockingEvent",
|
||||
"vpiReturnStmt",
|
||||
"vpiPackedArrayTypespec",
|
||||
"vpiPackedArrayNet",
|
||||
"vpiImmediateAssume",
|
||||
"vpiImmediateCover",
|
||||
"vpiSequenceTypespec",
|
||||
"vpiPropertyTypespec",
|
||||
"vpiEventTypespec",
|
||||
"vpiPropFormalDecl",
|
||||
};
|
||||
// clang-format on
|
||||
if (VL_UNCOVERABLE(vpiVal < 0)) return names[0];
|
||||
return names[(vpiVal <= vpiAutomatics) ? vpiVal : 0];
|
||||
if (VL_UNCOVERABLE(vpiVal < 0))
|
||||
return names[0];
|
||||
else if (vpiVal <= vpiAutomatics)
|
||||
return names[vpiVal];
|
||||
else if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl)
|
||||
return sv_names1[(vpiVal - vpiPackage)];
|
||||
else
|
||||
return names[0];
|
||||
}
|
||||
const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE {
|
||||
// clang-format off
|
||||
@@ -1313,6 +1425,104 @@ void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGenScope);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGenVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAutomatics);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackage);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterface);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiProgram);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterfaceArray);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiProgramArray);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModport);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterfaceTfDecl);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRefObj);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypeParameter);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLongIntVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortIntVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortRealVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiByteVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStringVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassObj);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayVar);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLongIntTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortRealTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiByteTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortIntTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStringTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumConst);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntegerTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRealTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLogicTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiArrayTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiVoidTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypespecMember);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDistItem);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAliasStmt);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiThread);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMethodFuncCall);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMethodTaskCall);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingBlock);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingIODecl);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassDefn);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiConstraint);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiConstraintOrdering);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyDecl);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertySpec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyExpr);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMulticlockSequenceExpr);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockedSeq);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyInst);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceDecl);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCaseProperty);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceInst);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateAssert);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiReturn);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAnyPattern);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTaggedPattern);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructPattern);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDoWhile);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOrderedWait);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWaitFork);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDisableFork);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiExpectStmt);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForeachStmt);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFinal);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiExtends);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDistribution);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSeqFormalDecl);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumNet);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntegerNet);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeNet);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructNet);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBreak);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiContinue);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssert);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssume);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCover);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDisableCondition);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingEvent);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiReturnStmt);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayNet);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateAssume);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateCover);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEventTypespec);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropFormalDecl);
|
||||
|
||||
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiCondition);
|
||||
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiStmt);
|
||||
@@ -1734,7 +1944,8 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c
|
||||
case VLVT_UINT64:
|
||||
case VLVT_WDATA: return status;
|
||||
case VLVT_STRING:
|
||||
if (isGetValue) {
|
||||
// string parameter values can't be changed
|
||||
if (isGetValue || !varp->isParam()) {
|
||||
return status;
|
||||
} else {
|
||||
status = false;
|
||||
@@ -1749,6 +1960,11 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c
|
||||
case VLVT_UINT32: return status;
|
||||
default: status = false;
|
||||
}
|
||||
} else if (valuep->format == vpiRealVal) {
|
||||
switch (varp->vltype()) {
|
||||
case VLVT_REAL: return status;
|
||||
default: status = false;
|
||||
}
|
||||
} else if (valuep->format == vpiSuppressVal) {
|
||||
return status;
|
||||
} else {
|
||||
@@ -1766,6 +1982,8 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
static thread_local char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
// cppcheck-suppress variableScope
|
||||
static const thread_local int t_outStrSz = sizeof(t_outStr) - 1;
|
||||
// string data type is dynamic and may vary in size during simulation
|
||||
static thread_local std::string t_outDynamicStr;
|
||||
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
|
||||
// This may cause backward compatibility issues with older code.
|
||||
if (valuep->format == vpiVectorVal) {
|
||||
@@ -1918,8 +2136,14 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
return;
|
||||
} else if (valuep->format == vpiStringVal) {
|
||||
if (varp->vltype() == VLVT_STRING) {
|
||||
valuep->value.str = reinterpret_cast<char*>(varDatap);
|
||||
return;
|
||||
if (varp->isParam()) {
|
||||
valuep->value.str = reinterpret_cast<char*>(varDatap);
|
||||
return;
|
||||
} else {
|
||||
t_outDynamicStr = *(reinterpret_cast<std::string*>(varDatap));
|
||||
valuep->value.str = const_cast<char*>(t_outDynamicStr.c_str());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
valuep->value.str = t_outStr;
|
||||
int bytes = VL_BYTES_I(varp->packed().elements());
|
||||
@@ -1954,6 +2178,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
valuep->value.integer = *(reinterpret_cast<IData*>(varDatap));
|
||||
return;
|
||||
}
|
||||
} else if (valuep->format == vpiRealVal) {
|
||||
valuep->value.real = *(reinterpret_cast<double*>(varDatap));
|
||||
return;
|
||||
} else if (valuep->format == vpiSuppressVal) {
|
||||
return;
|
||||
}
|
||||
@@ -2171,12 +2398,17 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
datap[(chars - 1) >> 1] &= vop->mask_byte((chars - 1) >> 1);
|
||||
return object;
|
||||
} else if (valuep->format == vpiStringVal) {
|
||||
const int bytes = VL_BYTES_I(vop->varp()->packed().elements());
|
||||
const int len = std::strlen(valuep->value.str);
|
||||
CData* const datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
for (int i = 0; i < bytes; ++i) {
|
||||
// prepend with 0 values before placing string the least significant bytes
|
||||
datap[i] = (i < len) ? valuep->value.str[len - i - 1] : 0;
|
||||
if (vop->varp()->vltype() == VLVT_STRING) {
|
||||
*(reinterpret_cast<std::string*>(vop->varDatap())) = valuep->value.str;
|
||||
return object;
|
||||
} else {
|
||||
const int bytes = VL_BYTES_I(vop->varp()->packed().elements());
|
||||
const int len = std::strlen(valuep->value.str);
|
||||
CData* const datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
for (int i = 0; i < bytes; ++i) {
|
||||
// prepend with 0 values before placing string the least significant bytes
|
||||
datap[i] = (i < len) ? valuep->value.str[len - i - 1] : 0;
|
||||
}
|
||||
}
|
||||
return object;
|
||||
} else if (valuep->format == vpiIntVal) {
|
||||
@@ -2190,6 +2422,11 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
*(reinterpret_cast<IData*>(vop->varDatap())) = vop->mask() & valuep->value.integer;
|
||||
return object;
|
||||
}
|
||||
} else if (valuep->format == vpiRealVal) {
|
||||
if (vop->varp()->vltype() == VLVT_REAL) {
|
||||
*(reinterpret_cast<double*>(vop->varDatap())) = valuep->value.real;
|
||||
return object;
|
||||
}
|
||||
}
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
#include "verilated_syms.h"
|
||||
|
||||
//======================================================================
|
||||
// From IEEE 1800-2009 annex K
|
||||
// From IEEE 1800-2017 annex M
|
||||
|
||||
#include "vltstd/vpi_user.h"
|
||||
#include "vltstd/sv_vpi_user.h"
|
||||
|
||||
//======================================================================
|
||||
|
||||
|
||||
+33
-16
@@ -94,20 +94,6 @@
|
||||
// Function requires a capability inbound (-fthread-safety)
|
||||
#define VL_CAPABILITY(x) \
|
||||
VL_CLANG_ATTR(capability(x))
|
||||
// Function requires not having a capability inbound (-fthread-safety)
|
||||
#define VL_REQUIRES(x) \
|
||||
VL_CLANG_ATTR(annotate("REQUIRES")) \
|
||||
VL_CLANG_ATTR(requires_capability(x))
|
||||
// Name of capability/lock (-fthread-safety)
|
||||
#define VL_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("GUARDED_BY")) \
|
||||
VL_CLANG_ATTR(guarded_by(x))
|
||||
// The data that the annotated pointer points to is protected by the given capability.
|
||||
// The pointer itself is not protected.
|
||||
// Allowed on: pointer data member. (-fthread-safety)
|
||||
#define VL_PT_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \
|
||||
VL_CLANG_ATTR(pt_guarded_by(x))
|
||||
// Name of mutex protecting this variable (-fthread-safety)
|
||||
#define VL_EXCLUDES(x) \
|
||||
VL_CLANG_ATTR(annotate("EXCLUDES")) \
|
||||
@@ -124,6 +110,32 @@
|
||||
#define VL_ASSERT_CAPABILITY(x) \
|
||||
VL_CLANG_ATTR(assert_capability(x))
|
||||
|
||||
// Require mutex locks only in code units which work with enabled multi-threading.
|
||||
#if !defined(VL_MT_DISABLED_CODE_UNIT)
|
||||
// Function requires not having a capability inbound (-fthread-safety)
|
||||
# define VL_REQUIRES(x) \
|
||||
VL_CLANG_ATTR(annotate("REQUIRES")) \
|
||||
VL_CLANG_ATTR(requires_capability(x))
|
||||
// Name of capability/lock (-fthread-safety)
|
||||
# define VL_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("GUARDED_BY")) \
|
||||
VL_CLANG_ATTR(guarded_by(x))
|
||||
// The data that the annotated pointer points to is protected by the given capability.
|
||||
// The pointer itself is not protected.
|
||||
// Allowed on: pointer data member. (-fthread-safety)
|
||||
# define VL_PT_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \
|
||||
VL_CLANG_ATTR(pt_guarded_by(x))
|
||||
#else
|
||||
// Keep annotations for clang_check_attributes
|
||||
# define VL_REQUIRES(x) \
|
||||
VL_CLANG_ATTR(annotate("REQUIRES"))
|
||||
# define VL_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("GUARDED_BY"))
|
||||
# define VL_PT_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("PT_GUARDED_BY"))
|
||||
#endif
|
||||
|
||||
// Defaults for unsupported compiler features
|
||||
#ifndef VL_ATTR_ALWINLINE
|
||||
# define VL_ATTR_ALWINLINE ///< Attribute to inline, even when not optimizing
|
||||
@@ -430,6 +442,11 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
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& operator=(Type&&) = delete
|
||||
|
||||
//=========================================================================
|
||||
// Verilated function size macros
|
||||
|
||||
@@ -577,8 +594,8 @@ static inline double VL_ROUND(double n) {
|
||||
//=========================================================================
|
||||
// Stringify macros
|
||||
|
||||
#define VL_STRINGIFY(x) VL_STRINGIFY2(x)
|
||||
#define VL_STRINGIFY2(x) #x
|
||||
#define VL_STRINGIFY(...) VL_STRINGIFY2(__VA_ARGS__)
|
||||
#define VL_STRINGIFY2(...) #__VA_ARGS__
|
||||
|
||||
//=========================================================================
|
||||
// Offset of field in type
|
||||
|
||||
@@ -0,0 +1,570 @@
|
||||
/***************************************************************************
|
||||
* sv_vpi_user.h
|
||||
*
|
||||
* SystemVerilog VPI extensions.
|
||||
*
|
||||
* This file contains the constant definitions, structure definitions, and
|
||||
* routine declarations used by the SystemVerilog Verification Procedural
|
||||
* Interface (VPI) access routines.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* NOTE:
|
||||
* The constant values 600 through 999 are reserved for use in this file.
|
||||
* - the range 600-749 is reserved for SV VPI model extensions
|
||||
* - the range 750-779 is reserved for the Coverage VPI
|
||||
* - the range 800-899 is reserved for future use
|
||||
* Overlaps in the numerical ranges are permitted for different categories
|
||||
* of identifiers; e.g.
|
||||
* - object types
|
||||
* - properties
|
||||
* - callbacks
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef SV_VPI_USER_H
|
||||
#define SV_VPI_USER_H
|
||||
|
||||
#include "vpi_user.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/****************************** OBJECT TYPES ******************************/
|
||||
#define vpiPackage 600
|
||||
#define vpiInterface 601
|
||||
#define vpiProgram 602
|
||||
#define vpiInterfaceArray 603
|
||||
#define vpiProgramArray 604
|
||||
#define vpiTypespec 605
|
||||
#define vpiModport 606
|
||||
#define vpiInterfaceTfDecl 607
|
||||
#define vpiRefObj 608
|
||||
#define vpiTypeParameter 609
|
||||
|
||||
/* variables */
|
||||
#define vpiVarBit vpiRegBit
|
||||
#define vpiLongIntVar 610
|
||||
#define vpiShortIntVar 611
|
||||
#define vpiIntVar 612
|
||||
#define vpiShortRealVar 613
|
||||
#define vpiByteVar 614
|
||||
#define vpiClassVar 615
|
||||
#define vpiStringVar 616
|
||||
#define vpiEnumVar 617
|
||||
#define vpiStructVar 618
|
||||
#define vpiUnionVar 619
|
||||
#define vpiBitVar 620
|
||||
#define vpiLogicVar vpiReg
|
||||
#define vpiArrayVar vpiRegArray
|
||||
#define vpiClassObj 621
|
||||
#define vpiChandleVar 622
|
||||
#define vpiPackedArrayVar 623
|
||||
#define vpiVirtualInterfaceVar 728
|
||||
|
||||
/* typespecs */
|
||||
#define vpiLongIntTypespec 625
|
||||
#define vpiShortRealTypespec 626
|
||||
#define vpiByteTypespec 627
|
||||
#define vpiShortIntTypespec 628
|
||||
#define vpiIntTypespec 629
|
||||
#define vpiClassTypespec 630
|
||||
#define vpiStringTypespec 631
|
||||
#define vpiChandleTypespec 632
|
||||
#define vpiEnumTypespec 633
|
||||
#define vpiEnumConst 634
|
||||
#define vpiIntegerTypespec 635
|
||||
#define vpiTimeTypespec 636
|
||||
#define vpiRealTypespec 637
|
||||
#define vpiStructTypespec 638
|
||||
#define vpiUnionTypespec 639
|
||||
#define vpiBitTypespec 640
|
||||
#define vpiLogicTypespec 641
|
||||
#define vpiArrayTypespec 642
|
||||
#define vpiVoidTypespec 643
|
||||
#define vpiTypespecMember 644
|
||||
#define vpiPackedArrayTypespec 692
|
||||
#define vpiSequenceTypespec 696
|
||||
#define vpiPropertyTypespec 697
|
||||
#define vpiEventTypespec 698
|
||||
#define vpiInterfaceTypespec 906
|
||||
|
||||
#define vpiClockingBlock 650
|
||||
#define vpiClockingIODecl 651
|
||||
#define vpiClassDefn 652
|
||||
#define vpiConstraint 653
|
||||
#define vpiConstraintOrdering 654
|
||||
|
||||
#define vpiDistItem 645
|
||||
#define vpiAliasStmt 646
|
||||
#define vpiThread 647
|
||||
#define vpiMethodFuncCall 648
|
||||
#define vpiMethodTaskCall 649
|
||||
|
||||
/* concurrent assertions */
|
||||
#define vpiAssert 686
|
||||
#define vpiAssume 687
|
||||
#define vpiCover 688
|
||||
#define vpiRestrict 901
|
||||
|
||||
#define vpiDisableCondition 689
|
||||
#define vpiClockingEvent 690
|
||||
/* property decl, spec */
|
||||
#define vpiPropertyDecl 655
|
||||
#define vpiPropertySpec 656
|
||||
#define vpiPropertyExpr 657
|
||||
#define vpiMulticlockSequenceExpr 658
|
||||
#define vpiClockedSeq 659
|
||||
#define vpiClockedProp 902
|
||||
#define vpiPropertyInst 660
|
||||
#define vpiSequenceDecl 661
|
||||
#define vpiCaseProperty 662 /* property case */
|
||||
#define vpiCasePropertyItem 905 /* property case item */
|
||||
#define vpiSequenceInst 664
|
||||
#define vpiImmediateAssert 665
|
||||
#define vpiImmediateAssume 694
|
||||
#define vpiImmediateCover 695
|
||||
#define vpiReturn 666
|
||||
/* pattern */
|
||||
#define vpiAnyPattern 667
|
||||
#define vpiTaggedPattern 668
|
||||
#define vpiStructPattern 669
|
||||
/* do .. while */
|
||||
#define vpiDoWhile 670
|
||||
/* waits */
|
||||
#define vpiOrderedWait 671
|
||||
#define vpiWaitFork 672
|
||||
/* disables */
|
||||
#define vpiDisableFork 673
|
||||
#define vpiExpectStmt 674
|
||||
#define vpiForeachStmt 675
|
||||
#define vpiReturnStmt 691
|
||||
#define vpiFinal 676
|
||||
#define vpiExtends 677
|
||||
#define vpiDistribution 678
|
||||
#define vpiSeqFormalDecl 679
|
||||
#define vpiPropFormalDecl 699
|
||||
#define vpiArrayNet vpiNetArray
|
||||
#define vpiEnumNet 680
|
||||
#define vpiIntegerNet 681
|
||||
#define vpiLogicNet vpiNet
|
||||
#define vpiTimeNet 682
|
||||
#define vpiStructNet 683
|
||||
#define vpiBreak 684
|
||||
#define vpiContinue 685
|
||||
#define vpiPackedArrayNet 693
|
||||
#define vpiConstraintExpr 747
|
||||
#define vpiElseConst 748
|
||||
#define vpiImplication 749
|
||||
#define vpiConstrIf 738
|
||||
#define vpiConstrIfElse 739
|
||||
#define vpiConstrForEach 736
|
||||
#define vpiSoftDisable 733
|
||||
#define vpiLetDecl 903
|
||||
#define vpiLetExpr 904
|
||||
|
||||
/******************************** METHODS *********************************/
|
||||
/************* methods used to traverse 1 to 1 relationships **************/
|
||||
#define vpiActual 700
|
||||
|
||||
#define vpiTypedefAlias 701
|
||||
|
||||
#define vpiIndexTypespec 702
|
||||
#define vpiBaseTypespec 703
|
||||
#define vpiElemTypespec 704
|
||||
|
||||
#define vpiInputSkew 706
|
||||
#define vpiOutputSkew 707
|
||||
#define vpiGlobalClocking 708
|
||||
#define vpiDefaultClocking 709
|
||||
#define vpiDefaultDisableIff 710
|
||||
|
||||
#define vpiOrigin 713
|
||||
#define vpiPrefix 714
|
||||
#define vpiWith 715
|
||||
|
||||
#define vpiProperty 718
|
||||
|
||||
#define vpiValueRange 720
|
||||
#define vpiPattern 721
|
||||
#define vpiWeight 722
|
||||
#define vpiConstraintItem 746
|
||||
|
||||
/************ methods used to traverse 1 to many relationships ************/
|
||||
#define vpiTypedef 725
|
||||
#define vpiImport 726
|
||||
#define vpiDerivedClasses 727
|
||||
#define vpiInterfaceDecl vpiVirtualInterfaceVar /* interface decl deprecated */
|
||||
|
||||
#define vpiMethods 730
|
||||
#define vpiSolveBefore 731
|
||||
#define vpiSolveAfter 732
|
||||
|
||||
#define vpiWaitingProcesses 734
|
||||
|
||||
#define vpiMessages 735
|
||||
#define vpiLoopVars 737
|
||||
|
||||
#define vpiConcurrentAssertions 740
|
||||
#define vpiMatchItem 741
|
||||
#define vpiMember 742
|
||||
#define vpiElement 743
|
||||
|
||||
/************* methods used to traverse 1 to many relationships ***************/
|
||||
#define vpiAssertion 744
|
||||
|
||||
/*********** methods used to traverse both 1-1 and 1-many relations ***********/
|
||||
#define vpiInstance 745
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
/************************ generic object properties ***********************/
|
||||
/**************************************************************************/
|
||||
#define vpiTop 600
|
||||
|
||||
#define vpiUnit 602
|
||||
|
||||
#define vpiJoinType 603
|
||||
#define vpiJoin 0
|
||||
#define vpiJoinNone 1
|
||||
#define vpiJoinAny 2
|
||||
|
||||
#define vpiAccessType 604
|
||||
#define vpiForkJoinAcc 1
|
||||
#define vpiExternAcc 2
|
||||
#define vpiDPIExportAcc 3
|
||||
#define vpiDPIImportAcc 4
|
||||
|
||||
#define vpiArrayType 606
|
||||
#define vpiStaticArray 1
|
||||
#define vpiDynamicArray 2
|
||||
#define vpiAssocArray 3
|
||||
#define vpiQueueArray 4
|
||||
#define vpiArrayMember 607
|
||||
|
||||
#define vpiIsRandomized 608
|
||||
#define vpiLocalVarDecls 609
|
||||
#define vpiOpStrong 656 /* strength of temporal operator */
|
||||
#define vpiRandType 610
|
||||
#define vpiNotRand 1
|
||||
#define vpiRand 2
|
||||
#define vpiRandC 3
|
||||
#define vpiPortType 611
|
||||
#define vpiInterfacePort 1
|
||||
#define vpiModportPort 2
|
||||
/* vpiPort is also a port type. It is defined in vpi_user.h */
|
||||
|
||||
|
||||
#define vpiConstantVariable 612
|
||||
#define vpiStructUnionMember 615
|
||||
|
||||
#define vpiVisibility 620
|
||||
#define vpiPublicVis 1
|
||||
#define vpiProtectedVis 2
|
||||
#define vpiLocalVis 3
|
||||
|
||||
/* Return values for vpiConstType property */
|
||||
#define vpiOneStepConst 9
|
||||
#define vpiUnboundedConst 10
|
||||
#define vpiNullConst 11
|
||||
|
||||
#define vpiAlwaysType 624
|
||||
#define vpiAlwaysComb 2
|
||||
#define vpiAlwaysFF 3
|
||||
#define vpiAlwaysLatch 4
|
||||
|
||||
#define vpiDistType 625
|
||||
#define vpiEqualDist 1 /* constraint equal distribution */
|
||||
#define vpiDivDist 2 /* constraint divided distribution */
|
||||
|
||||
#define vpiPacked 630
|
||||
#define vpiTagged 632
|
||||
#define vpiRef 6 /* Return value for vpiDirection property */
|
||||
#define vpiVirtual 635
|
||||
#define vpiHasActual 636
|
||||
#define vpiIsConstraintEnabled 638
|
||||
#define vpiSoft 639
|
||||
|
||||
|
||||
#define vpiClassType 640
|
||||
#define vpiMailboxClass 1
|
||||
#define vpiSemaphoreClass 2
|
||||
#define vpiUserDefinedClass 3
|
||||
#define vpiProcessClass 4
|
||||
|
||||
#define vpiMethod 645
|
||||
#define vpiIsClockInferred 649
|
||||
#define vpiIsDeferred 657
|
||||
#define vpiIsFinal 670
|
||||
#define vpiIsCoverSequence 659
|
||||
#define vpiQualifier 650
|
||||
#define vpiNoQualifier 0
|
||||
#define vpiUniqueQualifier 1
|
||||
#define vpiPriorityQualifier 2
|
||||
#define vpiTaggedQualifier 4
|
||||
#define vpiRandQualifier 8
|
||||
#define vpiInsideQualifier 16
|
||||
|
||||
#define vpiInputEdge 651 /* returns vpiNoEdge, vpiPosedge,
|
||||
vpiNegedge */
|
||||
#define vpiOutputEdge 652 /* returns vpiNoEdge, vpiPosedge,
|
||||
vpiNegedge */
|
||||
#define vpiGeneric 653
|
||||
|
||||
/* Compatibility-mode property and values (object argument == NULL) */
|
||||
#define vpiCompatibilityMode 654
|
||||
#define vpiMode1364v1995 1
|
||||
#define vpiMode1364v2001 2
|
||||
#define vpiMode1364v2005 3
|
||||
#define vpiMode1800v2005 4
|
||||
#define vpiMode1800v2009 5
|
||||
|
||||
#define vpiPackedArrayMember 655
|
||||
#define vpiStartLine 661
|
||||
#define vpiColumn 662
|
||||
#define vpiEndLine 663
|
||||
#define vpiEndColumn 664
|
||||
|
||||
/* memory allocation scheme for transient objects */
|
||||
#define vpiAllocScheme 658
|
||||
#define vpiAutomaticScheme 1
|
||||
#define vpiDynamicScheme 2
|
||||
#define vpiOtherScheme 3
|
||||
|
||||
#define vpiObjId 660
|
||||
|
||||
#define vpiDPIPure 665
|
||||
#define vpiDPIContext 666
|
||||
#define vpiDPICStr 667
|
||||
#define vpiDPI 1
|
||||
#define vpiDPIC 2
|
||||
#define vpiDPICIdentifier 668
|
||||
#define vpiIsModPort 669
|
||||
|
||||
/******************************** Operators *******************************/
|
||||
#define vpiImplyOp 50 /* -> implication operator */
|
||||
#define vpiNonOverlapImplyOp 51 /* |=> nonoverlapped implication */
|
||||
#define vpiOverlapImplyOp 52 /* |-> overlapped implication operator */
|
||||
#define vpiAcceptOnOp 83 /* accept_on operator */
|
||||
#define vpiRejectOnOp 84 /* reject_on operator */
|
||||
#define vpiSyncAcceptOnOp 85 /* sync_accept_on operator */
|
||||
#define vpiSyncRejectOnOp 86 /* sync_reject_on operator */
|
||||
#define vpiOverlapFollowedByOp 87 /* overlapped followed_by operator */
|
||||
#define vpiNonOverlapFollowedByOp 88 /* nonoverlapped followed_by operator */
|
||||
#define vpiNexttimeOp 89 /* nexttime operator */
|
||||
#define vpiAlwaysOp 90 /* always operator */
|
||||
#define vpiEventuallyOp 91 /* eventually operator */
|
||||
#define vpiUntilOp 92 /* until operator */
|
||||
#define vpiUntilWithOp 93 /* until_with operator */
|
||||
|
||||
#define vpiUnaryCycleDelayOp 53 /* binary cycle delay (##) operator */
|
||||
#define vpiCycleDelayOp 54 /* binary cycle delay (##) operator */
|
||||
#define vpiIntersectOp 55 /* intersection operator */
|
||||
#define vpiFirstMatchOp 56 /* first_match operator */
|
||||
#define vpiThroughoutOp 57 /* throughout operator */
|
||||
#define vpiWithinOp 58 /* within operator */
|
||||
#define vpiRepeatOp 59 /* [=] nonconsecutive repetition */
|
||||
#define vpiConsecutiveRepeatOp 60 /* [*] consecutive repetition */
|
||||
#define vpiGotoRepeatOp 61 /* [->] goto repetition */
|
||||
|
||||
#define vpiPostIncOp 62 /* ++ post-increment */
|
||||
#define vpiPreIncOp 63 /* ++ pre-increment */
|
||||
#define vpiPostDecOp 64 /* -- post-decrement */
|
||||
#define vpiPreDecOp 65 /* -- pre-decrement */
|
||||
|
||||
#define vpiMatchOp 66 /* match() operator */
|
||||
#define vpiCastOp 67 /* type'() operator */
|
||||
#define vpiIffOp 68 /* iff operator */
|
||||
#define vpiWildEqOp 69 /* ==? operator */
|
||||
#define vpiWildNeqOp 70 /* !=? operator */
|
||||
|
||||
#define vpiStreamLROp 71 /* left-to-right streaming {>>} operator */
|
||||
#define vpiStreamRLOp 72 /* right-to-left streaming {<<} operator */
|
||||
|
||||
#define vpiMatchedOp 73 /* the .matched sequence operation */
|
||||
#define vpiTriggeredOp 74 /* the .triggered sequence operation */
|
||||
#define vpiAssignmentPatternOp 75 /* '{} assignment pattern */
|
||||
#define vpiMultiAssignmentPatternOp 76 /* '{n{}} multi assignment pattern */
|
||||
#define vpiIfOp 77 /* if operator */
|
||||
#define vpiIfElseOp 78 /* if-else operator */
|
||||
#define vpiCompAndOp 79 /* Composite and operator */
|
||||
#define vpiCompOrOp 80 /* Composite or operator */
|
||||
#define vpiImpliesOp 94 /* implies operator */
|
||||
#define vpiInsideOp 95 /* inside operator */
|
||||
#define vpiTypeOp 81 /* type operator */
|
||||
#define vpiAssignmentOp 82 /* Normal assignment */
|
||||
|
||||
/*********************** task/function properties ***********************/
|
||||
#define vpiOtherFunc 6 /* returns other types; for property vpiFuncType */
|
||||
|
||||
/* vpiValid and vpiValidUnknown were deprecated in 1800-2009 */
|
||||
/*********************** value for vpiValid *****************************/
|
||||
#define vpiValidUnknown 2 /* Validity of variable is unknown */
|
||||
|
||||
/************************** STRUCTURE DEFINITIONS *************************/
|
||||
|
||||
/***************************** structure *****************************/
|
||||
|
||||
/**************************** CALLBACK REASONS ****************************/
|
||||
#define cbStartOfThread 600 /* callback on thread creation */
|
||||
#define cbEndOfThread 601 /* callback on thread termination */
|
||||
#define cbEnterThread 602 /* callback on reentering thread */
|
||||
#define cbStartOfFrame 603 /* callback on frame creation */
|
||||
#define cbEndOfFrame 604 /* callback on frame exit */
|
||||
#define cbSizeChange 605 /* callback on array variable size change */
|
||||
#define cbCreateObj 700 /* callback on class object creation */
|
||||
#define cbReclaimObj 701 /* callback on class object reclaimed by
|
||||
automatic memory management */
|
||||
#define cbEndOfObject 702 /* callback on transient object deletion */
|
||||
|
||||
/************************* FUNCTION DECLARATIONS **************************/
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
/*************************** Coverage VPI *********************************/
|
||||
/**************************************************************************/
|
||||
|
||||
/* coverage control */
|
||||
#define vpiCoverageStart 750
|
||||
#define vpiCoverageStop 751
|
||||
#define vpiCoverageReset 752
|
||||
#define vpiCoverageCheck 753
|
||||
#define vpiCoverageMerge 754
|
||||
#define vpiCoverageSave 755
|
||||
|
||||
/* coverage type properties */
|
||||
#define vpiAssertCoverage 760
|
||||
#define vpiFsmStateCoverage 761
|
||||
#define vpiStatementCoverage 762
|
||||
#define vpiToggleCoverage 763
|
||||
|
||||
/* coverage status properties */
|
||||
#define vpiCovered 765
|
||||
#define vpiCoverMax 766 /* preserved for backward compatibility */
|
||||
#define vpiCoveredMax 766
|
||||
#define vpiCoveredCount 767
|
||||
|
||||
/* assertion-specific coverage status properties */
|
||||
#define vpiAssertAttemptCovered 770
|
||||
#define vpiAssertSuccessCovered 771
|
||||
#define vpiAssertFailureCovered 772
|
||||
#define vpiAssertVacuousSuccessCovered 773
|
||||
#define vpiAssertDisableCovered 774
|
||||
#define vpiAssertKillCovered 777
|
||||
|
||||
/* FSM-specific coverage status properties */
|
||||
#define vpiFsmStates 775
|
||||
#define vpiFsmStateExpression 776
|
||||
|
||||
/* FSM handle types */
|
||||
#define vpiFsm 758
|
||||
#define vpiFsmHandle 759
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
/***************************** Assertion VPI *******************************/
|
||||
/***************************************************************************/
|
||||
|
||||
/* assertion callback types */
|
||||
#define cbAssertionStart 606
|
||||
#define cbAssertionSuccess 607
|
||||
#define cbAssertionFailure 608
|
||||
#define cbAssertionVacuousSuccess 657
|
||||
#define cbAssertionDisabledEvaluation 658
|
||||
#define cbAssertionStepSuccess 609
|
||||
#define cbAssertionStepFailure 610
|
||||
#define cbAssertionLock 661
|
||||
#define cbAssertionUnlock 662
|
||||
#define cbAssertionDisable 611
|
||||
#define cbAssertionEnable 612
|
||||
#define cbAssertionReset 613
|
||||
#define cbAssertionKill 614
|
||||
#define cbAssertionEnablePassAction 645
|
||||
#define cbAssertionEnableFailAction 646
|
||||
#define cbAssertionDisablePassAction 647
|
||||
#define cbAssertionDisableFailAction 648
|
||||
#define cbAssertionEnableNonvacuousAction 649
|
||||
#define cbAssertionDisableVacuousAction 650
|
||||
|
||||
/* assertion "system" callback types */
|
||||
#define cbAssertionSysInitialized 615
|
||||
#define cbAssertionSysOn 616
|
||||
#define cbAssertionSysOff 617
|
||||
#define cbAssertionSysKill 631
|
||||
#define cbAssertionSysLock 659
|
||||
#define cbAssertionSysUnlock 660
|
||||
#define cbAssertionSysEnd 618
|
||||
#define cbAssertionSysReset 619
|
||||
#define cbAssertionSysEnablePassAction 651
|
||||
#define cbAssertionSysEnableFailAction 652
|
||||
#define cbAssertionSysDisablePassAction 653
|
||||
#define cbAssertionSysDisableFailAction 654
|
||||
#define cbAssertionSysEnableNonvacuousAction 655
|
||||
#define cbAssertionSysDisableVacuousAction 656
|
||||
|
||||
/* assertion control constants */
|
||||
#define vpiAssertionLock 645
|
||||
#define vpiAssertionUnlock 646
|
||||
#define vpiAssertionDisable 620
|
||||
#define vpiAssertionEnable 621
|
||||
#define vpiAssertionReset 622
|
||||
#define vpiAssertionKill 623
|
||||
#define vpiAssertionEnableStep 624
|
||||
#define vpiAssertionDisableStep 625
|
||||
#define vpiAssertionClockSteps 626
|
||||
#define vpiAssertionSysLock 647
|
||||
#define vpiAssertionSysUnlock 648
|
||||
#define vpiAssertionSysOn 627
|
||||
#define vpiAssertionSysOff 628
|
||||
#define vpiAssertionSysKill 632
|
||||
#define vpiAssertionSysEnd 629
|
||||
#define vpiAssertionSysReset 630
|
||||
#define vpiAssertionDisablePassAction 633
|
||||
#define vpiAssertionEnablePassAction 634
|
||||
#define vpiAssertionDisableFailAction 635
|
||||
#define vpiAssertionEnableFailAction 636
|
||||
#define vpiAssertionDisableVacuousAction 637
|
||||
#define vpiAssertionEnableNonvacuousAction 638
|
||||
#define vpiAssertionSysEnablePassAction 639
|
||||
#define vpiAssertionSysEnableFailAction 640
|
||||
#define vpiAssertionSysDisablePassAction 641
|
||||
#define vpiAssertionSysDisableFailAction 642
|
||||
#define vpiAssertionSysEnableNonvacuousAction 643
|
||||
#define vpiAssertionSysDisableVacuousAction 644
|
||||
|
||||
typedef struct t_vpi_assertion_step_info {
|
||||
PLI_INT32 matched_expression_count;
|
||||
vpiHandle *matched_exprs; /* array of expressions */
|
||||
PLI_INT32 stateFrom, stateTo; /* identify transition */
|
||||
} s_vpi_assertion_step_info, *p_vpi_assertion_step_info;
|
||||
|
||||
typedef struct t_vpi_attempt_info {
|
||||
union {
|
||||
vpiHandle failExpr;
|
||||
p_vpi_assertion_step_info step;
|
||||
} detail;
|
||||
s_vpi_time attemptStartTime; /* Time attempt triggered */
|
||||
} s_vpi_attempt_info, *p_vpi_attempt_info;
|
||||
|
||||
/* typedef for vpi_register_assertion_cb callback function */
|
||||
typedef PLI_INT32(vpi_assertion_callback_func)(
|
||||
PLI_INT32 reason, /* callback reason */
|
||||
p_vpi_time cb_time, /* callback time */
|
||||
vpiHandle assertion, /* handle to assertion */
|
||||
p_vpi_attempt_info info, /* attempt related information */
|
||||
PLI_BYTE8 *user_data /* user data entered upon registration */
|
||||
);
|
||||
|
||||
vpiHandle vpi_register_assertion_cb(
|
||||
vpiHandle assertion, /* handle to assertion */
|
||||
PLI_INT32 reason, /* reason for which callbacks needed */
|
||||
vpi_assertion_callback_func *cb_rtn,
|
||||
PLI_BYTE8 *user_data /* user data to be supplied to cb */
|
||||
);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+268
-50
@@ -10,16 +10,36 @@ import argparse
|
||||
import os
|
||||
import sys
|
||||
import shlex
|
||||
from typing import Callable, Iterable, Optional, Union
|
||||
from typing import Callable, Iterable, Optional, Union, TYPE_CHECKING
|
||||
import dataclasses
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
from enum import Enum
|
||||
import multiprocessing
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
import clang.cindex
|
||||
from clang.cindex import CursorKind, Index, TranslationUnitSaveError, TranslationUnitLoadError
|
||||
from clang.cindex import (
|
||||
Index,
|
||||
TranslationUnitSaveError,
|
||||
TranslationUnitLoadError,
|
||||
CompilationDatabase,
|
||||
)
|
||||
|
||||
if not TYPE_CHECKING:
|
||||
from clang.cindex import CursorKind
|
||||
else:
|
||||
# Workaround for missing support for members defined out-of-class in Pylance:
|
||||
# https://github.com/microsoft/pylance-release/issues/2365#issuecomment-1035803067
|
||||
|
||||
class CursorKindMeta(type):
|
||||
|
||||
def __getattr__(cls, name: str) -> clang.cindex.CursorKind:
|
||||
return getattr(clang.cindex.CursorKind, name)
|
||||
|
||||
class CursorKind(clang.cindex.CursorKind, metaclass=CursorKindMeta):
|
||||
pass
|
||||
|
||||
|
||||
def fully_qualified_name(node):
|
||||
@@ -60,6 +80,7 @@ class VlAnnotations:
|
||||
stable_tree: bool = False
|
||||
mt_safe_postinit: bool = False
|
||||
mt_unsafe: bool = False
|
||||
mt_disabled: bool = False
|
||||
mt_unsafe_one: bool = False
|
||||
pure: bool = False
|
||||
guarded: bool = False
|
||||
@@ -81,7 +102,7 @@ class VlAnnotations:
|
||||
return self.stable_tree or self.mt_start
|
||||
|
||||
def is_mt_unsafe_call(self):
|
||||
return self.mt_unsafe or self.mt_unsafe_one
|
||||
return self.mt_unsafe or self.mt_unsafe_one or self.mt_disabled
|
||||
|
||||
def is_mt_safe_call(self):
|
||||
return (not self.is_mt_unsafe_call()
|
||||
@@ -131,6 +152,8 @@ class VlAnnotations:
|
||||
result.mt_unsafe = True
|
||||
elif node.displayname == "MT_UNSAFE_ONE":
|
||||
result.mt_unsafe_one = True
|
||||
elif node.displayname == "MT_DISABLED":
|
||||
result.mt_disabled = True
|
||||
elif node.displayname == "PURE":
|
||||
result.pure = True
|
||||
elif node.displayname in ["ACQUIRE", "ACQUIRE_SHARED"]:
|
||||
@@ -203,6 +226,19 @@ class FunctionInfo:
|
||||
def copy(self, /, **changes):
|
||||
return dataclasses.replace(self, **changes)
|
||||
|
||||
@staticmethod
|
||||
def from_decl_file_line_and_refd_node(file: str, line: int,
|
||||
refd: clang.cindex.Cursor,
|
||||
annotations: VlAnnotations):
|
||||
file = os.path.abspath(file)
|
||||
refd = refd.canonical
|
||||
assert refd is not None
|
||||
name_parts = fully_qualified_name(refd)
|
||||
usr = refd.get_usr()
|
||||
ftype = FunctionType.from_node(refd)
|
||||
|
||||
return FunctionInfo(name_parts, usr, file, line, annotations, ftype)
|
||||
|
||||
@staticmethod
|
||||
def from_node(node: clang.cindex.Cursor,
|
||||
refd: Optional[clang.cindex.Cursor] = None,
|
||||
@@ -228,6 +264,7 @@ class DiagnosticKind(Enum):
|
||||
NON_PURE_CALL_IN_PURE_CTX = enum.auto()
|
||||
NON_MT_SAFE_CALL_IN_MT_SAFE_CTX = enum.auto()
|
||||
NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX = enum.auto()
|
||||
MISSING_MT_DISABLED_ANNOTATION = enum.auto()
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.value < other.value
|
||||
@@ -265,35 +302,142 @@ class CallAnnotationsValidator:
|
||||
|
||||
self._index = Index.create()
|
||||
|
||||
self._processed_headers: set[str] = set()
|
||||
# Map key represents translation unit initial defines
|
||||
# (from command line and source's lines before any include)
|
||||
self._processed_headers: dict[str, set[str]] = {}
|
||||
self._external_decls: dict[str, set[tuple[str, int]]] = {}
|
||||
|
||||
# Current context
|
||||
self._main_source_file: str = ""
|
||||
self._defines: dict[str, str] = {}
|
||||
self._call_location: Optional[FunctionInfo] = None
|
||||
self._caller: Optional[FunctionInfo] = None
|
||||
self._level: int = 0
|
||||
self._constructor_context: list[clang.cindex.Cursor] = []
|
||||
self._level: int = 0
|
||||
|
||||
def is_mt_disabled_code_unit(self):
|
||||
return "VL_MT_DISABLED_CODE_UNIT" in self._defines
|
||||
|
||||
def is_constructor_context(self):
|
||||
return len(self._constructor_context) > 0
|
||||
|
||||
# Parses all lines in a form: `#define KEY VALUE` located before any `#include` line.
|
||||
# The parsing is very simple, there is no support for line breaks, etc.
|
||||
@staticmethod
|
||||
def parse_initial_defines(source_file: str) -> dict[str, str]:
|
||||
defs: dict[str, str] = {}
|
||||
with open(source_file, "r", encoding="utf-8") as file:
|
||||
for line in file:
|
||||
line = line.strip()
|
||||
match = re.fullmatch(
|
||||
r"^#\s*(define\s+(\w+)(?:\s+(.*))?|include\s+.*)$", line)
|
||||
if match:
|
||||
if match.group(1).startswith("define"):
|
||||
key = match.group(2)
|
||||
value = match.groups("1")[2]
|
||||
defs[key] = value
|
||||
elif match.group(1).startswith("include"):
|
||||
break
|
||||
return defs
|
||||
|
||||
@staticmethod
|
||||
def filter_out_unsupported_compiler_args(
|
||||
args: list[str]) -> tuple[list[str], dict[str, str]]:
|
||||
filtered_args = []
|
||||
defines = {}
|
||||
args_iter = iter(args)
|
||||
try:
|
||||
while arg := next(args_iter):
|
||||
# Skip positional arguments (input file name).
|
||||
if not arg.startswith("-") and (arg.endswith(".cpp")
|
||||
or arg.endswith(".c")
|
||||
or arg.endswith(".h")):
|
||||
continue
|
||||
|
||||
# Skipped options with separate value argument.
|
||||
if arg in ["-o", "-T", "-MT", "-MQ", "-MF"
|
||||
"-L"]:
|
||||
next(args_iter)
|
||||
continue
|
||||
|
||||
# Skipped options without separate value argument.
|
||||
if arg == "-c" or arg.startswith("-W") or arg.startswith("-L"):
|
||||
continue
|
||||
|
||||
# Preserved options with separate value argument.
|
||||
if arg in [
|
||||
"-x"
|
||||
"-Xclang", "-I", "-isystem", "-iquote", "-include",
|
||||
"-include-pch"
|
||||
]:
|
||||
filtered_args += [arg, next(args_iter)]
|
||||
continue
|
||||
|
||||
kv_str = None
|
||||
d_or_u = None
|
||||
# Preserve define/undefine with separate value argument.
|
||||
if arg in ["-D", "-U"]:
|
||||
filtered_args.append(arg)
|
||||
d_or_u = arg[1]
|
||||
kv_str = next(args_iter)
|
||||
filtered_args.append(kv_str)
|
||||
# Preserve define/undefine without separate value argument.
|
||||
elif arg[0:2] in ["-D", "-U"]:
|
||||
filtered_args.append(arg)
|
||||
kv_str = arg[2:]
|
||||
d_or_u = arg[1]
|
||||
# Preserve everything else.
|
||||
else:
|
||||
filtered_args.append(arg)
|
||||
continue
|
||||
|
||||
# Keep track of defines for class' internal purposes.
|
||||
key_value = kv_str.split("=", 1)
|
||||
key = key_value[0]
|
||||
val = "1" if len(key_value) == 1 else key_value[1]
|
||||
|
||||
if d_or_u == "D":
|
||||
defines[key] = val
|
||||
elif d_or_u == "U" and key in defines:
|
||||
del defines[key]
|
||||
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
return (filtered_args, defines)
|
||||
|
||||
def compile_and_analyze_file(self, source_file: str,
|
||||
compiler_args: list[str],
|
||||
build_dir: Optional[str]):
|
||||
filename = os.path.abspath(source_file)
|
||||
initial_cwd = "."
|
||||
|
||||
filtered_args, defines = self.filter_out_unsupported_compiler_args(
|
||||
compiler_args)
|
||||
defines.update(self.parse_initial_defines(source_file))
|
||||
|
||||
if build_dir:
|
||||
initial_cwd = os.getcwd()
|
||||
os.chdir(build_dir)
|
||||
translation_unit = self._index.parse(filename, compiler_args)
|
||||
has_errors = False
|
||||
for diag in translation_unit.diagnostics:
|
||||
if diag.severity > clang.cindex.Diagnostic.Error:
|
||||
has_errors = True
|
||||
if translation_unit and not has_errors:
|
||||
try:
|
||||
translation_unit = self._index.parse(filename, filtered_args)
|
||||
except TranslationUnitLoadError:
|
||||
translation_unit = None
|
||||
errors = []
|
||||
if translation_unit:
|
||||
for diag in translation_unit.diagnostics:
|
||||
if diag.severity >= clang.cindex.Diagnostic.Error:
|
||||
errors.append(str(diag))
|
||||
if translation_unit and len(errors) == 0:
|
||||
self._defines = defines
|
||||
self._main_source_file = filename
|
||||
self.process_translation_unit(translation_unit)
|
||||
self._main_source_file = ""
|
||||
self._defines = {}
|
||||
else:
|
||||
print(f"%Error: parsing failed: {filename}", file=sys.stderr)
|
||||
for error in errors:
|
||||
print(f" {error}", file=sys.stderr)
|
||||
if build_dir:
|
||||
os.chdir(initial_cwd)
|
||||
|
||||
@@ -563,6 +707,18 @@ class CallAnnotationsValidator:
|
||||
f" from: {node.location.file.name}:{node.location.line}")
|
||||
return True
|
||||
|
||||
def process_function_declaration(self, node: clang.cindex.Cursor):
|
||||
# Ignore declarations in main .cpp file
|
||||
if node.location.file.name != self._main_source_file:
|
||||
children = list(node.get_children())
|
||||
annotations = VlAnnotations.from_nodes_list(children)
|
||||
if not annotations.mt_disabled:
|
||||
self._external_decls.setdefault(node.get_usr(), set()).add(
|
||||
(str(node.location.file.name), int(node.location.line)))
|
||||
return self.iterate_children(children, self.dispatch_node)
|
||||
|
||||
return self.iterate_children(node.get_children(), self.dispatch_node)
|
||||
|
||||
# Definition handling
|
||||
|
||||
def dispatch_node_inside_definition(self, node: clang.cindex.Cursor):
|
||||
@@ -593,6 +749,18 @@ class CallAnnotationsValidator:
|
||||
assert refd is not None
|
||||
|
||||
def_annotations = VlAnnotations.from_nodes_list(node_children)
|
||||
# Implicitly mark definitions in VL_MT_DISABLED_CODE_UNIT .cpp files as
|
||||
# VL_MT_DISABLED. Existence of the annotation on declarations in .h
|
||||
# files is verified below.
|
||||
# Also sets VL_REQUIRES, as this annotation is added together with
|
||||
# explicit VL_MT_DISABLED.
|
||||
if self.is_mt_disabled_code_unit():
|
||||
if node.location.file.name == self._main_source_file:
|
||||
annotations.mt_disabled = True
|
||||
annotations.requires = True
|
||||
if refd.location.file.name == self._main_source_file:
|
||||
def_annotations.mt_disabled = True
|
||||
def_annotations.requires = True
|
||||
|
||||
if not (def_annotations.is_empty() or def_annotations == annotations):
|
||||
# Use definition's annotations for the diagnostic
|
||||
@@ -605,12 +773,26 @@ class CallAnnotationsValidator:
|
||||
DiagnosticKind.ANNOTATIONS_DEF_DECL_MISMATCH)
|
||||
|
||||
# Use concatenation of definition and declaration annotations
|
||||
# for callees validation.
|
||||
# for calls validation.
|
||||
self._caller = FunctionInfo.from_node(node, refd,
|
||||
def_annotations | annotations)
|
||||
prev_call_location = self._call_location
|
||||
self._call_location = self._caller
|
||||
|
||||
if self.is_mt_disabled_code_unit():
|
||||
# Report declarations of this functions that don't have MT_DISABLED annotation
|
||||
# and are located in headers.
|
||||
if node.location.file.name == self._main_source_file:
|
||||
usr = node.get_usr()
|
||||
declarations = self._external_decls.get(usr, set())
|
||||
for file, line in declarations:
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_decl_file_line_and_refd_node(
|
||||
file, line, refd, def_annotations),
|
||||
DiagnosticKind.MISSING_MT_DISABLED_ANNOTATION)
|
||||
if declarations:
|
||||
del self._external_decls[usr]
|
||||
|
||||
self.iterate_children(node_children,
|
||||
self.dispatch_node_inside_definition)
|
||||
|
||||
@@ -622,34 +804,36 @@ class CallAnnotationsValidator:
|
||||
# Nodes not located inside definition
|
||||
|
||||
def dispatch_node(self, node: clang.cindex.Cursor):
|
||||
if node.is_definition() and node.kind in [
|
||||
if node.kind in [
|
||||
CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL,
|
||||
CursorKind.CONSTRUCTOR, CursorKind.CONVERSION_FUNCTION
|
||||
]:
|
||||
return self.process_function_definition(node)
|
||||
if node.is_definition() and node.kind in [
|
||||
CursorKind.NAMESPACE, CursorKind.STRUCT_DECL,
|
||||
CursorKind.UNION_DECL, CursorKind.CLASS_DECL
|
||||
]:
|
||||
return self.iterate_children(node.get_children(),
|
||||
self.dispatch_node)
|
||||
if node.is_definition():
|
||||
return self.process_function_definition(node)
|
||||
# else:
|
||||
return self.process_function_declaration(node)
|
||||
|
||||
return self.iterate_children(node.get_children(), self.dispatch_node)
|
||||
|
||||
def process_translation_unit(
|
||||
self, translation_unit: clang.cindex.TranslationUnit):
|
||||
self._level += 1
|
||||
kv_defines = sorted([f"{k}={v}" for k, v in self._defines.items()])
|
||||
concat_defines = '\n'.join(kv_defines)
|
||||
# List of headers already processed in a TU with specified set of defines.
|
||||
tu_processed_headers = self._processed_headers.setdefault(
|
||||
concat_defines, set())
|
||||
for child in translation_unit.cursor.get_children():
|
||||
if self._is_ignored_top_level(child):
|
||||
continue
|
||||
if self._processed_headers:
|
||||
if tu_processed_headers:
|
||||
filename = os.path.abspath(child.location.file.name)
|
||||
if filename in self._processed_headers:
|
||||
if filename in tu_processed_headers:
|
||||
continue
|
||||
self.dispatch_node(child)
|
||||
self._level -= 1
|
||||
|
||||
self._processed_headers.update([
|
||||
tu_processed_headers.update([
|
||||
os.path.abspath(str(hdr.source))
|
||||
for hdr in translation_unit.get_includes()
|
||||
])
|
||||
@@ -711,34 +895,40 @@ def get_filter_funcs(verilator_root: str):
|
||||
|
||||
|
||||
def precompile_header(compile_command: CompileCommand, tmp_dir: str) -> str:
|
||||
initial_cwd = os.getcwd()
|
||||
errors = []
|
||||
try:
|
||||
initial_cwd = os.getcwd()
|
||||
os.chdir(compile_command.directory)
|
||||
|
||||
index = Index.create()
|
||||
translation_unit = index.parse(compile_command.filename,
|
||||
compile_command.args)
|
||||
for diag in translation_unit.diagnostics:
|
||||
if diag.severity > clang.cindex.Diagnostic.Error:
|
||||
pch_file = None
|
||||
break
|
||||
else:
|
||||
if diag.severity >= clang.cindex.Diagnostic.Error:
|
||||
errors.append(str(diag))
|
||||
|
||||
if len(errors) == 0:
|
||||
pch_file = os.path.join(
|
||||
tmp_dir,
|
||||
f"{compile_command.refid:02}_{os.path.basename(compile_command.filename)}.pch"
|
||||
)
|
||||
translation_unit.save(pch_file)
|
||||
|
||||
if pch_file:
|
||||
return pch_file
|
||||
|
||||
except (TranslationUnitSaveError, TranslationUnitLoadError,
|
||||
OSError) as exception:
|
||||
print(f"%Warning: {exception}", file=sys.stderr)
|
||||
|
||||
finally:
|
||||
os.chdir(initial_cwd)
|
||||
|
||||
if pch_file:
|
||||
return pch_file
|
||||
|
||||
except (TranslationUnitSaveError, TranslationUnitLoadError, OSError):
|
||||
pass
|
||||
|
||||
print(
|
||||
f"%Warning: Precompiling failed, skipping: {compile_command.filename}")
|
||||
f"%Warning: Precompilation failed, skipping: {compile_command.filename}",
|
||||
file=sys.stderr)
|
||||
for error in errors:
|
||||
print(f" {error}", file=sys.stderr)
|
||||
return ""
|
||||
|
||||
|
||||
@@ -761,10 +951,11 @@ def run_analysis(ccl: Iterable[CompileCommand], pccl: Iterable[CompileCommand],
|
||||
is_ignored_def, is_ignored_call)
|
||||
for compile_command in ccl:
|
||||
cav.compile_and_analyze_file(compile_command.filename,
|
||||
compile_command.args + extra_args,
|
||||
extra_args + compile_command.args,
|
||||
compile_command.directory)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ParallelAnalysisProcess:
|
||||
cav: Optional[CallAnnotationsValidator] = None
|
||||
diags: set[Diagnostic] = dataclasses.field(default_factory=set)
|
||||
@@ -881,6 +1072,10 @@ class TopDownSummaryPrinter():
|
||||
name += "is pure but calls non-pure function(s)"
|
||||
elif func.reason == DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX:
|
||||
name += "is stable_tree but calls non-stable_tree or non-mtsafe"
|
||||
elif func.reason == DiagnosticKind.MISSING_MT_DISABLED_ANNOTATION:
|
||||
name += ("defined in a file marked as " +
|
||||
"VL_MT_DISABLED_CODE_UNIT has declaration(s) " +
|
||||
"without VL_MT_DISABLED annotation")
|
||||
else:
|
||||
name += "for unknown reason (please add description)"
|
||||
|
||||
@@ -947,10 +1142,15 @@ def main():
|
||||
type=int,
|
||||
default=0,
|
||||
help="Number of parallel jobs to use.")
|
||||
parser.add_argument(
|
||||
"--compile-commands-dir",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Path to directory containing compile_commands.json.")
|
||||
parser.add_argument("--cxxflags",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Flags passed to clang++.")
|
||||
help="Extra flags passed to clang++.")
|
||||
parser.add_argument(
|
||||
"--compilation-root",
|
||||
type=str,
|
||||
@@ -975,34 +1175,52 @@ def main():
|
||||
cmdline.compilation_root = cmdline.verilator_root
|
||||
|
||||
verilator_root = os.path.abspath(cmdline.verilator_root)
|
||||
compilation_root = os.path.abspath(cmdline.compilation_root)
|
||||
default_compilation_root = os.path.abspath(cmdline.compilation_root)
|
||||
|
||||
compdb: Optional[CompilationDatabase] = None
|
||||
if cmdline.compile_commands_dir:
|
||||
compdb = CompilationDatabase.fromDirectory(
|
||||
cmdline.compile_commands_dir)
|
||||
|
||||
default_cxx_flags = [
|
||||
f"-I{verilator_root}/src",
|
||||
f"-I{verilator_root}/include",
|
||||
f"-I{verilator_root}/src/obj_opt",
|
||||
"-fcoroutines-ts",
|
||||
]
|
||||
if cmdline.cxxflags is not None:
|
||||
cxxflags = shlex.split(cmdline.cxxflags)
|
||||
common_cxxflags = shlex.split(cmdline.cxxflags)
|
||||
else:
|
||||
cxxflags = default_cxx_flags
|
||||
common_cxxflags = []
|
||||
|
||||
precompile_commands_list = []
|
||||
|
||||
if cmdline.precompile:
|
||||
hdr_cxxflags = ['-xc++-header'] + cxxflags
|
||||
hdr_cxxflags = ['-xc++-header'] + common_cxxflags
|
||||
for refid, file in enumerate(cmdline.precompile):
|
||||
filename = os.path.abspath(file)
|
||||
compile_command = CompileCommand(refid, filename, hdr_cxxflags,
|
||||
compilation_root)
|
||||
default_compilation_root)
|
||||
precompile_commands_list.append(compile_command)
|
||||
|
||||
compile_commands_list = []
|
||||
for refid, file in enumerate(cmdline.file):
|
||||
filename = os.path.abspath(file)
|
||||
compile_command = CompileCommand(refid, filename, cxxflags,
|
||||
compilation_root)
|
||||
root = default_compilation_root
|
||||
cxxflags = []
|
||||
if compdb:
|
||||
entry = compdb.getCompileCommands(filename)
|
||||
entry_list = list(entry)
|
||||
# Compilation database can contain multiple entries for single file,
|
||||
# e.g. when it has been updated by appending new entries.
|
||||
# Use last entry for the file, if it exists, as it is the newest one.
|
||||
if len(entry_list) > 0:
|
||||
last_entry = entry_list[-1]
|
||||
root = last_entry.directory
|
||||
entry_args = list(last_entry.arguments)
|
||||
# First argument in compile_commands.json arguments list is
|
||||
# compiler executable name/path. CIndex (libclang) always
|
||||
# implicitly prepends executable name, so it shouldn't be passed
|
||||
# here.
|
||||
cxxflags = common_cxxflags + entry_args[1:]
|
||||
else:
|
||||
cxxflags = common_cxxflags[:]
|
||||
|
||||
compile_command = CompileCommand(refid, filename, cxxflags, root)
|
||||
compile_commands_list.append(compile_command)
|
||||
|
||||
summary_printer = TopDownSummaryPrinter()
|
||||
|
||||
@@ -316,7 +316,7 @@ def run(command):
|
||||
print("\t%s" % command)
|
||||
status = subprocess.call(command, shell=True)
|
||||
if status < 0:
|
||||
raise Exception("%Error: Command failed " + command + ", stopped")
|
||||
raise RuntimeError("%Error: Command failed " + command + ", stopped")
|
||||
|
||||
|
||||
def unlink_ok(filename):
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ def run(command):
|
||||
os.system(command)
|
||||
status = subprocess.call(command, shell=True)
|
||||
if status < 0:
|
||||
raise Exception("%Error: Command failed " + command + ", stopped")
|
||||
raise RuntimeError("%Error: Command failed " + command + ", stopped")
|
||||
|
||||
|
||||
#######################################################################
|
||||
|
||||
+19
-6
@@ -35,7 +35,6 @@ set(HEADERS
|
||||
V3Assert.h
|
||||
V3AssertPre.h
|
||||
V3Ast.h
|
||||
V3AstConstOnly.h
|
||||
V3AstInlines.h
|
||||
V3AstNodeDType.h
|
||||
V3AstNodeExpr.h
|
||||
@@ -44,9 +43,10 @@ set(HEADERS
|
||||
V3Begin.h
|
||||
V3Branch.h
|
||||
V3Broken.h
|
||||
V3CCtors.h
|
||||
V3CUse.h
|
||||
V3Case.h
|
||||
V3Cast.h
|
||||
V3CCtors.h
|
||||
V3Class.h
|
||||
V3Clean.h
|
||||
V3Clock.h
|
||||
@@ -56,7 +56,6 @@ set(HEADERS
|
||||
V3Const.h
|
||||
V3Coverage.h
|
||||
V3CoverageJoin.h
|
||||
V3CUse.h
|
||||
V3Dead.h
|
||||
V3Delayed.h
|
||||
V3Depth.h
|
||||
@@ -96,6 +95,7 @@ set(HEADERS
|
||||
V3Inline.h
|
||||
V3Inst.h
|
||||
V3InstrCount.h
|
||||
V3Interface.h
|
||||
V3LangCode.h
|
||||
V3LanguageWords.h
|
||||
V3Life.h
|
||||
@@ -104,13 +104,16 @@ set(HEADERS
|
||||
V3LinkDot.h
|
||||
V3LinkInc.h
|
||||
V3LinkJump.h
|
||||
V3LinkLevel.h
|
||||
V3LinkLValue.h
|
||||
V3LinkLevel.h
|
||||
V3LinkParse.h
|
||||
V3LinkResolve.h
|
||||
V3List.h
|
||||
V3Localize.h
|
||||
V3MemberMap.h
|
||||
V3MergeCond.h
|
||||
V3Mutex.h
|
||||
V3Name.h
|
||||
V3Name.h
|
||||
V3Number.h
|
||||
V3OptionParser.h
|
||||
@@ -126,13 +129,16 @@ set(HEADERS
|
||||
V3ParseSym.h
|
||||
V3Partition.h
|
||||
V3PartitionGraph.h
|
||||
V3PchAstMT.h
|
||||
V3PchAstNoMT.h
|
||||
V3PreLex.h
|
||||
V3Premit.h
|
||||
V3PreProc.h
|
||||
V3PreShell.h
|
||||
V3Premit.h
|
||||
V3ProtectLib.h
|
||||
V3Randomize.h
|
||||
V3Reloop.h
|
||||
V3Rtti.h
|
||||
V3Sched.h
|
||||
V3Scope.h
|
||||
V3Scoreboard.h
|
||||
@@ -148,14 +154,15 @@ set(HEADERS
|
||||
V3String.h
|
||||
V3Subst.h
|
||||
V3SymTable.h
|
||||
V3TSP.h
|
||||
V3Table.h
|
||||
V3Task.h
|
||||
V3ThreadPool.h
|
||||
V3ThreadSafety.h
|
||||
V3Timing.h
|
||||
V3Trace.h
|
||||
V3TraceDecl.h
|
||||
V3Tristate.h
|
||||
V3TSP.h
|
||||
V3Undriven.h
|
||||
V3UniqueNames.h
|
||||
V3Unknown.h
|
||||
@@ -164,6 +171,7 @@ set(HEADERS
|
||||
V3Waiver.h
|
||||
V3Width.h
|
||||
V3WidthCommit.h
|
||||
V3WidthRemove.h
|
||||
VlcBucket.h
|
||||
VlcOptions.h
|
||||
VlcPoint.h
|
||||
@@ -219,6 +227,7 @@ set(COMMON_SOURCES
|
||||
V3EmitCMain.cpp
|
||||
V3EmitCMake.cpp
|
||||
V3EmitCModel.cpp
|
||||
V3EmitCPch.cpp
|
||||
V3EmitCSyms.cpp
|
||||
V3EmitMk.cpp
|
||||
V3EmitV.cpp
|
||||
@@ -242,6 +251,7 @@ set(COMMON_SOURCES
|
||||
V3Inline.cpp
|
||||
V3Inst.cpp
|
||||
V3InstrCount.cpp
|
||||
V3Interface.cpp
|
||||
V3Life.cpp
|
||||
V3LifePost.cpp
|
||||
V3LinkCells.cpp
|
||||
@@ -295,6 +305,7 @@ set(COMMON_SOURCES
|
||||
V3VariableOrder.cpp
|
||||
V3Waiver.cpp
|
||||
V3Width.cpp
|
||||
V3WidthCommit.cpp
|
||||
V3WidthSel.cpp
|
||||
V3ParseImp.cpp
|
||||
V3ParseGrammar.cpp
|
||||
@@ -490,11 +501,13 @@ target_include_directories(${verilator}
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_compile_options(${verilator} PRIVATE /bigobj)
|
||||
target_compile_definitions(${verilator} PRIVATE
|
||||
YY_NO_UNISTD_H
|
||||
)
|
||||
target_include_directories(${verilator} PRIVATE ../platform/win32)
|
||||
target_link_libraries(${verilator} PRIVATE bcrypt psapi)
|
||||
target_link_options(${verilator} PRIVATE /STACK:10000000)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${verilator})
|
||||
|
||||
+23
-4
@@ -18,14 +18,13 @@
|
||||
#### Start of system configuration section. ####
|
||||
|
||||
srcdir = @srcdir@
|
||||
PYTHON3 = @PYTHON3@
|
||||
EXEEXT = @EXEEXT@
|
||||
PYTHON3 = @PYTHON3@
|
||||
# VPATH only does sources; fix install_test's not creating ../bin
|
||||
vpath %.h @srcdir@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
|
||||
default: dbg opt
|
||||
debug: dbg
|
||||
optimize: opt
|
||||
@@ -36,6 +35,26 @@ endif
|
||||
|
||||
UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
|
||||
|
||||
ifeq (,$(wildcard obj_dbg/bear.o))
|
||||
ifneq (, $(shell which bear 2>/dev/null))
|
||||
BEAR := $(shell which bear)
|
||||
ifeq (, $(shell $(BEAR) --output obj_dbg/comptest.json -- true))
|
||||
$(shell which bear 2>/dev/null >obj_dbg/bear.o)
|
||||
else
|
||||
# unsupported version
|
||||
BEAR :=
|
||||
endif
|
||||
endif
|
||||
else
|
||||
BEAR := $(shell cat obj_dbg/bear.o)
|
||||
endif
|
||||
|
||||
ifneq ($(BEAR),)
|
||||
BEAR_OBJ_OPT := $(BEAR) --append --output obj_dbg/compile_commands.json --
|
||||
else
|
||||
BEAR_OBJ_OPT :=
|
||||
endif
|
||||
|
||||
#*********************************************************************
|
||||
|
||||
obj_opt:
|
||||
@@ -62,8 +81,8 @@ endif
|
||||
|
||||
dbg: ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
|
||||
../bin/verilator_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
$(BEAR_OBJ_OPT) $(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
$(BEAR_OBJ_OPT) $(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
|
||||
../bin/verilator_coverage_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
|
||||
|
||||
+85
-49
@@ -67,6 +67,11 @@ CFG_CXXFLAGS_PARSER = @CFG_CXXFLAGS_PARSER@
|
||||
CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
|
||||
CFG_LDFLAGS_SRC = @CFG_LDFLAGS_SRC@
|
||||
CFG_LIBS = @CFG_LIBS@
|
||||
CFG_CXXFLAGS_PCH = -x c++-header
|
||||
# Compiler option to put in front of filename to read precompiled header
|
||||
CFG_CXXFLAGS_PCH_I = @CFG_CXXFLAGS_PCH_I@
|
||||
# Compiler's filename prefix for precompiled headers, .gch if clang, empty if GCC
|
||||
CFG_GCH_IF_CLANG = @CFG_GCH_IF_CLANG@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
@@ -129,6 +134,11 @@ BISONPRE = $(srcdir)/bisonpre
|
||||
FLEXFIX = $(srcdir)/flexfix
|
||||
VLCOVGEN = $(srcdir)/vlcovgen
|
||||
|
||||
######################################################################
|
||||
# CCACHE flags (via environment as no command line option available)
|
||||
CCACHE_SLOPPINESS ?= pch_defines,time_macros
|
||||
export CCACHE_SLOPPINESS
|
||||
|
||||
######################################################################
|
||||
#### Top level
|
||||
|
||||
@@ -151,16 +161,55 @@ maintainer-copy::
|
||||
#### Top executable
|
||||
|
||||
RAW_OBJS = \
|
||||
V3Const__gen.o \
|
||||
V3Error.o \
|
||||
V3FileLine.o \
|
||||
V3Graph.o \
|
||||
V3GraphAcyc.o \
|
||||
V3GraphAlg.o \
|
||||
V3GraphPathChecker.o \
|
||||
V3GraphTest.o \
|
||||
V3Hash.o \
|
||||
V3OptionParser.o \
|
||||
V3Os.o \
|
||||
V3ParseGrammar.o \
|
||||
V3ParseImp.o \
|
||||
V3ParseLex.o \
|
||||
V3PreProc.o \
|
||||
V3PreShell.o \
|
||||
V3String.o \
|
||||
V3ThreadPool.o \
|
||||
V3Waiver.o \
|
||||
Verilator.o \
|
||||
|
||||
RAW_OBJS_PCH_ASTMT = \
|
||||
V3Ast.o \
|
||||
V3AstNodes.o \
|
||||
V3Broken.o \
|
||||
V3Config.o \
|
||||
V3EmitCBase.o \
|
||||
V3EmitCConstPool.o \
|
||||
V3EmitCFunc.o \
|
||||
V3EmitCHeaders.o \
|
||||
V3EmitCImp.o \
|
||||
V3EmitCInlines.o \
|
||||
V3EmitCPch.o \
|
||||
V3EmitV.o \
|
||||
V3File.o \
|
||||
V3Global.o \
|
||||
V3Hasher.o \
|
||||
V3Number.o \
|
||||
V3Options.o \
|
||||
V3Stats.o \
|
||||
V3StatsReport.o \
|
||||
|
||||
RAW_OBJS_PCH_ASTNOMT = \
|
||||
V3Active.o \
|
||||
V3ActiveTop.o \
|
||||
V3Assert.o \
|
||||
V3AssertPre.o \
|
||||
V3Ast.o \
|
||||
V3AstNodes.o \
|
||||
V3Begin.o \
|
||||
V3Branch.o \
|
||||
V3Broken.o \
|
||||
V3CCtors.o \
|
||||
V3CUse.o \
|
||||
V3Case.o \
|
||||
@@ -170,8 +219,6 @@ RAW_OBJS = \
|
||||
V3Clock.o \
|
||||
V3Combine.o \
|
||||
V3Common.o \
|
||||
V3Config.o \
|
||||
V3Const__gen.o \
|
||||
V3Coverage.o \
|
||||
V3CoverageJoin.o \
|
||||
V3Dead.o \
|
||||
@@ -187,38 +234,21 @@ RAW_OBJS = \
|
||||
V3DfgPasses.o \
|
||||
V3DfgPeephole.o \
|
||||
V3DupFinder.o \
|
||||
V3EmitCBase.o \
|
||||
V3EmitCConstPool.o \
|
||||
V3EmitCFunc.o \
|
||||
V3EmitCHeaders.o \
|
||||
V3EmitCImp.o \
|
||||
V3EmitCInlines.o \
|
||||
V3EmitCMain.o \
|
||||
V3EmitCMake.o \
|
||||
V3EmitCModel.o \
|
||||
V3EmitCSyms.o \
|
||||
V3EmitMk.o \
|
||||
V3EmitV.o \
|
||||
V3EmitXml.o \
|
||||
V3Error.o \
|
||||
V3Expand.o \
|
||||
V3File.o \
|
||||
V3FileLine.o \
|
||||
V3Force.o \
|
||||
V3Fork.o \
|
||||
V3Gate.o \
|
||||
V3Global.o \
|
||||
V3Graph.o \
|
||||
V3GraphAcyc.o \
|
||||
V3GraphAlg.o \
|
||||
V3GraphPathChecker.o \
|
||||
V3GraphTest.o \
|
||||
V3Hash.o \
|
||||
V3Hasher.o \
|
||||
V3HierBlock.o \
|
||||
V3Inline.o \
|
||||
V3Inst.o \
|
||||
V3InstrCount.o \
|
||||
V3Interface.o \
|
||||
V3Life.o \
|
||||
V3LifePost.o \
|
||||
V3LinkCells.o \
|
||||
@@ -232,18 +262,9 @@ RAW_OBJS = \
|
||||
V3Localize.o \
|
||||
V3MergeCond.o \
|
||||
V3Name.o \
|
||||
V3Number.o \
|
||||
V3OptionParser.o \
|
||||
V3Options.o \
|
||||
V3Order.o \
|
||||
V3Os.o \
|
||||
V3Param.o \
|
||||
V3ParseGrammar.o \
|
||||
V3ParseImp.o \
|
||||
V3ParseLex.o \
|
||||
V3Partition.o \
|
||||
V3PreProc.o \
|
||||
V3PreShell.o \
|
||||
V3Premit.o \
|
||||
V3ProtectLib.o \
|
||||
V3Randomize.o \
|
||||
@@ -259,14 +280,10 @@ RAW_OBJS = \
|
||||
V3Split.o \
|
||||
V3SplitAs.o \
|
||||
V3SplitVar.o \
|
||||
V3Stats.o \
|
||||
V3StatsReport.o \
|
||||
V3String.o \
|
||||
V3Subst.o \
|
||||
V3TSP.o \
|
||||
V3Table.o \
|
||||
V3Task.o \
|
||||
V3ThreadPool.o \
|
||||
V3Timing.o \
|
||||
V3Trace.o \
|
||||
V3TraceDecl.o \
|
||||
@@ -275,8 +292,8 @@ RAW_OBJS = \
|
||||
V3Unknown.o \
|
||||
V3Unroll.o \
|
||||
V3VariableOrder.o \
|
||||
V3Waiver.o \
|
||||
V3Width.o \
|
||||
V3WidthCommit.o \
|
||||
V3WidthSel.o \
|
||||
|
||||
# verilator_coverage
|
||||
@@ -284,20 +301,21 @@ VLCOV_OBJS = \
|
||||
VlcMain.o \
|
||||
|
||||
NON_STANDALONE_HEADERS = \
|
||||
V3AstInlines.h \
|
||||
V3AstNodeDType.h \
|
||||
V3AstNodeExpr.h \
|
||||
V3AstNodeOther.h \
|
||||
V3DfgVertices.h \
|
||||
V3WidthCommit.h \
|
||||
V3AstInlines.h \
|
||||
V3AstNodeDType.h \
|
||||
V3AstNodeExpr.h \
|
||||
V3AstNodeOther.h \
|
||||
V3DfgVertices.h \
|
||||
V3ThreadPool.h \
|
||||
V3WidthRemove.h \
|
||||
|
||||
AST_DEFS := \
|
||||
V3AstNodeDType.h \
|
||||
V3AstNodeExpr.h \
|
||||
V3AstNodeOther.h \
|
||||
V3AstNodeDType.h \
|
||||
V3AstNodeExpr.h \
|
||||
V3AstNodeOther.h \
|
||||
|
||||
DFG_DEFS := \
|
||||
V3DfgVertices.h
|
||||
V3DfgVertices.h
|
||||
|
||||
#### astgen common flags
|
||||
|
||||
@@ -309,7 +327,7 @@ ASTGENFLAGS += $(foreach f,$(DFG_DEFS),--dfgdef $f)
|
||||
|
||||
ifeq ($(VL_VLCOV),)
|
||||
PREDEP_H = V3Ast__gen_forward_class_decls.h
|
||||
OBJS += $(RAW_OBJS)
|
||||
OBJS += $(RAW_OBJS) $(RAW_OBJS_PCH_ASTMT) $(RAW_OBJS_PCH_ASTNOMT)
|
||||
else
|
||||
PREDEP_H =
|
||||
OBJS += $(VLCOV_OBJS)
|
||||
@@ -329,6 +347,8 @@ V3Number_test: V3Number_test.o
|
||||
|
||||
.SECONDARY:
|
||||
|
||||
%.gch: %
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH} $< -o $@
|
||||
%.o: %.cpp
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $< -o $@
|
||||
%.o: %.c
|
||||
@@ -346,6 +366,22 @@ V3ParseImp.o: V3ParseImp.cpp V3ParseBison.c
|
||||
V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $< -o $@
|
||||
|
||||
define CXX_ASTMT_template
|
||||
$(1): $(basename $(1)).cpp V3PchAstMT.h.gch
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstMT.h${CFG_GCH_IF_CLANG} -c $(srcdir)/$(basename $(1)).cpp -o $(1)
|
||||
|
||||
endef
|
||||
|
||||
$(foreach obj,$(RAW_OBJS_PCH_ASTMT),$(eval $(call CXX_ASTMT_template,$(obj))))
|
||||
|
||||
define CXX_ASTNOMT_template
|
||||
$(1): $(basename $(1)).cpp V3PchAstNoMT.h.gch
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstNoMT.h${CFG_GCH_IF_CLANG} -c $(srcdir)/$(basename $(1)).cpp -o $(1)
|
||||
|
||||
endef
|
||||
|
||||
$(foreach obj,$(RAW_OBJS_PCH_ASTNOMT),$(eval $(call CXX_ASTNOMT_template,$(obj))))
|
||||
|
||||
#### Generated files
|
||||
|
||||
# Target rule called before parallel build to make generated files
|
||||
@@ -388,7 +424,7 @@ HEADER_CC_H := $(filter-out $(NON_STANDALONE_HEADERS), $(notdir $(wildcard $(src
|
||||
header_cc: $(addsuffix __header_cc.o, $(basename $(HEADER_CC_H)))
|
||||
|
||||
%__header_cc.cpp: %.h
|
||||
$(PYTHON3) $(srcdir)/../bin/verilator_includer $^ > $@
|
||||
$(PYTHON3) $(srcdir)/../bin/verilator_includer -DVL_MT_DISABLED_CODE_UNIT=1 $^ > $@
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
|
||||
+2
-4
@@ -26,14 +26,11 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Active.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -47,6 +44,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
// Extend V3GraphVertex class for use in latch detection graph
|
||||
|
||||
class LatchDetectGraphVertex final : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(LatchDetectGraphVertex, V3GraphVertex)
|
||||
public:
|
||||
enum VertexType : uint8_t { VT_BLOCK, VT_BRANCH, VT_OUTPUT };
|
||||
|
||||
|
||||
+3
-1
@@ -20,13 +20,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Active final {
|
||||
public:
|
||||
static void activeAll(AstNetlist* nodep);
|
||||
static void activeAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+1
-4
@@ -23,14 +23,11 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3ActiveTop.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3SenTree.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
+3
-1
@@ -20,13 +20,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3ActiveTop final {
|
||||
public:
|
||||
static void activeTopAll(AstNetlist* nodep);
|
||||
static void activeTopAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+11
-15
@@ -14,14 +14,10 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Assert.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
@@ -158,7 +154,7 @@ private:
|
||||
if (m_procedurep) {
|
||||
// To support this need queue of asserts to activate
|
||||
nodep->v3error("Unsupported: Procedural concurrent assertion with"
|
||||
" clocking event inside always (IEEE 1800-2917 16.14.6)");
|
||||
" clocking event inside always (IEEE 1800-2017 16.14.6)");
|
||||
}
|
||||
}
|
||||
//
|
||||
@@ -243,7 +239,7 @@ private:
|
||||
}
|
||||
|
||||
// Build a bitmask of the true predicates
|
||||
AstNodeExpr* const predp = ifp->condp()->cloneTree(false);
|
||||
AstNodeExpr* const predp = ifp->condp()->cloneTreePure(false);
|
||||
if (propp) {
|
||||
propp = new AstConcat{nodep->fileline(), predp, propp};
|
||||
} else {
|
||||
@@ -256,7 +252,7 @@ private:
|
||||
ifp = nextifp;
|
||||
} while (ifp);
|
||||
|
||||
AstNode* const newifp = nodep->cloneTree(false);
|
||||
AstIf* const newifp = nodep->cloneTree(false);
|
||||
const bool allow_none = nodep->unique0Pragma();
|
||||
|
||||
// Empty case means no property
|
||||
@@ -312,17 +308,17 @@ private:
|
||||
icondp = VN_AS(icondp->nextp(), NodeExpr)) {
|
||||
AstNodeExpr* onep;
|
||||
if (AstInsideRange* const rcondp = VN_CAST(icondp, InsideRange)) {
|
||||
onep = rcondp->newAndFromInside(nodep->exprp(),
|
||||
rcondp->lhsp()->cloneTree(true),
|
||||
rcondp->rhsp()->cloneTree(true));
|
||||
onep = rcondp->newAndFromInside(
|
||||
nodep->exprp(), rcondp->lhsp()->cloneTreePure(true),
|
||||
rcondp->rhsp()->cloneTreePure(true));
|
||||
} else if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
|
||||
onep = AstEqWild::newTyped(itemp->fileline(),
|
||||
nodep->exprp()->cloneTree(false),
|
||||
icondp->cloneTree(false));
|
||||
nodep->exprp()->cloneTreePure(false),
|
||||
icondp->cloneTreePure(false));
|
||||
} else {
|
||||
onep = AstEq::newTyped(icondp->fileline(),
|
||||
nodep->exprp()->cloneTree(false),
|
||||
icondp->cloneTree(false));
|
||||
nodep->exprp()->cloneTreePure(false),
|
||||
icondp->cloneTreePure(false));
|
||||
}
|
||||
if (propp) {
|
||||
propp = new AstConcat{icondp->fileline(), onep, propp};
|
||||
|
||||
+2
-1
@@ -21,12 +21,13 @@
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Assert final {
|
||||
public:
|
||||
static void assertAll(AstNetlist* nodep);
|
||||
static void assertAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+10
-13
@@ -15,18 +15,15 @@
|
||||
//*************************************************************************
|
||||
// Pre steps:
|
||||
// Attach clocks to each assertion
|
||||
// Substitute property references by property body (IEEE Std 1800-2012, section 16.12.1).
|
||||
// Substitute property references by property body (IEEE 1800-2012 16.12.1).
|
||||
// Transform clocking blocks into imperative logic
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3AssertPre.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Task.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
@@ -177,7 +174,7 @@ private:
|
||||
if (nodep->direction() == VDirection::OUTPUT) {
|
||||
AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ};
|
||||
skewedRefp->user1(true);
|
||||
AstAssign* const assignp = new AstAssign{flp, exprp->cloneTree(false), skewedRefp};
|
||||
AstAssign* const assignp = new AstAssign{flp, exprp->cloneTreePure(false), skewedRefp};
|
||||
if (skewp->isZero()) {
|
||||
// Drive the var in Re-NBA (IEEE 1800-2017 14.16)
|
||||
m_clockingp->addNextHere(new AstAlwaysReactive{
|
||||
@@ -208,12 +205,12 @@ private:
|
||||
refp->user1(true);
|
||||
if (skewp->num().is1Step()) {
|
||||
// Assign the sampled expression to the clockvar (IEEE 1800-2017 14.13)
|
||||
AstSampled* const sampledp = new AstSampled{flp, exprp->cloneTree(false)};
|
||||
AstSampled* const sampledp = new AstSampled{flp, exprp->cloneTreePure(false)};
|
||||
sampledp->dtypeFrom(exprp);
|
||||
m_clockingp->addNextHere(new AstAssignW{flp, refp, sampledp});
|
||||
} else if (skewp->isZero()) {
|
||||
// #0 means the var has to be sampled in Observed (IEEE 1800-2017 14.13)
|
||||
AstAssign* const assignp = new AstAssign{flp, refp, exprp->cloneTree(false)};
|
||||
AstAssign* const assignp = new AstAssign{flp, refp, exprp->cloneTreePure(false)};
|
||||
m_clockingp->addNextHere(new AstAlwaysObserved{
|
||||
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, assignp});
|
||||
} else {
|
||||
@@ -230,7 +227,7 @@ private:
|
||||
AstCMethodHard* const pushp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, queueVarp, VAccess::WRITE}, "push",
|
||||
new AstTime{nodep->fileline(), m_modp->timeunit()}};
|
||||
pushp->addPinsp(exprp->cloneTree(false));
|
||||
pushp->addPinsp(exprp->cloneTreePure(false));
|
||||
pushp->dtypeSetVoid();
|
||||
m_clockingp->addNextHere(
|
||||
new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, pushp->makeStmt()});
|
||||
@@ -372,7 +369,7 @@ private:
|
||||
if (sentreep) sentreep->unlinkFrBack();
|
||||
AstNodeExpr* const past = new AstPast{fl, exprp, nullptr};
|
||||
past->dtypeFrom(exprp);
|
||||
exprp = new AstAnd{fl, past, new AstNot{fl, exprp->cloneTree(false)}};
|
||||
exprp = new AstAnd{fl, past, new AstNot{fl, exprp->cloneTreePure(false)}};
|
||||
exprp->dtypeSetBit();
|
||||
nodep->replaceWith(exprp);
|
||||
nodep->sentreep(newSenTree(nodep, sentreep));
|
||||
@@ -393,7 +390,7 @@ private:
|
||||
if (sentreep) sentreep->unlinkFrBack();
|
||||
AstNodeExpr* const past = new AstPast{fl, exprp, nullptr};
|
||||
past->dtypeFrom(exprp);
|
||||
exprp = new AstAnd{fl, new AstNot{fl, past}, exprp->cloneTree(false)};
|
||||
exprp = new AstAnd{fl, new AstNot{fl, past}, exprp->cloneTreePure(false)};
|
||||
exprp->dtypeSetBit();
|
||||
nodep->replaceWith(exprp);
|
||||
nodep->sentreep(newSenTree(nodep, sentreep));
|
||||
@@ -408,7 +405,7 @@ private:
|
||||
if (sentreep) sentreep->unlinkFrBack();
|
||||
AstNodeExpr* const past = new AstPast{fl, exprp, nullptr};
|
||||
past->dtypeFrom(exprp);
|
||||
exprp = new AstEq{fl, past, exprp->cloneTree(false)};
|
||||
exprp = new AstEq{fl, past, exprp->cloneTreePure(false)};
|
||||
exprp->dtypeSetBit();
|
||||
nodep->replaceWith(exprp);
|
||||
nodep->sentreep(newSenTree(nodep, sentreep));
|
||||
@@ -442,7 +439,7 @@ private:
|
||||
// Block is the new expression to evaluate
|
||||
AstNodeExpr* blockp = VN_AS(nodep->propp()->unlinkFrBack(), NodeExpr);
|
||||
if (AstNodeExpr* const disablep = nodep->disablep()) {
|
||||
m_disablep = disablep->cloneTree(false);
|
||||
m_disablep = disablep->cloneTreePure(false);
|
||||
if (VN_IS(nodep->backp(), Cover)) {
|
||||
blockp = new AstAnd{disablep->fileline(),
|
||||
new AstNot{disablep->fileline(), disablep->unlinkFrBack()},
|
||||
|
||||
+3
-1
@@ -20,13 +20,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3AssertPre final {
|
||||
public:
|
||||
static void assertPreAll(AstNetlist* nodep);
|
||||
static void assertPreAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+197
-50
@@ -14,16 +14,11 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3PchAstMT.h"
|
||||
|
||||
#include "V3Broken.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
@@ -33,6 +28,8 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
//======================================================================
|
||||
// Statics
|
||||
|
||||
uint64_t VIsCached::s_cachedCntGbl = 1;
|
||||
|
||||
uint64_t AstNode::s_editCntLast = 0;
|
||||
uint64_t AstNode::s_editCntGbl = 0; // Hot cache line
|
||||
|
||||
@@ -44,23 +41,37 @@ uint32_t VNUser1InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent
|
||||
uint32_t VNUser2InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent
|
||||
uint32_t VNUser3InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent
|
||||
uint32_t VNUser4InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent
|
||||
uint32_t VNUser5InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent
|
||||
|
||||
bool VNUser1InUse::s_userBusy = false;
|
||||
bool VNUser2InUse::s_userBusy = false;
|
||||
bool VNUser3InUse::s_userBusy = false;
|
||||
bool VNUser4InUse::s_userBusy = false;
|
||||
bool VNUser5InUse::s_userBusy = false;
|
||||
|
||||
int AstNodeDType::s_uniqueNum = 0;
|
||||
|
||||
//######################################################################
|
||||
// V3AstType
|
||||
// VNType
|
||||
|
||||
const VNTypeInfo VNType::typeInfoTable[] = {
|
||||
#include "V3Ast__gen_type_info.h" // From ./astgen
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, VNType rhs);
|
||||
|
||||
//######################################################################
|
||||
// Creators
|
||||
// VSelfPointerText
|
||||
|
||||
const std::shared_ptr<const string> VSelfPointerText::s_emptyp = std::make_shared<string>("");
|
||||
const std::shared_ptr<const string> VSelfPointerText::s_thisp = std::make_shared<string>("this");
|
||||
|
||||
string VSelfPointerText::protect(bool useSelfForThis, bool protect) const {
|
||||
const string& sp
|
||||
= useSelfForThis ? VString::replaceWord(asString(), "this", "vlSelf") : asString();
|
||||
return VIdProtect::protectWordsIf(sp, protect);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// AstNode
|
||||
|
||||
AstNode::AstNode(VNType t, FileLine* fl)
|
||||
: m_type{t}
|
||||
@@ -356,8 +367,8 @@ AstNode* AstNode::addNext<AstNode, AstNode>(AstNode* nodep, AstNode* newp) {
|
||||
|
||||
void AstNode::addNextHere(AstNode* newp) {
|
||||
// Add to m_nextp on exact node passed, not at the end.
|
||||
// This could be at head, tail, or both (single)
|
||||
// New could be head of single node, or list
|
||||
// 'this' could be at head, tail, or both (single)
|
||||
// 'newp' could be head of single node, or list
|
||||
UASSERT(newp, "Null item passed to addNext");
|
||||
UASSERT_OBJ(!newp->backp(), newp, "New node (back) already assigned?");
|
||||
debugTreeChange(this, "-addHereThs: ", __LINE__, false);
|
||||
@@ -783,26 +794,34 @@ void AstNode::swapWith(AstNode* bp) {
|
||||
//======================================================================
|
||||
// Clone
|
||||
|
||||
AstNode* AstNode::cloneTreeIter() {
|
||||
AstNode* AstNode::cloneTreeIter(bool needPure) {
|
||||
// private: Clone single node and children
|
||||
if (VL_UNLIKELY(needPure && !isPure())) {
|
||||
this->v3warn(SIDEEFFECT,
|
||||
"Expression side effect may be mishandled\n"
|
||||
<< this->warnMore()
|
||||
<< "... Suggest use a temporary variable in place of this expression");
|
||||
// this->v3fatalSrc("cloneTreePure debug backtrace"); // Comment in to debug where caused
|
||||
// it
|
||||
}
|
||||
AstNode* const newp = this->clone();
|
||||
if (this->m_op1p) newp->op1p(this->m_op1p->cloneTreeIterList());
|
||||
if (this->m_op2p) newp->op2p(this->m_op2p->cloneTreeIterList());
|
||||
if (this->m_op3p) newp->op3p(this->m_op3p->cloneTreeIterList());
|
||||
if (this->m_op4p) newp->op4p(this->m_op4p->cloneTreeIterList());
|
||||
if (this->m_op1p) newp->op1p(this->m_op1p->cloneTreeIterList(needPure));
|
||||
if (this->m_op2p) newp->op2p(this->m_op2p->cloneTreeIterList(needPure));
|
||||
if (this->m_op3p) newp->op3p(this->m_op3p->cloneTreeIterList(needPure));
|
||||
if (this->m_op4p) newp->op4p(this->m_op4p->cloneTreeIterList(needPure));
|
||||
newp->m_iterpp = nullptr;
|
||||
newp->clonep(this); // Save pointers to/from both to simplify relinking.
|
||||
this->clonep(newp); // Save pointers to/from both to simplify relinking.
|
||||
return newp;
|
||||
}
|
||||
|
||||
AstNode* AstNode::cloneTreeIterList() {
|
||||
AstNode* AstNode::cloneTreeIterList(bool needPure) {
|
||||
// private: Clone list of nodes, set m_headtailp
|
||||
AstNode* newheadp = nullptr;
|
||||
AstNode* newtailp = nullptr;
|
||||
// Audited to make sure this is never nullptr
|
||||
for (AstNode* oldp = this; oldp; oldp = oldp->m_nextp) {
|
||||
AstNode* const newp = oldp->cloneTreeIter();
|
||||
AstNode* const newp = oldp->cloneTreeIter(needPure);
|
||||
newp->m_headtailp = nullptr;
|
||||
newp->m_backp = newtailp;
|
||||
if (newtailp) newtailp->m_nextp = newp;
|
||||
@@ -814,14 +833,14 @@ AstNode* AstNode::cloneTreeIterList() {
|
||||
return newheadp;
|
||||
}
|
||||
|
||||
AstNode* AstNode::cloneTree(bool cloneNextLink) {
|
||||
AstNode* AstNode::cloneTree(bool cloneNextLink, bool needPure) {
|
||||
debugTreeChange(this, "-cloneThs: ", __LINE__, cloneNextLink);
|
||||
cloneClearTree();
|
||||
AstNode* newp;
|
||||
if (cloneNextLink && this->m_nextp) {
|
||||
newp = cloneTreeIterList();
|
||||
newp = cloneTreeIterList(needPure);
|
||||
} else {
|
||||
newp = cloneTreeIter();
|
||||
newp = cloneTreeIter(needPure);
|
||||
newp->m_nextp = nullptr;
|
||||
newp->m_headtailp = newp;
|
||||
}
|
||||
@@ -1098,9 +1117,57 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
|
||||
void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE {
|
||||
// private: Check a tree and children
|
||||
UASSERT_OBJ(prevBackp == this->backp(), this, "Back node inconsistent");
|
||||
switch (this->type()) {
|
||||
#include "V3Ast__gen_op_checks.h"
|
||||
default: VL_UNREACHABLE; // LCOV_EXCL_LINE
|
||||
const VNTypeInfo& typeInfo = *type().typeInfo();
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
AstNode* nodep = nullptr;
|
||||
switch (i) {
|
||||
case 1: nodep = op1p(); break;
|
||||
case 2: nodep = op2p(); break;
|
||||
case 3: nodep = op3p(); break;
|
||||
case 4: nodep = op4p(); break;
|
||||
default: this->v3fatalSrc("Bad case"); break;
|
||||
}
|
||||
const char* opName = typeInfo.m_opNamep[i - 1];
|
||||
switch (typeInfo.m_opType[i - 1]) {
|
||||
case VNTypeInfo::OP_UNUSED:
|
||||
UASSERT_OBJ(!nodep, this, typeInfo.m_namep << " must not use " << opName << "()");
|
||||
break;
|
||||
case VNTypeInfo::OP_USED:
|
||||
UASSERT_OBJ(nodep, this,
|
||||
typeInfo.m_namep << " must have non nullptr " << opName << "()");
|
||||
UASSERT_OBJ(!nodep->nextp(), this,
|
||||
typeInfo.m_namep << "::" << opName
|
||||
<< "() cannot have a non nullptr nextp()");
|
||||
nodep->checkTreeIter(this);
|
||||
break;
|
||||
case VNTypeInfo::OP_LIST:
|
||||
if (const AstNode* const headp = nodep) {
|
||||
const AstNode* backp = this;
|
||||
const AstNode* tailp = headp;
|
||||
const AstNode* opp = headp;
|
||||
do {
|
||||
opp->checkTreeIter(backp);
|
||||
UASSERT_OBJ(opp == headp || !opp->nextp() || !opp->m_headtailp, opp,
|
||||
"Headtailp should be null in middle of lists");
|
||||
backp = tailp = opp;
|
||||
opp = opp->nextp();
|
||||
} while (opp);
|
||||
UASSERT_OBJ(headp->m_headtailp == tailp, headp,
|
||||
"Tail in headtailp is inconsistent");
|
||||
UASSERT_OBJ(tailp->m_headtailp == headp, tailp,
|
||||
"Head in headtailp is inconsistent");
|
||||
}
|
||||
break;
|
||||
case VNTypeInfo::OP_OPTIONAL:
|
||||
if (nodep) {
|
||||
UASSERT_OBJ(!nodep->nextp(), this,
|
||||
typeInfo.m_namep << "::" << opName
|
||||
<< "() cannot have a non-nullptr nextp()");
|
||||
nodep->checkTreeIter(this);
|
||||
}
|
||||
break;
|
||||
default: this->v3fatalSrc("Bad case"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1166,7 +1233,6 @@ void AstNode::dumpPtrs(std::ostream& os) const {
|
||||
if (user2p()) os << " user2p=" << cvtToHex(user2p());
|
||||
if (user3p()) os << " user3p=" << cvtToHex(user3p());
|
||||
if (user4p()) os << " user4p=" << cvtToHex(user4p());
|
||||
if (user5p()) os << " user5p=" << cvtToHex(user5p());
|
||||
if (m_iterpp) {
|
||||
os << " iterpp=" << cvtToHex(m_iterpp);
|
||||
// This may cause address sanitizer failures as iterpp can be stale
|
||||
@@ -1279,49 +1345,29 @@ void AstNode::dumpTreeDotFile(const string& filename, bool append, bool doDump)
|
||||
}
|
||||
}
|
||||
|
||||
bool AstNode::isTreePureRecurse() const {
|
||||
// Should memoize this if call commonly
|
||||
if (!this->isPure()) return false;
|
||||
if (this->op1p() && !this->op1p()->isTreePureRecurse()) return false;
|
||||
if (this->op2p() && !this->op2p()->isTreePureRecurse()) return false;
|
||||
if (this->op3p() && !this->op3p()->isTreePureRecurse()) return false;
|
||||
if (this->op4p() && !this->op4p()->isTreePureRecurse()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
string AstNode::instanceStr() const {
|
||||
// Max iterations before giving up on location search,
|
||||
// in case we have some circular reference bug.
|
||||
constexpr unsigned maxIterations = 10000;
|
||||
unsigned iterCount = 0;
|
||||
|
||||
for (const AstNode* backp = this; backp; backp = backp->backp(), ++iterCount) {
|
||||
if (VL_UNCOVERABLE(iterCount >= maxIterations)) return ""; // LCOV_EXCL_LINE
|
||||
|
||||
// Prefer the enclosing scope, if there is one. This is always under the enclosing module,
|
||||
// so just pick it up when encountered
|
||||
if (const AstScope* const scopep = VN_CAST(backp, Scope)) {
|
||||
return scopep->isTop() ? "" : "... In instance " + scopep->prettyName();
|
||||
return scopep->isTop() ? "" : "... note: In instance " + scopep->prettyNameQ();
|
||||
}
|
||||
|
||||
// If scopes don't exist, report an example instance of the enclosing module
|
||||
if (const AstModule* const modp = VN_CAST(backp, Module)) {
|
||||
const string instanceName = modp->someInstanceName();
|
||||
return instanceName.empty() ? "" : "... In instance " + instanceName;
|
||||
return instanceName.empty() ? "" : "... note: In instance '" + instanceName + "'";
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
void AstNode::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
if (!m_fileline) {
|
||||
V3Error::s().v3errorEnd(str, instanceStr());
|
||||
V3Error::v3errorEnd(str, instanceStr());
|
||||
} else {
|
||||
std::ostringstream nsstr;
|
||||
nsstr << str.str();
|
||||
@@ -1338,6 +1384,11 @@ void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s()
|
||||
nsstr, m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr());
|
||||
}
|
||||
}
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Data type conversion
|
||||
@@ -1410,6 +1461,102 @@ AstNodeDType* AstNode::findStreamDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findStreamDType(fileline());
|
||||
}
|
||||
|
||||
static const AstNodeDType* computeCastableBase(const AstNodeDType* nodep) {
|
||||
while (true) {
|
||||
if (const AstPackArrayDType* const packp = VN_CAST(nodep, PackArrayDType)) {
|
||||
nodep = packp->subDTypep();
|
||||
continue;
|
||||
} else if (const AstNodeDType* const refp = nodep->skipRefToEnump()) {
|
||||
if (refp != nodep) {
|
||||
nodep = refp;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return nodep;
|
||||
}
|
||||
}
|
||||
|
||||
static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDType* fromDtp,
|
||||
const AstNode* fromConstp) {
|
||||
const VCastable castable = VCastable::UNSUPPORTED;
|
||||
toDtp = toDtp->skipRefToEnump();
|
||||
fromDtp = fromDtp->skipRefToEnump();
|
||||
if (toDtp == fromDtp) return VCastable::SAMEISH;
|
||||
if (toDtp->similarDType(fromDtp)) return VCastable::SAMEISH;
|
||||
// UNSUP unpacked struct/unions (treated like BasicDType)
|
||||
const AstNodeDType* fromBaseDtp = computeCastableBase(fromDtp);
|
||||
|
||||
const bool fromNumericable = VN_IS(fromBaseDtp, BasicDType) || VN_IS(fromBaseDtp, EnumDType)
|
||||
|| VN_IS(fromBaseDtp, StreamDType)
|
||||
|| VN_IS(fromBaseDtp, NodeUOrStructDType);
|
||||
|
||||
const AstNodeDType* toBaseDtp = computeCastableBase(toDtp);
|
||||
const bool toNumericable
|
||||
= VN_IS(toBaseDtp, BasicDType) || VN_IS(toBaseDtp, NodeUOrStructDType);
|
||||
|
||||
if (toBaseDtp == fromBaseDtp) {
|
||||
return VCastable::COMPATIBLE;
|
||||
} else if (toNumericable) {
|
||||
if (fromNumericable) return VCastable::COMPATIBLE;
|
||||
} else if (VN_IS(toDtp, EnumDType)) {
|
||||
if (VN_IS(fromBaseDtp, EnumDType) && toDtp->sameTree(fromDtp))
|
||||
return VCastable::ENUM_IMPLICIT;
|
||||
if (fromNumericable) return VCastable::ENUM_EXPLICIT;
|
||||
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) {
|
||||
if (VN_IS(fromConstp, Const) && VN_AS(fromConstp, Const)->num().isNull())
|
||||
return VCastable::COMPATIBLE;
|
||||
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) {
|
||||
const auto toClassp = VN_AS(toDtp, ClassRefDType)->classp();
|
||||
const auto 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;
|
||||
}
|
||||
}
|
||||
return castable;
|
||||
}
|
||||
|
||||
VCastable AstNode::computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp,
|
||||
const AstNode* fromConstp) {
|
||||
const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp);
|
||||
UINFO(9, " castable=" << castable << " for " << toDtp << endl);
|
||||
UINFO(9, " =?= " << fromDtp << endl);
|
||||
UINFO(9, " const= " << fromConstp << endl);
|
||||
return castable;
|
||||
}
|
||||
|
||||
AstNodeDType* AstNode::getCommonClassTypep(AstNode* nodep1, AstNode* nodep2) {
|
||||
// Return the class type that both nodep1 and nodep2 are castable to.
|
||||
// If both are null, return the type of null constant.
|
||||
// If one is a class and one is null, return AstClassRefDType that points to that class.
|
||||
// If no common class type exists, return nullptr.
|
||||
|
||||
// First handle cases with null values and when one class is a super class of the other.
|
||||
if (VN_IS(nodep1, Const)) std::swap(nodep1, nodep2);
|
||||
{
|
||||
const VCastable castable = computeCastable(nodep1->dtypep(), nodep2->dtypep(), nodep2);
|
||||
if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) {
|
||||
return nodep1->dtypep();
|
||||
} else if (castable == VCastable::DYNAMIC_CLASS) {
|
||||
return nodep2->dtypep();
|
||||
}
|
||||
}
|
||||
|
||||
AstClassRefDType* classDtypep1 = VN_CAST(nodep1->dtypep(), ClassRefDType);
|
||||
while (classDtypep1) {
|
||||
const VCastable castable = computeCastable(classDtypep1, nodep2->dtypep(), nodep2);
|
||||
if (castable == VCastable::COMPATIBLE) return classDtypep1;
|
||||
AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
|
||||
classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// VNDeleter
|
||||
|
||||
|
||||
+379
-125
@@ -98,7 +98,20 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
|
||||
|
||||
//######################################################################
|
||||
|
||||
struct VNTypeInfo {
|
||||
const char* m_namep;
|
||||
enum uint8_t {
|
||||
OP_UNUSED,
|
||||
OP_USED,
|
||||
OP_LIST,
|
||||
OP_OPTIONAL,
|
||||
} m_opType[4];
|
||||
const char* m_opNamep[4];
|
||||
};
|
||||
|
||||
class VNType final {
|
||||
static const VNTypeInfo typeInfoTable[];
|
||||
|
||||
public:
|
||||
#include "V3Ast__gen_type_enum.h" // From ./astgen
|
||||
// Above include has:
|
||||
@@ -111,6 +124,7 @@ public:
|
||||
constexpr VNType(en _e) VL_MT_SAFE : m_e{_e} {}
|
||||
explicit VNType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
const VNTypeInfo* typeInfo() const VL_MT_SAFE { return &typeInfoTable[m_e]; }
|
||||
constexpr operator en() const VL_MT_SAFE { return m_e; }
|
||||
};
|
||||
constexpr bool operator==(const VNType& lhs, const VNType& rhs) VL_PURE {
|
||||
@@ -153,6 +167,37 @@ inline std::ostream& operator<<(std::ostream& os, const VLifetime& rhs) {
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VIsCached final {
|
||||
// Used in some nodes to cache results of boolean methods
|
||||
// If cachedCnt == 0, not cached
|
||||
// else if cachedCnt == s_cachedCntGbl, then m_state is if cached
|
||||
uint64_t m_cachedCnt : 63; // Mark of when cache was computed
|
||||
uint64_t m_state : 1;
|
||||
static uint64_t s_cachedCntGbl; // Global computed count
|
||||
|
||||
public:
|
||||
VIsCached()
|
||||
: m_cachedCnt{0}
|
||||
, m_state{0} {}
|
||||
bool isCached() const { return m_cachedCnt == s_cachedCntGbl; }
|
||||
bool get() const { return m_state; }
|
||||
void set(bool flag) {
|
||||
m_cachedCnt = s_cachedCntGbl;
|
||||
m_state = flag;
|
||||
}
|
||||
void clearCache() {
|
||||
m_cachedCnt = 0;
|
||||
m_state = 0;
|
||||
}
|
||||
static void clearCacheTree() {
|
||||
++s_cachedCntGbl;
|
||||
// 64 bits so won't overflow
|
||||
// UASSERT_STATIC(s_cachedCntGbl < MAX_CNT, "Overflow of cache counting");
|
||||
}
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VAccess final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
@@ -279,7 +324,7 @@ public:
|
||||
ET_BOTHEDGE, // POSEDGE | NEGEDGE (i.e.: 'edge' in Verilog)
|
||||
ET_POSEDGE,
|
||||
ET_NEGEDGE,
|
||||
ET_EVENT, // VlEvent::isFired
|
||||
ET_EVENT, // VlEventBase::isFired
|
||||
// Involving an expression
|
||||
ET_TRUE,
|
||||
//
|
||||
@@ -630,6 +675,39 @@ public:
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char* traceSigType() const {
|
||||
// VerilatedTraceSigType to used in trace signal declaration
|
||||
static const char* const lut[] = {
|
||||
/* UNKNOWN: */ "", // Should not be traced
|
||||
/* BIT: */ "BIT",
|
||||
/* BYTE: */ "BYTE",
|
||||
/* CHANDLE: */ "",
|
||||
/* EVENT: */ "EVENT",
|
||||
/* INT: */ "INT",
|
||||
/* INTEGER: */ "INTEGER",
|
||||
/* LOGIC: */ "LOGIC",
|
||||
/* LONGINT: */ "LONGINT",
|
||||
/* DOUBLE: */ "DOUBLE",
|
||||
/* SHORTINT: */ "SHORTINT",
|
||||
/* TIME: */ "TIME",
|
||||
/* STRING: */ "",
|
||||
/* UNTYPED: */ "", // Should not be traced
|
||||
/* SCOPEPTR: */ "", // Should not be traced
|
||||
/* CHARPTR: */ "", // Should not be traced
|
||||
/* MTASKSTATE: */ "", // Should not be traced
|
||||
/* TRIGGERVEC: */ "", // Should not be traced
|
||||
/* DELAY_SCHEDULER: */ "", // Should not be traced
|
||||
/* TRIGGER_SCHEDULER: */ "", // Should not be traced
|
||||
/* DYNAMIC_TRIGGER_SCHEDULER: */ "", // Should not be traced
|
||||
/* FORK_SYNC: */ "", // Should not be traced
|
||||
/* PROCESS_REFERENCE: */ "", // Should not be traced
|
||||
/* UINT32: */ "BIT",
|
||||
/* UINT64: */ "BIT",
|
||||
/* LOGIC_IMPLICIT: */ "", // Should not be traced
|
||||
};
|
||||
return lut[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VBasicDTypeKwd& lhs, const VBasicDTypeKwd& rhs) VL_MT_SAFE {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
@@ -676,7 +754,8 @@ public:
|
||||
}
|
||||
bool isReadOnly() const VL_MT_SAFE { return m_e == INPUT || m_e == CONSTREF; }
|
||||
bool isWritable() const VL_MT_SAFE { return m_e == OUTPUT || m_e == INOUT || m_e == REF; }
|
||||
bool isRefOrConstRef() const VL_MT_SAFE { return m_e == REF || m_e == CONSTREF; }
|
||||
bool isRef() const VL_MT_SAFE { return m_e == REF; }
|
||||
bool isConstRef() const VL_MT_SAFE { return m_e == CONSTREF; }
|
||||
};
|
||||
constexpr bool operator==(const VDirection& lhs, const VDirection& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
@@ -776,7 +855,6 @@ public:
|
||||
SUPPLY1,
|
||||
WIRE,
|
||||
WREAL,
|
||||
IMPLICITWIRE,
|
||||
TRIWIRE,
|
||||
TRI0,
|
||||
TRI1,
|
||||
@@ -796,25 +874,25 @@ public:
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {
|
||||
"?", "GPARAM", "LPARAM", "GENVAR", "VAR", "SUPPLY0", "SUPPLY1",
|
||||
"WIRE", "WREAL", "IMPLICITWIRE", "TRIWIRE", "TRI0", "TRI1", "PORT",
|
||||
"BLOCKTEMP", "MODULETEMP", "STMTTEMP", "XTEMP", "IFACEREF", "MEMBER"};
|
||||
static const char* const names[]
|
||||
= {"?", "GPARAM", "LPARAM", "GENVAR", "VAR", "SUPPLY0", "SUPPLY1",
|
||||
"WIRE", "WREAL", "TRIWIRE", "TRI0", "TRI1", "PORT", "BLOCKTEMP",
|
||||
"MODULETEMP", "STMTTEMP", "XTEMP", "IFACEREF", "MEMBER"};
|
||||
return names[m_e];
|
||||
}
|
||||
bool isParam() const { return m_e == GPARAM || m_e == LPARAM; }
|
||||
bool isSignal() const {
|
||||
return (m_e == WIRE || m_e == WREAL || m_e == IMPLICITWIRE || m_e == TRIWIRE || m_e == TRI0
|
||||
|| m_e == TRI1 || m_e == PORT || m_e == SUPPLY0 || m_e == SUPPLY1 || m_e == VAR);
|
||||
return (m_e == WIRE || m_e == WREAL || m_e == TRIWIRE || m_e == TRI0 || m_e == TRI1
|
||||
|| m_e == PORT || m_e == SUPPLY0 || m_e == SUPPLY1 || m_e == VAR);
|
||||
}
|
||||
bool isNet() const {
|
||||
return (m_e == WIRE || m_e == IMPLICITWIRE || m_e == TRIWIRE || m_e == TRI0 || m_e == TRI1
|
||||
|| m_e == SUPPLY0 || m_e == SUPPLY1);
|
||||
return (m_e == WIRE || m_e == TRIWIRE || m_e == TRI0 || m_e == TRI1 || m_e == SUPPLY0
|
||||
|| m_e == SUPPLY1);
|
||||
}
|
||||
bool isContAssignable() const { // In Verilog, always ok in SystemVerilog
|
||||
return (m_e == SUPPLY0 || m_e == SUPPLY1 || m_e == WIRE || m_e == WREAL
|
||||
|| m_e == IMPLICITWIRE || m_e == TRIWIRE || m_e == TRI0 || m_e == TRI1
|
||||
|| m_e == PORT || m_e == BLOCKTEMP || m_e == MODULETEMP || m_e == STMTTEMP
|
||||
|| m_e == XTEMP || m_e == IFACEREF);
|
||||
return (m_e == SUPPLY0 || m_e == SUPPLY1 || m_e == WIRE || m_e == WREAL || m_e == TRIWIRE
|
||||
|| m_e == TRI0 || m_e == TRI1 || m_e == PORT || m_e == BLOCKTEMP
|
||||
|| m_e == MODULETEMP || m_e == STMTTEMP || m_e == XTEMP || m_e == IFACEREF);
|
||||
}
|
||||
bool isProcAssignable() const {
|
||||
return (m_e == GPARAM || m_e == LPARAM || m_e == GENVAR || m_e == VAR || m_e == BLOCKTEMP
|
||||
@@ -828,6 +906,32 @@ public:
|
||||
return (m_e == VAR || m_e == GPARAM || m_e == LPARAM || m_e == PORT || m_e == WIRE
|
||||
|| m_e == TRI0 || m_e == TRI1);
|
||||
}
|
||||
|
||||
const char* traceSigKind() const {
|
||||
// VerilatedTraceSigKind to used in trace signal declaration
|
||||
static const char* const lut[] = {
|
||||
/* UNKNOWN: */ "", // Should not be traced
|
||||
/* GPARAM: */ "PARAMETER",
|
||||
/* LPARAM: */ "PARAMETER",
|
||||
/* GENVAR: */ "PARAMETER",
|
||||
/* VAR: */ "VAR",
|
||||
/* SUPPLY0: */ "SUPPLY0",
|
||||
/* SUPPLY1: */ "SUPPLY1",
|
||||
/* WIRE: */ "WIRE",
|
||||
/* WREAL: */ "WIRE",
|
||||
/* TRIWIRE: */ "TRI",
|
||||
/* TRI0: */ "TRI0",
|
||||
/* TRI1: */ "TRI1",
|
||||
/* PORT: */ "WIRE",
|
||||
/* BLOCKTEMP: */ "VAR",
|
||||
/* MODULETEMP: */ "VAR",
|
||||
/* STMTTEMP: */ "VAR",
|
||||
/* XTEMP: */ "VAR",
|
||||
/* IFACEREF: */ "", // Should not be traced directly
|
||||
/* MEMBER: */ "VAR",
|
||||
};
|
||||
return lut[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VVarType& lhs, const VVarType& rhs) VL_MT_SAFE {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
@@ -1168,34 +1272,148 @@ inline std::ostream& operator<<(std::ostream& os, const VNumRange& rhs) {
|
||||
class VUseType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
IMP_INCLUDE, // Implementation (.cpp) needs an include
|
||||
INT_INCLUDE, // Interface (.h) needs an include
|
||||
IMP_FWD_CLASS, // Implementation (.cpp) needs a forward class declaration
|
||||
INT_FWD_CLASS, // Interface (.h) needs a forward class declaration
|
||||
// Enum values are compared with <, so order matters
|
||||
INT_FWD_CLASS = 1 << 0, // Interface (.h) needs a forward class declaration
|
||||
INT_INCLUDE = 1 << 1, // Interface (.h) needs an include
|
||||
};
|
||||
enum en m_e;
|
||||
VUseType()
|
||||
: m_e{IMP_FWD_CLASS} {}
|
||||
: m_e{INT_FWD_CLASS} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VUseType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VUseType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
bool isInclude() const { return m_e == IMP_INCLUDE || m_e == INT_INCLUDE; }
|
||||
bool isFwdClass() const { return m_e == IMP_FWD_CLASS || m_e == INT_FWD_CLASS; }
|
||||
constexpr operator en() const { return m_e; }
|
||||
bool containsAny(VUseType other) { return m_e & other.m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"IMP_INC", "INT_INC", "IMP_FWD", "INT_FWD"};
|
||||
return names[m_e];
|
||||
static const char* const names[] = {"INT_FWD", "INT_INC", "INT_FWD_INC"};
|
||||
return names[m_e - 1];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VUseType& lhs, const VUseType& rhs) { return lhs.m_e == rhs.m_e; }
|
||||
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);
|
||||
}
|
||||
constexpr VUseType::en operator&(VUseType::en lhs, VUseType::en rhs) {
|
||||
return VUseType::en((uint8_t)lhs & (uint8_t)rhs);
|
||||
}
|
||||
inline std::ostream& operator<<(std::ostream& os, const VUseType& rhs) {
|
||||
return os << rhs.ascii();
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VTraceType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
CONSTANT, // Constant value dump (once at the beginning)
|
||||
FULL, // Full value dump (always emitted)
|
||||
CHANGE // Incremental value dump (emitted only if the value changed)
|
||||
};
|
||||
enum en m_e;
|
||||
VTraceType()
|
||||
: m_e{CONSTANT} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VTraceType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VTraceType(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 {
|
||||
static const char* const names[] = {"CONSTANT", "FULL", "CHANGE"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VTraceType& lhs, const VTraceType& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
constexpr bool operator==(const VTraceType& lhs, VTraceType::en rhs) { return lhs.m_e == rhs; }
|
||||
constexpr bool operator==(VTraceType::en lhs, const VTraceType& rhs) { return lhs == rhs.m_e; }
|
||||
inline std::ostream& operator<<(std::ostream& os, const VTraceType& rhs) {
|
||||
return os << rhs.ascii();
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VTracePrefixType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
// Note: Entries must match VerilatedTracePrefixType
|
||||
ARRAY_PACKED,
|
||||
ARRAY_UNPACKED,
|
||||
SCOPE_MODULE,
|
||||
SCOPE_INTERFACE,
|
||||
STRUCT_PACKED,
|
||||
STRUCT_UNPACKED,
|
||||
UNION_PACKED,
|
||||
};
|
||||
enum en m_e;
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VTracePrefixType(en _e)
|
||||
: m_e{_e} {}
|
||||
constexpr operator en() const { return m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
= {"ARRAY_PACKED", "ARRAY_UNPACKED", "SCOPE_MODULE", "SCOPE_INTERFACE",
|
||||
"STRUCT_PACKED", "STRUCT_UNPACKED", "UNION_PACKED"};
|
||||
return names[m_e];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VTracePrefixType& lhs, const VTracePrefixType& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
constexpr bool operator==(const VTracePrefixType& lhs, VTracePrefixType::en rhs) {
|
||||
return lhs.m_e == rhs;
|
||||
}
|
||||
constexpr bool operator==(VTracePrefixType::en lhs, const VTracePrefixType& rhs) {
|
||||
return lhs == rhs.m_e;
|
||||
}
|
||||
inline std::ostream& operator<<(std::ostream& os, const VTracePrefixType& rhs) {
|
||||
return os << rhs.ascii();
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VCastable final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
UNSUPPORTED,
|
||||
SAMEISH,
|
||||
COMPATIBLE,
|
||||
ENUM_EXPLICIT,
|
||||
ENUM_IMPLICIT,
|
||||
DYNAMIC_CLASS,
|
||||
INCOMPATIBLE,
|
||||
_ENUM_MAX // Leave last
|
||||
};
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
= {"UNSUPPORTED", "SAMEISH", "COMPATIBLE", "ENUM_EXPLICIT",
|
||||
"ENUM_IMPLICIT", "DYNAMIC_CLASS", "INCOMPATIBLE"};
|
||||
return names[m_e];
|
||||
}
|
||||
VCastable()
|
||||
: m_e{UNSUPPORTED} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VCastable(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VCastable(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const { return m_e; }
|
||||
};
|
||||
constexpr bool operator==(const VCastable& lhs, const VCastable& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
constexpr bool operator==(const VCastable& lhs, VCastable::en rhs) { return lhs.m_e == rhs; }
|
||||
constexpr bool operator==(VCastable::en lhs, const VCastable& rhs) { return lhs == rhs.m_e; }
|
||||
inline std::ostream& operator<<(std::ostream& os, const VCastable& rhs) {
|
||||
return os << rhs.ascii();
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
|
||||
class VBasicTypeKey final {
|
||||
@@ -1232,6 +1450,44 @@ public:
|
||||
~VBasicTypeKey() = default;
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
// VSelfPointerText - Represents text to be emitted before a given var reference, call, etc. to
|
||||
// serve as a pointer to a 'self' object. For example, it could be empty (no self pointer), or the
|
||||
// string 'this', or 'vlSymsp->...'
|
||||
|
||||
class VSelfPointerText final {
|
||||
private:
|
||||
// STATIC MEMBERS
|
||||
// Keep these in shared pointers to avoid branching for special cases
|
||||
static const std::shared_ptr<const string> s_emptyp; // Holds ""
|
||||
static const std::shared_ptr<const string> s_thisp; // Holds "this"
|
||||
|
||||
// MEMBERS
|
||||
std::shared_ptr<const string> m_strp;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
class Empty {}; // for creator type-overload selection
|
||||
VSelfPointerText(Empty)
|
||||
: m_strp{s_emptyp} {}
|
||||
class This {}; // for creator type-overload selection
|
||||
VSelfPointerText(This)
|
||||
: m_strp{s_thisp} {}
|
||||
VSelfPointerText(This, const string& field)
|
||||
: m_strp{std::make_shared<const string>("this->" + field)} {}
|
||||
class VlSyms {}; // for creator type-overload selection
|
||||
VSelfPointerText(VlSyms, const string& field)
|
||||
: m_strp{std::make_shared<const string>("(&vlSymsp->" + field + ')')} {}
|
||||
|
||||
// METHODS
|
||||
bool isEmpty() const { return m_strp == s_emptyp; }
|
||||
bool isVlSym() const { return m_strp->find("vlSymsp") != string::npos; }
|
||||
bool hasThis() const { return m_strp == s_thisp || VString::startsWith(*m_strp, "this"); }
|
||||
string protect(bool useSelfForThis, bool protect) const;
|
||||
const std::string& asString() const { return *m_strp; }
|
||||
bool operator==(const VSelfPointerText& other) const { return *m_strp == *other.m_strp; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// AstNUser - Generic base class for AST User nodes.
|
||||
// - Also used to allow parameter passing up/down iterate calls
|
||||
@@ -1314,58 +1570,47 @@ protected:
|
||||
class VNUser1InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser1InUse() { allocate(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser1InUse() { free (1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser1InUse() { allocate(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser1InUse() { free (1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser2InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser3InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser4InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser5InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser5InUse() { allocate(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser5InUse() { free (5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
//######################################################################
|
||||
@@ -1374,7 +1619,7 @@ public:
|
||||
// nodes needs to be deferred to a later time, because pointers to the
|
||||
// removed nodes might still exist.
|
||||
|
||||
class VNDeleter VL_NOT_FINAL {
|
||||
class VNDeleter final {
|
||||
// MEMBERS
|
||||
std::vector<AstNode*> m_deleteps; // Nodes to delete
|
||||
|
||||
@@ -1387,11 +1632,11 @@ public:
|
||||
m_deleteps.push_back(nodep);
|
||||
}
|
||||
|
||||
// Delete all previously pushed nodes (by callint deleteTree)
|
||||
// Delete all previously pushed nodes (by calling deleteTree)
|
||||
void doDeletes();
|
||||
|
||||
// Do the deletions on destruction
|
||||
virtual ~VNDeleter() { doDeletes(); }
|
||||
~VNDeleter() { doDeletes(); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -1400,7 +1645,7 @@ public:
|
||||
// This only has the constant fuctions for non-modifying visitors.
|
||||
// For more typical usage see VNVisitor
|
||||
|
||||
class VNVisitorConst VL_NOT_FINAL : public VNDeleter {
|
||||
class VNVisitorConst VL_NOT_FINAL {
|
||||
friend class AstNode;
|
||||
|
||||
public:
|
||||
@@ -1418,6 +1663,7 @@ public:
|
||||
inline void iterateAndNextConstNullBackwards(AstNode* nodep);
|
||||
|
||||
virtual void visit(AstNode* nodep) = 0;
|
||||
virtual ~VNVisitorConst() {}
|
||||
#include "V3Ast__gen_visitor_decls.h" // From ./astgen
|
||||
};
|
||||
|
||||
@@ -1426,6 +1672,7 @@ public:
|
||||
// type without changing the base classes. See "Modern C++ Design".
|
||||
|
||||
class VNVisitor VL_NOT_FINAL : public VNVisitorConst {
|
||||
VNDeleter m_deleter; // Used to delay deletion of nodes
|
||||
public:
|
||||
/// Call visit()s on nodep
|
||||
inline void iterate(AstNode* nodep);
|
||||
@@ -1437,6 +1684,10 @@ public:
|
||||
inline void iterateAndNextNull(AstNode* nodep);
|
||||
/// Return edited nodep; see comments in V3Ast.cpp
|
||||
inline AstNode* iterateSubtreeReturnEdits(AstNode* nodep);
|
||||
|
||||
VNDeleter& deleter() { return m_deleter; }
|
||||
void pushDeletep(AstNode* nodep) { deleter().pushDeletep(nodep); }
|
||||
void doDeletes() { deleter().doDeletes(); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -1563,8 +1814,6 @@ class AstNode VL_NOT_FINAL {
|
||||
uint32_t m_user3Cnt = 0; // Mark of when userp was set
|
||||
uint32_t m_user4Cnt = 0; // Mark of when userp was set
|
||||
VNUser m_user4u{0}; // Contains any information the user iteration routine wants
|
||||
VNUser m_user5u{0}; // Contains any information the user iteration routine wants
|
||||
uint32_t m_user5Cnt = 0; // Mark of when userp was set
|
||||
|
||||
// METHODS
|
||||
void op1p(AstNode* nodep) {
|
||||
@@ -1585,8 +1834,8 @@ class AstNode VL_NOT_FINAL {
|
||||
}
|
||||
|
||||
private:
|
||||
AstNode* cloneTreeIter();
|
||||
AstNode* cloneTreeIterList();
|
||||
AstNode* cloneTreeIter(bool needPure);
|
||||
AstNode* cloneTreeIterList(bool needPure);
|
||||
void checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE;
|
||||
bool gateTreeIter() const;
|
||||
static bool sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext,
|
||||
@@ -1639,13 +1888,16 @@ protected:
|
||||
UASSERT_STATIC(s_cloneCntGbl, "Rollover");
|
||||
}
|
||||
|
||||
// Use instead isSame(), this is for each Ast* class, and assumes node is of same type
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
|
||||
public:
|
||||
// ACCESSORS
|
||||
VNType type() const VL_MT_SAFE { return m_type; }
|
||||
const char* typeName() const VL_MT_SAFE { return type().ascii(); } // See also prettyTypeName
|
||||
AstNode* nextp() const VL_MT_STABLE { return m_nextp; }
|
||||
AstNode* backp() const VL_MT_STABLE { return m_backp; }
|
||||
AstNode* abovep() const; // Parent node above, only when no nextp() as otherwise slow
|
||||
AstNode* abovep() const; // Get parent node above, only for list head and tail
|
||||
AstNode* op1p() const VL_MT_STABLE { return m_op1p; }
|
||||
AstNode* op2p() const VL_MT_STABLE { return m_op2p; }
|
||||
AstNode* op3p() const VL_MT_STABLE { return m_op3p; }
|
||||
@@ -1751,83 +2003,71 @@ public:
|
||||
inline bool isDouble() const VL_MT_STABLE;
|
||||
inline bool isSigned() const VL_MT_STABLE;
|
||||
inline bool isString() const VL_MT_STABLE;
|
||||
inline bool isEvent() const VL_MT_STABLE;
|
||||
|
||||
// clang-format off
|
||||
VNUser user1u() const VL_MT_STABLE {
|
||||
VNUser user1u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user1Cnt==VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "user1p used without AstUserInUse");
|
||||
return ((m_user1Cnt == VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
|
||||
}
|
||||
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
|
||||
void user1u(const VNUser& user) { m_user1u=user; m_user1Cnt=VNUser1InUse::s_userCntGbl; }
|
||||
void user1p(void* userp) { user1u(VNUser{userp}); }
|
||||
int user1() const { return user1u().toInt(); }
|
||||
void user1(int val) { user1u(VNUser{val}); }
|
||||
int user1Inc(int val=1) { int v=user1(); user1(v+val); return v; }
|
||||
int user1SetOnce() { int v=user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
|
||||
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
|
||||
void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; }
|
||||
void user1p(void* userp) { user1u(VNUser{userp}); }
|
||||
void user1(int val) { user1u(VNUser{val}); }
|
||||
int user1() const { return user1u().toInt(); }
|
||||
int user1Inc(int val = 1) { int v = user1(); user1(v + val); return v; }
|
||||
int user1SetOnce() { int v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
|
||||
static void user1ClearTree() { VNUser1InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user2u() const VL_MT_STABLE {
|
||||
VNUser user2u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user2Cnt==VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "user2p used without AstUserInUse");
|
||||
return ((m_user2Cnt == VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
|
||||
}
|
||||
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
|
||||
void user2u(const VNUser& user) { m_user2u=user; m_user2Cnt=VNUser2InUse::s_userCntGbl; }
|
||||
void user2p(void* userp) { user2u(VNUser{userp}); }
|
||||
int user2() const { return user2u().toInt(); }
|
||||
void user2(int val) { user2u(VNUser{val}); }
|
||||
int user2Inc(int val=1) { int v=user2(); user2(v+val); return v; }
|
||||
int user2SetOnce() { int v=user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
|
||||
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
|
||||
void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; }
|
||||
void user2p(void* userp) { user2u(VNUser{userp}); }
|
||||
void user2(int val) { user2u(VNUser{val}); }
|
||||
int user2() const { return user2u().toInt(); }
|
||||
int user2Inc(int val = 1) { int v = user2(); user2(v + val); return v; }
|
||||
int user2SetOnce() { int v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
|
||||
static void user2ClearTree() { VNUser2InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user3u() const VL_MT_STABLE {
|
||||
VNUser user3u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user3Cnt==VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "user3p used without AstUserInUse");
|
||||
return ((m_user3Cnt == VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
|
||||
}
|
||||
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
|
||||
void user3u(const VNUser& user) { m_user3u=user; m_user3Cnt=VNUser3InUse::s_userCntGbl; }
|
||||
void user3p(void* userp) { user3u(VNUser{userp}); }
|
||||
int user3() const { return user3u().toInt(); }
|
||||
void user3(int val) { user3u(VNUser{val}); }
|
||||
int user3Inc(int val=1) { int v=user3(); user3(v+val); return v; }
|
||||
int user3SetOnce() { int v=user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
|
||||
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
|
||||
void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; }
|
||||
void user3p(void* userp) { user3u(VNUser{userp}); }
|
||||
void user3(int val) { user3u(VNUser{val}); }
|
||||
int user3() const { return user3u().toInt(); }
|
||||
int user3Inc(int val = 1) { int v = user3(); user3(v + val); return v; }
|
||||
int user3SetOnce() { int v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
|
||||
static void user3ClearTree() { VNUser3InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user4u() const VL_MT_STABLE {
|
||||
VNUser user4u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user4Cnt==VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "user4p used without AstUserInUse");
|
||||
return ((m_user4Cnt == VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
|
||||
}
|
||||
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
|
||||
void user4u(const VNUser& user) { m_user4u=user; m_user4Cnt=VNUser4InUse::s_userCntGbl; }
|
||||
void user4p(void* userp) { user4u(VNUser{userp}); }
|
||||
int user4() const { return user4u().toInt(); }
|
||||
void user4(int val) { user4u(VNUser{val}); }
|
||||
int user4Inc(int val=1) { int v=user4(); user4(v+val); return v; }
|
||||
int user4SetOnce() { int v=user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
|
||||
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
|
||||
void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; }
|
||||
void user4p(void* userp) { user4u(VNUser{userp}); }
|
||||
void user4(int val) { user4u(VNUser{val}); }
|
||||
int user4() const { return user4u().toInt(); }
|
||||
int user4Inc(int val = 1) { int v = user4(); user4(v + val); return v; }
|
||||
int user4SetOnce() { int v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
|
||||
static void user4ClearTree() { VNUser4InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user5u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser5InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user5Cnt==VNUser5InUse::s_userCntGbl) ? m_user5u : VNUser{0});
|
||||
}
|
||||
AstNode* user5p() const VL_MT_STABLE { return user5u().toNodep(); }
|
||||
void user5u(const VNUser& user) { m_user5u=user; m_user5Cnt=VNUser5InUse::s_userCntGbl; }
|
||||
void user5p(void* userp) { user5u(VNUser{userp}); }
|
||||
int user5() const { return user5u().toInt(); }
|
||||
void user5(int val) { user5u(VNUser{val}); }
|
||||
int user5Inc(int val=1) { int v=user5(); user5(v+val); return v; }
|
||||
int user5SetOnce() { int v=user5(); if (!v) user5(1); return v; } // Better for cache than user5Inc()
|
||||
static void user5ClearTree() { VNUser5InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
// clang-format on
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
uint64_t editCount() const { return m_editCount; }
|
||||
void editCountInc() {
|
||||
m_editCount = ++s_editCntGbl; // Preincrement, so can "watch AstNode::s_editCntGbl=##"
|
||||
VIsCached::clearCacheTree(); // Any edit clears all caching
|
||||
}
|
||||
#else
|
||||
void editCountInc() { ++s_editCntGbl; }
|
||||
@@ -1900,10 +2140,14 @@ public:
|
||||
AstNodeDType* findBasicDType(VBasicDTypeKwd kwd) const;
|
||||
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
|
||||
static VCastable computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp,
|
||||
const AstNode* fromConstp);
|
||||
static AstNodeDType* getCommonClassTypep(AstNode* nodep1, AstNode* nodep2);
|
||||
|
||||
// METHODS - dump and error
|
||||
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex);
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN
|
||||
VL_REQUIRES(V3Error::s().m_mutex);
|
||||
VL_RELEASE(V3Error::s().m_mutex);
|
||||
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
return fileline()->warnContextPrimary();
|
||||
}
|
||||
@@ -1941,11 +2185,9 @@ public:
|
||||
AstNode* belowp); // When calling, "this" is second argument
|
||||
|
||||
// METHODS - Iterate on a tree
|
||||
// Clone or return nullptr if nullptr
|
||||
static AstNode* cloneTreeNull(AstNode* nodep, bool cloneNextLink) {
|
||||
return nodep ? nodep->cloneTree(cloneNextLink) : nullptr;
|
||||
}
|
||||
AstNode* cloneTree(bool cloneNextLink); // Not const, as sets clonep() on original nodep
|
||||
AstNode* cloneTree(bool cloneNextLink,
|
||||
bool needPure = false); // Not const, as sets clonep() on original nodep
|
||||
AstNode* cloneTreePure(bool cloneNextLink) { return cloneTree(cloneNextLink, true); }
|
||||
bool gateTree() { return gateTreeIter(); } // Is tree isGateOptimizable?
|
||||
inline bool sameTree(const AstNode* node2p) const; // Does tree of this == node2p?
|
||||
// Does tree of this == node2p?, not allowing non-isGateOptimizable
|
||||
@@ -1970,7 +2212,14 @@ public:
|
||||
void dumpTreeDot(std::ostream& os = std::cout) const;
|
||||
void dumpTreeDotFile(const string& filename, bool append = false, bool doDump = true);
|
||||
|
||||
bool isTreePureRecurse() const;
|
||||
// METHODS - static advancement
|
||||
static AstNode* afterCommentp(AstNode* nodep) {
|
||||
// Skip over comments starting at provided nodep,
|
||||
// such as to determine if a AstIf is empty.
|
||||
// nodep may be null, if so return nullptr.
|
||||
while (nodep && VN_IS(nodep, Comment)) nodep = nodep->nextp();
|
||||
return nodep;
|
||||
}
|
||||
|
||||
// METHODS - queries
|
||||
// Changes control flow, disable some optimizations
|
||||
@@ -1980,11 +2229,13 @@ public:
|
||||
// GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf)
|
||||
virtual bool isGateDedupable() const { return isGateOptimizable(); }
|
||||
// Else creates output or exits, etc, not unconsumed
|
||||
virtual bool isOutputter() const { return false; }
|
||||
virtual bool isOutputter() { return false; }
|
||||
// Else a AstTime etc which output can't be predicted from input
|
||||
virtual bool isPredictOptimizable() const { return !isTimingControl(); }
|
||||
// Else a $display, etc, that must be ordered with other displays
|
||||
virtual bool isPure() const { return true; }
|
||||
virtual bool isPure() { return true; }
|
||||
// Iff isPure on current node and any nextp()
|
||||
bool isPureAndNext() { return isPure() && (!nextp() || nextp()->isPure()); }
|
||||
// Else a AstTime etc that can't be substituted out
|
||||
virtual bool isSubstOptimizable() const { return true; }
|
||||
// An event control, delay, wait, etc.
|
||||
@@ -1993,7 +2244,10 @@ public:
|
||||
// statement is unlikely to be taken
|
||||
virtual bool isUnlikely() const { return false; }
|
||||
virtual int instrCount() const { return 0; }
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
// Iff node is identical to anouther node
|
||||
virtual bool isSame(const AstNode* samep) const {
|
||||
return type() == samep->type() && same(samep);
|
||||
}
|
||||
// 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
|
||||
|
||||
+5
-1
@@ -39,6 +39,10 @@ bool AstNode::isDouble() const VL_MT_STABLE {
|
||||
bool AstNode::isString() const VL_MT_STABLE {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
|
||||
}
|
||||
bool AstNode::isEvent() const VL_MT_STABLE {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isEvent();
|
||||
}
|
||||
|
||||
bool AstNode::isSigned() const VL_MT_STABLE { return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
bool AstNode::isClassHandleValue() const {
|
||||
@@ -176,7 +180,7 @@ bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
|
||||
return (varScopep() == samep->varScopep());
|
||||
} else {
|
||||
return (selfPointer() == samep->selfPointer()
|
||||
&& (!selfPointer().empty() || !samep->selfPointer().empty())
|
||||
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
|
||||
&& varp()->name() == samep->varp()->name());
|
||||
}
|
||||
}
|
||||
|
||||
+73
-25
@@ -160,14 +160,14 @@ public:
|
||||
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
const AstNodeArrayDType* const asamep = VN_DBG_AS(samep, NodeArrayDType);
|
||||
return (hi() == asamep->hi() && subDTypep() == asamep->subDTypep()
|
||||
&& rangenp()->sameTree(asamep->rangenp()));
|
||||
} // HashedDT doesn't recurse, so need to check children
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return (asamep && type() == samep->type() && hi() == asamep->hi()
|
||||
&& rangenp()->sameTree(asamep->rangenp())
|
||||
if (type() != samep->type()) return false;
|
||||
const AstNodeArrayDType* const asamep = VN_DBG_AS(samep, NodeArrayDType);
|
||||
return (hi() == asamep->hi() && rangenp()->sameTree(asamep->rangenp())
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
|
||||
}
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
@@ -314,14 +314,15 @@ public:
|
||||
if (m_keyDTypep && m_keyDTypep->clonep()) m_keyDTypep = m_keyDTypep->clonep();
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstAssocArrayDType* const asamep = static_cast<const AstAssocArrayDType*>(samep);
|
||||
const AstAssocArrayDType* const asamep = VN_DBG_AS(samep, AssocArrayDType);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
if (!asamep->keyDTypep()) return false;
|
||||
return (subDTypep() == asamep->subDTypep() && keyDTypep() == asamep->keyDTypep());
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
const AstAssocArrayDType* const asamep = static_cast<const AstAssocArrayDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
if (type() != samep->type()) return false;
|
||||
const AstAssocArrayDType* const asamep = VN_DBG_AS(samep, AssocArrayDType);
|
||||
return asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
string prettyDTypeName() const override;
|
||||
@@ -494,6 +495,41 @@ public:
|
||||
int widthTotalBytes() const override { V3ERROR_NA_RETURN(0); }
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
class AstCDType final : public AstNodeDType {
|
||||
// Raw "C" data type passed directly to output
|
||||
string m_name; // Name of data type, printed when do V3EmitC
|
||||
public:
|
||||
AstCDType(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_CDType(fl)
|
||||
, m_name{name} {
|
||||
this->dtypep(this);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstCDType;
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstCDType* const asamep = VN_DBG_AS(samep, CDType);
|
||||
return m_name == asamep->m_name;
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override { return same(samep); }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
string prettyDTypeName() const override { return m_name; }
|
||||
const char* broken() const override { return nullptr; }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return 8; } // Assume
|
||||
int widthTotalBytes() const override { return 8; } // Assume
|
||||
bool isCompound() const override { return true; }
|
||||
static string typeToHold(uint64_t maxItem) {
|
||||
return (maxItem < (1ULL << 8)) ? "CData"
|
||||
: (maxItem < (1ULL << 16)) ? "SData"
|
||||
: (maxItem < (1ULL << 32)) ? "IData"
|
||||
: "QData";
|
||||
}
|
||||
};
|
||||
class AstClassRefDType final : public AstNodeDType {
|
||||
// Reference to a class
|
||||
// @astgen op1 := paramsp: List[AstPin]
|
||||
@@ -512,7 +548,7 @@ public:
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override;
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstClassRefDType* const asamep = static_cast<const AstClassRefDType*>(samep);
|
||||
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
|
||||
return (m_classp == asamep->m_classp && m_classOrPackagep == asamep->m_classOrPackagep);
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
@@ -561,7 +597,7 @@ public:
|
||||
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstConstDType* const sp = static_cast<const AstConstDType*>(samep);
|
||||
const AstConstDType* const sp = VN_DBG_AS(samep, ConstDType);
|
||||
return (m_refDTypep == sp->m_refDTypep);
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
@@ -610,7 +646,7 @@ public:
|
||||
ASTGEN_MEMBERS_AstDefImplicitDType;
|
||||
int uniqueNum() const { return m_uniqueNum; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstDefImplicitDType* const sp = static_cast<const AstDefImplicitDType*>(samep);
|
||||
const AstDefImplicitDType* const sp = VN_DBG_AS(samep, DefImplicitDType);
|
||||
return uniqueNum() == sp->uniqueNum();
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
@@ -661,13 +697,14 @@ public:
|
||||
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstAssocArrayDType* const asamep = static_cast<const AstAssocArrayDType*>(samep);
|
||||
const AstDynArrayDType* const asamep = VN_DBG_AS(samep, DynArrayDType);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
return subDTypep() == asamep->subDTypep();
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
const AstAssocArrayDType* const asamep = static_cast<const AstAssocArrayDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
if (type() != samep->type()) return false;
|
||||
const AstDynArrayDType* const asamep = VN_DBG_AS(samep, DynArrayDType);
|
||||
return asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
string prettyDTypeName() const override;
|
||||
@@ -719,10 +756,14 @@ class AstEnumDType final : public AstNodeDType {
|
||||
// Parents: TYPEDEF/MODULE
|
||||
// @astgen op1 := childDTypep : Optional[AstNodeDType]
|
||||
// @astgen op2 := itemsp : List[AstEnumItem]
|
||||
public:
|
||||
using TableMap = std::map<VAttrType, AstVar*>;
|
||||
|
||||
private:
|
||||
string m_name; // Name from upper typedef, if any
|
||||
AstNodeDType* m_refDTypep = nullptr; // Elements are of this type after V3Width
|
||||
const int m_uniqueNum = 0;
|
||||
TableMap m_tableMap; // Created table for V3Width only to remove duplicates
|
||||
|
||||
public:
|
||||
AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstEnumItem* itemsp)
|
||||
@@ -735,9 +776,12 @@ public:
|
||||
widthFromSub(subDTypep());
|
||||
}
|
||||
ASTGEN_MEMBERS_AstEnumDType;
|
||||
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep())));
|
||||
BROKEN_RTN(std::any_of(m_tableMap.begin(), m_tableMap.end(),
|
||||
[](const auto& p) { return !p.second->brokeExists(); }));
|
||||
return nullptr;
|
||||
}
|
||||
void cloneRelink() override {
|
||||
@@ -745,7 +789,7 @@ public:
|
||||
}
|
||||
int uniqueNum() const { return m_uniqueNum; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstEnumDType* const sp = static_cast<const AstEnumDType*>(samep);
|
||||
const AstEnumDType* const sp = VN_DBG_AS(samep, EnumDType);
|
||||
return uniqueNum() == sp->uniqueNum();
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
@@ -774,6 +818,8 @@ public:
|
||||
return count;
|
||||
}
|
||||
bool isCompound() const override { return false; }
|
||||
TableMap& tableMap() { return m_tableMap; }
|
||||
const TableMap& tableMap() const { return m_tableMap; }
|
||||
};
|
||||
class AstIfaceRefDType final : public AstNodeDType {
|
||||
// Reference to an interface, either for a port, or inside parent cell
|
||||
@@ -938,9 +984,9 @@ public:
|
||||
AstNodeDType* skipRefToConstp() const override { return subDTypep()->skipRefToConstp(); }
|
||||
AstNodeDType* skipRefToEnump() const override { return subDTypep()->skipRefToEnump(); }
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
const AstParamTypeDType* const sp = static_cast<const AstParamTypeDType*>(samep);
|
||||
return type() == samep->type() && sp
|
||||
&& this->subDTypep()->skipRefp()->similarDType(sp->subDTypep()->skipRefp());
|
||||
if (type() != samep->type()) return false;
|
||||
const AstParamTypeDType* const sp = VN_DBG_AS(samep, ParamTypeDType);
|
||||
return this->subDTypep()->skipRefp()->similarDType(sp->subDTypep()->skipRefp());
|
||||
}
|
||||
int widthAlignBytes() const override { return dtypep()->widthAlignBytes(); }
|
||||
int widthTotalBytes() const override { return dtypep()->widthTotalBytes(); }
|
||||
@@ -1011,13 +1057,14 @@ public:
|
||||
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstQueueDType* const asamep = static_cast<const AstQueueDType*>(samep);
|
||||
const AstQueueDType* const asamep = VN_DBG_AS(samep, QueueDType);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
return (subDTypep() == asamep->subDTypep());
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
const AstQueueDType* const asamep = static_cast<const AstQueueDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
if (type() != samep->type()) return false;
|
||||
const AstQueueDType* const asamep = VN_DBG_AS(samep, QueueDType);
|
||||
return asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
@@ -1075,7 +1122,7 @@ public:
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override;
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstRefDType* const asamep = static_cast<const AstRefDType*>(samep);
|
||||
const AstRefDType* const asamep = VN_DBG_AS(samep, RefDType);
|
||||
return (m_typedefp == asamep->m_typedefp && m_refDTypep == asamep->m_refDTypep
|
||||
&& m_name == asamep->m_name && m_classOrPackagep == asamep->m_classOrPackagep);
|
||||
}
|
||||
@@ -1153,13 +1200,14 @@ public:
|
||||
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
const AstSampleQueueDType* const asamep = VN_DBG_AS(samep, SampleQueueDType);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
return (subDTypep() == asamep->subDTypep());
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
const AstSampleQueueDType* const asamep = static_cast<const AstSampleQueueDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
if (type() != samep->type()) return false;
|
||||
const AstSampleQueueDType* const asamep = VN_DBG_AS(samep, SampleQueueDType);
|
||||
return asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
@@ -1352,7 +1400,7 @@ public:
|
||||
ASTGEN_MEMBERS_AstUnpackArrayDType;
|
||||
string prettyDTypeName() const override;
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstUnpackArrayDType* const sp = static_cast<const AstUnpackArrayDType*>(samep);
|
||||
const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType);
|
||||
return m_isCompound == sp->m_isCompound;
|
||||
}
|
||||
// Outer dimension comes first. The first element is this node.
|
||||
|
||||
+259
-97
@@ -69,6 +69,8 @@ class AstNodeBiop VL_NOT_FINAL : public AstNodeExpr {
|
||||
// Binary expression
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
// @astgen op2 := rhsp : AstNodeExpr
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
protected:
|
||||
AstNodeBiop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: AstNodeExpr{t, fl} {
|
||||
@@ -93,6 +95,11 @@ public:
|
||||
virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors?
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool same(const AstNode*) const override { return true; }
|
||||
bool isPure() override;
|
||||
const char* broken() const override;
|
||||
|
||||
private:
|
||||
bool getPurityRecurse() const { return lhsp()->isPure() && rhsp()->isPure(); }
|
||||
};
|
||||
class AstNodeBiCom VL_NOT_FINAL : public AstNodeBiop {
|
||||
// Binary expr with commutative properties
|
||||
@@ -176,6 +183,7 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeExpr {
|
||||
// @astgen op2 := argsp : List[AstNodeExpr] // Note: op1 used by some sub-types only
|
||||
AstCFunc* m_funcp;
|
||||
string m_argTypes;
|
||||
bool m_superReference = false; // Called with super reference
|
||||
|
||||
protected:
|
||||
AstNodeCCall(VNType t, FileLine* fl, AstCFunc* funcp, AstNodeExpr* argsp = nullptr)
|
||||
@@ -191,13 +199,13 @@ public:
|
||||
const char* broken() const override;
|
||||
int instrCount() const override { return INSTR_COUNT_CALL; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstNodeCCall* const asamep = static_cast<const AstNodeCCall*>(samep);
|
||||
const AstNodeCCall* const asamep = VN_DBG_AS(samep, NodeCCall);
|
||||
return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes());
|
||||
}
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override;
|
||||
bool isOutputter() const override { return !isPure(); }
|
||||
bool isPure() override;
|
||||
bool isOutputter() override { return !isPure(); }
|
||||
AstCFunc* funcp() const { return m_funcp; }
|
||||
void funcp(AstCFunc* funcp) { m_funcp = funcp; }
|
||||
void argTypes(const string& str) { m_argTypes = str; }
|
||||
@@ -206,6 +214,8 @@ public:
|
||||
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() final override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const final override { return true; }
|
||||
bool superReference() const { return m_superReference; }
|
||||
void superReference(bool flag) { m_superReference = flag; }
|
||||
};
|
||||
class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeExpr {
|
||||
// A reference to a task (or function)
|
||||
@@ -219,6 +229,8 @@ class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeExpr {
|
||||
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)
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
protected:
|
||||
AstNodeFTaskRef(VNType t, FileLine* fl, AstNode* namep, AstNodeExpr* pinsp)
|
||||
: AstNodeExpr{t, fl} {
|
||||
@@ -249,11 +261,14 @@ public:
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
bool pli() const { return m_pli; }
|
||||
void pli(bool flag) { m_pli = flag; }
|
||||
bool isPure() const override;
|
||||
bool isPure() override;
|
||||
|
||||
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() final override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const final override { V3ERROR_NA_RETURN(true); }
|
||||
|
||||
private:
|
||||
bool getPurityRecurse() const;
|
||||
};
|
||||
class AstNodePreSel VL_NOT_FINAL : public AstNodeExpr {
|
||||
// Something that becomes an AstSel
|
||||
@@ -261,6 +276,8 @@ class AstNodePreSel VL_NOT_FINAL : public AstNodeExpr {
|
||||
// @astgen op2 := rhsp : AstNodeExpr
|
||||
// @astgen op3 := thsp : Optional[AstNodeExpr]
|
||||
// @astgen op4 := attrp : Optional[AstAttrOf]
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
protected:
|
||||
AstNodePreSel(VNType t, FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* rhsp, AstNodeExpr* thsp)
|
||||
: AstNodeExpr{t, fl} {
|
||||
@@ -277,6 +294,13 @@ public:
|
||||
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() final override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const final override { V3ERROR_NA_RETURN(true); }
|
||||
bool isPure() override;
|
||||
const char* broken() const override;
|
||||
|
||||
private:
|
||||
bool getPurityRecurse() const {
|
||||
return fromp()->isPure() && rhsp()->isPure() && (!thsp() || thsp()->isPure());
|
||||
}
|
||||
};
|
||||
class AstNodeQuadop VL_NOT_FINAL : public AstNodeExpr {
|
||||
// 4-ary expression
|
||||
@@ -284,6 +308,8 @@ class AstNodeQuadop VL_NOT_FINAL : public AstNodeExpr {
|
||||
// @astgen op2 := rhsp : AstNodeExpr
|
||||
// @astgen op3 := thsp : AstNodeExpr
|
||||
// @astgen op4 := fhsp : AstNodeExpr
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
protected:
|
||||
AstNodeQuadop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp,
|
||||
AstNodeExpr* fhsp)
|
||||
@@ -311,6 +337,13 @@ public:
|
||||
virtual bool sizeMattersFhs() const = 0; // True if output result depends on ths size
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool same(const AstNode*) const override { return true; }
|
||||
bool isPure() override;
|
||||
const char* broken() const override;
|
||||
|
||||
private:
|
||||
bool getPurityRecurse() const {
|
||||
return lhsp()->isPure() && rhsp()->isPure() && thsp()->isPure() && fhsp()->isPure();
|
||||
}
|
||||
};
|
||||
class AstNodeTermop VL_NOT_FINAL : public AstNodeExpr {
|
||||
// Terminal operator -- an operator with no "inputs"
|
||||
@@ -330,6 +363,8 @@ class AstNodeTriop VL_NOT_FINAL : public AstNodeExpr {
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
// @astgen op2 := rhsp : AstNodeExpr
|
||||
// @astgen op3 := thsp : AstNodeExpr
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
protected:
|
||||
AstNodeTriop(VNType t, FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp)
|
||||
: AstNodeExpr{t, fl} {
|
||||
@@ -354,20 +389,21 @@ public:
|
||||
virtual bool sizeMattersThs() const = 0; // True if output result depends on ths size
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool same(const AstNode*) const override { return true; }
|
||||
bool isPure() override;
|
||||
const char* broken() const override;
|
||||
|
||||
private:
|
||||
bool getPurityRecurse() const {
|
||||
return lhsp()->isPure() && rhsp()->isPure() && thsp()->isPure();
|
||||
}
|
||||
};
|
||||
class AstNodeCond VL_NOT_FINAL : public AstNodeTriop {
|
||||
// @astgen alias op1 := condp
|
||||
// @astgen alias op2 := thenp
|
||||
// @astgen alias op3 := elsep
|
||||
protected:
|
||||
AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep)
|
||||
: AstNodeTriop{t, fl, condp, thenp, elsep} {
|
||||
if (thenp) {
|
||||
dtypeFrom(thenp);
|
||||
} else if (elsep) {
|
||||
dtypeFrom(elsep);
|
||||
}
|
||||
}
|
||||
AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp,
|
||||
AstNodeExpr* elsep);
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstNodeCond;
|
||||
@@ -409,6 +445,8 @@ public:
|
||||
class AstNodeUniop VL_NOT_FINAL : public AstNodeExpr {
|
||||
// Unary expression
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
protected:
|
||||
AstNodeUniop(VNType t, FileLine* fl, AstNodeExpr* lhsp)
|
||||
: AstNodeExpr{t, fl} {
|
||||
@@ -430,6 +468,8 @@ public:
|
||||
virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors?
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool same(const AstNode*) const override { return true; }
|
||||
bool isPure() override;
|
||||
const char* broken() const override;
|
||||
};
|
||||
class AstNodeSystemUniopD VL_NOT_FINAL : public AstNodeUniop {
|
||||
public:
|
||||
@@ -450,8 +490,9 @@ class AstNodeVarRef VL_NOT_FINAL : public AstNodeExpr {
|
||||
AstVar* m_varp; // [AfterLink] Pointer to variable itself
|
||||
AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the variable
|
||||
string m_selfPointer; // Output code object pointer (e.g.: 'this')
|
||||
|
||||
VSelfPointerText m_selfPointer
|
||||
= VSelfPointerText{VSelfPointerText::Empty()}; // Output code object
|
||||
// pointer (e.g.: 'this')
|
||||
protected:
|
||||
AstNodeVarRef(VNType t, FileLine* fl, const VAccess& access)
|
||||
: AstNodeExpr{t, fl}
|
||||
@@ -480,9 +521,11 @@ public:
|
||||
}
|
||||
AstVarScope* varScopep() const { return m_varScopep; }
|
||||
void varScopep(AstVarScope* varscp) { m_varScopep = varscp; }
|
||||
string selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const string& value) { m_selfPointer = value; }
|
||||
string selfPointerProtect(bool useSelfForThis) const;
|
||||
const VSelfPointerText& selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
|
||||
string selfPointerProtect(bool useSelfForThis) const {
|
||||
return selfPointer().protect(useSelfForThis, protect());
|
||||
}
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
// Know no children, and hot function, so skip iterator for speed
|
||||
@@ -577,6 +620,7 @@ public:
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
bool isPure() override { return pure(); }
|
||||
bool pure() const { return m_pure; }
|
||||
void pure(bool flag) { m_pure = flag; }
|
||||
};
|
||||
@@ -588,35 +632,29 @@ class AstCMethodHard final : public AstNodeExpr {
|
||||
string m_name; // Name of method
|
||||
bool m_pure = false; // Pure optimizable
|
||||
public:
|
||||
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name,
|
||||
AstNodeExpr* pinsp = nullptr)
|
||||
: ASTGEN_SUPER_CMethodHard(fl)
|
||||
, m_name{name} {
|
||||
// TODO: this constructor is exactly the same as the other, bar the ignored tag argument
|
||||
this->fromp(fromp);
|
||||
this->addPinsp(pinsp);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, const string& name,
|
||||
AstNodeExpr* pinsp = nullptr)
|
||||
: ASTGEN_SUPER_CMethodHard(fl)
|
||||
, m_name{name} {
|
||||
this->fromp(fromp);
|
||||
this->addPinsp(pinsp);
|
||||
setPurity();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCMethodHard;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstCMethodHard* asamep = static_cast<const AstCMethodHard*>(samep);
|
||||
const AstCMethodHard* const asamep = VN_DBG_AS(samep, CMethodHard);
|
||||
return (m_name == asamep->m_name);
|
||||
}
|
||||
bool isPure() const override { return m_pure; }
|
||||
void pure(bool flag) { m_pure = flag; }
|
||||
bool isPure() override { return m_pure; }
|
||||
int instrCount() const override;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
|
||||
private:
|
||||
void setPurity();
|
||||
};
|
||||
class AstCast final : public AstNodeExpr {
|
||||
// Cast to appropriate data type
|
||||
@@ -738,7 +776,7 @@ public:
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
return (m_classOrPackageNodep
|
||||
== static_cast<const AstClassOrPackageRef*>(samep)->m_classOrPackageNodep);
|
||||
== VN_DBG_AS(samep, ClassOrPackageRef)->m_classOrPackageNodep);
|
||||
}
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
@@ -1009,9 +1047,14 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstConst* const sp = static_cast<const AstConst*>(samep);
|
||||
const AstConst* const sp = VN_DBG_AS(samep, Const);
|
||||
return num().isCaseEq(sp->num());
|
||||
}
|
||||
void cloneRelink() override { m_num.nodep(this); }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(m_num.nodep() && m_num.nodep() != this);
|
||||
return nullptr;
|
||||
}
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool isEqAllOnes() const { return num().isEqAllOnes(width()); }
|
||||
bool isEqAllOnesV() const { return num().isEqAllOnes(widthMinV()); }
|
||||
@@ -1019,6 +1062,34 @@ public:
|
||||
// May return nullptr on parse failure.
|
||||
static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
|
||||
};
|
||||
class AstCvtDynArrayToPacked final : public AstNodeExpr {
|
||||
// Cast from dynamic queue data type to packed array
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtDynArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtDynArrayToPacked(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtDynArrayToPacked;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstCvtPackedToDynArray final : public AstNodeExpr {
|
||||
// Cast from packed array to dynamic queue data type
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtPackedToDynArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtPackedToDynArray(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtPackedToDynArray;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstDot final : public AstNodeExpr {
|
||||
// A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef
|
||||
// These are eliminated in the link stage
|
||||
@@ -1070,11 +1141,9 @@ public:
|
||||
string name() const override VL_MT_STABLE { return itemp()->name(); }
|
||||
int instrCount() const override { return 0; }
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override {
|
||||
if (m_itemp->clonep()) m_itemp = m_itemp->clonep();
|
||||
}
|
||||
void cloneRelink() override;
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstEnumItemRef* const sp = static_cast<const AstEnumItemRef*>(samep);
|
||||
const AstEnumItemRef* const sp = VN_DBG_AS(samep, EnumItemRef);
|
||||
return itemp() == sp->itemp();
|
||||
}
|
||||
AstEnumItem* itemp() const VL_MT_STABLE { return m_itemp; }
|
||||
@@ -1102,6 +1171,10 @@ public:
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool isPure() override {
|
||||
if (AstNode::afterCommentp(stmtsp())) return false;
|
||||
return resultp()->isPure();
|
||||
}
|
||||
bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
class AstFError final : public AstNodeExpr {
|
||||
@@ -1119,7 +1192,7 @@ public:
|
||||
bool cleanOut() const override { return true; }
|
||||
int instrCount() const override { return widthInstrs() * 64; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstFOpen final : public AstNodeExpr {
|
||||
@@ -1138,8 +1211,8 @@ public:
|
||||
string verilogKwd() const override { return "$fopen"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
@@ -1157,8 +1230,8 @@ public:
|
||||
string verilogKwd() const override { return "$fopen"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
@@ -1181,8 +1254,8 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering
|
||||
bool isOutputter() const override { return true; } // SPECIAL: makes output
|
||||
bool isPure() override { return false; } // SPECIAL: has 'visual' ordering
|
||||
bool isOutputter() override { return true; } // SPECIAL: makes output
|
||||
bool cleanOut() const override { return false; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
@@ -1199,8 +1272,8 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
@@ -1224,11 +1297,11 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering
|
||||
bool isOutputter() const override { return true; } // SPECIAL: makes output
|
||||
bool isPure() override { return false; } // SPECIAL: has 'visual' ordering
|
||||
bool isOutputter() override { return true; } // SPECIAL: makes output
|
||||
bool cleanOut() const override { return false; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return text() == static_cast<const AstFScanF*>(samep)->text();
|
||||
return text() == VN_DBG_AS(samep, FScanF)->text();
|
||||
}
|
||||
string text() const { return m_text; } // * = Text to display
|
||||
void text(const string& text) { m_text = text; }
|
||||
@@ -1250,8 +1323,8 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering
|
||||
bool isOutputter() const override { return true; } // SPECIAL: makes output
|
||||
bool isPure() override { return false; } // SPECIAL: has 'visual' ordering
|
||||
bool isOutputter() override { return true; } // SPECIAL: makes output
|
||||
bool cleanOut() const override { return false; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
@@ -1268,8 +1341,8 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
@@ -1308,7 +1381,9 @@ public:
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstImplication final : public AstNodeExpr {
|
||||
// Verilog |-> |=>
|
||||
// Verilog Implication Operator
|
||||
// Nonoverlapping "|=>"
|
||||
// Overlapping "|->" (doesn't currently use this - might make new Ast type)
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
// @astgen op2 := rhsp : AstNodeExpr
|
||||
// @astgen op3 := sentreep : Optional[AstSenTree]
|
||||
@@ -1355,7 +1430,7 @@ public:
|
||||
bool same(const AstNode* samep) const override {
|
||||
// Only works if exact same children, instead should override comparison
|
||||
// of children list, and instead use map-vs-map key/value compare
|
||||
return m_map == static_cast<const AstInitArray*>(samep)->m_map;
|
||||
return m_map == VN_DBG_AS(samep, InitArray)->m_map;
|
||||
}
|
||||
void addValuep(AstNodeExpr* newp) { addIndexValuep(m_map.size(), newp); }
|
||||
const KeyItemMap& map() const { return m_map; }
|
||||
@@ -1511,7 +1586,7 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstParseRef* const asamep = static_cast<const AstParseRef*>(samep);
|
||||
const AstParseRef* const asamep = VN_DBG_AS(samep, ParseRef);
|
||||
return (expect() == asamep->expect() && m_name == asamep->m_name);
|
||||
}
|
||||
void name(const string& name) override { m_name = name; }
|
||||
@@ -1706,7 +1781,7 @@ public:
|
||||
string name() const override VL_MT_STABLE { return m_text; }
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return text() == static_cast<const AstSFormatF*>(samep)->text();
|
||||
return text() == VN_DBG_AS(samep, SFormatF)->text();
|
||||
}
|
||||
string verilogKwd() const override { return "$sformatf"; }
|
||||
string text() const { return m_text; } // * = Text to display
|
||||
@@ -1744,11 +1819,11 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering
|
||||
bool isOutputter() const override { return true; } // SPECIAL: makes output
|
||||
bool isPure() override { return false; } // SPECIAL: has 'visual' ordering
|
||||
bool isOutputter() override { return true; } // SPECIAL: makes output
|
||||
bool cleanOut() const override { return false; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return text() == static_cast<const AstSScanF*>(samep)->text();
|
||||
return text() == VN_DBG_AS(samep, SScanF)->text();
|
||||
}
|
||||
string text() const { return m_text; } // * = Text to display
|
||||
void text(const string& text) { m_text = text; }
|
||||
@@ -1788,8 +1863,8 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstScopeName;
|
||||
bool same(const AstNode* samep) const override {
|
||||
return (m_dpiExport == static_cast<const AstScopeName*>(samep)->m_dpiExport
|
||||
&& m_forFormat == static_cast<const AstScopeName*>(samep)->m_forFormat);
|
||||
const AstScopeName* const sp = VN_DBG_AS(samep, ScopeName);
|
||||
return (m_dpiExport == sp->m_dpiExport && m_forFormat == sp->m_forFormat);
|
||||
}
|
||||
string emitVerilog() override { return ""; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -1902,8 +1977,8 @@ public:
|
||||
string emitC() override { return "VL_STACKTRACE_N()"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
@@ -1932,7 +2007,7 @@ public:
|
||||
return VN_IS(fromp()->dtypep()->skipRefp(), StructDType);
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstStructSel* const sp = static_cast<const AstStructSel*>(samep);
|
||||
const AstStructSel* const sp = VN_DBG_AS(samep, StructSel);
|
||||
return m_name == sp->m_name;
|
||||
}
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
@@ -1948,8 +2023,8 @@ public:
|
||||
string verilogKwd() const override { return "$ignored"; }
|
||||
bool isGateOptimizable() const override { return false; } // Though deleted before opt
|
||||
bool isPredictOptimizable() const override { return false; } // Though deleted before opt
|
||||
bool isPure() const override { return false; } // Though deleted before opt
|
||||
bool isOutputter() const override { return true; } // Though deleted before opt
|
||||
bool isPure() override { return false; } // Though deleted before opt
|
||||
bool isOutputter() override { return true; } // Though deleted before opt
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -1970,8 +2045,8 @@ public:
|
||||
string emitC() override { return "VL_SYSTEM_%nq(%lw, %P)"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
@@ -2054,8 +2129,8 @@ public:
|
||||
bool cleanOut() const override { return false; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() override { return true; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isSubstOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
@@ -2110,7 +2185,7 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return !outp(); }
|
||||
bool isPure() override { return !outp(); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
@@ -2217,7 +2292,7 @@ public:
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() * 20; }
|
||||
bool isPure() const override { return true; }
|
||||
bool isPure() override { return true; }
|
||||
};
|
||||
class AstCompareNN final : public AstNodeBiop {
|
||||
// Verilog str.compare() and str.icompare()
|
||||
@@ -2441,7 +2516,7 @@ public:
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() * 64; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
AstNode* filep() const { return lhsp(); }
|
||||
AstNode* charp() const { return rhsp(); }
|
||||
};
|
||||
@@ -2726,11 +2801,6 @@ public:
|
||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
};
|
||||
class AstLogOr final : public AstNodeBiop {
|
||||
// LOGOR with optional side effects
|
||||
// Side effects currently used in some V3Width code
|
||||
// TBD if this concept is generally adopted for side-effect tracking
|
||||
// versus V3Const tracking it itself
|
||||
bool m_sideEffect = false; // Has side effect, relies on short-circuiting
|
||||
public:
|
||||
AstLogOr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_LogOr(fl, lhsp, rhsp) {
|
||||
@@ -2743,11 +2813,6 @@ public:
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opLogOr(lhs, rhs);
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstLogOr* const sp = static_cast<const AstLogOr*>(samep);
|
||||
return m_sideEffect == sp->m_sideEffect;
|
||||
}
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
string emitVerilog() override { return "%k(%l %f|| %r)"; }
|
||||
string emitC() override { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSimpleOperator() override { return "||"; }
|
||||
@@ -2757,9 +2822,6 @@ public:
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
bool isPure() const override { return !m_sideEffect; }
|
||||
void sideEffect(bool flag) { m_sideEffect = flag; }
|
||||
bool sideEffect() const { return m_sideEffect; }
|
||||
};
|
||||
class AstLt final : public AstNodeBiop {
|
||||
public:
|
||||
@@ -3214,6 +3276,30 @@ public:
|
||||
bool sizeMattersLhs() const override { return true; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
};
|
||||
class AstShiftLOvr final : public AstNodeBiop {
|
||||
// Like ShiftL but checks for an over shift and returns zeros
|
||||
// TODO: All Verilog shifts should start as these and later get demoted to AstShiftL
|
||||
public:
|
||||
AstShiftLOvr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
|
||||
: ASTGEN_SUPER_ShiftLOvr(fl, lhsp, rhsp) {
|
||||
if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstShiftLOvr;
|
||||
AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
|
||||
return new AstShiftLOvr{fileline(), lhsp, rhsp};
|
||||
}
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opShiftL(lhs, rhs);
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f<< %r)"; }
|
||||
string emitC() override { return "VL_SHIFTL_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return false; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return true; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
};
|
||||
class AstShiftR final : public AstNodeBiop {
|
||||
public:
|
||||
AstShiftR(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
|
||||
@@ -3239,6 +3325,31 @@ public:
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
};
|
||||
class AstShiftROvr final : public AstNodeBiop {
|
||||
// Like ShiftR but checks for an over shift and returns zeros
|
||||
// TODO: All Verilog shifts should start as these and later get demoted to AstShiftR
|
||||
public:
|
||||
AstShiftROvr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
|
||||
: ASTGEN_SUPER_ShiftROvr(fl, lhsp, rhsp) {
|
||||
if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstShiftROvr;
|
||||
AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
|
||||
return new AstShiftROvr{fileline(), lhsp, rhsp};
|
||||
}
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opShiftR(lhs, rhs);
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f>> %r)"; }
|
||||
string emitC() override { return "VL_SHIFTR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
// LHS size might be > output size, so don't want to force size
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
};
|
||||
class AstShiftRS final : public AstNodeBiop {
|
||||
// Shift right with sign extension, >>> operator
|
||||
// Output data type's width determines which bit is used for sign extension
|
||||
@@ -3265,6 +3376,33 @@ public:
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
bool signedFlavor() const override { return true; }
|
||||
};
|
||||
class AstShiftRSOvr final : public AstNodeBiop {
|
||||
// Shift right with sign extension, >>> operator, checks for an over shift and returns zeros
|
||||
// Output data type's width determines which bit is used for sign extension
|
||||
// TODO: All Verilog shifts should start as these and later get demoted to AstShiftRSOvr
|
||||
public:
|
||||
AstShiftRSOvr(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, int setwidth = 0)
|
||||
: ASTGEN_SUPER_ShiftRSOvr(fl, lhsp, rhsp) {
|
||||
// Important that widthMin be correct, as opExtend requires it after V3Expand
|
||||
if (setwidth) dtypeSetLogicSized(setwidth, VSigning::SIGNED);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstShiftRSOvr;
|
||||
AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override {
|
||||
return new AstShiftRSOvr{fileline(), lhsp, rhsp};
|
||||
}
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opShiftRS(lhs, rhs, lhsp()->widthMinV());
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %f>>> %r)"; }
|
||||
string emitC() override { return "VL_SHIFTRS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
bool signedFlavor() const override { return true; }
|
||||
};
|
||||
class AstSub final : public AstNodeBiop {
|
||||
public:
|
||||
AstSub(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
@@ -4023,16 +4161,19 @@ public:
|
||||
// === AstNodeCCall ===
|
||||
class AstCCall final : public AstNodeCCall {
|
||||
// C++ function call
|
||||
string m_selfPointer; // Output code object pointer (e.g.: 'this')
|
||||
|
||||
VSelfPointerText m_selfPointer
|
||||
= VSelfPointerText{VSelfPointerText::Empty()}; // Output code object
|
||||
// pointer (e.g.: 'this')
|
||||
public:
|
||||
AstCCall(FileLine* fl, AstCFunc* funcp, AstNodeExpr* argsp = nullptr)
|
||||
: ASTGEN_SUPER_CCall(fl, funcp, argsp) {}
|
||||
ASTGEN_MEMBERS_AstCCall;
|
||||
|
||||
string selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const string& value) { m_selfPointer = value; }
|
||||
string selfPointerProtect(bool useSelfForThis) const;
|
||||
const VSelfPointerText& selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
|
||||
string selfPointerProtect(bool useSelfForThis) const {
|
||||
return selfPointer().protect(useSelfForThis, protect());
|
||||
}
|
||||
};
|
||||
class AstCMethodCall final : public AstNodeCCall {
|
||||
// C++ method call
|
||||
@@ -4060,12 +4201,15 @@ public:
|
||||
// === AstNodeFTaskRef ===
|
||||
class AstFuncRef final : public AstNodeFTaskRef {
|
||||
// A reference to a function
|
||||
bool m_superReference = false; // Called with super reference
|
||||
public:
|
||||
AstFuncRef(FileLine* fl, AstParseRef* namep, AstNodeExpr* pinsp)
|
||||
: ASTGEN_SUPER_FuncRef(fl, (AstNode*)namep, pinsp) {}
|
||||
AstFuncRef(FileLine* fl, const string& name, AstNodeExpr* pinsp)
|
||||
: ASTGEN_SUPER_FuncRef(fl, name, pinsp) {}
|
||||
ASTGEN_MEMBERS_AstFuncRef;
|
||||
bool superReference() const { return m_superReference; }
|
||||
void superReference(bool flag) { m_superReference = flag; }
|
||||
};
|
||||
class AstMethodCall final : public AstNodeFTaskRef {
|
||||
// A reference to a member task (or function)
|
||||
@@ -4102,6 +4246,7 @@ public:
|
||||
};
|
||||
class AstTaskRef final : public AstNodeFTaskRef {
|
||||
// A reference to a task
|
||||
bool m_superReference = false; // Called with super reference
|
||||
public:
|
||||
AstTaskRef(FileLine* fl, AstParseRef* namep, AstNodeExpr* pinsp)
|
||||
: ASTGEN_SUPER_TaskRef(fl, (AstNode*)namep, pinsp) {
|
||||
@@ -4112,6 +4257,8 @@ public:
|
||||
dtypeSetVoid();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstTaskRef;
|
||||
bool superReference() const { return m_superReference; }
|
||||
void superReference(bool flag) { m_superReference = flag; }
|
||||
};
|
||||
|
||||
// === AstNodePreSel ===
|
||||
@@ -4162,10 +4309,10 @@ class AstCountBits final : public AstNodeQuadop {
|
||||
// Number of bits set in vector
|
||||
public:
|
||||
AstCountBits(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* ctrl1p)
|
||||
: ASTGEN_SUPER_CountBits(fl, exprp, ctrl1p, ctrl1p->cloneTree(false),
|
||||
ctrl1p->cloneTree(false)) {}
|
||||
: ASTGEN_SUPER_CountBits(fl, exprp, ctrl1p, ctrl1p->cloneTreePure(false),
|
||||
ctrl1p->cloneTreePure(false)) {}
|
||||
AstCountBits(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* ctrl1p, AstNodeExpr* ctrl2p)
|
||||
: ASTGEN_SUPER_CountBits(fl, exprp, ctrl1p, ctrl2p, ctrl2p->cloneTree(false)) {}
|
||||
: ASTGEN_SUPER_CountBits(fl, exprp, ctrl1p, ctrl2p, ctrl2p->cloneTreePure(false)) {}
|
||||
AstCountBits(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* ctrl1p, AstNodeExpr* ctrl2p,
|
||||
AstNodeExpr* ctrl3p)
|
||||
: ASTGEN_SUPER_CountBits(fl, exprp, ctrl1p, ctrl2p, ctrl3p) {}
|
||||
@@ -4612,7 +4759,7 @@ public:
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; } // Special cased in V3Cast
|
||||
bool same(const AstNode* samep) const override {
|
||||
return size() == static_cast<const AstCCast*>(samep)->size();
|
||||
return size() == VN_DBG_AS(samep, CCast)->size();
|
||||
}
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
//
|
||||
@@ -4735,7 +4882,7 @@ public:
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() * 16; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
AstNode* filep() const { return lhsp(); }
|
||||
};
|
||||
class AstFGetC final : public AstNodeUniop {
|
||||
@@ -4752,7 +4899,7 @@ public:
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() * 64; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
AstNode* filep() const { return lhsp(); }
|
||||
};
|
||||
class AstISToRD final : public AstNodeUniop {
|
||||
@@ -5058,6 +5205,21 @@ public:
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
int instrCount() const override { return 1 + V3Number::log2b(width()); }
|
||||
};
|
||||
class AstResizeLValue final : public AstNodeUniop {
|
||||
// Resize a LValue into a wider/narrower entity at function argument boundry
|
||||
// Width and signness is implied from nodep->width()
|
||||
public:
|
||||
AstResizeLValue(FileLine* fl, AstNodeExpr* lhsp)
|
||||
: ASTGEN_SUPER_ResizeLValue(fl, lhsp) {}
|
||||
ASTGEN_MEMBERS_AstResizeLValue;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { V3ERROR_NA; }
|
||||
string emitVerilog() override { return "%l"; }
|
||||
string emitC() final override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
int instrCount() const override { return 0; }
|
||||
};
|
||||
class AstSigned final : public AstNodeUniop {
|
||||
// $signed(lhs)
|
||||
public:
|
||||
@@ -5391,7 +5553,7 @@ public:
|
||||
bool cleanOut() const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstVarXRef* asamep = static_cast<const AstVarXRef*>(samep);
|
||||
const AstVarXRef* asamep = VN_DBG_AS(samep, VarXRef);
|
||||
return (selfPointer() == asamep->selfPointer() && varp() == asamep->varp()
|
||||
&& name() == asamep->name() && dotted() == asamep->dotted());
|
||||
}
|
||||
|
||||
+205
-122
@@ -32,7 +32,7 @@
|
||||
|
||||
class AstNodeBlock VL_NOT_FINAL : public AstNode {
|
||||
// A Begin/fork block
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
// @astgen op2 := stmtsp : List[AstNode]
|
||||
// Parents: statement
|
||||
private:
|
||||
string m_name; // Name of block
|
||||
@@ -81,13 +81,16 @@ private:
|
||||
bool m_isConstructor : 1; // Class constructor
|
||||
bool m_isHideLocal : 1; // Verilog local
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
bool m_pure : 1; // DPI import pure (vs. virtual pure)
|
||||
bool m_dpiPure : 1; // DPI import pure (vs. virtual pure)
|
||||
bool m_pureVirtual : 1; // Pure virtual
|
||||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
bool m_static : 1; // Static method in class
|
||||
bool m_underGenerate : 1; // Under generate (for warning)
|
||||
bool m_virtual : 1; // Virtual method in class
|
||||
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
bool m_needProcess : 1; // Needs access to VlProcess of the caller
|
||||
VLifetime m_lifetime; // Default lifetime of local vars
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
protected:
|
||||
AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp)
|
||||
: AstNode{t, fl}
|
||||
@@ -107,9 +110,10 @@ protected:
|
||||
, m_isConstructor{false}
|
||||
, m_isHideLocal{false}
|
||||
, m_isHideProtected{false}
|
||||
, m_pure{false}
|
||||
, m_dpiPure{false}
|
||||
, m_pureVirtual{false}
|
||||
, m_recursive{false}
|
||||
, m_static{false}
|
||||
, m_underGenerate{false}
|
||||
, m_virtual{false}
|
||||
, m_needProcess{false} {
|
||||
@@ -119,10 +123,13 @@ protected:
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstNodeFTask;
|
||||
virtual AstNodeFTask* cloneType(const string& name) = 0;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool isGateOptimizable() const override { return !((m_dpiExport || m_dpiImport) && !m_pure); }
|
||||
bool isGateOptimizable() const override {
|
||||
return !((m_dpiExport || m_dpiImport) && !m_dpiPure);
|
||||
}
|
||||
// {AstFunc only} op1 = Range output variable
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string cname() const { return m_cname; }
|
||||
@@ -162,12 +169,14 @@ public:
|
||||
void isHideLocal(bool flag) { m_isHideLocal = flag; }
|
||||
bool isHideProtected() const { return m_isHideProtected; }
|
||||
void isHideProtected(bool flag) { m_isHideProtected = flag; }
|
||||
void pure(bool flag) { m_pure = flag; }
|
||||
bool pure() const { return m_pure; }
|
||||
void dpiPure(bool flag) { m_dpiPure = flag; }
|
||||
bool dpiPure() const { return m_dpiPure; }
|
||||
void pureVirtual(bool flag) { m_pureVirtual = flag; }
|
||||
bool pureVirtual() const { return m_pureVirtual; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
void isStatic(bool flag) { m_static = flag; }
|
||||
bool isStatic() const { return m_static; }
|
||||
void underGenerate(bool flag) { m_underGenerate = flag; }
|
||||
bool underGenerate() const { return m_underGenerate; }
|
||||
void isVirtual(bool flag) { m_virtual = flag; }
|
||||
@@ -177,6 +186,20 @@ public:
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
bool isPure() override;
|
||||
const char* broken() const override;
|
||||
void propagateAttrFrom(const AstNodeFTask* fromp) {
|
||||
// Creating a wrapper with e.g. cloneType(); preserve some attributes
|
||||
classMethod(fromp->classMethod());
|
||||
isHideLocal(fromp->isHideLocal());
|
||||
isHideProtected(fromp->isHideProtected());
|
||||
isVirtual(fromp->isVirtual());
|
||||
lifetime(fromp->lifetime());
|
||||
underGenerate(fromp->underGenerate());
|
||||
}
|
||||
|
||||
private:
|
||||
bool getPurityRecurse() const;
|
||||
};
|
||||
class AstNodeFile VL_NOT_FINAL : public AstNode {
|
||||
// Emitted Output file
|
||||
@@ -215,6 +238,7 @@ private:
|
||||
bool m_inLibrary : 1; // From a library, no error if not used, never top level
|
||||
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
|
||||
bool m_hierBlock : 1; // Hierarchical Block marked by HIER_BLOCK pragma
|
||||
bool m_internal : 1; // Internally created
|
||||
bool m_recursive : 1; // Recursive module
|
||||
@@ -229,6 +253,7 @@ protected:
|
||||
, m_inLibrary{false}
|
||||
, m_dead{false}
|
||||
, m_hasGParam{false}
|
||||
, m_hasParameterList{false}
|
||||
, m_hierBlock{false}
|
||||
, m_internal{false}
|
||||
, m_recursive{false}
|
||||
@@ -258,6 +283,8 @@ public:
|
||||
bool dead() const { return m_dead; }
|
||||
void hasGParam(bool flag) { m_hasGParam = flag; }
|
||||
bool hasGParam() const { return m_hasGParam; }
|
||||
void hasParameterList(bool flag) { m_hasParameterList = flag; }
|
||||
bool hasParameterList() const { return m_hasParameterList; }
|
||||
void hierBlock(bool flag) { m_hierBlock = flag; }
|
||||
bool hierBlock() const { return m_hierBlock; }
|
||||
void internal(bool flag) { m_internal = flag; }
|
||||
@@ -277,7 +304,7 @@ class AstNodeProcedure VL_NOT_FINAL : public AstNode {
|
||||
// IEEE procedure: initial, final, always
|
||||
// @astgen op2 := stmtsp : List[AstNode] // Note: op1 is used in some sub-types only
|
||||
bool m_suspendable : 1; // Is suspendable by a Delay, EventControl, etc.
|
||||
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
||||
bool m_needProcess : 1; // Uses VlProcess
|
||||
protected:
|
||||
AstNodeProcedure(VNType t, FileLine* fl, AstNode* stmtsp)
|
||||
: AstNode{t, fl} {
|
||||
@@ -457,11 +484,11 @@ public:
|
||||
ASTGEN_MEMBERS_AstNodeReadWriteMem;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return isHex() == static_cast<const AstNodeReadWriteMem*>(samep)->isHex();
|
||||
return isHex() == VN_DBG_AS(samep, NodeReadWriteMem)->isHex();
|
||||
}
|
||||
bool isHex() const { return m_isHex; }
|
||||
virtual const char* cFuncPrefixp() const = 0;
|
||||
@@ -480,7 +507,7 @@ public:
|
||||
ASTGEN_MEMBERS_AstNodeText;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstNodeText* asamep = static_cast<const AstNodeText*>(samep);
|
||||
const AstNodeText* asamep = VN_DBG_AS(samep, NodeText);
|
||||
return text() == asamep->text();
|
||||
}
|
||||
const string& text() const VL_MT_SAFE { return m_text; }
|
||||
@@ -578,14 +605,15 @@ private:
|
||||
bool m_isInline : 1; // Inline function
|
||||
bool m_isVirtual : 1; // Virtual function
|
||||
bool m_entryPoint : 1; // User may call into this top level function
|
||||
bool m_pure : 1; // Pure function
|
||||
bool m_dpiPure : 1; // Pure DPI function
|
||||
bool m_dpiContext : 1; // Declared as 'context' DPI import/export function
|
||||
bool m_dpiExportDispatcher : 1; // This is the DPI export entry point (i.e.: called by user)
|
||||
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
|
||||
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
|
||||
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
|
||||
bool m_dpiTraceInit : 1; // DPI trace_init
|
||||
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
||||
bool m_needProcess : 1; // Needs access to VlProcess of the caller
|
||||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
public:
|
||||
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
|
||||
: ASTGEN_SUPER_CFunc(fl) {
|
||||
@@ -607,13 +635,14 @@ public:
|
||||
m_isVirtual = false;
|
||||
m_needProcess = false;
|
||||
m_entryPoint = false;
|
||||
m_pure = false;
|
||||
m_dpiPure = false;
|
||||
m_dpiContext = false;
|
||||
m_dpiExportDispatcher = false;
|
||||
m_dpiExportImpl = false;
|
||||
m_dpiImportPrototype = false;
|
||||
m_dpiImportWrapper = false;
|
||||
m_dpiTraceInit = false;
|
||||
m_recursive = false;
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCFunc;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
@@ -622,7 +651,7 @@ public:
|
||||
bool maybePointedTo() const override { return true; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstCFunc* const asamep = static_cast<const AstCFunc*>(samep);
|
||||
const AstCFunc* const asamep = VN_DBG_AS(samep, CFunc);
|
||||
return ((isTrace() == asamep->isTrace()) && (rtnTypeVoid() == asamep->rtnTypeVoid())
|
||||
&& (argTypes() == asamep->argTypes()) && (baseCtors() == asamep->baseCtors())
|
||||
&& isLoose() == asamep->isLoose()
|
||||
@@ -678,8 +707,8 @@ public:
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool entryPoint() const { return m_entryPoint; }
|
||||
void entryPoint(bool flag) { m_entryPoint = flag; }
|
||||
bool pure() const { return m_pure; }
|
||||
void pure(bool flag) { m_pure = flag; }
|
||||
bool dpiPure() const { return m_dpiPure; }
|
||||
void dpiPure(bool flag) { m_dpiPure = flag; }
|
||||
bool dpiContext() const { return m_dpiContext; }
|
||||
void dpiContext(bool flag) { m_dpiContext = flag; }
|
||||
bool dpiExportDispatcher() const VL_MT_SAFE { return m_dpiExportDispatcher; }
|
||||
@@ -693,12 +722,24 @@ public:
|
||||
void dpiTraceInit(bool flag) { m_dpiTraceInit = flag; }
|
||||
bool dpiTraceInit() const { return m_dpiTraceInit; }
|
||||
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
bool recursive() const { return m_recursive; }
|
||||
// Special methods
|
||||
bool emptyBody() const {
|
||||
return argsp() == nullptr && initsp() == nullptr && stmtsp() == nullptr
|
||||
&& finalsp() == nullptr;
|
||||
}
|
||||
};
|
||||
class AstCLocalScope final : public AstNode {
|
||||
// Pack statements into an unnamed scope when generating C++
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
public:
|
||||
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_CLocalScope(fl) {
|
||||
this->addStmtsp(stmtsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCLocalScope;
|
||||
};
|
||||
class AstCUse final : public AstNode {
|
||||
// C++ use of a class or #include; indicates need of forward declaration
|
||||
// Parents: NODEMODULE
|
||||
@@ -736,13 +777,14 @@ class AstCell final : public AstNode {
|
||||
// @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 op4 := intfRefsp : List[AstIntfRef] // List of interface references
|
||||
// @astgen op4 := intfRefsp : List[AstIntfRef] // List of interface references, for tracing
|
||||
FileLine* m_modNameFileline; // Where module the cell instances token was
|
||||
string m_name; // Cell name
|
||||
string m_origName; // Original name before dot addition
|
||||
string m_modName; // Module the cell instances
|
||||
AstNodeModule* m_modp = nullptr; // [AfterLink] Pointer to module instanced
|
||||
bool m_hasIfaceVar : 1; // True if a Var has been created for this cell
|
||||
bool m_hasNoParens : 1; // Instantiation has no parenthesis
|
||||
bool m_recursive : 1; // Self-recursive module
|
||||
bool m_trace : 1; // Trace this cell
|
||||
public:
|
||||
@@ -754,6 +796,7 @@ public:
|
||||
, m_origName{instName}
|
||||
, m_modName{modName}
|
||||
, m_hasIfaceVar{false}
|
||||
, m_hasNoParens{false}
|
||||
, m_recursive{false}
|
||||
, m_trace{true} {
|
||||
this->addPinsp(pinsp);
|
||||
@@ -777,6 +820,8 @@ public:
|
||||
void modp(AstNodeModule* nodep) { m_modp = nodep; }
|
||||
bool hasIfaceVar() const { return m_hasIfaceVar; }
|
||||
void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; }
|
||||
bool hasNoParens() const { return m_hasNoParens; }
|
||||
void hasNoParens(bool flag) { m_hasNoParens = flag; }
|
||||
void trace(bool flag) { m_trace = flag; }
|
||||
bool isTrace() const { return m_trace; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
@@ -972,11 +1017,11 @@ public:
|
||||
string verilogKwd() const override { return string{"$"} + string{displayType().ascii()}; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isOutputter() const override { return true; } // SPECIAL: $display makes output
|
||||
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isOutputter() override { return true; } // SPECIAL: $display makes output
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return displayType() == static_cast<const AstElabDisplay*>(samep)->displayType();
|
||||
return displayType() == VN_DBG_AS(samep, ElabDisplay)->displayType();
|
||||
}
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
VDisplayType displayType() const { return m_displayType; }
|
||||
@@ -1005,7 +1050,7 @@ class AstExecGraph final : public AstNode {
|
||||
const string m_name; // Name of this AstExecGraph (for uniqueness at code generation)
|
||||
|
||||
public:
|
||||
explicit AstExecGraph(FileLine* fl, const string& name);
|
||||
explicit AstExecGraph(FileLine* fl, const string& name) VL_MT_DISABLED;
|
||||
ASTGEN_MEMBERS_AstExecGraph;
|
||||
~AstExecGraph() override;
|
||||
const char* broken() const override {
|
||||
@@ -1154,6 +1199,8 @@ class AstNetlist final : public AstNode {
|
||||
AstCFunc* m_evalNbap = nullptr; // The '_eval__nba' function
|
||||
AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export trigger variable
|
||||
AstVar* m_delaySchedulerp = nullptr; // The delay scheduler variable
|
||||
AstVarScope* m_nbaEventp = nullptr; // The NBA event variable
|
||||
AstVarScope* m_nbaEventTriggerp = nullptr; // If set to 1, the NBA event should get triggered
|
||||
AstTopScope* m_topScopep = nullptr; // The singleton AstTopScope under the top module
|
||||
VTimescale m_timeunit; // Global time unit
|
||||
VTimescale m_timeprecision; // Global time precision
|
||||
@@ -1183,6 +1230,10 @@ public:
|
||||
void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; }
|
||||
AstVar* delaySchedulerp() const { return m_delaySchedulerp; }
|
||||
void delaySchedulerp(AstVar* const varScopep) { m_delaySchedulerp = varScopep; }
|
||||
AstVarScope* nbaEventp() const { return m_nbaEventp; }
|
||||
void nbaEventp(AstVarScope* const varScopep) { m_nbaEventp = varScopep; }
|
||||
AstVarScope* nbaEventTriggerp() const { return m_nbaEventTriggerp; }
|
||||
void nbaEventTriggerp(AstVarScope* const varScopep) { m_nbaEventTriggerp = varScopep; }
|
||||
void stdPackagep(AstPackage* const packagep) { m_stdPackagep = packagep; }
|
||||
AstPackage* stdPackagep() const { return m_stdPackagep; }
|
||||
AstTopScope* topScopep() const { return m_topScopep; }
|
||||
@@ -1310,7 +1361,7 @@ public:
|
||||
VPragmaType pragType() const { return m_pragType; } // *=type of the pragma
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return pragType() == static_cast<const AstPragma*>(samep)->pragType();
|
||||
return pragType() == VN_DBG_AS(samep, Pragma)->pragType();
|
||||
}
|
||||
};
|
||||
class AstPropSpec final : public AstNode {
|
||||
@@ -1345,7 +1396,7 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstPull;
|
||||
bool same(const AstNode* samep) const override {
|
||||
return direction() == static_cast<const AstPull*>(samep)->direction();
|
||||
return direction() == VN_DBG_AS(samep, Pull)->direction();
|
||||
}
|
||||
uint32_t direction() const { return (uint32_t)m_direction; }
|
||||
};
|
||||
@@ -1378,7 +1429,6 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
bool same(const AstNode* samep) const override;
|
||||
string nameDotless() const;
|
||||
string nameVlSym() const { return string{"vlSymsp->"} + nameDotless(); }
|
||||
AstNodeModule* modp() const { return m_modp; }
|
||||
//
|
||||
AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; }
|
||||
@@ -1426,7 +1476,7 @@ public:
|
||||
ASTGEN_MEMBERS_AstSenItem;
|
||||
void dump(std::ostream& str) const override;
|
||||
bool same(const AstNode* samep) const override {
|
||||
return edgeType() == static_cast<const AstSenItem*>(samep)->edgeType();
|
||||
return edgeType() == VN_DBG_AS(samep, SenItem)->edgeType();
|
||||
}
|
||||
VEdgeType edgeType() const { return m_edgeType; }
|
||||
void edgeType(VEdgeType type) {
|
||||
@@ -1627,12 +1677,11 @@ class AstVar final : public AstNode {
|
||||
VVarType m_varType; // Type of variable
|
||||
VDirection m_direction; // Direction input/output etc
|
||||
VDirection m_declDirection; // Declared direction input/output etc
|
||||
VBasicDTypeKwd m_declKwd; // Keyword at declaration time
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
VVarAttrClocker m_attrClocker;
|
||||
MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var
|
||||
int m_pinNum = 0; // For XML, if non-zero the connection pin number
|
||||
bool m_ansi : 1; // ANSI port list variable (for dedup check)
|
||||
bool m_ansi : 1; // Params or pins declared in the module header, rather than the body
|
||||
bool m_declTyped : 1; // Declared as type (for dedup check)
|
||||
bool m_tristate : 1; // Inout or triwire or trireg
|
||||
bool m_primaryIO : 1; // In/out to top level (or directly assigned from same)
|
||||
@@ -1656,6 +1705,7 @@ class AstVar final : public AstNode {
|
||||
bool m_attrSplitVar : 1; // declared with split_var metacomment
|
||||
bool m_fileDescr : 1; // File descriptor
|
||||
bool m_isRand : 1; // Random variable
|
||||
bool m_isRandC : 1; // Random cyclic variable (isRand also set)
|
||||
bool m_isConst : 1; // Table contains constant data
|
||||
bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
|
||||
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
|
||||
@@ -1663,6 +1713,7 @@ class AstVar final : public AstNode {
|
||||
bool m_isPulldown : 1; // Tri0
|
||||
bool m_isPullup : 1; // Tri1
|
||||
bool m_isIfaceParent : 1; // dtype is reference to interface present in this module
|
||||
bool m_isInternal : 1; // Internal state, don't add to method pinter
|
||||
bool m_isDpiOpenArray : 1; // DPI import open array
|
||||
bool m_isHideLocal : 1; // Verilog local
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
@@ -1700,6 +1751,7 @@ class AstVar final : public AstNode {
|
||||
m_attrSplitVar = false;
|
||||
m_fileDescr = false;
|
||||
m_isRand = false;
|
||||
m_isRandC = false;
|
||||
m_isConst = false;
|
||||
m_isContinuously = false;
|
||||
m_hasStrengthAssignment = false;
|
||||
@@ -1707,6 +1759,7 @@ class AstVar final : public AstNode {
|
||||
m_isPulldown = false;
|
||||
m_isPullup = false;
|
||||
m_isIfaceParent = false;
|
||||
m_isInternal = false;
|
||||
m_isDpiOpenArray = false;
|
||||
m_isHideLocal = false;
|
||||
m_isHideProtected = false;
|
||||
@@ -1730,11 +1783,6 @@ public:
|
||||
combineType(type);
|
||||
childDTypep(dtp); // Only for parser
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
if (dtp->basicp()) {
|
||||
m_declKwd = dtp->basicp()->keyword();
|
||||
} else {
|
||||
m_declKwd = VBasicDTypeKwd::LOGIC;
|
||||
}
|
||||
}
|
||||
AstVar(FileLine* fl, VVarType type, const string& name, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_Var(fl)
|
||||
@@ -1744,11 +1792,6 @@ public:
|
||||
combineType(type);
|
||||
UASSERT(dtp, "AstVar created with no dtype");
|
||||
dtypep(dtp);
|
||||
if (dtp->basicp()) {
|
||||
m_declKwd = dtp->basicp()->keyword();
|
||||
} else {
|
||||
m_declKwd = VBasicDTypeKwd::LOGIC;
|
||||
}
|
||||
}
|
||||
AstVar(FileLine* fl, VVarType type, const string& name, VFlagLogicPacked, int wantwidth)
|
||||
: ASTGEN_SUPER_Var(fl)
|
||||
@@ -1757,7 +1800,6 @@ public:
|
||||
init();
|
||||
combineType(type);
|
||||
dtypeSetLogicSized(wantwidth, VSigning::UNSIGNED);
|
||||
m_declKwd = VBasicDTypeKwd::LOGIC;
|
||||
}
|
||||
AstVar(FileLine* fl, VVarType type, const string& name, VFlagBitPacked, int wantwidth)
|
||||
: ASTGEN_SUPER_Var(fl)
|
||||
@@ -1766,7 +1808,6 @@ public:
|
||||
init();
|
||||
combineType(type);
|
||||
dtypeSetBitSized(wantwidth, VSigning::UNSIGNED);
|
||||
m_declKwd = VBasicDTypeKwd::BIT;
|
||||
}
|
||||
AstVar(FileLine* fl, VVarType type, const string& name, AstVar* examplep)
|
||||
: ASTGEN_SUPER_Var(fl)
|
||||
@@ -1776,7 +1817,6 @@ public:
|
||||
combineType(type);
|
||||
if (examplep->childDTypep()) childDTypep(examplep->childDTypep()->cloneTree(true));
|
||||
dtypeFrom(examplep);
|
||||
m_declKwd = examplep->declKwd();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstVar;
|
||||
void dump(std::ostream& str) const override;
|
||||
@@ -1804,7 +1844,6 @@ public:
|
||||
m_tristate = false;
|
||||
m_direction = VDirection::INPUT;
|
||||
}
|
||||
VBasicDTypeKwd declKwd() const { return m_declKwd; }
|
||||
string scType() const; // Return SysC type: bool, uint32_t, uint64_t, sc_bv
|
||||
// Return C /*public*/ type for argument: bool, uint32_t, uint64_t, etc.
|
||||
string cPubArgType(bool named, bool forReturn) const;
|
||||
@@ -1852,10 +1891,15 @@ public:
|
||||
void scSensitive(bool flag) { m_scSensitive = flag; }
|
||||
void primaryIO(bool flag) { m_primaryIO = flag; }
|
||||
void isRand(bool flag) { m_isRand = flag; }
|
||||
void isRandC(bool flag) {
|
||||
m_isRandC = flag;
|
||||
if (flag) isRand(true);
|
||||
}
|
||||
void isConst(bool flag) { m_isConst = flag; }
|
||||
void isContinuously(bool flag) { m_isContinuously = flag; }
|
||||
void isStatic(bool flag) { m_isStatic = flag; }
|
||||
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
|
||||
void isInternal(bool flag) { m_isInternal = flag; }
|
||||
void funcLocal(bool flag) {
|
||||
m_funcLocal = flag;
|
||||
if (flag) m_funcLocalSticky = true;
|
||||
@@ -1894,12 +1938,15 @@ public:
|
||||
bool isInoutish() const { return m_direction.isInoutish(); }
|
||||
bool isNonOutput() const { return m_direction.isNonOutput(); }
|
||||
bool isReadOnly() const VL_MT_SAFE { return m_direction.isReadOnly(); }
|
||||
bool isConstRef() const VL_MT_SAFE { return m_direction.isConstRef(); }
|
||||
bool isRef() const VL_MT_SAFE { return m_direction.isRef(); }
|
||||
bool isWritable() const VL_MT_SAFE { return m_direction.isWritable(); }
|
||||
bool isTristate() const { return m_tristate; }
|
||||
bool isPrimaryIO() const { return m_primaryIO; }
|
||||
bool isPrimaryInish() const { return isPrimaryIO() && isNonOutput(); }
|
||||
bool isIfaceRef() const { return (varType() == VVarType::IFACEREF); }
|
||||
bool isIfaceParent() const { return m_isIfaceParent; }
|
||||
bool isInternal() const { return m_isInternal; }
|
||||
bool isSignal() const { return varType().isSignal(); }
|
||||
bool isNet() const { return varType().isNet(); }
|
||||
bool isTemp() const { return varType().isTemp(); }
|
||||
@@ -1912,9 +1959,7 @@ public:
|
||||
bool isClassMember() const { return varType() == VVarType::MEMBER; }
|
||||
bool isStatementTemp() const { return (varType() == VVarType::STMTTEMP); }
|
||||
bool isXTemp() const { return (varType() == VVarType::XTEMP); }
|
||||
bool isParam() const VL_MT_SAFE {
|
||||
return (varType() == VVarType::LPARAM || varType() == VVarType::GPARAM);
|
||||
}
|
||||
bool isParam() const { return varType().isParam(); }
|
||||
bool isGParam() const { return (varType() == VVarType::GPARAM); }
|
||||
bool isGenVar() const { return (varType() == VVarType::GENVAR); }
|
||||
bool isBitLogic() const {
|
||||
@@ -1937,6 +1982,7 @@ public:
|
||||
bool isSigUserRWPublic() const { return m_sigUserRWPublic; }
|
||||
bool isTrace() const { return m_trace; }
|
||||
bool isRand() const { return m_isRand; }
|
||||
bool isRandC() const { return m_isRandC; }
|
||||
bool isConst() const VL_MT_SAFE { return m_isConst; }
|
||||
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
|
||||
bool isLatched() const { return m_isLatched; }
|
||||
@@ -1955,18 +2001,25 @@ public:
|
||||
string verilogKwd() const override;
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
void propagateAttrFrom(AstVar* fromp) {
|
||||
void propagateAttrFrom(const AstVar* fromp) {
|
||||
// This is getting connected to fromp; keep attributes
|
||||
// Note the method below too
|
||||
if (fromp->attrFileDescr()) attrFileDescr(true);
|
||||
if (fromp->attrIsolateAssign()) attrIsolateAssign(true);
|
||||
if (fromp->isContinuously()) isContinuously(true);
|
||||
}
|
||||
void propagateWrapAttrFrom(const AstVar* fromp) {
|
||||
// Creating a function wrapper; keep attributes
|
||||
propagateAttrFrom(fromp);
|
||||
direction(fromp->direction());
|
||||
declDirection(fromp->declDirection());
|
||||
lifetime(fromp->lifetime());
|
||||
}
|
||||
bool gateMultiInputOptimizable() const {
|
||||
// Ok to gate optimize; must return false if propagateAttrFrom would do anything
|
||||
return !isUsedClock();
|
||||
}
|
||||
void combineType(AstVar* typevarp) {
|
||||
void combineType(const AstVar* typevarp) {
|
||||
// This is same as typevarp (for combining input & reg decls)
|
||||
// "this" is the input var. typevarp is the reg var.
|
||||
propagateAttrFrom(typevarp);
|
||||
@@ -2040,25 +2093,30 @@ public:
|
||||
class AstBegin final : public AstNodeBlock {
|
||||
// A Begin/end named block, only exists shortly after parsing until linking
|
||||
// Parents: statement
|
||||
// @astgen op2 := genforp : Optional[AstNode]
|
||||
// @astgen op1 := genforp : Optional[AstNode]
|
||||
|
||||
bool m_generate; // Underneath a generate
|
||||
const bool m_implied; // Not inserted by user
|
||||
bool m_generate : 1; // Underneath a generate
|
||||
bool m_needProcess : 1; // Uses VlProcess
|
||||
const bool m_implied : 1; // Not inserted by user
|
||||
public:
|
||||
// Node that puts name into the output stream
|
||||
AstBegin(FileLine* fl, const string& name, AstNode* stmtsp, bool generate = false,
|
||||
bool implied = false)
|
||||
: ASTGEN_SUPER_Begin(fl, name, stmtsp)
|
||||
, m_generate{generate}
|
||||
, m_needProcess{false}
|
||||
, m_implied{implied} {}
|
||||
ASTGEN_MEMBERS_AstBegin;
|
||||
void dump(std::ostream& str) const override;
|
||||
void generate(bool flag) { m_generate = flag; }
|
||||
bool generate() const { return m_generate; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
bool implied() const { return m_implied; }
|
||||
};
|
||||
class AstFork final : public AstNodeBlock {
|
||||
// A fork named block
|
||||
// @astgen op1 := initsp : List[AstNode]
|
||||
// Parents: statement
|
||||
// Children: statements
|
||||
private:
|
||||
@@ -2084,6 +2142,24 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstFunc;
|
||||
bool hasDType() const override { return true; }
|
||||
AstNodeFTask* cloneType(const string& name) override {
|
||||
return new AstFunc{fileline(), name, nullptr, nullptr};
|
||||
}
|
||||
};
|
||||
class AstLet final : public AstNodeFTask {
|
||||
// Verilog "let" statement
|
||||
// Parents: MODULE
|
||||
// stmtp is always a StmtExpr as Let always returns AstNodeExpr
|
||||
public:
|
||||
AstLet(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_Let(fl, name, nullptr) {}
|
||||
ASTGEN_MEMBERS_AstLet;
|
||||
bool hasDType() const override { return true; }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!VN_IS(stmtsp(), StmtExpr));
|
||||
return nullptr;
|
||||
}
|
||||
AstNodeFTask* cloneType(const string& name) override { return new AstLet{fileline(), name}; }
|
||||
};
|
||||
class AstProperty final : public AstNodeFTask {
|
||||
// A property inside a module
|
||||
@@ -2092,6 +2168,9 @@ public:
|
||||
: ASTGEN_SUPER_Property(fl, name, stmtp) {}
|
||||
ASTGEN_MEMBERS_AstProperty;
|
||||
bool hasDType() const override { return true; }
|
||||
AstNodeFTask* cloneType(const string& name) override {
|
||||
return new AstProperty{fileline(), name, nullptr};
|
||||
}
|
||||
};
|
||||
class AstTask final : public AstNodeFTask {
|
||||
// A task inside a module
|
||||
@@ -2099,6 +2178,9 @@ public:
|
||||
AstTask(FileLine* fl, const string& name, AstNode* stmtp)
|
||||
: ASTGEN_SUPER_Task(fl, name, stmtp) {}
|
||||
ASTGEN_MEMBERS_AstTask;
|
||||
AstNodeFTask* cloneType(const string& name) override {
|
||||
return new AstTask{fileline(), name, nullptr};
|
||||
}
|
||||
};
|
||||
|
||||
// === AstNodeFile ===
|
||||
@@ -2532,7 +2614,7 @@ public:
|
||||
void hier(const string& flag) { m_hier = flag; }
|
||||
void comment(const string& flag) { m_text = flag; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstCoverDecl* const asamep = static_cast<const AstCoverDecl*>(samep);
|
||||
const AstCoverDecl* const asamep = VN_DBG_AS(samep, CoverDecl);
|
||||
return (fileline() == asamep->fileline() && linescov() == asamep->linescov()
|
||||
&& hier() == asamep->hier() && comment() == asamep->comment());
|
||||
}
|
||||
@@ -2561,11 +2643,11 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return declp() == static_cast<const AstCoverInc*>(samep)->declp();
|
||||
return declp() == VN_DBG_AS(samep, CoverInc)->declp();
|
||||
}
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isOutputter() override { return true; }
|
||||
// but isPure() true
|
||||
AstCoverDecl* declp() const { return m_declp; } // Where defined
|
||||
};
|
||||
@@ -2587,7 +2669,7 @@ public:
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return true; }
|
||||
bool isOutputter() const override {
|
||||
bool isOutputter() override {
|
||||
return false; // Though the AstCoverInc under this is an outputter
|
||||
}
|
||||
// but isPure() true
|
||||
@@ -2664,11 +2746,11 @@ public:
|
||||
}
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isOutputter() const override { return true; } // SPECIAL: $display makes output
|
||||
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isOutputter() override { return true; } // SPECIAL: $display makes output
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return displayType() == static_cast<const AstDisplay*>(samep)->displayType();
|
||||
return displayType() == VN_DBG_AS(samep, Display)->displayType();
|
||||
}
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
VDisplayType displayType() const { return m_displayType; }
|
||||
@@ -2707,7 +2789,7 @@ public:
|
||||
string verilogKwd() const override { return ctlType().ascii(); }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isOutputter() override { return true; }
|
||||
virtual bool cleanOut() const { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
VDumpCtlType ctlType() const { return m_ctlType; }
|
||||
@@ -2739,8 +2821,8 @@ public:
|
||||
string verilogKwd() const override { return "$fclose"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
@@ -2756,8 +2838,8 @@ public:
|
||||
string verilogKwd() const override { return "$fflush"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
@@ -2768,8 +2850,8 @@ public:
|
||||
ASTGEN_MEMBERS_AstFinish;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isOutputter() const override { return true; } // SPECIAL: $display makes output
|
||||
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isOutputter() override { return true; } // SPECIAL: $display makes output
|
||||
bool isUnlikely() const override { return true; }
|
||||
int instrCount() const override { return 0; } // Rarely executes
|
||||
bool same(const AstNode* samep) const override { return fileline() == samep->fileline(); }
|
||||
@@ -2845,7 +2927,7 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return labelp() == static_cast<const AstJumpGo*>(samep)->labelp();
|
||||
return labelp() == VN_DBG_AS(samep, JumpGo)->labelp();
|
||||
}
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isBrancher() const override {
|
||||
@@ -2876,7 +2958,7 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
int instrCount() const override { return 0; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return blockp() == static_cast<const AstJumpLabel*>(samep)->blockp();
|
||||
return blockp() == VN_DBG_AS(samep, JumpLabel)->blockp();
|
||||
}
|
||||
AstJumpBlock* blockp() const { return m_blockp; }
|
||||
};
|
||||
@@ -2891,11 +2973,11 @@ public:
|
||||
string verilogKwd() const override { return m_off ? "$monitoroff" : "$monitoron"; }
|
||||
bool isGateOptimizable() const override { return false; } // Though deleted before opt
|
||||
bool isPredictOptimizable() const override { return false; } // Though deleted before opt
|
||||
bool isPure() const override { return false; } // Though deleted before opt
|
||||
bool isOutputter() const override { return true; } // Though deleted before opt
|
||||
bool isPure() override { return false; } // Though deleted before opt
|
||||
bool isOutputter() override { return true; } // Though deleted before opt
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return m_off == static_cast<const AstMonitorOff*>(samep)->m_off;
|
||||
return m_off == VN_DBG_AS(samep, MonitorOff)->m_off;
|
||||
}
|
||||
bool off() const { return m_off; }
|
||||
};
|
||||
@@ -2913,8 +2995,8 @@ public:
|
||||
string verilogKwd() const override { return "$printtimescale"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
@@ -2991,8 +3073,8 @@ public:
|
||||
string verilogKwd() const override { return "$sformat"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return true; }
|
||||
bool isPure() const override { return true; }
|
||||
bool isOutputter() const override { return false; }
|
||||
bool isPure() override { return true; }
|
||||
bool isOutputter() override { return false; }
|
||||
virtual bool cleanOut() const { return false; }
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
@@ -3006,8 +3088,8 @@ public:
|
||||
string verilogKwd() const override { return "$stacktrace"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
@@ -3028,8 +3110,8 @@ public:
|
||||
ASTGEN_MEMBERS_AstStop;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isOutputter() const override { return true; } // SPECIAL: $display makes output
|
||||
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool isOutputter() override { return true; } // SPECIAL: $display makes output
|
||||
bool isUnlikely() const override { return true; }
|
||||
int instrCount() const override { return 0; } // Rarely executes
|
||||
bool same(const AstNode* samep) const override { return fileline() == samep->fileline(); }
|
||||
@@ -3047,8 +3129,8 @@ public:
|
||||
string verilogKwd() const override { return ""; }
|
||||
bool isGateOptimizable() const override { return true; }
|
||||
bool isPredictOptimizable() const override { return true; }
|
||||
bool isPure() const override { return true; }
|
||||
bool isOutputter() const override { return false; }
|
||||
bool isPure() override { return true; }
|
||||
bool isOutputter() override { return false; }
|
||||
int instrCount() const override { return 0; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
@@ -3064,8 +3146,8 @@ public:
|
||||
string verilogKwd() const override { return "$system"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
@@ -3088,8 +3170,8 @@ public:
|
||||
string verilogKwd() const override { return "$timeformat"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
};
|
||||
class AstTraceDecl final : public AstNodeStmt {
|
||||
@@ -3099,13 +3181,13 @@ class AstTraceDecl final : public AstNodeStmt {
|
||||
// Expression being traced - Moved to AstTraceInc by V3Trace
|
||||
// @astgen op1 := valuep : Optional[AstNodeExpr]
|
||||
private:
|
||||
uint32_t m_code = 0; // Trace identifier code; converted to ASCII by trace routines
|
||||
uint32_t m_code{0}; // Trace identifier code
|
||||
uint32_t m_fidx{0}; // Trace function index
|
||||
const string m_showname; // Name of variable
|
||||
const VNumRange m_bitRange; // Property of var the trace details
|
||||
const VNumRange m_arrayRange; // Property of var the trace details
|
||||
const uint32_t m_codeInc; // Code increment
|
||||
const VVarType m_varType; // Type of variable (for localparam vs. param)
|
||||
const VBasicDTypeKwd m_declKwd; // Keyword at declaration time
|
||||
const VDirection m_declDirection; // Declared direction input/output etc
|
||||
public:
|
||||
AstTraceDecl(FileLine* fl, const string& showname,
|
||||
@@ -3119,7 +3201,6 @@ public:
|
||||
((arrayRange.ranged() ? arrayRange.elements() : 1) * valuep->dtypep()->widthWords()
|
||||
* (VL_EDATASIZE / 32))) // A code is always 32-bits
|
||||
, m_varType{varp->varType()}
|
||||
, m_declKwd{varp->declKwd()}
|
||||
, m_declDirection{varp->declDirection()} {
|
||||
dtypeFrom(valuep);
|
||||
this->valuep(valuep);
|
||||
@@ -3135,11 +3216,12 @@ public:
|
||||
// Details on what we're tracing
|
||||
uint32_t code() const { return m_code; }
|
||||
void code(uint32_t code) { m_code = code; }
|
||||
uint32_t fidx() const { return m_fidx; }
|
||||
void fidx(uint32_t fidx) { m_fidx = fidx; }
|
||||
uint32_t codeInc() const { return m_codeInc; }
|
||||
const VNumRange& bitRange() const { return m_bitRange; }
|
||||
const VNumRange& arrayRange() const { return m_arrayRange; }
|
||||
VVarType varType() const { return m_varType; }
|
||||
VBasicDTypeKwd declKwd() const { return m_declKwd; }
|
||||
VDirection declDirection() const { return m_declDirection; }
|
||||
};
|
||||
class AstTraceInc final : public AstNodeStmt {
|
||||
@@ -3150,14 +3232,14 @@ class AstTraceInc final : public AstNodeStmt {
|
||||
private:
|
||||
AstTraceDecl* m_declp; // Pointer to declaration
|
||||
const uint32_t m_baseCode; // Trace code base value in function containing this AstTraceInc
|
||||
const bool m_full; // Is this a full vs incremental dump
|
||||
const VTraceType m_traceType; // Is this a const/full/incremental dump
|
||||
|
||||
public:
|
||||
AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full, uint32_t baseCode = 0)
|
||||
AstTraceInc(FileLine* fl, AstTraceDecl* declp, VTraceType traceType, uint32_t baseCode = 0)
|
||||
: ASTGEN_SUPER_TraceInc(fl)
|
||||
, m_declp{declp}
|
||||
, m_baseCode{baseCode}
|
||||
, m_full{full} {
|
||||
, m_traceType{traceType} {
|
||||
dtypeFrom(declp);
|
||||
this->valuep(
|
||||
declp->valuep()->cloneTree(true)); // TODO: maybe use reference to TraceDecl instead?
|
||||
@@ -3174,35 +3256,35 @@ public:
|
||||
int instrCount() const override { return 10 + 2 * INSTR_COUNT_LD; }
|
||||
bool hasDType() const override { return true; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return declp() == static_cast<const AstTraceInc*>(samep)->declp();
|
||||
return declp() == VN_DBG_AS(samep, TraceInc)->declp();
|
||||
}
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isOutputter() override { return true; }
|
||||
// but isPure() true
|
||||
AstTraceDecl* declp() const { return m_declp; }
|
||||
bool full() const { return m_full; }
|
||||
VTraceType traceType() const { return m_traceType; }
|
||||
uint32_t baseCode() const { return m_baseCode; }
|
||||
};
|
||||
class AstTracePopNamePrefix final : public AstNodeStmt {
|
||||
const unsigned m_count; // How many levels to pop
|
||||
class AstTracePopPrefix final : public AstNodeStmt {
|
||||
public:
|
||||
AstTracePopNamePrefix(FileLine* fl, unsigned count)
|
||||
: ASTGEN_SUPER_TracePopNamePrefix(fl)
|
||||
, m_count{count} {}
|
||||
ASTGEN_MEMBERS_AstTracePopNamePrefix;
|
||||
AstTracePopPrefix(FileLine* fl)
|
||||
: ASTGEN_SUPER_TracePopPrefix(fl) {}
|
||||
ASTGEN_MEMBERS_AstTracePopPrefix;
|
||||
bool same(const AstNode* samep) const override { return false; }
|
||||
unsigned count() const { return m_count; }
|
||||
};
|
||||
class AstTracePushNamePrefix final : public AstNodeStmt {
|
||||
class AstTracePushPrefix final : public AstNodeStmt {
|
||||
const string m_prefix; // Prefix to add to signal names
|
||||
const VTracePrefixType m_prefixType; // Type of prefix being pushed
|
||||
public:
|
||||
AstTracePushNamePrefix(FileLine* fl, const string& prefix)
|
||||
: ASTGEN_SUPER_TracePushNamePrefix(fl)
|
||||
, m_prefix{prefix} {}
|
||||
ASTGEN_MEMBERS_AstTracePushNamePrefix;
|
||||
AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType)
|
||||
: ASTGEN_SUPER_TracePushPrefix(fl)
|
||||
, m_prefix{prefix}
|
||||
, m_prefixType{prefixType} {}
|
||||
ASTGEN_MEMBERS_AstTracePushPrefix;
|
||||
bool same(const AstNode* samep) const override { return false; }
|
||||
string prefix() const { return m_prefix; }
|
||||
VTracePrefixType prefixType() const { return m_prefixType; }
|
||||
};
|
||||
class AstUCStmt final : public AstNodeStmt {
|
||||
// User $c statement
|
||||
@@ -3215,8 +3297,8 @@ public:
|
||||
ASTGEN_MEMBERS_AstUCStmt;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; }
|
||||
bool isOutputter() override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstWait final : public AstNodeStmt {
|
||||
@@ -3238,6 +3320,7 @@ public:
|
||||
explicit AstWaitFork(FileLine* fl)
|
||||
: ASTGEN_SUPER_WaitFork(fl) {}
|
||||
ASTGEN_MEMBERS_AstWaitFork;
|
||||
bool isTimingControl() const override { return true; }
|
||||
};
|
||||
class AstWhile final : public AstNodeStmt {
|
||||
// @astgen op1 := precondsp : List[AstNode]
|
||||
@@ -3384,7 +3467,7 @@ public:
|
||||
ASTGEN_MEMBERS_AstCase;
|
||||
string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return m_casex == static_cast<const AstCase*>(samep)->m_casex;
|
||||
return m_casex == VN_DBG_AS(samep, Case)->m_casex;
|
||||
}
|
||||
bool casex() const { return m_casex == VCaseType::CT_CASEX; }
|
||||
bool casez() const { return m_casex == VCaseType::CT_CASEZ; }
|
||||
@@ -3506,48 +3589,48 @@ public:
|
||||
AstScCtor(FileLine* fl, const string& textp)
|
||||
: ASTGEN_SUPER_ScCtor(fl, textp) {}
|
||||
ASTGEN_MEMBERS_AstScCtor;
|
||||
bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() override { return true; }
|
||||
};
|
||||
class AstScDtor final : public AstNodeText {
|
||||
public:
|
||||
AstScDtor(FileLine* fl, const string& textp)
|
||||
: ASTGEN_SUPER_ScDtor(fl, textp) {}
|
||||
ASTGEN_MEMBERS_AstScDtor;
|
||||
bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() override { return true; }
|
||||
};
|
||||
class AstScHdr final : public AstNodeText {
|
||||
public:
|
||||
AstScHdr(FileLine* fl, const string& textp)
|
||||
: ASTGEN_SUPER_ScHdr(fl, textp) {}
|
||||
ASTGEN_MEMBERS_AstScHdr;
|
||||
bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() override { return true; }
|
||||
};
|
||||
class AstScImp final : public AstNodeText {
|
||||
public:
|
||||
AstScImp(FileLine* fl, const string& textp)
|
||||
: ASTGEN_SUPER_ScImp(fl, textp) {}
|
||||
ASTGEN_MEMBERS_AstScImp;
|
||||
bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() override { return true; }
|
||||
};
|
||||
class AstScImpHdr final : public AstNodeText {
|
||||
public:
|
||||
AstScImpHdr(FileLine* fl, const string& textp)
|
||||
: ASTGEN_SUPER_ScImpHdr(fl, textp) {}
|
||||
ASTGEN_MEMBERS_AstScImpHdr;
|
||||
bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() override { return true; }
|
||||
};
|
||||
class AstScInt final : public AstNodeText {
|
||||
public:
|
||||
AstScInt(FileLine* fl, const string& textp)
|
||||
: ASTGEN_SUPER_ScInt(fl, textp) {}
|
||||
ASTGEN_MEMBERS_AstScInt;
|
||||
bool isPure() const override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
|
||||
bool isOutputter() override { return true; }
|
||||
};
|
||||
|
||||
// === AstNodeSimpleText ===
|
||||
|
||||
+242
-77
@@ -14,13 +14,10 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstMT.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3Hasher.h"
|
||||
#include "V3PartitionGraph.h" // Just for mtask dumping
|
||||
@@ -50,6 +47,7 @@ AstIface* AstIfaceRefDType::ifaceViaCellp() const {
|
||||
const char* AstNodeFTaskRef::broken() const {
|
||||
BROKEN_RTN(m_taskp && !m_taskp->brokeExists());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
BROKEN_RTN(m_purity.isCached() && m_purity.get() != getPurityRecurse());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -60,12 +58,29 @@ void AstNodeFTaskRef::cloneRelink() {
|
||||
}
|
||||
}
|
||||
|
||||
bool AstNodeFTaskRef::isPure() const {
|
||||
// TODO: For non-DPI functions we could traverse the AST of function's body to determine
|
||||
// pureness.
|
||||
return this->taskp() && this->taskp()->dpiImport() && this->taskp()->pure();
|
||||
bool AstNodeFTaskRef::isPure() {
|
||||
if (!this->taskp()) {
|
||||
// The task isn't linked yet, so it's assumed that it is impure, but the value shouldn't be
|
||||
// cached.
|
||||
return false;
|
||||
} else {
|
||||
if (!m_purity.isCached()) m_purity.set(this->getPurityRecurse());
|
||||
return m_purity.get();
|
||||
}
|
||||
}
|
||||
|
||||
bool AstNodeFTaskRef::getPurityRecurse() const {
|
||||
AstNodeFTask* const taskp = this->taskp();
|
||||
// Unlinked yet, so treat as impure
|
||||
if (!taskp) return false;
|
||||
|
||||
// First compute the purity of arguments
|
||||
for (AstNode* pinp = this->pinsp(); pinp; pinp = pinp->nextp()) {
|
||||
if (!pinp->isPure()) return false;
|
||||
}
|
||||
|
||||
return taskp->isPure();
|
||||
}
|
||||
bool AstNodeFTaskRef::isGateOptimizable() const { return m_taskp && m_taskp->isGateOptimizable(); }
|
||||
|
||||
const char* AstNodeVarRef::broken() const {
|
||||
@@ -83,12 +98,6 @@ void AstNodeVarRef::cloneRelink() {
|
||||
}
|
||||
}
|
||||
|
||||
string AstNodeVarRef::selfPointerProtect(bool useSelfForThis) const {
|
||||
const string& sp
|
||||
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
|
||||
return VIdProtect::protectWordsIf(sp, protect());
|
||||
}
|
||||
|
||||
void AstAddrOfCFunc::cloneRelink() {
|
||||
if (m_funcp && m_funcp->clonep()) m_funcp = m_funcp->clonep();
|
||||
}
|
||||
@@ -126,14 +135,65 @@ const char* AstNodeCCall::broken() const {
|
||||
BROKEN_RTN(m_funcp && !m_funcp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
bool AstNodeCCall::isPure() const { return funcp()->pure(); }
|
||||
|
||||
string AstCCall::selfPointerProtect(bool useSelfForThis) const {
|
||||
const string& sp
|
||||
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
|
||||
return VIdProtect::protectWordsIf(sp, protect());
|
||||
bool AstNodeCCall::isPure() { return funcp()->dpiPure(); }
|
||||
bool AstNodeUniop::isPure() {
|
||||
if (!m_purity.isCached()) m_purity.set(lhsp()->isPure());
|
||||
return m_purity.get();
|
||||
}
|
||||
const char* AstNodeUniop::broken() const {
|
||||
BROKEN_RTN(m_purity.isCached() && m_purity.get() != lhsp()->isPure());
|
||||
return nullptr;
|
||||
}
|
||||
bool AstNodeBiop::isPure() {
|
||||
if (!m_purity.isCached()) m_purity.set(getPurityRecurse());
|
||||
return m_purity.get();
|
||||
}
|
||||
const char* AstNodeBiop::broken() const {
|
||||
BROKEN_RTN(m_purity.isCached() && m_purity.get() != getPurityRecurse());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AstNodeTriop::isPure() {
|
||||
if (!m_purity.isCached()) m_purity.set(getPurityRecurse());
|
||||
return m_purity.get();
|
||||
}
|
||||
const char* AstNodeTriop::broken() const {
|
||||
BROKEN_RTN(m_purity.isCached() && m_purity.get() != getPurityRecurse());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AstNodePreSel::isPure() {
|
||||
if (!m_purity.isCached()) m_purity.set(getPurityRecurse());
|
||||
return m_purity.get();
|
||||
}
|
||||
const char* AstNodePreSel::broken() const {
|
||||
BROKEN_RTN(m_purity.isCached() && m_purity.get() != getPurityRecurse());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AstNodeQuadop::isPure() {
|
||||
if (!m_purity.isCached()) m_purity.set(getPurityRecurse());
|
||||
return m_purity.get();
|
||||
}
|
||||
const char* AstNodeQuadop::broken() const {
|
||||
BROKEN_RTN(m_purity.isCached() && m_purity.get() != getPurityRecurse());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNodeCond::AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp,
|
||||
AstNodeExpr* elsep)
|
||||
: AstNodeTriop{t, fl, condp, thenp, elsep} {
|
||||
UASSERT_OBJ(thenp, this, "No thenp expression");
|
||||
UASSERT_OBJ(elsep, this, "No elsep expression");
|
||||
if (thenp->isClassHandleValue() && elsep->isClassHandleValue()) {
|
||||
// Get the most-deriving class type that both arguments can be casted to.
|
||||
AstNodeDType* const commonClassTypep = getCommonClassTypep(thenp, elsep);
|
||||
UASSERT_OBJ(commonClassTypep, this, "No common base class exists");
|
||||
dtypep(commonClassTypep);
|
||||
} else {
|
||||
dtypeFrom(thenp);
|
||||
}
|
||||
}
|
||||
void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
|
||||
const V3Number& ths) {
|
||||
if (lhs.isNeqZero()) {
|
||||
@@ -213,12 +273,10 @@ int AstBasicDType::widthTotalBytes() const {
|
||||
}
|
||||
|
||||
bool AstBasicDType::same(const AstNode* samep) const {
|
||||
const AstBasicDType* const sp = static_cast<const AstBasicDType*>(samep);
|
||||
if (!rangep() && !sp->rangep() && m == sp->m) {
|
||||
return true;
|
||||
} else {
|
||||
return m == sp->m && rangep() && rangep()->sameTree(sp->rangep());
|
||||
}
|
||||
const AstBasicDType* const sp = VN_DBG_AS(samep, BasicDType);
|
||||
if (!(m == sp->m) || numeric() != sp->numeric()) return false;
|
||||
if (!rangep() && !sp->rangep()) return true;
|
||||
return rangep() && rangep()->sameTree(sp->rangep());
|
||||
}
|
||||
|
||||
int AstNodeUOrStructDType::widthTotalBytes() const {
|
||||
@@ -267,17 +325,17 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* r
|
||||
}
|
||||
}
|
||||
|
||||
AstExecGraph::AstExecGraph(FileLine* fileline, const string& name)
|
||||
: ASTGEN_SUPER_ExecGraph(fileline)
|
||||
, m_depGraphp{new V3Graph}
|
||||
, m_name{name} {}
|
||||
AstExecGraph::AstExecGraph(FileLine* fileline, const string& name) VL_MT_DISABLED
|
||||
: ASTGEN_SUPER_ExecGraph(fileline),
|
||||
m_depGraphp{new V3Graph},
|
||||
m_name{name} {}
|
||||
|
||||
AstExecGraph::~AstExecGraph() { VL_DO_DANGLING(delete m_depGraphp, m_depGraphp); }
|
||||
|
||||
AstNodeExpr* AstInsideRange::newAndFromInside(AstNodeExpr* exprp, AstNodeExpr* lhsp,
|
||||
AstNodeExpr* rhsp) {
|
||||
AstNodeExpr* const ap = new AstGte{fileline(), exprp->cloneTree(true), lhsp};
|
||||
AstNodeExpr* const bp = new AstLte{fileline(), exprp->cloneTree(true), rhsp};
|
||||
AstNodeExpr* const ap = new AstGte{fileline(), exprp->cloneTreePure(true), lhsp};
|
||||
AstNodeExpr* const bp = new AstLte{fileline(), exprp->cloneTreePure(true), rhsp};
|
||||
ap->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true);
|
||||
bp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true);
|
||||
return new AstAnd{fileline(), ap, bp};
|
||||
@@ -385,7 +443,8 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
||||
if (isStatic() && namespc.empty()) ostatic = "static ";
|
||||
|
||||
const bool isRef = isDpiOpenArray()
|
||||
|| (forFunc && (isWritable() || direction().isRefOrConstRef())) || asRef;
|
||||
|| (forFunc && (isWritable() || this->isRef() || this->isConstRef()))
|
||||
|| asRef;
|
||||
|
||||
if (forFunc && isReadOnly() && isRef) ostatic = ostatic + "const ";
|
||||
|
||||
@@ -407,6 +466,8 @@ string AstVar::vlEnumType() const {
|
||||
return "VLVT_PTR";
|
||||
} else if (strtype) {
|
||||
arg += "VLVT_STRING";
|
||||
} else if (isDouble()) {
|
||||
arg += "VLVT_REAL";
|
||||
} else if (widthMin() <= 8) {
|
||||
arg += "VLVT_UINT8";
|
||||
} else if (widthMin() <= 16) {
|
||||
@@ -499,7 +560,7 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
|
||||
if (forReturn) named = false;
|
||||
string arg;
|
||||
if (isWide() && isReadOnly()) arg += "const ";
|
||||
const bool isRef = !forReturn && (isWritable() || direction().isRefOrConstRef());
|
||||
const bool isRef = !forReturn && (isWritable() || this->isRef() || this->isConstRef());
|
||||
if (VN_IS(dtypeSkipRefp(), BasicDType) && !dtypeSkipRefp()->isDouble()
|
||||
&& !dtypeSkipRefp()->isString()) {
|
||||
// Backward compatible type declaration
|
||||
@@ -627,13 +688,14 @@ string AstVar::dpiTmpVarType(const string& varName) const {
|
||||
|
||||
string AstVar::scType() const {
|
||||
if (isScBigUint()) {
|
||||
return (string{"sc_biguint<"} + cvtToStr(widthMin())
|
||||
return (string{"sc_dt::sc_biguint<"} + cvtToStr(widthMin())
|
||||
+ "> "); // Keep the space so don't get >>
|
||||
} else if (isScUint()) {
|
||||
return (string{"sc_uint<"} + cvtToStr(widthMin())
|
||||
return (string{"sc_dt::sc_uint<"} + cvtToStr(widthMin())
|
||||
+ "> "); // Keep the space so don't get >>
|
||||
} else if (isScBv()) {
|
||||
return (string{"sc_bv<"} + cvtToStr(widthMin()) + "> "); // Keep the space so don't get >>
|
||||
return (string{"sc_dt::sc_bv<"} + cvtToStr(widthMin())
|
||||
+ "> "); // Keep the space so don't get >>
|
||||
} else if (widthMin() == 1) {
|
||||
return "bool";
|
||||
} else if (widthMin() <= VL_IDATASIZE) {
|
||||
@@ -709,6 +771,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true);
|
||||
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, CDType)) {
|
||||
info.m_type = adtypep->name();
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
info.m_type = "VlAssocArray<std::string, " + sub.m_type + ">";
|
||||
@@ -766,7 +830,7 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
} else if (bdtypep->isProcessRef()) {
|
||||
info.m_type = "VlProcessRef";
|
||||
} else if (bdtypep->isEvent()) {
|
||||
info.m_type = "VlEvent";
|
||||
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
|
||||
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
|
||||
info.m_type = "CData" + bitvec;
|
||||
} else if (dtypep->widthMin() <= 16) {
|
||||
@@ -1114,13 +1178,10 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd,
|
||||
AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
|
||||
const VBasicTypeKey key{nodep->width(), nodep->widthMin(), nodep->numeric(), nodep->keyword(),
|
||||
nodep->nrange()};
|
||||
DetailedMap& mapr = m_detailedMap;
|
||||
const auto it = mapr.find(key);
|
||||
if (it != mapr.end()) return it->second;
|
||||
mapr.emplace(key, nodep);
|
||||
nodep->generic(true);
|
||||
auto pair = m_detailedMap.emplace(key, nodep);
|
||||
if (pair.second) nodep->generic(true);
|
||||
// No addTypesp; the upper function that called new() is responsible for adding
|
||||
return nodep;
|
||||
return pair.first->second;
|
||||
}
|
||||
|
||||
AstConstPool::AstConstPool(FileLine* fl)
|
||||
@@ -1318,7 +1379,6 @@ void AstNode::dump(std::ostream& str) const {
|
||||
if (user2p()) str << " u2=" << nodeAddr(user2p());
|
||||
if (user3p()) str << " u3=" << nodeAddr(user3p());
|
||||
if (user4p()) str << " u4=" << nodeAddr(user4p());
|
||||
if (user5p()) str << " u5=" << nodeAddr(user5p());
|
||||
if (hasDType()) {
|
||||
// Final @ so less likely to by accident read it as a nodep
|
||||
if (dtypep() == this) {
|
||||
@@ -1392,7 +1452,7 @@ const char* AstCell::broken() const {
|
||||
void AstCellInline::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> " << origModName();
|
||||
str << " [scopep=" << reinterpret_cast<const void*>(scopep()) << "]";
|
||||
str << " [scopep=" << nodeAddr(scopep()) << "]";
|
||||
}
|
||||
const char* AstCellInline::broken() const {
|
||||
BROKEN_RTN(m_scopep && !m_scopep->brokeExists());
|
||||
@@ -1522,6 +1582,12 @@ const char* AstEnumItemRef::broken() const {
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstEnumItemRef::cloneRelink() {
|
||||
if (m_itemp->clonep()) m_itemp = m_itemp->clonep();
|
||||
if (m_classOrPackagep && m_classOrPackagep->clonep()) {
|
||||
m_classOrPackagep = m_classOrPackagep->clonep();
|
||||
}
|
||||
}
|
||||
void AstIfaceRefDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (cellName() != "") str << " cell=" << cellName();
|
||||
@@ -1555,7 +1621,7 @@ void AstInitArray::dump(std::ostream& str) const {
|
||||
str << " ...";
|
||||
break;
|
||||
}
|
||||
str << " [" << itr.first << "]=" << reinterpret_cast<const void*>(itr.second);
|
||||
str << " [" << itr.first << "]=" << nodeAddr(itr.second);
|
||||
}
|
||||
}
|
||||
const char* AstInitArray::broken() const {
|
||||
@@ -1571,13 +1637,13 @@ void AstInitArray::cloneRelink() {
|
||||
}
|
||||
}
|
||||
void AstInitArray::addIndexValuep(uint64_t index, AstNodeExpr* newp) {
|
||||
const auto it = m_map.find(index);
|
||||
if (it != m_map.end()) {
|
||||
it->second->valuep(newp);
|
||||
} else {
|
||||
const auto pair = m_map.emplace(index, nullptr);
|
||||
if (pair.second) {
|
||||
AstInitItem* const itemp = new AstInitItem{fileline(), newp};
|
||||
m_map.emplace(index, itemp);
|
||||
pair.first->second = itemp;
|
||||
addInitsp(itemp);
|
||||
} else {
|
||||
pair.first->second->valuep(newp);
|
||||
}
|
||||
}
|
||||
AstNodeExpr* AstInitArray::getIndexValuep(uint64_t index) const {
|
||||
@@ -1619,10 +1685,6 @@ void AstJumpLabel::dump(std::ostream& str) const {
|
||||
str << "%Error:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstLogOr::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (sideEffect()) str << " [SIDE]";
|
||||
}
|
||||
|
||||
void AstMemberDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
@@ -1637,8 +1699,8 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
|
||||
}
|
||||
|
||||
bool AstMemberSel::same(const AstNode* samep) const {
|
||||
const AstMemberSel* const sp = static_cast<const AstMemberSel*>(samep);
|
||||
return sp != nullptr && access() == sp->access() && fromp()->same(sp->fromp())
|
||||
const AstMemberSel* const sp = VN_DBG_AS(samep, MemberSel);
|
||||
return sp != nullptr && access() == sp->access() && fromp()->isSame(sp->fromp())
|
||||
&& name() == sp->name() && varp()->same(sp->varp());
|
||||
}
|
||||
|
||||
@@ -1873,6 +1935,8 @@ const char* AstNetlist::broken() const {
|
||||
BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists());
|
||||
BROKEN_RTN(m_topScopep && !m_topScopep->brokeExists());
|
||||
BROKEN_RTN(m_delaySchedulerp && !m_delaySchedulerp->brokeExists());
|
||||
BROKEN_RTN(m_nbaEventp && !m_nbaEventp->brokeExists());
|
||||
BROKEN_RTN(m_nbaEventTriggerp && !m_nbaEventTriggerp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
AstPackage* AstNetlist::dollarUnitPkgAddp() {
|
||||
@@ -1971,7 +2035,7 @@ void AstTypeTable::dump(std::ostream& str) const {
|
||||
}
|
||||
void AstAssocArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[assoc-" << reinterpret_cast<const void*>(keyDTypep()) << "]";
|
||||
str << "[assoc-" << nodeAddr(keyDTypep()) << "]";
|
||||
}
|
||||
string AstAssocArrayDType::prettyDTypeName() const {
|
||||
return subDTypep()->prettyDTypeName() + "[" + keyDTypep()->prettyDTypeName() + "]";
|
||||
@@ -1995,13 +2059,14 @@ void AstWildcardArrayDType::dumpSmall(std::ostream& str) const {
|
||||
str << "[*]";
|
||||
}
|
||||
bool AstWildcardArrayDType::same(const AstNode* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
const AstWildcardArrayDType* const asamep = VN_DBG_AS(samep, WildcardArrayDType);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
return (subDTypep() == asamep->subDTypep());
|
||||
}
|
||||
bool AstWildcardArrayDType::similarDType(const AstNodeDType* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
if (type() != samep->type()) return false;
|
||||
const AstWildcardArrayDType* const asamep = VN_DBG_AS(samep, WildcardArrayDType);
|
||||
return asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
void AstSampleQueueDType::dumpSmall(std::ostream& str) const {
|
||||
@@ -2013,13 +2078,14 @@ void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
||||
str << "[]";
|
||||
}
|
||||
bool AstUnsizedArrayDType::same(const AstNode* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
const AstUnsizedArrayDType* const asamep = VN_DBG_AS(samep, UnsizedArrayDType);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
return (subDTypep() == asamep->subDTypep());
|
||||
}
|
||||
bool AstUnsizedArrayDType::similarDType(const AstNodeDType* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
if (type() != samep->type()) return false;
|
||||
const AstUnsizedArrayDType* const asamep = VN_DBG_AS(samep, UnsizedArrayDType);
|
||||
return asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
void AstEmptyQueueDType::dumpSmall(std::ostream& str) const {
|
||||
@@ -2037,7 +2103,7 @@ void AstStreamDType::dumpSmall(std::ostream& str) const {
|
||||
void AstVarScope::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isTrace()) str << " [T]";
|
||||
if (scopep()) str << " [scopep=" << reinterpret_cast<const void*>(scopep()) << "]";
|
||||
if (scopep()) str << " [scopep=" << nodeAddr(scopep()) << "]";
|
||||
if (varp()) {
|
||||
str << " -> ";
|
||||
varp()->dump(str);
|
||||
@@ -2046,7 +2112,7 @@ void AstVarScope::dump(std::ostream& str) const {
|
||||
}
|
||||
}
|
||||
bool AstVarScope::same(const AstNode* samep) const {
|
||||
const AstVarScope* const asamep = static_cast<const AstVarScope*>(samep);
|
||||
const AstVarScope* const asamep = VN_DBG_AS(samep, VarScope);
|
||||
return varp()->same(asamep->varp()) && scopep()->same(asamep->scopep());
|
||||
}
|
||||
void AstNodeVarRef::dump(std::ostream& str) const {
|
||||
@@ -2080,9 +2146,7 @@ const char* AstVarRef::broken() const {
|
||||
BROKEN_RTN(!varp());
|
||||
return AstNodeVarRef::broken();
|
||||
}
|
||||
bool AstVarRef::same(const AstNode* samep) const {
|
||||
return same(static_cast<const AstVarRef*>(samep));
|
||||
}
|
||||
bool AstVarRef::same(const AstNode* samep) const { return same(VN_DBG_AS(samep, VarRef)); }
|
||||
int AstVarRef::instrCount() const {
|
||||
// Account for the target of hard-coded method calls as just an address computation
|
||||
if (const AstCMethodHard* const callp = VN_CAST(backp(), CMethodHard)) {
|
||||
@@ -2101,8 +2165,14 @@ void AstVar::dump(std::ostream& str) const {
|
||||
if (isPulldown()) str << " [PULLDOWN]";
|
||||
if (isUsedClock()) str << " [CLK]";
|
||||
if (isSigPublic()) str << " [P]";
|
||||
if (isInternal()) str << " [INTERNAL]";
|
||||
if (isLatched()) str << " [LATCHED]";
|
||||
if (isUsedLoopIdx()) str << " [LOOP]";
|
||||
if (isRandC()) {
|
||||
str << " [RANDC]";
|
||||
} else if (isRand()) {
|
||||
str << " [RAND]";
|
||||
}
|
||||
if (noReset()) str << " [!RST]";
|
||||
if (attrIsolateAssign()) str << " [aISO]";
|
||||
if (attrFileDescr()) str << " [aFD]";
|
||||
@@ -2117,17 +2187,17 @@ void AstVar::dump(std::ostream& str) const {
|
||||
str << " " << varType();
|
||||
}
|
||||
bool AstVar::same(const AstNode* samep) const {
|
||||
const AstVar* const asamep = static_cast<const AstVar*>(samep);
|
||||
const AstVar* const asamep = VN_DBG_AS(samep, Var);
|
||||
return name() == asamep->name() && varType() == asamep->varType();
|
||||
}
|
||||
void AstScope::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [abovep=" << reinterpret_cast<const void*>(aboveScopep()) << "]";
|
||||
str << " [cellp=" << reinterpret_cast<const void*>(aboveCellp()) << "]";
|
||||
str << " [modp=" << reinterpret_cast<const void*>(modp()) << "]";
|
||||
str << " [abovep=" << nodeAddr(aboveScopep()) << "]";
|
||||
str << " [cellp=" << nodeAddr(aboveCellp()) << "]";
|
||||
str << " [modp=" << nodeAddr(modp()) << "]";
|
||||
}
|
||||
bool AstScope::same(const AstNode* samep) const {
|
||||
const AstScope* const asamep = static_cast<const AstScope*>(samep);
|
||||
const AstScope* const asamep = VN_DBG_AS(samep, Scope);
|
||||
return name() == asamep->name()
|
||||
&& ((!aboveScopep() && !asamep->aboveScopep())
|
||||
|| (aboveScopep() && asamep->aboveScopep()
|
||||
@@ -2215,6 +2285,31 @@ void AstNodeFTask::dump(std::ostream& str) const {
|
||||
if (taskPublic()) str << " [PUBLIC]";
|
||||
if ((dpiImport() || dpiExport()) && cname() != name()) str << " [c=" << cname() << "]";
|
||||
}
|
||||
bool AstNodeFTask::isPure() {
|
||||
if (!m_purity.isCached()) m_purity.set(getPurityRecurse());
|
||||
return m_purity.get();
|
||||
}
|
||||
const char* AstNodeFTask::broken() const {
|
||||
BROKEN_RTN(m_purity.isCached() && m_purity.get() != getPurityRecurse());
|
||||
return nullptr;
|
||||
}
|
||||
bool AstNodeFTask::getPurityRecurse() const {
|
||||
if (this->dpiImport()) return this->dpiPure();
|
||||
// Check the list of statements if it contains any impure statement
|
||||
// or any write reference to a variable that isn't an automatic function local.
|
||||
for (AstNode* stmtp = this->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST(stmtp, Var)) {
|
||||
if (varp->isInoutish() || varp->isRef()) return false;
|
||||
}
|
||||
if (!stmtp->isPure()) return false;
|
||||
if (stmtp->exists([](const AstNodeVarRef* const varrefp) {
|
||||
return (!varrefp->varp()->isFuncLocal() || varrefp->varp()->lifetime().isStatic())
|
||||
&& varrefp->access().isWriteOrRW();
|
||||
}))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void AstNodeBlock::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (unnamed()) str << " [UNNAMED]";
|
||||
@@ -2224,6 +2319,7 @@ void AstBegin::dump(std::ostream& str) const {
|
||||
if (generate()) str << " [GEN]";
|
||||
if (genforp()) str << " [GENFOR]";
|
||||
if (implied()) str << " [IMPLIED]";
|
||||
if (needProcess()) str << " [NPRC]";
|
||||
}
|
||||
void AstCoverDecl::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
@@ -2284,7 +2380,7 @@ void AstCFile::dump(std::ostream& str) const {
|
||||
void AstCFunc::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (slow()) str << " [SLOW]";
|
||||
if (pure()) str << " [PURE]";
|
||||
if (dpiPure()) str << " [DPIPURE]";
|
||||
if (isStatic()) str << " [STATIC]";
|
||||
if (dpiExportDispatcher()) str << " [DPIED]";
|
||||
if (dpiExportImpl()) str << " [DPIEI]";
|
||||
@@ -2314,7 +2410,7 @@ void AstCAwait::dump(std::ostream& str) const {
|
||||
int AstCMethodHard::instrCount() const {
|
||||
if (AstBasicDType* const basicp = fromp()->dtypep()->basicp()) {
|
||||
// TODO: add a more structured description of library methods, rather than using string
|
||||
// matching. See #3715.
|
||||
// matching. See issue #3715.
|
||||
if (basicp->isTriggerVec() && m_name == "word") {
|
||||
// This is an important special case for scheduling so we compute it precisely,
|
||||
// it is simply a load.
|
||||
@@ -2323,6 +2419,75 @@ int AstCMethodHard::instrCount() const {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void AstCMethodHard::setPurity() {
|
||||
static const std::map<std::string, bool> isPureMethod{{"andNot", false},
|
||||
{"any", true},
|
||||
{"anyTriggered", false},
|
||||
{"assign", false},
|
||||
{"at", true},
|
||||
{"atBack", true},
|
||||
{"awaitingCurrentTime", true},
|
||||
{"clear", false},
|
||||
{"clearFired", false},
|
||||
{"commit", false},
|
||||
{"delay", false},
|
||||
{"done", false},
|
||||
{"erase", false},
|
||||
{"evaluate", false},
|
||||
{"evaluation", false},
|
||||
{"exists", true},
|
||||
{"find", true},
|
||||
{"find_first", true},
|
||||
{"find_first_index", true},
|
||||
{"find_index", true},
|
||||
{"find_last", true},
|
||||
{"find_last_index", true},
|
||||
{"fire", false},
|
||||
{"first", false},
|
||||
{"init", false},
|
||||
{"insert", false},
|
||||
{"isFired", true},
|
||||
{"isTriggered", true},
|
||||
{"join", false},
|
||||
{"last", false},
|
||||
{"max", true},
|
||||
{"min", true},
|
||||
{"neq", true},
|
||||
{"next", false},
|
||||
{"pop", false},
|
||||
{"pop_back", false},
|
||||
{"pop_front", false},
|
||||
{"prev", false},
|
||||
{"push", false},
|
||||
{"push_back", false},
|
||||
{"push_front", false},
|
||||
{"r_and", true},
|
||||
{"r_or", true},
|
||||
{"r_product", true},
|
||||
{"r_sum", true},
|
||||
{"r_xor", true},
|
||||
{"renew", false},
|
||||
{"renew_copy", false},
|
||||
{"resume", false},
|
||||
{"reverse", false},
|
||||
{"rsort", false},
|
||||
{"set", false},
|
||||
{"shuffle", false},
|
||||
{"size", true},
|
||||
{"slice", true},
|
||||
{"sliceBackBack", true},
|
||||
{"sliceFrontBack", true},
|
||||
{"sort", false},
|
||||
{"thisOr", false},
|
||||
{"trigger", false},
|
||||
{"unique", true},
|
||||
{"unique_index", true},
|
||||
{"word", true}};
|
||||
|
||||
auto isPureIt = isPureMethod.find(name());
|
||||
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
|
||||
m_pure = isPureIt->second;
|
||||
}
|
||||
const char* AstCFunc::broken() const {
|
||||
BROKEN_RTN((m_scopep && !m_scopep->brokeExists()));
|
||||
return nullptr;
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
|
||||
template <class T_Node, class T_Data, int T_UserN>
|
||||
class AstUserAllocatorBase VL_NOT_FINAL {
|
||||
static_assert(1 <= T_UserN && T_UserN <= 5, "Wrong user pointer number");
|
||||
static_assert(1 <= T_UserN && T_UserN <= 4, "Wrong user pointer number");
|
||||
static_assert(std::is_base_of<AstNode, T_Node>::value, "T_Node must be an AstNode type");
|
||||
|
||||
private:
|
||||
std::vector<T_Data*> m_allocated;
|
||||
std::deque<T_Data> m_allocated;
|
||||
|
||||
T_Data* getUserp(const T_Node* nodep) const {
|
||||
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
|
||||
@@ -45,11 +45,8 @@ private:
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
|
||||
const VNUser user = nodep->user3u();
|
||||
return user.to<T_Data*>();
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
|
||||
const VNUser user = nodep->user4u();
|
||||
return user.to<T_Data*>();
|
||||
} else {
|
||||
const VNUser user = nodep->user5u();
|
||||
const VNUser user = nodep->user4u();
|
||||
return user.to<T_Data*>();
|
||||
}
|
||||
}
|
||||
@@ -61,10 +58,8 @@ private:
|
||||
nodep->user2u(VNUser{userp});
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
|
||||
nodep->user3u(VNUser{userp});
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
|
||||
nodep->user4u(VNUser{userp});
|
||||
} else {
|
||||
nodep->user5u(VNUser{userp});
|
||||
nodep->user4u(VNUser{userp});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,18 +71,11 @@ protected:
|
||||
VNUser2InUse::check();
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
|
||||
VNUser3InUse::check();
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
|
||||
VNUser4InUse::check();
|
||||
} else {
|
||||
VNUser5InUse::check();
|
||||
VNUser4InUse::check();
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~AstUserAllocatorBase() {
|
||||
// Delete all allocated data structures
|
||||
for (T_Data* const p : m_allocated) { delete p; }
|
||||
}
|
||||
|
||||
VL_UNCOPYABLE(AstUserAllocatorBase);
|
||||
|
||||
public:
|
||||
@@ -96,8 +84,8 @@ public:
|
||||
T_Data& operator()(T_Node* nodep, Args&&... args) {
|
||||
T_Data* userp = getUserp(nodep);
|
||||
if (!userp) {
|
||||
userp = new T_Data{std::forward<Args>(args)...};
|
||||
m_allocated.push_back(userp);
|
||||
m_allocated.emplace_back(std::forward<Args>(args)...);
|
||||
userp = &m_allocated.back();
|
||||
setUserp(nodep, userp);
|
||||
}
|
||||
return *userp;
|
||||
@@ -112,6 +100,8 @@ public:
|
||||
|
||||
// Get a pointer to the user data if exists, otherwise nullptr
|
||||
T_Data* tryGet(const T_Node* nodep) { return getUserp(nodep); }
|
||||
|
||||
void clear() { m_allocated.clear(); }
|
||||
};
|
||||
|
||||
// User pointer allocator classes. T_Node is the type of node the allocator should be applied to
|
||||
@@ -125,7 +115,5 @@ template <class T_Node, class T_Data>
|
||||
class AstUser3Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 3> {};
|
||||
template <class T_Node, class T_Data>
|
||||
class AstUser4Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 4> {};
|
||||
template <class T_Node, class T_Data>
|
||||
class AstUser5Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 5> {};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+1
-7
@@ -26,16 +26,10 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Begin.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
+3
-1
@@ -20,13 +20,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Begin final {
|
||||
public:
|
||||
static void debeginAll(AstNetlist* nodep);
|
||||
static void debeginAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+1
-7
@@ -23,16 +23,10 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Branch.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
+3
-1
@@ -20,6 +20,8 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
@@ -27,7 +29,7 @@ class AstNetlist;
|
||||
class V3Branch final {
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
static void branchAll(AstNetlist* nodep);
|
||||
static void branchAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+32
-21
@@ -22,18 +22,10 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstMT.h"
|
||||
|
||||
#include "V3Broken.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
|
||||
#include "V3AstConstOnly.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
@@ -147,22 +139,25 @@ bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinka
|
||||
// Check every node in tree
|
||||
|
||||
class BrokenCheckVisitor final : public VNVisitorConst {
|
||||
bool m_inScope = false; // Under AstScope
|
||||
|
||||
// Constants for marking we are under/not under a node
|
||||
const uint8_t m_brokenCntCurrentNotUnder = s_brokenCntGlobal.get(); // Top bit is clear
|
||||
const uint8_t m_brokenCntCurrentUnder = m_brokenCntCurrentNotUnder | 0x80; // Top bit is set
|
||||
|
||||
// Current CFunc, if any
|
||||
const AstCFunc* m_cfuncp = nullptr;
|
||||
// STATE - across all visitors
|
||||
// All local variables declared in current function
|
||||
std::unordered_set<const AstVar*> m_localVars;
|
||||
std::set<const AstVar*> m_localVars;
|
||||
// Variable references in current function that do not reference an in-scope local
|
||||
std::unordered_map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
|
||||
std::map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
|
||||
// Local variables declared in the scope of the current statement
|
||||
std::vector<std::unordered_set<const AstVar*>> m_localsStack;
|
||||
|
||||
// STATE - for current visit position (use VL_RESTORER)
|
||||
const AstCFunc* m_cfuncp = nullptr; // Current CFunc, if any
|
||||
bool m_inScope = false; // Under AstScope
|
||||
std::set<std::string> m_cFuncNames; // CFunc by name in current class/module
|
||||
|
||||
private:
|
||||
// METHODS
|
||||
static void checkWidthMin(const AstNode* nodep) {
|
||||
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|
||||
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
|
||||
@@ -220,6 +215,7 @@ private:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// VISITORS
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && nodep->brokeLhsMustBeLvalue()
|
||||
@@ -235,10 +231,20 @@ private:
|
||||
}
|
||||
void visit(AstScope* nodep) override {
|
||||
VL_RESTORER(m_inScope);
|
||||
{
|
||||
m_inScope = true;
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
m_inScope = true;
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeUOrStructDType* nodep) override {
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
@@ -260,12 +266,19 @@ private:
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
|
||||
VL_RESTORER(m_cfuncp);
|
||||
m_cfuncp = nodep;
|
||||
m_localVars.clear();
|
||||
m_suspectRefs.clear();
|
||||
m_localsStack.clear();
|
||||
pushLocalScope();
|
||||
|
||||
// Check for duplicate names, otherwise linker might just ignore them
|
||||
string nameArgs = nodep->name();
|
||||
if (!nodep->argTypes().empty()) nameArgs += "(" + nodep->argTypes() + ")";
|
||||
UASSERT_OBJ(m_cFuncNames.emplace(nameArgs).second, nodep,
|
||||
"Duplicate cfunc name: '" << nameArgs << "'");
|
||||
|
||||
processAndIterate(nodep);
|
||||
|
||||
// Check suspect references are all to non-locals
|
||||
@@ -273,8 +286,6 @@ private:
|
||||
UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second,
|
||||
"Local variable not in scope where referenced: " << pair.first);
|
||||
}
|
||||
|
||||
m_cfuncp = nullptr;
|
||||
}
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
// Each branch is a separate local variable scope
|
||||
|
||||
+3
-6
@@ -24,15 +24,12 @@
|
||||
// This transformation honors outputSplitCFuncs.
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3CCtors.h"
|
||||
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
@@ -117,7 +114,7 @@ public:
|
||||
callp->argTypes("vlSymsp");
|
||||
} else {
|
||||
if (m_type.isCoverage()) callp->argTypes("first");
|
||||
callp->selfPointer("this");
|
||||
callp->selfPointer(VSelfPointerText{VSelfPointerText::This()});
|
||||
}
|
||||
rootFuncp->addStmtsp(callp->makeStmt());
|
||||
}
|
||||
@@ -229,7 +226,7 @@ void V3CCtors::evalAsserts() {
|
||||
// if (signal & CONST(upper_non_clean_mask)) { fail; }
|
||||
AstVarRef* const vrefp
|
||||
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
|
||||
vrefp->selfPointer("this");
|
||||
vrefp->selfPointer(VSelfPointerText{VSelfPointerText::This()});
|
||||
AstNodeExpr* newp = vrefp;
|
||||
if (varp->isWide()) {
|
||||
newp = new AstWordSel{
|
||||
|
||||
+4
-2
@@ -20,14 +20,16 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3CCtors final {
|
||||
public:
|
||||
static void cctorsAll();
|
||||
static void cctorsAll() VL_MT_DISABLED;
|
||||
|
||||
private:
|
||||
static void evalAsserts();
|
||||
static void evalAsserts() VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+17
-38
@@ -22,16 +22,10 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3CUse.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
@@ -46,52 +40,31 @@ class CUseVisitor final : public VNVisitor {
|
||||
|
||||
// MEMBERS
|
||||
AstNodeModule* const m_modp; // Current module
|
||||
std::set<std::pair<VUseType, std::string>> m_didUse; // What we already used
|
||||
bool m_dtypesImplOnly = false;
|
||||
std::map<std::string, std::pair<FileLine*, VUseType>> m_didUse; // What we already used
|
||||
|
||||
// METHODS
|
||||
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
|
||||
if (m_dtypesImplOnly
|
||||
&& (useType == VUseType::INT_INCLUDE || useType == VUseType::INT_FWD_CLASS))
|
||||
return;
|
||||
|
||||
if (m_didUse.emplace(useType, name).second) {
|
||||
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
|
||||
m_modp->addStmtsp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
auto e = m_didUse.emplace(name, std::make_pair(nodep->fileline(), useType));
|
||||
if (e.second || ((e.first->second.second & useType) != useType)) {
|
||||
e.first->second.second = e.first->second.second | useType;
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstClassRefDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateAndNextNull(nodep->argsp());
|
||||
|
||||
{
|
||||
VL_RESTORER(m_dtypesImplOnly);
|
||||
m_dtypesImplOnly = true;
|
||||
|
||||
iterateAndNextNull(nodep->initsp());
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
iterateAndNextNull(nodep->finalsp());
|
||||
}
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
}
|
||||
void visit(AstCCall* nodep) override { return; }
|
||||
void visit(AstCReturn* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (m_dtypesImplOnly) {
|
||||
for (AstNode* exprp = nodep->op1p(); exprp; exprp = exprp->nextp()) {
|
||||
if (exprp->dtypep()) iterate(exprp->dtypep());
|
||||
}
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
UASSERT(!nodep->user1SetOnce(), "Visited same return twice.");
|
||||
iterate(nodep->lhsp()->dtypep());
|
||||
}
|
||||
void visit(AstNodeDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
|
||||
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
|
||||
|
||||
@@ -106,7 +79,7 @@ class CUseVisitor final : public VNVisitor {
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
|
||||
if (nodep->dtypep()) iterate(nodep->dtypep());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCell* nodep) override {
|
||||
@@ -121,6 +94,12 @@ public:
|
||||
explicit CUseVisitor(AstNodeModule* modp)
|
||||
: m_modp(modp) {
|
||||
iterate(modp);
|
||||
|
||||
for (auto& used : m_didUse) {
|
||||
AstCUse* const newp = new AstCUse{used.second.first, used.second.second, used.first};
|
||||
m_modp->addStmtsp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
}
|
||||
}
|
||||
~CUseVisitor() override = default;
|
||||
VL_UNCOPYABLE(CUseVisitor);
|
||||
|
||||
+3
-1
@@ -20,11 +20,13 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3CUse final {
|
||||
public:
|
||||
static void cUseAll();
|
||||
static void cUseAll() VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+90
-52
@@ -34,17 +34,12 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Case.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
#define CASE_OVERLAP_WIDTH 16 // Maximum width we can check for overlaps in
|
||||
@@ -139,32 +134,42 @@ private:
|
||||
std::array<AstNode*, 1 << CASE_OVERLAP_WIDTH> m_valueItem;
|
||||
|
||||
// METHODS
|
||||
bool caseIsEnumComplete(AstCase* nodep, uint32_t numCases) {
|
||||
// Return true if case is across an enum, and every value in the case
|
||||
// statement corresponds to one of the enum values
|
||||
if (!nodep->uniquePragma() && !nodep->unique0Pragma()) return false;
|
||||
AstEnumDType* const enumDtp
|
||||
//! Determine whether we should check case items are complete
|
||||
//! @return Enum's dtype if should check, nullptr if shouldn't
|
||||
const AstEnumDType* getEnumCompletionCheckDType(const AstCase* const nodep) {
|
||||
if (!nodep->uniquePragma() && !nodep->unique0Pragma()) return nullptr;
|
||||
const AstEnumDType* const enumDtp
|
||||
= VN_CAST(nodep->exprp()->dtypep()->skipRefToEnump(), EnumDType);
|
||||
if (!enumDtp) return false; // Case isn't enum
|
||||
AstBasicDType* const basicp = enumDtp->subDTypep()->basicp();
|
||||
if (!basicp) return false; // Not simple type (perhaps IEEE illegal)
|
||||
if (basicp->width() > 32) return false;
|
||||
// Find all case values into a set
|
||||
std::set<uint32_t> caseSet;
|
||||
for (uint32_t i = 0; i < numCases; ++i) { // All case items
|
||||
if (m_valueItem[i]) caseSet.emplace(i);
|
||||
}
|
||||
// Find all enum values into a set
|
||||
std::set<uint32_t> enumSet;
|
||||
for (AstEnumItem* itemp = enumDtp->itemsp(); itemp;
|
||||
if (!enumDtp) return nullptr; // Case isn't enum
|
||||
const AstBasicDType* const basicp = enumDtp->subDTypep()->basicp();
|
||||
if (!basicp) return nullptr; // Not simple type (perhaps IEEE illegal)
|
||||
if (basicp->width() > 32) return nullptr;
|
||||
return enumDtp;
|
||||
}
|
||||
//! @return True if case items are complete, false if there are uncovered enums
|
||||
bool checkCaseEnumComplete(const AstCase* const nodep, const AstEnumDType* const dtype) {
|
||||
const uint32_t numCases = 1UL << m_caseWidth;
|
||||
for (AstEnumItem* itemp = dtype->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
||||
AstConst* const econstp = VN_AS(itemp->valuep(), Const);
|
||||
const uint32_t val = econstp->toUInt();
|
||||
// UINFO(9, "Complete enum item " << val << ": " << itemp << endl);
|
||||
enumSet.emplace(val);
|
||||
V3Number nummask{itemp, econstp->width()};
|
||||
nummask.opBitsNonX(econstp->num());
|
||||
const uint32_t mask = nummask.toUInt();
|
||||
V3Number numval{itemp, econstp->width()};
|
||||
numval.opBitsOne(econstp->num());
|
||||
const uint32_t val = numval.toUInt();
|
||||
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if ((i & mask) == val) {
|
||||
if (!m_valueItem[i]) {
|
||||
nodep->v3warn(CASEINCOMPLETE, "Enum item " << itemp->prettyNameQ()
|
||||
<< " not covered by case\n");
|
||||
return false; // enum has uncovered value by case items
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If sets match, all covered
|
||||
return (caseSet == enumSet);
|
||||
return true; // enum is fully covered
|
||||
}
|
||||
bool isCaseTreeFast(AstCase* nodep) {
|
||||
int width = 0;
|
||||
@@ -193,6 +198,8 @@ private:
|
||||
// We can cheat and use uint32_t's because we only support narrow case's
|
||||
bool reportedOverlap = false;
|
||||
bool reportedSubcase = false;
|
||||
bool hasDefaultCase = false;
|
||||
std::map<AstNode*, AstCaseItem*> caseItemMap; // case condition -> case item
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) {
|
||||
@@ -202,6 +209,7 @@ private:
|
||||
if (neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
} else {
|
||||
const bool isCondWildcard = iconstp->num().isAnyXZ();
|
||||
V3Number nummask{itemp, iconstp->width()};
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
const uint32_t mask = nummask.toUInt();
|
||||
@@ -210,16 +218,17 @@ private:
|
||||
const uint32_t val = numval.toUInt();
|
||||
|
||||
uint32_t firstOverlap = 0;
|
||||
bool foundOverlap = false;
|
||||
AstNode* overlappedCondp = nullptr;
|
||||
bool foundHit = false;
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if ((i & mask) == val) {
|
||||
if (!m_valueItem[i]) {
|
||||
m_valueItem[i] = itemp;
|
||||
m_valueItem[i] = icondp;
|
||||
caseItemMap[icondp] = itemp;
|
||||
foundHit = true;
|
||||
} else if (!foundOverlap) {
|
||||
} else if (!overlappedCondp) {
|
||||
firstOverlap = i;
|
||||
foundOverlap = true;
|
||||
overlappedCondp = m_valueItem[i];
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
}
|
||||
}
|
||||
@@ -227,9 +236,19 @@ private:
|
||||
if (!nodep->priorityPragma()) {
|
||||
// If this case statement doesn't have the priority
|
||||
// keyword, we want to warn on any overlap.
|
||||
if (!reportedOverlap && foundOverlap) {
|
||||
icondp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x"
|
||||
<< std::hex << firstOverlap << ")");
|
||||
if (!reportedOverlap && overlappedCondp) {
|
||||
std::ostringstream examplePattern;
|
||||
if (isCondWildcard) {
|
||||
examplePattern << " (example pattern 0x" << std::hex
|
||||
<< firstOverlap << ")";
|
||||
}
|
||||
icondp->v3warn(CASEOVERLAP,
|
||||
"Case conditions overlap"
|
||||
<< examplePattern.str() << "\n"
|
||||
<< icondp->warnContextPrimary() << '\n'
|
||||
<< overlappedCondp->warnOther()
|
||||
<< "... Location of overlapping condition\n"
|
||||
<< overlappedCondp->warnContextSecondary());
|
||||
reportedOverlap = true;
|
||||
}
|
||||
} else {
|
||||
@@ -240,7 +259,11 @@ private:
|
||||
if (!reportedSubcase && !foundHit) {
|
||||
icondp->v3warn(CASEOVERLAP,
|
||||
"Case item ignored: every matching value is covered "
|
||||
"by an earlier item");
|
||||
"by an earlier condition\n"
|
||||
<< icondp->warnContextPrimary() << '\n'
|
||||
<< overlappedCondp->warnOther()
|
||||
<< "... Location of previous condition\n"
|
||||
<< overlappedCondp->warnContextPrimary());
|
||||
reportedSubcase = true;
|
||||
}
|
||||
}
|
||||
@@ -251,17 +274,28 @@ private:
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (!m_valueItem[i]) m_valueItem[i] = itemp;
|
||||
}
|
||||
caseItemMap[itemp] = itemp;
|
||||
hasDefaultCase = true;
|
||||
}
|
||||
}
|
||||
if (!caseIsEnumComplete(nodep, numCases)) {
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (!m_valueItem[i]) {
|
||||
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered "
|
||||
"(example pattern 0x"
|
||||
<< std::hex << i << ")");
|
||||
if (!hasDefaultCase) {
|
||||
const AstEnumDType* const dtype = getEnumCompletionCheckDType(nodep);
|
||||
if (dtype) {
|
||||
if (!checkCaseEnumComplete(nodep, dtype)) {
|
||||
// checkCaseEnumComplete has already warned of incompletion
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (!m_valueItem[i]) { // has uncovered case
|
||||
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered "
|
||||
"(example pattern 0x"
|
||||
<< std::hex << i << ")");
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,8 +308,10 @@ private:
|
||||
// Convert valueItem from AstCaseItem* to the expression
|
||||
// Not done earlier, as we may now have a nullptr because it's just a ";" NOP branch
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (AstCaseItem* const itemp = VN_AS(m_valueItem[i], CaseItem)) {
|
||||
m_valueItem[i] = itemp->stmtsp();
|
||||
if (AstNode* const condp = m_valueItem[i]) {
|
||||
AstCaseItem* caseItemp = caseItemMap[condp];
|
||||
UASSERT(caseItemp, "caseItemp should exist");
|
||||
m_valueItem[i] = caseItemp->stmtsp();
|
||||
}
|
||||
}
|
||||
return true; // All is fine
|
||||
@@ -321,10 +357,10 @@ private:
|
||||
|
||||
// Alternate scheme if we ever do multiple bits at a time:
|
||||
// V3Number nummask (cexprp, cexprp->width(), (1UL<<msb));
|
||||
// AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTree(false),
|
||||
// AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTreePure(false),
|
||||
// new AstConst(cexprp->fileline(), nummask));
|
||||
AstNodeExpr* const and1p
|
||||
= new AstSel{cexprp->fileline(), cexprp->cloneTree(false), msb, 1};
|
||||
= new AstSel{cexprp->fileline(), cexprp->cloneTreePure(false), msb, 1};
|
||||
AstNodeExpr* const eqp
|
||||
= new AstNeq{cexprp->fileline(), new AstConst{cexprp->fileline(), 0}, and1p};
|
||||
AstIf* const ifp = new AstIf{cexprp->fileline(), eqp, tree1p, tree0p};
|
||||
@@ -409,7 +445,7 @@ private:
|
||||
V3Number numval{itemp, iconstp->width()};
|
||||
numval.opBitsOne(iconstp->num());
|
||||
AstNodeExpr* const and1p
|
||||
= new AstAnd{itemp->fileline(), cexprp->cloneTree(false),
|
||||
= new AstAnd{itemp->fileline(), cexprp->cloneTreePure(false),
|
||||
new AstConst{itemp->fileline(), nummask}};
|
||||
AstNodeExpr* const and2p = new AstAnd{
|
||||
itemp->fileline(), new AstConst{itemp->fileline(), numval},
|
||||
@@ -419,7 +455,7 @@ private:
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
} else {
|
||||
// Not a caseX mask, we can build CASEEQ(cexpr icond)
|
||||
AstNodeExpr* const and1p = cexprp->cloneTree(false);
|
||||
AstNodeExpr* const and1p = cexprp->cloneTreePure(false);
|
||||
AstNodeExpr* const and2p = icondp;
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
}
|
||||
@@ -461,7 +497,7 @@ private:
|
||||
if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
|
||||
if (depth == 1) { // First group or starting new group
|
||||
itemnextp = nullptr;
|
||||
AstIf* const newp = new AstIf{itemp->fileline(), ifexprp->cloneTree(true)};
|
||||
AstIf* const newp = new AstIf{itemp->fileline(), ifexprp->cloneTreePure(true)};
|
||||
if (groupnextp) {
|
||||
groupnextp->addElsesp(newp);
|
||||
} else {
|
||||
@@ -471,7 +507,7 @@ private:
|
||||
} else { // Continue group, modify if condition to OR in this new condition
|
||||
AstNodeExpr* const condp = groupnextp->condp()->unlinkFrBack();
|
||||
groupnextp->condp(
|
||||
new AstOr{ifexprp->fileline(), condp, ifexprp->cloneTree(true)});
|
||||
new AstOr{ifexprp->fileline(), condp, ifexprp->cloneTreePure(true)});
|
||||
}
|
||||
}
|
||||
{ // Make the new lower IF and attach in the tree
|
||||
@@ -543,10 +579,12 @@ private:
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
void visit(AstNode* nodep) override {
|
||||
if (VN_IS(nodep, Always)) m_alwaysp = nodep;
|
||||
void visit(AstAlways* nodep) override {
|
||||
VL_RESTORER(m_alwaysp)
|
||||
m_alwaysp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
||||
+4
-2
@@ -20,6 +20,8 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
class AstNodeCase;
|
||||
|
||||
@@ -27,8 +29,8 @@ class AstNodeCase;
|
||||
|
||||
class V3Case final {
|
||||
public:
|
||||
static void caseAll(AstNetlist* nodep);
|
||||
static void caseLint(AstNodeCase* nodep);
|
||||
static void caseAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
static void caseLint(AstNodeCase* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+13
-10
@@ -37,16 +37,10 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Cast.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
@@ -56,7 +50,7 @@ class CastVisitor final : public VNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNode::user() // bool. Indicates node is of known size
|
||||
// AstNode::user1() // bool. Indicates node is of known size
|
||||
const VNUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
@@ -64,12 +58,12 @@ private:
|
||||
// METHODS
|
||||
|
||||
void insertCast(AstNodeExpr* nodep, int needsize) { // We'll insert ABOVE passed node
|
||||
UINFO(4, " NeedCast " << nodep << endl);
|
||||
VNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
//
|
||||
AstCCast* const castp
|
||||
= new AstCCast{nodep->fileline(), nodep, needsize, nodep->widthMin()};
|
||||
UINFO(4, " MadeCast " << static_cast<void*>(castp) << " for " << nodep << endl);
|
||||
relinkHandle.relink(castp);
|
||||
// if (debug() > 8) castp->dumpTree("- castins: ");
|
||||
//
|
||||
@@ -168,6 +162,15 @@ private:
|
||||
ensureLower32Cast(nodep);
|
||||
nodep->user1(1);
|
||||
}
|
||||
void visit(AstConsPackMember* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (VN_IS(nodep->rhsp()->dtypep()->skipRefp(), BasicDType)) ensureCast(nodep->rhsp());
|
||||
nodep->user1(1);
|
||||
}
|
||||
void visit(AstExprStmt* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(1);
|
||||
}
|
||||
void visit(AstNegate* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
@@ -187,7 +190,7 @@ private:
|
||||
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, StructSel) && !VN_IS(backp, RedXor)
|
||||
&& (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec()
|
||||
&& !nodep->varp()->basicp()->isForkSync()
|
||||
&& !nodep->varp()->basicp()->isProcessRef())
|
||||
&& !nodep->varp()->basicp()->isProcessRef() && !nodep->varp()->basicp()->isEvent())
|
||||
&& backp->width() && castSize(nodep) != castSize(nodep->varp())) {
|
||||
// Cast vars to IData first, else below has upper bits wrongly set
|
||||
// CData x=3; out = (QData)(x<<30);
|
||||
|
||||
+3
-1
@@ -20,13 +20,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Cast final {
|
||||
public:
|
||||
static void castAll(AstNetlist* nodep);
|
||||
static void castAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+10
-17
@@ -20,13 +20,10 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Class.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
@@ -121,17 +118,17 @@ private:
|
||||
void visit(AstVar* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep) {
|
||||
if (m_ftaskp && m_ftaskp->lifetime().isStatic()) {
|
||||
if (m_ftaskp && m_ftaskp->isStatic()) {
|
||||
// Move later, or we wouldn't keep iterating the class
|
||||
// We're really moving the VarScope but we might not
|
||||
// have a pointer to it yet
|
||||
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
|
||||
m_toScopeMoves.emplace_back(nodep, m_packageScopep);
|
||||
}
|
||||
if (!m_ftaskp && nodep->lifetime().isStatic()) {
|
||||
m_toPackageMoves.emplace_back(std::make_pair(nodep, m_classPackagep));
|
||||
m_toPackageMoves.emplace_back(nodep, m_classPackagep);
|
||||
// We're really moving the VarScope but we might not
|
||||
// have a pointer to it yet
|
||||
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
|
||||
m_toScopeMoves.emplace_back(nodep, m_packageScopep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,8 +143,8 @@ private:
|
||||
{
|
||||
m_ftaskp = nodep;
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep && nodep->lifetime().isStatic()) {
|
||||
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
|
||||
if (m_packageScopep && nodep->isStatic()) {
|
||||
m_toScopeMoves.emplace_back(nodep, m_packageScopep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,7 +153,7 @@ private:
|
||||
// Don't move now, or wouldn't keep iterating the class
|
||||
// TODO move function statics only
|
||||
// if (m_classScopep) {
|
||||
// m_toScopeMoves.push_back(std::make_pair(nodep, m_classScopep));
|
||||
// m_toScopeMoves.emplace_back(nodep, m_classScopep);
|
||||
//}
|
||||
}
|
||||
void visit(AstCoverDecl* nodep) override {
|
||||
@@ -167,16 +164,12 @@ private:
|
||||
void visit(AstInitial* nodep) override {
|
||||
// But not AstInitialAutomatic, which remains under the class
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep) {
|
||||
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
|
||||
}
|
||||
if (m_packageScopep) { m_toScopeMoves.emplace_back(nodep, m_packageScopep); }
|
||||
}
|
||||
void visit(AstInitialStatic* nodep) override {
|
||||
// But not AstInitialAutomatic, which remains under the class
|
||||
iterateChildren(nodep);
|
||||
if (m_packageScopep) {
|
||||
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
|
||||
}
|
||||
if (m_packageScopep) { m_toScopeMoves.emplace_back(nodep, m_packageScopep); }
|
||||
}
|
||||
|
||||
void setStructModulep(AstNodeUOrStructDType* const dtypep) {
|
||||
|
||||
+3
-1
@@ -20,13 +20,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Class final {
|
||||
public:
|
||||
static void classAll(AstNetlist* nodep);
|
||||
static void classAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+1
-7
@@ -23,16 +23,10 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Clean.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
+3
-1
@@ -20,13 +20,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Clean final {
|
||||
public:
|
||||
static void cleanAll(AstNetlist* nodep);
|
||||
static void cleanAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+1
-6
@@ -27,17 +27,12 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Clock.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Sched.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
+3
-1
@@ -20,13 +20,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Clock final {
|
||||
public:
|
||||
static void clockAll(AstNetlist* nodep);
|
||||
static void clockAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+1
-4
@@ -19,15 +19,12 @@
|
||||
// Also drop empty CFuncs
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Combine.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3AstUserAllocator.h"
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
+3
-1
@@ -20,13 +20,15 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3ThreadSafety.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Combine final {
|
||||
public:
|
||||
static void combineAll(AstNetlist* nodep);
|
||||
static void combineAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+2
-5
@@ -20,14 +20,11 @@
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3Common.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
@@ -116,7 +113,7 @@ static void makeToStringMiddle(AstClass* nodep) {
|
||||
std::string comma;
|
||||
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const auto* const varp = VN_CAST(itemp, Var)) {
|
||||
if (!varp->isParam()) {
|
||||
if (!varp->isParam() && !varp->isInternal()) {
|
||||
string stmt = "out += \"";
|
||||
stmt += comma;
|
||||
comma = ", ";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user