mirror of
https://github.com/verilator/verilator.git
synced 2026-10-03 08:33:21 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
960813cb0f | ||
|
|
cd56dfee1c | ||
|
|
3e03cd5a4d | ||
|
|
cd9e9da4b5 | ||
|
|
f64d9d87f6 | ||
|
|
43aa3229fb | ||
|
|
27d53691bd | ||
|
|
959793cde3 | ||
|
|
c3d64d9743 | ||
|
|
f9806595f2 | ||
|
|
34a0bb448e | ||
|
|
8681861be9 | ||
|
|
fffc63970d | ||
|
|
8f527a0c6e | ||
|
|
c69ddc46f8 | ||
|
|
4ec8bc3589 | ||
|
|
536bdf506e | ||
|
|
d1836b7b6f | ||
|
|
00fe36f44c | ||
|
|
5adc856950 | ||
|
|
bc3e24c8cd | ||
|
|
2c8456ea75 | ||
|
|
519cc7d61c | ||
|
|
0f4daa12e4 | ||
|
|
6bad0e14ce | ||
|
|
3ec3c2c268 | ||
|
|
b8e804f05b | ||
|
|
cdeb6e792f | ||
|
|
deebfa3239 | ||
|
|
f937e3282f | ||
|
|
b46664c70a | ||
|
|
33af14eba8 | ||
|
|
18b0f6387d | ||
|
|
43ecaec9a0 | ||
|
|
275885a797 | ||
|
|
68aa731ce9 | ||
|
|
b90fce55f4 | ||
|
|
44173b40a1 | ||
|
|
ab13a2ebdc | ||
|
|
19875d2e85 | ||
|
|
1de33b9fb7 | ||
|
|
90c917ee83 | ||
|
|
4ab4c0c8ba | ||
|
|
9907d211ff | ||
|
|
13933743ad | ||
|
|
fc25721e55 | ||
|
|
fa64f8fb32 | ||
|
|
ab4063f098 | ||
|
|
8bb77f86ec | ||
|
|
42d918c163 | ||
|
|
e3dd70c373 | ||
|
|
9273fafbbf | ||
|
|
d9c893af11 | ||
|
|
a9c4a96c0f | ||
|
|
32e99ba0d9 | ||
|
|
da8930a57a | ||
|
|
1166728a8d | ||
|
|
30fa5e41be | ||
|
|
cf39331d1f | ||
|
|
bac84726e1 | ||
|
|
ddef61d62e | ||
|
|
4081a1a539 | ||
|
|
1691bbfe25 | ||
|
|
17cc452f79 | ||
|
|
8ecdc85cf7 | ||
|
|
5ad3c4e499 | ||
|
|
8073e8bb46 | ||
|
|
8a9d6d225b | ||
|
|
c7499133b2 | ||
|
|
687dd440a9 | ||
|
|
f1bb0bb725 | ||
|
|
a8168d5d62 | ||
|
|
896b18e024 | ||
|
|
766ad14ae0 | ||
|
|
add3811f46 | ||
|
|
f55177a49f | ||
|
|
bdceb08963 | ||
|
|
825d6b87a8 | ||
|
|
61e2e55ba5 | ||
|
|
e6b75f752b | ||
|
|
a4f5d95648 | ||
|
|
686baaf2cf | ||
|
|
76b3776fa3 | ||
|
|
3ed2af638b | ||
|
|
8e2ba6a003 | ||
|
|
3aa6332f25 | ||
|
|
36599133bf | ||
|
|
941f96c913 | ||
|
|
93578d64d2 | ||
|
|
8c705ee145 | ||
|
|
c5c5f11e16 | ||
|
|
fd0446f481 | ||
|
|
2ebed755e6 | ||
|
|
2a7aa28b20 | ||
|
|
fb56f4f880 | ||
|
|
d4e73e215e | ||
|
|
d5c9369103 | ||
|
|
af27702188 | ||
|
|
2c813488f4 | ||
|
|
708abe0dd1 | ||
|
|
22e0f3edbe | ||
|
|
2cdf0aae72 | ||
|
|
ee4a601c9e | ||
|
|
e7b6a7ef6a | ||
|
|
2825940fad | ||
|
|
c6f6dab413 | ||
|
|
bbbee8b89e | ||
|
|
5250408019 | ||
|
|
a198a3c1e1 | ||
|
|
f311a0a5a5 | ||
|
|
ec1c112791 | ||
|
|
7ecc2d4df7 | ||
|
|
512fe0a2d1 | ||
|
|
ec4eb18846 | ||
|
|
f064a94f1d | ||
|
|
19398efc4c | ||
|
|
fcb8bc22bd | ||
|
|
2ee52222e2 | ||
|
|
c11cd18491 | ||
|
|
eebda248c7 | ||
|
|
8d737271ca | ||
|
|
52cde49a6f | ||
|
|
6c9c16c31d | ||
|
|
0c93c3844f | ||
|
|
e5e5bc0fa3 | ||
|
|
d6237e55b2 | ||
|
|
3ec1e9eb07 | ||
|
|
d5bdd07c01 | ||
|
|
5fddf51e8c | ||
|
|
0a28fc8c63 | ||
|
|
9eafca5e28 | ||
|
|
729bd268de | ||
|
|
6c332a2f8e | ||
|
|
a8f83d5758 | ||
|
|
65bfb4e5ff | ||
|
|
c75a686081 | ||
|
|
2705715bb1 | ||
|
|
208f1504fb | ||
|
|
24b5215cf9 | ||
|
|
6016e74b55 | ||
|
|
0c4d88bacc | ||
|
|
01a54d6960 | ||
|
|
23fc08bdf9 | ||
|
|
13ddd0bc1c | ||
|
|
31121141db | ||
|
|
9d3e800311 | ||
|
|
7280307a39 | ||
|
|
c207e98306 | ||
|
|
60d5f0e86b | ||
|
|
18cabc369b | ||
|
|
cd871ca79e | ||
|
|
c67fe02f06 | ||
|
|
2d2bd5b95e | ||
|
|
02835f199b | ||
|
|
e6e7bd8d83 | ||
|
|
b1c7de4ad5 | ||
|
|
f0f68f42d1 |
@@ -108,4 +108,3 @@ Standard: Cpp11
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
|
||||
|
||||
+71
-70
@@ -16,128 +16,129 @@ env:
|
||||
CI_COMMIT: ${{ github.sha }}
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_LIMIT_MULTIPLE: 0.95
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: repo
|
||||
|
||||
jobs:
|
||||
|
||||
|
||||
Matrix:
|
||||
runs-on: ubuntu-latest
|
||||
name: Generate Build matrix
|
||||
outputs:
|
||||
matrix: ${{ steps.generate.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: generate
|
||||
name: Run 'generate_matrix.sh'
|
||||
run: |
|
||||
if [ '${{ github.event_name }}' = 'pull_request' ]; then
|
||||
matrix='[ "ubuntu-20.04" ]'
|
||||
else
|
||||
matrix='[ "ubuntu-16.04", "ubuntu-18.04", "ubuntu-20.04" ]'
|
||||
fi
|
||||
echo "::set-output name=matrix::$matrix"
|
||||
|
||||
|
||||
Build:
|
||||
needs: Matrix
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{ fromJson(needs.Matrix.outputs.matrix) }}
|
||||
debug: [ opt, dbg ]
|
||||
os: [ubuntu-20.04, ubuntu-18.04]
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
m32: [0, 1]
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-20.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-20.04
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.debug }}
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} ${{ matrix.m32 && '| -m32' || '' }}
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
CI_RUNS_ON: ${{ matrix.os }}
|
||||
CI_MAKE_SRC_TARGET: ${{ matrix.debug }}
|
||||
CI_M32: ${{ matrix.m32 }}
|
||||
CC: ${{ matrix.compiler.cc }}
|
||||
CXX: ${{ matrix.compiler.cxx }}
|
||||
CACHE_KEY: build-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.debug }}
|
||||
CCACHE_MAXSIZE: 256Mi # Per build matrix entry
|
||||
CACHE_BASE_KEY: build-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}
|
||||
CCACHE_MAXSIZE: 250M # Per build matrix entry (2000M in total)
|
||||
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}${{ matrix.m32 && '-m32' || '' }}.tar.gz
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: repo
|
||||
|
||||
- name: Cache
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: ccache
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
|
||||
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ env.CACHE_KEY }}-
|
||||
|
||||
- name: Install packages for build
|
||||
run: ./ci/ci-install.bash
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
ccache -z
|
||||
./ci/ci-script.bash
|
||||
ccache -s
|
||||
tar cvzf verilator-${{ matrix.os}}-${CI_COMMIT}-${{ matrix.compiler.cc }}-${{ matrix.debug }}.tgz ./bin
|
||||
run: ./ci/ci-script.bash
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
- name: Tar up repository
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
|
||||
|
||||
- name: Upload tar archive
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: verilator-${{ matrix.os}}-${{ env.CI_COMMIT }}-${{ matrix.compiler.cc }}-${{ matrix.debug }}.tgz
|
||||
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
|
||||
|
||||
Test:
|
||||
needs: [ Matrix, Build ]
|
||||
test:
|
||||
needs: build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{ fromJson(needs.Matrix.outputs.matrix) }}
|
||||
os: [ubuntu-20.04, ubuntu-18.04]
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
suite:
|
||||
- dist-vlt-0
|
||||
- dist-vlt-1
|
||||
- dist-vlt-2
|
||||
- vltmt-0
|
||||
- vltmt-1
|
||||
m32: [0, 1]
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-20.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-20.04
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }}
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }} ${{ matrix.m32 && '| -m32' || '' }}
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: test
|
||||
CI_RUNS_ON: ${{ matrix.os }}
|
||||
CI_M32: ${{ matrix.m32 }}
|
||||
CC: ${{ matrix.compiler.cc }}
|
||||
CXX: ${{ matrix.compiler.cxx }}
|
||||
CACHE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.suite }}
|
||||
CCACHE_MAXSIZE: 256Mi # Per build matrix entry
|
||||
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}-${ matrix.suite }}
|
||||
CCACHE_MAXSIZE: 64M # Per build matrix entry (2160M in total)
|
||||
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}${{ matrix.m32 && '-m32' || '' }}.tar.gz
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Download tar archive
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
path: ${{ github.workspace }}
|
||||
|
||||
- name: Cache
|
||||
- name: Unpack tar archive
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: tar -x -z -f ${{ env.VERILATOR_ARCHIVE }}
|
||||
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: ccache
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
|
||||
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ env.CACHE_KEY }}-
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
|
||||
- name: Install Verilator and test dependencies
|
||||
run: |
|
||||
tar xvzf artifact/verilator-${{ matrix.os}}-${CI_COMMIT}-${{ matrix.compiler.cc }}-opt.tgz
|
||||
tar xvzf artifact/verilator-${{ matrix.os}}-${CI_COMMIT}-${{ matrix.compiler.cc }}-dbg.tgz
|
||||
rm -rf artifact
|
||||
./ci/ci-install.bash
|
||||
- name: Install test dependencies
|
||||
run: ./ci/ci-install.bash
|
||||
|
||||
- name: Test
|
||||
env:
|
||||
TESTS: ${{ matrix.suite }}
|
||||
run: |
|
||||
ccache -z
|
||||
./ci/ci-script.bash
|
||||
ccache -s
|
||||
run: ./ci/ci-script.bash
|
||||
|
||||
@@ -11,9 +11,14 @@ on:
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CI_COMMIT: ${{ github.sha }}
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CI_M32: 0
|
||||
COVERAGE: 1
|
||||
VERILATOR_ARCHIVE: verilator-coverage-${{ github.sha }}.tar.gz
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: repo
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -23,37 +28,28 @@ jobs:
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CACHE_KEY: coverage-build
|
||||
CCACHE_MAXSIZE: 512Mi
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: ccache
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
|
||||
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
|
||||
path: repo
|
||||
|
||||
- name: Install packages for build
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
run: ./ci/ci-install.bash
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
ccache -z
|
||||
./ci/ci-script.bash
|
||||
ccache -s
|
||||
tar cvzf verilator-${CI_COMMIT}-coverage.tgz bin src/obj*/*.o src/obj*/*.gcno
|
||||
run: ./ci/ci-script.bash
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
- name: Tar up repository
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
|
||||
|
||||
- name: Upload tar archive
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: verilator-${{ env.CI_COMMIT }}-coverage.tgz
|
||||
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
|
||||
|
||||
Test:
|
||||
@@ -82,34 +78,22 @@ jobs:
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: test
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CACHE_KEY: coverage-test-${{ matrix.test }}${{ matrix.num }}
|
||||
CCACHE_MAXSIZE: 512Mi # Per build matrix entry
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: ccache
|
||||
- name: Download tar archive
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ env.CACHE_KEY }}-${{ env.cache-name }}-${{ github.sha }}
|
||||
restore-keys: ${{ env.CACHE_KEY }}-${{ env.cache-name }}
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
path: ${{ github.workspace }}
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- name: Unpack tar archive
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: tar -x -z -f ${{ env.VERILATOR_ARCHIVE }}
|
||||
|
||||
- name: Install Verilator and test dependencies
|
||||
run: |
|
||||
tar xvzf artifact/verilator-${CI_COMMIT}-coverage.tgz
|
||||
touch src/obj*/*.o src/obj*/*.gcno
|
||||
./ci/ci-install.bash
|
||||
- name: Install test dependencies
|
||||
run: ./ci/ci-install.bash
|
||||
|
||||
- name: Test
|
||||
env:
|
||||
TESTS: coverage-${{ matrix.test }}${{ matrix.num }}
|
||||
run: |
|
||||
ccache -z
|
||||
./ci/ci-script.bash
|
||||
ccache -s
|
||||
run: ./ci/ci-script.bash
|
||||
|
||||
@@ -16,6 +16,7 @@ jobs:
|
||||
CI_OS_NAME: linux
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CI_COMMIT: ${{ github.sha }}
|
||||
CI_M32: 0
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
@@ -26,14 +27,14 @@ jobs:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
run: |
|
||||
bash ci/ci-install.bash &&
|
||||
sudo apt-get install clang-10 yapf3 &&
|
||||
sudo apt-get install clang-format-11 yapf3 &&
|
||||
git config --global user.email "[email protected]" &&
|
||||
git config --global user.name "github action"
|
||||
- name: Format code
|
||||
run: |
|
||||
autoconf &&
|
||||
./configure &&
|
||||
make -j 2 format CLANGFORMAT=clang-format-10 &&
|
||||
make -j 2 format CLANGFORMAT=clang-format-11 &&
|
||||
git status
|
||||
- name: Push
|
||||
run: |
|
||||
|
||||
@@ -8,6 +8,61 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 4.212 2021-09-01
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore]
|
||||
* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore]
|
||||
* Support timeunit/timeprecision in $unit.
|
||||
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski]
|
||||
* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded
|
||||
model scheduling. Default value changed to 200 (#3068). [Yinan Xu]
|
||||
* Output files are split based on the set of headers required
|
||||
in order to aid incremental compilation via ccache (#3071). [Geza Lore]
|
||||
* Parameter values are now emitted as 'static constexpr' instead of enum.
|
||||
C++ direct references to parameters might require updating (#3077). [Geza Lore]
|
||||
* Refactored Verilated include files; include verilated.h not verilated_heavy.h.
|
||||
* Add header guards on Dpi.h generated files (#2979). [Tood Strader]
|
||||
* Add XML ccall, constpool, initarray, and if/while begins (#3080). [Steven Hugg]
|
||||
* Add error when constant function under a generate (#3103). [Don Owen]
|
||||
* Fix -G to treat simple integer literals as signed (#3060). [Anikin1610]
|
||||
* Fix emitted string array initializers (#2895). [Iztok Jeras]
|
||||
* Fix bitop tree optimization dropping necessary & operator (#3096). [Flavien Solt]
|
||||
* Fix internal error on wide -x-initial unique (#3106). [Alexandre Joannou]
|
||||
* Fix traces to show array instances with brackets (#3092) (#3095). [Pieter Kapsenberg]
|
||||
|
||||
|
||||
Verilator 4.210 2021-07-07
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* Generated code is now emitted as global functions rather than methods. '$c'
|
||||
contents might need to be updated, see the docs (#3006). [Geza Lore]
|
||||
* The generated model class instantiated by the user is now an interface
|
||||
object and no longer the TOP module instance. User code with direct
|
||||
C++ member access to model internals, including verilator public_flat
|
||||
items will likely need to be updated. See the manual for instructions:
|
||||
https://verilator.org/guide/latest/connecting.html#porting-from-pre-4-210
|
||||
(#3036). [Geza Lore]
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add --prof-c to pass profiling to compiler (#3059). [Alexander Grobman]
|
||||
* Optimize a lot more model variables into function locals (#3027). [Geza Lore]
|
||||
* Support middle-of-design nested topmodules (#3026). [Dan Petrisko]
|
||||
* Remove deprecated --no-relative-cfuncs option (#3024). [Geza Lore]
|
||||
* Remove deprecated --inhibit-sim option (#3035). [Geza Lore]
|
||||
* Merge const static data globally into a new constant pool (#3013). [Geza Lore]
|
||||
* Allow configure override of AR program (#2999). [ahouska]
|
||||
* In XML, show pinIndex information (#2877). [errae233]
|
||||
* Fix error on unsupported recursive functions (#2957). [Trefor Southwell]
|
||||
* Fix type parameter specialization when struct names are same (#3055). [7FM]
|
||||
* Improve speed of table optimization (-OA) pass. [Geza Lore]
|
||||
|
||||
|
||||
Verilator 4.204 2021-06-12
|
||||
==========================
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
\.ccache/
|
||||
\.clang-format
|
||||
\.clang-tidy
|
||||
\.git/
|
||||
\.git$
|
||||
\.github/
|
||||
\.svn/
|
||||
\.(bak|old)/
|
||||
\.(bak|old)$
|
||||
\.tar\.
|
||||
.*\.tgz
|
||||
.*\.log
|
||||
\..*\.swp
|
||||
.*\.tmp
|
||||
.*\.tidy
|
||||
.*\.tex
|
||||
.*\.key
|
||||
.*\.vcd
|
||||
.*\.1
|
||||
\.codacy\.yml
|
||||
\.travis\.yml
|
||||
/_build/
|
||||
/build/
|
||||
/obj_dbg/
|
||||
/obj_dir/
|
||||
/obj_dist/
|
||||
/obj_iv/
|
||||
/obj_ms/
|
||||
/obj_nc/
|
||||
/obj_opt/
|
||||
/obj_vcs/
|
||||
/obj_vlt/
|
||||
/obj_vltmt/
|
||||
INCA_libs/
|
||||
/cov_work/
|
||||
/logs/
|
||||
^Makefile$
|
||||
README.html
|
||||
bin/verilator_bin.*
|
||||
bin/verilator_coverage_bin.*
|
||||
docs/.*\.html$
|
||||
docs/_build/
|
||||
docs/clang-format.txt$
|
||||
docs/doxygen-doc/.*
|
||||
docs/spelling.txt
|
||||
examples/xml_py/copied/
|
||||
examples/xml_py/graph.*
|
||||
sonar-project.properties
|
||||
src/Makefile$
|
||||
src/Makefile_obj$
|
||||
include/verilated.mk$
|
||||
test_regress/.gdbinit$
|
||||
test_regress/transcript
|
||||
codecov.yml
|
||||
config.cache$
|
||||
config.status$
|
||||
verilator\.log
|
||||
verilator\.tex
|
||||
verilator-config.cmake$
|
||||
verilator-config-version.cmake$
|
||||
verilator.pc$
|
||||
verilator_bin.*
|
||||
verilator_coverage_bin.*
|
||||
.vcsmx_rebuild$
|
||||
ncverilog.history
|
||||
autom4te\.cache/
|
||||
nodist/
|
||||
ci/
|
||||
/simv$
|
||||
/simv.daidir/
|
||||
/vc_hdrs.h$
|
||||
/csrc/
|
||||
obj_dir.*
|
||||
TAGS
|
||||
gmon.out
|
||||
.*~
|
||||
+8
-151
@@ -118,81 +118,6 @@ INFOS = verilator.html verilator.pdf
|
||||
|
||||
INFOS_OLD = README README.html README.pdf
|
||||
|
||||
# Files that can be generated, but should be up to date for a distribution.
|
||||
DISTDEP = info Makefile
|
||||
|
||||
DISTFILES1 = $(INFOS) .gitignore \
|
||||
*.in *.ac \
|
||||
Artistic \
|
||||
Changes \
|
||||
LICENSE \
|
||||
MANIFEST.SKIP \
|
||||
README.rst \
|
||||
verilator-config.cmake.in \
|
||||
verilator-config-version.cmake.in \
|
||||
bin/verilator \
|
||||
bin/verilator_ccache_report \
|
||||
bin/verilator_coverage \
|
||||
bin/verilator_difftree \
|
||||
bin/verilator_gantt \
|
||||
bin/verilator_includer \
|
||||
bin/verilator_profcfunc \
|
||||
docs/.gitignore \
|
||||
docs/CONTRIBUTING.rst \
|
||||
docs/CONTRIBUTORS \
|
||||
docs/Makefile \
|
||||
docs/_static/*.png \
|
||||
docs/_static/css/* \
|
||||
docs/bin/* \
|
||||
docs/gen/* \
|
||||
docs/guide/*.py \
|
||||
docs/guide/*.rst \
|
||||
docs/guide/figures/* \
|
||||
docs/install.rst \
|
||||
docs/internals.rst \
|
||||
docs/internals.rst \
|
||||
docs/verilated.dox \
|
||||
docs/xml.rst \
|
||||
install-sh configure *.pod \
|
||||
include/*.[chv]* \
|
||||
include/*.in \
|
||||
include/.*ignore \
|
||||
include/gtkwave/*.[chv]* \
|
||||
include/vltstd/*.[chv]* \
|
||||
.*attributes */.*attributes */*/.*attributes \
|
||||
src/.*ignore src/*.in src/*.cpp src/*.[chly] \
|
||||
src/astgen src/bisonpre src/*fix src/cppcheck_filtered \
|
||||
src/config_rev \
|
||||
src/vlcovgen src/mkinstalldirs \
|
||||
src/.gdbinit \
|
||||
src/*.pl src/*.pod \
|
||||
examples/*/.*ignore examples/*/Makefile* \
|
||||
examples/*/*.[chv]* examples/*/vl_* \
|
||||
examples/*/CMakeLists.txt \
|
||||
|
||||
DISTFILES2 = \
|
||||
test_*/.*ignore test_*/Makefile* test_*/*.cpp \
|
||||
test_*/*.pl test_*/*.v test_*/*.vc test_*/*.vh \
|
||||
test_regress/*.pl \
|
||||
test_regress/Makefile \
|
||||
test_regress/Makefile_obj \
|
||||
test_regress/input.vc \
|
||||
test_regress/CMakeLists.txt \
|
||||
test_regress/t/t*/*.sv* \
|
||||
test_regress/t/t*/*.v* \
|
||||
test_regress/t/t*/*/*.sv* \
|
||||
test_regress/t/t*/*/*.v* \
|
||||
test_regress/t/t*/*.cpp \
|
||||
test_regress/t/t*/CMakeLists.txt \
|
||||
test_regress/t/*.cpp \
|
||||
test_regress/t/*.h \
|
||||
test_regress/t/*.dat \
|
||||
test_regress/t/*.mem \
|
||||
test_regress/t/*.out \
|
||||
test_regress/t/*.pl \
|
||||
test_regress/t/*.pf \
|
||||
test_regress/t/*.v* \
|
||||
|
||||
INST_PROJ_FILES = \
|
||||
bin/verilator \
|
||||
bin/verilator_ccache_report \
|
||||
@@ -230,7 +155,7 @@ all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
|
||||
verilator_exe verilator_bin$(EXEEXT) verilator_bin_dbg$(EXEEXT) verilator_coverage_bin_dbg$(EXEEXT):
|
||||
@echo ------------------------------------------------------------
|
||||
@echo "making verilator in src"
|
||||
$(MAKE) -C src $(OBJCACHE_JOBS) $(CI_MAKE_SRC_TARGET)
|
||||
$(MAKE) -C src $(OBJCACHE_JOBS)
|
||||
|
||||
.PHONY:msg_test
|
||||
msg_test: all_nomsg
|
||||
@@ -386,44 +311,7 @@ uninstall:
|
||||
install: all_nomsg install-all
|
||||
install-all: installbin installman installdata install-msg
|
||||
|
||||
install-here: installman ftp
|
||||
|
||||
ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users
|
||||
DISTNAMEREV = $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q ${srcdir}/src/config_rev.h)
|
||||
|
||||
VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH)
|
||||
INST_PROJ_CVS = cp_if_cvs_diff
|
||||
|
||||
install-cadtools: dist
|
||||
@echo "Install-project to $(CAD_DIR)"
|
||||
strip bin/verilator_bin*
|
||||
strip bin/verilator_coverage_bin*
|
||||
$(MAKE) install-cadtools-quick
|
||||
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/man/man1
|
||||
for p in $(VL_INST_MAN_FILES) ; do \
|
||||
$(INSTALL_DATA) $$p $(VERILATOR_CAD_DIR)/man/man1/$$p; \
|
||||
done
|
||||
$(INST_PROJ_CVS) $(DISTNAME).tgz $(VERILATOR_CAD_DIR)/verilator.tgz
|
||||
rm $(DISTNAME).tgz
|
||||
|
||||
install-cadtools-quick:
|
||||
ifeq ($(CFG_WITH_DEFENV),yes)
|
||||
@echo "%Error: Reconfigure with './configure --disable-defenv' to avoid hardcoded paths."
|
||||
false
|
||||
endif
|
||||
@echo "Install-cadtools-quick (no strip) to $(VERILATOR_CAD_DIR)"
|
||||
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/gtkwave
|
||||
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/vltstd
|
||||
$(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/bin
|
||||
for p in $(INST_PROJ_FILES) ; do \
|
||||
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
|
||||
done
|
||||
for p in $(INST_PROJ_BIN_FILES) ; do \
|
||||
$(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \
|
||||
done
|
||||
|
||||
# VERILATOR_AUTHOR_SITE
|
||||
endif
|
||||
install-here: installman info
|
||||
|
||||
# Use --xml flag to see the cppcheck code to use for suppression
|
||||
CPPCHECK_CPP = $(wildcard \
|
||||
@@ -470,13 +358,13 @@ analyzer-include:
|
||||
|
||||
format: clang-format yapf format-pl-exec
|
||||
|
||||
CLANGFORMAT = clang-format
|
||||
CLANGFORMAT = clang-format-11
|
||||
CLANGFORMAT_FLAGS = -i
|
||||
CLANGFORMAT_FILES = $(CPPCHECK_CPP) $(CPPCHECK_H) $(CPPCHECK_YL) test_regress/t/*.c* test_regress/t/*.h
|
||||
|
||||
clang-format:
|
||||
@$(CLANGFORMAT) --version | egrep 10.0 > /dev/null \
|
||||
|| echo "*** You are not using clang-format 10.0, indents may differ from master's ***"
|
||||
@$(CLANGFORMAT) --version | egrep 11.0 > /dev/null \
|
||||
|| echo "*** You are not using clang-format 11.0, indents may differ from master's ***"
|
||||
$(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES)
|
||||
|
||||
PY_PROGRAMS = \
|
||||
@@ -492,6 +380,7 @@ PY_PROGRAMS = \
|
||||
src/cppcheck_filtered \
|
||||
src/flexfix \
|
||||
src/vlcovgen \
|
||||
test_regress/t/*.pf \
|
||||
nodist/code_coverage \
|
||||
nodist/dot_importer \
|
||||
nodist/fuzzer/actual_fail \
|
||||
@@ -523,9 +412,6 @@ lint-py:
|
||||
format-pl-exec:
|
||||
-chmod a+x test_regress/t/*.pl
|
||||
|
||||
|
||||
ftp: info
|
||||
|
||||
install-msg:
|
||||
@echo
|
||||
@echo "Installed binaries to $(DESTDIR)$(bindir)/verilator"
|
||||
@@ -596,15 +482,6 @@ TAGS: $(TAGFILES)
|
||||
doxygen:
|
||||
$(MAKE) -C docs doxygen
|
||||
|
||||
######################################################################
|
||||
# Test targets
|
||||
|
||||
dist-file-list:
|
||||
@echo "begin-dist-file-list:"; # Scripts look for this
|
||||
@echo $(wildcard $(DISTFILES1))
|
||||
@echo $(wildcard $(DISTFILES2))
|
||||
@echo "end-dist-file-list:"; # Scripts look for this
|
||||
|
||||
######################################################################
|
||||
# Distributions
|
||||
|
||||
@@ -612,36 +489,16 @@ DISTTITLE := Verilator $(word 1,$(PACKAGE_VERSION))
|
||||
DISTNAME := verilator-$(word 1,$(PACKAGE_VERSION))
|
||||
DISTDATEPRE := $(word 2,$(PACKAGE_VERSION))
|
||||
DISTDATE := $(subst /,-,$(DISTDATEPRE))
|
||||
|
||||
DISTTAGNAME := $(subst .,_,$(subst -,_,$(DISTNAME)))
|
||||
|
||||
tag:
|
||||
svnorcvs tag $(DISTTAGNAME)
|
||||
|
||||
# Don't depend on DISTFILES because there's no rule for "standards.info*".
|
||||
dist: $(DISTDEP) maintainer-copy
|
||||
-rm -fr $(DISTNAME)
|
||||
for file in $(DISTFILES1); do \
|
||||
mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \
|
||||
ln $$file $(DISTNAME)/$$file \
|
||||
|| { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \
|
||||
done; true;
|
||||
for file in $(DISTFILES2); do \
|
||||
mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \
|
||||
ln $$file $(DISTNAME)/$$file \
|
||||
|| { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \
|
||||
done; true;
|
||||
chmod -R a+r $(DISTNAME)
|
||||
tar chf $(DISTNAME).tar $(DISTNAME)
|
||||
gzip --force --best $(DISTNAME).tar
|
||||
mv $(DISTNAME).tar.gz $(DISTNAME).tgz
|
||||
rm -fr $(DISTNAME)
|
||||
|
||||
maintainer-diff:
|
||||
svnorcvs diff $(DISTTAGNAME)
|
||||
|
||||
preexist:
|
||||
svnorcvs nexists $(DISTTAGNAME)
|
||||
|
||||
maintainer-dist: preexist dist tag
|
||||
svnorcvs release $(DISTNAME).tgz
|
||||
maintainer-dist: preexist tag
|
||||
svnorcvs release $(DISTTAGNAME)
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0]
|
||||
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
||||
:target: https://opensource.org/licenses/Artistic-2.0
|
||||
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
||||
:target: https://repology.org/project/verilator/versions
|
||||
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
|
||||
:target: https://www.codacy.com/gh/verilator/verilator
|
||||
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
|
||||
@@ -12,6 +14,7 @@
|
||||
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
|
||||
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
|
||||
|
||||
|
||||
Welcome to Verilator
|
||||
====================
|
||||
|
||||
|
||||
+3
-2
@@ -332,8 +332,8 @@ detailed descriptions of these arguments.
|
||||
--gate-stmts <value> Tune gate optimizer depth
|
||||
--if-depth <value> Tune IFDEPTH warning
|
||||
+incdir+<dir> Directory to search for includes
|
||||
--inhibit-sim Create function to turn off sim
|
||||
--inline-mult <value> Tune module inlining
|
||||
--instr-count-dpi <value> Assumed dynamic instruction count of DPI imports
|
||||
-LDFLAGS <flags> Linker pre-object arguments for makefile
|
||||
--l2-name <value> Verilog scope name of the top module
|
||||
--language <lang> Default language standard to parse
|
||||
@@ -344,6 +344,7 @@ detailed descriptions of these arguments.
|
||||
--MMD Create .d dependency files
|
||||
--MP Create phony dependency targets
|
||||
--Mdir <directory> Name of output object directory
|
||||
--no-merge-const-pool Disable merging of different types in const pool
|
||||
--mod-prefix <topname> Name to prepend to lower classes
|
||||
--no-clk <signal-name> Prevent marking specified signal as clock
|
||||
--no-decoration Disable comments and symbol decorations
|
||||
@@ -367,6 +368,7 @@ detailed descriptions of these arguments.
|
||||
--pipe-filter <command> Filter all input through a script
|
||||
--pp-comments Show preprocessor comments with -E
|
||||
--prefix <topname> Name of top level class
|
||||
--prof-c Compile C++ code with profiling
|
||||
--prof-cfuncs Name functions for profiling
|
||||
--prof-threads Enable generating gantt chart data for threads
|
||||
--protect-key <key> Key for symbol protection
|
||||
@@ -378,7 +380,6 @@ detailed descriptions of these arguments.
|
||||
-pvalue+<name>=<value> Overwrite toplevel parameter
|
||||
--quiet-exit Don't print the command on failure
|
||||
--relative-includes Resolve includes relative to current file
|
||||
--no-relative-cfuncs Disallow 'this->' in generated functions
|
||||
--reloop-limit Minimum iterations for forming loops
|
||||
--report-unoptflat Extra diagnostics for UNOPTFLAT
|
||||
--rr Run Verilator and record with rr
|
||||
|
||||
@@ -88,6 +88,7 @@ else:
|
||||
args.o.write("{:{width}}| {}s\n".format(k,
|
||||
v.total_seconds(),
|
||||
width=wnames))
|
||||
if i > 4: break
|
||||
if i > 4:
|
||||
break
|
||||
|
||||
args.o.write("#" * 80 + "\n")
|
||||
|
||||
@@ -202,7 +202,7 @@ verilator_profcfunc - Read gprof report created with --prof-cfuncs
|
||||
=head1 SYNOPSIS
|
||||
|
||||
verilator --prof-cfuncs ....
|
||||
gcc --ggdb -pg ....
|
||||
gcc ....
|
||||
{run executable}
|
||||
gprof
|
||||
verilator_profcfuncs gprof.out
|
||||
|
||||
+9
-6
@@ -23,6 +23,12 @@ fatal() {
|
||||
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
|
||||
}
|
||||
|
||||
if [ "$CI_M32" = "0" ]; then
|
||||
unset CI_M32
|
||||
elif [ "$CI_M32" != "1" ]; then
|
||||
fatal "\$CI_M32 must be '0' or '1'";
|
||||
fi
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
MAKE=make
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
@@ -36,7 +42,7 @@ fi
|
||||
install-vcddiff() {
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
git clone https://github.com/veripool/vcddiff "$TMP_DIR"
|
||||
git -C "${TMP_DIR}" checkout 5112f88b7ba8818dce9dfb72619e64a1fc19542c
|
||||
git -C "${TMP_DIR}" checkout e5664be5fe39d353bf3fcb50aa05214ab7ed4ac4
|
||||
"$MAKE" -C "${TMP_DIR}"
|
||||
sudo cp "${TMP_DIR}/vcddiff" /usr/local/bin
|
||||
}
|
||||
@@ -55,7 +61,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
if [ "$COVERAGE" = 1 ]; then
|
||||
yes yes | sudo cpan -fi Parallel::Forker
|
||||
fi
|
||||
if [ "$M32" = 1 ]; then
|
||||
if [ "$CI_M32" = 1 ]; then
|
||||
sudo apt-get install gcc-multilib g++-multilib
|
||||
fi
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
@@ -82,7 +88,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
|
||||
sudo apt-get install libsystemc-dev
|
||||
fi
|
||||
if [ "$M32" = 1 ]; then
|
||||
if [ "$CI_M32" = 1 ]; then
|
||||
sudo apt-get install lib32z1-dev gcc-multilib g++-multilib
|
||||
fi
|
||||
elif [ "$CI_OS_NAME" = "osx" ]; then
|
||||
@@ -101,9 +107,6 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
fi
|
||||
yes yes | sudo cpan -M $CI_CPAN_REPO -fi Parallel::Forker
|
||||
install-vcddiff
|
||||
|
||||
autoconf
|
||||
./configure --enable-longtests --enable-ccwarn
|
||||
else
|
||||
##############################################################################
|
||||
# Unknown build stage
|
||||
|
||||
+15
-2
@@ -21,6 +21,12 @@ fatal() {
|
||||
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
|
||||
}
|
||||
|
||||
if [ "$CI_M32" = "0" ]; then
|
||||
unset CI_M32
|
||||
elif [ "$CI_M32" != "1" ]; then
|
||||
fatal "\$CI_M32 must be '0' or '1'";
|
||||
fi
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
export MAKE=make
|
||||
NPROC=$(nproc)
|
||||
@@ -40,8 +46,10 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
|
||||
if [ "$COVERAGE" != 1 ]; then
|
||||
autoconf
|
||||
./configure --enable-longtests --enable-ccwarn
|
||||
./configure --enable-longtests --enable-ccwarn ${CI_M32:+--enable-m32}
|
||||
ccache -z
|
||||
"$MAKE" -j "$NPROC" -k
|
||||
ccache -s
|
||||
if [ "$CI_OS_NAME" = "osx" ]; then
|
||||
file bin/verilator_bin
|
||||
file bin/verilator_bin_dbg
|
||||
@@ -61,6 +69,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
|
||||
if [ "$CI_OS_NAME" = "osx" ]; then
|
||||
export VERILATOR_TEST_NO_GDB=1 # Pain to get GDB to work on OS X
|
||||
# TODO below may no longer be requried as configure checks for -pg
|
||||
export VERILATOR_TEST_NO_GPROF=1 # Apple Clang has no -pg
|
||||
# export PATH="/Applications/gtkwave.app/Contents/Resources/bin:$PATH" # fst2vcd
|
||||
file bin/verilator_bin
|
||||
@@ -76,13 +85,15 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
"$MAKE" -j "$NPROC" -k
|
||||
elif [ "$CI_OS_NAME" = "freebsd" ]; then
|
||||
export VERILATOR_TEST_NO_GDB=1 # Disable for now, ideally should run
|
||||
# TODO below may no longer be requried as configure checks for -pg
|
||||
export VERILATOR_TEST_NO_GPROF=1 # gprof is a bit different on FreeBSD, disable
|
||||
fi
|
||||
|
||||
# Run sanitize on Ubuntu 20.04 only
|
||||
[ "$CI_RUNS_ON" = 'ubuntu-20.04' ] && sanitize='--sanitize' || sanitize=''
|
||||
[ "$CI_RUNS_ON" = 'ubuntu-20.04' ] && [ "$CI_M32" = "" ] && sanitize='--sanitize' || sanitize=''
|
||||
|
||||
# Run the specified test
|
||||
ccache -z
|
||||
case $TESTS in
|
||||
dist-vlt-0)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/3
|
||||
@@ -169,6 +180,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
fatal "Unknown test: $TESTS"
|
||||
;;
|
||||
esac
|
||||
ccache -s
|
||||
|
||||
# Upload coverage data
|
||||
if [[ $TESTS == coverage-* ]]; then
|
||||
bash <(cat ci/coverage-upload.sh) -f nodist/obj_dir/coverage/app_total.info
|
||||
|
||||
+104
-28
@@ -7,7 +7,7 @@
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.204 2021-06-12],
|
||||
AC_INIT([Verilator],[4.212 2021-09-01],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
# When releasing, also update header of Changes file
|
||||
@@ -35,7 +35,7 @@ AC_ARG_ENABLE([silent-rules],
|
||||
AC_MSG_CHECKING(whether to perform partial static linking of Verilator binary)
|
||||
AC_ARG_ENABLE([partial-static],
|
||||
[AS_HELP_STRING([--disable-partial-static],
|
||||
[By default, for Verilation peformance, Verilator
|
||||
[By default, for Verilation performance, Verilator
|
||||
is linked against some of its dependencies
|
||||
statically. Use this to link the Verilator binary
|
||||
fully dynamically.])],
|
||||
@@ -52,7 +52,7 @@ AC_MSG_CHECKING(whether to use tcmalloc)
|
||||
AC_ARG_ENABLE([tcmalloc],
|
||||
[AS_HELP_STRING([--enable-tcmalloc],
|
||||
[Use libtcmalloc_minimal for faster dynamic memory
|
||||
management in Verilator binary@<:@default=check@:>@])],
|
||||
management in Verilator binary @<:@default=check@:>@])],
|
||||
[case "${enableval}" in
|
||||
yes) CFG_WITH_TCMALLOC=yes ;;
|
||||
no) CFG_WITH_TCMALLOC=no ;;
|
||||
@@ -61,6 +61,34 @@ AC_ARG_ENABLE([tcmalloc],
|
||||
[CFG_WITH_TCMALLOC=check;])
|
||||
AC_MSG_RESULT($CFG_WITH_TCMALLOC)
|
||||
|
||||
# Flag to enable -m32 build
|
||||
AC_MSG_CHECKING(whether to use -m32)
|
||||
AC_ARG_ENABLE([m32],
|
||||
[AS_HELP_STRING([--enable-m32],
|
||||
[Use -m32 for all compilation and link,
|
||||
including Verialtor and generated models.])],
|
||||
[case "${enableval}" in
|
||||
yes) CFG_ENABLE_M32=yes ;;
|
||||
no) CFG_ENABLE_M32=no ;;
|
||||
*) AC_MSG_ERROR([bad value '${enableval}' for --enable-m32]) ;;
|
||||
esac],
|
||||
CFG_ENABLE_M32=no)
|
||||
AC_MSG_RESULT($CFG_ENABLE_M32)
|
||||
|
||||
# Flag to enable coverage build
|
||||
AC_MSG_CHECKING(whether to build for coverage collection)
|
||||
AC_ARG_ENABLE([coverage],
|
||||
[AS_HELP_STRING([--enable-coverage],
|
||||
[Build Verilator for code coverage collection.
|
||||
For developers only.])],
|
||||
[case "${enableval}" in
|
||||
yes) CFG_ENABLE_COVERAGE=yes ;;
|
||||
no) CFG_ENABLE_COVERAGE=no ;;
|
||||
*) AC_MSG_ERROR([bad value '${enableval}' for --enable-coverage]) ;;
|
||||
esac],
|
||||
CFG_ENABLE_COVERAGE=no)
|
||||
AC_MSG_RESULT($CFG_ENABLE_COVERAGE)
|
||||
|
||||
# Special Substitutions - CFG_WITH_DEFENV
|
||||
AC_MSG_CHECKING(whether to use hardcoded paths)
|
||||
AC_ARG_ENABLE([defenv],
|
||||
@@ -135,6 +163,11 @@ AC_RUN_IFELSE(
|
||||
AC_MSG_RESULT(no);AC_MSG_ERROR([a working C++ compiler is required]),
|
||||
AC_MSG_RESULT(yes))
|
||||
|
||||
AC_CHECK_PROG(AR,ar,ar)
|
||||
if test "x$AR" = "x" ; then
|
||||
AC_MSG_ERROR([Cannot find "ar" in your PATH, please install it])
|
||||
fi
|
||||
|
||||
AC_PATH_PROG(PERL,perl)
|
||||
if test "x$PERL" = "x" ; then
|
||||
AC_MSG_ERROR([Cannot find "perl" in your PATH, please install it])
|
||||
@@ -211,24 +244,34 @@ AC_DEFUN([_MY_CXX_CHECK_FLAG],
|
||||
CXXFLAGS="$ACO_SAVE_CXXFLAGS"
|
||||
])
|
||||
|
||||
AC_DEFUN([_MY_CXX_CHECK_IFELSE],
|
||||
[# _MY_CXX_CHECK_IFELSE(option,action-if-supported,action-if-not-supported)
|
||||
# Check if compiler supports specific option. If it does,
|
||||
# do action-if-supported, otherwise do action-if-not-supported
|
||||
_MY_CXX_CHECK_FLAG($1)
|
||||
if test "$_my_result" = "yes" ; then
|
||||
true
|
||||
$2
|
||||
else
|
||||
true
|
||||
$3
|
||||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN([_MY_CXX_CHECK_SET],
|
||||
[# _MY_CXX_CHECK_SET(variable,flag) -- Check if compiler supports specific options
|
||||
# If it does, set variable to flag, only if not previously set
|
||||
[# _MY_CXX_CHECK_SET(variable,option)
|
||||
# Check if compiler supports specific option. If it does,
|
||||
# set variable to option, only if not previously set.
|
||||
if test "$$1" = ""; then
|
||||
_MY_CXX_CHECK_FLAG($2)
|
||||
if test "$_my_result" = "yes" ; then
|
||||
$1="$2"
|
||||
fi
|
||||
_MY_CXX_CHECK_IFELSE($2, $1="$2")
|
||||
fi
|
||||
])
|
||||
|
||||
AC_DEFUN([_MY_CXX_CHECK_OPT],
|
||||
[# _MY_CXX_CHECK_OPT(flag) -- Check if compiler supports specific options
|
||||
# If it does, append flag to variable
|
||||
_MY_CXX_CHECK_FLAG($2)
|
||||
if test "$_my_result" = "yes" ; then
|
||||
$1="$$1 $2"
|
||||
fi
|
||||
[# _MY_CXX_CHECK_OPT(variable,option)
|
||||
# Check if compiler supports specific option. If it does,
|
||||
# append option to variable
|
||||
_MY_CXX_CHECK_IFELSE($2, $1="$$1 $2")
|
||||
])
|
||||
|
||||
AC_DEFUN([_MY_LDLIBS_CHECK_FLAG],
|
||||
@@ -270,16 +313,42 @@ AC_DEFUN([_MY_LDLIBS_CHECK_OPT],
|
||||
_MY_LDLIBS_CHECK_IFELSE($2, $1="$$1 $2")
|
||||
])
|
||||
|
||||
# When using -m32. Check this first as later checks may fail with the -m32 flag.
|
||||
if test "$CFG_ENABLE_M32" = "yes"; then
|
||||
_MY_CXX_CHECK_IFELSE(
|
||||
-m32,
|
||||
[CXX="$CXX -m32"],
|
||||
[AC_MSG_ERROR([--enable-m32 was given but compiler does not support -m32])])
|
||||
fi
|
||||
|
||||
# Similarly, add the coverage flags early as they influence later checks.
|
||||
if test "$CFG_ENABLE_COVERAGE" = "yes"; then
|
||||
_MY_CXX_CHECK_OPT(CXX,--coverage)
|
||||
# Otherwise inline may not show as uncovered
|
||||
# If we use this then e.g. verilated.h functions properly show up
|
||||
# if unused.
|
||||
# However, VerilatedSerialize::write then changes from covered
|
||||
# to uncovered (in G++ 9.3.0) even with all inlining turned off.
|
||||
# Having false negative coverage is more effort then missing negatives.
|
||||
# Also this seems to explode the runtime (since a lot more data).
|
||||
# _MY_CXX_CHECK_OPT(CXX,-fkeep-inline-functions)
|
||||
# Otherwise static may not show as uncovered
|
||||
_MY_CXX_CHECK_OPT(CXX,-fkeep-static-functions)
|
||||
# Exceptions can pollute the branch coverage data
|
||||
_MY_CXX_CHECK_OPT(CXX,-fno-exceptions)
|
||||
# Define-out some impossible stuff
|
||||
_MY_CXX_CHECK_OPT(CXX,-DVL_GCOV)
|
||||
fi
|
||||
|
||||
# Compiler flags to enable profiling
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PROFILE,-pg)
|
||||
AC_SUBST(CFG_CXXFLAGS_PROFILE)
|
||||
|
||||
# Flag to select newest language standard supported
|
||||
# Macros work such that first option that passes is the one we take
|
||||
# Currently enabled gnu++14/c++14 due to packaged SystemC dependency
|
||||
# gnu++17 is the newest that Verilator supports
|
||||
# std++03 is the oldest that Verilator supports
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++03)
|
||||
# Currently enabled c++14 due to packaged SystemC dependency
|
||||
# c++17 is the newest that Verilator supports
|
||||
# c++03 is the oldest that Verilator supports
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
|
||||
@@ -293,11 +362,6 @@ _MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++11)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++14)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++20)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++03)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++11)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++14)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++20)
|
||||
AC_SUBST(CFG_CXXFLAGS_STD_OLDEST)
|
||||
|
||||
# Flags for compiling Verilator internals including parser, and Verilated files
|
||||
@@ -322,6 +386,18 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PARSER,-Wno-parentheses-equality)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PARSER,-Wno-unused)
|
||||
AC_SUBST(CFG_CXXFLAGS_PARSER)
|
||||
|
||||
# Flags for compiling the debug version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
|
||||
if test "$CFG_ENABLE_COVERAGE" = "no"; then # Do not optimize for the coverage build
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DEBUG,-Og)
|
||||
fi
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DEBUG,-ggdb)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DEBUG,-gz)
|
||||
AC_SUBST(CFG_CXXFLAGS_DEBUG)
|
||||
|
||||
# Flags for linking the debug version of Verilator (in addition to above CFG_LDFLAGS_SRC)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_DEBUG,-gz)
|
||||
AC_SUBST(CFG_LDFLAGS_DEBUG)
|
||||
|
||||
# Flags for Verilated makefile
|
||||
# For example, -Wno-div-by-zero isn't in 4.1.2
|
||||
# Random code often does / 0. Unfortunately VL_DIV_I(0,0) will warn
|
||||
|
||||
@@ -20,6 +20,7 @@ Driss Hafdi
|
||||
Edgar E. Iglesias
|
||||
Eric Rippey
|
||||
Fan Shupei
|
||||
Felix Yan
|
||||
Garrett Smith
|
||||
Geza Lore
|
||||
Gianfranco Costamagna
|
||||
@@ -29,6 +30,7 @@ Harald Heckmann
|
||||
Howard Su
|
||||
Huang Rui
|
||||
HyungKi Jeong
|
||||
Ivan Vnučec
|
||||
Iztok Jeras
|
||||
James Hanlon
|
||||
James Hutchinson
|
||||
@@ -82,6 +84,7 @@ Sebastien Van Cauwenberghe
|
||||
Sergi Granell
|
||||
Stefan Wallentowitz
|
||||
Stephen Henry
|
||||
Steven Hugg
|
||||
Tim Snyder
|
||||
Tobias Rosenkranz
|
||||
Tobias Wölfel
|
||||
|
||||
Vendored
+19
-10
@@ -1,16 +1,25 @@
|
||||
/*Verilator blue #008fd7; set in html*/
|
||||
/* Verilator blue #008fd7; set in html */
|
||||
|
||||
.wy-side-nav-search > div.version {
|
||||
color: #111;
|
||||
color: #111;
|
||||
}
|
||||
.fa-github::before, .icon-github::before {
|
||||
content: "";
|
||||
|
||||
.fa-github::before,
|
||||
.icon-github::before {
|
||||
content: "";
|
||||
}
|
||||
.fa-home::before, .icon-home::before {
|
||||
content: "";
|
||||
|
||||
.fa-home::before,
|
||||
.icon-home::before {
|
||||
content: "";
|
||||
}
|
||||
.fa-arrow-circle-right::before, .icon-circle-arrow-right::before {
|
||||
content: "";
|
||||
|
||||
.fa-arrow-circle-right::before,
|
||||
.icon-circle-arrow-right::before {
|
||||
content: "";
|
||||
}
|
||||
.fa-arrow-circle-left::before, .icon-circle-arrow-left::before {
|
||||
content: "";
|
||||
|
||||
.fa-arrow-circle-left::before,
|
||||
.icon-circle-arrow-left::before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
+87
-22
@@ -7,22 +7,86 @@
|
||||
Connecting to Verilated Models
|
||||
******************************
|
||||
|
||||
Structure of the Verilated Model
|
||||
================================
|
||||
|
||||
Verilator outputs a :file:`{prefix}.h` header file which defines a class
|
||||
named :code:`{prefix}` which represents the generated model the user is
|
||||
supposed to instantiate. This model class defines the interface of the
|
||||
Verilated model.
|
||||
|
||||
Verilator will additionally create a :file:`{prefix}.cpp` file, together
|
||||
with additional .h and .cpp files for internals. See the :file:`examples`
|
||||
directory in the kit for examples. See :ref:`Files Read/Written` for
|
||||
information on all the files Verilator might output.
|
||||
|
||||
The output of Verilator will contain a :file:`{prefix}.mk` file that may be
|
||||
used with Make to build a :file:`{prefix}__ALL.a` library with all required
|
||||
objects in it.
|
||||
|
||||
The generated model class file manages all internal state required by the
|
||||
model, and exposes the following interface that allows interaction with the
|
||||
model:
|
||||
|
||||
* Top level IO ports are exposed as references to the appropriate internal
|
||||
equivalents.
|
||||
|
||||
* Public top level module instances are exposed as pointers to allow access
|
||||
to :code:`/* verilator public */` items.
|
||||
|
||||
* The root of the design hierarchy (as in SystemVerilog :code:`$root`) is
|
||||
exposed via the :code:`rootp` member pointer to allow access to model
|
||||
internals, including :code:`/* verilator public_flat */` items.
|
||||
|
||||
|
||||
.. _Porting from pre 4.210:
|
||||
|
||||
Model interface changes in version 4.210
|
||||
------------------------------------------
|
||||
|
||||
Starting from version 4.210, the model class is an interface object.
|
||||
|
||||
Up until Verilator version 4.204 inclusive, the generated model class was
|
||||
also the instance of the top level instance in the design hierarchy (what
|
||||
you would refer to with :code:`$root` in SystemVerilog). This meant that
|
||||
all internal variables that were implemented by Verilator in the root scope
|
||||
were accessible as members of the model class itself. Note there were often
|
||||
many such variable due to module inlining, including :code:`/* verilator
|
||||
public_flat */` items.
|
||||
|
||||
This means that user code that accesses internal signals in the model
|
||||
(likely including :code:`/* verilator public_flat */` signals, as they are
|
||||
often inlined into the root scope) will need to be updated as follows:
|
||||
|
||||
* No change required for accessing top level IO signals. These are directly
|
||||
accessible in the model class via references.
|
||||
|
||||
* No change required for accessing :code:`/* verilator public */` items.
|
||||
These are directly accessible via sub-module pointers in the model class.
|
||||
|
||||
* Accessing any other internal members, including
|
||||
:code:`/* verilator public_flat */` items requires the following changes:
|
||||
|
||||
* Additionally include :file:`{prefix}___024root.h`. This header defines
|
||||
type of the :code:`rootp` pointer within the model class. Note the
|
||||
:code:`__024` substring is the Verilator escape sequence for the
|
||||
:code:`$` character, i.e.: :code:`rootp` points to the Verilated
|
||||
SystemVerilog :code:`$root` scope.
|
||||
|
||||
* Replace :code:`modelp->internal->member->lookup` references with
|
||||
:code:`modelp->rootp->internal->member->lookup` references, which
|
||||
contain one additional indirection via the :code:`rootp` pointer.
|
||||
|
||||
|
||||
.. _Connecting to C++:
|
||||
|
||||
Connecting to C++
|
||||
=================
|
||||
|
||||
Verilator creates a :file:`{prefix}.h` and :file:`{prefix}.cpp` file for
|
||||
the top level module, together with additional .h and .cpp files for
|
||||
internals. See the :file:`examples` directory in the kit for examples. See
|
||||
:ref:`Files Read/Written` for information on all the files it writes.
|
||||
|
||||
After the model is created, there will be a :file:`{prefix}.mk` file that
|
||||
may be used with Make to produce a :file:`{prefix}__ALL.a` file with all
|
||||
required objects in it.
|
||||
|
||||
The user must write a C++ wrapper and main loop for the simulation, to link
|
||||
with the Verilated model. Here is a simple example:
|
||||
In C++ output mode (:vlopt:`--cc`), the Verilator generated model class is a
|
||||
simple C++ class. The user must write a C++ wrapper and main loop for the
|
||||
simulation, which instantiates the model class, and link with the Verilated
|
||||
model. Here is a simple example:
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
@@ -30,7 +94,7 @@ with the Verilated model. Here is a simple example:
|
||||
#include <iostream> // Need std::cout
|
||||
#include "Vtop.h" // From Verilating "top.v"
|
||||
|
||||
Vtop *top; // Instantiation of module
|
||||
Vtop *top; // Instantiation of model
|
||||
|
||||
vluint64_t main_time = 0; // Current simulation time
|
||||
// This is a 64-bit integer to reduce wrap over issues and
|
||||
@@ -45,7 +109,7 @@ with the Verilated model. Here is a simple example:
|
||||
int main(int argc, char** argv) {
|
||||
Verilated::commandArgs(argc, argv); // Remember args
|
||||
|
||||
top = new Vtop; // Create instance
|
||||
top = new Vtop; // Create model
|
||||
|
||||
top->reset_l = 0; // Set some inputs
|
||||
|
||||
@@ -70,17 +134,18 @@ with the Verilated model. Here is a simple example:
|
||||
}
|
||||
|
||||
|
||||
Note signals are read and written as member variables of the model. You
|
||||
call the :code:`eval()` method to evaluate the model. When the simulation
|
||||
is complete call the :code:`final()` method to execute any SystemVerilog
|
||||
final blocks, and complete any assertions. See :ref:`Evaluation Loop`.
|
||||
Note top level IO signals are read and written as members of the model. You
|
||||
call the :code:`eval()` method to evaluate the model. When the simulation is
|
||||
complete call the :code:`final()` method to execute any SystemVerilog final
|
||||
blocks, and complete any assertions. See :ref:`Evaluation Loop`.
|
||||
|
||||
|
||||
Connecting to SystemC
|
||||
=====================
|
||||
|
||||
Verilator will convert the top level module to a SC_MODULE. This module
|
||||
will attach directly into a SystemC netlist as an instantiation.
|
||||
In SystemC output mode (:vlopt:`--sc`), the Verilator generated model class
|
||||
is a SystemC SC_MODULE. This module will attach directly into a SystemC
|
||||
netlist as an instantiation.
|
||||
|
||||
The SC_MODULE gets the same pinout as the Verilog module, with the
|
||||
following type conversions: Pins of a single bit become bool. Pins 2-32
|
||||
@@ -88,9 +153,9 @@ bits wide become uint32_t's. Pins 33-64 bits wide become sc_bv's or
|
||||
vluint64_t's depending on the :vlopt:`--no-pins64` option. Wider pins
|
||||
become sc_bv's. (Uints simulate the fastest so are used where possible.)
|
||||
|
||||
Lower modules are not pure SystemC code. This is a feature, as using the
|
||||
SystemC pin interconnect scheme everywhere would reduce performance by an
|
||||
order of magnitude.
|
||||
Model internals, including lower level sub-modules are not pure SystemC
|
||||
code. This is a feature, as using the SystemC pin interconnect scheme
|
||||
everywhere would reduce performance by an order of magnitude.
|
||||
|
||||
|
||||
Direct Programming Interface (DPI)
|
||||
|
||||
@@ -13,7 +13,8 @@ When possible, please instead report bugs at `Verilator Issues
|
||||
|
||||
Primary author is Wilson Snyder <[email protected]>.
|
||||
|
||||
Major concepts by Paul Wasson, Duane Galbi, John Coiner and Jie Xu.
|
||||
Major concepts by Paul Wasson, Duane Galbi, John Coiner, Geza Lore, Yutetsu
|
||||
Takatsukasa, and Jie Xu.
|
||||
|
||||
|
||||
Contributors
|
||||
|
||||
@@ -11,13 +11,10 @@ C++11 compiler support
|
||||
require C++14 or newer compilers for both compiling Verilator and
|
||||
compiling Verilated models no sooner than January 2022.
|
||||
|
||||
No-Relative-Cfuncs Option
|
||||
The :vlopt:`--no-relative-cfuncs` option is not be required by any C++11
|
||||
compliant compiler and is planned for removal no sooner than July 2021.
|
||||
|
||||
Inhibit-Sim Option
|
||||
The :vlopt:`--inhibit-sim` option is planned for removal no sooner than
|
||||
July 2021.
|
||||
Verilated_heavy.h
|
||||
The legacy "verilated_heavy.h" include was replaced with just including
|
||||
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
|
||||
July 2022.
|
||||
|
||||
Configuration File -msg
|
||||
The :vlopt:`lint_off` "-msg" option has been replaced with the "-rule"
|
||||
|
||||
@@ -527,14 +527,6 @@ Summary:
|
||||
|
||||
See :vlopt:`-y`.
|
||||
|
||||
.. option:: --inhibit-sim
|
||||
|
||||
Rarely needed and deprecated. Create a :code:`inhibitSim(bool)`
|
||||
function to enable and disable evaluation. This allows an upper level
|
||||
testbench to disable modules that are not important in a given
|
||||
simulation, without needing to recompile or change the SystemC modules
|
||||
instantiated.
|
||||
|
||||
.. option:: --inline-mult <value>
|
||||
|
||||
Tune the inlining of modules. The default value of 2000 specifies that up
|
||||
@@ -544,6 +536,14 @@ Summary:
|
||||
times, but potentially faster simulation speed. This setting is ignored
|
||||
for very small modules; they will always be inlined, if allowed.
|
||||
|
||||
.. option:: --instr-count-dpi <value>
|
||||
|
||||
Assumed dynamic instruction count of the average DPI import. This is used
|
||||
by the partitioning algorithm when creating a multithread model. The
|
||||
default value is 200. Adjusting this to an appropriate value can yield
|
||||
performance improvements in multithreaded models. Ignored when creating a
|
||||
single threaded model.
|
||||
|
||||
.. option:: -j [<value>]
|
||||
|
||||
Specify the level of parallelism for :vlopt:`--build`. The <value> must
|
||||
@@ -633,6 +633,13 @@ Summary:
|
||||
The directory is created if it does not exist and the parent directories
|
||||
exist; otherwise manually create the Mdir before calling Verilator.
|
||||
|
||||
.. option:: --no-merge-const-pool
|
||||
|
||||
Rarely needed. In order to minimize cache footprint, values of different
|
||||
data type, that are yet emitted identically in C++ are merged in the
|
||||
constant pool. This option disables this and causes every constant pool
|
||||
entry with a distinct data type to be emitted separately.
|
||||
|
||||
.. option:: --mod-prefix <topname>
|
||||
|
||||
Specifies the name to prepend to all lower level classes. Defaults to
|
||||
@@ -654,10 +661,6 @@ Summary:
|
||||
|
||||
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
|
||||
|
||||
.. option:: --no-relative-cfuncs
|
||||
|
||||
Deprecated.
|
||||
|
||||
.. option:: --no-skip-identical =item --skip-identical
|
||||
|
||||
Rarely needed. Disables or enables skipping execution of Verilator if
|
||||
@@ -808,6 +811,13 @@ Summary:
|
||||
prepended to the name of the :vlopt:`--top` option, or V prepended to
|
||||
the first Verilog filename passed on the command line.
|
||||
|
||||
.. option:: --prof-c
|
||||
|
||||
When compiling the C++ code, enable the compiler's profiling flag
|
||||
(e.g. :code:`g++ -pg`). See :ref:`Profiling`.
|
||||
|
||||
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`prof-c`.
|
||||
|
||||
.. option:: --prof-cfuncs
|
||||
|
||||
Modify the created C++ functions to support profiling. The functions
|
||||
@@ -818,6 +828,8 @@ Summary:
|
||||
came from. This allows gprof or oprofile reports to be correlated with
|
||||
the original Verilog source statements. See :ref:`Profiling`.
|
||||
|
||||
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`prof-c`.
|
||||
|
||||
.. option:: --prof-threads
|
||||
|
||||
Enable gantt chart data collection for threaded builds. See :ref:`Thread
|
||||
@@ -1040,6 +1052,8 @@ Summary:
|
||||
Verilator assumes DPI pure imports are threadsafe, but non-pure DPI
|
||||
imports are not.
|
||||
|
||||
See also :vlopt:`--instr-count-dpi` option.
|
||||
|
||||
.. option:: --threads-max-mtasks <value>
|
||||
|
||||
Rarely needed. When using :vlopt:`--threads`, specify the number of
|
||||
|
||||
@@ -33,7 +33,12 @@ or "`ifdef`"'s may break other tools.
|
||||
that returns up to a 32-bit number (without a trailing ;). This can be
|
||||
used to call C++ functions from your Verilog code.
|
||||
|
||||
String arguments will be put directly into the output C++ code.
|
||||
String arguments will be put directly into the output C++ code, except
|
||||
the word 'this' (i.e.: the object pointer) might be replaced with a
|
||||
different pointer as Verilator might implement logic with non-member
|
||||
functions. For this reason, any references to class members must be made
|
||||
via an explicit 'this->' pointer dereference.
|
||||
|
||||
Expression arguments will have the code to evaluate the expression
|
||||
inserted. Thus to call a C++ function, :code:`$c("func(",a,")")` will
|
||||
result in :code:`func(a)` in the output C++ code. For input arguments,
|
||||
|
||||
+22
-16
@@ -54,32 +54,38 @@ For --cc/--sc, it creates:
|
||||
- Arguments for hierarchical Verilation (from --make gmake)
|
||||
* - *{prefix}*\ _hierCMakeArgs.f
|
||||
- Arguments for hierarchical Verilation (from --make cmake)
|
||||
* - *{prefix}*\ .cpp
|
||||
- Top level C++ file
|
||||
* - *{prefix}{__n}*\ .cpp
|
||||
- Additional top C++ files (from --output-split)
|
||||
* - *{prefix}*\ .h
|
||||
- Top level header
|
||||
* - *{prefix}*\ __Slow\ *{__n}*\ .cpp
|
||||
- Model header
|
||||
* - *{prefix}*\ .cpp
|
||||
- Model C++ file
|
||||
* - *{prefix}*\ ___024root.h
|
||||
- Top level (SystemVerilog $root) internal header file
|
||||
* - *{prefix}*\ ___024root.cpp
|
||||
- Top level (SystemVerilog $root) internal C++ file
|
||||
* - *{prefix}*___024root*{__n}*\ .cpp
|
||||
- Additional top level internal C++ files (from --output-split)
|
||||
* - *{prefix}*\ ___024root__Slow\ *{__n}*\ .cpp
|
||||
- Infrequent cold routines
|
||||
* - *{prefix}*\ __Dpi.cpp
|
||||
- DPI import and export wrappers (from --dpi)
|
||||
* - *{prefix}*\ ___024root__Trace{__n}*\ .cpp
|
||||
- Wave file generation code (from --trace)
|
||||
* - *{prefix}*\ ___024root__Trace__Slow{__n}*\ .cpp
|
||||
- Wave file generation code (from --trace)
|
||||
* - *{prefix}*\ __Dpi.h
|
||||
- DPI import and export declarations (from --dpi)
|
||||
* - *{prefix}*\ __Dpi.cpp
|
||||
- Global DPI export wrappers (from --dpi)
|
||||
* - *{prefix}*\ __Dpi_Export\ *{__n}\ .cpp
|
||||
- DPI export wrappers scoped to this particular model (from --dpi)
|
||||
* - *{prefix}*\ __Inlines.h
|
||||
- Inline support functions
|
||||
* - *{prefix}*\ __Syms.cpp
|
||||
- Global symbol table C++
|
||||
* - *{prefix}*\ __Syms.h
|
||||
- Global symbol table header
|
||||
* - *{prefix}*\ __Trace{__n}*\ .cpp
|
||||
- Wave file generation code (from --trace)
|
||||
* - *{prefix}*\ __Trace__Slow{__n}*\ .cpp
|
||||
- Wave file generation code (from --trace)
|
||||
* - *{prefix}{each_verilog_module}*\ .cpp
|
||||
- Lower level internal C++ files
|
||||
* - *{prefix}*\ __Syms.cpp
|
||||
- Global symbol table C++
|
||||
* - *{prefix}{each_verilog_module}*\ .h
|
||||
- Lower level internal header files
|
||||
* - *{prefix}{each_verilog_module}*\ .cpp
|
||||
- Lower level internal C++ files
|
||||
* - *{prefix}{each_verilog_module}{__n}*\ .cpp
|
||||
- Additional lower C++ files (from --output-split)
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ package:
|
||||
|
||||
apt-get install verilator # On Ubuntu
|
||||
|
||||
For other distributions refer to `Repology Verilator Distro Packages
|
||||
<https://repology.org/project/verilator>`__.
|
||||
|
||||
.. _Git Install:
|
||||
|
||||
@@ -44,7 +46,6 @@ In brief, to install from git:
|
||||
#sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
|
||||
|
||||
git clone https://github.com/verilator/verilator # Only first time
|
||||
## Note the URL above is not a page you can see with a browser, it's for git only
|
||||
|
||||
# Every time you need to build:
|
||||
unsetenv VERILATOR_ROOT # For csh; ignore error if on bash
|
||||
@@ -116,7 +117,7 @@ Those developing Verilator itself may also want these (see internals.rst):
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install gdb graphviz cmake clang clang-format gprof lcov
|
||||
sudo apt-get install gdb graphviz cmake clang clang-format-11 gprof lcov
|
||||
sudo pip3 install sphinx sphinx_rtd_theme breathe
|
||||
cpan install Pod::Perldoc
|
||||
cpan install Parallel::Forker
|
||||
|
||||
@@ -272,8 +272,6 @@ profiled C++ code functions.
|
||||
To use profiling:
|
||||
|
||||
#. Use Verilator's :vlopt:`--prof-cfuncs`.
|
||||
#. Use Verilator's :vlopt:`-CFLAGS "-g -pg" <-CFLAGS>` to pass the
|
||||
profiling flags through to GCC/Clang.
|
||||
#. Build and run the simulation model.
|
||||
#. The model will create gmon.out.
|
||||
#. Run :command:`gprof` to see where in the C++ code the time is spent.
|
||||
|
||||
@@ -216,6 +216,12 @@ be done by a "main thread". In most cases the eval thread and main thread
|
||||
are the same thread (i.e. the user's top C++ testbench runs on a single
|
||||
thread), but this is not required.
|
||||
|
||||
When making frequent use of DPI imported functions in a multi-threaded
|
||||
model, it may be beneficial to performance to adjust the
|
||||
:vlopt:`--instr-count-dpi` option based on some experimentation. This
|
||||
influences the partitioning of the model by adjusting the assumed execution
|
||||
time of DPI imports.
|
||||
|
||||
The :vlopt:`--trace-threads` options can be used to produce trace dumps
|
||||
using multiple threads. If :vlopt:`--trace-threads` is set without
|
||||
:vlopt:`--threads`, then :vlopt:`--trace-threads` will imply
|
||||
|
||||
+3
-3
@@ -502,7 +502,7 @@ Some of the code implementing passes is extremely repetitive, and must be
|
||||
implemented for each sub-class of ``AstNode``. However, while repetitive,
|
||||
there is more variability than can be handled in C++ macros.
|
||||
|
||||
In Verilator this is implemented by using a Perl script, ``astgen`` to
|
||||
In Verilator this is implemented by using a script, ``astgen`` to
|
||||
pre-process the C++ code. For example in ``V3Const.cpp`` this is used to
|
||||
implement the ``visit()`` functions for each binary operation using the
|
||||
``TREEOP`` macro.
|
||||
@@ -750,8 +750,8 @@ test. For run-time tests, this is followed by a call to the ``execute``
|
||||
subroutine. Both of these functions can optionally be provided with a hash
|
||||
table as argument specifying additional options.
|
||||
|
||||
The test driver assumes by default that the source Verilog file name
|
||||
matches the PERL driver name. So a test whose driver is
|
||||
The driver.pl script assumes by default that the source Verilog file name
|
||||
matches the test script name. So a test whose driver is
|
||||
``t/t_mytest.pl`` will expect a Verilog source file ``t/t_mytest.v``.
|
||||
This can be changed using the ``top_filename`` subroutine, for example
|
||||
|
||||
|
||||
@@ -4989,9 +4989,7 @@ unsigned int secnum = 0;
|
||||
int blocks_skipped = 0;
|
||||
fst_off_t blkpos = 0;
|
||||
uint64_t seclen, beg_tim;
|
||||
#ifdef FST_DEBUG
|
||||
uint64_t end_tim;
|
||||
#endif
|
||||
uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle;
|
||||
fst_off_t vc_start;
|
||||
fst_off_t indx_pntr, indx_pos;
|
||||
@@ -5058,14 +5056,11 @@ for(;;)
|
||||
if(!seclen) break;
|
||||
|
||||
beg_tim = fstReaderUint64(xc->f);
|
||||
#ifdef FST_DEBUG
|
||||
end_tim =
|
||||
#endif
|
||||
fstReaderUint64(xc->f);
|
||||
end_tim = fstReaderUint64(xc->f);
|
||||
|
||||
if(xc->limit_range_valid)
|
||||
{
|
||||
if(beg_tim < xc->limit_range_start)
|
||||
if(end_tim < xc->limit_range_start)
|
||||
{
|
||||
blocks_skipped++;
|
||||
blkpos += seclen;
|
||||
|
||||
+230
-225
File diff suppressed because it is too large
Load Diff
+13
-2126
File diff suppressed because it is too large
Load Diff
+13
-4
@@ -9,17 +9,18 @@
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
######################################################################
|
||||
|
||||
PERL = @PERL@
|
||||
PYTHON3 = @PYTHON3@
|
||||
AR = @AR@
|
||||
CXX = @CXX@
|
||||
LINK = @CXX@
|
||||
AR = ar
|
||||
RANLIB = ranlib
|
||||
OBJCACHE ?= @OBJCACHE@
|
||||
PERL = @PERL@
|
||||
PYTHON3 = @PYTHON3@
|
||||
|
||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
|
||||
|
||||
# Compiler flags to enable profiling
|
||||
CFG_CXXFLAGS_PROFILE = @CFG_CXXFLAGS_PROFILE@
|
||||
# Select newest language
|
||||
CFG_CXXFLAGS_STD_NEWEST = @CFG_CXXFLAGS_STD_NEWEST@
|
||||
# Select oldest language (for Verilator internal testing only)
|
||||
@@ -104,6 +105,14 @@ OPT_FAST = -Os
|
||||
# to change this as the library is small, but can have significant speed impact.
|
||||
OPT_GLOBAL = -Os
|
||||
|
||||
#######################################################################
|
||||
##### Profile builds
|
||||
|
||||
ifeq ($(VM_PROFC),1)
|
||||
CPPFLAGS += $(CFG_CXXFLAGS_PROFILE)
|
||||
LDFLAGS += $(CFG_CXXFLAGS_PROFILE)
|
||||
endif
|
||||
|
||||
#######################################################################
|
||||
##### SystemC builds
|
||||
|
||||
|
||||
+35
-32
@@ -132,7 +132,7 @@ private:
|
||||
int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) {
|
||||
const auto iter = m_valueIndexes.find(value);
|
||||
if (iter != m_valueIndexes.end()) return iter->second;
|
||||
m_nextIndex++;
|
||||
++m_nextIndex;
|
||||
assert(m_nextIndex > 0); // Didn't rollover
|
||||
m_valueIndexes.emplace(value, m_nextIndex);
|
||||
m_indexValues.emplace(m_nextIndex, value);
|
||||
@@ -143,8 +143,9 @@ private:
|
||||
std::string rtn;
|
||||
for (const char* pos = text.c_str(); *pos; ++pos) {
|
||||
if (!std::isprint(*pos) || *pos == '%' || *pos == '"') {
|
||||
char hex[10];
|
||||
VL_SNPRINTF(hex, 10, "%%%02X", pos[0]);
|
||||
constexpr size_t LEN_MAX_HEX = 20;
|
||||
char hex[LEN_MAX_HEX];
|
||||
VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]);
|
||||
rtn += hex;
|
||||
} else {
|
||||
rtn += *pos;
|
||||
@@ -165,11 +166,11 @@ private:
|
||||
const std::string& value) VL_PURE {
|
||||
std::string name;
|
||||
if (key.length() == 1 && std::isalpha(key[0])) {
|
||||
name += std::string("\001") + key;
|
||||
name += std::string{"\001"} + key;
|
||||
} else {
|
||||
name += std::string("\001") + dequote(key);
|
||||
name += std::string{"\001"} + dequote(key);
|
||||
}
|
||||
name += std::string("\002") + dequote(value);
|
||||
name += std::string{"\002"} + dequote(value);
|
||||
return name;
|
||||
}
|
||||
static std::string combineHier(const std::string& old, const std::string& add) VL_PURE {
|
||||
@@ -180,31 +181,32 @@ private:
|
||||
if (old.empty()) return add;
|
||||
if (add.empty()) return old;
|
||||
|
||||
const char* a = old.c_str();
|
||||
const char* b = add.c_str();
|
||||
const char* const a = old.c_str();
|
||||
const char* const b = add.c_str();
|
||||
|
||||
// Scan forward to first mismatch
|
||||
const char* apre = a;
|
||||
const char* bpre = b;
|
||||
while (*apre == *bpre) {
|
||||
apre++;
|
||||
bpre++;
|
||||
++apre;
|
||||
++bpre;
|
||||
}
|
||||
|
||||
// We used to backup and split on only .'s but it seems better to be verbose
|
||||
// and not assume . is the separator
|
||||
std::string prefix = std::string(a, apre - a);
|
||||
const size_t prefix_len = apre - a;
|
||||
const std::string prefix = std::string{a, prefix_len};
|
||||
|
||||
// Scan backward to last mismatch
|
||||
const char* apost = a + std::strlen(a) - 1;
|
||||
const char* bpost = b + std::strlen(b) - 1;
|
||||
while (*apost == *bpost && apost > apre && bpost > bpre) {
|
||||
apost--;
|
||||
bpost--;
|
||||
--apost;
|
||||
--bpost;
|
||||
}
|
||||
|
||||
// Forward to . so we have a whole word
|
||||
std::string suffix = *bpost ? std::string(bpost + 1) : "";
|
||||
std::string suffix = *bpost ? std::string{bpost + 1} : "";
|
||||
|
||||
std::string out = prefix + "*" + suffix;
|
||||
|
||||
@@ -251,19 +253,19 @@ private:
|
||||
|
||||
public:
|
||||
// PUBLIC METHODS
|
||||
void forcePerInstance(bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_forcePerInstance = flag;
|
||||
}
|
||||
void clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
clearGuts();
|
||||
}
|
||||
void clearNonMatch(const char* matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (matchp && matchp[0]) {
|
||||
ItemList newlist;
|
||||
for (const auto& itemp : m_items) {
|
||||
@@ -278,24 +280,24 @@ public:
|
||||
}
|
||||
void zero() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
for (const auto& itemp : m_items) itemp->zero();
|
||||
}
|
||||
|
||||
// We assume there's always call to i/f/p in that order
|
||||
void inserti(VerilatedCovImpItem* itemp) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
assert(!m_insertp);
|
||||
m_insertp = itemp;
|
||||
}
|
||||
void insertf(const char* filenamep, int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
void insertf(const char* const filenamep, const int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_insertFilenamep = filenamep;
|
||||
m_insertLineno = lineno;
|
||||
}
|
||||
void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS],
|
||||
const char* valps[VerilatedCovConst::MAX_KEYS]) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
assert(m_insertp);
|
||||
// First two key/vals are filename
|
||||
ckeyps[0] = "filename";
|
||||
@@ -308,7 +310,8 @@ public:
|
||||
while (const char* foundp = std::strchr(fnstartp, '/')) fnstartp = foundp + 1;
|
||||
const char* fnendp = fnstartp;
|
||||
for (; *fnendp && *fnendp != '.'; fnendp++) {}
|
||||
std::string page_default = "sp_user/" + std::string(fnstartp, fnendp - fnstartp);
|
||||
const size_t page_len = fnendp - fnstartp;
|
||||
const std::string page_default = "sp_user/" + std::string{fnstartp, page_len};
|
||||
ckeyps[2] = "page";
|
||||
valps[2] = page_default.c_str();
|
||||
|
||||
@@ -337,7 +340,7 @@ public:
|
||||
// cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
|
||||
m_insertp->m_keys[addKeynum] = valueIndex(key);
|
||||
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
||||
addKeynum++;
|
||||
++addKeynum;
|
||||
if (VL_UNCOVERABLE(!legalKey(key))) {
|
||||
std::string msg
|
||||
= ("%Error: Coverage keys of one character, or letter+digit are illegal: "
|
||||
@@ -353,15 +356,15 @@ public:
|
||||
|
||||
void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
#ifndef VM_COVERAGE
|
||||
VL_FATAL_MT("", 0, "", "%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n");
|
||||
#endif
|
||||
selftest();
|
||||
|
||||
std::ofstream os(filename);
|
||||
std::ofstream os{filename};
|
||||
if (os.fail()) {
|
||||
std::string msg = std::string("%Error: Can't write '") + filename + "'";
|
||||
std::string msg = std::string{"%Error: Can't write '"} + filename + "'";
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
return;
|
||||
}
|
||||
@@ -435,10 +438,10 @@ void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
|
||||
void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
|
||||
void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); }
|
||||
void VerilatedCovContext::_inserti(vluint32_t* itemp) VL_MT_SAFE {
|
||||
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>(itemp));
|
||||
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>{itemp});
|
||||
}
|
||||
void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE {
|
||||
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
|
||||
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>{itemp});
|
||||
}
|
||||
void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
|
||||
impp()->insertf(filename, lineno);
|
||||
@@ -511,7 +514,7 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
|
||||
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
|
||||
static VerilatedMutex s_mutex;
|
||||
if (VL_UNLIKELY(!m_coveragep)) {
|
||||
const VerilatedLockGuard lock(s_mutex);
|
||||
const VerilatedLockGuard lock{s_mutex};
|
||||
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
|
||||
m_coveragep.reset(new VerilatedCovImp);
|
||||
}
|
||||
|
||||
+86
-86
@@ -79,19 +79,19 @@ void svPutBitselLogic(svLogicVecVal* dp, int bit, svLogic s) {
|
||||
|
||||
void svGetPartselBit(svBitVecVal* dp, const svBitVecVal* sp, int lsb, int width) {
|
||||
// See also VL_SEL_WWI
|
||||
int msb = lsb + width - 1;
|
||||
int word_shift = VL_BITWORD_I(lsb);
|
||||
const int msb = lsb + width - 1;
|
||||
const int word_shift = VL_BITWORD_I(lsb);
|
||||
if (VL_BITBIT_I(lsb) == 0) {
|
||||
// Just a word extract
|
||||
for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift];
|
||||
} else {
|
||||
int loffset = lsb & VL_SIZEBITS_I;
|
||||
int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
|
||||
const int loffset = lsb & VL_SIZEBITS_I;
|
||||
const int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
|
||||
// Middle words
|
||||
int words = VL_WORDS_I(msb - lsb + 1);
|
||||
const int words = VL_WORDS_I(msb - lsb + 1);
|
||||
for (int i = 0; i < words; ++i) {
|
||||
dp[i] = sp[i + word_shift] >> loffset;
|
||||
int upperword = i + word_shift + 1;
|
||||
const int upperword = i + word_shift + 1;
|
||||
if (upperword <= static_cast<int>(VL_BITWORD_I(msb))) {
|
||||
dp[i] |= sp[upperword] << nbitsfromlow;
|
||||
}
|
||||
@@ -101,20 +101,20 @@ void svGetPartselBit(svBitVecVal* dp, const svBitVecVal* sp, int lsb, int width)
|
||||
dp[VL_WORDS_I(width) - 1] &= VL_MASK_I(width);
|
||||
}
|
||||
void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int width) {
|
||||
int msb = lsb + width - 1;
|
||||
int word_shift = VL_BITWORD_I(lsb);
|
||||
const int msb = lsb + width - 1;
|
||||
const int word_shift = VL_BITWORD_I(lsb);
|
||||
if (VL_BITBIT_I(lsb) == 0) {
|
||||
// Just a word extract
|
||||
for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift];
|
||||
} else {
|
||||
int loffset = lsb & VL_SIZEBITS_I;
|
||||
int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
|
||||
const int loffset = lsb & VL_SIZEBITS_I;
|
||||
const int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
|
||||
// Middle words
|
||||
int words = VL_WORDS_I(msb - lsb + 1);
|
||||
const int words = VL_WORDS_I(msb - lsb + 1);
|
||||
for (int i = 0; i < words; ++i) {
|
||||
dp[i].aval = sp[i + word_shift].aval >> loffset;
|
||||
dp[i].bval = sp[i + word_shift].bval >> loffset;
|
||||
int upperword = i + word_shift + 1;
|
||||
const int upperword = i + word_shift + 1;
|
||||
if (upperword <= static_cast<int>(VL_BITWORD_I(msb))) {
|
||||
dp[i].aval |= sp[upperword].aval << nbitsfromlow;
|
||||
dp[i].bval |= sp[upperword].bval << nbitsfromlow;
|
||||
@@ -127,22 +127,22 @@ void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int
|
||||
}
|
||||
void svPutPartselBit(svBitVecVal* dp, const svBitVecVal s, int lbit, int width) {
|
||||
// See also _vl_insert_WI
|
||||
int hbit = lbit + width - 1;
|
||||
int hoffset = VL_BITBIT_I(hbit);
|
||||
int loffset = VL_BITBIT_I(lbit);
|
||||
const int hbit = lbit + width - 1;
|
||||
const int hoffset = VL_BITBIT_I(hbit);
|
||||
const int loffset = VL_BITBIT_I(lbit);
|
||||
if (hoffset == VL_SIZEBITS_I && loffset == 0) {
|
||||
// Fast and common case, word based insertion
|
||||
dp[VL_BITWORD_I(lbit)] = s;
|
||||
} else {
|
||||
int hword = VL_BITWORD_I(hbit);
|
||||
int lword = VL_BITWORD_I(lbit);
|
||||
const int hword = VL_BITWORD_I(hbit);
|
||||
const int lword = VL_BITWORD_I(lbit);
|
||||
if (hword == lword) { // know < 32 bits because above checks it
|
||||
IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
|
||||
const IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
|
||||
dp[lword] = (dp[lword] & ~insmask) | ((s << loffset) & insmask);
|
||||
} else {
|
||||
IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
|
||||
IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
|
||||
int nbitsonright = 32 - loffset; // bits that end up in lword
|
||||
const IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
|
||||
const IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
|
||||
const int nbitsonright = 32 - loffset; // bits that end up in lword
|
||||
dp[lword] = (dp[lword] & ~linsmask) | ((s << loffset) & linsmask);
|
||||
dp[hword] = (dp[hword] & ~hinsmask) | ((s >> nbitsonright) & hinsmask);
|
||||
}
|
||||
@@ -150,24 +150,24 @@ void svPutPartselBit(svBitVecVal* dp, const svBitVecVal s, int lbit, int width)
|
||||
}
|
||||
// cppcheck-suppress passedByValue
|
||||
void svPutPartselLogic(svLogicVecVal* dp, const svLogicVecVal s, int lbit, int width) {
|
||||
int hbit = lbit + width - 1;
|
||||
int hoffset = VL_BITBIT_I(hbit);
|
||||
int loffset = VL_BITBIT_I(lbit);
|
||||
const int hbit = lbit + width - 1;
|
||||
const int hoffset = VL_BITBIT_I(hbit);
|
||||
const int loffset = VL_BITBIT_I(lbit);
|
||||
if (hoffset == VL_SIZEBITS_I && loffset == 0) {
|
||||
// Fast and common case, word based insertion
|
||||
dp[VL_BITWORD_I(lbit)].aval = s.aval;
|
||||
dp[VL_BITWORD_I(lbit)].bval = s.bval;
|
||||
} else {
|
||||
int hword = VL_BITWORD_I(hbit);
|
||||
int lword = VL_BITWORD_I(lbit);
|
||||
const int hword = VL_BITWORD_I(hbit);
|
||||
const int lword = VL_BITWORD_I(lbit);
|
||||
if (hword == lword) { // know < 32 bits because above checks it
|
||||
IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
|
||||
const IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
|
||||
dp[lword].aval = (dp[lword].aval & ~insmask) | ((s.aval << loffset) & insmask);
|
||||
dp[lword].bval = (dp[lword].bval & ~insmask) | ((s.bval << loffset) & insmask);
|
||||
} else {
|
||||
IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
|
||||
IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
|
||||
int nbitsonright = 32 - loffset; // bits that end up in lword
|
||||
const IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
|
||||
const IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
|
||||
const int nbitsonright = 32 - loffset; // bits that end up in lword
|
||||
dp[lword].aval = (dp[lword].aval & ~linsmask) | ((s.aval << loffset) & linsmask);
|
||||
dp[lword].bval = (dp[lword].bval & ~linsmask) | ((s.bval << loffset) & linsmask);
|
||||
dp[hword].aval = (dp[hword].aval & ~hinsmask) | ((s.aval >> nbitsonright) & hinsmask);
|
||||
@@ -184,7 +184,7 @@ static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHa
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"%%Error: DPI svOpenArrayHandle function called with nullptr handle");
|
||||
}
|
||||
const VerilatedDpiOpenVar* varp = reinterpret_cast<const VerilatedDpiOpenVar*>(h);
|
||||
const VerilatedDpiOpenVar* const varp = reinterpret_cast<const VerilatedDpiOpenVar*>(h);
|
||||
if (VL_UNLIKELY(!varp->magicOk())) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"%%Error: DPI svOpenArrayHandle function called with non-Verilator handle");
|
||||
@@ -205,13 +205,13 @@ int svDimensions(const svOpenArrayHandle h) { return _vl_openhandle_varp(h)->udi
|
||||
|
||||
// Return pointer to open array data, or nullptr if not in IEEE standard C layout
|
||||
void* svGetArrayPtr(const svOpenArrayHandle h) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
|
||||
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
|
||||
return varp->datap();
|
||||
}
|
||||
// Return size of open array, or 0 if not in IEEE standard C layout
|
||||
int svSizeOfArray(const svOpenArrayHandle h) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
|
||||
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return 0;
|
||||
// Truncate 64 bits to int; DPI is limited to 4GB
|
||||
return static_cast<int>(varp->totalSize());
|
||||
@@ -264,22 +264,22 @@ static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h, int nargs, int indx1
|
||||
int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
|
||||
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
return datap;
|
||||
}
|
||||
|
||||
// Copy to user bit array from simulator open array
|
||||
static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int nargs,
|
||||
int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8: d[0] = *(reinterpret_cast<CData*>(datap)); return;
|
||||
case VLVT_UINT16: d[0] = *(reinterpret_cast<SData*>(datap)); return;
|
||||
case VLVT_UINT32: d[0] = *(reinterpret_cast<IData*>(datap)); return;
|
||||
case VLVT_UINT64: {
|
||||
WData lwp[2];
|
||||
VlWide<2> lwp;
|
||||
VL_SET_WQ(lwp, *(reinterpret_cast<QData*>(datap)));
|
||||
d[0] = lwp[0];
|
||||
d[1] = lwp[1];
|
||||
@@ -299,8 +299,8 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
|
||||
// Copy to user logic array from simulator open array
|
||||
static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int nargs,
|
||||
int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8:
|
||||
@@ -316,7 +316,7 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
|
||||
d[0].bval = 0;
|
||||
return;
|
||||
case VLVT_UINT64: {
|
||||
WData lwp[2];
|
||||
VlWide<2> lwp;
|
||||
VL_SET_WQ(lwp, *(reinterpret_cast<QData*>(datap)));
|
||||
d[0].aval = lwp[0];
|
||||
d[0].bval = 0;
|
||||
@@ -342,8 +342,8 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
|
||||
// Copy to simulator open array from from user bit array
|
||||
static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int nargs,
|
||||
int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0]; return;
|
||||
@@ -364,8 +364,8 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV
|
||||
// Copy to simulator open array from from user logic array
|
||||
static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
|
||||
int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0].aval; return;
|
||||
@@ -386,10 +386,10 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic
|
||||
|
||||
// Return bit from simulator open array
|
||||
static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1, int indx2,
|
||||
int indx3, int indx4) VL_MT_SAFE {
|
||||
int indx3, int) VL_MT_SAFE {
|
||||
// One extra index supported, as need bit number
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return 0;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8: return (*(reinterpret_cast<CData*>(datap))) & 1;
|
||||
@@ -401,11 +401,11 @@ static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1
|
||||
}
|
||||
// Update simulator open array from bit
|
||||
static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int nargs, int indx1,
|
||||
int indx2, int indx3, int indx4) VL_MT_SAFE {
|
||||
int indx2, int indx3, int) VL_MT_SAFE {
|
||||
// One extra index supported, as need bit number
|
||||
value &= 1; // Make sure clean
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) { // LCOV_EXCL_BR_LINE
|
||||
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = value; return;
|
||||
@@ -420,7 +420,7 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg
|
||||
// DPI accessors that simply call above functions
|
||||
|
||||
void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
|
||||
void* datap;
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
@@ -454,19 +454,19 @@ void* svGetArrElemPtr3(const svOpenArrayHandle h, int indx1, int indx2, int indx
|
||||
}
|
||||
|
||||
void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svPutBitArrElemVecVal(d, s, 1, indx1, 0, 0); break;
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
const int indx3 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
break;
|
||||
}
|
||||
@@ -486,19 +486,19 @@ void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int
|
||||
_vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
}
|
||||
void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svPutLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break;
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
_vl_svPutLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
const int indx3 = va_arg(ap, int);
|
||||
_vl_svPutLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
break;
|
||||
}
|
||||
@@ -522,19 +522,19 @@ void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
|
||||
// From simulator storage into user space
|
||||
|
||||
void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svGetBitArrElemVecVal(d, s, 1, indx1, 0, 0); break;
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
_vl_svGetBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
const int indx3 = va_arg(ap, int);
|
||||
_vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
break;
|
||||
}
|
||||
@@ -553,19 +553,19 @@ void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1
|
||||
_vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
}
|
||||
void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svGetLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break;
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
_vl_svGetLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
const int indx3 = va_arg(ap, int);
|
||||
_vl_svGetLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
break;
|
||||
}
|
||||
@@ -585,20 +585,20 @@ void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int i
|
||||
}
|
||||
|
||||
svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
svBit out;
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break;
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
const int indx3 = va_arg(ap, int);
|
||||
out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0);
|
||||
break;
|
||||
}
|
||||
@@ -618,20 +618,20 @@ svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx
|
||||
}
|
||||
svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) {
|
||||
// Verilator doesn't support X/Z so can just call Bit version
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
|
||||
svBit out;
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break;
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
const int indx3 = va_arg(ap, int);
|
||||
out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0);
|
||||
break;
|
||||
}
|
||||
@@ -654,19 +654,19 @@ svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int
|
||||
}
|
||||
|
||||
void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break;
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
const int indx3 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0);
|
||||
break;
|
||||
}
|
||||
@@ -685,19 +685,19 @@ void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, int ind
|
||||
}
|
||||
void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...) {
|
||||
// Verilator doesn't support X/Z so can just call Bit version
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break;
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
const int indx2 = va_arg(ap, int);
|
||||
const int indx3 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0);
|
||||
break;
|
||||
}
|
||||
@@ -732,15 +732,15 @@ svScope svGetScope() {
|
||||
}
|
||||
|
||||
svScope svSetScope(const svScope scope) {
|
||||
const VerilatedScope* prevScopep = Verilated::dpiScope();
|
||||
const VerilatedScope* vscopep = reinterpret_cast<const VerilatedScope*>(scope);
|
||||
const VerilatedScope* const prevScopep = Verilated::dpiScope();
|
||||
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
|
||||
Verilated::dpiScope(vscopep);
|
||||
// NOLINTNEXTLINE(google-readability-casting)
|
||||
return (svScope)(prevScopep);
|
||||
}
|
||||
|
||||
const char* svGetNameFromScope(const svScope scope) {
|
||||
const VerilatedScope* vscopep = reinterpret_cast<const VerilatedScope*>(scope);
|
||||
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
|
||||
return vscopep->name();
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -37,7 +37,7 @@
|
||||
|
||||
// Convert svBitVecVal to Verilator internal data
|
||||
static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
int words = VL_WORDS_I(obits);
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
||||
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
@@ -47,20 +47,20 @@ static inline QData VL_SET_Q_SVBV(const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
static inline IData VL_SET_I_SVBV(const svBitVecVal* lwp) VL_MT_SAFE { return lwp[0]; }
|
||||
|
||||
// Convert Verilator internal data to svBitVecVal
|
||||
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, WDataInP lwp) VL_MT_SAFE {
|
||||
int words = VL_WORDS_I(obits);
|
||||
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
||||
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, IData ld) VL_MT_SAFE { owp[0] = ld; }
|
||||
static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, QData ld) VL_MT_SAFE {
|
||||
static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; }
|
||||
static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE {
|
||||
VL_SET_WQ(owp, ld);
|
||||
}
|
||||
|
||||
// Convert svLogicVecVal to Verilator internal data
|
||||
// Note these functions ignore X/Z in svLogicVecVal
|
||||
static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
int words = VL_WORDS_I(obits);
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval;
|
||||
owp[words - 1] = lwp[words - 1].aval & VL_MASK_I(obits);
|
||||
}
|
||||
@@ -71,18 +71,18 @@ static inline IData VL_SET_I_SVLV(const svLogicVecVal* lwp) VL_MT_SAFE { return
|
||||
|
||||
// Convert Verilator internal data to svLogicVecVal
|
||||
// Note these functions never create X/Z in svLogicVecVal
|
||||
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, WDataInP lwp) VL_MT_SAFE {
|
||||
int words = VL_WORDS_I(obits);
|
||||
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
|
||||
const int words = VL_WORDS_I(obits);
|
||||
for (int i = 0; i < words; ++i) owp[i].bval = 0;
|
||||
for (int i = 0; i < words - 1; ++i) owp[i].aval = lwp[i];
|
||||
owp[words - 1].aval = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, IData ld) VL_MT_SAFE {
|
||||
static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, const IData ld) VL_MT_SAFE {
|
||||
owp[0].aval = ld;
|
||||
owp[0].bval = 0;
|
||||
}
|
||||
static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, QData ld) VL_MT_SAFE {
|
||||
WData lwp[2];
|
||||
static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, const QData ld) VL_MT_SAFE {
|
||||
VlWide<2> lwp;
|
||||
VL_SET_WQ(lwp, ld);
|
||||
owp[0].aval = lwp[0];
|
||||
owp[0].bval = 0;
|
||||
|
||||
+24
-22
@@ -53,29 +53,31 @@
|
||||
|
||||
//=============================================================================
|
||||
// Check that vltscope_t matches fstScopeType
|
||||
static_assert((int)FST_ST_VCD_MODULE == (int)VLT_TRACE_SCOPE_MODULE,
|
||||
|
||||
static_assert(static_cast<int>(FST_ST_VCD_MODULE) == static_cast<int>(VLT_TRACE_SCOPE_MODULE),
|
||||
"VLT_TRACE_SCOPE_MODULE mismatches");
|
||||
static_assert((int)FST_ST_VCD_TASK == (int)VLT_TRACE_SCOPE_TASK,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_TASK) == static_cast<int>(VLT_TRACE_SCOPE_TASK),
|
||||
"VLT_TRACE_SCOPE_TASK mismatches");
|
||||
static_assert((int)FST_ST_VCD_FUNCTION == (int)VLT_TRACE_SCOPE_FUNCTION,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_FUNCTION) == static_cast<int>(VLT_TRACE_SCOPE_FUNCTION),
|
||||
"VLT_TRACE_SCOPE_FUNCTION mismatches");
|
||||
static_assert((int)FST_ST_VCD_BEGIN == (int)VLT_TRACE_SCOPE_BEGIN,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_BEGIN) == static_cast<int>(VLT_TRACE_SCOPE_BEGIN),
|
||||
"VLT_TRACE_SCOPE_BEGIN mismatches");
|
||||
static_assert((int)FST_ST_VCD_FORK == (int)VLT_TRACE_SCOPE_FORK,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_FORK) == static_cast<int>(VLT_TRACE_SCOPE_FORK),
|
||||
"VLT_TRACE_SCOPE_FORK mismatches");
|
||||
static_assert((int)FST_ST_VCD_GENERATE == (int)VLT_TRACE_SCOPE_GENERATE,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_GENERATE) == static_cast<int>(VLT_TRACE_SCOPE_GENERATE),
|
||||
"VLT_TRACE_SCOPE_GENERATE mismatches");
|
||||
static_assert((int)FST_ST_VCD_STRUCT == (int)VLT_TRACE_SCOPE_STRUCT,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_STRUCT) == static_cast<int>(VLT_TRACE_SCOPE_STRUCT),
|
||||
"VLT_TRACE_SCOPE_STRUCT mismatches");
|
||||
static_assert((int)FST_ST_VCD_UNION == (int)VLT_TRACE_SCOPE_UNION,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_UNION) == static_cast<int>(VLT_TRACE_SCOPE_UNION),
|
||||
"VLT_TRACE_SCOPE_UNION mismatches");
|
||||
static_assert((int)FST_ST_VCD_CLASS == (int)VLT_TRACE_SCOPE_CLASS,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_CLASS) == static_cast<int>(VLT_TRACE_SCOPE_CLASS),
|
||||
"VLT_TRACE_SCOPE_CLASS mismatches");
|
||||
static_assert((int)FST_ST_VCD_INTERFACE == (int)VLT_TRACE_SCOPE_INTERFACE,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_INTERFACE)
|
||||
== static_cast<int>(VLT_TRACE_SCOPE_INTERFACE),
|
||||
"VLT_TRACE_SCOPE_INTERFACE mismatches");
|
||||
static_assert((int)FST_ST_VCD_PACKAGE == (int)VLT_TRACE_SCOPE_PACKAGE,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_PACKAGE) == static_cast<int>(VLT_TRACE_SCOPE_PACKAGE),
|
||||
"VLT_TRACE_SCOPE_PACKAGE mismatches");
|
||||
static_assert((int)FST_ST_VCD_PROGRAM == (int)VLT_TRACE_SCOPE_PROGRAM,
|
||||
static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE_SCOPE_PROGRAM),
|
||||
"VLT_TRACE_SCOPE_PROGRAM mismatches");
|
||||
|
||||
//=============================================================================
|
||||
@@ -98,7 +100,7 @@ VerilatedFst::~VerilatedFst() {
|
||||
}
|
||||
|
||||
void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_fst = fstWriterCreate(filename, 1);
|
||||
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
|
||||
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
|
||||
@@ -130,14 +132,14 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
}
|
||||
|
||||
void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedFst>::closeBase();
|
||||
fstWriterClose(m_fst);
|
||||
m_fst = nullptr;
|
||||
}
|
||||
|
||||
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedFst>::flushBase();
|
||||
fstWriterFlushContext(m_fst);
|
||||
}
|
||||
@@ -150,7 +152,7 @@ void VerilatedFst::emitTimeChange(vluint64_t timeui) { fstWriterEmitTimeChange(m
|
||||
void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
|
||||
unsigned int minValbits, const char** itemNamesp,
|
||||
const char** itemValuesp) {
|
||||
fstEnumHandle enumNum
|
||||
const fstEnumHandle enumNum
|
||||
= fstWriterCreateEnumTable(m_fst, name, elements, minValbits, itemNamesp, itemValuesp);
|
||||
m_local2fstdtype[dtypenum] = enumNum;
|
||||
}
|
||||
@@ -161,11 +163,11 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
|
||||
|
||||
VerilatedTrace<VerilatedFst>::declCode(code, bits, false);
|
||||
|
||||
std::istringstream nameiss(name);
|
||||
std::istringstream nameiss{name};
|
||||
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());
|
||||
std::string symbol_name{tokens.back()};
|
||||
tokens.pop_back(); // Remove symbol name from hierarchy
|
||||
tokens.insert(tokens.begin(), moduleName()); // Add current module to the hierarchy
|
||||
std::string tmpModName;
|
||||
@@ -191,8 +193,8 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
|
||||
tmpModName = *new_it;
|
||||
tmpModName.pop_back();
|
||||
// If the scope ends with a non-ascii character, it will be 0x80 + fstScopeType
|
||||
fstWriterSetScope(m_fst, (fstScopeType)(new_it->back() & 0x7f), tmpModName.c_str(),
|
||||
nullptr);
|
||||
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);
|
||||
|
||||
@@ -202,11 +204,11 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
|
||||
|
||||
std::stringstream name_ss;
|
||||
name_ss << symbol_name;
|
||||
if (array) name_ss << "(" << arraynum << ")";
|
||||
if (array) name_ss << "[" << arraynum << "]";
|
||||
std::string name_str = name_ss.str();
|
||||
|
||||
if (dtypenum > 0) {
|
||||
fstEnumHandle enumNum = m_local2fstdtype[dtypenum];
|
||||
const fstEnumHandle enumNum = m_local2fstdtype[dtypenum];
|
||||
fstWriterEmitEnumTableRef(m_fst, enumNum);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,9 +42,9 @@ public:
|
||||
// We want to avoid a depreciated warning, but still be back compatible.
|
||||
// Turning off the message just for this still results in an
|
||||
// annoying "to turn off" message.
|
||||
sc_time t1sec(1, SC_SEC);
|
||||
const sc_time t1sec(1, SC_SEC);
|
||||
if (t1sec.to_default_time_units() != 0) {
|
||||
sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC);
|
||||
const sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC);
|
||||
spTrace()->set_time_unit(tunits.to_string());
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+7
-957
@@ -12,970 +12,20 @@
|
||||
//*************************************************************************
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated string and data-type header
|
||||
/// \brief Verilated old string and data-type header
|
||||
///
|
||||
/// This file is included automatically by Verilator at the top of
|
||||
/// all C++ files it generates. It is used when strings or other
|
||||
/// heavyweight types are required; these contents are not part of
|
||||
/// verilated.h to save compile time when such types aren't used.
|
||||
/// This file is deprecated, and provided for backwards compatibility.
|
||||
/// Include verilated.h instead.
|
||||
///
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_VERILATED_HEAVY_H_
|
||||
#define VERILATOR_VERILATED_HEAVY_H_
|
||||
|
||||
#ifdef VL_NO_LEGACY
|
||||
#error "Include <verilated.h> instead of <verilated_heavy.h>"
|
||||
#endif
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
//===================================================================
|
||||
// String formatters (required by below containers)
|
||||
|
||||
extern std::string VL_TO_STRING(CData lhs);
|
||||
extern std::string VL_TO_STRING(SData lhs);
|
||||
extern std::string VL_TO_STRING(IData lhs);
|
||||
extern std::string VL_TO_STRING(QData lhs);
|
||||
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
|
||||
extern std::string VL_TO_STRING_W(int words, WDataInP obj);
|
||||
|
||||
//===================================================================
|
||||
// Shuffle RNG
|
||||
|
||||
class VlURNG final {
|
||||
public:
|
||||
using result_type = size_t;
|
||||
static constexpr size_t min() { return 0; }
|
||||
static constexpr size_t max() { return 1ULL << 31; }
|
||||
size_t operator()() { return VL_MASK_I(31) & VL_RANDOM_I(32); }
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Readmem/Writemem operation classes
|
||||
|
||||
class VlReadMem final {
|
||||
bool m_hex; // Hex format
|
||||
int m_bits; // Bit width of values
|
||||
const std::string& m_filename; // Filename
|
||||
QData m_end; // End address (as specified by user)
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to read
|
||||
int m_linenum; // Line number last read from file
|
||||
public:
|
||||
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlReadMem();
|
||||
bool isOpen() const { return m_fp != nullptr; }
|
||||
int linenum() const { return m_linenum; }
|
||||
bool get(QData& addrr, std::string& valuer);
|
||||
void setData(void* valuep, const std::string& rhs);
|
||||
};
|
||||
|
||||
class VlWriteMem final {
|
||||
bool m_hex; // Hex format
|
||||
int m_bits; // Bit width of values
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to write
|
||||
public:
|
||||
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlWriteMem();
|
||||
bool isOpen() const { return m_fp != nullptr; }
|
||||
void print(QData addr, bool addrstamp, const void* valuep);
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
/// Verilog wide unpacked bit container.
|
||||
/// Similar to std::array<WData, N>, but lighter weight, only methods needed
|
||||
/// by Verilator, to help compile time.
|
||||
///
|
||||
/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
|
||||
/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
|
||||
/// zero in memory, but during intermediate operations in the Verilated
|
||||
/// internals is unpredictable.
|
||||
|
||||
template <std::size_t T_Words> class VlWide final {
|
||||
EData m_storage[T_Words];
|
||||
|
||||
public:
|
||||
// cppcheck-suppress uninitVar
|
||||
VlWide() = default;
|
||||
~VlWide() = default;
|
||||
VlWide(const VlWide&) = default;
|
||||
VlWide(VlWide&&) = default;
|
||||
|
||||
// OPERATOR METHODS
|
||||
VlWide& operator=(const VlWide&) = default;
|
||||
VlWide& operator=(VlWide&&) = default;
|
||||
const EData& operator[](size_t index) const { return m_storage[index]; };
|
||||
EData& operator[](size_t index) { return m_storage[index]; };
|
||||
operator WDataOutP() { return &m_storage[0]; }
|
||||
operator WDataInP() const { return &m_storage[0]; }
|
||||
|
||||
// METHODS
|
||||
const EData& at(size_t index) const { return m_storage[index]; }
|
||||
EData& at(size_t index) { return m_storage[index]; }
|
||||
WData* data() { return &m_storage[0]; }
|
||||
const WData* data() const { return &m_storage[0]; }
|
||||
bool operator<(const VlWide<T_Words>& rhs) const {
|
||||
return VL_LT_W(T_Words, data(), rhs.data());
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a C array to std::array reference by pointer magic, without copy.
|
||||
// Data type (second argument) is so the function template can automatically generate.
|
||||
template <std::size_t T_Words> VlWide<T_Words>& VL_CVT_W_A(WDataInP inp, const VlWide<T_Words>&) {
|
||||
return *((VlWide<T_Words>*)inp);
|
||||
}
|
||||
|
||||
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
|
||||
return VL_TO_STRING_W(T_Words, obj.data());
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog queue and dynamic array container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
//
|
||||
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
|
||||
// For dynamic arrays it is always zero
|
||||
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
|
||||
private:
|
||||
// TYPES
|
||||
using Deque = std::deque<T_Value>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename Deque::const_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
Deque m_deque; // State of the assoc array
|
||||
T_Value m_defaultValue; // Default value
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// m_defaultValue isn't defaulted. Caller's constructor must do it.
|
||||
VlQueue() = default;
|
||||
~VlQueue() = default;
|
||||
VlQueue(const VlQueue&) = default;
|
||||
VlQueue(VlQueue&&) = default;
|
||||
VlQueue& operator=(const VlQueue&) = default;
|
||||
VlQueue& operator=(VlQueue&&) = default;
|
||||
|
||||
// Standard copy constructor works. Verilog: assoca = assocb
|
||||
// Also must allow conversion from a different T_MaxSize queue
|
||||
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
|
||||
m_deque = rhs.privateDeque();
|
||||
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static VlQueue cons(const T_Value& lhs) {
|
||||
VlQueue out;
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
|
||||
VlQueue out;
|
||||
out.push_back(rhs);
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
|
||||
VlQueue out = lhs;
|
||||
out.push_front(rhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
|
||||
VlQueue out = rhs;
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
|
||||
VlQueue out = rhs;
|
||||
for (const auto& i : lhs.m_deque) out.push_back(i);
|
||||
return out;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
T_Value& atDefault() { return m_defaultValue; }
|
||||
const T_Value& atDefault() const { return m_defaultValue; }
|
||||
const Deque& privateDeque() const { return m_deque; }
|
||||
|
||||
// Size. Verilog: function int size(), or int num()
|
||||
int size() const { return m_deque.size(); }
|
||||
// Clear array. Verilog: function void delete([input index])
|
||||
void clear() { m_deque.clear(); }
|
||||
void erase(vlsint32_t index) {
|
||||
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
|
||||
m_deque.erase(m_deque.begin() + index);
|
||||
}
|
||||
|
||||
// Dynamic array new[] becomes a renew()
|
||||
void renew(size_t size) {
|
||||
clear();
|
||||
m_deque.resize(size, atDefault());
|
||||
}
|
||||
// Dynamic array new[]() becomes a renew_copy()
|
||||
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
|
||||
if (size == 0) {
|
||||
clear();
|
||||
} else {
|
||||
*this = rhs;
|
||||
m_deque.resize(size, atDefault());
|
||||
}
|
||||
}
|
||||
|
||||
// function void q.push_front(value)
|
||||
void push_front(const T_Value& value) {
|
||||
m_deque.push_front(value);
|
||||
if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back();
|
||||
}
|
||||
// function void q.push_back(value)
|
||||
void push_back(const T_Value& value) {
|
||||
if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value);
|
||||
}
|
||||
// function value_t q.pop_front();
|
||||
T_Value pop_front() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
T_Value v = m_deque.front();
|
||||
m_deque.pop_front();
|
||||
return v;
|
||||
}
|
||||
// function value_t q.pop_back();
|
||||
T_Value pop_back() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
T_Value v = m_deque.back();
|
||||
m_deque.pop_back();
|
||||
return v;
|
||||
}
|
||||
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(vlsint32_t index) {
|
||||
static T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
s_throwAway = atDefault();
|
||||
return s_throwAway;
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(vlsint32_t index) const {
|
||||
static T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
return atDefault();
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
}
|
||||
// function void q.insert(index, value);
|
||||
void insert(vlsint32_t index, const T_Value& value) {
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
|
||||
m_deque.insert(m_deque.begin() + index, value);
|
||||
}
|
||||
|
||||
// Return slice q[lsb:msb]
|
||||
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
|
||||
VlQueue out;
|
||||
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
|
||||
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
|
||||
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
|
||||
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
|
||||
return out;
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_deque.begin(); }
|
||||
const_iterator end() const { return m_deque.end(); }
|
||||
|
||||
// Methods
|
||||
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
|
||||
template <typename Func> void sort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaninless with sort, as it changes
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
}
|
||||
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
|
||||
template <typename Func> void rsort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaninless with sort, as it changes
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
}
|
||||
void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
|
||||
void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG()); }
|
||||
VlQueue unique() const {
|
||||
VlQueue out;
|
||||
std::unordered_set<T_Value> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
auto it = saw.find(i);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i);
|
||||
out.push_back(i);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<IData> unique_index() const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
std::unordered_set<T_Value> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
auto it = saw.find(i);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i);
|
||||
out.push_back(index);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find(Func with_func) const {
|
||||
VlQueue out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) out.push_back(i);
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) out.push_back(index);
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find_first(Func with_func) const {
|
||||
// Can't use std::find_if as need index number
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) return VlQueue::cons(i);
|
||||
++index;
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) return VlQueue<IData>::cons(index);
|
||||
++index;
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
}
|
||||
template <typename Func> VlQueue find_last(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
|
||||
if (with_func(index, *it)) return VlQueue::cons(*it);
|
||||
--index;
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
|
||||
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
|
||||
--index;
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue min() const {
|
||||
if (m_deque.empty()) return VlQueue();
|
||||
const auto it = std::min_element(m_deque.begin(), m_deque.end());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
VlQueue max() const {
|
||||
if (m_deque.empty()) return VlQueue();
|
||||
const auto it = std::max_element(m_deque.begin(), m_deque.end());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out += i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out += with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out *= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out &= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out |= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out |= with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
T_Value r_xor() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out ^= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out ^= with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
if (m_deque.empty()) return "'{}"; // No trailing space
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (const auto& i : m_deque) {
|
||||
out += comma + VL_TO_STRING(i);
|
||||
comma = ", ";
|
||||
}
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog associative array container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
//
|
||||
template <class T_Key, class T_Value> class VlAssocArray final {
|
||||
private:
|
||||
// TYPES
|
||||
using Map = std::map<T_Key, T_Value>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename Map::const_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
Map m_map; // State of the assoc array
|
||||
T_Value m_defaultValue; // Default value
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// m_defaultValue isn't defaulted. Caller's constructor must do it.
|
||||
VlAssocArray() = default;
|
||||
~VlAssocArray() = default;
|
||||
VlAssocArray(const VlAssocArray&) = default;
|
||||
VlAssocArray(VlAssocArray&&) = default;
|
||||
VlAssocArray& operator=(const VlAssocArray&) = default;
|
||||
VlAssocArray& operator=(VlAssocArray&&) = default;
|
||||
|
||||
// METHODS
|
||||
T_Value& atDefault() { return m_defaultValue; }
|
||||
const T_Value& atDefault() const { return m_defaultValue; }
|
||||
|
||||
// Size of array. Verilog: function int size(), or int num()
|
||||
int size() const { return m_map.size(); }
|
||||
// Clear array. Verilog: function void delete([input index])
|
||||
void clear() { m_map.clear(); }
|
||||
void erase(const T_Key& index) { m_map.erase(index); }
|
||||
// Return 0/1 if element exists. Verilog: function int exists(input index)
|
||||
int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
|
||||
// Return first element. Verilog: function int first(ref index);
|
||||
int first(T_Key& indexr) const {
|
||||
const auto it = m_map.cbegin();
|
||||
if (it == m_map.end()) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return last element. Verilog: function int last(ref index)
|
||||
int last(T_Key& indexr) const {
|
||||
const auto it = m_map.crbegin();
|
||||
if (it == m_map.rend()) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return next element. Verilog: function int next(ref index)
|
||||
int next(T_Key& indexr) const {
|
||||
auto it = m_map.find(indexr);
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
++it;
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return prev element. Verilog: function int prev(ref index)
|
||||
int prev(T_Key& indexr) const {
|
||||
auto it = m_map.find(indexr);
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
if (VL_UNLIKELY(it == m_map.begin())) return 0;
|
||||
--it;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(const T_Key& index) {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
|
||||
return pit.first->second;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(const T_Key& index) const {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
return m_defaultValue;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
// Setting as a chained operation
|
||||
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
||||
at(index) = value;
|
||||
return *this;
|
||||
}
|
||||
VlAssocArray& setDefault(const T_Value& value) {
|
||||
atDefault() = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_map.begin(); }
|
||||
const_iterator end() const { return m_map.end(); }
|
||||
|
||||
// Methods
|
||||
VlQueue<T_Value> unique() const {
|
||||
VlQueue<T_Value> out;
|
||||
std::set<T_Value> saw;
|
||||
for (const auto& i : m_map) {
|
||||
auto it = saw.find(i.second);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i.second);
|
||||
out.push_back(i.second);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<T_Key> unique_index() const {
|
||||
VlQueue<T_Key> out;
|
||||
std::set<T_Key> saw;
|
||||
for (const auto& i : m_map) {
|
||||
auto it = saw.find(i.second);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i.second);
|
||||
out.push_back(i.first);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
for (const auto& i : m_map)
|
||||
if (with_func(i.first, i.second)) out.push_back(i.second);
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
for (const auto& i : m_map)
|
||||
if (with_func(i.first, i.second)) out.push_back(i.first);
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue<T_Value> min() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::min_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
VlQueue<T_Value> max() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out += i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out += with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out |= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out |= with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_xor() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out ^= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
if (m_map.empty()) return "'{}"; // No trailing space
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (const auto& i : m_map) {
|
||||
out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
|
||||
comma = ", ";
|
||||
}
|
||||
// Default not printed - maybe random init data
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
|
||||
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
|
||||
VlReadMem rmem(hex, bits, filename, start, end);
|
||||
if (VL_UNLIKELY(!rmem.isOpen())) return;
|
||||
while (true) {
|
||||
QData addr;
|
||||
std::string data;
|
||||
if (rmem.get(addr /*ref*/, data /*ref*/)) {
|
||||
rmem.setData(&(obj.at(addr)), data);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
|
||||
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
|
||||
VlWriteMem wmem(hex, bits, filename, start, end);
|
||||
if (VL_UNLIKELY(!wmem.isOpen())) return;
|
||||
for (const auto& i : obj) {
|
||||
QData addr = i.first;
|
||||
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
/// Verilog packed array container
|
||||
/// For when a standard C++[] array is not sufficient, e.g. an
|
||||
/// array under a queue, or methods operating on the array.
|
||||
///
|
||||
/// This class may get exposed to a Verilated Model's top I/O, if the top
|
||||
/// IO has an unpacked array.
|
||||
|
||||
template <class T_Value, std::size_t T_Depth> class VlUnpacked final {
|
||||
private:
|
||||
// TYPES
|
||||
using Array = std::array<T_Value, T_Depth>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename Array::const_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
Array m_array; // Contents of the packed array
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlUnpacked() = default;
|
||||
~VlUnpacked() = default;
|
||||
VlUnpacked(const VlUnpacked&) = default;
|
||||
VlUnpacked(VlUnpacked&&) = default;
|
||||
VlUnpacked& operator=(const VlUnpacked&) = default;
|
||||
VlUnpacked& operator=(VlUnpacked&&) = default;
|
||||
|
||||
// METHODS
|
||||
// Raw access
|
||||
WData* data() { return &m_array[0]; }
|
||||
const WData* data() const { return &m_array[0]; }
|
||||
|
||||
T_Value& operator[](size_t index) { return m_array[index]; };
|
||||
const T_Value& operator[](size_t index) const { return m_array[index]; };
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (int i = 0; i < T_Depth; ++i) {
|
||||
out += comma + VL_TO_STRING(m_array[i]);
|
||||
comma = ", ";
|
||||
}
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value, std::size_t T_Depth>
|
||||
std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog class reference container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
|
||||
#define VlClassRef std::shared_ptr
|
||||
|
||||
template <class T> // T typically of type VlClassRef<x>
|
||||
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
|
||||
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
|
||||
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
|
||||
if (VL_LIKELY(casted)) {
|
||||
outr = casted;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Conversion functions
|
||||
|
||||
extern std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE;
|
||||
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
|
||||
WData lw[VL_WQ_WORDS_E];
|
||||
VL_SET_WQ(lw, lhs);
|
||||
return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw);
|
||||
}
|
||||
inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; }
|
||||
inline std::string& VL_CVT_PACK_STR_NN(std::string& lhs) VL_PURE { return lhs; }
|
||||
inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE {
|
||||
WData lw[VL_WQ_WORDS_E];
|
||||
VL_SET_WI(lw, lhs);
|
||||
return VL_CVT_PACK_STR_NW(1, lw);
|
||||
}
|
||||
inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE {
|
||||
return lhs + rhs;
|
||||
}
|
||||
inline std::string VL_REPLICATEN_NNQ(int, int, int, const std::string& lhs, IData rep) VL_PURE {
|
||||
std::string out;
|
||||
out.reserve(lhs.length() * rep);
|
||||
for (unsigned times = 0; times < rep; ++times) out += lhs;
|
||||
return out;
|
||||
}
|
||||
inline std::string VL_REPLICATEN_NNI(int obits, int lbits, int rbits, const std::string& lhs,
|
||||
IData rep) VL_PURE {
|
||||
return VL_REPLICATEN_NNQ(obits, lbits, rbits, lhs, rep);
|
||||
}
|
||||
|
||||
inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); }
|
||||
extern std::string VL_TOLOWER_NN(const std::string& ld);
|
||||
extern std::string VL_TOUPPER_NN(const std::string& ld);
|
||||
|
||||
extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE;
|
||||
extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE;
|
||||
extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE;
|
||||
extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb,
|
||||
const std::string& filename, void* memp, QData start,
|
||||
QData end) VL_MT_SAFE;
|
||||
extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb,
|
||||
const std::string& filename, const void* memp, QData start,
|
||||
QData end) VL_MT_SAFE;
|
||||
extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE;
|
||||
extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp,
|
||||
...) VL_MT_SAFE;
|
||||
extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE;
|
||||
extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width,
|
||||
VerilatedContext* contextp) VL_MT_SAFE;
|
||||
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE;
|
||||
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE {
|
||||
WData rwp[2]; // WData must always be at least 2
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
|
||||
if (got) rdr = rwp[0];
|
||||
return got;
|
||||
}
|
||||
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE {
|
||||
WData rwp[2]; // WData must always be at least 2
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
|
||||
if (got) rdr = rwp[0];
|
||||
return got;
|
||||
}
|
||||
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
|
||||
WData rwp[2];
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
|
||||
if (got) rdr = rwp[0];
|
||||
return got;
|
||||
}
|
||||
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE {
|
||||
WData rwp[2];
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
|
||||
if (got) rdr = VL_SET_QW(rwp);
|
||||
return got;
|
||||
}
|
||||
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE {
|
||||
WData rwp[2];
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
|
||||
if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp));
|
||||
return got;
|
||||
}
|
||||
extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE;
|
||||
|
||||
//======================================================================
|
||||
// Strings
|
||||
|
||||
extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE;
|
||||
extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE;
|
||||
extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE;
|
||||
|
||||
inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE {
|
||||
// SystemVerilog does not allow a string variable to contain '\0'.
|
||||
// So C functions such as strcmp() can correctly compare strings.
|
||||
if (ignoreCase) {
|
||||
return VL_STRCASECMP(lhs.c_str(), rhs.c_str());
|
||||
} else {
|
||||
return std::strcmp(lhs.c_str(), rhs.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
|
||||
|
||||
extern IData VL_FGETS_NI(std::string& dest, IData fpi);
|
||||
|
||||
#endif // Guard
|
||||
|
||||
+25
-26
@@ -30,7 +30,6 @@
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_heavy.h"
|
||||
#include "verilated_syms.h"
|
||||
|
||||
#include <deque>
|
||||
@@ -104,7 +103,7 @@ public:
|
||||
// METHODS
|
||||
// Add message to queue (called by producer)
|
||||
void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_queue.insert(msg); // Pass by value to copy the message into queue
|
||||
++m_depth;
|
||||
}
|
||||
@@ -124,7 +123,7 @@ public:
|
||||
const VerilatedMsg msg = *(it);
|
||||
m_queue.erase(it);
|
||||
m_mutex.unlock();
|
||||
m_depth--; // Ok if outside critical section as only this code checks the value
|
||||
--m_depth; // Ok if outside critical section as only this code checks the value
|
||||
{
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from mtaskId=%d\n", msg.mtaskId()););
|
||||
msg.run();
|
||||
@@ -179,7 +178,7 @@ public:
|
||||
|
||||
// FILE* list constructed from a file-descriptor
|
||||
class VerilatedFpList final {
|
||||
FILE* m_fp[31];
|
||||
FILE* m_fp[31] = {};
|
||||
std::size_t m_sz = 0;
|
||||
|
||||
public:
|
||||
@@ -256,11 +255,11 @@ public: // But only for verilated*.cpp
|
||||
int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; }
|
||||
void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; }
|
||||
std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
const VerilatedLockGuard lock(m_timeDumpMutex);
|
||||
const VerilatedLockGuard lock{m_timeDumpMutex};
|
||||
return m_timeFormatSuffix;
|
||||
}
|
||||
void timeFormatSuffix(const std::string& value) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
const VerilatedLockGuard lock(m_timeDumpMutex);
|
||||
const VerilatedLockGuard lock{m_timeDumpMutex};
|
||||
m_timeFormatSuffix = value;
|
||||
}
|
||||
|
||||
@@ -276,7 +275,7 @@ public: // But only for verilated*.cpp
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - file IO
|
||||
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
if (m_fdFreeMct.empty()) return 0;
|
||||
IData idx = m_fdFreeMct.back();
|
||||
m_fdFreeMct.pop_back();
|
||||
@@ -285,10 +284,10 @@ public: // But only for verilated*.cpp
|
||||
return (1 << idx);
|
||||
}
|
||||
IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
FILE* fp = std::fopen(filenamep, modep);
|
||||
FILE* const fp = std::fopen(filenamep, modep);
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
// Bit 31 indicates it's a descriptor not a MCD
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
if (m_fdFree.empty()) {
|
||||
// Need to create more space in m_fdps and m_fdFree
|
||||
const std::size_t start = std::max<std::size_t>(31UL + 1UL + 3UL, m_fdps.size());
|
||||
@@ -306,25 +305,25 @@ public: // But only for verilated*.cpp
|
||||
return (idx | (1UL << 31)); // bit 31 indicates not MCD
|
||||
}
|
||||
void fdFlush(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
for (const auto& i : fdlist) std::fflush(i);
|
||||
}
|
||||
IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
|
||||
return static_cast<IData>(
|
||||
std::fseek(*fdlist.begin(), static_cast<long>(offset), static_cast<int>(origin)));
|
||||
}
|
||||
IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
|
||||
return static_cast<IData>(std::ftell(*fdlist.begin()));
|
||||
}
|
||||
void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
for (const auto& i : fdlist) {
|
||||
if (VL_UNLIKELY(!i)) continue;
|
||||
@@ -332,7 +331,7 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
}
|
||||
void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
if (VL_BITISSET_I(fdi, 31)) {
|
||||
// Non-MCD case
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
@@ -356,7 +355,7 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
}
|
||||
inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
|
||||
return *fdlist.begin();
|
||||
@@ -463,7 +462,7 @@ public:
|
||||
// per map overhead * N scopes would take much more space and cache thrashing.
|
||||
// As scopep's are pointers, this implicitly handles multiple Context's
|
||||
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(s().m_userMapMutex);
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
|
||||
if (it != s().m_userMap.end()) {
|
||||
it->second = userData;
|
||||
@@ -472,7 +471,7 @@ public:
|
||||
}
|
||||
}
|
||||
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(s().m_userMapMutex);
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
|
||||
if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
|
||||
return it->second;
|
||||
@@ -482,7 +481,7 @@ public: // But only for verilated.cpp
|
||||
// Symbol table destruction cleans up the entries for each scope.
|
||||
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope on destruction, so we simply iterate.
|
||||
const VerilatedLockGuard lock(s().m_userMapMutex);
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
|
||||
if (it->first.first == scopep) {
|
||||
s().m_userMap.erase(it++);
|
||||
@@ -492,7 +491,7 @@ public: // But only for verilated.cpp
|
||||
}
|
||||
}
|
||||
static void userDump() VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(s().m_userMapMutex); // Avoid it changing in middle of dump
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex}; // Avoid it changing in middle of dump
|
||||
bool first = true;
|
||||
for (const auto& i : s().m_userMap) {
|
||||
if (first) {
|
||||
@@ -508,13 +507,13 @@ public: // But only for verilated*.cpp
|
||||
// METHODS - hierarchy
|
||||
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
|
||||
// Slow ok - called at construction for VPI accessible elements
|
||||
const VerilatedLockGuard lock(s().m_hierMapMutex);
|
||||
const VerilatedLockGuard lock{s().m_hierMapMutex};
|
||||
s().m_hierMap[fromp].push_back(top);
|
||||
}
|
||||
static void hierarchyRemove(const VerilatedScope* fromp,
|
||||
const VerilatedScope* top) VL_MT_SAFE {
|
||||
// Slow ok - called at destruction for VPI accessible elements
|
||||
const VerilatedLockGuard lock(s().m_hierMapMutex);
|
||||
const VerilatedLockGuard lock{s().m_hierMapMutex};
|
||||
VerilatedHierarchyMap& map = s().m_hierMap;
|
||||
if (map.find(fromp) == map.end()) return;
|
||||
auto& scopes = map[fromp];
|
||||
@@ -537,7 +536,7 @@ public: // But only for verilated*.cpp
|
||||
// miss at the cost of a multiply, and all lookups move to slowpath.
|
||||
static int exportInsert(const char* namep) VL_MT_SAFE {
|
||||
// Slow ok - called once/function at creation
|
||||
const VerilatedLockGuard lock(s().m_exportMutex);
|
||||
const VerilatedLockGuard lock{s().m_exportMutex};
|
||||
const auto it = s().m_exportMap.find(namep);
|
||||
if (it == s().m_exportMap.end()) {
|
||||
s().m_exportMap.emplace(namep, s().m_exportNext++);
|
||||
@@ -547,24 +546,24 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
}
|
||||
static int exportFind(const char* namep) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(s().m_exportMutex);
|
||||
const VerilatedLockGuard lock{s().m_exportMutex};
|
||||
const auto& it = s().m_exportMap.find(namep);
|
||||
if (VL_LIKELY(it != s().m_exportMap.end())) return it->second;
|
||||
std::string msg = (std::string("%Error: Testbench C called ") + namep
|
||||
std::string msg = (std::string{"%Error: Testbench C called "} + namep
|
||||
+ " but no such DPI export function name exists in ANY model");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
return -1;
|
||||
}
|
||||
static const char* exportName(int funcnum) VL_MT_SAFE {
|
||||
// Slowpath; find name for given export; errors only so no map to reverse-map it
|
||||
const VerilatedLockGuard lock(s().m_exportMutex);
|
||||
const VerilatedLockGuard lock{s().m_exportMutex};
|
||||
for (const auto& i : s().m_exportMap) {
|
||||
if (i.second == funcnum) return i.first;
|
||||
}
|
||||
return "*UNKNOWN*";
|
||||
}
|
||||
static void exportsDump() VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(s().m_exportMutex);
|
||||
const VerilatedLockGuard lock{s().m_exportMutex};
|
||||
bool first = true;
|
||||
for (const auto& i : s().m_exportMap) {
|
||||
if (first) {
|
||||
|
||||
+18
-16
@@ -72,9 +72,10 @@ bool VerilatedDeserialize::readDiffers(const void* __restrict datap,
|
||||
VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap,
|
||||
size_t size) VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(readDiffers(datap, size))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = "Can't deserialize save-restore file as was made from different model: "
|
||||
+ filename();
|
||||
const std::string fn = filename();
|
||||
const std::string msg
|
||||
= "Can't deserialize save-restore file as was made from different model: "
|
||||
+ filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
// Die before we close() as close would infinite loop
|
||||
}
|
||||
@@ -90,10 +91,10 @@ void VerilatedSerialize::header() VL_MT_UNSAFE_ONE {
|
||||
void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) {
|
||||
std::string fn = filename();
|
||||
std::string msg
|
||||
= std::string(
|
||||
"Can't deserialize; file has wrong header signature, or file not found: ")
|
||||
const std::string fn = filename();
|
||||
const std::string msg
|
||||
= std::
|
||||
string{"Can't deserialize; file has wrong header signature, or file not found: "}
|
||||
+ filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
// Die before we close() as close would infinite loop
|
||||
@@ -109,9 +110,10 @@ void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR)))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature: ")
|
||||
+ filename();
|
||||
const std::string fn = filename();
|
||||
const std::string msg
|
||||
= std::string{"Can't deserialize; file has wrong end-of-file signature: "}
|
||||
+ filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
// Die before we close() as close would infinite loop
|
||||
}
|
||||
@@ -192,17 +194,17 @@ void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
vluint8_t* wp = m_bufp;
|
||||
while (true) {
|
||||
ssize_t remaining = (m_cp - wp);
|
||||
const ssize_t remaining = (m_cp - wp);
|
||||
if (remaining == 0) break;
|
||||
errno = 0;
|
||||
ssize_t got = ::write(m_fd, wp, remaining);
|
||||
const ssize_t got = ::write(m_fd, wp, remaining);
|
||||
if (got > 0) {
|
||||
wp += got;
|
||||
} else if (VL_UNCOVERABLE(got < 0)) {
|
||||
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
|
||||
// LCOV_EXCL_START
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno);
|
||||
std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
close();
|
||||
break;
|
||||
@@ -223,17 +225,17 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
|
||||
m_cp = m_bufp; // Reset buffer
|
||||
// Read into buffer starting at m_endp
|
||||
while (true) {
|
||||
ssize_t remaining = (m_bufp + bufferSize() - m_endp);
|
||||
const ssize_t remaining = (m_bufp + bufferSize() - m_endp);
|
||||
if (remaining == 0) break;
|
||||
errno = 0;
|
||||
ssize_t got = ::read(m_fd, m_endp, remaining);
|
||||
const ssize_t got = ::read(m_fd, m_endp, remaining);
|
||||
if (got > 0) {
|
||||
m_endp += got;
|
||||
} else if (VL_UNCOVERABLE(got < 0)) {
|
||||
if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
|
||||
// LCOV_EXCL_START
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno);
|
||||
const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
close();
|
||||
break;
|
||||
|
||||
+11
-11
@@ -23,7 +23,7 @@
|
||||
#define VERILATOR_VERILATED_SAVE_C_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_heavy.h"
|
||||
#include "verilated.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -236,43 +236,43 @@ public:
|
||||
|
||||
//=============================================================================
|
||||
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint64_t& rhs) {
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint64_t& rhs) {
|
||||
return os.write(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint64_t& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint32_t& rhs) {
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint32_t& rhs) {
|
||||
return os.write(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint32_t& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint16_t& rhs) {
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint16_t& rhs) {
|
||||
return os.write(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint16_t& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint8_t& rhs) {
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint8_t& rhs) {
|
||||
return os.write(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint8_t& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, bool& rhs) {
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const bool& rhs) {
|
||||
return os.write(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, bool& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, double& rhs) {
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const double& rhs) {
|
||||
return os.write(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, double& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, float& rhs) {
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const float& rhs) {
|
||||
return os.write(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) {
|
||||
@@ -295,11 +295,11 @@ VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhs
|
||||
template <class T_Key, class T_Value>
|
||||
VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
|
||||
os << rhs.atDefault();
|
||||
vluint32_t len = rhs.size();
|
||||
const vluint32_t len = rhs.size();
|
||||
os << len;
|
||||
for (const auto& i : rhs) {
|
||||
T_Key index = i.first; // Copy to get around const_iterator
|
||||
T_Value value = i.second;
|
||||
const T_Key index = i.first; // Copy to get around const_iterator
|
||||
const T_Value value = i.second;
|
||||
os << index << value;
|
||||
}
|
||||
return os;
|
||||
|
||||
@@ -152,30 +152,34 @@ public:
|
||||
const VerilatedRange& unpacked() const { return m_unpacked[0]; }
|
||||
// DPI accessors
|
||||
int left(int dim) const {
|
||||
return dim == 0 ? m_packed.left()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left() : 0;
|
||||
return dim == 0 ? m_packed.left()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left()
|
||||
: 0;
|
||||
}
|
||||
int right(int dim) const {
|
||||
return dim == 0 ? m_packed.right()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right() : 0;
|
||||
return dim == 0 ? m_packed.right()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right()
|
||||
: 0;
|
||||
}
|
||||
int low(int dim) const {
|
||||
return dim == 0 ? m_packed.low()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low() : 0;
|
||||
return dim == 0 ? m_packed.low()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low()
|
||||
: 0;
|
||||
}
|
||||
int high(int dim) const {
|
||||
return dim == 0 ? m_packed.high()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high() : 0;
|
||||
return dim == 0 ? m_packed.high()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high()
|
||||
: 0;
|
||||
}
|
||||
int increment(int dim) const {
|
||||
return dim == 0
|
||||
? m_packed.increment()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment() : 0;
|
||||
return dim == 0 ? m_packed.increment()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment()
|
||||
: 0;
|
||||
}
|
||||
int elements(int dim) const {
|
||||
return dim == 0
|
||||
? m_packed.elements()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements() : 0;
|
||||
return dim == 0 ? m_packed.elements()
|
||||
: VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements()
|
||||
: 0;
|
||||
}
|
||||
// Total size in bytes (note DPI limited to 4GB)
|
||||
size_t totalSize() const;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#define VERILATOR_VERILATED_SYMS_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_heavy.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_sym_props.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -48,7 +48,8 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
|
||||
// VlWorkerThread
|
||||
|
||||
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling)
|
||||
: m_poolp{poolp}
|
||||
: m_ready_size{0}
|
||||
, m_poolp{poolp}
|
||||
, m_profiling{profiling} // Must init this last -- after setting up fields that it might read:
|
||||
, m_exiting{false}
|
||||
, m_cthread{startWorker, this}
|
||||
@@ -74,7 +75,7 @@ void VlWorkerThread::workerLoop() {
|
||||
if (VL_UNLIKELY(m_exiting.load(std::memory_order_acquire))) break;
|
||||
|
||||
if (VL_LIKELY(work.m_fnp)) {
|
||||
work.m_fnp(work.m_evenCycle, work.m_sym);
|
||||
work.m_fnp(work.m_selfp, work.m_evenCycle);
|
||||
work.m_fnp = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -93,7 +94,7 @@ void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
|
||||
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling)
|
||||
: m_profiling{profiling} {
|
||||
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
|
||||
unsigned cpus = std::thread::hardware_concurrency();
|
||||
const unsigned cpus = std::thread::hardware_concurrency();
|
||||
if (cpus < nThreads + 1) {
|
||||
static int warnedOnce = 0;
|
||||
if (!warnedOnce++) {
|
||||
@@ -104,7 +105,7 @@ VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profil
|
||||
}
|
||||
// Create'em
|
||||
for (int i = 0; i < nThreads; ++i) {
|
||||
m_workers.push_back(new VlWorkerThread(this, contextp, profiling));
|
||||
m_workers.push_back(new VlWorkerThread{this, contextp, profiling});
|
||||
}
|
||||
// Set up a profile buffer for the current thread too -- on the
|
||||
// assumption that it's the same thread that calls eval and may be
|
||||
@@ -131,13 +132,13 @@ void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// try not to malloc while collecting profiling.
|
||||
t_profilep->reserve(4096);
|
||||
{
|
||||
const VerilatedLockGuard lk(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_allProfiles.insert(t_profilep);
|
||||
}
|
||||
}
|
||||
|
||||
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lk(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
for (const auto& profilep : m_allProfiles) {
|
||||
// Every thread's profile trace gets a copy of rec.
|
||||
profilep->emplace_back(rec);
|
||||
@@ -146,10 +147,10 @@ void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES
|
||||
|
||||
void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lk(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
|
||||
|
||||
FILE* fp = std::fopen(filenamep, "w");
|
||||
FILE* const fp = std::fopen(filenamep, "w");
|
||||
if (VL_UNLIKELY(!fp)) {
|
||||
VL_FATAL_MT(filenamep, 0, "", "+prof+threads+file file not writable");
|
||||
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
|
||||
|
||||
+16
-16
@@ -48,12 +48,12 @@
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
// VlMTaskVertex and VlThreadpool will work with multiple symbol table types.
|
||||
// VlMTaskVertex and VlThreadpool will work with multiple model class types.
|
||||
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
|
||||
// as a void* here.
|
||||
using VlThrSymTab = void*;
|
||||
using VlSelfP = void*;
|
||||
|
||||
using VlExecFnp = void (*)(bool, VlThrSymTab);
|
||||
using VlExecFnp = void (*)(VlSelfP, bool);
|
||||
|
||||
// Track dependencies for a single MTask.
|
||||
class VlMTaskVertex final {
|
||||
@@ -99,19 +99,19 @@ public:
|
||||
// false while it's still waiting on more dependencies.
|
||||
inline bool signalUpstreamDone(bool evenCycle) {
|
||||
if (evenCycle) {
|
||||
vluint32_t upstreamDepsDone
|
||||
const vluint32_t upstreamDepsDone
|
||||
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
|
||||
assert(upstreamDepsDone <= m_upstreamDepCount);
|
||||
return (upstreamDepsDone == m_upstreamDepCount);
|
||||
} else {
|
||||
vluint32_t upstreamDepsDone_prev
|
||||
const vluint32_t upstreamDepsDone_prev
|
||||
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
|
||||
assert(upstreamDepsDone_prev > 0);
|
||||
return (upstreamDepsDone_prev == 1);
|
||||
}
|
||||
}
|
||||
inline bool areUpstreamDepsDone(bool evenCycle) const {
|
||||
vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
|
||||
const vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
|
||||
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
|
||||
}
|
||||
inline void waitUntilUpstreamDone(bool evenCycle) const {
|
||||
@@ -177,15 +177,15 @@ private:
|
||||
// TYPES
|
||||
struct ExecRec {
|
||||
VlExecFnp m_fnp; // Function to execute
|
||||
VlThrSymTab m_sym; // Symbol table to execute
|
||||
VlSelfP m_selfp; // Symbol table to execute
|
||||
bool m_evenCycle; // Even/odd for flag alternation
|
||||
ExecRec()
|
||||
: m_fnp{nullptr}
|
||||
, m_sym{nullptr}
|
||||
, m_selfp{nullptr}
|
||||
, m_evenCycle{false} {}
|
||||
ExecRec(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym)
|
||||
ExecRec(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
|
||||
: m_fnp{fnp}
|
||||
, m_sym{sym}
|
||||
, m_selfp{selfp}
|
||||
, m_evenCycle{evenCycle} {}
|
||||
};
|
||||
|
||||
@@ -225,10 +225,10 @@ public:
|
||||
}
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
while (m_ready.empty()) {
|
||||
m_waiting = true;
|
||||
m_cv.wait(lk);
|
||||
m_cv.wait(lock);
|
||||
}
|
||||
m_waiting = false;
|
||||
// As noted above this is inefficient if our ready list is ever
|
||||
@@ -237,13 +237,13 @@ public:
|
||||
m_ready.erase(m_ready.begin());
|
||||
m_ready_size.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
inline void wakeUp() { addTask(nullptr, false, nullptr); }
|
||||
inline void addTask(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym)
|
||||
inline void wakeUp() { addTask(nullptr, nullptr, false); }
|
||||
inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
bool notify;
|
||||
{
|
||||
const VerilatedLockGuard lk(m_mutex);
|
||||
m_ready.emplace_back(fnp, evenCycle, sym);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ready.emplace_back(fnp, selfp, evenCycle);
|
||||
m_ready_size.fetch_add(1, std::memory_order_relaxed);
|
||||
notify = m_waiting;
|
||||
}
|
||||
|
||||
@@ -52,21 +52,21 @@ private:
|
||||
public:
|
||||
// Put an element at the back of the queue
|
||||
void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_queue.push_back(value);
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
// Put an element at the front of the queue
|
||||
void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_queue.push_front(value);
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
// Get an element from the front of the queue. Blocks if none available
|
||||
T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
|
||||
assert(!m_queue.empty());
|
||||
T value = m_queue.front();
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
|
||||
// Non blocking get
|
||||
bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lockGuard(m_mutex);
|
||||
const VerilatedLockGuard lockGuard{m_mutex};
|
||||
if (m_queue.empty()) return false;
|
||||
result = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
|
||||
@@ -246,7 +246,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::closeBase() {
|
||||
shutdownWorker();
|
||||
while (m_numTraceBuffers) {
|
||||
delete[] m_buffersFromWorker.get();
|
||||
m_numTraceBuffers--;
|
||||
--m_numTraceBuffers;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -351,7 +351,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
|
||||
m_traceBufferSize = nextCode() + numSignals() * 2 + 4;
|
||||
|
||||
// Start the worker thread
|
||||
m_workerThread.reset(new std::thread(&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this));
|
||||
m_workerThread.reset(new std::thread{&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this});
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -398,7 +398,7 @@ void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m
|
||||
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
|
||||
// This does get the mutex, but if multiple threads are trying to dump
|
||||
// chances are the data being dumped will have other problems
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
|
||||
VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64
|
||||
"u, dump call ignored\n",
|
||||
@@ -479,9 +479,9 @@ template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
|
||||
CallbackRecord& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
|
||||
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
|
||||
std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__
|
||||
+ " called with already open file");
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
} // LCOV_EXCL_STOP
|
||||
@@ -489,19 +489,19 @@ void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_initCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_fullCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_chgCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr(cb, userp);
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_cleanupCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::module(const std::string& name) VL_MT_UNSAFE {
|
||||
|
||||
@@ -0,0 +1,897 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2003-2021 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 common data type containers
|
||||
///
|
||||
/// verilated.h should be included instead of this file.
|
||||
///
|
||||
/// Those macro/function/variable starting or ending in _ are internal,
|
||||
/// however many of the other function/macros here are also internal.
|
||||
///
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_VERILATED_TYPES_H_
|
||||
#define VERILATOR_VERILATED_TYPES_H_
|
||||
|
||||
#ifndef VERILATOR_VERILATED_H_INTERNAL_
|
||||
#error "verilated_types.h should only be included by verilated.h"
|
||||
#endif
|
||||
|
||||
//===================================================================
|
||||
// String formatters (required by below containers)
|
||||
|
||||
extern std::string VL_TO_STRING(CData lhs);
|
||||
extern std::string VL_TO_STRING(SData lhs);
|
||||
extern std::string VL_TO_STRING(IData lhs);
|
||||
extern std::string VL_TO_STRING(QData lhs);
|
||||
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
|
||||
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
|
||||
|
||||
//=========================================================================
|
||||
// Declare net data types
|
||||
|
||||
#define VL_SIG8(name, msb, lsb) CData name ///< Declare signal, 1-8 bits
|
||||
#define VL_SIG16(name, msb, lsb) SData name ///< Declare signal, 9-16 bits
|
||||
#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits
|
||||
#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits
|
||||
#define VL_SIGW(name, msb, lsb, words) WData name[words] ///< Declare signal, 65+ bits
|
||||
#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits
|
||||
#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits
|
||||
#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits
|
||||
#define VL_IN(name, msb, lsb) IData name ///< Declare input signal, 17-32 bits
|
||||
#define VL_INW(name, msb, lsb, words) WData name[words] ///< Declare input signal, 65+ bits
|
||||
#define VL_INOUT8(name, msb, lsb) CData name ///< Declare bidir signal, 1-8 bits
|
||||
#define VL_INOUT16(name, msb, lsb) SData name ///< Declare bidir signal, 9-16 bits
|
||||
#define VL_INOUT64(name, msb, lsb) QData name ///< Declare bidir signal, 33-64 bits
|
||||
#define VL_INOUT(name, msb, lsb) IData name ///< Declare bidir signal, 17-32 bits
|
||||
#define VL_INOUTW(name, msb, lsb, words) WData name[words] ///< 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_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
|
||||
#define VL_OUTW(name, msb, lsb, words) WData name[words] ///< Declare output signal, 65+ bits
|
||||
|
||||
//===================================================================
|
||||
// Shuffle RNG
|
||||
|
||||
extern vluint64_t vl_rand64() VL_MT_SAFE;
|
||||
|
||||
class VlURNG final {
|
||||
public:
|
||||
using result_type = size_t;
|
||||
static constexpr size_t min() { return 0; }
|
||||
static constexpr size_t max() { return 1ULL << 31; }
|
||||
size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Readmem/Writemem operation classes
|
||||
|
||||
class VlReadMem final {
|
||||
bool m_hex; // Hex format
|
||||
int m_bits; // Bit width of values
|
||||
const std::string& m_filename; // Filename
|
||||
QData m_end; // End address (as specified by user)
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to read
|
||||
int m_linenum; // Line number last read from file
|
||||
public:
|
||||
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlReadMem();
|
||||
bool isOpen() const { return m_fp != nullptr; }
|
||||
int linenum() const { return m_linenum; }
|
||||
bool get(QData& addrr, std::string& valuer);
|
||||
void setData(void* valuep, const std::string& rhs);
|
||||
};
|
||||
|
||||
class VlWriteMem final {
|
||||
bool m_hex; // Hex format
|
||||
int m_bits; // Bit width of values
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to write
|
||||
public:
|
||||
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlWriteMem();
|
||||
bool isOpen() const { return m_fp != nullptr; }
|
||||
void print(QData addr, bool addrstamp, const void* valuep);
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
/// Verilog wide packed bit container.
|
||||
/// Similar to std::array<WData, N>, but lighter weight, only methods needed
|
||||
/// by Verilator, to help compile time.
|
||||
///
|
||||
/// A 'struct' as we want this to be an aggregate type that allows
|
||||
/// static aggregate initialization. Consider data members private.
|
||||
///
|
||||
/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
|
||||
/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
|
||||
/// zero in memory, but during intermediate operations in the Verilated
|
||||
/// internals is unpredictable.
|
||||
|
||||
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE;
|
||||
|
||||
template <std::size_t T_Words> struct VlWide final {
|
||||
// MEMBERS
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
EData m_storage[T_Words]; // Contents of the packed array
|
||||
|
||||
// CONSTRUCTORS
|
||||
// Default constructors and destructor are used. Note however that C++20 requires that
|
||||
// aggregate types do not have a user declared constructor, not even an explicitly defaulted
|
||||
// one.
|
||||
|
||||
// OPERATOR METHODS
|
||||
// Default copy assignment operators are used.
|
||||
operator WDataOutP() { return &m_storage[0]; } // This also allows []
|
||||
operator WDataInP() const { return &m_storage[0]; } // This also allows []
|
||||
|
||||
// METHODS
|
||||
const EData& at(size_t index) const { return m_storage[index]; }
|
||||
EData& at(size_t index) { return m_storage[index]; }
|
||||
WData* data() { return &m_storage[0]; }
|
||||
const WData* data() const { return &m_storage[0]; }
|
||||
bool operator<(const VlWide<T_Words>& rhs) const {
|
||||
return _vl_cmp_w(T_Words, data(), rhs.data()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a C array to std::array reference by pointer magic, without copy.
|
||||
// Data type (second argument) is so the function template can automatically generate.
|
||||
template <std::size_t T_Words>
|
||||
VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
|
||||
return *((VlWide<T_Words>*)inp);
|
||||
}
|
||||
|
||||
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
|
||||
return VL_TO_STRING_W(T_Words, obj.data());
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog queue and dynamic array container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
//
|
||||
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
|
||||
// For dynamic arrays it is always zero
|
||||
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
|
||||
private:
|
||||
// TYPES
|
||||
using Deque = std::deque<T_Value>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename Deque::const_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
Deque m_deque; // State of the assoc array
|
||||
T_Value m_defaultValue; // Default value
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// m_defaultValue isn't defaulted. Caller's constructor must do it.
|
||||
VlQueue() = default;
|
||||
~VlQueue() = default;
|
||||
VlQueue(const VlQueue&) = default;
|
||||
VlQueue(VlQueue&&) = default;
|
||||
VlQueue& operator=(const VlQueue&) = default;
|
||||
VlQueue& operator=(VlQueue&&) = default;
|
||||
|
||||
// Standard copy constructor works. Verilog: assoca = assocb
|
||||
// Also must allow conversion from a different T_MaxSize queue
|
||||
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
|
||||
m_deque = rhs.privateDeque();
|
||||
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static VlQueue cons(const T_Value& lhs) {
|
||||
VlQueue out;
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
|
||||
VlQueue out;
|
||||
out.push_back(rhs);
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
|
||||
VlQueue out = lhs;
|
||||
out.push_front(rhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
|
||||
VlQueue out = rhs;
|
||||
out.push_back(lhs);
|
||||
return out;
|
||||
}
|
||||
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
|
||||
VlQueue out = rhs;
|
||||
for (const auto& i : lhs.m_deque) out.push_back(i);
|
||||
return out;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
T_Value& atDefault() { return m_defaultValue; }
|
||||
const T_Value& atDefault() const { return m_defaultValue; }
|
||||
const Deque& privateDeque() const { return m_deque; }
|
||||
|
||||
// Size. Verilog: function int size(), or int num()
|
||||
int size() const { return m_deque.size(); }
|
||||
// Clear array. Verilog: function void delete([input index])
|
||||
void clear() { m_deque.clear(); }
|
||||
void erase(vlsint32_t index) {
|
||||
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
|
||||
m_deque.erase(m_deque.begin() + index);
|
||||
}
|
||||
|
||||
// Dynamic array new[] becomes a renew()
|
||||
void renew(size_t size) {
|
||||
clear();
|
||||
m_deque.resize(size, atDefault());
|
||||
}
|
||||
// Dynamic array new[]() becomes a renew_copy()
|
||||
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
|
||||
if (size == 0) {
|
||||
clear();
|
||||
} else {
|
||||
*this = rhs;
|
||||
m_deque.resize(size, atDefault());
|
||||
}
|
||||
}
|
||||
|
||||
// function void q.push_front(value)
|
||||
void push_front(const T_Value& value) {
|
||||
m_deque.push_front(value);
|
||||
if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back();
|
||||
}
|
||||
// function void q.push_back(value)
|
||||
void push_back(const T_Value& value) {
|
||||
if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value);
|
||||
}
|
||||
// function value_t q.pop_front();
|
||||
T_Value pop_front() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
T_Value v = m_deque.front();
|
||||
m_deque.pop_front();
|
||||
return v;
|
||||
}
|
||||
// function value_t q.pop_back();
|
||||
T_Value pop_back() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
T_Value v = m_deque.back();
|
||||
m_deque.pop_back();
|
||||
return v;
|
||||
}
|
||||
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(vlsint32_t index) {
|
||||
static T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
s_throwAway = atDefault();
|
||||
return s_throwAway;
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(vlsint32_t index) const {
|
||||
static T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
return atDefault();
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
}
|
||||
// function void q.insert(index, value);
|
||||
void insert(vlsint32_t index, const T_Value& value) {
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
|
||||
m_deque.insert(m_deque.begin() + index, value);
|
||||
}
|
||||
|
||||
// Return slice q[lsb:msb]
|
||||
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
|
||||
VlQueue out;
|
||||
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
|
||||
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
|
||||
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
|
||||
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
|
||||
return out;
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_deque.begin(); }
|
||||
const_iterator end() const { return m_deque.end(); }
|
||||
|
||||
// Methods
|
||||
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
|
||||
template <typename Func> void sort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaninless with sort, as it changes
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
}
|
||||
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
|
||||
template <typename Func> void rsort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaninless with sort, as it changes
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
}
|
||||
void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
|
||||
void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG{}); }
|
||||
VlQueue unique() const {
|
||||
VlQueue out;
|
||||
std::unordered_set<T_Value> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
auto it = saw.find(i);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i);
|
||||
out.push_back(i);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<IData> unique_index() const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
std::unordered_set<T_Value> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
auto it = saw.find(i);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i);
|
||||
out.push_back(index);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find(Func with_func) const {
|
||||
VlQueue out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) out.push_back(i);
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) out.push_back(index);
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find_first(Func with_func) const {
|
||||
// Can't use std::find_if as need index number
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) return VlQueue::cons(i);
|
||||
++index;
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) return VlQueue<IData>::cons(index);
|
||||
++index;
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
}
|
||||
template <typename Func> VlQueue find_last(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
|
||||
if (with_func(index, *it)) return VlQueue::cons(*it);
|
||||
--index;
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
|
||||
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
|
||||
--index;
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue min() const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::min_element(m_deque.begin(), m_deque.end());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
VlQueue max() const {
|
||||
if (m_deque.empty()) return VlQueue{};
|
||||
const auto it = std::max_element(m_deque.begin(), m_deque.end());
|
||||
return VlQueue::cons(*it);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out += i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out += with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out *= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
T_Value out{*it};
|
||||
++it;
|
||||
for (; it != m_deque.end(); ++it) out &= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
IData index = 0;
|
||||
T_Value out{with_func(index, *it)};
|
||||
++it;
|
||||
++index;
|
||||
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out |= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out |= with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
T_Value r_xor() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_deque) out ^= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out ^= with_func(index++, i);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
if (m_deque.empty()) return "'{}"; // No trailing space
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (const auto& i : m_deque) {
|
||||
out += comma + VL_TO_STRING(i);
|
||||
comma = ", ";
|
||||
}
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog associative array container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
//
|
||||
template <class T_Key, class T_Value> class VlAssocArray final {
|
||||
private:
|
||||
// TYPES
|
||||
using Map = std::map<T_Key, T_Value>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename Map::const_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
Map m_map; // State of the assoc array
|
||||
T_Value m_defaultValue; // Default value
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// m_defaultValue isn't defaulted. Caller's constructor must do it.
|
||||
VlAssocArray() = default;
|
||||
~VlAssocArray() = default;
|
||||
VlAssocArray(const VlAssocArray&) = default;
|
||||
VlAssocArray(VlAssocArray&&) = default;
|
||||
VlAssocArray& operator=(const VlAssocArray&) = default;
|
||||
VlAssocArray& operator=(VlAssocArray&&) = default;
|
||||
|
||||
// METHODS
|
||||
T_Value& atDefault() { return m_defaultValue; }
|
||||
const T_Value& atDefault() const { return m_defaultValue; }
|
||||
|
||||
// Size of array. Verilog: function int size(), or int num()
|
||||
int size() const { return m_map.size(); }
|
||||
// Clear array. Verilog: function void delete([input index])
|
||||
void clear() { m_map.clear(); }
|
||||
void erase(const T_Key& index) { m_map.erase(index); }
|
||||
// Return 0/1 if element exists. Verilog: function int exists(input index)
|
||||
int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
|
||||
// Return first element. Verilog: function int first(ref index);
|
||||
int first(T_Key& indexr) const {
|
||||
const auto it = m_map.cbegin();
|
||||
if (it == m_map.end()) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return last element. Verilog: function int last(ref index)
|
||||
int last(T_Key& indexr) const {
|
||||
const auto it = m_map.crbegin();
|
||||
if (it == m_map.rend()) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return next element. Verilog: function int next(ref index)
|
||||
int next(T_Key& indexr) const {
|
||||
auto it = m_map.find(indexr);
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
++it;
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Return prev element. Verilog: function int prev(ref index)
|
||||
int prev(T_Key& indexr) const {
|
||||
auto it = m_map.find(indexr);
|
||||
if (VL_UNLIKELY(it == m_map.end())) return 0;
|
||||
if (VL_UNLIKELY(it == m_map.begin())) return 0;
|
||||
--it;
|
||||
indexr = it->first;
|
||||
return 1;
|
||||
}
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(const T_Key& index) {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
|
||||
return pit.first->second;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(const T_Key& index) const {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
return m_defaultValue;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
// Setting as a chained operation
|
||||
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
||||
at(index) = value;
|
||||
return *this;
|
||||
}
|
||||
VlAssocArray& setDefault(const T_Value& value) {
|
||||
atDefault() = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_map.begin(); }
|
||||
const_iterator end() const { return m_map.end(); }
|
||||
|
||||
// Methods
|
||||
VlQueue<T_Value> unique() const {
|
||||
VlQueue<T_Value> out;
|
||||
std::set<T_Value> saw;
|
||||
for (const auto& i : m_map) {
|
||||
auto it = saw.find(i.second);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i.second);
|
||||
out.push_back(i.second);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<T_Key> unique_index() const {
|
||||
VlQueue<T_Key> out;
|
||||
std::set<T_Key> saw;
|
||||
for (const auto& i : m_map) {
|
||||
auto it = saw.find(i.second);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i.second);
|
||||
out.push_back(i.first);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
for (const auto& i : m_map)
|
||||
if (with_func(i.first, i.second)) out.push_back(i.second);
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
for (const auto& i : m_map)
|
||||
if (with_func(i.first, i.second)) out.push_back(i.first);
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
});
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue<T_Value> min() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::min_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
VlQueue<T_Value> max() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out += i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out += with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_product() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_and() const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{it->second};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
++it;
|
||||
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_or() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out |= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out |= with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
T_Value r_xor() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out ^= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
if (m_map.empty()) return "'{}"; // No trailing space
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (const auto& i : m_map) {
|
||||
out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
|
||||
comma = ", ";
|
||||
}
|
||||
// Default not printed - maybe random init data
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
|
||||
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
|
||||
VlReadMem rmem{hex, bits, filename, start, end};
|
||||
if (VL_UNLIKELY(!rmem.isOpen())) return;
|
||||
while (true) {
|
||||
QData addr;
|
||||
std::string data;
|
||||
if (rmem.get(addr /*ref*/, data /*ref*/)) {
|
||||
rmem.setData(&(obj.at(addr)), data);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T_Key, class T_Value>
|
||||
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
|
||||
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
|
||||
VlWriteMem wmem{hex, bits, filename, start, end};
|
||||
if (VL_UNLIKELY(!wmem.isOpen())) return;
|
||||
for (const auto& i : obj) {
|
||||
const QData addr = i.first;
|
||||
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
/// Verilog unpacked array container
|
||||
/// For when a standard C++[] array is not sufficient, e.g. an
|
||||
/// array under a queue, or methods operating on the array.
|
||||
///
|
||||
/// A 'struct' as we want this to be an aggregate type that allows
|
||||
/// static aggregate initialization. Consider data members private.
|
||||
///
|
||||
/// This class may get exposed to a Verilated Model's top I/O, if the top
|
||||
/// IO has an unpacked array.
|
||||
|
||||
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
|
||||
// MEMBERS
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
T_Value m_storage[T_Depth]; // Contents of the unpacked array
|
||||
|
||||
// CONSTRUCTORS
|
||||
// Default constructors and destructor are used. Note however that C++20 requires that
|
||||
// aggregate types do not have a user declared constructor, not even an explicitly defaulted
|
||||
// one.
|
||||
|
||||
// OPERATOR METHODS
|
||||
// Default copy assignment operators are used.
|
||||
|
||||
// METHODS
|
||||
// Raw access
|
||||
WData* data() { return &m_storage[0]; }
|
||||
const WData* data() const { return &m_storage[0]; }
|
||||
|
||||
T_Value& operator[](size_t index) { return m_storage[index]; };
|
||||
const T_Value& operator[](size_t index) const { return m_storage[index]; };
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (int i = 0; i < T_Depth; ++i) {
|
||||
out += comma + VL_TO_STRING(m_storage[i]);
|
||||
comma = ", ";
|
||||
}
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value, std::size_t T_Depth>
|
||||
std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog class reference container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
|
||||
#define VlClassRef std::shared_ptr
|
||||
|
||||
template <class T> // T typically of type VlClassRef<x>
|
||||
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
|
||||
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
|
||||
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
|
||||
if (VL_LIKELY(casted)) {
|
||||
outr = casted;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
#endif // Guard
|
||||
+26
-25
@@ -102,7 +102,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
|
||||
}
|
||||
|
||||
void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (isOpen()) return;
|
||||
|
||||
// Set member variables
|
||||
@@ -120,7 +120,7 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Open next filename in concat sequence, mangle filename if
|
||||
// incFilename is true.
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
openNextImp(incFilename);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ void VerilatedVcd::openNextImp(bool incFilename) {
|
||||
if (incFilename) {
|
||||
// Find _0000.{ext} in filename
|
||||
std::string name = m_filename;
|
||||
size_t pos = name.rfind('.');
|
||||
const size_t pos = name.rfind('.');
|
||||
if (pos > 8 && 0 == std::strncmp("_cat", name.c_str() + pos - 8, 4)
|
||||
&& std::isdigit(name.c_str()[pos - 4]) && std::isdigit(name.c_str()[pos - 3])
|
||||
&& std::isdigit(name.c_str()[pos - 2]) && std::isdigit(name.c_str()[pos - 1])) {
|
||||
@@ -195,11 +195,11 @@ void VerilatedVcd::makeNameMap() {
|
||||
if (!hiername.empty() && hiername[0] == '\t') nullScope = true;
|
||||
}
|
||||
if (nullScope) {
|
||||
NameMap* newmapp = new NameMap;
|
||||
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 = std::string("top");
|
||||
std::string newname{"top"};
|
||||
if (hiername[0] != '\t') newname += ' ';
|
||||
newname += hiername;
|
||||
newmapp->emplace(newname, decl);
|
||||
@@ -243,7 +243,7 @@ void VerilatedVcd::closeErr() {
|
||||
|
||||
void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// This function is on the flush() call path
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (!isOpen()) return;
|
||||
if (m_evcd) {
|
||||
printStr("$vcdclose ");
|
||||
@@ -257,7 +257,7 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
}
|
||||
|
||||
void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedVcd>::flushBase();
|
||||
bufferFlush();
|
||||
}
|
||||
@@ -271,8 +271,9 @@ void VerilatedVcd::printStr(const char* str) {
|
||||
}
|
||||
|
||||
void VerilatedVcd::printQuad(vluint64_t n) {
|
||||
char buf[100];
|
||||
VL_SNPRINTF(buf, 100, "%" VL_PRI64 "u", n);
|
||||
constexpr size_t LEN_STR_QUAD = 40;
|
||||
char buf[LEN_STR_QUAD];
|
||||
VL_SNPRINTF(buf, LEN_STR_QUAD, "%" VL_PRI64 "u", n);
|
||||
printStr(buf);
|
||||
}
|
||||
|
||||
@@ -299,10 +300,10 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
char* wp = m_wrBufp;
|
||||
while (true) {
|
||||
ssize_t remaining = (m_writep - wp);
|
||||
const ssize_t remaining = (m_writep - wp);
|
||||
if (remaining == 0) break;
|
||||
errno = 0;
|
||||
ssize_t got = m_filep->write(wp, remaining);
|
||||
const ssize_t got = m_filep->write(wp, remaining);
|
||||
if (got > 0) {
|
||||
wp += got;
|
||||
m_wroteBytes += got;
|
||||
@@ -311,7 +312,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
// LCOV_EXCL_START
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg
|
||||
= std::string("VerilatedVcd::bufferFlush: ") + std::strerror(errno);
|
||||
= std::string{"VerilatedVcd::bufferFlush: "} + std::strerror(errno);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
closeErr();
|
||||
break;
|
||||
@@ -331,7 +332,7 @@ char* VerilatedVcd::writeCode(char* writep, vluint32_t code) {
|
||||
*writep++ = static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
while (code) {
|
||||
code--;
|
||||
--code;
|
||||
*writep++ = static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
}
|
||||
@@ -352,12 +353,12 @@ void VerilatedVcd::dumpHeader() {
|
||||
printStr("$version Generated by VerilatedVcd $end\n");
|
||||
printStr("$date ");
|
||||
{
|
||||
time_t tick = time(nullptr);
|
||||
const time_t tick = time(nullptr);
|
||||
tm ticktm;
|
||||
VL_LOCALTIME_R(&tick, &ticktm);
|
||||
constexpr int bufsize = 50;
|
||||
char buf[bufsize];
|
||||
std::strftime(buf, bufsize, "%c", &ticktm);
|
||||
constexpr size_t LEN_BUF = 50;
|
||||
char buf[LEN_BUF];
|
||||
std::strftime(buf, LEN_BUF, "%c", &ticktm);
|
||||
printStr(buf);
|
||||
}
|
||||
printStr(" $end\n");
|
||||
@@ -393,8 +394,8 @@ void VerilatedVcd::dumpHeader() {
|
||||
// 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--;
|
||||
--np;
|
||||
--lp;
|
||||
}
|
||||
// printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np);
|
||||
|
||||
@@ -430,9 +431,9 @@ void VerilatedVcd::dumpHeader() {
|
||||
|
||||
for (; *np && *np != ' ' && *np != '\t'; np++) {
|
||||
if (*np == '[') {
|
||||
printStr("(");
|
||||
printStr("[");
|
||||
} else if (*np == ']') {
|
||||
printStr(")");
|
||||
printStr("]");
|
||||
} else if (!(*np & '\x80')) {
|
||||
*m_writep++ = *np;
|
||||
}
|
||||
@@ -531,7 +532,7 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
||||
decl += " ";
|
||||
decl += basename;
|
||||
if (array) {
|
||||
VL_SNPRINTF(buf, bufsize, "(%d)", arraynum);
|
||||
VL_SNPRINTF(buf, bufsize, "[%d]", arraynum);
|
||||
decl += buf;
|
||||
hiername += buf;
|
||||
}
|
||||
@@ -896,9 +897,9 @@ void vcdTestMain(const char* filenamep) {
|
||||
VerilatedVcdC* vcdp = new VerilatedVcdC;
|
||||
vcdp->evcd(true);
|
||||
vcdp->set_time_unit("1ms");
|
||||
vcdp->set_time_unit(std::string("1ms"));
|
||||
vcdp->set_time_unit(std::string{"1ms"});
|
||||
vcdp->set_time_resolution("1ns");
|
||||
vcdp->set_time_resolution(std::string("1ns"));
|
||||
vcdp->set_time_resolution(std::string{"1ns"});
|
||||
vcdp->spTrace()->addInitCb(&vcdInit, 0);
|
||||
vcdp->spTrace()->addFullCb(&vcdFull, 0);
|
||||
vcdp->spTrace()->addChgCb(&vcdChange, 0);
|
||||
@@ -927,7 +928,7 @@ void vcdTestMain(const char* filenamep) {
|
||||
vcdp->dump(++timestamp);
|
||||
vcdp->dump(++timestamp);
|
||||
# ifdef VERILATED_VCD_TEST_64BIT
|
||||
vluint64_t bytesPerDump = 15ULL;
|
||||
const vluint64_t bytesPerDump = 15ULL;
|
||||
for (vluint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) {
|
||||
v1 = i;
|
||||
vcdp->dump(++timestamp);
|
||||
|
||||
@@ -233,11 +233,11 @@ public:
|
||||
// Inside dumping routines, dump one signal if it has changed.
|
||||
// We do want to inline these to avoid calls when the value did not change.
|
||||
inline void chgBit(vluint32_t code, const vluint32_t newval) {
|
||||
vluint32_t diff = oldp(code)[0] ^ newval;
|
||||
const vluint32_t diff = oldp(code)[0] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(code, newval);
|
||||
}
|
||||
inline void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
vluint32_t diff = oldp(code)[0] ^ newval;
|
||||
const vluint32_t diff = oldp(code)[0] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
|
||||
fullBus(code, newval, bits);
|
||||
@@ -245,7 +245,7 @@ public:
|
||||
}
|
||||
}
|
||||
inline void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval;
|
||||
const vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
|
||||
fullQuad(code, newval, bits);
|
||||
@@ -270,7 +270,7 @@ public:
|
||||
}
|
||||
}
|
||||
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below
|
||||
// is only for back compatibility
|
||||
@@ -281,7 +281,7 @@ public:
|
||||
}
|
||||
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
|
||||
int bits) {
|
||||
vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
|
||||
fullTriBus(code, newval, newtri, bits);
|
||||
@@ -290,8 +290,8 @@ public:
|
||||
}
|
||||
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri,
|
||||
int bits) {
|
||||
vluint64_t diff = (((*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval)
|
||||
| ((*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) ^ newtri));
|
||||
const vluint64_t diff = (((*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval)
|
||||
| ((*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
|
||||
fullTriQuad(code, newval, newtri, bits);
|
||||
|
||||
@@ -44,9 +44,9 @@ public:
|
||||
// We want to avoid a depreciated warning, but still be back compatible.
|
||||
// Turning off the message just for this still results in an
|
||||
// annoying "to turn off" message.
|
||||
sc_time t1sec(1, SC_SEC);
|
||||
const sc_time t1sec{1, SC_SEC};
|
||||
if (t1sec.to_default_time_units() != 0) {
|
||||
sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC);
|
||||
const sc_time tunits{1.0 / t1sec.to_default_time_units(), SC_SEC};
|
||||
spTrace()->set_time_unit(tunits.to_string());
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
|
||||
+145
-139
@@ -80,7 +80,7 @@ public:
|
||||
static const size_t chunk = 96;
|
||||
if (VL_UNCOVERABLE(size > chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
|
||||
if (VL_LIKELY(t_freeHead)) {
|
||||
vluint8_t* newp = t_freeHead;
|
||||
vluint8_t* const newp = t_freeHead;
|
||||
t_freeHead = *(reinterpret_cast<vluint8_t**>(newp));
|
||||
*(reinterpret_cast<vluint32_t*>(newp)) = activeMagic();
|
||||
return newp + 8;
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
return newp + 8;
|
||||
}
|
||||
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
|
||||
vluint8_t* oldp = (static_cast<vluint8_t*>(obj)) - 8;
|
||||
vluint8_t* const oldp = (static_cast<vluint8_t*>(obj)) - 8;
|
||||
if (VL_UNLIKELY(*(reinterpret_cast<vluint32_t*>(oldp)) != activeMagic())) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"vpi_release_handle() called on same object twice, or on non-Verilator "
|
||||
@@ -128,7 +128,7 @@ class VerilatedVpioTimedCb final : public VerilatedVpio {
|
||||
|
||||
public:
|
||||
VerilatedVpioTimedCb(vluint64_t id, QData time)
|
||||
: m_id(id)
|
||||
: m_id{id}
|
||||
, m_time{time} {}
|
||||
virtual ~VerilatedVpioTimedCb() override = default;
|
||||
static VerilatedVpioTimedCb* castp(vpiHandle h) {
|
||||
@@ -147,7 +147,7 @@ class VerilatedVpioReasonCb final : public VerilatedVpio {
|
||||
public:
|
||||
// cppcheck-suppress uninitVar // m_value
|
||||
VerilatedVpioReasonCb(vluint64_t id, PLI_INT32 reason)
|
||||
: m_id(id)
|
||||
: m_id{id}
|
||||
, m_reason{reason} {}
|
||||
virtual ~VerilatedVpioReasonCb() override = default;
|
||||
static VerilatedVpioReasonCb* castp(vpiHandle h) {
|
||||
@@ -200,7 +200,7 @@ public:
|
||||
virtual const char* name() const override { return m_varp->name(); }
|
||||
virtual const char* fullname() const override {
|
||||
static VL_THREAD_LOCAL std::string t_out;
|
||||
t_out = std::string(m_scopep->name()) + "." + name();
|
||||
t_out = std::string{m_scopep->name()} + "." + name();
|
||||
return t_out.c_str();
|
||||
}
|
||||
};
|
||||
@@ -208,7 +208,7 @@ public:
|
||||
class VerilatedVpioParam final : public VerilatedVpioVarBase {
|
||||
public:
|
||||
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: VerilatedVpioVarBase(varp, scopep) {}
|
||||
: VerilatedVpioVarBase{varp, scopep} {}
|
||||
virtual ~VerilatedVpioParam() override = default;
|
||||
|
||||
static VerilatedVpioParam* castp(vpiHandle h) {
|
||||
@@ -252,7 +252,7 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
m_done = true;
|
||||
return ((new VerilatedVpioRange(m_range))->castVpiHandle());
|
||||
return ((new VerilatedVpioRange{m_range})->castVpiHandle());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -286,13 +286,13 @@ protected:
|
||||
|
||||
public:
|
||||
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: VerilatedVpioVarBase(varp, scopep) {
|
||||
: VerilatedVpioVarBase{varp, scopep} {
|
||||
m_mask.u32 = VL_MASK_I(varp->packed().elements());
|
||||
m_entSize = varp->entSize();
|
||||
m_varDatap = varp->datap();
|
||||
}
|
||||
explicit VerilatedVpioVar(const VerilatedVpioVar* varp)
|
||||
: VerilatedVpioVarBase(varp) {
|
||||
: VerilatedVpioVarBase{varp} {
|
||||
if (varp) {
|
||||
m_mask.u32 = varp->m_mask.u32;
|
||||
m_entSize = varp->m_entSize;
|
||||
@@ -310,7 +310,7 @@ public:
|
||||
return dynamic_cast<VerilatedVpioVar*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
vluint32_t mask() const { return m_mask.u32; }
|
||||
vluint8_t mask_byte(int idx) { return m_mask.u8[idx & 3]; }
|
||||
vluint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
|
||||
vluint32_t entSize() const { return m_entSize; }
|
||||
vluint32_t index() const { return m_index; }
|
||||
virtual vluint32_t type() const override {
|
||||
@@ -343,9 +343,10 @@ public:
|
||||
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
|
||||
virtual const char* fullname() const override {
|
||||
static VL_THREAD_LOCAL std::string t_out;
|
||||
char num[25];
|
||||
VL_SNPRINTF(num, 25, "%d", m_index);
|
||||
t_out = std::string(scopep()->name()) + "." + name() + "[" + num + "]";
|
||||
constexpr size_t LEN_MAX_INDEX = 25;
|
||||
char num[LEN_MAX_INDEX];
|
||||
VL_SNPRINTF(num, LEN_MAX_INDEX, "%d", m_index);
|
||||
t_out = std::string{scopep()->name()} + "." + name() + "[" + num + "]";
|
||||
return t_out.c_str();
|
||||
}
|
||||
};
|
||||
@@ -365,7 +366,7 @@ public:
|
||||
virtual vluint32_t type() const override { return vpiIterator; }
|
||||
virtual vpiHandle dovpi_scan() override {
|
||||
if (VL_LIKELY(m_scopep->varsp())) {
|
||||
VerilatedVarNameMap* varsp = m_scopep->varsp();
|
||||
const VerilatedVarNameMap* const varsp = m_scopep->varsp();
|
||||
if (VL_UNLIKELY(!m_started)) {
|
||||
m_it = varsp->begin();
|
||||
m_started = true;
|
||||
@@ -379,7 +380,7 @@ public:
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
}
|
||||
return ((new VerilatedVpioVar(&(m_it->second), m_scopep))->castVpiHandle());
|
||||
return ((new VerilatedVpioVar{&(m_it->second), m_scopep})->castVpiHandle());
|
||||
}
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr; // End of list - only one deep
|
||||
@@ -457,7 +458,7 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
const VerilatedScope* modp = *m_it++;
|
||||
return (new VerilatedVpioModule(modp))->castVpiHandle();
|
||||
return (new VerilatedVpioModule{modp})->castVpiHandle();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -475,16 +476,16 @@ class VerilatedVpiCbHolder final {
|
||||
public:
|
||||
// cppcheck-suppress uninitVar // m_value
|
||||
VerilatedVpiCbHolder(vluint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
|
||||
: m_id(id)
|
||||
, m_cbData(*cbDatap)
|
||||
, m_varo(varop) {
|
||||
: m_id{id}
|
||||
, m_cbData{*cbDatap}
|
||||
, m_varo{varop} {
|
||||
m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
|
||||
m_cbData.value = &m_value;
|
||||
if (varop) {
|
||||
m_cbData.obj = m_varo.castVpiHandle();
|
||||
m_varo.createPrevDatap();
|
||||
} else {
|
||||
m_cbData.obj = NULL;
|
||||
m_cbData.obj = nullptr;
|
||||
}
|
||||
}
|
||||
~VerilatedVpiCbHolder() = default;
|
||||
@@ -564,7 +565,7 @@ public:
|
||||
}
|
||||
static void callTimedCbs() VL_MT_UNSAFE_ONE {
|
||||
assertOneCheck();
|
||||
QData time = VL_TIME_Q();
|
||||
const QData time = VL_TIME_Q();
|
||||
for (auto it = s().m_timedCbs.begin(); it != s().m_timedCbs.end();) {
|
||||
if (VL_UNLIKELY(it->first.first <= time)) {
|
||||
VerilatedVpiCbHolder& ho = it->second;
|
||||
@@ -587,7 +588,7 @@ public:
|
||||
if (VL_LIKELY(it != s().m_timedCbs.cend())) return it->first.first;
|
||||
return ~0ULL; // maxquad
|
||||
}
|
||||
static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
|
||||
static bool callCbs(const vluint32_t reason) VL_MT_UNSAFE_ONE {
|
||||
VpioCbList& cbObjList = s().m_cbObjLists[reason];
|
||||
bool called = false;
|
||||
if (cbObjList.empty()) return called;
|
||||
@@ -619,23 +620,25 @@ public:
|
||||
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
|
||||
for (auto it = cbObjList.begin(); true;) {
|
||||
// cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
|
||||
bool was_last = it == last;
|
||||
const bool was_last = it == last;
|
||||
if (VL_UNLIKELY(it->invalid())) { // Deleted earlier, cleanup
|
||||
it = cbObjList.erase(it);
|
||||
if (was_last) break;
|
||||
continue;
|
||||
}
|
||||
VerilatedVpiCbHolder& ho = *it++;
|
||||
if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
|
||||
void* newDatap = varop->varDatap();
|
||||
void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback
|
||||
if (VerilatedVpioVar* const varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
|
||||
void* const newDatap = varop->varDatap();
|
||||
void* const prevDatap
|
||||
= varop->prevDatap(); // Was malloced when we added the callback
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
|
||||
varop->fullname(), *((CData*)newDatap),
|
||||
*((CData*)prevDatap), newDatap, prevDatap););
|
||||
varop->fullname(), *(static_cast<CData*>(newDatap)),
|
||||
*(static_cast<CData*>(prevDatap)), newDatap,
|
||||
prevDatap););
|
||||
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64
|
||||
"d %s v[0]=%d\n",
|
||||
ho.id(), varop->fullname(), *((CData*)newDatap)););
|
||||
VL_DEBUG_IF_PLI(
|
||||
VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64 "d %s v[0]=%d\n", ho.id(),
|
||||
varop->fullname(), *(static_cast<CData*>(newDatap))););
|
||||
update.insert(varop);
|
||||
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
||||
(ho.cb_rtnp())(ho.cb_datap());
|
||||
@@ -706,7 +709,8 @@ public:
|
||||
va_end(args);
|
||||
m_errorInfo.state = vpiPLI;
|
||||
t_filehold = file;
|
||||
setError((PLI_BYTE8*)m_buff, nullptr, const_cast<PLI_BYTE8*>(t_filehold.c_str()), line);
|
||||
setError(static_cast<PLI_BYTE8*>(m_buff), nullptr,
|
||||
const_cast<PLI_BYTE8*>(t_filehold.c_str()), line);
|
||||
}
|
||||
p_vpi_error_info getError() {
|
||||
if (m_flag) return &m_errorInfo;
|
||||
@@ -715,7 +719,7 @@ public:
|
||||
void resetError() { m_flag = false; }
|
||||
static void vpi_unsupported() {
|
||||
// Not supported yet
|
||||
p_vpi_error_info error_info_p = VerilatedVpiImp::error_info()->getError();
|
||||
const p_vpi_error_info error_info_p = VerilatedVpiImp::error_info()->getError();
|
||||
if (error_info_p) {
|
||||
VL_FATAL_MT(error_info_p->file, error_info_p->line, "", error_info_p->message);
|
||||
return;
|
||||
@@ -758,7 +762,7 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
|
||||
|
||||
VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError();
|
||||
if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError;
|
||||
return s().m_errorInfop;
|
||||
}
|
||||
|
||||
@@ -1108,13 +1112,13 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE {
|
||||
#define SELF_CHECK_RESULT_CSTR(got, exp) \
|
||||
if (0 != std::strcmp((got), (exp))) { \
|
||||
std::string msg \
|
||||
= std::string("%Error: ") + "GOT = '" + got + "'" + " EXP = '" + exp + "'"; \
|
||||
= std::string{"%Error: "} + "GOT = '" + (got) + "'" + " EXP = '" + (exp) + "'"; \
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); \
|
||||
}
|
||||
|
||||
#define SELF_CHECK_ENUM_STR(fn, enumn) \
|
||||
do { \
|
||||
const char* strVal = VerilatedVpiError::fn(enumn); \
|
||||
const char* const strVal = VerilatedVpiError::fn(enumn); \
|
||||
SELF_CHECK_RESULT_CSTR(strVal, #enumn); \
|
||||
} while (0)
|
||||
|
||||
@@ -1300,9 +1304,9 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
case cbAfterDelay: {
|
||||
QData time = 0;
|
||||
if (cb_data_p->time) time = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
|
||||
QData abstime = VL_TIME_Q() + time;
|
||||
const QData abstime = VL_TIME_Q() + time;
|
||||
vluint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
VerilatedVpioTimedCb* vop = new VerilatedVpioTimedCb{id, abstime};
|
||||
VerilatedVpioTimedCb* const vop = new VerilatedVpioTimedCb{id, abstime};
|
||||
VerilatedVpiImp::cbTimedAdd(id, cb_data_p, abstime);
|
||||
return vop->castVpiHandle();
|
||||
}
|
||||
@@ -1316,8 +1320,8 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
||||
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
||||
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
|
||||
vluint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
VerilatedVpioReasonCb* vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
|
||||
const vluint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
|
||||
VerilatedVpiImp::cbReasonAdd(id, cb_data_p);
|
||||
return vop->castVpiHandle();
|
||||
}
|
||||
@@ -1332,7 +1336,7 @@ PLI_INT32 vpi_remove_cb(vpiHandle cb_obj) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n", cb_obj););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VL_VPI_ERROR_RESET_();
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(cb_obj);
|
||||
VerilatedVpio* const vop = VerilatedVpio::castp(cb_obj);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
return vop->dovpi_remove_cb();
|
||||
}
|
||||
@@ -1355,10 +1359,10 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n", namep, scope););
|
||||
const VerilatedVar* varp = nullptr;
|
||||
const VerilatedScope* scopep;
|
||||
VerilatedVpioScope* voScopep = VerilatedVpioScope::castp(scope);
|
||||
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
|
||||
std::string scopeAndName = namep;
|
||||
if (voScopep) {
|
||||
scopeAndName = std::string(voScopep->fullname()) + "." + namep;
|
||||
scopeAndName = std::string{voScopep->fullname()} + "." + namep;
|
||||
namep = const_cast<PLI_BYTE8*>(scopeAndName.c_str());
|
||||
}
|
||||
{
|
||||
@@ -1366,17 +1370,18 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
scopep = Verilated::threadContextp()->scopeFind(namep);
|
||||
if (scopep) { // Whole thing found as a scope
|
||||
if (scopep->type() == VerilatedScope::SCOPE_MODULE) {
|
||||
return (new VerilatedVpioModule(scopep))->castVpiHandle();
|
||||
return (new VerilatedVpioModule{scopep})->castVpiHandle();
|
||||
} else {
|
||||
return (new VerilatedVpioScope(scopep))->castVpiHandle();
|
||||
return (new VerilatedVpioScope{scopep})->castVpiHandle();
|
||||
}
|
||||
}
|
||||
const char* baseNamep = scopeAndName.c_str();
|
||||
std::string scopename;
|
||||
const char* dotp = std::strrchr(namep, '.');
|
||||
const char* const dotp = std::strrchr(namep, '.');
|
||||
if (VL_LIKELY(dotp)) {
|
||||
baseNamep = dotp + 1;
|
||||
scopename = std::string(namep, dotp - namep);
|
||||
const size_t len = dotp - namep;
|
||||
scopename = std::string{namep, len};
|
||||
}
|
||||
|
||||
if (scopename.find('.') == std::string::npos) {
|
||||
@@ -1393,9 +1398,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
if (!varp) return nullptr;
|
||||
|
||||
if (varp->isParam()) {
|
||||
return (new VerilatedVpioParam(varp, scopep))->castVpiHandle();
|
||||
return (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
|
||||
} else {
|
||||
return (new VerilatedVpioVar(varp, scopep))->castVpiHandle();
|
||||
return (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1405,24 +1410,24 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VL_VPI_ERROR_RESET_();
|
||||
// Memory words are not indexable
|
||||
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
|
||||
VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object);
|
||||
if (VL_UNLIKELY(vop)) return nullptr;
|
||||
VerilatedVpioVar* varop = VerilatedVpioVar::castp(object);
|
||||
const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object);
|
||||
if (VL_LIKELY(varop)) {
|
||||
if (varop->varp()->dims() < 2) return nullptr;
|
||||
if (VL_LIKELY(varop->varp()->unpacked().left() >= varop->varp()->unpacked().right())) {
|
||||
if (VL_UNLIKELY(indx > varop->varp()->unpacked().left()
|
||||
|| indx < varop->varp()->unpacked().right()))
|
||||
return nullptr;
|
||||
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->unpacked().right()))
|
||||
return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->unpacked().right()})
|
||||
->castVpiHandle();
|
||||
}
|
||||
if (VL_UNLIKELY(indx < varop->varp()->unpacked().left()
|
||||
|| indx > varop->varp()->unpacked().right()))
|
||||
return nullptr;
|
||||
return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->unpacked().left()))
|
||||
return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx,
|
||||
indx - varop->varp()->unpacked().left()})
|
||||
->castVpiHandle();
|
||||
}
|
||||
VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__);
|
||||
@@ -1437,12 +1442,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
VL_VPI_ERROR_RESET_();
|
||||
switch (type) {
|
||||
case vpiLeftRange: {
|
||||
if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) {
|
||||
if (VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
|
||||
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
||||
return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle();
|
||||
} else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) {
|
||||
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
|
||||
} else if (VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
|
||||
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
||||
return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle();
|
||||
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
|
||||
}
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
|
||||
@@ -1450,12 +1455,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
return nullptr;
|
||||
}
|
||||
case vpiRightRange: {
|
||||
if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) {
|
||||
if (VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
|
||||
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
||||
return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle();
|
||||
} else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) {
|
||||
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
|
||||
} else if (VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
|
||||
if (VL_UNLIKELY(!vop->rangep())) return nullptr;
|
||||
return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle();
|
||||
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
|
||||
}
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
|
||||
@@ -1463,19 +1468,20 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
return nullptr;
|
||||
}
|
||||
case vpiIndex: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
return (new VerilatedVpioConst(vop->index()))->castVpiHandle();
|
||||
const vlsint32_t val = vop->index();
|
||||
return (new VerilatedVpioConst{val})->castVpiHandle();
|
||||
}
|
||||
case vpiScope: {
|
||||
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
|
||||
VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
return (new VerilatedVpioScope(vop->scopep()))->castVpiHandle();
|
||||
return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle();
|
||||
}
|
||||
case vpiParent: {
|
||||
VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object);
|
||||
VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
return (new VerilatedVpioVar(vop->varp(), vop->scopep()))->castVpiHandle();
|
||||
return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle();
|
||||
}
|
||||
default:
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
||||
@@ -1496,7 +1502,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
VL_VPI_ERROR_RESET_();
|
||||
switch (type) {
|
||||
case vpiMemoryWord: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
if (vop->varp()->dims() < 2) return nullptr;
|
||||
if (vop->varp()->dims() > 2) {
|
||||
@@ -1505,10 +1511,10 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
VerilatedVpiError::strFromVpiMethod(type), vop->fullname(),
|
||||
vop->varp()->dims());
|
||||
}
|
||||
return (new VerilatedVpioMemoryWordIter(object, vop->varp()))->castVpiHandle();
|
||||
return (new VerilatedVpioMemoryWordIter{object, vop->varp()})->castVpiHandle();
|
||||
}
|
||||
case vpiRange: {
|
||||
VerilatedVpioVar* vop = VerilatedVpioVar::castp(object);
|
||||
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
if (vop->varp()->dims() < 2) return nullptr;
|
||||
// Unsupported is multidim list
|
||||
@@ -1518,20 +1524,20 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
VerilatedVpiError::strFromVpiMethod(type), vop->fullname(),
|
||||
vop->varp()->dims());
|
||||
}
|
||||
return ((new VerilatedVpioRangeIter(vop->rangep()))->castVpiHandle());
|
||||
return ((new VerilatedVpioRangeIter{vop->rangep()})->castVpiHandle());
|
||||
}
|
||||
case vpiReg: {
|
||||
VerilatedVpioScope* vop = VerilatedVpioScope::castp(object);
|
||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
return ((new VerilatedVpioVarIter(vop->scopep()))->castVpiHandle());
|
||||
return ((new VerilatedVpioVarIter{vop->scopep()})->castVpiHandle());
|
||||
}
|
||||
case vpiModule: {
|
||||
VerilatedVpioModule* vop = VerilatedVpioModule::castp(object);
|
||||
const VerilatedHierarchyMap* map = VerilatedImp::hierarchyMap();
|
||||
const VerilatedScope* mod = vop ? vop->scopep() : nullptr;
|
||||
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(mod));
|
||||
const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object);
|
||||
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
|
||||
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
|
||||
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
|
||||
if (it == map->end()) return nullptr;
|
||||
return ((new VerilatedVpioModuleIter(it->second))->castVpiHandle());
|
||||
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
|
||||
}
|
||||
default:
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
|
||||
@@ -1543,7 +1549,7 @@ vpiHandle vpi_scan(vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n", object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VL_VPI_ERROR_RESET_();
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
VerilatedVpio* const vop = VerilatedVpio::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
return vop->dovpi_scan();
|
||||
}
|
||||
@@ -1560,30 +1566,30 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
|
||||
return Verilated::threadContextp()->timeprecision();
|
||||
}
|
||||
case vpiTimeUnit: {
|
||||
VerilatedVpioScope* vop = VerilatedVpioScope::castp(object);
|
||||
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
|
||||
if (!vop)
|
||||
return Verilated::threadContextp()->timeunit(); // Null asks for global, not unlikely
|
||||
return vop->scopep()->timeunit();
|
||||
}
|
||||
case vpiType: {
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
const VerilatedVpio* const vop = VerilatedVpio::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
return vop->type();
|
||||
}
|
||||
case vpiDirection: {
|
||||
// By forthought, the directions already are vpi enumerated
|
||||
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
|
||||
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
return vop->varp()->vldir();
|
||||
}
|
||||
case vpiScalar: // FALLTHRU
|
||||
case vpiVector: {
|
||||
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
|
||||
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
return (property == vpiVector) ^ (vop->varp()->dims() == 0);
|
||||
}
|
||||
case vpiSize: {
|
||||
VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object);
|
||||
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
return vop->size();
|
||||
}
|
||||
@@ -1602,7 +1608,7 @@ PLI_INT64 vpi_get64(PLI_INT32 /*property*/, vpiHandle /*object*/) {
|
||||
PLI_BYTE8* vpi_get_str(PLI_INT32 property, vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_str %d %p\n", property, object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
const VerilatedVpio* const vop = VerilatedVpio::castp(object);
|
||||
VL_VPI_ERROR_RESET_();
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
switch (property) {
|
||||
@@ -1689,7 +1695,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
const char* fullname) {
|
||||
if (!vl_check_format(varp, valuep, fullname, true)) return;
|
||||
// Maximum required size is for binary string, one byte per bit plus null termination
|
||||
static VL_THREAD_LOCAL char t_outStr[1 + VL_MULS_MAX_WORDS * 32];
|
||||
static VL_THREAD_LOCAL char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
// cppcheck-suppress variableScope
|
||||
static VL_THREAD_LOCAL int t_outStrSz = sizeof(t_outStr) - 1;
|
||||
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
|
||||
@@ -1697,7 +1703,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
if (valuep->format == vpiVectorVal) {
|
||||
// Vector pointer must come from our memory pool
|
||||
// It only needs to persist until the next vpi_get_value
|
||||
static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_MULS_MAX_WORDS * 2];
|
||||
static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2];
|
||||
valuep->value.vector = t_out;
|
||||
if (varp->vltype() == VLVT_UINT8) {
|
||||
t_out[0].aval = *(reinterpret_cast<CData*>(varDatap));
|
||||
@@ -1720,12 +1726,12 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
return;
|
||||
} else if (varp->vltype() == VLVT_WDATA) {
|
||||
int words = VL_WORDS_I(varp->packed().elements());
|
||||
if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) {
|
||||
VL_FATAL_MT(
|
||||
__FILE__, __LINE__, "",
|
||||
"vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile");
|
||||
if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"vpi_get_value with more than VL_VALUE_STRING_MAX_WORDS; increase and "
|
||||
"recompile");
|
||||
}
|
||||
WDataInP datap = (reinterpret_cast<EData*>(varDatap));
|
||||
const WDataInP datap = (reinterpret_cast<EData*>(varDatap));
|
||||
for (int i = 0; i < words; ++i) {
|
||||
t_out[i].aval = datap[i];
|
||||
t_out[i].bval = 0;
|
||||
@@ -1743,9 +1749,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_MULS_MAX_WORDS, bits);
|
||||
VL_VALUE_STRING_MAX_WORDS, bits);
|
||||
}
|
||||
for (i = 0; i < bits; ++i) {
|
||||
char val = (datap[i >> 3] >> (i & 7)) & 1;
|
||||
@@ -1756,7 +1762,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
} else if (valuep->format == vpiOctStrVal) {
|
||||
valuep->value.str = t_outStr;
|
||||
int chars = (varp->packed().elements() + 2) / 3;
|
||||
int bytes = VL_BYTES_I(varp->packed().elements());
|
||||
const int bytes = VL_BYTES_I(varp->packed().elements());
|
||||
CData* datap = (reinterpret_cast<CData*>(varDatap));
|
||||
int i;
|
||||
if (chars > t_outStrSz) {
|
||||
@@ -1764,13 +1770,13 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_MULS_MAX_WORDS, chars);
|
||||
VL_VALUE_STRING_MAX_WORDS, chars);
|
||||
chars = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < chars; ++i) {
|
||||
div_t idx = div(i * 3, 8);
|
||||
const div_t idx = div(i * 3, 8);
|
||||
int val = datap[idx.quot];
|
||||
if ((idx.quot + 1) < bytes) {
|
||||
// if the next byte is valid or that in
|
||||
@@ -1822,9 +1828,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
|
||||
VL_MULS_MAX_WORDS, chars);
|
||||
VL_VALUE_STRING_MAX_WORDS, chars);
|
||||
chars = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < chars; ++i) {
|
||||
@@ -1832,7 +1838,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
if (i == (chars - 1)) {
|
||||
// most signifcant char, mask off non existant bits when vector
|
||||
// size is not a multiple of 4
|
||||
unsigned int rem = varp->packed().elements() & 3;
|
||||
const unsigned int rem = varp->packed().elements() & 3;
|
||||
if (rem) {
|
||||
// generate bit mask & zero non existant bits
|
||||
val &= (1 << rem) - 1;
|
||||
@@ -1853,16 +1859,16 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
int i;
|
||||
if (bytes > t_outStrSz) {
|
||||
// limit maximum size of output to size of buffer to prevent overrun.
|
||||
VL_VPI_WARNING_(
|
||||
__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname,
|
||||
t_outStrSz, VL_MULS_MAX_WORDS, bytes);
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__,
|
||||
"%s: Truncating string value of %s for %s"
|
||||
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than "
|
||||
"required (%d)",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format),
|
||||
fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes);
|
||||
bytes = t_outStrSz;
|
||||
}
|
||||
for (i = 0; i < bytes; ++i) {
|
||||
char val = datap[bytes - i - 1];
|
||||
const char val = datap[bytes - i - 1];
|
||||
// other simulators replace [leading?] zero chars with spaces, replicate here.
|
||||
t_outStr[i] = val ? val : ' ';
|
||||
}
|
||||
@@ -1893,13 +1899,13 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
|
||||
VL_VPI_ERROR_RESET_();
|
||||
if (VL_UNLIKELY(!valuep)) return;
|
||||
|
||||
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
|
||||
if (VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
|
||||
vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname());
|
||||
return;
|
||||
} else if (VerilatedVpioParam* vop = VerilatedVpioParam::castp(object)) {
|
||||
} else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
|
||||
vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname());
|
||||
return;
|
||||
} else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) {
|
||||
} else if (VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
|
||||
if (valuep->format == vpiIntVal) {
|
||||
valuep->value.integer = vop->num();
|
||||
return;
|
||||
@@ -1920,7 +1926,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
|
||||
return nullptr;
|
||||
}
|
||||
if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) {
|
||||
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
|
||||
VL_DEBUG_IF_PLI(
|
||||
VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(),
|
||||
valuep->format, valuep->value.integer);
|
||||
@@ -1953,7 +1959,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
valuep->value.vector[1].aval & vop->mask(), valuep->value.vector[0].aval);
|
||||
return object;
|
||||
} else if (vop->varp()->vltype() == VLVT_WDATA) {
|
||||
int words = VL_WORDS_I(vop->varp()->packed().elements());
|
||||
const int words = VL_WORDS_I(vop->varp()->packed().elements());
|
||||
WDataOutP datap = (reinterpret_cast<EData*>(vop->varDatap()));
|
||||
for (int i = 0; i < words; ++i) {
|
||||
datap[i] = valuep->value.vector[i].aval;
|
||||
@@ -1962,8 +1968,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
return object;
|
||||
}
|
||||
} else if (valuep->format == vpiBinStrVal) {
|
||||
int bits = vop->varp()->packed().elements();
|
||||
int len = std::strlen(valuep->value.str);
|
||||
const int bits = vop->varp()->packed().elements();
|
||||
const int len = std::strlen(valuep->value.str);
|
||||
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
for (int i = 0; i < bits; ++i) {
|
||||
char set = (i < len) ? (valuep->value.str[len - i - 1] == '1') : 0;
|
||||
@@ -1977,9 +1983,9 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
}
|
||||
return object;
|
||||
} else if (valuep->format == vpiOctStrVal) {
|
||||
int chars = (vop->varp()->packed().elements() + 2) / 3;
|
||||
int bytes = VL_BYTES_I(vop->varp()->packed().elements());
|
||||
int len = std::strlen(valuep->value.str);
|
||||
const int chars = (vop->varp()->packed().elements() + 2) / 3;
|
||||
const int bytes = VL_BYTES_I(vop->varp()->packed().elements());
|
||||
const int len = std::strlen(valuep->value.str);
|
||||
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
div_t idx;
|
||||
datap[0] = 0; // reset zero'th byte
|
||||
@@ -2029,7 +2035,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
} else if (valuep->format == vpiDecStrVal) {
|
||||
char remainder[16];
|
||||
unsigned long long val;
|
||||
int success = std::sscanf(valuep->value.str, "%30llu%15s", &val, remainder);
|
||||
const int success = std::sscanf(valuep->value.str, "%30llu%15s", &val, remainder);
|
||||
if (success < 1) {
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s",
|
||||
__func__, valuep->value.str,
|
||||
@@ -2057,12 +2063,12 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
return object;
|
||||
}
|
||||
} else if (valuep->format == vpiHexStrVal) {
|
||||
int chars = (vop->varp()->packed().elements() + 3) >> 2;
|
||||
const int chars = (vop->varp()->packed().elements() + 3) >> 2;
|
||||
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
char* val = valuep->value.str;
|
||||
// skip hex ident if one is detected at the start of the string
|
||||
if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2;
|
||||
int len = std::strlen(val);
|
||||
const int len = std::strlen(val);
|
||||
for (int i = 0; i < chars; ++i) {
|
||||
char hex;
|
||||
// compute hex digit value
|
||||
@@ -2097,8 +2103,8 @@ 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) {
|
||||
int bytes = VL_BYTES_I(vop->varp()->packed().elements());
|
||||
int len = std::strlen(valuep->value.str);
|
||||
const int bytes = VL_BYTES_I(vop->varp()->packed().elements());
|
||||
const int len = std::strlen(valuep->value.str);
|
||||
CData* datap = (reinterpret_cast<CData*>(vop->varDatap()));
|
||||
for (int i = 0; i < bytes; ++i) {
|
||||
// prepend with 0 values before placing string the least significant bytes
|
||||
@@ -2120,11 +2126,11 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s",
|
||||
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
|
||||
return nullptr;
|
||||
} else if (VerilatedVpioParam* vop = VerilatedVpioParam::castp(object)) {
|
||||
} else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiParameter: %s",
|
||||
__func__, vop->fullname());
|
||||
return nullptr;
|
||||
} else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) {
|
||||
} else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
|
||||
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiConstant: %s",
|
||||
__func__, vop->fullname());
|
||||
return nullptr;
|
||||
@@ -2153,18 +2159,18 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
|
||||
return;
|
||||
}
|
||||
if (time_p->type == vpiSimTime) {
|
||||
QData qtime = VL_TIME_Q();
|
||||
WData itime[2];
|
||||
const QData qtime = VL_TIME_Q();
|
||||
VlWide<2> itime;
|
||||
VL_SET_WQ(itime, qtime);
|
||||
time_p->low = itime[0];
|
||||
time_p->high = itime[1];
|
||||
return;
|
||||
} else if (time_p->type == vpiScaledRealTime) {
|
||||
double dtime = VL_TIME_D();
|
||||
if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) {
|
||||
int scalePow10
|
||||
if (VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object)) {
|
||||
const int scalePow10
|
||||
= Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit();
|
||||
double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001
|
||||
const double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001
|
||||
dtime *= scale;
|
||||
}
|
||||
time_p->real = dtime;
|
||||
@@ -2198,7 +2204,7 @@ PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8* formatp, ...) {
|
||||
VL_VPI_ERROR_RESET_();
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
int chars = vpi_mcd_vprintf(mcd, formatp, ap);
|
||||
const int chars = vpi_mcd_vprintf(mcd, formatp, ap);
|
||||
va_end(ap);
|
||||
return chars;
|
||||
}
|
||||
@@ -2208,7 +2214,7 @@ PLI_INT32 vpi_printf(PLI_BYTE8* formatp, ...) {
|
||||
VL_VPI_ERROR_RESET_();
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
int chars = vpi_vprintf(formatp, ap);
|
||||
const int chars = vpi_vprintf(formatp, ap);
|
||||
va_end(ap);
|
||||
return chars;
|
||||
}
|
||||
@@ -2221,11 +2227,11 @@ PLI_INT32 vpi_vprintf(PLI_BYTE8* formatp, va_list ap) {
|
||||
|
||||
PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8* format, va_list ap) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
FILE* fp = VL_CVT_I_FP(mcd);
|
||||
FILE* const fp = VL_CVT_I_FP(mcd);
|
||||
VL_VPI_ERROR_RESET_();
|
||||
// cppcheck-suppress nullPointer
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
int chars = vfprintf(fp, format, ap);
|
||||
const int chars = vfprintf(fp, format, ap);
|
||||
return chars;
|
||||
}
|
||||
|
||||
@@ -2238,7 +2244,7 @@ PLI_INT32 vpi_flush(void) {
|
||||
|
||||
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
FILE* fp = VL_CVT_I_FP(mcd);
|
||||
FILE* const fp = VL_CVT_I_FP(mcd);
|
||||
VL_VPI_ERROR_RESET_();
|
||||
if (VL_UNLIKELY(!fp)) return 1;
|
||||
std::fflush(fp);
|
||||
@@ -2255,7 +2261,7 @@ PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) {
|
||||
// executing vpi_chk_error does not reset error
|
||||
// error_info_p can be nullptr, so only return level in that case
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
p_vpi_error_info _error_info_p = VerilatedVpiImp::error_info()->getError();
|
||||
p_vpi_error_info const _error_info_p = VerilatedVpiImp::error_info()->getError();
|
||||
if (error_info_p && _error_info_p) *error_info_p = *_error_info_p;
|
||||
if (!_error_info_p) return 0; // no error occured
|
||||
return _error_info_p->level; // return error severity level
|
||||
@@ -2271,7 +2277,7 @@ PLI_INT32 vpi_free_object(vpiHandle object) {
|
||||
PLI_INT32 vpi_release_handle(vpiHandle object) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n", object););
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VerilatedVpio* vop = VerilatedVpio::castp(object);
|
||||
VerilatedVpio* const vop = VerilatedVpio::castp(object);
|
||||
VL_VPI_ERROR_RESET_();
|
||||
if (VL_UNLIKELY(!vop)) return 0;
|
||||
VL_DO_DANGLING(delete vop, vop);
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
static bool callValueCbs() VL_MT_UNSAFE_ONE;
|
||||
/// Call callbacks of arbitrary types.
|
||||
/// User wrapper code should call this from their main loops.
|
||||
static bool callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE;
|
||||
static bool callCbs(const vluint32_t reason) VL_MT_UNSAFE_ONE;
|
||||
/// Returns time of the next registered VPI callback, or
|
||||
/// ~(0ULL) if none are registered
|
||||
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
|
||||
|
||||
@@ -421,7 +421,8 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
// Verilated function size macros
|
||||
|
||||
#define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation
|
||||
#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
|
||||
#define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
|
||||
#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE / VL_BYTESIZE)
|
||||
|
||||
//=========================================================================
|
||||
// Base macros
|
||||
|
||||
+1
-17
@@ -38,23 +38,7 @@ def test():
|
||||
ci_fold_start("configure")
|
||||
print("Stage 1: configure (coverage on)")
|
||||
run("autoconf")
|
||||
cxx_flags = (
|
||||
"--coverage"
|
||||
# Otherwise inline may not show as uncovered
|
||||
# If we use this then e.g. verilated.h functions properly show up
|
||||
# if unused.
|
||||
# However, VerilatedSerialize::write then changes from covered
|
||||
# to uncovered (in G++ 9.3.0) even with all inlining turned off.
|
||||
# Having false negative coverage is more effort then missing negatives.
|
||||
# Also this seems to explode the runtime (since a lot more data).
|
||||
# + " -fkeep-inline-functions"
|
||||
# Otherwise static may not show as uncovered
|
||||
+ " -fkeep-static-functions"
|
||||
# Exceptions can pollute the branch coverage data
|
||||
+ " -fno-exceptions"
|
||||
# Define-out some impossible stuff
|
||||
+ " -DVL_GCOV")
|
||||
run("./configure --enable-longtests CXX='g++ " + cxx_flags + "'")
|
||||
run("./configure --enable-longtests --enable-coverage CXX=g++")
|
||||
ci_fold_end()
|
||||
|
||||
if Args.stage_enabled[2]:
|
||||
|
||||
+2
-2
@@ -66,11 +66,11 @@ def cwrite(filename):
|
||||
fh.write(" auto* gp = &m_graph;\n")
|
||||
for ver in sorted(Vertexes, key=lambda ver: ver['num']):
|
||||
fh.write(
|
||||
" auto* %s = new V3GraphTestVertex(gp, \"%s\"); if (%s) {}\n"
|
||||
" auto* %s = new V3GraphTestVertex{gp, \"%s\"}; if (%s) {}\n"
|
||||
% (ver['name'], ver['name'], ver['name']))
|
||||
fh.write("\n")
|
||||
for edge in Edges:
|
||||
fh.write(" new V3GraphEdge(gp, %s, %s, %s, %s);\n" %
|
||||
fh.write(" new V3GraphEdge{gp, %s, %s, %s, %s};\n" %
|
||||
(edge['from'], edge['to'], edge['weight'],
|
||||
"true" if edge['cutable'] else "false"))
|
||||
fh.write("}\n")
|
||||
|
||||
+13
-9
@@ -77,13 +77,11 @@ TGT = ../../verilator_bin$(EXEEXT)
|
||||
#################
|
||||
ifeq ($(VL_DEBUG),)
|
||||
# Optimize
|
||||
COPT = -O2
|
||||
CPPFLAGS += -O2
|
||||
else
|
||||
# Debug
|
||||
COPT = -ggdb -DVL_DEBUG -D_GLIBCXX_DEBUG
|
||||
# Debug & Profile:
|
||||
#LDFLAGS += -pg -g
|
||||
#COPT = -ggdb -pg -g
|
||||
CPPFLAGS += @CFG_CXXFLAGS_DEBUG@ -DVL_DEBUG -D_GLIBCXX_DEBUG
|
||||
LDFLAGS += @CFG_LDFLAGS_DEBUG@
|
||||
endif
|
||||
#################
|
||||
|
||||
@@ -96,7 +94,6 @@ LIBS = $(CFG_LIBS) -lm
|
||||
CPPFLAGS += -MMD
|
||||
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
|
||||
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
|
||||
CPPFLAGS += $(COPT)
|
||||
CPPFLAGS += -MP # Only works on recent GCC versions
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
CPPFLAGS += -W -Wall $(CFG_CXXFLAGS_WEXTRA) $(CFG_CXXFLAGS_SRC) -Werror
|
||||
@@ -172,6 +169,7 @@ RAW_OBJS = \
|
||||
V3Clean.o \
|
||||
V3Clock.o \
|
||||
V3Combine.o \
|
||||
V3Common.o \
|
||||
V3Config.o \
|
||||
V3Const__gen.o \
|
||||
V3Coverage.o \
|
||||
@@ -182,11 +180,16 @@ RAW_OBJS = \
|
||||
V3DepthBlock.o \
|
||||
V3Descope.o \
|
||||
V3DupFinder.o \
|
||||
V3EmitC.o \
|
||||
V3EmitCBase.o \
|
||||
V3EmitCConstPool.o \
|
||||
V3EmitCFunc.o \
|
||||
V3EmitCHeaders.o \
|
||||
V3EmitCImp.o \
|
||||
V3EmitCInlines.o \
|
||||
V3EmitCSyms.o \
|
||||
V3EmitCMake.o \
|
||||
V3EmitCMain.o \
|
||||
V3EmitCMake.o \
|
||||
V3EmitCModel.o \
|
||||
V3EmitCSyms.o \
|
||||
V3EmitMk.o \
|
||||
V3EmitV.o \
|
||||
V3EmitXml.o \
|
||||
@@ -253,6 +256,7 @@ RAW_OBJS = \
|
||||
V3Undriven.o \
|
||||
V3Unknown.o \
|
||||
V3Unroll.o \
|
||||
V3VariableOrder.o \
|
||||
V3Waiver.o \
|
||||
V3Width.o \
|
||||
V3WidthSel.o \
|
||||
|
||||
+19
-20
@@ -64,11 +64,11 @@ private:
|
||||
|
||||
public:
|
||||
LatchDetectGraphVertex(V3Graph* graphp, const string& name, VertexType type = VT_BLOCK)
|
||||
: V3GraphVertex(graphp)
|
||||
, m_name(name)
|
||||
, m_type(type) {}
|
||||
virtual string name() const { return m_name + " " + typestr(); }
|
||||
virtual string dotColor() const { return user() ? "green" : "black"; }
|
||||
: V3GraphVertex{graphp}
|
||||
, m_name{name}
|
||||
, m_type{type} {}
|
||||
virtual string name() const override { return m_name + " " + typestr(); }
|
||||
virtual string dotColor() const override { return user() ? "green" : "black"; }
|
||||
virtual int type() const { return m_type; }
|
||||
};
|
||||
|
||||
@@ -120,7 +120,7 @@ protected:
|
||||
|
||||
public:
|
||||
LatchDetectGraph() { clear(); }
|
||||
~LatchDetectGraph() { clear(); }
|
||||
virtual ~LatchDetectGraph() override { clear(); }
|
||||
// ACCESSORS
|
||||
LatchDetectGraphVertex* currentp() { return m_curVertexp; }
|
||||
void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; }
|
||||
@@ -142,17 +142,17 @@ public:
|
||||
// Add a new control path and connect it to its parent
|
||||
LatchDetectGraphVertex* addPathVertex(LatchDetectGraphVertex* parent, const string& name,
|
||||
bool branch = false) {
|
||||
m_curVertexp = new LatchDetectGraphVertex(this, name,
|
||||
m_curVertexp = new LatchDetectGraphVertex{this, name,
|
||||
branch ? LatchDetectGraphVertex::VT_BRANCH
|
||||
: LatchDetectGraphVertex::VT_BLOCK);
|
||||
new V3GraphEdge(this, parent, m_curVertexp, 1);
|
||||
: LatchDetectGraphVertex::VT_BLOCK};
|
||||
new V3GraphEdge{this, parent, m_curVertexp, 1};
|
||||
return m_curVertexp;
|
||||
}
|
||||
// Add a new output variable vertex and store a pointer to it in the user1 field of the
|
||||
// variables AstNode
|
||||
LatchDetectGraphVertex* addOutputVertex(AstVarRef* nodep) {
|
||||
LatchDetectGraphVertex* outVertexp
|
||||
= new LatchDetectGraphVertex(this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT);
|
||||
= new LatchDetectGraphVertex{this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT};
|
||||
nodep->varp()->user1p(outVertexp);
|
||||
m_outputs.push_back(nodep);
|
||||
return outVertexp;
|
||||
@@ -437,7 +437,7 @@ private:
|
||||
virtual void visit(AstInitial* nodep) override {
|
||||
// Relink to IACTIVE, unless already under it
|
||||
UINFO(4, " INITIAL " << nodep << endl);
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
@@ -470,15 +470,14 @@ private:
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
if (!m_scopeFinalp) {
|
||||
m_scopeFinalp = new AstCFunc(
|
||||
nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep());
|
||||
m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_scopeFinalp->addInitsp(
|
||||
new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign() + "\n"));
|
||||
m_scopeFinalp->dontCombine(true);
|
||||
m_scopeFinalp->formCallTree(true);
|
||||
m_scopeFinalp->isStatic(false);
|
||||
m_scopeFinalp->isLoose(true);
|
||||
m_scopeFinalp->slow(true);
|
||||
m_namer.scopep()->addActivep(m_scopeFinalp);
|
||||
}
|
||||
@@ -541,14 +540,14 @@ private:
|
||||
|
||||
// Warn and/or convert any delayed assignments
|
||||
if (combo && !sequent) {
|
||||
ActiveLatchCheckVisitor latchvisitor(nodep, kwd);
|
||||
ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
|
||||
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_LATCH);
|
||||
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_LATCH};
|
||||
} else {
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_COMBO);
|
||||
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_COMBO};
|
||||
}
|
||||
} else if (!combo && sequent) {
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_SEQ);
|
||||
ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_SEQ};
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) override {
|
||||
@@ -621,6 +620,6 @@ public:
|
||||
|
||||
void V3Active::activeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveVisitor visitor(nodep); } // Destruct before checking
|
||||
{ ActiveVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+1
-1
@@ -136,6 +136,6 @@ public:
|
||||
|
||||
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveTopVisitor visitor(nodep); } // Destruct before checking
|
||||
{ ActiveTopVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+4
-4
@@ -163,7 +163,7 @@ private:
|
||||
} else {
|
||||
++m_statAsNotImm;
|
||||
}
|
||||
bool force = VN_IS(nodep, AssertIntrinsic);
|
||||
const bool force = VN_IS(nodep, AssertIntrinsic);
|
||||
if (passsp) passsp = newIfAssertOn(passsp, force);
|
||||
if (failsp) failsp = newIfAssertOn(failsp, force);
|
||||
if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
|
||||
@@ -231,7 +231,7 @@ private:
|
||||
} while (ifp);
|
||||
|
||||
AstNode* newifp = nodep->cloneTree(false);
|
||||
bool allow_none = nodep->unique0Pragma();
|
||||
const bool allow_none = nodep->unique0Pragma();
|
||||
|
||||
// Empty case means no property
|
||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::BitFalse());
|
||||
@@ -305,7 +305,7 @@ private:
|
||||
// Empty case means no property
|
||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::BitFalse());
|
||||
|
||||
bool allow_none = has_default || nodep->unique0Pragma();
|
||||
const bool allow_none = has_default || nodep->unique0Pragma();
|
||||
AstNode* ohot
|
||||
= (allow_none
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
@@ -474,6 +474,6 @@ public:
|
||||
|
||||
void V3Assert::assertAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertVisitor visitor(nodep); } // Destruct before checking
|
||||
{ AssertVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+1
-1
@@ -205,6 +205,6 @@ public:
|
||||
|
||||
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertPreVisitor visitor(nodep); } // Destruct before checking
|
||||
{ AssertPreVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+16
-10
@@ -74,9 +74,10 @@ AstNode::AstNode(AstType t, FileLine* fl)
|
||||
m_clonep = nullptr;
|
||||
m_cloneCnt = 0;
|
||||
// Attributes
|
||||
m_didWidth = false;
|
||||
m_doingWidth = false;
|
||||
m_protect = true;
|
||||
m_flags.didWidth = false;
|
||||
m_flags.doingWidth = false;
|
||||
m_flags.protect = true;
|
||||
m_flags.unused = 0; // Initializing this avoids a read-modify-write on construction
|
||||
m_user1u = VNUser(0);
|
||||
m_user1Cnt = 0;
|
||||
m_user2u = VNUser(0);
|
||||
@@ -119,7 +120,7 @@ string AstNode::encodeName(const string& namein) {
|
||||
// a user identifier nor a temp we create in Verilator.
|
||||
// We also do *NOT* use __DOT__ etc, as we search for those
|
||||
// in some replacements, and don't want to mangle the user's names.
|
||||
unsigned val = pos[0] & 0xff; // Mask to avoid sign extension
|
||||
const unsigned val = pos[0] & 0xff; // Mask to avoid sign extension
|
||||
std::stringstream hex;
|
||||
hex << std::setfill('0') << std::setw(2) << std::hex << val;
|
||||
out += "__0" + hex.str();
|
||||
@@ -932,7 +933,9 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
|
||||
void AstNode::cloneRelinkTree() {
|
||||
// private: Cleanup clone() operation on whole tree. Publicly call cloneTree() instead.
|
||||
for (AstNode* nodep = this; nodep; nodep = nodep->m_nextp) {
|
||||
if (m_dtypep && m_dtypep->clonep()) m_dtypep = m_dtypep->clonep();
|
||||
if (nodep->m_dtypep && nodep->m_dtypep->clonep()) {
|
||||
nodep->m_dtypep = nodep->m_dtypep->clonep();
|
||||
}
|
||||
nodep->cloneRelink();
|
||||
if (nodep->m_op1p) nodep->m_op1p->cloneRelinkTree();
|
||||
if (nodep->m_op2p) nodep->m_op2p->cloneRelinkTree();
|
||||
@@ -1045,7 +1048,7 @@ void AstNode::dumpTreeFileGdb(const AstNode* nodep, // LCOV_EXCL_START
|
||||
cout << "<nullptr>" << endl;
|
||||
return;
|
||||
}
|
||||
string filename = filenamep ? filenamep : v3Global.debugFilename("debug.tree", 98);
|
||||
const string filename = filenamep ? filenamep : v3Global.debugFilename("debug.tree", 98);
|
||||
v3Global.rootp()->dumpTreeFile(filename);
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
@@ -1121,7 +1124,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
|
||||
if (doDump) {
|
||||
{ // Write log & close
|
||||
UINFO(2, "Dumping " << filename << endl);
|
||||
const std::unique_ptr<std::ofstream> logsp(V3File::new_ofstream(filename, append));
|
||||
const std::unique_ptr<std::ofstream> logsp{V3File::new_ofstream(filename, append)};
|
||||
if (logsp->fail()) v3fatal("Can't write " << filename);
|
||||
*logsp << "Verilator Tree Dump (format 0x3900) from <e" << std::dec << editCountLast();
|
||||
*logsp << "> to <e" << std::dec << editCountGbl() << ">\n";
|
||||
@@ -1178,7 +1181,7 @@ string AstNode::locationStr() const {
|
||||
str += modp->hierName();
|
||||
return str;
|
||||
} else if ((nvrp = VN_CAST_CONST(backp, NodeVarRef))) {
|
||||
string prettyName = nvrp->prettyName();
|
||||
const string prettyName = nvrp->prettyName();
|
||||
// VarRefs have not been flattened yet and do not contain location information
|
||||
if (prettyName != nvrp->name()) {
|
||||
str += prettyName;
|
||||
@@ -1261,12 +1264,15 @@ AstNodeDType* AstNode::findBitRangeDType(const VNumRange& range, int widthMin,
|
||||
AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) {
|
||||
return v3Global.rootp()->typeTablep()->findInsertSameDType(nodep);
|
||||
}
|
||||
AstNodeDType* AstNode::findVoidDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
|
||||
AstNodeDType* AstNode::findEmptyQueueDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findEmptyQueueDType(fileline());
|
||||
}
|
||||
AstNodeDType* AstNode::findQueueIndexDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findQueueIndexDType(fileline());
|
||||
}
|
||||
AstNodeDType* AstNode::findVoidDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// AstNVisitor
|
||||
|
||||
+118
-107
@@ -24,8 +24,10 @@
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Number.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Broken.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "V3Ast__gen_classes.h" // From ./astgen
|
||||
@@ -49,7 +51,7 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
|
||||
// For broken() function, return error string if have a match
|
||||
#define BROKEN_RTN(test) \
|
||||
do { \
|
||||
if (VL_UNCOVERABLE(test)) return #test; \
|
||||
if (VL_UNCOVERABLE(test)) return "'" #test "' @ " __FILE__ ":" VL_STRINGIFY(__LINE__); \
|
||||
} while (false)
|
||||
// For broken() function, return error string if a base of this class has a match
|
||||
#define BROKEN_BASE_RTN(test) \
|
||||
@@ -155,6 +157,7 @@ public:
|
||||
return (m_e == READWRITE) ? VAccess(m_e) : (m_e == WRITE ? VAccess(READ) : VAccess(WRITE));
|
||||
}
|
||||
bool isReadOnly() const { return m_e == READ; } // False with READWRITE
|
||||
bool isWriteOnly() const { return m_e == WRITE; } // False with READWRITE
|
||||
bool isReadOrRW() const { return m_e == READ || m_e == READWRITE; }
|
||||
bool isWriteOrRW() const { return m_e == WRITE || m_e == READWRITE; }
|
||||
bool isRW() const { return m_e == READWRITE; }
|
||||
@@ -236,39 +239,6 @@ inline bool operator==(AstPragmaType::en lhs, const AstPragmaType& rhs) { return
|
||||
|
||||
//######################################################################
|
||||
|
||||
class AstCFuncType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
FT_NORMAL,
|
||||
TRACE_REGISTER,
|
||||
TRACE_INIT,
|
||||
TRACE_INIT_SUB,
|
||||
TRACE_FULL,
|
||||
TRACE_FULL_SUB,
|
||||
TRACE_CHANGE,
|
||||
TRACE_CHANGE_SUB,
|
||||
TRACE_CLEANUP
|
||||
};
|
||||
enum en m_e;
|
||||
inline AstCFuncType()
|
||||
: m_e{FT_NORMAL} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline AstCFuncType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit inline AstCFuncType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
operator en() const { return m_e; }
|
||||
// METHODS
|
||||
bool isTrace() const { return m_e != FT_NORMAL; }
|
||||
};
|
||||
inline bool operator==(const AstCFuncType& lhs, const AstCFuncType& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
inline bool operator==(const AstCFuncType& lhs, AstCFuncType::en rhs) { return lhs.m_e == rhs; }
|
||||
inline bool operator==(AstCFuncType::en lhs, const AstCFuncType& rhs) { return lhs == rhs.m_e; }
|
||||
|
||||
//######################################################################
|
||||
|
||||
class VEdgeType final {
|
||||
public:
|
||||
// REMEMBER to edit the strings below too
|
||||
@@ -457,6 +427,7 @@ public:
|
||||
// Internal types for mid-steps
|
||||
SCOPEPTR,
|
||||
CHARPTR,
|
||||
MTASKSTATE,
|
||||
// Unsigned and two state; fundamental types
|
||||
UINT32,
|
||||
UINT64,
|
||||
@@ -467,18 +438,19 @@ public:
|
||||
};
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {
|
||||
"%E-unk", "bit", "byte", "chandle", "event", "int", "integer",
|
||||
"logic", "longint", "real", "shortint", "time", "string", "VerilatedScope*",
|
||||
"char*", "IData", "QData", "LOGIC_IMPLICIT", " MAX"};
|
||||
static const char* const names[]
|
||||
= {"%E-unk", "bit", "byte", "chandle", "event",
|
||||
"int", "integer", "logic", "longint", "real",
|
||||
"shortint", "time", "string", "VerilatedScope*", "char*",
|
||||
"VlMTaskState", "IData", "QData", "LOGIC_IMPLICIT", " MAX"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* dpiType() const {
|
||||
static const char* const names[]
|
||||
= {"%E-unk", "svBit", "char", "void*", "char", "int",
|
||||
"%E-integer", "svLogic", "long long", "double", "short", "%E-time",
|
||||
"const char*", "dpiScope", "const char*", "IData", "QData", "%E-logic-implicit",
|
||||
" MAX"};
|
||||
= {"%E-unk", "svBit", "char", "void*", "char",
|
||||
"int", "%E-integer", "svLogic", "long long", "double",
|
||||
"short", "%E-time", "const char*", "dpiScope", "const char*",
|
||||
"%E-mtaskstate", "IData", "QData", "%E-logic-implct", " MAX"};
|
||||
return names[m_e];
|
||||
}
|
||||
static void selfTest() {
|
||||
@@ -511,6 +483,7 @@ public:
|
||||
case STRING: return 64; // opaque // Just the pointer, for today
|
||||
case SCOPEPTR: return 0; // opaque
|
||||
case CHARPTR: return 0; // opaque
|
||||
case MTASKSTATE: return 0; // opaque
|
||||
case UINT32: return 32;
|
||||
case UINT64: return 64;
|
||||
default: return 0;
|
||||
@@ -549,11 +522,32 @@ public:
|
||||
|| m_e == DOUBLE || m_e == SHORTINT || m_e == UINT32 || m_e == UINT64);
|
||||
}
|
||||
bool isOpaque() const { // IE not a simple number we can bit optimize
|
||||
return (m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR || m_e == DOUBLE);
|
||||
return (m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR || m_e == MTASKSTATE
|
||||
|| m_e == DOUBLE);
|
||||
}
|
||||
bool isDouble() const { return m_e == DOUBLE; }
|
||||
bool isEventValue() const { return m_e == EVENTVALUE; }
|
||||
bool isString() const { return m_e == STRING; }
|
||||
bool isMTaskState() const { return m_e == MTASKSTATE; }
|
||||
// Does this represent a C++ LiteralType? (can be constexpr)
|
||||
bool isLiteralType() const {
|
||||
switch (m_e) {
|
||||
case BIT:
|
||||
case BYTE:
|
||||
case CHANDLE:
|
||||
case INT:
|
||||
case INTEGER:
|
||||
case LOGIC:
|
||||
case LONGINT:
|
||||
case DOUBLE:
|
||||
case SHORTINT:
|
||||
case SCOPEPTR:
|
||||
case CHARPTR:
|
||||
case UINT32:
|
||||
case UINT64: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
inline bool operator==(const AstBasicDTypeKwd& lhs, const AstBasicDTypeKwd& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
@@ -1136,10 +1130,14 @@ public:
|
||||
explicit VNUser(void* p) { m_u.up = p; }
|
||||
~VNUser() = default;
|
||||
// Casters
|
||||
WidthVP* c() const { return reinterpret_cast<WidthVP*>(m_u.up); }
|
||||
VSymEnt* toSymEnt() const { return reinterpret_cast<VSymEnt*>(m_u.up); }
|
||||
AstNode* toNodep() const { return reinterpret_cast<AstNode*>(m_u.up); }
|
||||
V3GraphVertex* toGraphVertex() const { return reinterpret_cast<V3GraphVertex*>(m_u.up); }
|
||||
template <class T> //
|
||||
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const {
|
||||
return reinterpret_cast<T>(m_u.up);
|
||||
}
|
||||
WidthVP* c() const { return to<WidthVP*>(); }
|
||||
VSymEnt* toSymEnt() const { return to<VSymEnt*>(); }
|
||||
AstNode* toNodep() const { return to<AstNode*>(); }
|
||||
V3GraphVertex* toGraphVertex() const { return to<V3GraphVertex*>(); }
|
||||
int toInt() const { return m_u.ui; }
|
||||
static VNUser fromInt(int i) { return VNUser(i); }
|
||||
};
|
||||
@@ -1356,8 +1354,32 @@ class AstNode VL_NOT_FINAL {
|
||||
const AstType m_type; // Node sub-type identifier
|
||||
// ^ ASTNODE_PREFETCH depends on above ordering of members
|
||||
|
||||
// padding - 2 extra bytes here after m_type
|
||||
int m_cloneCnt; // Mark of when userp was set
|
||||
// AstType is 2 bytes, so we can stick another 6 bytes after it to utilize what would
|
||||
// otherwise be padding (on a 64-bit system). We stick the attribute flags, broken state,
|
||||
// and the clone count here.
|
||||
|
||||
struct {
|
||||
bool didWidth : 1; // Did V3Width computation
|
||||
bool doingWidth : 1; // Inside V3Width
|
||||
bool protect : 1; // Protect name if protection is on
|
||||
// Space for more flags here (there must be 8 bits in total)
|
||||
uint8_t unused : 5;
|
||||
} m_flags; // Attribute flags
|
||||
|
||||
// State variable used by V3Broken for consistency checking. The top bit of this is byte is a
|
||||
// flag, representing V3Broken is currently proceeding under this node. The bottom 7 bits are
|
||||
// a generation number. This is hot when --debug-checks so we access as a whole to avoid bit
|
||||
// field masking resulting in unnecessary read-modify-write ops.
|
||||
uint8_t m_brokenState = 0;
|
||||
|
||||
int m_cloneCnt; // Sequence number for when last clone was made
|
||||
|
||||
#if defined(__x86_64__) && defined(__gnu_linux__)
|
||||
// Only assert this on known platforms, as it only affects performance, not correctness
|
||||
static_assert(sizeof(m_type) + sizeof(m_flags) + sizeof(m_brokenState) + sizeof(m_cloneCnt)
|
||||
<= sizeof(void*),
|
||||
"packing requires padding");
|
||||
#endif
|
||||
|
||||
AstNodeDType* m_dtypep; // Data type of output or assignment (etc)
|
||||
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
|
||||
@@ -1370,12 +1392,6 @@ class AstNode VL_NOT_FINAL {
|
||||
AstNode* m_clonep; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY)
|
||||
static int s_cloneCntGbl; // Count of which userp is set
|
||||
|
||||
// Attributes
|
||||
bool m_didWidth : 1; // Did V3Width computation
|
||||
bool m_doingWidth : 1; // Inside V3Width
|
||||
bool m_protect : 1; // Protect name if protection is on
|
||||
// // Space for more bools here
|
||||
|
||||
// This member ordering both allows 64 bit alignment and puts associated data together
|
||||
VNUser m_user1u; // Contains any information the user iteration routine wants
|
||||
uint32_t m_user1Cnt; // Mark of when userp was set
|
||||
@@ -1478,9 +1494,14 @@ public:
|
||||
AstNode* firstAbovep() const { // Returns nullptr when second or later in list
|
||||
return ((backp() && backp()->nextp() != this) ? backp() : nullptr);
|
||||
}
|
||||
bool brokeExists() const;
|
||||
bool brokeExistsAbove() const;
|
||||
bool brokeExistsBelow() const;
|
||||
uint8_t brokenState() const { return m_brokenState; }
|
||||
void brokenState(uint8_t value) { m_brokenState = value; }
|
||||
|
||||
// Used by AstNode::broken()
|
||||
bool brokeExists() const { return V3Broken::isLinkable(this); }
|
||||
bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); }
|
||||
bool brokeExistsBelow() const { return brokeExists() && !(m_brokenState >> 7); }
|
||||
// Note: brokeExistsBelow is not quite precise, as it is true for sibling nodes as well
|
||||
|
||||
// CONSTRUCTORS
|
||||
virtual ~AstNode() = default;
|
||||
@@ -1489,21 +1510,20 @@ public:
|
||||
static void operator delete(void* obj, size_t size);
|
||||
#endif
|
||||
|
||||
// CONSTANT ACCESSORS
|
||||
static int instrCountBranch() { return 4; } ///< Instruction cycles to branch
|
||||
static int instrCountDiv() { return 10; } ///< Instruction cycles to divide
|
||||
static int instrCountDpi() { return 1000; } ///< Instruction cycles to call user function
|
||||
static int instrCountLd() { return 2; } ///< Instruction cycles to load memory
|
||||
static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers
|
||||
static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines
|
||||
static int instrCountDouble() { return 8; } ///< Instruction cycles to convert or do floats
|
||||
static int instrCountDoubleDiv() { return 40; } ///< Instruction cycles to divide floats
|
||||
static int instrCountDoubleTrig() { return 200; } ///< Instruction cycles to do trigonomics
|
||||
static int instrCountString() { return 100; } ///< Instruction cycles to do string ops
|
||||
/// Instruction cycles to call subroutine
|
||||
static int instrCountCall() { return instrCountBranch() + 10; }
|
||||
/// Instruction cycles to determine simulation time
|
||||
static int instrCountTime() { return instrCountCall() + 5; }
|
||||
// CONSTANTS
|
||||
// The following are relative dynamic costs (~ execution cycle count) of various operations.
|
||||
// They are used by V3InstCount to estimate the relative execution time of code fragments.
|
||||
static constexpr int INSTR_COUNT_BRANCH = 4; // Branch
|
||||
static constexpr int INSTR_COUNT_CALL = INSTR_COUNT_BRANCH + 10; // Subroutine call
|
||||
static constexpr int INSTR_COUNT_LD = 2; // Load memory
|
||||
static constexpr int INSTR_COUNT_INT_MUL = 3; // Integer multiply
|
||||
static constexpr int INSTR_COUNT_INT_DIV = 10; // Integer divide
|
||||
static constexpr int INSTR_COUNT_DBL = 8; // Convert or do float ops
|
||||
static constexpr int INSTR_COUNT_DBL_DIV = 40; // Double divide
|
||||
static constexpr int INSTR_COUNT_DBL_TRIG = 200; // Double trigonometric ops
|
||||
static constexpr int INSTR_COUNT_STR = 100; // String ops
|
||||
static constexpr int INSTR_COUNT_TIME = INSTR_COUNT_CALL + 5; // Determine simulation time
|
||||
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
|
||||
|
||||
// ACCESSORS
|
||||
virtual string name() const { return ""; }
|
||||
@@ -1534,17 +1554,17 @@ public:
|
||||
void fileline(FileLine* fl) { m_fileline = fl; }
|
||||
bool width1() const;
|
||||
int widthInstrs() const;
|
||||
void didWidth(bool flag) { m_didWidth = flag; }
|
||||
bool didWidth() const { return m_didWidth; }
|
||||
void didWidth(bool flag) { m_flags.didWidth = flag; }
|
||||
bool didWidth() const { return m_flags.didWidth; }
|
||||
bool didWidthAndSet() {
|
||||
if (didWidth()) return true;
|
||||
didWidth(true);
|
||||
return false;
|
||||
}
|
||||
bool doingWidth() const { return m_doingWidth; }
|
||||
void doingWidth(bool flag) { m_doingWidth = flag; }
|
||||
bool protect() const { return m_protect; }
|
||||
void protect(bool flag) { m_protect = flag; }
|
||||
bool doingWidth() const { return m_flags.doingWidth; }
|
||||
void doingWidth(bool flag) { m_flags.doingWidth = flag; }
|
||||
bool protect() const { return m_flags.protect; }
|
||||
void protect(bool flag) { m_flags.protect = flag; }
|
||||
|
||||
// TODO stomp these width functions out, and call via dtypep() instead
|
||||
int width() const;
|
||||
@@ -1678,6 +1698,7 @@ public:
|
||||
void dtypeSetSigned32() { dtypep(findSigned32DType()); }
|
||||
void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate
|
||||
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
|
||||
void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); }
|
||||
void dtypeSetVoid() { dtypep(findVoidDType()); }
|
||||
|
||||
// Data type locators
|
||||
@@ -1687,6 +1708,8 @@ public:
|
||||
AstNodeDType* findSigned32DType() { return findBasicDType(AstBasicDTypeKwd::INTEGER); }
|
||||
AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); }
|
||||
AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); }
|
||||
AstNodeDType* findCHandleDType() { return findBasicDType(AstBasicDTypeKwd::CHANDLE); }
|
||||
AstNodeDType* findEmptyQueueDType() const;
|
||||
AstNodeDType* findVoidDType() const;
|
||||
AstNodeDType* findQueueIndexDType() const;
|
||||
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
|
||||
@@ -1695,7 +1718,7 @@ public:
|
||||
VSigning numeric) const;
|
||||
AstNodeDType* findBitRangeDType(const VNumRange& range, int widthMin, VSigning numeric) const;
|
||||
AstNodeDType* findBasicDType(AstBasicDTypeKwd kwd) const;
|
||||
AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
|
||||
// METHODS - dump and error
|
||||
void v3errorEnd(std::ostringstream& str) const;
|
||||
@@ -1770,8 +1793,6 @@ public:
|
||||
virtual bool isGateOptimizable() const { return true; }
|
||||
// GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf)
|
||||
virtual bool isGateDedupable() const { return isGateOptimizable(); }
|
||||
// Needs verilated_heavy.h (uses std::string or some others)
|
||||
virtual bool isHeavy() const { return false; }
|
||||
// Else creates output or exits, etc, not unconsumed
|
||||
virtual bool isOutputter() const { return false; }
|
||||
// Else a AstTime etc which output can't be predicted from input
|
||||
@@ -1868,7 +1889,7 @@ public:
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual bool hasDType() const override { return true; }
|
||||
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
|
||||
// For documentation on emitC format see EmitCStmts::emitOpName
|
||||
// For documentation on emitC format see EmitCFunc::emitOpName
|
||||
virtual string emitC() = 0;
|
||||
virtual string emitSimpleOperator() { return ""; } // "" means not ok to use
|
||||
virtual bool emitCheckMaxWords() { return false; } // Check VL_MULS_MAX_WORDS
|
||||
@@ -2074,7 +2095,7 @@ public:
|
||||
virtual bool sizeMattersLhs() const override { return false; }
|
||||
virtual bool sizeMattersRhs() const override { return false; }
|
||||
virtual bool sizeMattersThs() const override { return false; }
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
virtual AstNode* cloneType(AstNode* condp, AstNode* expr1p, AstNode* expr2p) = 0;
|
||||
};
|
||||
|
||||
@@ -2209,7 +2230,7 @@ public:
|
||||
AstNode* incsp() const { return op3p(); } // op3 = increment statements
|
||||
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
|
||||
@@ -2236,7 +2257,7 @@ public:
|
||||
void addElsesp(AstNode* newp) { addOp3p(newp); }
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isGateDedupable() const override { return true; }
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
void branchPred(VBranchPred flag) { m_branchPred = flag; }
|
||||
VBranchPred branchPred() const { return m_branchPred; }
|
||||
@@ -2254,7 +2275,7 @@ protected:
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeCase)
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
AstNode* exprp() const { return op1p(); } // op1 = case condition <expression>
|
||||
AstCaseItem* itemsp() const {
|
||||
return VN_CAST(op2p(), CaseItem);
|
||||
@@ -2272,9 +2293,7 @@ private:
|
||||
AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
string m_name; // Name of variable
|
||||
string m_hiernameToProt; // Scope converted into name-> for emitting
|
||||
string m_hiernameToUnprot; // Scope converted into name-> for emitting
|
||||
bool m_hierThis = false; // Hiername points to "this" function
|
||||
string m_selfPointer; // Output code object pointer (e.g.: 'this')
|
||||
|
||||
protected:
|
||||
AstNodeVarRef(AstType t, FileLine* fl, const string& name, const VAccess& access)
|
||||
@@ -2306,13 +2325,9 @@ public:
|
||||
void varp(AstVar* varp);
|
||||
AstVarScope* varScopep() const { return m_varScopep; }
|
||||
void varScopep(AstVarScope* varscp) { m_varScopep = varscp; }
|
||||
string hiernameToProt() const { return m_hiernameToProt; }
|
||||
void hiernameToProt(const string& hn) { m_hiernameToProt = hn; }
|
||||
string hiernameToUnprot() const { return m_hiernameToUnprot; }
|
||||
void hiernameToUnprot(const string& hn) { m_hiernameToUnprot = hn; }
|
||||
string hiernameProtect() const;
|
||||
bool hierThis() const { return m_hierThis; }
|
||||
void hierThis(bool flag) { m_hierThis = flag; }
|
||||
string selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const string& value) { m_selfPointer = value; }
|
||||
string selfPointerProtect(bool useSelfForThis) const;
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
// Know no children, and hot function, so skip iterator for speed
|
||||
@@ -2439,6 +2454,7 @@ public:
|
||||
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
|
||||
}
|
||||
string cType(const string& name, bool forFunc, bool isRef) const;
|
||||
bool isLiteralType() const; // Does this represent a C++ LiteralType? (can be constexpr)
|
||||
|
||||
private:
|
||||
class CTypeRecursed;
|
||||
@@ -2615,8 +2631,6 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeStmt {
|
||||
// A call of a C++ function, perhaps a AstCFunc or perhaps globally named
|
||||
// Functions are not statements, while tasks are. AstNodeStmt needs isStatement() to deal.
|
||||
AstCFunc* m_funcp;
|
||||
string m_hiernameToProt;
|
||||
string m_hiernameToUnprot;
|
||||
string m_argTypes;
|
||||
|
||||
protected:
|
||||
@@ -2631,7 +2645,7 @@ public:
|
||||
virtual void dump(std::ostream& str = std::cout) const override;
|
||||
virtual void cloneRelink() override;
|
||||
virtual const char* broken() const override;
|
||||
virtual int instrCount() const override { return instrCountCall(); }
|
||||
virtual int instrCount() const override { return INSTR_COUNT_CALL; }
|
||||
virtual bool same(const AstNode* samep) const override {
|
||||
const AstNodeCCall* asamep = static_cast<const AstNodeCCall*>(samep);
|
||||
return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes());
|
||||
@@ -2642,11 +2656,6 @@ public:
|
||||
virtual bool isPure() const override;
|
||||
virtual bool isOutputter() const override { return !isPure(); }
|
||||
AstCFunc* funcp() const { return m_funcp; }
|
||||
string hiernameToProt() const { return m_hiernameToProt; }
|
||||
void hiernameToProt(const string& hn) { m_hiernameToProt = hn; }
|
||||
string hiernameToUnprot() const { return m_hiernameToUnprot; }
|
||||
void hiernameToUnprot(const string& hn) { m_hiernameToUnprot = hn; }
|
||||
string hiernameProtect() const;
|
||||
void argTypes(const string& str) { m_argTypes = str; }
|
||||
string argTypes() const { return m_argTypes; }
|
||||
// op1p reserved for AstCMethodCall
|
||||
@@ -2675,6 +2684,7 @@ private:
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
bool m_pure : 1; // DPI import pure (vs. virtual pure)
|
||||
bool m_pureVirtual : 1; // Pure virtual
|
||||
bool m_underGenerate : 1; // Under generate (for warning)
|
||||
bool m_virtual : 1; // Virtual method in class
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
protected:
|
||||
@@ -2697,6 +2707,7 @@ protected:
|
||||
, m_isHideProtected{false}
|
||||
, m_pure{false}
|
||||
, m_pureVirtual{false}
|
||||
, m_underGenerate{false}
|
||||
, m_virtual{false} {
|
||||
addNOp3p(stmtsp);
|
||||
cname(name); // Might be overridden by dpi import/export
|
||||
@@ -2763,6 +2774,8 @@ public:
|
||||
bool pure() const { return m_pure; }
|
||||
void pureVirtual(bool flag) { m_pureVirtual = flag; }
|
||||
bool pureVirtual() const { return m_pureVirtual; }
|
||||
void underGenerate(bool flag) { m_underGenerate = flag; }
|
||||
bool underGenerate() const { return m_underGenerate; }
|
||||
void isVirtual(bool flag) { m_virtual = flag; }
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
@@ -2842,7 +2855,6 @@ private:
|
||||
bool m_recursive : 1; // Recursive module
|
||||
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
|
||||
int m_level = 0; // 1=top module, 2=cell off top module, ...
|
||||
int m_varNum = 0; // Incrementing variable number
|
||||
int m_typeNum = 0; // Incrementing implicit type number
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
VTimescale m_timeunit; // Global time unit
|
||||
@@ -2883,7 +2895,6 @@ public:
|
||||
void level(int level) { m_level = level; }
|
||||
int level() const { return m_level; }
|
||||
bool isTop() const { return level() == 1; }
|
||||
int varNumGetInc() { return ++m_varNum; }
|
||||
int typeNumGetInc() { return ++m_typeNum; }
|
||||
void modPublic(bool flag) { m_modPublic = flag; }
|
||||
bool modPublic() const { return m_modPublic; }
|
||||
|
||||
+210
-41
@@ -18,16 +18,18 @@
|
||||
#include "verilatedos.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
|
||||
#include "V3String.h"
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
#include "V3AstNodes__gen_macros.h" // Generated by 'astgen'
|
||||
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
//======================================================================
|
||||
@@ -55,8 +57,19 @@ void AstNodeVarRef::cloneRelink() {
|
||||
if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep();
|
||||
}
|
||||
|
||||
string AstNodeVarRef::hiernameProtect() const {
|
||||
return hiernameToUnprot() + VIdProtect::protectWordsIf(hiernameToProt(), protect());
|
||||
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();
|
||||
}
|
||||
|
||||
const char* AstAddrOfCFunc::broken() const {
|
||||
BROKEN_RTN(m_funcp && !m_funcp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int AstNodeSel::bitConst() const {
|
||||
@@ -108,8 +121,11 @@ const char* AstNodeCCall::broken() const {
|
||||
return nullptr;
|
||||
}
|
||||
bool AstNodeCCall::isPure() const { return funcp()->pure(); }
|
||||
string AstNodeCCall::hiernameProtect() const {
|
||||
return hiernameToUnprot() + VIdProtect::protectWordsIf(hiernameToProt(), protect());
|
||||
|
||||
string AstCCall::selfPointerProtect(bool useSelfForThis) const {
|
||||
const string& sp
|
||||
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
|
||||
return VIdProtect::protectWordsIf(sp, protect());
|
||||
}
|
||||
|
||||
void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
|
||||
@@ -216,9 +232,9 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
}
|
||||
|
||||
AstExecGraph::AstExecGraph(FileLine* fileline)
|
||||
: ASTGEN_SUPER_ExecGraph(fileline) {
|
||||
m_depGraphp = new V3Graph;
|
||||
}
|
||||
: ASTGEN_SUPER_ExecGraph(fileline)
|
||||
, m_depGraphp{new V3Graph} {}
|
||||
|
||||
AstExecGraph::~AstExecGraph() { VL_DO_DANGLING(delete m_depGraphp, m_depGraphp); }
|
||||
|
||||
AstNode* AstInsideRange::newAndFromInside(AstNode* exprp, AstNode* lhsp, AstNode* rhsp) {
|
||||
@@ -252,7 +268,7 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
|
||||
char* endp;
|
||||
int v = strtol(literal.c_str(), &endp, 0);
|
||||
if ((v != 0) && (endp[0] == 0)) { // C literal
|
||||
return new AstConst(fl, AstConst::WidthedValue(), 32, v);
|
||||
return new AstConst(fl, AstConst::Signed32(), v);
|
||||
} else { // Try a Verilog literal (fatals if not)
|
||||
return new AstConst(fl, AstConst::StringToParse(), literal.c_str());
|
||||
}
|
||||
@@ -260,8 +276,16 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNetlist::AstNetlist()
|
||||
: ASTGEN_SUPER_Netlist(new FileLine(FileLine::builtInFilename()))
|
||||
, m_typeTablep{new AstTypeTable(fileline())}
|
||||
, m_constPoolp{new AstConstPool(fileline())} {
|
||||
addMiscsp(m_typeTablep);
|
||||
addMiscsp(m_constPoolp);
|
||||
}
|
||||
|
||||
void AstNetlist::timeprecisionMerge(FileLine*, const VTimescale& value) {
|
||||
VTimescale prec = v3Global.opt.timeComputePrec(value);
|
||||
const VTimescale prec = v3Global.opt.timeComputePrec(value);
|
||||
if (prec.isNone() || prec == m_timeprecision) {
|
||||
} else if (m_timeprecision.isNone()) {
|
||||
m_timeprecision = prec;
|
||||
@@ -314,13 +338,15 @@ string AstVar::verilogKwd() const {
|
||||
}
|
||||
}
|
||||
|
||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc) const {
|
||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
|
||||
bool asRef) const {
|
||||
UASSERT_OBJ(!forReturn, this,
|
||||
"Internal data is never passed as return, but as first argument");
|
||||
string ostatic;
|
||||
if (isStatic() && namespc.empty()) ostatic = "static ";
|
||||
|
||||
bool isRef = isDpiOpenArray() || (forFunc && (isWritable() || direction().isRefOrConstRef()));
|
||||
const bool isRef = isDpiOpenArray()
|
||||
|| (forFunc && (isWritable() || direction().isRefOrConstRef())) || asRef;
|
||||
|
||||
if (forFunc && isReadOnly() && isRef) ostatic = ostatic + "const ";
|
||||
|
||||
@@ -335,7 +361,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
||||
string AstVar::vlEnumType() const {
|
||||
string arg;
|
||||
AstBasicDType* bdtypep = basicp();
|
||||
bool strtype = bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::STRING;
|
||||
const bool strtype = bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::STRING;
|
||||
if (bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::CHARPTR) {
|
||||
return "VLVT_PTR";
|
||||
} else if (bdtypep && bdtypep->keyword() == AstBasicDTypeKwd::SCOPEPTR) {
|
||||
@@ -610,13 +636,6 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
string AstVar::mtasksString() const {
|
||||
std::ostringstream os;
|
||||
os << "all: ";
|
||||
for (const auto& id : m_mtaskIds) os << id << " ";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
class AstNodeDType::CTypeRecursed final {
|
||||
public:
|
||||
string m_type; // The base type, e.g.: "Foo_t"s
|
||||
@@ -624,15 +643,22 @@ public:
|
||||
string render(const string& name, bool isRef) const {
|
||||
string out;
|
||||
out += m_type;
|
||||
if (name != "") out += " ";
|
||||
out += isRef ? "(&" + name + ")" : name;
|
||||
if (!name.empty()) out += " ";
|
||||
if (isRef) {
|
||||
if (!m_dims.empty()) out += "(";
|
||||
out += "&";
|
||||
out += name;
|
||||
if (!m_dims.empty()) out += ")";
|
||||
} else {
|
||||
out += name;
|
||||
}
|
||||
out += m_dims;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
string AstNodeDType::cType(const string& name, bool forFunc, bool isRef) const {
|
||||
CTypeRecursed info = cTypeRecurse(false);
|
||||
const CTypeRecursed info = cTypeRecurse(false);
|
||||
return info.render(name, isRef);
|
||||
}
|
||||
|
||||
@@ -672,10 +698,12 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
info.m_type = "const char*";
|
||||
} else if (bdtypep->keyword() == AstBasicDTypeKwd::SCOPEPTR) {
|
||||
info.m_type = "const VerilatedScope*";
|
||||
} else if (bdtypep->keyword() == AstBasicDTypeKwd::DOUBLE) {
|
||||
} else if (bdtypep->keyword().isDouble()) {
|
||||
info.m_type = "double";
|
||||
} else if (bdtypep->keyword() == AstBasicDTypeKwd::STRING) {
|
||||
} else if (bdtypep->keyword().isString()) {
|
||||
info.m_type = "std::string";
|
||||
} else if (bdtypep->keyword().isMTaskState()) {
|
||||
info.m_type = "VlMTaskVertex";
|
||||
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
|
||||
info.m_type = "CData" + bitvec;
|
||||
} else if (dtypep->widthMin() <= 16) {
|
||||
@@ -748,6 +776,20 @@ int AstNodeDType::widthPow2() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool AstNodeDType::isLiteralType() const {
|
||||
if (auto* const dtypep = VN_CAST_CONST(skipRefp(), BasicDType)) {
|
||||
return dtypep->keyword().isLiteralType();
|
||||
} else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), UnpackArrayDType)) {
|
||||
return dtypep->basicp()->isLiteralType();
|
||||
} else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), StructDType)) {
|
||||
// Currently all structs are packed, later this can be expanded to
|
||||
// 'forall members _.isLiteralType()'
|
||||
return dtypep->packed();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// What is the base variable (or const) this dereferences?
|
||||
AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
|
||||
// Else AstArraySel etc; search for the base
|
||||
@@ -864,6 +906,11 @@ bool AstSenTree::hasCombo() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
AstTypeTable::AstTypeTable(FileLine* fl)
|
||||
: ASTGEN_SUPER_TypeTable(fl) {
|
||||
for (int i = 0; i < AstBasicDTypeKwd::_ENUM_MAX; ++i) m_basicps[i] = nullptr;
|
||||
}
|
||||
|
||||
void AstTypeTable::clearCache() {
|
||||
// When we mass-change widthMin in V3WidthCommit, we need to correct the table.
|
||||
// Just clear out the maps; the search functions will be used to rebuild the map
|
||||
@@ -885,9 +932,18 @@ void AstTypeTable::repairCache() {
|
||||
}
|
||||
}
|
||||
|
||||
AstEmptyQueueDType* AstTypeTable::findEmptyQueueDType(FileLine* fl) {
|
||||
if (VL_UNLIKELY(!m_emptyQueuep)) {
|
||||
AstEmptyQueueDType* newp = new AstEmptyQueueDType{fl};
|
||||
addTypesp(newp);
|
||||
m_emptyQueuep = newp;
|
||||
}
|
||||
return m_emptyQueuep;
|
||||
}
|
||||
|
||||
AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
|
||||
if (VL_UNLIKELY(!m_voidp)) {
|
||||
AstVoidDType* newp = new AstVoidDType(fl);
|
||||
AstVoidDType* newp = new AstVoidDType{fl};
|
||||
addTypesp(newp);
|
||||
m_voidp = newp;
|
||||
}
|
||||
@@ -958,6 +1014,115 @@ AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) {
|
||||
return nodep;
|
||||
}
|
||||
|
||||
AstConstPool::AstConstPool(FileLine* fl)
|
||||
: ASTGEN_SUPER_ConstPool(fl)
|
||||
, m_modp{new AstModule(fl, "@CONST-POOL@")}
|
||||
, m_scopep{new AstScope(fl, m_modp, "@CONST-POOL@", nullptr, nullptr)} {
|
||||
addOp1p(m_modp);
|
||||
m_modp->addStmtp(m_scopep);
|
||||
}
|
||||
|
||||
AstVarScope* AstConstPool::createNewEntry(const string& name, AstNode* initp) {
|
||||
FileLine* const fl = initp->fileline();
|
||||
AstVar* const varp = new AstVar(fl, AstVarType::MODULETEMP, name, initp->dtypep());
|
||||
varp->isConst(true);
|
||||
varp->isStatic(true);
|
||||
varp->valuep(initp->cloneTree(false));
|
||||
m_modp->addStmtp(varp);
|
||||
AstVarScope* const varScopep = new AstVarScope(fl, m_scopep, varp);
|
||||
m_scopep->addVarp(varScopep);
|
||||
return varScopep;
|
||||
}
|
||||
|
||||
static bool sameInit(const AstInitArray* ap, const AstInitArray* bp) {
|
||||
// Unpacked array initializers must have equivalent values
|
||||
// Note, sadly we can't just call ap->sameTree(pb), because both:
|
||||
// - the dtypes might be different instances
|
||||
// - the default/inititem children might be in different order yet still yield the same table
|
||||
// See note in AstInitArray::same about the same. This function instead compares by initializer
|
||||
// value, rather than by tree structure.
|
||||
const AstUnpackArrayDType* const aDTypep = VN_CAST(ap->dtypep(), UnpackArrayDType);
|
||||
const AstUnpackArrayDType* const bDTypep = VN_CAST(bp->dtypep(), UnpackArrayDType);
|
||||
UASSERT_STATIC(aDTypep, "Bad type in array initializer");
|
||||
UASSERT_STATIC(bDTypep, "Bad type in array initializer");
|
||||
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) { // Element types differ
|
||||
return false;
|
||||
}
|
||||
if (!aDTypep->rangep()->sameTree(bDTypep->rangep())) { // Ranges differ
|
||||
return false;
|
||||
}
|
||||
// Compare initializer arrays by value. Note this is only called when they hash the same,
|
||||
// so they likely run at most once per call to 'AstConstPool::findTable'.
|
||||
const uint32_t size = aDTypep->elementsConst();
|
||||
for (uint32_t n = 0; n < size; ++n) {
|
||||
const AstNode* const valAp = ap->getIndexDefaultedValuep(n);
|
||||
const AstNode* const valBp = bp->getIndexDefaultedValuep(n);
|
||||
if (!valAp->sameTree(valBp)) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
AstVarScope* AstConstPool::findTable(AstInitArray* initp) {
|
||||
AstNode* const defaultp = initp->defaultp();
|
||||
// Verify initializer is well formed
|
||||
UASSERT_OBJ(VN_IS(initp->dtypep(), UnpackArrayDType), initp,
|
||||
"Const pool table must have AstUnpackArrayDType dtype");
|
||||
UASSERT_OBJ(!defaultp || VN_IS(defaultp, Const), initp,
|
||||
"Const pool table default must be Const");
|
||||
for (AstNode* nodep = initp->initsp(); nodep; nodep = nodep->nextp()) {
|
||||
AstNode* const valuep = VN_CAST(nodep, InitItem)->valuep();
|
||||
UASSERT_OBJ(VN_IS(valuep, Const), valuep, "Const pool table entry must be Const");
|
||||
}
|
||||
// Try to find an existing table with the same content
|
||||
const V3Hash hash = V3Hasher::uncachedHash(initp);
|
||||
const auto& er = m_tables.equal_range(hash.value());
|
||||
for (auto it = er.first; it != er.second; ++it) {
|
||||
AstVarScope* const varScopep = it->second;
|
||||
const AstInitArray* const init2p = VN_CAST(varScopep->varp()->valuep(), InitArray);
|
||||
if (sameInit(initp, init2p)) {
|
||||
return varScopep; // Found identical table
|
||||
}
|
||||
}
|
||||
// No such table yet, create it.
|
||||
string name = "TABLE_";
|
||||
name += hash.toString();
|
||||
name += "_";
|
||||
name += cvtToStr(std::distance(er.first, er.second));
|
||||
AstVarScope* const varScopep = createNewEntry(name, initp);
|
||||
m_tables.emplace(hash.value(), varScopep);
|
||||
return varScopep;
|
||||
}
|
||||
|
||||
static bool sameInit(const AstConst* ap, const AstConst* bp) {
|
||||
// Similarly to the AstInitArray comparison, we ignore the dtype instance, so long as they
|
||||
// are compatible. For now, we assume the dtype is not relevant, as we only call this from
|
||||
// V3Prelim which is a late pass.
|
||||
// Compare initializers by value. This checks widths as well.
|
||||
return ap->num().isCaseEq(bp->num());
|
||||
}
|
||||
|
||||
AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
|
||||
// Try to find an existing constant with the same value
|
||||
const V3Hash hash = initp->num().toHash();
|
||||
const auto& er = m_consts.equal_range(hash.value());
|
||||
for (auto it = er.first; it != er.second; ++it) {
|
||||
AstVarScope* const varScopep = it->second;
|
||||
const AstConst* const init2p = VN_CAST(varScopep->varp()->valuep(), Const);
|
||||
if (sameInit(initp, init2p)
|
||||
&& (mergeDType || varScopep->dtypep()->sameTree(initp->dtypep()))) {
|
||||
return varScopep; // Found identical constant
|
||||
}
|
||||
}
|
||||
// No such constant yet, create it.
|
||||
string name = "CONST_";
|
||||
name += hash.toString();
|
||||
name += "_";
|
||||
name += cvtToStr(std::distance(er.first, er.second));
|
||||
AstVarScope* const varScopep = createNewEntry(name, initp);
|
||||
m_consts.emplace(hash.value(), varScopep);
|
||||
return varScopep;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Special walking tree inserters
|
||||
|
||||
@@ -1119,8 +1284,9 @@ const char* AstClassPackage::broken() const {
|
||||
return nullptr;
|
||||
}
|
||||
void AstClass::insertCache(AstNode* nodep) {
|
||||
bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_CAST(nodep, NodeFTask)->isExternProto()));
|
||||
const bool doit
|
||||
= (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_CAST(nodep, NodeFTask)->isExternProto()));
|
||||
if (doit) {
|
||||
if (m_members.find(nodep->name()) != m_members.end()) {
|
||||
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
|
||||
@@ -1316,10 +1482,10 @@ void AstRefDType::dump(std::ostream& str) const {
|
||||
if (!s_recursing) { // Prevent infinite dump if circular typedefs
|
||||
s_recursing = true;
|
||||
str << " -> ";
|
||||
if (typedefp()) {
|
||||
typedefp()->dump(str);
|
||||
} else if (subDTypep()) {
|
||||
subDTypep()->dump(str);
|
||||
if (const auto subp = typedefp()) {
|
||||
subp->dump(str);
|
||||
} else if (const auto subp = subDTypep()) {
|
||||
subp->dump(str);
|
||||
}
|
||||
s_recursing = false;
|
||||
}
|
||||
@@ -1364,7 +1530,8 @@ void AstNodeArrayDType::dump(std::ostream& str) const {
|
||||
}
|
||||
string AstPackArrayDType::prettyDTypeName() const {
|
||||
std::ostringstream os;
|
||||
os << subDTypep()->prettyDTypeName() << declRange();
|
||||
if (const auto subp = subDTypep()) os << subp->prettyDTypeName();
|
||||
os << declRange();
|
||||
return os.str();
|
||||
}
|
||||
string AstUnpackArrayDType::prettyDTypeName() const {
|
||||
@@ -1484,6 +1651,10 @@ void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[]";
|
||||
}
|
||||
void AstEmptyQueueDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "emptyq";
|
||||
}
|
||||
void AstVoidDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "void";
|
||||
@@ -1679,14 +1850,12 @@ void AstCFunc::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (slow()) str << " [SLOW]";
|
||||
if (pure()) str << " [PURE]";
|
||||
if (isStatic().unknown()) {
|
||||
str << " [STATICU]";
|
||||
} else if (isStatic().trueUnknown()) {
|
||||
str << " [STATIC]";
|
||||
}
|
||||
if (dpiImport()) str << " [DPII]";
|
||||
if (dpiExport()) str << " [DPIX]";
|
||||
if (dpiExportWrapper()) str << " [DPIXWR]";
|
||||
if (isStatic()) str << " [STATIC]";
|
||||
if (dpiExportDispatcher()) str << " [DPIED]";
|
||||
if (dpiExportImpl()) str << " [DPIEI]";
|
||||
if (dpiImportPrototype()) str << " [DPIIP]";
|
||||
if (dpiImportWrapper()) str << " [DPIIW]";
|
||||
if (dpiContext()) str << " [DPICTX]";
|
||||
if (isConstructor()) str << " [CTOR]";
|
||||
if (isDestructor()) str << " [DTOR]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
|
||||
+262
-160
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,126 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Utility to hang advanced data structures of
|
||||
// AstNode::user*p() pointers with automatic memory management.
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2021 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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3ASTUSERALLOCATOR_H_
|
||||
#define VERILATOR_V3ASTUSERALLOCATOR_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
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(std::is_base_of<AstNode, T_Node>::value, "T_Node must be an AstNode type");
|
||||
|
||||
private:
|
||||
std::vector<T_Data*> m_allocated;
|
||||
|
||||
inline T_Data* getUserp(const T_Node* nodep) const {
|
||||
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
|
||||
if (T_UserN == 1) {
|
||||
const VNUser user = nodep->user1u();
|
||||
return user.to<T_Data*>();
|
||||
} else if (T_UserN == 2) {
|
||||
const VNUser user = nodep->user2u();
|
||||
return user.to<T_Data*>();
|
||||
} else if (T_UserN == 3) {
|
||||
const VNUser user = nodep->user3u();
|
||||
return user.to<T_Data*>();
|
||||
} else if (T_UserN == 4) {
|
||||
const VNUser user = nodep->user4u();
|
||||
return user.to<T_Data*>();
|
||||
} else {
|
||||
const VNUser user = nodep->user5u();
|
||||
return user.to<T_Data*>();
|
||||
}
|
||||
}
|
||||
|
||||
inline void setUserp(T_Node* nodep, T_Data* userp) const {
|
||||
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
|
||||
if (T_UserN == 1) {
|
||||
nodep->user1u(VNUser(userp));
|
||||
} else if (T_UserN == 2) {
|
||||
nodep->user2u(VNUser(userp));
|
||||
} else if (T_UserN == 3) {
|
||||
nodep->user3u(VNUser(userp));
|
||||
} else if (T_UserN == 4) {
|
||||
nodep->user4u(VNUser(userp));
|
||||
} else {
|
||||
nodep->user5u(VNUser(userp));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
AstUserAllocatorBase() {
|
||||
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
|
||||
if (T_UserN == 1) {
|
||||
AstUser1InUse::check();
|
||||
} else if (T_UserN == 2) {
|
||||
AstUser2InUse::check();
|
||||
} else if (T_UserN == 3) {
|
||||
AstUser3InUse::check();
|
||||
} else if (T_UserN == 4) {
|
||||
AstUser4InUse::check();
|
||||
} else {
|
||||
AstUser5InUse::check();
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~AstUserAllocatorBase() {
|
||||
// Delete all allocated data structures
|
||||
for (T_Data* const p : m_allocated) { delete p; }
|
||||
}
|
||||
|
||||
public:
|
||||
// Get a reference to the user data
|
||||
T_Data& operator()(T_Node* nodep) {
|
||||
T_Data* userp = getUserp(nodep);
|
||||
if (!userp) {
|
||||
userp = new T_Data;
|
||||
m_allocated.push_back(userp);
|
||||
setUserp(nodep, userp);
|
||||
}
|
||||
return *userp;
|
||||
}
|
||||
|
||||
// Get a reference to the user data
|
||||
T_Data& operator()(const T_Node* nodep) {
|
||||
T_Data* userp = getUserp(nodep);
|
||||
UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode");
|
||||
return *userp;
|
||||
}
|
||||
};
|
||||
|
||||
// User pointer allocator classes. T_Node is the type of node the allocator should be applied to
|
||||
// and is simply there for a bit of extra type safety. T_Data is the type of the data structure
|
||||
// managed by the allocator.
|
||||
template <class T_Node, class T_Data>
|
||||
class AstUser1Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 1> {};
|
||||
template <class T_Node, class T_Data>
|
||||
class AstUser2Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 2> {};
|
||||
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
|
||||
+32
-17
@@ -62,6 +62,7 @@ private:
|
||||
BeginState* m_statep; // Current global state
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
AstNode* m_liftedp = nullptr; // Local nodes we are lifting into m_ftaskp
|
||||
string m_namedScope; // Name of begin blocks above us
|
||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
int m_ifDepth = 0; // Current if depth
|
||||
@@ -74,6 +75,21 @@ private:
|
||||
return a + "__DOT__" + b;
|
||||
}
|
||||
|
||||
void liftNode(AstNode* nodep) {
|
||||
nodep->unlinkFrBack();
|
||||
if (m_ftaskp) {
|
||||
// AstBegin under ftask, just move into the ftask
|
||||
if (!m_liftedp) {
|
||||
m_liftedp = nodep;
|
||||
} else {
|
||||
m_liftedp->addNext(nodep);
|
||||
}
|
||||
} else {
|
||||
// Move to module
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
@@ -101,7 +117,17 @@ private:
|
||||
m_namedScope = "";
|
||||
m_unnamedScope = "";
|
||||
m_ftaskp = nodep;
|
||||
m_liftedp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
if (m_liftedp) {
|
||||
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
|
||||
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
m_liftedp->addNext(stmtsp);
|
||||
}
|
||||
nodep->addStmtsp(m_liftedp);
|
||||
m_liftedp = nullptr;
|
||||
}
|
||||
m_ftaskp = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -117,7 +143,7 @@ private:
|
||||
string dottedname = nodep->name() + "__DOT__"; // So always found
|
||||
string::size_type pos;
|
||||
while ((pos = dottedname.find("__DOT__")) != string::npos) {
|
||||
string ident = dottedname.substr(0, pos);
|
||||
const string ident = dottedname.substr(0, pos);
|
||||
dottedname = dottedname.substr(pos + strlen("__DOT__"));
|
||||
if (nodep->name() != "") m_namedScope = dot(m_namedScope, ident);
|
||||
m_unnamedScope = dot(m_unnamedScope, ident);
|
||||
@@ -157,13 +183,8 @@ private:
|
||||
// Rename it
|
||||
nodep->name(dot(m_unnamedScope, nodep->name()));
|
||||
m_statep->userMarkChanged(nodep);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
if (m_ftaskp) {
|
||||
m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
|
||||
} else {
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
// Move it under enclosing tree
|
||||
liftNode(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) override {
|
||||
@@ -171,14 +192,8 @@ private:
|
||||
// Rename it
|
||||
nodep->name(dot(m_unnamedScope, nodep->name()));
|
||||
m_statep->userMarkChanged(nodep);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
// Begins under funcs just move into the func
|
||||
if (m_ftaskp) {
|
||||
m_ftaskp->addStmtsp(nodep);
|
||||
} else {
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
// Move it under enclosing tree
|
||||
liftNode(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
@@ -293,7 +308,7 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{
|
||||
BeginState state;
|
||||
{ BeginVisitor bvisitor(nodep, &state); }
|
||||
{ BeginVisitor bvisitor{nodep, &state}; }
|
||||
if (state.anyFuncInBegin()) { BeginRelinkVisitor brvisitor(nodep, &state); }
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("begin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
|
||||
+8
-8
@@ -43,8 +43,8 @@ private:
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
int m_likely; // Excuses for branch likely taken
|
||||
int m_unlikely; // Excuses for branch likely not taken
|
||||
int m_likely = false; // Excuses for branch likely taken
|
||||
int m_unlikely = false; // Excuses for branch likely not taken
|
||||
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
|
||||
|
||||
// METHODS
|
||||
@@ -70,15 +70,15 @@ private:
|
||||
// Do if
|
||||
reset();
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
int ifLikely = m_likely;
|
||||
int ifUnlikely = m_unlikely;
|
||||
const int ifLikely = m_likely;
|
||||
const int ifUnlikely = m_unlikely;
|
||||
// Do else
|
||||
reset();
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
int elseLikely = m_likely;
|
||||
int elseUnlikely = m_unlikely;
|
||||
const int elseLikely = m_likely;
|
||||
const int elseUnlikely = m_unlikely;
|
||||
// Compute
|
||||
int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
|
||||
const int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely);
|
||||
if (likeness > 0) {
|
||||
nodep->branchPred(VBranchPred::BP_LIKELY);
|
||||
} else if (likeness < 0) {
|
||||
@@ -123,5 +123,5 @@ public:
|
||||
|
||||
void V3Branch::branchAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
BranchVisitor visitor(nodep);
|
||||
BranchVisitor visitor{nodep};
|
||||
}
|
||||
|
||||
+205
-182
@@ -18,6 +18,7 @@
|
||||
// Entire netlist
|
||||
// Mark all nodes
|
||||
// Check all links point to marked nodes
|
||||
// Check local variables in CFuncs appear before they are referenced
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -31,201 +32,129 @@
|
||||
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
|
||||
#include "V3AstConstOnly.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
//######################################################################
|
||||
// Generation counter for AstNode::m_brokenState
|
||||
|
||||
class BrokenTable VL_NOT_FINAL : public AstNVisitor {
|
||||
// Table of brokenExists node pointers
|
||||
static class BrokenCntGlobal {
|
||||
// This is a 7 bit generation counter, stored in the bottom 7 bits of AstNode::m_brokenState,
|
||||
// used to mark a node as being present under the root AstNetlist in the current traversal. A
|
||||
// value 0 is invalid, as the AstNode constructor uses that to initialize m_brokenState
|
||||
static constexpr uint8_t MIN_VALUE = 1;
|
||||
static constexpr uint8_t MAX_VALUE = 127;
|
||||
|
||||
uint8_t m_count = MIN_VALUE;
|
||||
|
||||
public:
|
||||
uint8_t get() {
|
||||
UASSERT(MIN_VALUE <= m_count && m_count <= MAX_VALUE, "Invalid generation number");
|
||||
return m_count;
|
||||
}
|
||||
|
||||
void inc() {
|
||||
++m_count;
|
||||
if (m_count > MAX_VALUE) m_count = MIN_VALUE;
|
||||
}
|
||||
} s_brokenCntGlobal;
|
||||
|
||||
//######################################################################
|
||||
// Table of allocated AstNode pointers
|
||||
|
||||
static class AllocTable final {
|
||||
private:
|
||||
// MEMBERS
|
||||
// For each node, we keep if it exists or not.
|
||||
using NodeMap = std::unordered_map<const AstNode*, int>; // Performance matters (when --debug)
|
||||
static NodeMap s_nodes; // Set of all nodes that exist
|
||||
// BITMASK
|
||||
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
|
||||
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
|
||||
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
|
||||
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
|
||||
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
|
||||
std::unordered_set<const AstNode*> m_allocated; // Set of all nodes allocated but not freed
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
static void deleted(const AstNode* nodep) {
|
||||
// Called by operator delete on any node - only if VL_LEAK_CHECKS
|
||||
if (debug() >= 9) cout << "-nodeDel: " << cvtToHex(nodep) << endl;
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
UASSERT_OBJ(!(iter == s_nodes.end() || !(iter->second & FLAG_ALLOCATED)),
|
||||
reinterpret_cast<const AstNode*>(nodep),
|
||||
"Deleting AstNode object that was never tracked or already deleted");
|
||||
if (iter != s_nodes.end()) s_nodes.erase(iter);
|
||||
}
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
|
||||
// GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
static void addNewed(const AstNode* nodep) {
|
||||
void addNewed(const AstNode* nodep) {
|
||||
// Called by operator new on any node - only if VL_LEAK_CHECKS
|
||||
if (debug() >= 9) cout << "-nodeNew: " << cvtToHex(nodep) << endl;
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
UASSERT_OBJ(!(iter != s_nodes.end() && (iter->second & FLAG_ALLOCATED)), nodep,
|
||||
"Newing AstNode object that is already allocated");
|
||||
if (iter == s_nodes.end()) {
|
||||
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
|
||||
s_nodes.emplace(nodep, flags);
|
||||
// LCOV_EXCL_START
|
||||
if (VL_UNCOVERABLE(!m_allocated.emplace(nodep).second)) {
|
||||
nodep->v3fatalSrc("Newing AstNode object that is already allocated");
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
static void setUnder(const AstNode* nodep, bool flag) {
|
||||
// Called by BrokenCheckVisitor when each node entered/exited
|
||||
if (!okIfLinkedTo(nodep)) return;
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (iter != s_nodes.end()) {
|
||||
iter->second &= ~FLAG_UNDER_NOW;
|
||||
if (flag) iter->second |= FLAG_UNDER_NOW;
|
||||
void deleted(const AstNode* nodep) {
|
||||
// Called by operator delete on any node - only if VL_LEAK_CHECKS
|
||||
// LCOV_EXCL_START
|
||||
if (VL_UNCOVERABLE(m_allocated.erase(nodep) == 0)) {
|
||||
nodep->v3fatalSrc("Deleting AstNode object that was not allocated or already freed");
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
static void addInTree(AstNode* nodep, bool linkable) {
|
||||
#ifndef VL_LEAK_CHECKS
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!linkable) return; // save some time, else the map will get huge!
|
||||
#endif
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (VL_UNCOVERABLE(iter == s_nodes.end())) {
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
nodep->v3fatalSrc("AstNode is in tree, but not allocated");
|
||||
#endif
|
||||
} else {
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
UASSERT_OBJ(iter->second & FLAG_ALLOCATED, nodep,
|
||||
"AstNode is in tree, but not allocated");
|
||||
#endif
|
||||
UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep,
|
||||
"AstNode is already in tree at another location");
|
||||
}
|
||||
int or_flags = FLAG_IN_TREE | (linkable ? FLAG_LINKABLE : 0);
|
||||
if (iter == s_nodes.end()) {
|
||||
s_nodes.emplace(nodep, or_flags);
|
||||
} else {
|
||||
iter->second |= or_flags;
|
||||
}
|
||||
}
|
||||
static bool isAllocated(const AstNode* nodep) {
|
||||
// Some generic node has a pointer to this node. Is it allocated?
|
||||
// Use this when might not be in tree; otherwise use okIfLinkedTo().
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
if (!(iter->second & FLAG_ALLOCATED)) return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
static bool okIfLinkedTo(const AstNode* nodep) {
|
||||
// Some node in tree has a pointer to this node. Is it kosher?
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
if (!(iter->second & FLAG_ALLOCATED)) return false;
|
||||
#endif
|
||||
if (!(iter->second & FLAG_IN_TREE)) return false;
|
||||
if (!(iter->second & FLAG_LINKABLE)) return false;
|
||||
return true;
|
||||
}
|
||||
static bool okIfAbove(const AstNode* nodep) {
|
||||
// Must be linked to and below current node
|
||||
if (!okIfLinkedTo(nodep)) return false;
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
if ((iter->second & FLAG_UNDER_NOW)) return false;
|
||||
return true;
|
||||
}
|
||||
static bool okIfBelow(const AstNode* nodep) {
|
||||
// Must be linked to and below current node
|
||||
if (!okIfLinkedTo(nodep)) return false;
|
||||
const auto iter = s_nodes.find(nodep);
|
||||
if (iter == s_nodes.end()) return false;
|
||||
if (!(iter->second & FLAG_UNDER_NOW)) return false;
|
||||
return true;
|
||||
}
|
||||
static void prepForTree() {
|
||||
#ifndef VL_LEAK_CHECKS
|
||||
s_nodes.clear();
|
||||
#else
|
||||
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
||||
it->second &= ~FLAG_IN_TREE;
|
||||
it->second &= ~FLAG_LINKABLE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
static void doneWithTree() {
|
||||
for (int backs = 0; backs < 2;
|
||||
backs++) { // Those with backp() are probably under one leaking without
|
||||
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
||||
// LCOV_EXCL_START
|
||||
if (VL_UNCOVERABLE((it->second & FLAG_ALLOCATED) && !(it->second & FLAG_IN_TREE)
|
||||
&& !(it->second & FLAG_LEAKED)
|
||||
&& (it->first->backp() ? backs == 1 : backs == 0))) {
|
||||
bool isAllocated(const AstNode* nodep) const { return m_allocated.count(nodep) != 0; }
|
||||
void checkForLeaks() {
|
||||
if (!v3Global.opt.debugCheck()) return;
|
||||
|
||||
const uint8_t brokenCntCurrent = s_brokenCntGlobal.get();
|
||||
|
||||
// Those with backp() are probably under a parent that was leaked and has no backp()
|
||||
for (const bool withBack : {false, true}) {
|
||||
for (const AstNode* const nodep : m_allocated) {
|
||||
// LCOV_EXCL_START
|
||||
// Most likely not leaked, so check that first
|
||||
if (VL_UNCOVERABLE(nodep->brokenState() != brokenCntCurrent)) {
|
||||
const bool hasBack = nodep->backp() != nullptr;
|
||||
if (hasBack != withBack) continue;
|
||||
// Use only AstNode::dump instead of the virtual one, as there
|
||||
// may be varp() and other cross links that are bad.
|
||||
if (v3Global.opt.debugCheck()) {
|
||||
// When get this message, find what forgot to delete the
|
||||
// node by running GDB, where for node "<e###>" use:
|
||||
// watch AstNode::s_editCntGbl==####
|
||||
// run
|
||||
// bt
|
||||
std::cerr << "%Error: LeakedNode"
|
||||
<< (it->first->backp() ? "Back: " : ": ");
|
||||
AstNode* rawp
|
||||
= const_cast<AstNode*>(static_cast<const AstNode*>(it->first));
|
||||
rawp->AstNode::dump(std::cerr);
|
||||
std::cerr << endl;
|
||||
V3Error::incErrors();
|
||||
}
|
||||
it->second |= FLAG_LEAKED;
|
||||
// When get this message, find what forgot to delete the
|
||||
// node by running GDB, where for node "<e###>" use:
|
||||
// watch AstNode::s_editCntGbl==####
|
||||
// run
|
||||
// bt
|
||||
std::cerr << "%Error: LeakedNode" << (withBack ? "with back pointer: " : ": ");
|
||||
nodep->AstNode::dump(std::cerr);
|
||||
std::cerr << endl;
|
||||
V3Error::incErrors();
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
}
|
||||
} s_allocTable;
|
||||
|
||||
// CONSTRUCTORS
|
||||
BrokenTable() = default;
|
||||
virtual ~BrokenTable() override = default;
|
||||
};
|
||||
|
||||
BrokenTable::NodeMap BrokenTable::s_nodes;
|
||||
|
||||
bool AstNode::brokeExists() const {
|
||||
// Called by node->broken() routines to do table lookup
|
||||
return BrokenTable::okIfLinkedTo(this);
|
||||
}
|
||||
bool AstNode::brokeExistsAbove() const {
|
||||
// Called by node->broken() routines to do table lookup
|
||||
return BrokenTable::okIfBelow(this);
|
||||
}
|
||||
bool AstNode::brokeExistsBelow() const {
|
||||
// Called by node->broken() routines to do table lookup
|
||||
return BrokenTable::okIfAbove(this);
|
||||
}
|
||||
void V3Broken::addNewed(const AstNode* nodep) { s_allocTable.addNewed(nodep); }
|
||||
void V3Broken::deleted(const AstNode* nodep) { s_allocTable.deleted(nodep); }
|
||||
|
||||
//######################################################################
|
||||
// Table of AstNode pointers that can be linked to via member pointers
|
||||
|
||||
static class LinkableTable final {
|
||||
private:
|
||||
// MEMBERS
|
||||
std::unordered_set<const AstNode*> m_linkable; // Set of all nodes allocated but not freed
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
void clear() { m_linkable.clear(); }
|
||||
inline void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); }
|
||||
inline bool isLinkable(const AstNode* nodep) const { return m_linkable.count(nodep) != 0; }
|
||||
} s_linkableTable;
|
||||
|
||||
bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinkable(nodep); }
|
||||
|
||||
//######################################################################
|
||||
// Mark every node in the tree
|
||||
|
||||
class BrokenMarkVisitor final : public AstNVisitor {
|
||||
// Mark every node in the tree
|
||||
private:
|
||||
// NODE STATE
|
||||
// Nothing! // This may be called deep inside other routines
|
||||
// // so userp and friends may not be used
|
||||
// METHODS
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
BrokenTable::addInTree(nodep, nodep->maybePointedTo());
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
const uint8_t m_brokenCntCurrent = s_brokenCntGlobal.get();
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
// Process not just iterate
|
||||
processAndIterate(nodep);
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
UASSERT_OBJ(s_allocTable.isAllocated(nodep), nodep,
|
||||
"AstNode is in tree, but not allocated");
|
||||
#endif
|
||||
UASSERT_OBJ(nodep->brokenState() != m_brokenCntCurrent, nodep,
|
||||
"AstNode is already in tree at another location");
|
||||
if (nodep->maybePointedTo()) s_linkableTable.addLinkable(nodep);
|
||||
nodep->brokenState(m_brokenCntCurrent);
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -235,19 +164,33 @@ public:
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Broken state, as a visitor of each AstNode
|
||||
// Check every node in tree
|
||||
|
||||
class BrokenCheckVisitor final : public AstNVisitor {
|
||||
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;
|
||||
// All local variables declared in current function
|
||||
std::unordered_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;
|
||||
// Local variables declared in the scope of the current statement
|
||||
std::vector<std::unordered_set<const AstVar*>> m_localsStack;
|
||||
|
||||
private:
|
||||
static void checkWidthMin(const AstNode* nodep) {
|
||||
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|
||||
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
|
||||
nodep, "Width != WidthMin");
|
||||
}
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
BrokenTable::setUnder(nodep, true);
|
||||
|
||||
void processEnter(AstNode* nodep) {
|
||||
nodep->brokenState(m_brokenCntCurrentUnder);
|
||||
const char* whyp = nodep->broken();
|
||||
UASSERT_OBJ(!whyp, nodep,
|
||||
"Broken link in node (or something without maybePointedTo): " << whyp);
|
||||
@@ -270,8 +213,30 @@ private:
|
||||
if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep);
|
||||
}
|
||||
checkWidthMin(nodep);
|
||||
}
|
||||
void processExit(AstNode* nodep) { nodep->brokenState(m_brokenCntCurrentNotUnder); }
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
processEnter(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
BrokenTable::setUnder(nodep, false);
|
||||
processExit(nodep);
|
||||
}
|
||||
void processAndIterateList(AstNode* nodep) {
|
||||
while (nodep) {
|
||||
processAndIterate(nodep);
|
||||
nodep = nodep->nextp();
|
||||
};
|
||||
}
|
||||
void pushLocalScope() {
|
||||
if (m_cfuncp) m_localsStack.emplace_back();
|
||||
}
|
||||
void popLocalScope() {
|
||||
if (m_cfuncp) m_localsStack.pop_back();
|
||||
}
|
||||
bool isInScopeLocal(const AstVar* varp) const {
|
||||
for (const auto& set : m_localsStack) {
|
||||
if (set.count(varp)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
@@ -295,6 +260,65 @@ private:
|
||||
UASSERT_OBJ(
|
||||
!(v3Global.assertScoped() && m_inScope && nodep->varp() && !nodep->varScopep()), nodep,
|
||||
"VarRef missing VarScope pointer");
|
||||
if (m_cfuncp) {
|
||||
// Check if variable is an in-scope local, otherwise mark as suspect
|
||||
if (const AstVar* const varp = nodep->varp()) {
|
||||
if (!isInScopeLocal(varp)) {
|
||||
// This only stores the first ref for each Var, which is what we want
|
||||
m_suspectRefs.emplace(varp, nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
|
||||
m_cfuncp = nodep;
|
||||
m_localVars.clear();
|
||||
m_suspectRefs.clear();
|
||||
m_localsStack.clear();
|
||||
pushLocalScope();
|
||||
|
||||
processAndIterate(nodep);
|
||||
|
||||
// Check suspect references are all to non-locals
|
||||
for (const auto& pair : m_suspectRefs) {
|
||||
UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second,
|
||||
"Local variable not in scope where referenced: " << pair.first);
|
||||
}
|
||||
|
||||
m_cfuncp = nullptr;
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) override {
|
||||
// Each branch is a separate local variable scope
|
||||
pushLocalScope();
|
||||
processEnter(nodep);
|
||||
processAndIterate(nodep->condp());
|
||||
if (AstNode* const ifsp = nodep->ifsp()) {
|
||||
pushLocalScope();
|
||||
processAndIterateList(ifsp);
|
||||
popLocalScope();
|
||||
}
|
||||
if (AstNode* const elsesp = nodep->elsesp()) {
|
||||
pushLocalScope();
|
||||
processAndIterateList(elsesp);
|
||||
popLocalScope();
|
||||
}
|
||||
processExit(nodep);
|
||||
popLocalScope();
|
||||
}
|
||||
virtual void visit(AstNodeStmt* nodep) override {
|
||||
// For local variable checking act as if any statement introduces a new scope.
|
||||
// This is aggressive but conservatively correct.
|
||||
pushLocalScope();
|
||||
processAndIterate(nodep);
|
||||
popLocalScope();
|
||||
}
|
||||
virtual void visit(AstVar* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
if (m_cfuncp) {
|
||||
m_localVars.insert(nodep);
|
||||
m_localsStack.back().insert(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
// Process not just iterate
|
||||
@@ -308,7 +332,7 @@ public:
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Broken class functions
|
||||
// Broken check entry point
|
||||
|
||||
void V3Broken::brokenAll(AstNetlist* nodep) {
|
||||
// UINFO(9, __FUNCTION__ << ": " << endl);
|
||||
@@ -318,24 +342,23 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
|
||||
UINFO(1, "Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE
|
||||
} else {
|
||||
inBroken = true;
|
||||
BrokenTable::prepForTree();
|
||||
BrokenMarkVisitor mvisitor(nodep);
|
||||
BrokenCheckVisitor cvisitor(nodep);
|
||||
BrokenTable::doneWithTree();
|
||||
BrokenMarkVisitor mvisitor{nodep};
|
||||
BrokenCheckVisitor cvisitor{nodep};
|
||||
s_allocTable.checkForLeaks();
|
||||
s_linkableTable.clear();
|
||||
s_brokenCntGlobal.inc();
|
||||
inBroken = false;
|
||||
}
|
||||
}
|
||||
|
||||
void V3Broken::addNewed(AstNode* nodep) { BrokenTable::addNewed(nodep); }
|
||||
void V3Broken::deleted(AstNode* nodep) { BrokenTable::deleted(nodep); }
|
||||
bool V3Broken::isAllocated(AstNode* nodep) { return BrokenTable::isAllocated(nodep); }
|
||||
//######################################################################
|
||||
// Self test
|
||||
|
||||
void V3Broken::selfTest() {
|
||||
// Warmup addNewed and deleted for coverage, as otherwise only with VL_LEAK_CHECKS
|
||||
FileLine* fl = new FileLine(FileLine::commandLineFilename());
|
||||
auto* newp = new AstBegin(fl, "[EditWrapper]", nullptr);
|
||||
#ifndef VL_LEAK_CHECKS
|
||||
// Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS
|
||||
FileLine* const fl = new FileLine(FileLine::commandLineFilename());
|
||||
const AstNode* const newp = new AstBegin(fl, "[EditWrapper]", nullptr);
|
||||
addNewed(newp);
|
||||
deleted(newp);
|
||||
#endif
|
||||
VL_DO_DANGLING(delete newp, newp);
|
||||
}
|
||||
|
||||
+7
-6
@@ -20,17 +20,18 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
// Forward declare so we can include this in V3Ast.h
|
||||
class AstNode;
|
||||
class AstNetlist;
|
||||
|
||||
class V3Broken final {
|
||||
public:
|
||||
static void brokenAll(AstNetlist* nodep);
|
||||
static void addNewed(AstNode* nodep);
|
||||
static void deleted(AstNode* nodep);
|
||||
static bool isAllocated(AstNode* nodep);
|
||||
static bool isLinkable(const AstNode* nodep);
|
||||
static void addNewed(const AstNode* nodep);
|
||||
static void deleted(const AstNode* nodep);
|
||||
static void selfTest();
|
||||
};
|
||||
|
||||
|
||||
+43
-40
@@ -60,24 +60,22 @@ private:
|
||||
AstCFunc* makeNewFunc() {
|
||||
const int funcNum = m_newFunctions.size();
|
||||
const string funcName = m_basename + "_" + cvtToStr(funcNum);
|
||||
AstCFunc* const funcp = new AstCFunc(m_modp->fileline(), funcName, nullptr, "void");
|
||||
funcp->isStatic(!m_type.isClass()); // Class constructors are non static
|
||||
AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"};
|
||||
funcp->isStatic(false);
|
||||
funcp->isLoose(!m_type.isClass());
|
||||
funcp->declPrivate(true);
|
||||
funcp->slow(!m_type.isClass()); // Only classes construct on fast path
|
||||
string preventUnusedStmt;
|
||||
if (m_type.isClass()) {
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
preventUnusedStmt = "if (false && vlSymsp) {}";
|
||||
preventUnusedStmt = "if (false && vlSymsp) {} // Prevent unused\n";
|
||||
} else if (m_type.isCoverage()) {
|
||||
funcp->argTypes(EmitCBaseVisitor::prefixNameProtect(m_modp) + "* self, "
|
||||
+ EmitCBaseVisitor::symClassVar() + ", bool first");
|
||||
preventUnusedStmt = "if (false && self && vlSymsp && first) {}";
|
||||
} else { // Module
|
||||
funcp->argTypes(EmitCBaseVisitor::prefixNameProtect(m_modp) + "* self");
|
||||
preventUnusedStmt = "if (false && self) {}";
|
||||
funcp->argTypes("bool first");
|
||||
preventUnusedStmt = "if (false && first) {} // Prevent unused\n";
|
||||
}
|
||||
if (!preventUnusedStmt.empty()) {
|
||||
funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt});
|
||||
}
|
||||
preventUnusedStmt += " // Prevent unused\n";
|
||||
funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt));
|
||||
m_modp->addStmtp(funcp);
|
||||
m_numStmts = 0;
|
||||
return funcp;
|
||||
@@ -93,9 +91,9 @@ public:
|
||||
}
|
||||
|
||||
V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type)
|
||||
: m_modp(nodep)
|
||||
: m_modp{nodep}
|
||||
, m_basename{basename}
|
||||
, m_type(type) {
|
||||
, m_type{type} {
|
||||
// Note: The constructor is always called, even if empty, so we must always create at least
|
||||
// one.
|
||||
m_newFunctions.push_back(makeNewFunc());
|
||||
@@ -110,13 +108,12 @@ public:
|
||||
AstCFunc* const rootFuncp = makeNewFunc();
|
||||
rootFuncp->name(m_basename);
|
||||
for (AstCFunc* const funcp : m_newFunctions) {
|
||||
AstCCall* const callp = new AstCCall(m_modp->fileline(), funcp);
|
||||
AstCCall* const callp = new AstCCall{m_modp->fileline(), funcp};
|
||||
if (m_type.isClass()) {
|
||||
callp->argTypes("vlSymsp");
|
||||
} else if (m_type.isCoverage()) {
|
||||
callp->argTypes("self, vlSymsp, first");
|
||||
} else { // Module
|
||||
callp->argTypes("self");
|
||||
} else {
|
||||
if (m_type.isCoverage()) callp->argTypes("first");
|
||||
callp->selfPointer("this");
|
||||
}
|
||||
rootFuncp->addStmtsp(callp);
|
||||
}
|
||||
@@ -130,35 +127,40 @@ private:
|
||||
//######################################################################
|
||||
|
||||
void V3CCtors::evalAsserts() {
|
||||
AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module wrapper
|
||||
AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", nullptr, "void");
|
||||
AstNodeModule* const modp = v3Global.rootp()->modulesp(); // Top module wrapper
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{modp->fileline(), "_eval_debug_assertions", nullptr, "void"};
|
||||
funcp->declPrivate(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->isLoose(true);
|
||||
funcp->slow(false);
|
||||
funcp->ifdef("VL_DEBUG");
|
||||
modp->addStmtp(funcp);
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||
if (AstVar* const varp = VN_CAST(np, Var)) {
|
||||
if (varp->isPrimaryInish() && !varp->isSc()) {
|
||||
if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
|
||||
int storedWidth = basicp->widthAlignBytes() * 8;
|
||||
int lastWordWidth = varp->width() % storedWidth;
|
||||
const int storedWidth = basicp->widthAlignBytes() * 8;
|
||||
const int lastWordWidth = varp->width() % storedWidth;
|
||||
if (lastWordWidth != 0) {
|
||||
// if (signal & CONST(upper_non_clean_mask)) { fail; }
|
||||
AstNode* newp = new AstVarRef(varp->fileline(), varp, VAccess::READ);
|
||||
AstVarRef* const vrefp
|
||||
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
|
||||
vrefp->selfPointer("this");
|
||||
AstNode* newp = vrefp;
|
||||
if (varp->isWide()) {
|
||||
newp = new AstWordSel(
|
||||
newp = new AstWordSel{
|
||||
varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), varp->widthWords() - 1));
|
||||
new AstConst(varp->fileline(), varp->widthWords() - 1)};
|
||||
}
|
||||
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
|
||||
newp = new AstAnd(varp->fileline(), newp,
|
||||
const uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
|
||||
newp = new AstAnd{varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), AstConst::WidthedValue(),
|
||||
storedWidth, value));
|
||||
AstNodeIf* ifp = new AstIf(
|
||||
storedWidth, value)};
|
||||
AstNodeIf* const ifp = new AstIf{
|
||||
varp->fileline(), newp,
|
||||
new AstCStmt(varp->fileline(), "Verilated::overWidthError(\""
|
||||
+ varp->prettyName() + "\");"));
|
||||
new AstCStmt{varp->fileline(), "Verilated::overWidthError(\""
|
||||
+ varp->prettyName() + "\");"}};
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
newp = ifp;
|
||||
funcp->addStmtsp(newp);
|
||||
@@ -176,20 +178,21 @@ void V3CCtors::cctorsAll() {
|
||||
modp = VN_CAST(modp->nextp(), NodeModule)) {
|
||||
// Process each module in turn
|
||||
{
|
||||
V3CCtorsBuilder var_reset(modp, "_ctor_var_reset",
|
||||
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE);
|
||||
V3CCtorsBuilder var_reset{modp, "_ctor_var_reset",
|
||||
VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE};
|
||||
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* const varp = VN_CAST(np, Var)) {
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) {
|
||||
const auto vrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE);
|
||||
var_reset.add(new AstCReset(varp->fileline(), vrefp));
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()
|
||||
&& !varp->isParam()) {
|
||||
const auto vrefp = new AstVarRef{varp->fileline(), varp, VAccess::WRITE};
|
||||
var_reset.add(new AstCReset{varp->fileline(), vrefp});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.coverage()) {
|
||||
V3CCtorsBuilder configure_coverage(modp, "_configure_coverage", VCtorType::COVERAGE);
|
||||
V3CCtorsBuilder configure_coverage{modp, "_configure_coverage", VCtorType::COVERAGE};
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) {
|
||||
np = coverp->backp();
|
||||
@@ -197,8 +200,8 @@ void V3CCtors::cctorsAll() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AstClass* classp = VN_CAST(modp, Class)) {
|
||||
AstCFunc* funcp = new AstCFunc(modp->fileline(), "~", nullptr, "");
|
||||
if (AstClass* const classp = VN_CAST(modp, Class)) {
|
||||
AstCFunc* const funcp = new AstCFunc{modp->fileline(), "~", nullptr, ""};
|
||||
funcp->isDestructor(true);
|
||||
funcp->isStatic(false);
|
||||
// If can be referred to by base pointer, need virtual delete
|
||||
|
||||
+35
-165
@@ -13,14 +13,12 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Class's Transformations:
|
||||
// V3CUse's Transformations:
|
||||
//
|
||||
// Each module:
|
||||
// Each cell:
|
||||
// Create CUse for cell forward declaration
|
||||
// Each class:
|
||||
// Create string access functions
|
||||
// Search for dtypes referencing class, and create CUse for forward declaraion
|
||||
// Search for dtypes referencing class, and create CUse for forward declaration
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
@@ -30,61 +28,37 @@
|
||||
#include "V3Global.h"
|
||||
#include "V3CUse.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CUseState final {
|
||||
private:
|
||||
// MEMBERS
|
||||
AstNodeModule* m_modInsertp; // Current module to insert AstCUse under
|
||||
using UseString = std::pair<VUseType, std::string>;
|
||||
std::map<const UseString, AstCUse*> m_didUse; // What we already used
|
||||
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstClass::user1() -> bool. True if class needs to_string dumper
|
||||
AstUser1InUse m_inuser1;
|
||||
// AstClass::user2() -> bool. True if iterated
|
||||
AstUser2InUse m_inuser2;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
public:
|
||||
AstCUse* newUse(AstNode* nodep, VUseType useType, const string& name) {
|
||||
UseString key(useType, name);
|
||||
if (m_didUse.find(key) == m_didUse.end()) {
|
||||
AstCUse* newp = new AstCUse(nodep->fileline(), useType, name);
|
||||
m_modInsertp->addStmtp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
m_didUse[key] = newp;
|
||||
}
|
||||
return m_didUse[key];
|
||||
}
|
||||
|
||||
// CONSTRUCTORS
|
||||
explicit CUseState(AstNodeModule* nodep)
|
||||
: m_modInsertp{nodep} {}
|
||||
virtual ~CUseState() = default;
|
||||
VL_UNCOPYABLE(CUseState);
|
||||
};
|
||||
|
||||
// Visit within a module all nodes and data types they reference, finding
|
||||
// any classes so we can make sure they are defined when Verilated code
|
||||
// compiles
|
||||
class CUseDTypeVisitor final : public AstNVisitor {
|
||||
class CUseVisitor final : public AstNVisitor {
|
||||
// NODE STATE
|
||||
// AstNode::user1() -> bool. True if already visited
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// MEMBERS
|
||||
CUseState& m_stater; // State for inserter
|
||||
bool m_impOnly = false; // In details needed only for implementation
|
||||
AstNodeModule* const m_modp; // Current module
|
||||
std::set<std::pair<VUseType, std::string>> m_didUse; // What we already used
|
||||
|
||||
// METHODS
|
||||
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
|
||||
if (m_didUse.emplace(useType, name).second) {
|
||||
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
|
||||
m_modp->addStmtp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstClassRefDType* nodep) override {
|
||||
if (nodep->user2SetOnce()) return; // Process once
|
||||
if (!m_impOnly) m_stater.newUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
|
||||
// No class.h, it's inside the class package's h file
|
||||
m_stater.newUse(nodep, VUseType::IMP_INCLUDE, nodep->classp()->classOrPackagep()->name());
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
|
||||
// Need to include extends() when we implement, but no need for pointers to know
|
||||
VL_RESTORER(m_impOnly);
|
||||
{
|
||||
@@ -93,131 +67,27 @@ class CUseDTypeVisitor final : public AstNVisitor {
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep) override {
|
||||
if (nodep->user2SetOnce()) return; // Process once
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
|
||||
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
if (nodep->user2SetOnce()) return; // Process once
|
||||
if (nodep->dtypep() && !nodep->dtypep()->user2()) iterate(nodep->dtypep());
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
// Currently no IMP_INCLUDE because we include __Syms which has them all
|
||||
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CUseDTypeVisitor(AstNodeModule* nodep, CUseState& stater)
|
||||
: m_stater(stater) { // Need () or GCC 4.8 false warning
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CUseDTypeVisitor() override = default;
|
||||
VL_UNCOPYABLE(CUseDTypeVisitor);
|
||||
};
|
||||
|
||||
class CUseVisitor final : public AstNVisitor {
|
||||
// MEMBERS
|
||||
CUseState m_state; // Inserter state
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// Module use builders
|
||||
void makeUseCells(AstNodeModule* nodep) {
|
||||
for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (AstCell* cellp = VN_CAST(itemp, Cell)) {
|
||||
// Currently no include because we include __Syms which has them all
|
||||
m_state.newUse(nodep, VUseType::INT_FWD_CLASS, cellp->modp()->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
void makeVlToString(AstClass* nodep) {
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "VL_TO_STRING", nullptr, "std::string");
|
||||
funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep)
|
||||
+ ">& obj");
|
||||
funcp->isMethod(false);
|
||||
funcp->isConst(false);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
AstNode* exprp = new AstCMath(nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0);
|
||||
exprp->dtypeSetString();
|
||||
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
void makeToString(AstClass* nodep) {
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "to_string", nullptr, "std::string");
|
||||
funcp->isConst(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
AstNode* exprp = new AstCMath(nodep->fileline(),
|
||||
R"(std::string("'{") + to_string_middle() + "}")", 0);
|
||||
exprp->dtypeSetString();
|
||||
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
void makeToStringMiddle(AstClass* nodep) {
|
||||
AstCFunc* funcp
|
||||
= new AstCFunc(nodep->fileline(), "to_string_middle", nullptr, "std::string");
|
||||
funcp->isConst(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "std::string out;\n"));
|
||||
std::string comma;
|
||||
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (auto* varp = VN_CAST(itemp, Var)) {
|
||||
if (!varp->isParam()) {
|
||||
string stmt = "out += \"";
|
||||
stmt += comma;
|
||||
comma = ", ";
|
||||
stmt += itemp->origNameProtect();
|
||||
stmt += ":\" + ";
|
||||
if (itemp->isWide()) {
|
||||
stmt += "VL_TO_STRING_W(";
|
||||
stmt += cvtToStr(itemp->widthWords());
|
||||
stmt += ", ";
|
||||
} else {
|
||||
stmt += "VL_TO_STRING(";
|
||||
}
|
||||
stmt += itemp->nameProtect();
|
||||
stmt += ");\n";
|
||||
nodep->user1(true); // So what we extend dumps this
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
|
||||
string stmt = "out += \"";
|
||||
if (!comma.empty()) stmt += "\", \"+ ";
|
||||
// comma = ", "; // Nothing further so not needed
|
||||
stmt += nodep->extendsp()->dtypep()->nameProtect();
|
||||
stmt += "::to_string_middle();\n";
|
||||
nodep->user1(true); // So what we extend dumps this
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||
}
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "return out;\n"));
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
if (v3Global.opt.trace()) {
|
||||
AstCUse* usep
|
||||
= m_state.newUse(nodep, VUseType::INT_FWD_CLASS, v3Global.opt.traceClassBase());
|
||||
usep->protect(false);
|
||||
}
|
||||
makeUseCells(nodep);
|
||||
{ CUseDTypeVisitor dtypeVisitor(nodep, m_state); }
|
||||
if (AstClass* classp = VN_CAST(nodep, Class)) {
|
||||
makeVlToString(classp);
|
||||
makeToString(classp);
|
||||
makeToStringMiddle(classp);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode*) override {} // All in AstNodeModule
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CUseVisitor(AstNodeModule* nodep)
|
||||
: m_state{nodep} {
|
||||
iterate(nodep);
|
||||
explicit CUseVisitor(AstNodeModule* modp)
|
||||
: m_modp(modp) {
|
||||
iterate(modp);
|
||||
}
|
||||
virtual ~CUseVisitor() override = default;
|
||||
VL_UNCOPYABLE(CUseVisitor);
|
||||
@@ -233,7 +103,7 @@ void V3CUse::cUseAll() {
|
||||
modp = VN_CAST(modp->nextp(), NodeModule)) {
|
||||
// Insert under this module; someday we should e.g. make Ast
|
||||
// for each output file and put under that
|
||||
CUseVisitor visitor(modp);
|
||||
CUseVisitor{modp};
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("cuse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+2
-2
@@ -530,10 +530,10 @@ public:
|
||||
|
||||
void V3Case::caseAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CaseVisitor visitor(nodep); } // Destruct before checking
|
||||
{ CaseVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("case", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
void V3Case::caseLint(AstNodeCase* nodep) {
|
||||
UINFO(4, __FUNCTION__ << ": " << endl);
|
||||
CaseLintVisitor visitor(nodep);
|
||||
CaseLintVisitor visitor{nodep};
|
||||
}
|
||||
|
||||
+8
-6
@@ -66,7 +66,8 @@ private:
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
//
|
||||
AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin());
|
||||
AstCCast* const castp
|
||||
= new AstCCast{nodep->fileline(), nodep, needsize, nodep->widthMin()};
|
||||
relinkHandle.relink(castp);
|
||||
// if (debug() > 8) castp->dumpTree(cout, "-castins: ");
|
||||
//
|
||||
@@ -103,7 +104,7 @@ private:
|
||||
if (!VN_IS(nodep->backp(), NullCheck)) {
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
AstNode* newp = new AstNullCheck(nodep->fileline(), nodep);
|
||||
AstNode* const newp = new AstNullCheck{nodep->fileline(), nodep};
|
||||
relinkHandle.relink(newp);
|
||||
}
|
||||
}
|
||||
@@ -154,9 +155,10 @@ private:
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
if (nodep->access().isReadOnly() && !VN_IS(nodep->backp(), CCast)
|
||||
&& VN_IS(nodep->backp(), NodeMath) && !VN_IS(nodep->backp(), ArraySel)
|
||||
&& nodep->backp()->width() && castSize(nodep) != castSize(nodep->varp())) {
|
||||
AstNode* const backp = nodep->backp();
|
||||
if (nodep->access().isReadOnly() && !VN_IS(backp, CCast) && VN_IS(backp, NodeMath)
|
||||
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, RedXor) && 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);
|
||||
insertCast(nodep, castSize(nodep));
|
||||
@@ -201,6 +203,6 @@ public:
|
||||
|
||||
void V3Cast::castAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CastVisitor visitor(nodep); } // Destruct before checking
|
||||
{ CastVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("cast", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+10
-9
@@ -151,7 +151,7 @@ private:
|
||||
*m_ofp << " ";
|
||||
}
|
||||
*m_ofp << nodep->prettyTypeName() << "\n";
|
||||
string lastPrefix = m_prefix;
|
||||
const string lastPrefix = m_prefix;
|
||||
m_prefix = lastPrefix + "1:";
|
||||
iterateAndNextNull(nodep->op1p());
|
||||
m_prefix = lastPrefix + "2:";
|
||||
@@ -337,7 +337,7 @@ private:
|
||||
|
||||
int filelineWidth() {
|
||||
if (!m_filelineWidth) {
|
||||
CdcWidthVisitor visitor(v3Global.rootp());
|
||||
CdcWidthVisitor visitor{v3Global.rootp()};
|
||||
m_filelineWidth = visitor.maxWidth();
|
||||
}
|
||||
return m_filelineWidth;
|
||||
@@ -449,7 +449,7 @@ private:
|
||||
if (!vertexp->asyncPath() && level != 0) return false; // Not part of path
|
||||
|
||||
// Other logic in the path
|
||||
string cont = prefix + sep;
|
||||
const string cont = prefix + sep;
|
||||
string nextsep = " ";
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
@@ -495,7 +495,7 @@ private:
|
||||
UINFO(3, __FUNCTION__ << ": " << endl);
|
||||
|
||||
// Trace all sources and sinks
|
||||
for (int traceDests = 0; traceDests < 2; ++traceDests) {
|
||||
for (const bool& traceDests : {false, true}) {
|
||||
UINFO(9, " Trace Direction " << (traceDests ? "dst" : "src") << endl);
|
||||
m_graph.userClearVertices(); // user1: bool - was analyzed
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
@@ -506,8 +506,9 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt";
|
||||
const std::unique_ptr<std::ofstream> ofp(V3File::new_ofstream(filename));
|
||||
const string filename
|
||||
= v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt";
|
||||
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
|
||||
if (ofp->fail()) v3fatal("Can't write " << filename);
|
||||
*ofp << "Edge Report for " << v3Global.opt.prefix() << '\n';
|
||||
|
||||
@@ -523,7 +524,7 @@ private:
|
||||
os.setf(std::ios::left);
|
||||
// Module name - doesn't work due to flattening having lost the original
|
||||
// so we assume the modulename matches the filebasename
|
||||
string fname = vvertexp->varScp()->fileline()->filebasename() + ":";
|
||||
const string fname = vvertexp->varScp()->fileline()->filebasename() + ":";
|
||||
os << " " << std::setw(20) << fname;
|
||||
os << " " << std::setw(8) << what;
|
||||
os << " " << std::setw(40) << vvertexp->varScp()->prettyName();
|
||||
@@ -727,7 +728,7 @@ public:
|
||||
// CONSTRUCTORS
|
||||
explicit CdcVisitor(AstNode* nodep) {
|
||||
// Make report of all signal names and what clock edges they have
|
||||
string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc.txt";
|
||||
const string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc.txt";
|
||||
m_ofp = V3File::new_ofstream(filename);
|
||||
if (m_ofp->fail()) v3fatal("Can't write " << filename);
|
||||
m_ofFilename = filename;
|
||||
@@ -757,5 +758,5 @@ public:
|
||||
|
||||
void V3Cdc::cdcAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
CdcVisitor visitor(nodep);
|
||||
CdcVisitor visitor{nodep};
|
||||
}
|
||||
|
||||
+60
-45
@@ -47,11 +47,33 @@ public:
|
||||
AstCFunc* m_tlChgFuncp = nullptr; // Top level change function we're building
|
||||
int m_numStmts = 0; // Number of statements added to m_chgFuncp
|
||||
int m_funcNum = 0; // Number of change functions emitted
|
||||
bool m_madeTopChg = false;
|
||||
|
||||
ChangedState() = default;
|
||||
~ChangedState() = default;
|
||||
|
||||
void maybeCreateChgFuncp() {
|
||||
maybeCreateTopChg();
|
||||
maybeCreateMidChg();
|
||||
}
|
||||
void maybeCreateTopChg() {
|
||||
if (m_madeTopChg) return;
|
||||
m_madeTopChg = true;
|
||||
v3Global.rootp()->changeRequest(true);
|
||||
|
||||
// Create a wrapper change detection function that calls each change detection function
|
||||
m_tlChgFuncp
|
||||
= new AstCFunc{m_scopetopp->fileline(), "_change_request", m_scopetopp, "QData"};
|
||||
m_tlChgFuncp->isStatic(false);
|
||||
m_tlChgFuncp->isLoose(true);
|
||||
m_tlChgFuncp->declPrivate(true);
|
||||
m_scopetopp->addActivep(m_tlChgFuncp);
|
||||
// Each change detection function needs at least one AstChangeDet
|
||||
// to ensure that V3EmitC outputs the necessary code.
|
||||
maybeCreateMidChg();
|
||||
m_chgFuncp->addStmtsp(new AstChangeDet{m_scopetopp->fileline(), nullptr, nullptr, false});
|
||||
}
|
||||
void maybeCreateMidChg() {
|
||||
// Don't create an extra function call if splitting is disabled
|
||||
if (!v3Global.opt.outputSplitCFuncs()) {
|
||||
m_chgFuncp = m_tlChgFuncp;
|
||||
@@ -59,30 +81,29 @@ public:
|
||||
}
|
||||
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
m_chgFuncp
|
||||
= new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
|
||||
m_scopetopp, "QData");
|
||||
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_chgFuncp->symProlog(true);
|
||||
= new AstCFunc{m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
|
||||
m_scopetopp, "QData"};
|
||||
m_chgFuncp->isStatic(false);
|
||||
m_chgFuncp->isLoose(true);
|
||||
m_chgFuncp->declPrivate(true);
|
||||
m_scopetopp->addActivep(m_chgFuncp);
|
||||
|
||||
// Add a top call to it
|
||||
AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp);
|
||||
callp->argTypes("vlSymsp");
|
||||
AstCCall* const callp = new AstCCall{m_scopetopp->fileline(), m_chgFuncp};
|
||||
|
||||
if (!m_tlChgFuncp->stmtsp()) {
|
||||
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
|
||||
m_tlChgFuncp->addStmtsp(new AstCReturn{m_scopetopp->fileline(), callp});
|
||||
} else {
|
||||
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
|
||||
AstCReturn* const returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
|
||||
UASSERT_OBJ(returnp, m_scopetopp, "Lost CReturn in top change function");
|
||||
// This is currently using AstLogOr which will shortcut the
|
||||
// evaluation if any function returns true. This is likely what
|
||||
// we want and is similar to the logic already in use inside
|
||||
// V3EmitC, however, it also means that verbose logging may
|
||||
// miss to print change detect variables.
|
||||
AstNode* newp = new AstCReturn(
|
||||
AstNode* const newp = new AstCReturn{
|
||||
m_scopetopp->fileline(),
|
||||
new AstLogOr(m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()));
|
||||
new AstLogOr{m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()}};
|
||||
returnp->replaceWith(newp);
|
||||
VL_DO_DANGLING(returnp->deleteTree(), returnp);
|
||||
}
|
||||
@@ -124,14 +145,17 @@ private:
|
||||
}
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
|
||||
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(), m_varEqnp->cloneTree(true),
|
||||
m_newRvEqnp->cloneTree(true), false);
|
||||
AstChangeDet* const changep = new AstChangeDet{
|
||||
m_vscp->fileline(), m_varEqnp->cloneTree(true), m_newRvEqnp->cloneTree(true), false};
|
||||
m_statep->m_chgFuncp->addStmtsp(changep);
|
||||
AstAssign* initp = new AstAssign(m_vscp->fileline(), m_newLvEqnp->cloneTree(true),
|
||||
m_varEqnp->cloneTree(true));
|
||||
AstAssign* const initp = new AstAssign{m_vscp->fileline(), m_newLvEqnp->cloneTree(true),
|
||||
m_varEqnp->cloneTree(true)};
|
||||
m_statep->m_chgFuncp->addFinalsp(initp);
|
||||
EmitCBaseCounterVisitor visitor(initp);
|
||||
m_statep->m_numStmts += visitor.count();
|
||||
|
||||
// Later code will expand words which adds to GCC compile time,
|
||||
// so add penalty based on word width also
|
||||
EmitCBaseCounterVisitor visitor{initp};
|
||||
m_statep->m_numStmts += visitor.count() + m_varEqnp->widthWords();
|
||||
}
|
||||
|
||||
virtual void visit(AstBasicDType*) override { //
|
||||
@@ -146,11 +170,11 @@ private:
|
||||
VL_RESTORER(m_newLvEqnp);
|
||||
VL_RESTORER(m_newRvEqnp);
|
||||
{
|
||||
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index);
|
||||
m_varEqnp = new AstArraySel{nodep->fileline(), m_varEqnp->cloneTree(true), index};
|
||||
m_newLvEqnp
|
||||
= new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
|
||||
= new AstArraySel{nodep->fileline(), m_newLvEqnp->cloneTree(true), index};
|
||||
m_newRvEqnp
|
||||
= new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
|
||||
= new AstArraySel{nodep->fileline(), m_newRvEqnp->cloneTree(true), index};
|
||||
|
||||
iterate(nodep->subDTypep()->skipRefp());
|
||||
|
||||
@@ -181,26 +205,30 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) {
|
||||
// DPI export trigger should never need change detect. See similar assertions in V3Order
|
||||
// (OrderVisitor::nodeMarkCircular), and V3GenClk (GenClkRenameVisitor::genInpClk).
|
||||
UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp,
|
||||
"DPI export trigger should not need change detect");
|
||||
m_statep = statep;
|
||||
m_vscp = vscp;
|
||||
m_detects = 0;
|
||||
{
|
||||
AstVar* varp = m_vscp->varp();
|
||||
string newvarname
|
||||
= ("__Vchglast__" + m_vscp->scopep()->nameDotless() + "__" + varp->shortName());
|
||||
AstVar* const varp = m_vscp->varp();
|
||||
string newvarname{"__Vchglast__" + m_vscp->scopep()->nameDotless() + "__"
|
||||
+ varp->shortName()};
|
||||
// Create: VARREF(_last)
|
||||
// ASSIGN(VARREF(_last), VARREF(var))
|
||||
// ...
|
||||
// CHANGEDET(VARREF(_last), VARREF(var))
|
||||
AstVar* newvarp
|
||||
= new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
|
||||
AstVar* const newvarp
|
||||
= new AstVar{varp->fileline(), AstVarType::MODULETEMP, newvarname, varp};
|
||||
m_statep->m_topModp->addStmtp(newvarp);
|
||||
m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp);
|
||||
m_newvscp = new AstVarScope{m_vscp->fileline(), m_statep->m_scopetopp, newvarp};
|
||||
m_statep->m_scopetopp->addVarp(m_newvscp);
|
||||
|
||||
m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, VAccess::READ);
|
||||
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::WRITE);
|
||||
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::READ);
|
||||
m_varEqnp = new AstVarRef{m_vscp->fileline(), m_vscp, VAccess::READ};
|
||||
m_newLvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::WRITE};
|
||||
m_newRvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::READ};
|
||||
}
|
||||
iterate(vscp->dtypep()->skipRefp());
|
||||
m_varEqnp->deleteTree();
|
||||
@@ -229,7 +257,7 @@ private:
|
||||
|
||||
void genChangeDet(AstVarScope* vscp) {
|
||||
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: " << vscp->prettyNameQ());
|
||||
ChangedInsertVisitor visitor(vscp, m_statep);
|
||||
ChangedInsertVisitor visitor{vscp, m_statep};
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
@@ -243,24 +271,11 @@ private:
|
||||
UINFO(4, " TS " << nodep << endl);
|
||||
// Clearing
|
||||
AstNode::user1ClearTree();
|
||||
// Create the change detection function
|
||||
AstScope* scopep = nodep->scopep();
|
||||
// Prep for if make change detection function
|
||||
AstScope* const scopep = nodep->scopep();
|
||||
UASSERT_OBJ(scopep, nodep, "No scope found on top level, perhaps you have no statements?");
|
||||
m_statep->m_scopetopp = scopep;
|
||||
|
||||
// Create a wrapper change detection function that calls each change detection function
|
||||
m_statep->m_tlChgFuncp
|
||||
= new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData");
|
||||
m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_statep->m_tlChgFuncp->symProlog(true);
|
||||
m_statep->m_tlChgFuncp->declPrivate(true);
|
||||
m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp);
|
||||
// Each change detection function needs at least one AstChangeDet
|
||||
// to ensure that V3EmitC outputs the necessary code.
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
m_statep->m_chgFuncp->addStmtsp(
|
||||
new AstChangeDet(nodep->fileline(), nullptr, nullptr, false));
|
||||
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep) override {
|
||||
@@ -289,7 +304,7 @@ void V3Changed::changedAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{
|
||||
ChangedState state;
|
||||
ChangedVisitor visitor(nodep, &state);
|
||||
ChangedVisitor visitor{nodep, &state};
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+1
-1
@@ -153,6 +153,6 @@ public:
|
||||
|
||||
void V3Class::classAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ClassVisitor visitor(nodep); } // Destruct before checking
|
||||
{ ClassVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("class", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+6
-5
@@ -68,7 +68,7 @@ private:
|
||||
void setCppWidth(AstNode* nodep) {
|
||||
nodep->user2(true); // Don't resize it again
|
||||
AstNodeDType* old_dtypep = nodep->dtypep();
|
||||
int width = cppWidth(nodep); // widthMin is unchanged
|
||||
const int width = cppWidth(nodep); // widthMin is unchanged
|
||||
if (old_dtypep->width() != width) {
|
||||
// Since any given dtype's cppWidth() is the same, we can just
|
||||
// remember one conversion for each, and reuse it
|
||||
@@ -103,7 +103,7 @@ private:
|
||||
void setCleanState(AstNode* nodep, CleanState clean) { nodep->user1(clean); }
|
||||
CleanState getCleanState(AstNode* nodep) { return static_cast<CleanState>(nodep->user1()); }
|
||||
bool isClean(AstNode* nodep) {
|
||||
CleanState clstate = getCleanState(nodep);
|
||||
const CleanState clstate = getCleanState(nodep);
|
||||
if (clstate == CS_CLEAN) return true;
|
||||
if (clstate == CS_DIRTY) return false;
|
||||
nodep->v3fatalSrc("Unknown clean state on node: " + nodep->prettyTypeName());
|
||||
@@ -111,8 +111,9 @@ private:
|
||||
}
|
||||
void setClean(AstNode* nodep, bool isClean) {
|
||||
computeCppWidth(nodep); // Just to be sure it's in widthMin
|
||||
bool wholeUint = (nodep->widthMin() == VL_IDATASIZE || nodep->widthMin() == VL_QUADSIZE
|
||||
|| (nodep->widthMin() % VL_EDATASIZE) == 0);
|
||||
const bool wholeUint
|
||||
= (nodep->widthMin() == VL_IDATASIZE || nodep->widthMin() == VL_QUADSIZE
|
||||
|| (nodep->widthMin() % VL_EDATASIZE) == 0);
|
||||
setCleanState(nodep, ((isClean || wholeUint) ? CS_CLEAN : CS_DIRTY));
|
||||
}
|
||||
|
||||
@@ -313,6 +314,6 @@ public:
|
||||
|
||||
void V3Clean::cleanAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CleanVisitor visitor(nodep); } // Destruct before checking
|
||||
{ CleanVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("clean", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+60
-60
@@ -37,6 +37,34 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
//######################################################################
|
||||
// Convert every WRITE AstVarRef to a READ ref
|
||||
|
||||
class ConvertWriteRefsToRead final : public AstNVisitor {
|
||||
private:
|
||||
// MEMBERS
|
||||
AstNode* m_result = nullptr;
|
||||
|
||||
// CONSTRUCTORS
|
||||
explicit ConvertWriteRefsToRead(AstNode* nodep) {
|
||||
m_result = iterateSubtreeReturnEdits(nodep);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstVarRef* nodep) override {
|
||||
UASSERT_OBJ(!nodep->access().isRW(), nodep, "Cannot handle a READWRITE reference");
|
||||
if (nodep->access().isWriteOnly()) {
|
||||
nodep->replaceWith(
|
||||
new AstVarRef(nodep->fileline(), nodep->varScopep(), VAccess::READ));
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
static AstNode* main(AstNode* nodep) { return ConvertWriteRefsToRead(nodep).m_result; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Clock state, as a visitor of each AstNode
|
||||
|
||||
@@ -164,6 +192,18 @@ private:
|
||||
m_lastSenp = nullptr;
|
||||
m_lastIfp = nullptr;
|
||||
}
|
||||
AstCFunc* makeTopFunction(const string& name, bool slow = false) {
|
||||
AstCFunc* const funcp = new AstCFunc{m_topScopep->fileline(), name, m_topScopep->scopep()};
|
||||
funcp->dontCombine(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->isLoose(true);
|
||||
funcp->entryPoint(true);
|
||||
funcp->slow(slow);
|
||||
funcp->isConst(false);
|
||||
funcp->declPrivate(true);
|
||||
m_topScopep->scopep()->addActivep(funcp);
|
||||
return funcp;
|
||||
}
|
||||
void splitCheck(AstCFunc* ofuncp) {
|
||||
if (!v3Global.opt.outputSplitCFuncs() || !ofuncp->stmtsp()) return;
|
||||
if (EmitCBaseCounterVisitor(ofuncp->stmtsp()).count() < v3Global.opt.outputSplitCFuncs())
|
||||
@@ -179,20 +219,18 @@ private:
|
||||
if (ofuncp->finalsp()) tempp->addStmtsp(ofuncp->finalsp()->unlinkFrBackWithNext());
|
||||
while (tempp->stmtsp()) {
|
||||
AstNode* itemp = tempp->stmtsp()->unlinkFrBack();
|
||||
int stmts = EmitCBaseCounterVisitor(itemp).count();
|
||||
const int stmts = EmitCBaseCounterVisitor(itemp).count();
|
||||
if (!funcp || (func_stmts + stmts) > v3Global.opt.outputSplitCFuncs()) {
|
||||
// Make a new function
|
||||
funcp = new AstCFunc{ofuncp->fileline(), ofuncp->name() + cvtToStr(++funcnum),
|
||||
m_topScopep->scopep()};
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->dontCombine(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->isLoose(true);
|
||||
funcp->slow(ofuncp->slow());
|
||||
m_topScopep->scopep()->addActivep(funcp);
|
||||
//
|
||||
AstCCall* callp = new AstCCall{funcp->fileline(), funcp};
|
||||
callp->argTypes("vlSymsp");
|
||||
ofuncp->addStmtsp(callp);
|
||||
func_stmts = 0;
|
||||
}
|
||||
@@ -212,58 +250,21 @@ private:
|
||||
// VV***** We reset all user1p()
|
||||
AstNode::user1ClearTree();
|
||||
// Make top functions
|
||||
{
|
||||
AstCFunc* funcp = new AstCFunc{nodep->fileline(), "_eval", m_topScopep->scopep()};
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->dontCombine(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->entryPoint(true);
|
||||
m_topScopep->scopep()->addActivep(funcp);
|
||||
m_evalFuncp = funcp;
|
||||
}
|
||||
{
|
||||
AstCFunc* funcp
|
||||
= new AstCFunc{nodep->fileline(), "_eval_initial", m_topScopep->scopep()};
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->dontCombine(true);
|
||||
funcp->slow(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->entryPoint(true);
|
||||
m_topScopep->scopep()->addActivep(funcp);
|
||||
m_initFuncp = funcp;
|
||||
}
|
||||
{
|
||||
AstCFunc* funcp = new AstCFunc{nodep->fileline(), "final", m_topScopep->scopep()};
|
||||
funcp->skipDecl(true);
|
||||
funcp->dontCombine(true);
|
||||
funcp->slow(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->entryPoint(true);
|
||||
funcp->protect(false);
|
||||
funcp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symClassVar()
|
||||
+ " = this->__VlSymsp;\n"));
|
||||
funcp->addInitsp(
|
||||
new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign() + "\n"));
|
||||
m_topScopep->scopep()->addActivep(funcp);
|
||||
m_finalFuncp = funcp;
|
||||
}
|
||||
{
|
||||
AstCFunc* funcp
|
||||
= new AstCFunc{nodep->fileline(), "_eval_settle", m_topScopep->scopep()};
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->dontCombine(true);
|
||||
funcp->slow(true);
|
||||
funcp->isStatic(true);
|
||||
funcp->symProlog(true);
|
||||
funcp->entryPoint(true);
|
||||
m_topScopep->scopep()->addActivep(funcp);
|
||||
m_settleFuncp = funcp;
|
||||
}
|
||||
m_evalFuncp = makeTopFunction("_eval");
|
||||
m_initFuncp = makeTopFunction("_eval_initial", /* slow: */ true);
|
||||
m_settleFuncp = makeTopFunction("_eval_settle", /* slow: */ true);
|
||||
m_finalFuncp = makeTopFunction("_final", /* slow: */ true);
|
||||
// Process the activates
|
||||
iterateChildren(nodep);
|
||||
UINFO(4, " TOPSCOPE iter done " << nodep << endl);
|
||||
// Clear the DPI export trigger flag at the end of eval
|
||||
if (AstVarScope* const dpiExportTriggerp = v3Global.rootp()->dpiExportTriggerp()) {
|
||||
FileLine* const fl = dpiExportTriggerp->fileline();
|
||||
AstAssign* const assignp
|
||||
= new AstAssign{fl, new AstVarRef{fl, dpiExportTriggerp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitFalse{}}};
|
||||
m_evalFuncp->addFinalsp(assignp);
|
||||
}
|
||||
// Split large functions
|
||||
splitCheck(m_evalFuncp);
|
||||
splitCheck(m_initFuncp);
|
||||
@@ -307,14 +308,14 @@ private:
|
||||
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
|
||||
AstNode* incp = nodep->incp()->unlinkFrBack();
|
||||
AstNode* origp = nodep->origp()->unlinkFrBack();
|
||||
AstNode* changep = nodep->changep()->unlinkFrBack();
|
||||
AstIf* newp = new AstIf(nodep->fileline(), new AstXor(nodep->fileline(), origp, changep),
|
||||
AstNode* changeWrp = nodep->changep()->unlinkFrBack();
|
||||
AstNode* changeRdp = ConvertWriteRefsToRead::main(changeWrp->cloneTree(false));
|
||||
AstIf* newp = new AstIf(nodep->fileline(), new AstXor(nodep->fileline(), origp, changeRdp),
|
||||
incp, nullptr);
|
||||
// We could add another IF to detect posedges, and only increment if so.
|
||||
// It's another whole branch though versus a potential memory miss.
|
||||
// We'll go with the miss.
|
||||
newp->addIfsp(
|
||||
new AstAssign(nodep->fileline(), changep->cloneTree(false), origp->cloneTree(false)));
|
||||
newp->addIfsp(new AstAssign(nodep->fileline(), changeWrp, origp->cloneTree(false)));
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
@@ -324,7 +325,6 @@ private:
|
||||
if (nodep->formCallTree()) {
|
||||
UINFO(4, " formCallTree " << nodep << endl);
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
|
||||
callp->argTypes("vlSymsp");
|
||||
m_finalFuncp->addStmtsp(callp);
|
||||
}
|
||||
}
|
||||
@@ -443,6 +443,6 @@ public:
|
||||
|
||||
void V3Clock::clockAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ClockVisitor visitor(nodep); } // Destruct before checking
|
||||
{ ClockVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("clock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+16
-8
@@ -72,8 +72,7 @@ public:
|
||||
AstNode* const argsp
|
||||
= oldp->argsp() ? oldp->argsp()->unlinkFrBackWithNext() : nullptr;
|
||||
AstCCall* const newp = new AstCCall(oldp->fileline(), newfuncp, argsp);
|
||||
newp->hiernameToProt(oldp->hiernameToProt());
|
||||
newp->hiernameToUnprot(oldp->hiernameToUnprot());
|
||||
newp->selfPointer(oldp->selfPointer());
|
||||
newp->argTypes(oldp->argTypes());
|
||||
addCall(newp); // Fix the table, in case the newfuncp itself gets replaced
|
||||
oldp->replaceWith(newp);
|
||||
@@ -86,14 +85,23 @@ public:
|
||||
}
|
||||
}
|
||||
// METHODS
|
||||
void addCall(AstCCall* nodep) {
|
||||
if (nodep->funcp()->dontCombine()) return;
|
||||
m_callMmap.emplace(nodep->funcp(), nodep);
|
||||
}
|
||||
void addCall(AstCCall* nodep) { m_callMmap.emplace(nodep->funcp(), nodep); }
|
||||
|
||||
private:
|
||||
// VISITORS
|
||||
virtual void visit(AstCCall* nodep) override { addCall(nodep); }
|
||||
virtual void visit(AstCCall* nodep) override {
|
||||
if (nodep->funcp()->dontCombine()) return;
|
||||
addCall(nodep);
|
||||
}
|
||||
// LCOV_EXCL_START
|
||||
virtual void visit(AstAddrOfCFunc* nodep) override {
|
||||
// We cannot yet handle references via AstAddrOfCFunc, but currently those are
|
||||
// only used in tracing functions, which are not combined. Blow up in case this changes.
|
||||
if (nodep->funcp()->dontCombine()) return;
|
||||
nodep->v3fatalSrc(
|
||||
"Don't know how to combine functions that are referenced via AstAddrOfCFunc");
|
||||
}
|
||||
// LCOV_EXCL_END
|
||||
// Speed things up
|
||||
virtual void visit(AstNodeAssign*) override {}
|
||||
virtual void visit(AstNodeMath*) override {}
|
||||
@@ -215,6 +223,6 @@ public:
|
||||
|
||||
void V3Combine::combineAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CombineVisitor visitor(nodep); } // Destruct before checking
|
||||
{ CombineVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("combine", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Add common contents to modules
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2021 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
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Common's Transformations:
|
||||
//
|
||||
// Each class:
|
||||
// Create string access functions
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Common.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
//######################################################################
|
||||
// Common component builders
|
||||
|
||||
static void makeVlToString(AstClass* nodep) {
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
|
||||
funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep) + ">& obj");
|
||||
funcp->isMethod(false);
|
||||
funcp->isConst(false);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
AstNode* const exprp = new AstCMath{nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0};
|
||||
exprp->dtypeSetString();
|
||||
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
static void makeToString(AstClass* nodep) {
|
||||
AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"};
|
||||
funcp->isConst(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
AstNode* const exprp
|
||||
= new AstCMath{nodep->fileline(), R"(std::string("'{") + to_string_middle() + "}")", 0};
|
||||
exprp->dtypeSetString();
|
||||
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
static void makeToStringMiddle(AstClass* nodep) {
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{nodep->fileline(), "to_string_middle", nullptr, "std::string"};
|
||||
funcp->isConst(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "std::string out;\n"});
|
||||
std::string comma;
|
||||
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (auto* const varp = VN_CAST(itemp, Var)) {
|
||||
if (!varp->isParam()) {
|
||||
string stmt = "out += \"";
|
||||
stmt += comma;
|
||||
comma = ", ";
|
||||
stmt += itemp->origNameProtect();
|
||||
stmt += ":\" + ";
|
||||
if (itemp->isWide()) {
|
||||
stmt += "VL_TO_STRING_W(";
|
||||
stmt += cvtToStr(itemp->widthWords());
|
||||
stmt += ", ";
|
||||
} else {
|
||||
stmt += "VL_TO_STRING(";
|
||||
}
|
||||
stmt += itemp->nameProtect();
|
||||
stmt += ");\n";
|
||||
nodep->user1(true); // So what we extend dumps this
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
|
||||
string stmt = "out += \"";
|
||||
if (!comma.empty()) stmt += "\", \"+ ";
|
||||
// comma = ", "; // Nothing further so not needed
|
||||
stmt += nodep->extendsp()->dtypep()->nameProtect();
|
||||
stmt += "::to_string_middle();\n";
|
||||
nodep->user1(true); // So what we extend dumps this
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
|
||||
}
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "return out;\n"});
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// V3Common class functions
|
||||
|
||||
void V3Common::commonAll() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
// Create common contents for each module
|
||||
for (AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
|
||||
if (AstClass* const classp = VN_CAST(nodep, Class)) {
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstClass::user1() -> bool. True if class needs to_string dumper
|
||||
AstUser1InUse m_inuser1;
|
||||
// Create ToString methods
|
||||
makeVlToString(classp);
|
||||
makeToString(classp);
|
||||
makeToStringMiddle(classp);
|
||||
}
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("common", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Add common contents to modules
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2021 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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3COMMON_H_
|
||||
#define VERILATOR_V3COMMON_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Common final {
|
||||
public:
|
||||
static void commonAll();
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
+4
-4
@@ -201,7 +201,7 @@ public:
|
||||
}
|
||||
|
||||
void applyBlock(AstNodeBlock* nodep) {
|
||||
AstPragmaType pragma = AstPragmaType::COVERAGE_BLOCK_OFF;
|
||||
const AstPragmaType pragma = AstPragmaType::COVERAGE_BLOCK_OFF;
|
||||
if (!nodep->unnamed()) {
|
||||
for (const string& i : m_coverageOffBlocks) {
|
||||
if (VString::wildmatch(nodep->name(), i)) {
|
||||
@@ -297,14 +297,14 @@ public:
|
||||
|
||||
void applyBlock(AstNodeBlock* nodep) {
|
||||
// Apply to block at this line
|
||||
AstPragmaType pragma = AstPragmaType::COVERAGE_BLOCK_OFF;
|
||||
const AstPragmaType pragma = AstPragmaType::COVERAGE_BLOCK_OFF;
|
||||
if (lineMatch(nodep->fileline()->lineno(), pragma)) {
|
||||
nodep->addStmtsp(new AstPragma(nodep->fileline(), pragma));
|
||||
}
|
||||
}
|
||||
void applyCase(AstCase* nodep) {
|
||||
// Apply to this case at this line
|
||||
int lineno = nodep->fileline()->lineno();
|
||||
const int lineno = nodep->fileline()->lineno();
|
||||
if (lineMatch(lineno, AstPragmaType::FULL_CASE)) nodep->fullPragma(true);
|
||||
if (lineMatch(lineno, AstPragmaType::PARALLEL_CASE)) nodep->parallelPragma(true);
|
||||
}
|
||||
@@ -313,7 +313,7 @@ public:
|
||||
if (m_lastIgnore.lineno != filelinep->lineno()) {
|
||||
// UINFO(9, " ApplyIgnores for " << filelinep->ascii() << endl);
|
||||
// Process all on/offs for lines up to and including the current line
|
||||
int curlineno = filelinep->lastLineno();
|
||||
const int curlineno = filelinep->lastLineno();
|
||||
for (; m_lastIgnore.it != m_ignLines.end(); ++m_lastIgnore.it) {
|
||||
if (m_lastIgnore.it->m_lineno > curlineno) break;
|
||||
// UINFO(9, " Hit " << *m_lastIt << endl);
|
||||
|
||||
+680
-292
File diff suppressed because it is too large
Load Diff
+15
-14
@@ -92,7 +92,7 @@ private:
|
||||
// See also similar rule in V3TraceDecl::varIgnoreTrace
|
||||
if (!nodep->isToggleCoverable()) return "Not relevant signal type";
|
||||
if (!v3Global.opt.coverageUnderscore()) {
|
||||
string prettyName = nodep->prettyName();
|
||||
const string prettyName = nodep->prettyName();
|
||||
if (prettyName[0] == '_') return "Leading underscore";
|
||||
if (prettyName.find("._") != string::npos) return "Inlined leading underscore";
|
||||
}
|
||||
@@ -114,7 +114,7 @@ private:
|
||||
// Note the module name could have parameters appended, we'll consider this
|
||||
// a feature as it allows for each parameterized block to be counted separately.
|
||||
// Someday the user might be allowed to specify a different page suffix
|
||||
string page = page_prefix + "/" + m_modp->prettyName();
|
||||
const string page = page_prefix + "/" + m_modp->prettyName();
|
||||
|
||||
AstCoverDecl* declp = new AstCoverDecl(fl, page, comment, linescov, offset);
|
||||
declp->hier(hier);
|
||||
@@ -144,7 +144,7 @@ private:
|
||||
if (it == m_varnames.end()) {
|
||||
m_varnames.emplace(name, 1);
|
||||
} else {
|
||||
int suffix = (it->second)++;
|
||||
const int suffix = (it->second)++;
|
||||
name += "_" + cvtToStr(suffix);
|
||||
}
|
||||
return name;
|
||||
@@ -278,7 +278,7 @@ private:
|
||||
// We'll do this, and make the if(...) coverinc later.
|
||||
|
||||
// Add signal to hold the old value
|
||||
string newvarname = string("__Vtogcov__") + nodep->shortName();
|
||||
const string newvarname = string("__Vtogcov__") + nodep->shortName();
|
||||
AstVar* chgVarp
|
||||
= new AstVar(nodep->fileline(), AstVarType::MODULETEMP, newvarname, nodep);
|
||||
chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
|
||||
@@ -312,7 +312,7 @@ private:
|
||||
if (bdtypep->isRanged()) {
|
||||
for (int index_docs = bdtypep->lo(); index_docs < bdtypep->hi() + 1;
|
||||
++index_docs) {
|
||||
int index_code = index_docs - bdtypep->lo();
|
||||
const int index_code = index_docs - bdtypep->lo();
|
||||
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
|
||||
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code, 1),
|
||||
@@ -326,7 +326,7 @@ private:
|
||||
}
|
||||
} else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
|
||||
int index_code = index_docs - adtypep->lo();
|
||||
const int index_code = index_docs - adtypep->lo();
|
||||
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
|
||||
new AstArraySel(varp->fileline(),
|
||||
above.m_varRefp->cloneTree(true), index_code),
|
||||
@@ -339,7 +339,7 @@ private:
|
||||
} else if (AstPackArrayDType* adtypep = VN_CAST(dtypep, PackArrayDType)) {
|
||||
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
|
||||
AstNodeDType* subtypep = adtypep->subDTypep()->skipRefp();
|
||||
int index_code = index_docs - adtypep->lo();
|
||||
const int index_code = index_docs - adtypep->lo();
|
||||
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
|
||||
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code * subtypep->width(), subtypep->width()),
|
||||
@@ -354,7 +354,7 @@ private:
|
||||
for (AstMemberDType* itemp = adtypep->membersp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
|
||||
int index_code = itemp->lsb();
|
||||
const int index_code = itemp->lsb();
|
||||
ToggleEnt newent(above.m_comment + string(".") + itemp->name(),
|
||||
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
||||
index_code, subtypep->width()),
|
||||
@@ -385,17 +385,18 @@ private:
|
||||
UINFO(4, " IF: " << nodep << endl);
|
||||
if (m_state.m_on) {
|
||||
// An else-if. When we iterate the if, use "elsif" marking
|
||||
bool elsif = nodep->ifsp() && VN_IS(nodep->elsesp(), If) && !nodep->elsesp()->nextp();
|
||||
const bool elsif
|
||||
= nodep->ifsp() && VN_IS(nodep->elsesp(), If) && !nodep->elsesp()->nextp();
|
||||
if (elsif) VN_CAST(nodep->elsesp(), If)->user1(true);
|
||||
bool first_elsif = !nodep->user1() && elsif;
|
||||
bool cont_elsif = nodep->user1() && elsif;
|
||||
bool final_elsif = nodep->user1() && !elsif && nodep->elsesp();
|
||||
const bool first_elsif = !nodep->user1() && elsif;
|
||||
const bool cont_elsif = nodep->user1() && elsif;
|
||||
const bool final_elsif = nodep->user1() && !elsif && nodep->elsesp();
|
||||
//
|
||||
// Considered: If conditional is on a different line from if/else then we
|
||||
// can show it as part of line coverage of the statement
|
||||
// above. Otherwise show it based on what is inside.
|
||||
// But: Seemed too complicated, and fragile.
|
||||
CheckState lastState = m_state;
|
||||
const CheckState lastState = m_state;
|
||||
CheckState ifState;
|
||||
CheckState elseState;
|
||||
{
|
||||
@@ -542,6 +543,6 @@ public:
|
||||
|
||||
void V3Coverage::coverage(AstNetlist* rootp) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CoverageVisitor visitor(rootp); } // Destruct before checking
|
||||
{ CoverageVisitor visitor{rootp}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("coverage", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
@@ -114,6 +114,6 @@ public:
|
||||
|
||||
void V3CoverageJoin::coverageJoin(AstNetlist* rootp) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CoverageJoinVisitor visitor(rootp); } // Destruct before checking
|
||||
{ CoverageJoinVisitor visitor{rootp}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("coveragejoin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+6
-6
@@ -318,7 +318,7 @@ private:
|
||||
// And its children may now be killable too; correct counts
|
||||
// Recurse, as cells may not be directly under the module but in a generate
|
||||
if (!modp->dead()) { // If was dead didn't increment user1's
|
||||
DeadModVisitor visitor(modp);
|
||||
DeadModVisitor visitor{modp};
|
||||
}
|
||||
VL_DO_DANGLING(modp->unlinkFrBack()->deleteTree(), modp);
|
||||
retry = true;
|
||||
@@ -463,31 +463,31 @@ public:
|
||||
|
||||
void V3Dead::deadifyModules(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor visitor(nodep, false, false, false, false); } // Destruct before checking
|
||||
{ DeadVisitor visitor{nodep, false, false, false, false}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadModules", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor visitor(nodep, false, true, false, false); } // Destruct before checking
|
||||
{ DeadVisitor visitor{nodep, false, true, false, false}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadDtypes", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor visitor(nodep, false, true, true, false); } // Destruct before checking
|
||||
{ DeadVisitor visitor{nodep, false, true, true, false}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0,
|
||||
v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor visitor(nodep, true, true, false, true); } // Destruct before checking
|
||||
{ DeadVisitor visitor{nodep, true, true, false, true}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAll", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor visitor(nodep, true, true, true, true); } // Destruct before checking
|
||||
{ DeadVisitor visitor{nodep, true, true, true, true}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+8
-8
@@ -110,7 +110,7 @@ private:
|
||||
if (!lastrefp) {
|
||||
vscp->user5p(nodep);
|
||||
} else {
|
||||
bool last_was_blocking = lastrefp->user5();
|
||||
const bool last_was_blocking = lastrefp->user5();
|
||||
if (last_was_blocking != blocking) {
|
||||
AstNode* nonblockingp = blocking ? nodep : lastrefp;
|
||||
AstNode* blockingp = blocking ? lastrefp : nodep;
|
||||
@@ -228,7 +228,7 @@ private:
|
||||
UASSERT_OBJ(varrefp->varScopep(), varrefp, "Var didn't get varscoped in V3Scope.cpp");
|
||||
varrefp->unlinkFrBack();
|
||||
AstVar* oldvarp = varrefp->varp();
|
||||
int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
|
||||
const int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
|
||||
//
|
||||
std::deque<AstNode*> dimreadps; // Read value for each dimension of assignment
|
||||
for (unsigned dimension = 0; dimension < dimvalp.size(); dimension++) {
|
||||
@@ -236,8 +236,8 @@ private:
|
||||
if (VN_IS(dimp, Const)) { // bit = const, can just use it
|
||||
dimreadps.push_front(dimp);
|
||||
} else {
|
||||
string bitvarname = (string("__Vdlyvdim") + cvtToStr(dimension) + "__"
|
||||
+ oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
|
||||
const string bitvarname = (string("__Vdlyvdim") + cvtToStr(dimension) + "__"
|
||||
+ oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp
|
||||
= createVarSc(varrefp->varScopep(), bitvarname, dimp->width(), nullptr);
|
||||
AstAssign* bitassignp = new AstAssign(
|
||||
@@ -256,8 +256,8 @@ private:
|
||||
// vlsb = constant, can just push constant into where we use it
|
||||
bitreadp = lsbvaluep;
|
||||
} else {
|
||||
string bitvarname = (string("__Vdlyvlsb__") + oldvarp->shortName() + "__v"
|
||||
+ cvtToStr(modVecNum));
|
||||
const string bitvarname = (string("__Vdlyvlsb__") + oldvarp->shortName() + "__v"
|
||||
+ cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp
|
||||
= createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width(), nullptr);
|
||||
AstAssign* bitassignp = new AstAssign(
|
||||
@@ -440,7 +440,7 @@ private:
|
||||
checkActivePost(nodep, oldactivep);
|
||||
}
|
||||
if (!dlyvscp) { // First use of this delayed variable
|
||||
string newvarname = (string("__Vdly__") + nodep->varp()->shortName());
|
||||
const string newvarname = (string("__Vdly__") + nodep->varp()->shortName());
|
||||
dlyvscp = createVarSc(oldvscp, newvarname, 0, nullptr);
|
||||
AstNodeAssign* prep;
|
||||
AstBasicDType* basicp = oldvscp->dtypep()->basicp();
|
||||
@@ -510,6 +510,6 @@ public:
|
||||
|
||||
void V3Delayed::delayedAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DelayedVisitor visitor(nodep); } // Destruct before checking
|
||||
{ DelayedVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("delayed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+14
-20
@@ -29,6 +29,7 @@
|
||||
#include "V3Global.h"
|
||||
#include "V3Depth.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -39,11 +40,11 @@ private:
|
||||
// NODE STATE
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstCFunc* m_cfuncp = nullptr; // Current block
|
||||
AstNode* m_stmtp = nullptr; // Current statement
|
||||
int m_depth = 0; // How deep in an expression
|
||||
int m_maxdepth = 0; // Maximum depth in an expression
|
||||
V3UniqueNames m_tempNames; // For generating unique temporary variable names
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
@@ -51,18 +52,16 @@ private:
|
||||
void createDeepTemp(AstNode* nodep) {
|
||||
UINFO(6, " Deep " << nodep << endl);
|
||||
// if (debug() >= 9) nodep->dumpTree(cout, "deep:");
|
||||
|
||||
string newvarname = (string("__Vdeeptemp") + cvtToStr(m_modp->varNumGetInc()));
|
||||
AstVar* varp
|
||||
= new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep());
|
||||
AstVar* const varp = new AstVar{nodep->fileline(), AstVarType::STMTTEMP,
|
||||
m_tempNames.get(nodep), nodep->dtypep()};
|
||||
UASSERT_OBJ(m_cfuncp, nodep, "Deep expression not under a function");
|
||||
m_cfuncp->addInitsp(varp);
|
||||
// Replace node tree with reference to var
|
||||
AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, VAccess::READ);
|
||||
AstVarRef* const newp = new AstVarRef{nodep->fileline(), varp, VAccess::READ};
|
||||
nodep->replaceWith(newp);
|
||||
// Put assignment before the referencing statement
|
||||
AstAssign* assp = new AstAssign(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), varp, VAccess::WRITE), nodep);
|
||||
AstAssign* const assp = new AstAssign{
|
||||
nodep->fileline(), new AstVarRef{nodep->fileline(), varp, VAccess::WRITE}, nodep};
|
||||
AstNRelinker linker2;
|
||||
m_stmtp->unlinkFrBack(&linker2);
|
||||
assp->addNext(m_stmtp);
|
||||
@@ -70,21 +69,13 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
m_cfuncp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
{
|
||||
m_cfuncp = nodep;
|
||||
m_depth = 0;
|
||||
m_maxdepth = 0;
|
||||
m_tempNames.reset();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
@@ -128,7 +119,7 @@ private:
|
||||
// (Here instead of new visitor after V3Descope just to avoid another visitor)
|
||||
void needNonStaticFunc(AstNode* nodep) {
|
||||
UASSERT_OBJ(m_cfuncp, nodep, "Non-static accessor not under a function");
|
||||
if (m_cfuncp->isStatic().trueUnknown()) {
|
||||
if (m_cfuncp->isStatic()) {
|
||||
UINFO(5, "Mark non-public due to " << nodep << endl);
|
||||
m_cfuncp->isStatic(false);
|
||||
}
|
||||
@@ -149,7 +140,10 @@ private:
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit DepthVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
explicit DepthVisitor(AstNetlist* nodep)
|
||||
: m_tempNames{"__Vdeeptemp"} {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~DepthVisitor() override = default;
|
||||
};
|
||||
|
||||
@@ -158,6 +152,6 @@ public:
|
||||
|
||||
void V3Depth::depthAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DepthVisitor visitor(nodep); } // Destruct before checking
|
||||
{ DepthVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("depth", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
||||
}
|
||||
|
||||
+15
-11
@@ -48,20 +48,24 @@ private:
|
||||
AstCFunc* createDeepFunc(AstNode* nodep) {
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
// Create function
|
||||
string name = m_cfuncp->name() + "__deep" + cvtToStr(++m_deepNum);
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), name, nullptr);
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->symProlog(true);
|
||||
// Create sub function
|
||||
AstScope* const scopep = m_cfuncp->scopep();
|
||||
const string name = m_cfuncp->name() + "__deep" + cvtToStr(++m_deepNum);
|
||||
AstCFunc* const funcp = new AstCFunc(nodep->fileline(), name, scopep);
|
||||
funcp->slow(m_cfuncp->slow());
|
||||
funcp->isStatic(m_cfuncp->isStatic());
|
||||
funcp->isLoose(m_cfuncp->isLoose());
|
||||
funcp->addStmtsp(nodep);
|
||||
m_modp->addStmtp(funcp);
|
||||
// Call it at the point where the body was removed from
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), funcp);
|
||||
callp->argTypes("vlSymsp");
|
||||
scopep->addActivep(funcp);
|
||||
// Call sub function at the point where the body was removed from
|
||||
AstCCall* const callp = new AstCCall(nodep->fileline(), funcp);
|
||||
if (VN_IS(m_modp, Class)) {
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
callp->argTypes("vlSymsp");
|
||||
}
|
||||
UINFO(6, " New " << callp << endl);
|
||||
//
|
||||
relinkHandle.relink(callp);
|
||||
// Done
|
||||
return funcp;
|
||||
}
|
||||
|
||||
@@ -125,6 +129,6 @@ public:
|
||||
|
||||
void V3DepthBlock::depthBlockAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DepthBlockVisitor visitor(nodep); } // Destruct before checking
|
||||
{ DepthBlockVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deepblock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+73
-101
@@ -46,17 +46,17 @@ private:
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstScope* m_scopep = nullptr; // Current scope
|
||||
bool m_modSingleton = false; // m_modp is only instanced once
|
||||
bool m_allowThis = false; // Allow function non-static
|
||||
bool m_needThis = false; // Make function non-static
|
||||
const AstScope* m_scopep = nullptr; // Current scope
|
||||
const AstCFunc* m_funcp = nullptr; // Current function
|
||||
bool m_modSingleton = false; // m_modp is only instantiated once
|
||||
FuncMmap m_modFuncs; // Name of public functions added
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
static bool modIsSingleton(AstNodeModule* modp) {
|
||||
// True iff there's exactly one instance of this module in the design.
|
||||
// True iff there's exactly one instance of this module in the design (including top).
|
||||
if (modp->isTop()) return true;
|
||||
int instances = 0;
|
||||
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (VN_IS(stmtp, Scope)) {
|
||||
@@ -66,82 +66,38 @@ private:
|
||||
return (instances == 1);
|
||||
}
|
||||
|
||||
// Construct the best prefix to reference an object in 'scopep'
|
||||
// from a CFunc in 'm_scopep'. Result may be relative
|
||||
// ("this->[...]") or absolute ("vlTOPp->[...]").
|
||||
// Construct the best self pointer to reference an object in 'scopep' from a CFunc in
|
||||
// 'm_scopep'. Result may be relative ("this->[...]") or absolute ("vlSyms->[...]").
|
||||
//
|
||||
// Using relative references allows V3Combine'ing
|
||||
// code across multiple instances of the same module.
|
||||
//
|
||||
// Sets 'hierThisr' true if the object is local to this scope
|
||||
// (and could be made into a function-local later in V3Localize),
|
||||
// false if the object is in another scope.
|
||||
string descopedName(bool& hierThisr, string& hierUnprot, const AstScope* scopep,
|
||||
const AstVar* varp) {
|
||||
// Using relative references allows V3Combine'ing code across multiple instances of the same
|
||||
// module.
|
||||
string descopedSelfPointer(const AstScope* scopep) {
|
||||
UASSERT(scopep, "Var/Func not scoped");
|
||||
hierThisr = (scopep == m_scopep);
|
||||
UASSERT(!VN_IS(scopep->modp(), Class), "References to classes handled elsewhere");
|
||||
|
||||
// It's possible to disable relative references. This is a concession
|
||||
// to older compilers (gcc < 4.5.x) that don't understand __restrict__
|
||||
// well and emit extra ld/st to guard against pointer aliasing
|
||||
// when this-> and vlTOPp-> are mixed in the same function.
|
||||
//
|
||||
// "vlTOPp" is declared "restrict" so better compilers understand
|
||||
// that it won't alias with "this".
|
||||
bool relativeRefOk = v3Global.opt.relativeCFuncs();
|
||||
//
|
||||
// Static functions can't use this
|
||||
if (!m_allowThis) relativeRefOk = false;
|
||||
//
|
||||
// Use absolute refs in top-scoped routines, keep them static.
|
||||
// The DPI callback registration depends on representing top-level
|
||||
// static routines as plain function pointers. That breaks if those
|
||||
// become true OO routines.
|
||||
//
|
||||
// V3Combine wouldn't likely be able to combine top-level
|
||||
// routines anyway, so there's no harm in keeping these static.
|
||||
UASSERT_OBJ(m_modp, scopep, "Scope not under module");
|
||||
if (m_modp->isTop()) relativeRefOk = false;
|
||||
//
|
||||
// Use absolute refs if this scope is the only instance of the module.
|
||||
// Saves a bit of overhead on passing the 'this' pointer, and there's no
|
||||
// need to be nice to V3Combine when we have only a single instance.
|
||||
// The risk that this prevents combining identical logic from differently-
|
||||
// named but identical modules seems low.
|
||||
if (m_modSingleton) relativeRefOk = false;
|
||||
//
|
||||
// Class methods need relative
|
||||
if (m_modp && VN_IS(m_modp, Class)) relativeRefOk = true;
|
||||
// Static functions can't use relative references via 'this->'
|
||||
const bool relativeRefOk = !m_funcp->isStatic();
|
||||
|
||||
if (varp && varp->isFuncLocal()) {
|
||||
hierThisr = true;
|
||||
return ""; // Relative to function, not in this
|
||||
} else if (relativeRefOk && scopep == m_scopep) {
|
||||
m_needThis = true;
|
||||
return "this->";
|
||||
} else if (VN_IS(scopep->modp(), Class)) {
|
||||
hierUnprot = v3Global.opt.modPrefix() + "_"; // Prefix before protected part
|
||||
return scopep->modp()->name() + "::";
|
||||
} else if (relativeRefOk && scopep->aboveScopep() && scopep->aboveScopep() == m_scopep) {
|
||||
// Reference to scope of instance directly under this module, can just "cell->"
|
||||
UINFO(8, " Descope ref under " << m_scopep << endl);
|
||||
UINFO(8, " ref to " << scopep << endl);
|
||||
UINFO(8, " aboveScope " << scopep->aboveScopep() << endl);
|
||||
|
||||
if (relativeRefOk && scopep == m_scopep) {
|
||||
return "this";
|
||||
} else if (relativeRefOk && !m_modSingleton && scopep->aboveScopep() == m_scopep
|
||||
&& VN_IS(scopep->modp(), Module)) {
|
||||
// Reference to scope of instance directly under this module, can just "this->cell",
|
||||
// which can potentially be V3Combined, but note this requires one extra pointer
|
||||
// dereference which is slower, so we only use it if the source scope is not a
|
||||
// singleton.
|
||||
string name = scopep->name();
|
||||
string::size_type pos;
|
||||
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
|
||||
m_needThis = true;
|
||||
return name + "->";
|
||||
return "this->" + name;
|
||||
} else {
|
||||
// Reference to something elsewhere, or relative references
|
||||
// are disabled. Use global variable
|
||||
UINFO(8, " Descope " << scopep << endl);
|
||||
UINFO(8, " to " << scopep->name() << endl);
|
||||
UINFO(8, " under " << m_scopep->name() << endl);
|
||||
if (!scopep->aboveScopep()) { // Top
|
||||
// We could also return "vlSymsp->TOPp->" here, but GCC would
|
||||
// suspect aliases.
|
||||
return "vlTOPp->";
|
||||
} else {
|
||||
return scopep->nameVlSym() + ".";
|
||||
}
|
||||
// Reference to something elsewhere, or relative references are disabled. Use global
|
||||
// variable
|
||||
return "(&" + scopep->nameVlSym() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +106,7 @@ private:
|
||||
// If for any given name only one function exists, we can use that function directly.
|
||||
// If multiple functions exist, we need to select the appropriate scope.
|
||||
for (FuncMmap::iterator it = m_modFuncs.begin(); it != m_modFuncs.end(); ++it) {
|
||||
string name = it->first;
|
||||
const string name = it->first;
|
||||
AstCFunc* topFuncp = it->second;
|
||||
auto nextIt1 = it;
|
||||
++nextIt1;
|
||||
@@ -164,11 +120,6 @@ private:
|
||||
if (newfuncp->finalsp()) newfuncp->finalsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
newfuncp->name(name);
|
||||
newfuncp->isStatic(false);
|
||||
newfuncp->addInitsp(
|
||||
new AstCStmt(newfuncp->fileline(),
|
||||
EmitCBaseVisitor::symClassVar() + " = this->__VlSymsp;\n"));
|
||||
newfuncp->addInitsp(
|
||||
new AstCStmt(newfuncp->fileline(), EmitCBaseVisitor::symTopAssign() + "\n"));
|
||||
topFuncp->addNextHere(newfuncp);
|
||||
// In the body, call each function if it matches the given scope
|
||||
for (FuncMmap::iterator eachIt = it;
|
||||
@@ -177,7 +128,8 @@ private:
|
||||
AstCFunc* funcp = eachIt->second;
|
||||
auto nextIt2 = eachIt;
|
||||
++nextIt2;
|
||||
bool moreOfSame = (nextIt2 != m_modFuncs.end() && nextIt2->first == name);
|
||||
const bool moreOfSame
|
||||
= (nextIt2 != m_modFuncs.end() && nextIt2->first == name);
|
||||
UASSERT_OBJ(funcp->scopep(), funcp, "Not scoped");
|
||||
|
||||
UINFO(6, " Wrapping " << name << " " << funcp << endl);
|
||||
@@ -230,6 +182,10 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) override {
|
||||
nodep->dpiExportTriggerp(nullptr);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
@@ -252,42 +208,59 @@ private:
|
||||
}
|
||||
virtual void visit(AstNodeVarRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->varScopep()) {
|
||||
UASSERT_OBJ(nodep->varp()->isFuncLocal(), nodep,
|
||||
"unscoped reference can only appear to function locals at this point");
|
||||
return;
|
||||
}
|
||||
// Convert the hierch name
|
||||
UINFO(9, " ref-in " << nodep << endl);
|
||||
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
|
||||
bool hierThis;
|
||||
string hierUnprot;
|
||||
nodep->hiernameToProt(descopedName(hierThis /*ref*/, hierUnprot /*ref*/,
|
||||
nodep->varScopep()->scopep(),
|
||||
nodep->varScopep()->varp()));
|
||||
nodep->hiernameToUnprot(hierUnprot);
|
||||
nodep->hierThis(hierThis);
|
||||
const AstVar* const varp = nodep->varScopep()->varp();
|
||||
const AstScope* const scopep = nodep->varScopep()->scopep();
|
||||
if (varp->isFuncLocal()) {
|
||||
// Reference to function locals need no self pointer
|
||||
nodep->selfPointer("");
|
||||
} else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) {
|
||||
// Reference to constant pool value need no self pointer
|
||||
nodep->selfPointer("");
|
||||
} else if (VN_IS(scopep->modp(), Class)) {
|
||||
// Direct reference to class members are from within the class itself, references from
|
||||
// outside the class must go via AstMemberSel
|
||||
nodep->selfPointer("this");
|
||||
} else {
|
||||
nodep->selfPointer(descopedSelfPointer(scopep));
|
||||
}
|
||||
nodep->varScopep(nullptr);
|
||||
UINFO(9, " refout " << nodep << endl);
|
||||
}
|
||||
virtual void visit(AstNodeCCall* nodep) override {
|
||||
virtual void visit(AstCCall* nodep) override {
|
||||
// UINFO(9, " " << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
// Convert the hierch name
|
||||
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
|
||||
UASSERT_OBJ(nodep->funcp()->scopep(), nodep, "CFunc not under scope");
|
||||
bool hierThis;
|
||||
string hierUnprot;
|
||||
nodep->hiernameToProt(
|
||||
descopedName(hierThis /*ref*/, hierUnprot /*ref*/, nodep->funcp()->scopep(), nullptr));
|
||||
nodep->hiernameToUnprot(hierUnprot);
|
||||
const AstScope* const scopep = nodep->funcp()->scopep();
|
||||
if (VN_IS(scopep->modp(), Class)) {
|
||||
// Direct call to class methods are from within the class itself, method calls from
|
||||
// outside the class must go via AstCMethodCall
|
||||
nodep->selfPointer("this");
|
||||
} else {
|
||||
nodep->selfPointer(descopedSelfPointer(scopep));
|
||||
}
|
||||
// Can't do this, as we may have more calls later
|
||||
// nodep->funcp()->scopep(nullptr);
|
||||
}
|
||||
virtual void visit(AstCMethodCall* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstCNew* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_needThis);
|
||||
VL_RESTORER(m_allowThis);
|
||||
VL_RESTORER(m_funcp);
|
||||
if (!nodep->user1()) {
|
||||
m_needThis = false;
|
||||
m_allowThis = nodep->isStatic().falseUnknown(); // Non-static or unknown if static
|
||||
// Static functions should have been moved under the corresponding AstClassPackage
|
||||
UASSERT(!(nodep->isStatic() && VN_IS(m_modp, Class)),
|
||||
"Static function under AstClass");
|
||||
m_funcp = nodep;
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(true);
|
||||
if (m_needThis) nodep->isStatic(false);
|
||||
// If it's under a scope, move it up to the top
|
||||
if (m_scopep) {
|
||||
nodep->unlinkFrBack();
|
||||
@@ -316,7 +289,6 @@ public:
|
||||
|
||||
void V3Descope::descopeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
v3Global.assertScoped(false);
|
||||
{ DescopeVisitor visitor(nodep); } // Destruct before checking
|
||||
{ DescopeVisitor visitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("descope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ V3DupFinder::iterator V3DupFinder::findDuplicate(AstNode* nodep, V3DupFinderUser
|
||||
}
|
||||
|
||||
void V3DupFinder::dumpFile(const string& filename, bool tree) {
|
||||
const std::unique_ptr<std::ofstream> logp(V3File::new_ofstream(filename));
|
||||
const std::unique_ptr<std::ofstream> logp{V3File::new_ofstream(filename)};
|
||||
if (logp->fail()) v3fatal("Can't write " << filename);
|
||||
|
||||
std::unordered_map<int, int> dist;
|
||||
|
||||
-3994
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -27,10 +27,12 @@
|
||||
|
||||
class V3EmitC final {
|
||||
public:
|
||||
static void emitc();
|
||||
static void emitcConstPool();
|
||||
static void emitcHeaders();
|
||||
static void emitcImp();
|
||||
static void emitcInlines();
|
||||
static void emitcModel();
|
||||
static void emitcSyms(bool dpiHdrOnly = false);
|
||||
static void emitcTrace();
|
||||
static void emitcFiles();
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Emit C++ for tree
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2021 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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Task.h"
|
||||
|
||||
//######################################################################
|
||||
// EmitCParentModule implementation
|
||||
|
||||
EmitCParentModule::EmitCParentModule() {
|
||||
const auto setAll = [](AstNodeModule* modp) -> void {
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) { nodep->user4p(modp); }
|
||||
}
|
||||
};
|
||||
for (AstNode* modp = v3Global.rootp()->modulesp(); modp; modp = modp->nextp()) {
|
||||
setAll(VN_CAST(modp, NodeModule));
|
||||
}
|
||||
setAll(v3Global.rootp()->constPoolp()->modp());
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// EmitCBaseVisitor implementation
|
||||
|
||||
string EmitCBaseVisitor::funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) {
|
||||
modp = modp ? modp : EmitCParentModule::get(nodep);
|
||||
string name;
|
||||
if (nodep->isConstructor()) {
|
||||
name += prefixNameProtect(modp);
|
||||
} else if (nodep->isDestructor()) {
|
||||
name += "~";
|
||||
name += prefixNameProtect(modp);
|
||||
} else {
|
||||
if (nodep->isLoose()) {
|
||||
name += prefixNameProtect(modp);
|
||||
name += "__";
|
||||
}
|
||||
name += nodep->nameProtect();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
AstCFile* EmitCBaseVisitor::newCFile(const string& filename, bool slow, bool source) {
|
||||
AstCFile* cfilep = new AstCFile(v3Global.rootp()->fileline(), filename);
|
||||
cfilep->slow(slow);
|
||||
cfilep->source(source);
|
||||
v3Global.rootp()->addFilesp(cfilep);
|
||||
return cfilep;
|
||||
}
|
||||
|
||||
string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) {
|
||||
// Return argument list for given C function
|
||||
string args;
|
||||
if (nodep->isLoose() && !nodep->isStatic()) {
|
||||
if (nodep->isConst().trueKnown()) args += "const ";
|
||||
args += prefixNameProtect(EmitCParentModule::get(nodep));
|
||||
args += "* vlSelf";
|
||||
}
|
||||
if (!nodep->argTypes().empty()) {
|
||||
if (!args.empty()) args += ", ";
|
||||
args += nodep->argTypes();
|
||||
}
|
||||
// Might be a user function with argument list.
|
||||
for (const AstNode* stmtp = nodep->argsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (const AstVar* portp = VN_CAST_CONST(stmtp, Var)) {
|
||||
if (portp->isIO() && !portp->isFuncReturn()) {
|
||||
if (args != "") args += ", ";
|
||||
if (nodep->dpiImportPrototype() || nodep->dpiExportDispatcher()) {
|
||||
args += portp->dpiArgType(true, false);
|
||||
} else if (nodep->funcPublic()) {
|
||||
args += portp->cPubArgType(true, false);
|
||||
} else {
|
||||
args += portp->vlArgType(true, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp,
|
||||
bool withScope) {
|
||||
if (funcp->slow()) puts("VL_ATTR_COLD ");
|
||||
if (!funcp->isConstructor() && !funcp->isDestructor()) {
|
||||
puts(funcp->rtnTypeVoid());
|
||||
puts(" ");
|
||||
}
|
||||
if (withScope) {
|
||||
if (funcp->dpiExportDispatcher()) {
|
||||
puts(topClassName() + "::");
|
||||
} else if (funcp->isProperMethod()) {
|
||||
puts(prefixNameProtect(modp) + "::");
|
||||
}
|
||||
}
|
||||
puts(funcNameProtect(funcp, modp));
|
||||
puts("(" + cFuncArgs(funcp) + ")");
|
||||
if (funcp->isConst().trueKnown() && funcp->isProperMethod()) puts(" const");
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp,
|
||||
bool cLinkage) {
|
||||
ensureNewLine();
|
||||
if (!funcp->ifdef().empty()) puts("#ifdef " + funcp->ifdef() + "\n");
|
||||
if (cLinkage) puts("extern \"C\" ");
|
||||
if (funcp->isStatic() && funcp->isProperMethod()) puts("static ");
|
||||
if (funcp->isVirtual()) {
|
||||
UASSERT_OBJ(funcp->isProperMethod(), funcp, "Virtual function is not a proper method");
|
||||
puts("virtual ");
|
||||
}
|
||||
emitCFuncHeader(funcp, modp, /* withScope: */ false);
|
||||
puts(";\n");
|
||||
if (!funcp->ifdef().empty()) puts("#endif // " + funcp->ifdef() + "\n");
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, bool asRef) {
|
||||
const AstBasicDType* const basicp = nodep->basicp();
|
||||
bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType);
|
||||
|
||||
const auto emitDeclArrayBrackets = [this](const AstVar* nodep) -> void {
|
||||
// This isn't very robust and may need cleanup for other data types
|
||||
for (const AstUnpackArrayDType* arrayp
|
||||
= VN_CAST_CONST(nodep->dtypeSkipRefp(), UnpackArrayDType);
|
||||
arrayp; arrayp = VN_CAST_CONST(arrayp->subDTypep()->skipRefp(), UnpackArrayDType)) {
|
||||
puts("[" + cvtToStr(arrayp->elementsConst()) + "]");
|
||||
}
|
||||
};
|
||||
|
||||
if (nodep->isIO() && nodep->isSc()) {
|
||||
UASSERT_OBJ(basicp, nodep, "Unimplemented: Outputting this data type");
|
||||
if (nodep->attrScClocked() && nodep->isReadOnly()) {
|
||||
puts("sc_in_clk ");
|
||||
} else {
|
||||
if (nodep->isInoutish()) {
|
||||
puts("sc_inout<");
|
||||
} else if (nodep->isWritable()) {
|
||||
puts("sc_out<");
|
||||
} else if (nodep->isNonOutput()) {
|
||||
puts("sc_in<");
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown type");
|
||||
}
|
||||
puts(nodep->scType());
|
||||
puts("> ");
|
||||
}
|
||||
if (asRef) {
|
||||
if (refNeedParens) puts("(");
|
||||
puts("&");
|
||||
}
|
||||
puts(nodep->nameProtect());
|
||||
if (asRef && refNeedParens) { puts(")"); }
|
||||
emitDeclArrayBrackets(nodep);
|
||||
puts(";\n");
|
||||
} else if (nodep->isIO() && basicp && !basicp->isOpaque()) {
|
||||
if (nodep->isInoutish()) {
|
||||
puts("VL_INOUT");
|
||||
} else if (nodep->isWritable()) {
|
||||
puts("VL_OUT");
|
||||
} else if (nodep->isNonOutput()) {
|
||||
puts("VL_IN");
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown type");
|
||||
}
|
||||
|
||||
if (nodep->isQuad()) {
|
||||
puts("64");
|
||||
} else if (nodep->widthMin() <= 8) {
|
||||
puts("8");
|
||||
} else if (nodep->widthMin() <= 16) {
|
||||
puts("16");
|
||||
} else if (nodep->isWide()) {
|
||||
puts("W");
|
||||
refNeedParens = true;
|
||||
}
|
||||
|
||||
puts("(");
|
||||
if (asRef) {
|
||||
if (refNeedParens) puts("(");
|
||||
puts("&");
|
||||
}
|
||||
puts(nodep->nameProtect());
|
||||
if (asRef && refNeedParens) { puts(")"); }
|
||||
emitDeclArrayBrackets(nodep);
|
||||
// If it's a packed struct/array then nodep->width is the whole
|
||||
// thing, msb/lsb is just lowest dimension
|
||||
puts("," + cvtToStr(basicp->lo() + nodep->width() - 1) + "," + cvtToStr(basicp->lo()));
|
||||
if (nodep->isWide()) puts("," + cvtToStr(nodep->widthWords()));
|
||||
puts(");\n");
|
||||
} else {
|
||||
// strings and other fundamental c types
|
||||
if (nodep->isFuncLocal() && nodep->isString()) {
|
||||
const string name = nodep->name();
|
||||
const string suffix = V3Task::dpiTemporaryVarSuffix();
|
||||
// string temporary variable for DPI-C needs to be static because c_str() will be
|
||||
// passed to C code and the lifetime of the variable must be long enough. See also
|
||||
// Issue 2622.
|
||||
const bool beStatic = name.size() >= suffix.size()
|
||||
&& name.substr(name.size() - suffix.size()) == suffix;
|
||||
if (beStatic) puts("static VL_THREAD_LOCAL ");
|
||||
}
|
||||
puts(nodep->vlArgType(true, false, false, "", asRef));
|
||||
puts(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitModCUse(const AstNodeModule* modp, VUseType useType) {
|
||||
string nl;
|
||||
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (AstCUse* usep = VN_CAST(itemp, CUse)) {
|
||||
if (usep->useType() == useType) {
|
||||
if (usep->useType().isInclude()) {
|
||||
puts("#include \"" + prefixNameProtect(usep) + ".h\"\n");
|
||||
}
|
||||
if (usep->useType().isFwdClass()) {
|
||||
puts("class " + prefixNameProtect(usep) + ";\n");
|
||||
}
|
||||
nl = "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
puts(nl);
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitTextSection(const AstNodeModule* modp, AstType type) {
|
||||
int last_line = -999;
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstNodeText* textp = VN_CAST(nodep, NodeText)) {
|
||||
if (nodep->type() == type) {
|
||||
if (last_line != nodep->fileline()->lineno()) {
|
||||
if (last_line < 0) {
|
||||
puts("\n//*** Below code from `systemc in Verilog file\n");
|
||||
}
|
||||
putsDecoration(
|
||||
ifNoProtect("// From `systemc at " + nodep->fileline()->ascii() + "\n"));
|
||||
last_line = nodep->fileline()->lineno();
|
||||
}
|
||||
ofp()->putsNoTracking(textp->text());
|
||||
last_line++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (last_line > 0) puts("//*** Above code from `systemc in Verilog file\n\n");
|
||||
}
|
||||
+46
-43
@@ -27,6 +27,24 @@
|
||||
#include <cstdarg>
|
||||
#include <cmath>
|
||||
|
||||
//######################################################################
|
||||
// Set user4p in all CFunc and Var to point to the containing AstNodeModule
|
||||
|
||||
class EmitCParentModule final {
|
||||
// NODE STATE
|
||||
// AstFunc::user4p() AstNodeModule* Parent module pointer
|
||||
// AstVar::user4p() AstNodeModule* Parent module pointer
|
||||
AstUser4InUse user4InUse;
|
||||
|
||||
public:
|
||||
EmitCParentModule();
|
||||
VL_UNCOPYABLE(EmitCParentModule);
|
||||
|
||||
static const AstNodeModule* get(const AstNode* nodep) {
|
||||
return VN_CAST_CONST(nodep->user4p(), NodeModule);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Base Visitor class -- holds output file pointer
|
||||
|
||||
@@ -43,6 +61,7 @@ public:
|
||||
if (v3Global.opt.decoration()) puts(str);
|
||||
}
|
||||
void putsQuoted(const string& str) { ofp()->putsQuoted(str); }
|
||||
void ensureNewLine() { ofp()->ensureNewLine(); }
|
||||
bool optSystemC() { return v3Global.opt.systemC(); }
|
||||
static string protect(const string& name) { return VIdProtect::protectIf(name, true); }
|
||||
static string protectIf(const string& name, bool doIt) {
|
||||
@@ -52,59 +71,43 @@ public:
|
||||
return VIdProtect::protectWordsIf(name, doIt);
|
||||
}
|
||||
static string ifNoProtect(const string& in) { return v3Global.opt.protectIds() ? "" : in; }
|
||||
static string voidSelfAssign(const AstNodeModule* modp) {
|
||||
const string className = prefixNameProtect(modp);
|
||||
return className + "* const __restrict vlSelf VL_ATTR_UNUSED = static_cast<" + className
|
||||
+ "*>(voidSelf);\n";
|
||||
}
|
||||
static string symClassName() { return v3Global.opt.prefix() + "_" + protect("_Syms"); }
|
||||
static string symClassVar() { return symClassName() + "* __restrict vlSymsp"; }
|
||||
static string symTopAssign() {
|
||||
return v3Global.opt.prefix() + "* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;";
|
||||
}
|
||||
static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) {
|
||||
if (nodep->isConstructor()) {
|
||||
return prefixNameProtect(modp);
|
||||
} else if (nodep->isDestructor()) {
|
||||
return string("~") + prefixNameProtect(modp);
|
||||
} else {
|
||||
return nodep->nameProtect();
|
||||
}
|
||||
static string symClassAssign() {
|
||||
return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n";
|
||||
}
|
||||
static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp = nullptr);
|
||||
static string prefixNameProtect(const AstNode* nodep) { // C++ name with prefix
|
||||
const AstNodeModule* modp = VN_CAST_CONST(nodep, NodeModule);
|
||||
if (modp && modp->isTop()) {
|
||||
return v3Global.opt.prefix();
|
||||
} else {
|
||||
return v3Global.opt.modPrefix() + "_" + protect(nodep->name());
|
||||
}
|
||||
return v3Global.opt.modPrefix() + "_" + protect(nodep->name());
|
||||
}
|
||||
static string topClassName() { // Return name of top wrapper module
|
||||
return v3Global.opt.prefix();
|
||||
}
|
||||
static AstCFile* newCFile(const string& filename, bool slow, bool source) {
|
||||
AstCFile* cfilep = new AstCFile(v3Global.rootp()->fileline(), filename);
|
||||
cfilep->slow(slow);
|
||||
cfilep->source(source);
|
||||
v3Global.rootp()->addFilesp(cfilep);
|
||||
return cfilep;
|
||||
|
||||
static bool isConstPoolMod(const AstNode* modp) {
|
||||
return modp == v3Global.rootp()->constPoolp()->modp();
|
||||
}
|
||||
string cFuncArgs(const AstCFunc* nodep) {
|
||||
// Return argument list for given C function
|
||||
string args = nodep->argTypes();
|
||||
// Might be a user function with argument list.
|
||||
for (const AstNode* stmtp = nodep->argsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (const AstVar* portp = VN_CAST_CONST(stmtp, Var)) {
|
||||
if (portp->isIO() && !portp->isFuncReturn()) {
|
||||
if (args != "") args += ", ";
|
||||
if (nodep->dpiImport() || nodep->dpiExportWrapper()) {
|
||||
args += portp->dpiArgType(true, false);
|
||||
} else if (nodep->funcPublic()) {
|
||||
args += portp->cPubArgType(true, false);
|
||||
} else {
|
||||
args += portp->vlArgType(true, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return args;
|
||||
|
||||
static bool isAnonOk(const AstVar* varp) {
|
||||
return v3Global.opt.compLimitMembers() != 0 // Enabled
|
||||
&& !varp->isStatic() // Not a static variable
|
||||
&& !varp->isSc() // Aggregates can't be anon
|
||||
&& (varp->basicp() && !varp->basicp()->isOpaque()); // Aggregates can't be anon
|
||||
}
|
||||
|
||||
static AstCFile* newCFile(const string& filename, bool slow, bool source);
|
||||
string cFuncArgs(const AstCFunc* nodep);
|
||||
void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope);
|
||||
void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false);
|
||||
void emitVarDecl(const AstVar* nodep, bool asRef = false);
|
||||
void emitModCUse(const AstNodeModule* modp, VUseType useType);
|
||||
void emitTextSection(const AstNodeModule* modp, AstType type);
|
||||
|
||||
// CONSTRUCTORS
|
||||
EmitCBaseVisitor() = default;
|
||||
virtual ~EmitCBaseVisitor() override = default;
|
||||
@@ -119,7 +122,7 @@ private:
|
||||
int m_count = 0; // Number of statements
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
m_count++;
|
||||
++m_count;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Emit C++ for tree
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2021 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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Error.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
//######################################################################
|
||||
// Emitter that can emit constant initializer expressions
|
||||
|
||||
class EmitCConstInit VL_NOT_FINAL : public EmitCBaseVisitor {
|
||||
// MEMBERS
|
||||
bool m_inUnpacked = false;
|
||||
uint32_t m_unpackedWord = 0;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
protected:
|
||||
// VISITORS
|
||||
virtual void visit(AstInitArray* nodep) override {
|
||||
const AstUnpackArrayDType* const dtypep
|
||||
= VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType);
|
||||
UASSERT_OBJ(dtypep, nodep, "Array initializer has non-array dtype");
|
||||
const uint32_t size = dtypep->elementsConst();
|
||||
const uint32_t elemBytes = dtypep->subDTypep()->widthTotalBytes();
|
||||
const uint32_t tabMod = dtypep->subDTypep()->isString() ? 1 // String
|
||||
: elemBytes <= 2 ? 8 // CData, SData
|
||||
: elemBytes <= 4 ? 4 // IData
|
||||
: elemBytes <= 8 ? 2 // QData
|
||||
: 1;
|
||||
VL_RESTORER(m_inUnpacked);
|
||||
VL_RESTORER(m_unpackedWord);
|
||||
m_inUnpacked = true;
|
||||
// Note the double {{ initializer. The first { starts the initializer of the VlUnpacked,
|
||||
// and the second starts the initializer of m_storage within the VlUnpacked.
|
||||
puts("{");
|
||||
ofp()->putsNoTracking("{");
|
||||
puts("\n");
|
||||
for (uint32_t n = 0; n < size; ++n) {
|
||||
m_unpackedWord = n;
|
||||
if (n) puts((n % tabMod) ? ", " : ",\n");
|
||||
iterate(nodep->getIndexDefaultedValuep(n));
|
||||
}
|
||||
puts("\n");
|
||||
puts("}");
|
||||
ofp()->putsNoTracking("}");
|
||||
}
|
||||
|
||||
virtual void visit(AstInitItem* nodep) override { // LCOV_EXCL_START
|
||||
nodep->v3fatal("Handled by AstInitArray");
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
virtual void visit(AstConst* nodep) override {
|
||||
const V3Number& num = nodep->num();
|
||||
UASSERT_OBJ(!num.isFourState(), nodep, "4-state value in constant pool");
|
||||
AstNodeDType* const dtypep = nodep->dtypep();
|
||||
if (num.isString()) {
|
||||
// Note: putsQuoted does not track indentation, so we use this instead
|
||||
puts("\"");
|
||||
puts(num.toString());
|
||||
puts("\"");
|
||||
} else if (dtypep->isWide()) {
|
||||
const uint32_t size = dtypep->widthWords();
|
||||
// Note the double {{ initializer. The first { starts the initializer of the VlWide,
|
||||
// and the second starts the initializer of m_storage within the VlWide.
|
||||
puts("{");
|
||||
ofp()->putsNoTracking("{");
|
||||
if (m_inUnpacked) puts(" // VlWide " + cvtToStr(m_unpackedWord));
|
||||
puts("\n");
|
||||
for (uint32_t n = 0; n < size; ++n) {
|
||||
if (n) puts((n % 4) ? ", " : ",\n");
|
||||
ofp()->printf("0x%08" PRIx32, num.edataWord(n));
|
||||
}
|
||||
puts("\n");
|
||||
puts("}");
|
||||
ofp()->putsNoTracking("}");
|
||||
} else if (dtypep->isDouble()) {
|
||||
const double dnum = num.toDouble();
|
||||
const char* const fmt
|
||||
= !m_inUnpacked && (static_cast<int>(dnum) == dnum && -1000 < dnum && dnum < 1000)
|
||||
? "%3.1f" // Force decimal point
|
||||
: "%.17e"; // %e always yields a float literal
|
||||
ofp()->printf(fmt, dnum);
|
||||
} else if (dtypep->isQuad()) {
|
||||
const uint64_t qnum = static_cast<uint64_t>(num.toUQuad());
|
||||
const char* const fmt
|
||||
= !m_inUnpacked && (qnum < 10) ? ("%" PRIx64 "ULL") : ("0x%016" PRIx64 "ULL");
|
||||
ofp()->printf(fmt, qnum);
|
||||
} else {
|
||||
const uint32_t unum = num.toUInt();
|
||||
const char* const fmt = !m_inUnpacked && (unum < 10) ? ("%" PRIu32 "U")
|
||||
: (dtypep->widthMin() > 16) ? ("0x%08" PRIx32 "U")
|
||||
: (dtypep->widthMin() > 8) ? ("0x%04" PRIx32 "U")
|
||||
: ("0x%02" PRIx32 "U");
|
||||
ofp()->printf(fmt, unum);
|
||||
}
|
||||
}
|
||||
|
||||
// Default
|
||||
virtual void visit(AstNode* nodep) override { // LCOV_EXCL_START
|
||||
nodep->v3fatalSrc("Unknown node type reached EmitCConstInit: " << nodep->prettyTypeName());
|
||||
} // LCOV_EXCL_STOP
|
||||
};
|
||||
@@ -0,0 +1,122 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Emit C++ for tree
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2021 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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3EmitC.h"
|
||||
#include "V3EmitCConstInit.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
||||
//######################################################################
|
||||
// Const pool emitter
|
||||
|
||||
class EmitCConstPool final : public EmitCConstInit {
|
||||
// MEMBERS
|
||||
uint32_t m_outFileCount = 0;
|
||||
int m_outFileSize = 0;
|
||||
VDouble0 m_tablesEmitted;
|
||||
VDouble0 m_constsEmitted;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
V3OutCFile* newOutCFile() const {
|
||||
const string fileName = v3Global.opt.makeDir() + "/" + topClassName() + "__ConstPool_"
|
||||
+ cvtToStr(m_outFileCount) + ".cpp";
|
||||
newCFile(fileName, /* slow: */ true, /* source: */ true);
|
||||
V3OutCFile* const ofp = new V3OutCFile(fileName);
|
||||
ofp->putsHeader();
|
||||
ofp->puts("// DESCRIPTION: Verilator output: Constant pool\n");
|
||||
ofp->puts("//\n");
|
||||
ofp->puts("\n");
|
||||
ofp->puts("#include \"verilated.h\"\n");
|
||||
return ofp;
|
||||
}
|
||||
|
||||
void maybeSplitCFile() {
|
||||
if (v3Global.opt.outputSplit() && m_outFileSize < v3Global.opt.outputSplit()) return;
|
||||
// Splitting file, so using parallel build.
|
||||
v3Global.useParallelBuild(true);
|
||||
// Close current file
|
||||
VL_DO_DANGLING(delete m_ofp, m_ofp);
|
||||
// Open next file
|
||||
m_outFileSize = 0;
|
||||
++m_outFileCount;
|
||||
m_ofp = newOutCFile();
|
||||
}
|
||||
|
||||
void emitVars(const AstConstPool* poolp) {
|
||||
std::vector<const AstVar*> varps;
|
||||
for (AstNode* nodep = poolp->modp()->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { varps.push_back(varp); }
|
||||
}
|
||||
|
||||
if (varps.empty()) return; // Constant pool is empty, so we are done
|
||||
|
||||
stable_sort(varps.begin(), varps.end(), [](const AstVar* ap, const AstVar* bp) { //
|
||||
return ap->name() < bp->name();
|
||||
});
|
||||
|
||||
m_ofp = newOutCFile();
|
||||
|
||||
for (const AstVar* varp : varps) {
|
||||
maybeSplitCFile();
|
||||
const string nameProtect = topClassName() + "__ConstPool__" + varp->nameProtect();
|
||||
puts("\n");
|
||||
puts("extern const ");
|
||||
puts(varp->dtypep()->cType(nameProtect, false, false));
|
||||
puts(" = ");
|
||||
iterate(varp->valuep());
|
||||
puts(";\n");
|
||||
// Keep track of stats
|
||||
if (VN_IS(varp->dtypep(), UnpackArrayDType)) {
|
||||
++m_tablesEmitted;
|
||||
} else {
|
||||
++m_constsEmitted;
|
||||
}
|
||||
}
|
||||
|
||||
VL_DO_DANGLING(delete m_ofp, m_ofp);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstConst* nodep) override {
|
||||
m_outFileSize += nodep->num().isString() ? 10 : nodep->isWide() ? nodep->widthWords() : 1;
|
||||
EmitCConstInit::visit(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit EmitCConstPool(AstConstPool* poolp) {
|
||||
emitVars(poolp);
|
||||
V3Stats::addStatSum("ConstPool, Tables emitted", m_tablesEmitted);
|
||||
V3Stats::addStatSum("ConstPool, Constants emitted", m_constsEmitted);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// EmitC static functions
|
||||
|
||||
void V3EmitC::emitcConstPool() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
EmitCConstPool(v3Global.rootp()->constPoolp());
|
||||
}
|
||||
@@ -0,0 +1,804 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Emit C++ for tree
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2021 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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
#include "V3EmitCFunc.h"
|
||||
#include "V3TSP.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
// We use a static char array in VL_VALUE_STRING
|
||||
constexpr int VL_VALUE_STRING_MAX_WIDTH = 8192;
|
||||
|
||||
//######################################################################
|
||||
// EmitCFunc
|
||||
|
||||
bool EmitCFunc::emitSimpleOk(AstNodeMath* nodep) {
|
||||
// Can we put out a simple (A + B) instead of VL_ADD_III(A,B)?
|
||||
if (nodep->emitSimpleOperator() == "") return false;
|
||||
if (nodep->isWide()) return false;
|
||||
if (nodep->op1p()) {
|
||||
if (nodep->op1p()->isWide()) return false;
|
||||
}
|
||||
if (nodep->op2p()) {
|
||||
if (nodep->op2p()->isWide()) return false;
|
||||
}
|
||||
if (nodep->op3p()) {
|
||||
if (nodep->op3p()->isWide()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp, AstNode* rhsp,
|
||||
AstNode* thsp) {
|
||||
// Look at emitOperator() format for term/uni/dual/triops,
|
||||
// and write out appropriate text.
|
||||
// %n* node
|
||||
// %nq emitIQW on the [node]
|
||||
// %nw width in bits
|
||||
// %nW width in words
|
||||
// %ni iterate
|
||||
// %l* lhsp - if appropriate, then second char as above
|
||||
// %r* rhsp - if appropriate, then second char as above
|
||||
// %t* thsp - if appropriate, then second char as above
|
||||
// %k Potential line break
|
||||
// %P Wide temporary name
|
||||
// , Commas suppressed if the previous field is suppressed
|
||||
string nextComma;
|
||||
bool needComma = false;
|
||||
#define COMMA \
|
||||
do { \
|
||||
if (!nextComma.empty()) { \
|
||||
puts(nextComma); \
|
||||
nextComma = ""; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
putbs("");
|
||||
for (string::const_iterator pos = format.begin(); pos != format.end(); ++pos) {
|
||||
if (pos[0] == ',') {
|
||||
// Remember we need to add one, but don't do yet to avoid ",)"
|
||||
if (needComma) {
|
||||
if (pos[1] == ' ') {
|
||||
nextComma = ", ";
|
||||
} else
|
||||
nextComma = ",";
|
||||
needComma = false;
|
||||
}
|
||||
if (pos[1] == ' ') ++pos; // Must do even if no nextComma
|
||||
} else if (pos[0] == '%') {
|
||||
++pos;
|
||||
bool detail = false;
|
||||
AstNode* detailp = nullptr;
|
||||
switch (pos[0]) {
|
||||
case '%': puts("%"); break;
|
||||
case 'k': putbs(""); break;
|
||||
case 'n':
|
||||
detail = true;
|
||||
detailp = nodep;
|
||||
break;
|
||||
case 'l':
|
||||
detail = true;
|
||||
detailp = lhsp;
|
||||
break;
|
||||
case 'r':
|
||||
detail = true;
|
||||
detailp = rhsp;
|
||||
break;
|
||||
case 't':
|
||||
detail = true;
|
||||
detailp = thsp;
|
||||
break;
|
||||
case 'P':
|
||||
if (nodep->isWide()) {
|
||||
UASSERT_OBJ(m_wideTempRefp, nodep,
|
||||
"Wide Op w/ no temp, perhaps missing op in V3EmitC?");
|
||||
COMMA;
|
||||
if (!m_wideTempRefp->selfPointer().empty()) {
|
||||
emitDereference(m_wideTempRefp->selfPointerProtect(m_useSelfForThis));
|
||||
}
|
||||
puts(m_wideTempRefp->varp()->nameProtect());
|
||||
m_wideTempRefp = nullptr;
|
||||
needComma = true;
|
||||
}
|
||||
break;
|
||||
default: nodep->v3fatalSrc("Unknown emitOperator format code: %" << pos[0]); break;
|
||||
}
|
||||
if (detail) {
|
||||
// Get next letter of %[nlrt]
|
||||
++pos;
|
||||
switch (pos[0]) {
|
||||
case 'q': emitIQW(detailp); break;
|
||||
case 'w':
|
||||
COMMA;
|
||||
puts(cvtToStr(detailp->widthMin()));
|
||||
needComma = true;
|
||||
break;
|
||||
case 'W':
|
||||
if (lhsp->isWide()) {
|
||||
COMMA;
|
||||
puts(cvtToStr(lhsp->widthWords()));
|
||||
needComma = true;
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
COMMA;
|
||||
UASSERT_OBJ(detailp, nodep, "emitOperator() references undef node");
|
||||
iterateAndNextNull(detailp);
|
||||
needComma = true;
|
||||
break;
|
||||
default:
|
||||
nodep->v3fatalSrc("Unknown emitOperator format code: %[nlrt]" << pos[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (pos[0] == ')') {
|
||||
nextComma = "";
|
||||
puts(")");
|
||||
} else if (pos[0] == '(') {
|
||||
COMMA;
|
||||
needComma = false;
|
||||
puts("(");
|
||||
} else {
|
||||
// Normal text
|
||||
if (isalnum(pos[0])) needComma = true;
|
||||
COMMA;
|
||||
string s;
|
||||
s += pos[0];
|
||||
puts(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We only do one display at once, so can just use static state
|
||||
static struct EmitDispState {
|
||||
string m_format; // "%s" and text from user
|
||||
std::vector<char> m_argsChar; // Format of each argument to be printed
|
||||
std::vector<AstNode*> m_argsp; // Each argument to be printed
|
||||
std::vector<string> m_argsFunc; // Function before each argument to be printed
|
||||
EmitDispState() { clear(); }
|
||||
void clear() {
|
||||
m_format = "";
|
||||
m_argsChar.clear();
|
||||
m_argsp.clear();
|
||||
m_argsFunc.clear();
|
||||
}
|
||||
void pushFormat(const string& fmt) { m_format += fmt; }
|
||||
void pushFormat(char fmt) { m_format += fmt; }
|
||||
void pushArg(char fmtChar, AstNode* nodep, const string& func) {
|
||||
m_argsChar.push_back(fmtChar);
|
||||
m_argsp.push_back(nodep);
|
||||
m_argsFunc.push_back(func);
|
||||
}
|
||||
} emitDispState;
|
||||
|
||||
void EmitCFunc::displayEmit(AstNode* nodep, bool isScan) {
|
||||
if (emitDispState.m_format == ""
|
||||
&& VN_IS(nodep, Display)) { // not fscanf etc, as they need to return value
|
||||
// NOP
|
||||
} else {
|
||||
// Format
|
||||
bool isStmt = false;
|
||||
if (const AstFScanF* dispp = VN_CAST(nodep, FScanF)) {
|
||||
isStmt = false;
|
||||
puts("VL_FSCANF_IX(");
|
||||
iterate(dispp->filep());
|
||||
puts(",");
|
||||
} else if (const AstSScanF* dispp = VN_CAST(nodep, SScanF)) {
|
||||
isStmt = false;
|
||||
checkMaxWords(dispp->fromp());
|
||||
puts("VL_SSCANF_I");
|
||||
emitIQW(dispp->fromp());
|
||||
puts("X(");
|
||||
puts(cvtToStr(dispp->fromp()->widthMin()));
|
||||
puts(",");
|
||||
iterate(dispp->fromp());
|
||||
puts(",");
|
||||
} else if (const AstDisplay* dispp = VN_CAST(nodep, Display)) {
|
||||
isStmt = true;
|
||||
if (dispp->filep()) {
|
||||
puts("VL_FWRITEF(");
|
||||
iterate(dispp->filep());
|
||||
puts(",");
|
||||
} else {
|
||||
puts("VL_WRITEF(");
|
||||
}
|
||||
} else if (const AstSFormat* dispp = VN_CAST(nodep, SFormat)) {
|
||||
isStmt = true;
|
||||
puts("VL_SFORMAT_X(");
|
||||
puts(cvtToStr(dispp->lhsp()->widthMin()));
|
||||
putbs(",");
|
||||
iterate(dispp->lhsp());
|
||||
putbs(",");
|
||||
} else if (VN_IS(nodep, SFormatF)) {
|
||||
isStmt = false;
|
||||
puts("VL_SFORMATF_NX(");
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown displayEmit node type");
|
||||
}
|
||||
ofp()->putsQuoted(emitDispState.m_format);
|
||||
// Arguments
|
||||
for (unsigned i = 0; i < emitDispState.m_argsp.size(); i++) {
|
||||
const char fmt = emitDispState.m_argsChar[i];
|
||||
AstNode* argp = emitDispState.m_argsp[i];
|
||||
const string func = emitDispState.m_argsFunc[i];
|
||||
if (func != "" || argp) {
|
||||
puts(",");
|
||||
ofp()->indentInc();
|
||||
ofp()->putbs("");
|
||||
if (func != "") {
|
||||
puts(func);
|
||||
} else if (argp) {
|
||||
const bool addrof = isScan || (fmt == '@');
|
||||
if (addrof) puts("&(");
|
||||
iterate(argp);
|
||||
if (!addrof) emitDatap(argp);
|
||||
if (addrof) puts(")");
|
||||
}
|
||||
ofp()->indentDec();
|
||||
}
|
||||
}
|
||||
// End
|
||||
puts(")");
|
||||
if (isStmt) {
|
||||
puts(";\n");
|
||||
} else {
|
||||
puts(" ");
|
||||
}
|
||||
// Prep for next
|
||||
emitDispState.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const string& vfmt,
|
||||
bool ignore, char fmtLetter) {
|
||||
// Print display argument, edits elistp
|
||||
AstNode* argp = nullptr;
|
||||
if (!ignore) {
|
||||
argp = *elistp;
|
||||
// Prep for next parameter
|
||||
*elistp = (*elistp)->nextp();
|
||||
if (VL_UNCOVERABLE(!argp)) {
|
||||
// expectDisplay() checks this first, so internal error if found here
|
||||
dispp->v3error(
|
||||
"Internal: Missing arguments for $display-like format"); // LCOV_EXCL_LINE
|
||||
return; // LCOV_EXCL_LINE
|
||||
}
|
||||
if (argp->widthMin() > VL_VALUE_STRING_MAX_WIDTH) {
|
||||
dispp->v3error("Exceeded limit of " + cvtToStr(VL_VALUE_STRING_MAX_WIDTH)
|
||||
+ " bits for any $display-like arguments");
|
||||
}
|
||||
if (argp->widthMin() > 8 && fmtLetter == 'c') {
|
||||
// Technically legal, but surely not what the user intended.
|
||||
argp->v3warn(WIDTH, dispp->verilogKwd() << "of %c format of > 8 bit value");
|
||||
}
|
||||
}
|
||||
// string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter;
|
||||
string pfmt;
|
||||
if ((fmtLetter == '#' || fmtLetter == 'd') && !isScan
|
||||
&& vfmt == "") { // Size decimal output. Spec says leading spaces, not zeros
|
||||
const double mantissabits = ignore ? 0 : (argp->widthMin() - ((fmtLetter == 'd') ? 1 : 0));
|
||||
// This is log10(2**mantissabits) as log2(2**mantissabits)/log2(10),
|
||||
// + 1.0 rounding bias.
|
||||
double dchars = mantissabits / 3.321928094887362 + 1.0;
|
||||
if (fmtLetter == 'd') dchars++; // space for sign
|
||||
const int nchars = int(dchars);
|
||||
pfmt = string("%") + cvtToStr(nchars) + fmtLetter;
|
||||
} else {
|
||||
pfmt = string("%") + vfmt + fmtLetter;
|
||||
}
|
||||
emitDispState.pushFormat(pfmt);
|
||||
if (!ignore) {
|
||||
if (argp->dtypep()->basicp()->keyword() == AstBasicDTypeKwd::STRING) {
|
||||
// string in SystemVerilog is std::string in C++ which is not POD
|
||||
emitDispState.pushArg(' ', nullptr, "-1");
|
||||
} else {
|
||||
emitDispState.pushArg(' ', nullptr, cvtToStr(argp->widthMin()));
|
||||
}
|
||||
emitDispState.pushArg(fmtLetter, argp, "");
|
||||
if (fmtLetter == 't' || fmtLetter == '^') {
|
||||
AstSFormatF* fmtp = nullptr;
|
||||
if (AstDisplay* nodep = VN_CAST(dispp, Display))
|
||||
fmtp = nodep->fmtp();
|
||||
else if (AstSFormat* nodep = VN_CAST(dispp, SFormat))
|
||||
fmtp = nodep->fmtp();
|
||||
else
|
||||
fmtp = VN_CAST(dispp, SFormatF);
|
||||
UASSERT_OBJ(fmtp, dispp,
|
||||
"Use of %t must be under AstDisplay, AstSFormat, or AstSFormatF");
|
||||
UASSERT_OBJ(!fmtp->timeunit().isNone(), fmtp, "timenunit must be set");
|
||||
emitDispState.pushArg(' ', nullptr, cvtToStr((int)fmtp->timeunit().powerOfTen()));
|
||||
}
|
||||
} else {
|
||||
emitDispState.pushArg(fmtLetter, nullptr, "");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const string& vformat,
|
||||
AstNode* exprsp, bool isScan) {
|
||||
AstNode* elistp = exprsp;
|
||||
|
||||
// Convert Verilog display to C printf formats
|
||||
// "%0t" becomes "%d"
|
||||
emitDispState.clear();
|
||||
string vfmt;
|
||||
string::const_iterator pos = vformat.begin();
|
||||
bool inPct = false;
|
||||
bool ignore = false;
|
||||
for (; pos != vformat.end(); ++pos) {
|
||||
// UINFO(1, "Parse '" << *pos << "' IP" << inPct << " List " << cvtToHex(elistp) << endl);
|
||||
if (!inPct && pos[0] == '%') {
|
||||
inPct = true;
|
||||
ignore = false;
|
||||
vfmt = "";
|
||||
} else if (!inPct) { // Normal text
|
||||
emitDispState.pushFormat(*pos);
|
||||
} else { // Format character
|
||||
inPct = false;
|
||||
switch (tolower(pos[0])) {
|
||||
case '0': // FALLTHRU
|
||||
case '1': // FALLTHRU
|
||||
case '2': // FALLTHRU
|
||||
case '3': // FALLTHRU
|
||||
case '4': // FALLTHRU
|
||||
case '5': // FALLTHRU
|
||||
case '6': // FALLTHRU
|
||||
case '7': // FALLTHRU
|
||||
case '8': // FALLTHRU
|
||||
case '9': // FALLTHRU
|
||||
case '.': // FALLTHRU
|
||||
case '-':
|
||||
// Digits, like %5d, etc.
|
||||
vfmt += pos[0];
|
||||
inPct = true; // Get more digits
|
||||
break;
|
||||
case '%':
|
||||
emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the %
|
||||
break;
|
||||
case '*':
|
||||
vfmt += pos[0];
|
||||
inPct = true; // Get more digits
|
||||
ignore = true;
|
||||
break;
|
||||
// Special codes
|
||||
case '~':
|
||||
displayArg(nodep, &elistp, isScan, vfmt, ignore, 'd');
|
||||
break; // Signed decimal
|
||||
case '@':
|
||||
displayArg(nodep, &elistp, isScan, vfmt, ignore, '@');
|
||||
break; // Packed string
|
||||
// Spec: h d o b c l
|
||||
case 'b': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'b'); break;
|
||||
case 'c': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'c'); break;
|
||||
case 't': displayArg(nodep, &elistp, isScan, vfmt, ignore, 't'); break;
|
||||
case 'd':
|
||||
displayArg(nodep, &elistp, isScan, vfmt, ignore, '#');
|
||||
break; // Unsigned decimal
|
||||
case 'o': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'o'); break;
|
||||
case 'h': // FALLTHRU
|
||||
case 'x': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'x'); break;
|
||||
case 's': displayArg(nodep, &elistp, isScan, vfmt, ignore, 's'); break;
|
||||
case 'e': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'e'); break;
|
||||
case 'f': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'f'); break;
|
||||
case 'g': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'g'); break;
|
||||
case '^': displayArg(nodep, &elistp, isScan, vfmt, ignore, '^'); break; // Realtime
|
||||
case 'v': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'v'); break;
|
||||
case 'u': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'u'); break;
|
||||
case 'z': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'z'); break;
|
||||
case 'm': {
|
||||
UASSERT_OBJ(scopenamep, nodep, "Display with %m but no AstScopeName");
|
||||
const string suffix = scopenamep->scopePrettySymName();
|
||||
if (suffix == "") {
|
||||
emitDispState.pushFormat("%S");
|
||||
} else {
|
||||
emitDispState.pushFormat("%N"); // Add a . when needed
|
||||
}
|
||||
emitDispState.pushArg(' ', nullptr, "vlSymsp->name()");
|
||||
emitDispState.pushFormat(suffix);
|
||||
break;
|
||||
}
|
||||
case 'l': {
|
||||
// Better than not compiling
|
||||
emitDispState.pushFormat("----");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
nodep->v3error("Unknown $display-like format code: '%" << pos[0] << "'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (VL_UNCOVERABLE(elistp)) {
|
||||
// expectFormat also checks this, and should have found it first, so internal
|
||||
elistp->v3error("Internal: Extra arguments for $display-like format"); // LCOV_EXCL_LINE
|
||||
}
|
||||
displayEmit(nodep, isScan);
|
||||
}
|
||||
|
||||
void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer) {
|
||||
puts("(");
|
||||
bool comma = false;
|
||||
if (nodep->funcp()->isLoose() && !nodep->funcp()->isStatic()) {
|
||||
UASSERT_OBJ(!selfPointer.empty(), nodep, "Call to loose method without self pointer");
|
||||
puts(selfPointer);
|
||||
comma = true;
|
||||
}
|
||||
if (!nodep->argTypes().empty()) {
|
||||
if (comma) puts(", ");
|
||||
puts(nodep->argTypes());
|
||||
comma = true;
|
||||
}
|
||||
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
|
||||
if (comma) puts(", ");
|
||||
iterate(subnodep);
|
||||
comma = true;
|
||||
}
|
||||
if (VN_IS(nodep->backp(), NodeMath) || VN_IS(nodep->backp(), CReturn)) {
|
||||
// We should have a separate CCall for math and statement usage, but...
|
||||
puts(")");
|
||||
} else {
|
||||
puts(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCFunc::emitDereference(const string& pointer) {
|
||||
if (pointer[0] == '(' && pointer[1] == '&') {
|
||||
// remove "address of" followed by immediate dereference
|
||||
// Note: this relies on only the form '(&OBJECT)' being used by Verilator
|
||||
puts(pointer.substr(2, pointer.length() - 3));
|
||||
puts(".");
|
||||
} else {
|
||||
puts(pointer);
|
||||
puts("->");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCFunc::emitCvtPackStr(AstNode* nodep) {
|
||||
if (const AstConst* constp = VN_CAST(nodep, Const)) {
|
||||
putbs("std::string(");
|
||||
putsQuoted(constp->num().toString());
|
||||
puts(")");
|
||||
} else {
|
||||
putbs("VL_CVT_PACK_STR_N");
|
||||
emitIQW(nodep);
|
||||
puts("(");
|
||||
if (nodep->isWide()) {
|
||||
// Note argument width, not node width (which is always 32)
|
||||
puts(cvtToStr(nodep->widthWords()));
|
||||
puts(", ");
|
||||
}
|
||||
iterateAndNextNull(nodep);
|
||||
puts(")");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCFunc::emitCvtWideArray(AstNode* nodep, AstNode* fromp) {
|
||||
putbs("VL_CVT_W_A(");
|
||||
iterate(nodep);
|
||||
puts(", ");
|
||||
iterate(fromp);
|
||||
putbs(".atDefault()"); // Not accessed; only to get the proper type of values
|
||||
puts(")");
|
||||
}
|
||||
|
||||
void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string& assignString) {
|
||||
// Put out constant set to the specified variable, or given variable in a string
|
||||
if (nodep->num().isFourState()) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 4-state numbers in this context");
|
||||
} else if (nodep->num().isString()) {
|
||||
putbs("std::string(");
|
||||
putsQuoted(nodep->num().toString());
|
||||
puts(")");
|
||||
} else if (nodep->isWide()) {
|
||||
int upWidth = nodep->num().widthMin();
|
||||
int chunks = 0;
|
||||
if (upWidth > EMITC_NUM_CONSTW * VL_EDATASIZE) {
|
||||
// Output e.g. 8 words in groups of e.g. 8
|
||||
chunks = (upWidth - 1) / (EMITC_NUM_CONSTW * VL_EDATASIZE);
|
||||
upWidth %= (EMITC_NUM_CONSTW * VL_EDATASIZE);
|
||||
if (upWidth == 0) upWidth = (EMITC_NUM_CONSTW * VL_EDATASIZE);
|
||||
}
|
||||
{ // Upper e.g. 8 words
|
||||
if (chunks) {
|
||||
putbs("VL_CONSTHI_W_");
|
||||
puts(cvtToStr(VL_WORDS_I(upWidth)));
|
||||
puts("X(");
|
||||
puts(cvtToStr(nodep->widthMin()));
|
||||
puts(",");
|
||||
puts(cvtToStr(chunks * EMITC_NUM_CONSTW * VL_EDATASIZE));
|
||||
} else {
|
||||
putbs("VL_CONST_W_");
|
||||
puts(cvtToStr(VL_WORDS_I(upWidth)));
|
||||
puts("X(");
|
||||
puts(cvtToStr(nodep->widthMin()));
|
||||
}
|
||||
puts(",");
|
||||
if (!assigntop) {
|
||||
puts(assignString);
|
||||
} else if (VN_IS(assigntop, VarRef)) {
|
||||
if (!assigntop->selfPointer().empty()) {
|
||||
emitDereference(assigntop->selfPointerProtect(m_useSelfForThis));
|
||||
}
|
||||
puts(assigntop->varp()->nameProtect());
|
||||
} else {
|
||||
iterateAndNextNull(assigntop);
|
||||
}
|
||||
for (int word = VL_WORDS_I(upWidth) - 1; word >= 0; word--) {
|
||||
// Only 32 bits - llx + long long here just to appease CPP format warning
|
||||
ofp()->printf(",0x%08" VL_PRI64 "x",
|
||||
static_cast<vluint64_t>(
|
||||
nodep->num().edataWord(word + chunks * EMITC_NUM_CONSTW)));
|
||||
}
|
||||
puts(")");
|
||||
}
|
||||
for (chunks--; chunks >= 0; chunks--) {
|
||||
puts(";\n");
|
||||
putbs("VL_CONSTLO_W_");
|
||||
puts(cvtToStr(EMITC_NUM_CONSTW));
|
||||
puts("X(");
|
||||
puts(cvtToStr(chunks * EMITC_NUM_CONSTW * VL_EDATASIZE));
|
||||
puts(",");
|
||||
if (!assigntop) {
|
||||
puts(assignString);
|
||||
} else if (VN_IS(assigntop, VarRef)) {
|
||||
if (!assigntop->selfPointer().empty()) {
|
||||
emitDereference(assigntop->selfPointerProtect(m_useSelfForThis));
|
||||
}
|
||||
puts(assigntop->varp()->nameProtect());
|
||||
} else {
|
||||
iterateAndNextNull(assigntop);
|
||||
}
|
||||
for (int word = EMITC_NUM_CONSTW - 1; word >= 0; word--) {
|
||||
// Only 32 bits - llx + long long here just to appease CPP format warning
|
||||
ofp()->printf(",0x%08" VL_PRI64 "x",
|
||||
static_cast<vluint64_t>(
|
||||
nodep->num().edataWord(word + chunks * EMITC_NUM_CONSTW)));
|
||||
}
|
||||
puts(")");
|
||||
}
|
||||
} else if (nodep->isDouble()) {
|
||||
if (int(nodep->num().toDouble()) == nodep->num().toDouble()
|
||||
&& nodep->num().toDouble() < 1000 && nodep->num().toDouble() > -1000) {
|
||||
ofp()->printf("%3.1f", nodep->num().toDouble()); // Force decimal point
|
||||
} else {
|
||||
// Not %g as will not always put in decimal point, so not obvious to compiler
|
||||
// is a real number
|
||||
ofp()->printf("%.17e", nodep->num().toDouble());
|
||||
}
|
||||
} else if (nodep->isQuad()) {
|
||||
vluint64_t num = nodep->toUQuad();
|
||||
if (num < 10) {
|
||||
ofp()->printf("%" VL_PRI64 "uULL", num);
|
||||
} else {
|
||||
ofp()->printf("0x%" VL_PRI64 "xULL", num);
|
||||
}
|
||||
} else {
|
||||
uint32_t num = nodep->toUInt();
|
||||
// Only 32 bits - llx + long long here just to appease CPP format warning
|
||||
if (num < 10) {
|
||||
puts(cvtToStr(num));
|
||||
} else {
|
||||
ofp()->printf("0x%" VL_PRI64 "x", static_cast<vluint64_t>(num));
|
||||
}
|
||||
// If signed, we'll do our own functions
|
||||
// But must be here, or <= comparisons etc may end up signed
|
||||
puts("U");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCFunc::emitSetVarConstant(const string& assignString, AstConst* constp) {
|
||||
if (!constp->isWide()) {
|
||||
puts(assignString);
|
||||
puts(" = ");
|
||||
}
|
||||
emitConstant(constp, nullptr, assignString);
|
||||
puts(";\n");
|
||||
}
|
||||
|
||||
void EmitCFunc::emitVarReset(AstVar* varp) {
|
||||
AstNodeDType* const dtypep = varp->dtypep()->skipRefp();
|
||||
const string varNameProtected
|
||||
= VN_IS(m_modp, Class) ? varp->nameProtect() : "vlSelf->" + varp->nameProtect();
|
||||
if (varp->isIO() && m_modp->isTop() && optSystemC()) {
|
||||
// System C top I/O doesn't need loading, as the lower level subinst code does it.}
|
||||
} else if (varp->isParam()) {
|
||||
UASSERT_OBJ(varp->valuep(), varp, "No init for a param?");
|
||||
// If a simple CONST value we initialize it using an enum
|
||||
// If an ARRAYINIT we initialize it using an initial block similar to a signal
|
||||
// puts("// parameter "+varp->nameProtect()+" = "+varp->valuep()->name()+"\n");
|
||||
} else if (AstInitArray* initarp = VN_CAST(varp->valuep(), InitArray)) {
|
||||
if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
if (initarp->defaultp()) {
|
||||
puts("for (int __Vi=0; __Vi<" + cvtToStr(adtypep->elementsConst()));
|
||||
puts("; ++__Vi) {\n");
|
||||
emitSetVarConstant(varNameProtected + "[__Vi]",
|
||||
VN_CAST(initarp->defaultp(), Const));
|
||||
puts("}\n");
|
||||
}
|
||||
const AstInitArray::KeyItemMap& mapr = initarp->map();
|
||||
for (const auto& itr : mapr) {
|
||||
AstNode* valuep = itr.second->valuep();
|
||||
emitSetVarConstant(varNameProtected + "[" + cvtToStr(itr.first) + "]",
|
||||
VN_CAST(valuep, Const));
|
||||
}
|
||||
} else {
|
||||
varp->v3fatalSrc("InitArray under non-arrayed var");
|
||||
}
|
||||
} else {
|
||||
puts(emitVarResetRecurse(varp, varNameProtected, dtypep, 0, ""));
|
||||
}
|
||||
}
|
||||
|
||||
string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameProtected,
|
||||
AstNodeDType* dtypep, int depth, const string& suffix) {
|
||||
dtypep = dtypep->skipRefp();
|
||||
AstBasicDType* basicp = dtypep->basicp();
|
||||
// Returns string to do resetting, empty to do nothing (which caller should handle)
|
||||
if (AstAssocArrayDType* adtypep = VN_CAST(dtypep, AssocArrayDType)) {
|
||||
// Access std::array as C array
|
||||
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
||||
suffix + ".atDefault()" + cvtarray);
|
||||
} else if (VN_IS(dtypep, ClassRefDType)) {
|
||||
return ""; // Constructor does it
|
||||
} else if (AstDynArrayDType* adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
// Access std::array as C array
|
||||
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
||||
suffix + ".atDefault()" + cvtarray);
|
||||
} else if (AstQueueDType* adtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
// Access std::array as C array
|
||||
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
||||
suffix + ".atDefault()" + cvtarray);
|
||||
} else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
UASSERT_OBJ(adtypep->hi() >= adtypep->lo(), varp,
|
||||
"Should have swapped msb & lsb earlier.");
|
||||
const string ivar = string("__Vi") + cvtToStr(depth);
|
||||
const string pre = ("for (int " + ivar + "=" + cvtToStr(0) + "; " + ivar + "<"
|
||||
+ cvtToStr(adtypep->elementsConst()) + "; ++" + ivar + ") {\n");
|
||||
const string below = emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(),
|
||||
depth + 1, suffix + "[" + ivar + "]");
|
||||
const string post = "}\n";
|
||||
return below.empty() ? "" : pre + below + post;
|
||||
} else if (basicp && basicp->keyword() == AstBasicDTypeKwd::STRING) {
|
||||
// String's constructor deals with it
|
||||
return "";
|
||||
} else if (basicp) {
|
||||
bool zeroit
|
||||
= (varp->attrFileDescr() // Zero so we don't core dump if never $fopen
|
||||
|| (basicp && basicp->isZeroInit())
|
||||
|| (v3Global.opt.underlineZero() && !varp->name().empty() && varp->name()[0] == '_')
|
||||
|| (v3Global.opt.xInitial() == "fast" || v3Global.opt.xInitial() == "0"));
|
||||
splitSizeInc(1);
|
||||
if (dtypep->isWide()) { // Handle unpacked; not basicp->isWide
|
||||
string out;
|
||||
if (varp->valuep()) {
|
||||
AstConst* const constp = VN_CAST(varp->valuep(), Const);
|
||||
if (!constp) varp->v3fatalSrc("non-const initializer for variable");
|
||||
for (int w = 0; w < varp->widthWords(); ++w) {
|
||||
out += varNameProtected + suffix + "[" + cvtToStr(w) + "] = ";
|
||||
out += cvtToStr(constp->num().edataWord(w)) + "U;\n";
|
||||
}
|
||||
} else {
|
||||
out += zeroit ? "VL_ZERO_RESET_W(" : "VL_RAND_RESET_W(";
|
||||
out += cvtToStr(dtypep->widthMin());
|
||||
out += ", " + varNameProtected + suffix + ");\n";
|
||||
}
|
||||
return out;
|
||||
} else {
|
||||
string out = varNameProtected + suffix;
|
||||
// If --x-initial-edge is set, we want to force an initial
|
||||
// edge on uninitialized clocks (from 'X' to whatever the
|
||||
// first value is). Since the class is instantiated before
|
||||
// initial blocks are evaluated, this should not clash
|
||||
// with any initial block settings.
|
||||
if (zeroit || (v3Global.opt.xInitialEdge() && varp->isUsedClock())) {
|
||||
out += " = 0;\n";
|
||||
} else {
|
||||
out += " = VL_RAND_RESET_";
|
||||
out += dtypep->charIQWN();
|
||||
out += "(" + cvtToStr(dtypep->widthMin()) + ");\n";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
} else {
|
||||
v3fatalSrc("Unknown node type in reset generator: " << varp->prettyTypeName());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void EmitCFunc::doubleOrDetect(AstChangeDet* changep, bool& gotOne) {
|
||||
// cppcheck-suppress variableScope
|
||||
static int s_addDoubleOr = 10; // Determined experimentally as best
|
||||
if (!changep->rhsp()) {
|
||||
if (!gotOne) {
|
||||
gotOne = true;
|
||||
} else {
|
||||
puts(" | ");
|
||||
}
|
||||
iterateAndNextNull(changep->lhsp());
|
||||
} else {
|
||||
AstNode* lhsp = changep->lhsp();
|
||||
AstNode* rhsp = changep->rhsp();
|
||||
UASSERT_OBJ(VN_IS(lhsp, VarRef) || VN_IS(lhsp, ArraySel), changep, "Not ref?");
|
||||
UASSERT_OBJ(VN_IS(rhsp, VarRef) || VN_IS(rhsp, ArraySel), changep, "Not ref?");
|
||||
for (int word = 0; word < (changep->lhsp()->isWide() ? changep->lhsp()->widthWords() : 1);
|
||||
++word) {
|
||||
if (!gotOne) {
|
||||
gotOne = true;
|
||||
s_addDoubleOr = 10;
|
||||
puts("(");
|
||||
} else if (--s_addDoubleOr == 0) {
|
||||
puts("|| (");
|
||||
s_addDoubleOr = 10;
|
||||
} else {
|
||||
puts(" | (");
|
||||
}
|
||||
iterateAndNextNull(changep->lhsp());
|
||||
if (changep->lhsp()->isWide()) puts("[" + cvtToStr(word) + "]");
|
||||
if (changep->lhsp()->isDouble()) {
|
||||
puts(" != ");
|
||||
} else {
|
||||
puts(" ^ ");
|
||||
}
|
||||
iterateAndNextNull(changep->rhsp());
|
||||
if (changep->lhsp()->isWide()) puts("[" + cvtToStr(word) + "]");
|
||||
puts(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCFunc::emitChangeDet() {
|
||||
putsDecoration("// Change detection\n");
|
||||
puts("QData __req = false; // Logically a bool\n"); // But not because it results in
|
||||
// faster code
|
||||
bool gotOne = false;
|
||||
for (AstChangeDet* const changep : m_blkChangeDetVec) {
|
||||
if (changep->lhsp()) {
|
||||
if (!gotOne) { // Not a clocked block
|
||||
puts("__req |= (");
|
||||
} else {
|
||||
puts("\n");
|
||||
}
|
||||
doubleOrDetect(changep, gotOne);
|
||||
}
|
||||
}
|
||||
if (gotOne) puts(");\n");
|
||||
if (gotOne && !v3Global.opt.protectIds()) {
|
||||
// puts("VL_DEBUG_IF( if (__req) cout<<\"- CLOCKREQ );");
|
||||
for (AstChangeDet* nodep : m_blkChangeDetVec) {
|
||||
if (nodep->lhsp()) {
|
||||
puts("VL_DEBUG_IF( if(__req && (");
|
||||
bool gotOneIgnore = false;
|
||||
doubleOrDetect(nodep, gotOneIgnore);
|
||||
string varname;
|
||||
if (VN_IS(nodep->lhsp(), VarRef)) {
|
||||
varname = ": " + VN_CAST(nodep->lhsp(), VarRef)->varp()->prettyName();
|
||||
}
|
||||
puts(")) VL_DBG_MSGF(\" CHANGE: ");
|
||||
puts(protect(nodep->fileline()->filename()));
|
||||
puts(":" + cvtToStr(nodep->fileline()->lineno()));
|
||||
puts(varname + "\\n\"); );\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1244
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,340 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Emit C++ for tree
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2021 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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3EmitC.h"
|
||||
#include "V3EmitCConstInit.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
//######################################################################
|
||||
// Internal EmitC implementation
|
||||
|
||||
class EmitCHeader final : public EmitCConstInit {
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void decorateFirst(bool& first, const string& str) {
|
||||
if (first) {
|
||||
putsDecoration(str);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
void emitCellDecls(const AstNodeModule* modp) {
|
||||
bool first = true;
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstCell* const cellp = VN_CAST_CONST(nodep, Cell)) {
|
||||
decorateFirst(first, "// CELLS\n");
|
||||
puts(prefixNameProtect(cellp->modp()) + "* " + cellp->nameProtect() + ";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
void emitDesignVarDecls(const AstNodeModule* modp) {
|
||||
bool first = true;
|
||||
std::vector<const AstVar*> varList;
|
||||
bool lastAnon = false; // initial value is not important, but is used
|
||||
|
||||
const auto emitCurrentList = [this, &first, &varList, &lastAnon]() {
|
||||
if (varList.empty()) return;
|
||||
|
||||
decorateFirst(first, "\n// DESIGN SPECIFIC STATE\n");
|
||||
|
||||
if (lastAnon) { // Output as anons
|
||||
const int anonMembers = varList.size();
|
||||
const int lim = v3Global.opt.compLimitMembers();
|
||||
int anonL3s = 1;
|
||||
int anonL2s = 1;
|
||||
int anonL1s = 1;
|
||||
if (anonMembers > (lim * lim * lim)) {
|
||||
anonL3s = (anonMembers + (lim * lim * lim) - 1) / (lim * lim * lim);
|
||||
anonL2s = lim;
|
||||
anonL1s = lim;
|
||||
} else if (anonMembers > (lim * lim)) {
|
||||
anonL2s = (anonMembers + (lim * lim) - 1) / (lim * lim);
|
||||
anonL1s = lim;
|
||||
} else if (anonMembers > lim) {
|
||||
anonL1s = (anonMembers + lim - 1) / lim;
|
||||
}
|
||||
if (anonL1s != 1)
|
||||
puts("// Anonymous structures to workaround compiler member-count bugs\n");
|
||||
auto it = varList.cbegin();
|
||||
for (int l3 = 0; l3 < anonL3s && it != varList.cend(); ++l3) {
|
||||
if (anonL3s != 1) puts("struct {\n");
|
||||
for (int l2 = 0; l2 < anonL2s && it != varList.cend(); ++l2) {
|
||||
if (anonL2s != 1) puts("struct {\n");
|
||||
for (int l1 = 0; l1 < anonL1s && it != varList.cend(); ++l1) {
|
||||
if (anonL1s != 1) puts("struct {\n");
|
||||
for (int l0 = 0; l0 < lim && it != varList.cend(); ++l0) {
|
||||
emitVarDecl(*it);
|
||||
++it;
|
||||
}
|
||||
if (anonL1s != 1) puts("};\n");
|
||||
}
|
||||
if (anonL2s != 1) puts("};\n");
|
||||
}
|
||||
if (anonL3s != 1) puts("};\n");
|
||||
}
|
||||
// Leftovers, just in case off by one error somewhere above
|
||||
for (; it != varList.cend(); ++it) emitVarDecl(*it);
|
||||
} else { // Output as nonanons
|
||||
for (const auto& pair : varList) emitVarDecl(pair);
|
||||
}
|
||||
|
||||
varList.clear();
|
||||
};
|
||||
|
||||
// Emit variables in consecutive anon and non-anon batches
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) {
|
||||
if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp()) {
|
||||
const bool anon = isAnonOk(varp);
|
||||
if (anon != lastAnon) emitCurrentList();
|
||||
lastAnon = anon;
|
||||
varList.emplace_back(varp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Emit final batch
|
||||
emitCurrentList();
|
||||
}
|
||||
void emitInternalVarDecls(const AstNodeModule* modp) {
|
||||
if (!VN_IS(modp, Class)) {
|
||||
putsDecoration("\n// INTERNAL VARIABLES\n");
|
||||
puts(symClassName() + "* vlSymsp; // Symbol table\n");
|
||||
}
|
||||
}
|
||||
void emitParamDecls(const AstNodeModule* modp) {
|
||||
bool first = true;
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
|
||||
if (varp->isParam()) {
|
||||
decorateFirst(first, "\n// PARAMETERS\n");
|
||||
UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?");
|
||||
// Only C++ LiteralTypes can be constexpr
|
||||
const bool canBeConstexpr = varp->dtypep()->isLiteralType();
|
||||
puts("static ");
|
||||
puts(canBeConstexpr ? "constexpr " : "const ");
|
||||
puts(varp->dtypep()->cType(varp->nameProtect(), false, false));
|
||||
if (canBeConstexpr) {
|
||||
puts(" = ");
|
||||
iterate(varp->valuep());
|
||||
}
|
||||
puts(";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void emitCtorDtorDecls(const AstNodeModule* modp) {
|
||||
if (!VN_IS(modp, Class)) { // Classes use CFuncs with isConstructor/isDestructor
|
||||
const string& name = prefixNameProtect(modp);
|
||||
putsDecoration("\n// CONSTRUCTORS\n");
|
||||
puts(name + "(const char* name);\n");
|
||||
puts("~" + name + "();\n");
|
||||
puts("VL_UNCOPYABLE(" + name + ");\n");
|
||||
}
|
||||
}
|
||||
void emitInternalMethodDecls(const AstNodeModule* modp) {
|
||||
bool first = true;
|
||||
const string section = "\n// INTERNAL METHODS\n";
|
||||
|
||||
if (!VN_IS(modp, Class)) {
|
||||
decorateFirst(first, section);
|
||||
puts("void " + protect("__Vconfigure") + "(" + symClassName()
|
||||
+ "* symsp, bool first);\n");
|
||||
}
|
||||
|
||||
if (v3Global.opt.coverage()) {
|
||||
decorateFirst(first, section);
|
||||
puts("void __vlCoverInsert(");
|
||||
puts(v3Global.opt.threads() ? "std::atomic<uint32_t>" : "uint32_t");
|
||||
puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n");
|
||||
puts("const char* hierp, const char* pagep, const char* commentp, const char* "
|
||||
"linescovp);\n");
|
||||
}
|
||||
|
||||
if (v3Global.opt.savable()) {
|
||||
decorateFirst(first, section);
|
||||
puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n");
|
||||
puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n");
|
||||
}
|
||||
}
|
||||
void emitEnums(const AstNodeModule* modp) {
|
||||
bool first = true;
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
const AstTypedef* const tdefp = VN_CAST_CONST(nodep, Typedef);
|
||||
if (!tdefp) continue;
|
||||
if (!tdefp->attrPublic()) continue;
|
||||
const AstEnumDType* const edtypep
|
||||
= VN_CAST(tdefp->dtypep()->skipRefToEnump(), EnumDType);
|
||||
if (!edtypep) continue;
|
||||
decorateFirst(first, "\n// ENUMS (that were declared public)\n");
|
||||
if (edtypep->width() > 64) {
|
||||
putsDecoration("// enum " + tdefp->nameProtect() + " ignored: Too wide for C++\n");
|
||||
} else {
|
||||
puts("enum " + tdefp->name() + " {\n");
|
||||
for (const AstEnumItem* itemp = edtypep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), EnumItem)) {
|
||||
puts(itemp->nameProtect());
|
||||
puts(" = ");
|
||||
iterate(itemp->valuep());
|
||||
if (VN_IS(itemp->nextp(), EnumItem)) puts(",");
|
||||
puts("\n");
|
||||
}
|
||||
puts("};\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
void emitFuncDecls(const AstNodeModule* modp, bool inClassBody) {
|
||||
std::vector<const AstCFunc*> funcsp;
|
||||
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstCFunc* const funcp = VN_CAST_CONST(nodep, CFunc)) {
|
||||
if (funcp->dpiImportPrototype()) // Declared in __Dpi.h
|
||||
continue;
|
||||
if (funcp->dpiExportDispatcher()) // Declared in __Dpi.h
|
||||
continue;
|
||||
if (funcp->isMethod() != inClassBody) // Only methods go inside class
|
||||
continue;
|
||||
if (funcp->isMethod() && funcp->isLoose()) // Loose methods are declared lazily
|
||||
continue;
|
||||
funcsp.push_back(funcp);
|
||||
}
|
||||
}
|
||||
|
||||
std::stable_sort(funcsp.begin(), funcsp.end(), [](const AstNode* ap, const AstNode* bp) {
|
||||
return ap->name() < bp->name();
|
||||
});
|
||||
|
||||
for (const AstCFunc* const funcp : funcsp) {
|
||||
if (inClassBody) ofp()->putsPrivate(funcp->declPrivate());
|
||||
emitCFuncDecl(funcp, modp);
|
||||
}
|
||||
}
|
||||
void emitAll(const AstNodeModule* modp) {
|
||||
// Include files required by this AstNodeModule
|
||||
if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) {
|
||||
if (classp->extendsp())
|
||||
puts("#include \""
|
||||
+ prefixNameProtect(classp->extendsp()->classp()->classOrPackagep())
|
||||
+ ".h\"\n");
|
||||
}
|
||||
emitModCUse(modp, VUseType::INT_INCLUDE);
|
||||
|
||||
// Forward declarations required by this AstNodeModule
|
||||
puts("\nclass " + symClassName() + ";\n");
|
||||
emitModCUse(modp, VUseType::INT_FWD_CLASS);
|
||||
|
||||
// From `systemc_header
|
||||
emitTextSection(modp, AstType::atScHdr);
|
||||
|
||||
// Open class body {{{
|
||||
if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) {
|
||||
puts("class ");
|
||||
puts(prefixNameProtect(modp));
|
||||
if (classp->extendsp()) {
|
||||
puts(" : public ");
|
||||
puts(prefixNameProtect(classp->extendsp()->classp()));
|
||||
}
|
||||
} else {
|
||||
puts("VL_MODULE(" + prefixNameProtect(modp) + ")");
|
||||
}
|
||||
puts(" {\n");
|
||||
ofp()->resetPrivate();
|
||||
ofp()->putsPrivate(false); // public:
|
||||
|
||||
// Emit all class body contents
|
||||
emitCellDecls(modp);
|
||||
emitEnums(modp);
|
||||
emitDesignVarDecls(modp);
|
||||
emitInternalVarDecls(modp);
|
||||
emitParamDecls(modp);
|
||||
emitCtorDtorDecls(modp);
|
||||
emitInternalMethodDecls(modp);
|
||||
emitFuncDecls(modp, /* inClassBody: */ true);
|
||||
|
||||
// From `systemc_interface
|
||||
emitTextSection(modp, AstType::atScInt);
|
||||
|
||||
// Close class body
|
||||
if (!VN_IS(modp, Class)) {
|
||||
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
|
||||
} else {
|
||||
puts("};\n");
|
||||
}
|
||||
// }}}
|
||||
|
||||
// Emit out of class function declarations
|
||||
puts("\n");
|
||||
emitFuncDecls(modp, /* inClassBody: */ false);
|
||||
}
|
||||
|
||||
explicit EmitCHeader(const AstNodeModule* modp) {
|
||||
UINFO(5, " Emitting header for " << prefixNameProtect(modp) << endl);
|
||||
|
||||
// Open output file
|
||||
const string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(modp) + ".h";
|
||||
newCFile(filename, /* slow: */ false, /* source: */ false);
|
||||
m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) : new V3OutCFile(filename);
|
||||
|
||||
ofp()->putsHeader();
|
||||
puts("// DESCRIPTION: Verilator output: Design internal header\n");
|
||||
puts("// See " + topClassName() + ".h for the primary calling header\n");
|
||||
|
||||
ofp()->putsGuard();
|
||||
|
||||
// Include files
|
||||
puts("\n");
|
||||
ofp()->putsIntTopInclude();
|
||||
puts("#include \"verilated.h\"\n");
|
||||
if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n");
|
||||
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
|
||||
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
|
||||
|
||||
emitAll(modp);
|
||||
|
||||
if (const AstClassPackage* const packagep = VN_CAST_CONST(modp, ClassPackage)) {
|
||||
// Put the non-static class implementation in same h file for speed
|
||||
emitAll(packagep->classp());
|
||||
}
|
||||
|
||||
ofp()->putsEndGuard();
|
||||
|
||||
// Close output file
|
||||
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
||||
}
|
||||
virtual ~EmitCHeader() override = default;
|
||||
|
||||
public:
|
||||
static void main(const AstNodeModule* modp) { EmitCHeader emitCHeader(modp); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// EmitC class functions
|
||||
|
||||
void V3EmitC::emitcHeaders() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
|
||||
// Process each module in turn
|
||||
for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
|
||||
if (VN_IS(nodep, Class)) continue; // Declared with the ClassPackage
|
||||
EmitCHeader::main(VN_CAST_CONST(nodep, NodeModule));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user