mirror of
https://github.com/verilator/verilator.git
synced 2026-10-04 00:53:42 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
e3341e9a7c | ||
|
|
1abd339863 | ||
|
|
d22df4e907 | ||
|
|
ac19d552b1 | ||
|
|
58b4ed0995 | ||
|
|
7ef9b32faa | ||
|
|
24b3816f5e | ||
|
|
5555f20bd2 | ||
|
|
809701d7c6 | ||
|
|
6fd48da7cf | ||
|
|
b976b8dac9 | ||
|
|
c238d3da76 | ||
|
|
0edf1f0c94 | ||
|
|
31bb73e3de | ||
|
|
8f2e4f6bb0 | ||
|
|
2158a4573e | ||
|
|
1e89392e76 | ||
|
|
b0c1ac7ea2 | ||
|
|
1f19e8e206 | ||
|
|
258d9adfea | ||
|
|
fa063574d0 | ||
|
|
eea7e1bd2a | ||
|
|
fb561d925a | ||
|
|
9f5eb8f66e | ||
|
|
1f331bd94f | ||
|
|
2143bcfad5 | ||
|
|
e1f9fffb42 | ||
|
|
e4dcbb22e3 | ||
|
|
f4c1a7efeb | ||
|
|
ef9f477df2 | ||
|
|
2dd5ef5e8b | ||
|
|
a43eba7011 | ||
|
|
61c9866a1e | ||
|
|
63782556ae | ||
|
|
8814111724 | ||
|
|
fd35492226 | ||
|
|
a44d2b2570 | ||
|
|
aba3883092 | ||
|
|
9699192de8 | ||
|
|
0f7ec6c9ba | ||
|
|
471f14d26a | ||
|
|
bdc162db87 | ||
|
|
706842d093 | ||
|
|
31779b8b8b | ||
|
|
80d62adec7 | ||
|
|
38cab569ed | ||
|
|
828f78ece4 | ||
|
|
5e95cc9280 | ||
|
|
88fed4bc2f | ||
|
|
3718fe1ca1 | ||
|
|
a4ab3e12f6 | ||
|
|
9c85426e77 | ||
|
|
e3b20a3ea2 | ||
|
|
f7c23c4ade | ||
|
|
00cedf3797 | ||
|
|
1422c23434 | ||
|
|
1a6378291a | ||
|
|
267c6f6dce | ||
|
|
45fbd98ff1 | ||
|
|
f6c0108c86 | ||
|
|
37d68d39c8 | ||
|
|
5e56f4d11b | ||
|
|
2bf248bf60 | ||
|
|
39ce2f77f4 | ||
|
|
9797af0ad4 | ||
|
|
1e4839e5d1 | ||
|
|
b2139f65d8 | ||
|
|
44fd205e12 | ||
|
|
8624ce6a84 | ||
|
|
e68788d914 | ||
|
|
490f9f757d | ||
|
|
5290062549 | ||
|
|
53d9c30277 | ||
|
|
76494891cf | ||
|
|
008b8d5b56 | ||
|
|
d77c9da983 | ||
|
|
422c076fec | ||
|
|
12416bc0a3 | ||
|
|
4351abfe71 | ||
|
|
2022ec43cf |
+74
-63
@@ -16,120 +16,131 @@ env:
|
||||
CI_COMMIT: ${{ github.sha }}
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_MAXSIZE: 2Gi # 2GiB for clang and gcc, 4GiB in total
|
||||
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, ubuntu-16.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' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.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}
|
||||
- {os: ubuntu-16.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: ${{ matrix.os }}-${{ matrix.compiler.cc }}
|
||||
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: |
|
||||
./ci/ci-script.bash
|
||||
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
|
||||
max-parallel: 8
|
||||
matrix:
|
||||
os: ${{ fromJson(needs.Matrix.outputs.matrix) }}
|
||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04]
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
suite:
|
||||
- dist-vlt-0
|
||||
- dist-vlt-1
|
||||
- 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' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.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}
|
||||
- {os: ubuntu-16.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: ${{ matrix.os }}-${{ matrix.compiler.cc }}
|
||||
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:
|
||||
|
||||
@@ -11,10 +11,14 @@ on:
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CI_COMMIT: ${{ github.sha }}
|
||||
CCACHE_COMPRESS: 1
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_MAXSIZE: 2Gi # 2GiB for clang and gcc, 4GiB in total
|
||||
CI_M32: 0
|
||||
COVERAGE: 1
|
||||
VERILATOR_ARCHIVE: verilator-coverage-${{ github.sha }}.tar.gz
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: repo
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -24,34 +28,28 @@ jobs:
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CACHE_KEY: ubuntu-20.04-${{ matrix.compiler.cc }}-coverage
|
||||
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: coverage-${{ env.cache-name }}
|
||||
path: repo
|
||||
|
||||
- name: Install packages for build
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
run: ./ci/ci-install.bash
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
./ci/ci-script.bash
|
||||
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:
|
||||
@@ -80,28 +78,20 @@ jobs:
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: test
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CACHE_KEY: ubuntu-20.04-${{ matrix.compiler.cc }}-coverage
|
||||
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: coverage-${{ 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:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# DESCRIPTION: Github actions config
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
name: clang-format
|
||||
name: format
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -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,18 +27,18 @@ 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: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
git commit . -m "Apply clang-format" &&
|
||||
git commit . -m "Apply 'make format'" &&
|
||||
git push origin
|
||||
fi
|
||||
@@ -8,6 +8,70 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
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
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add 'make ccache-report' (#3011). [Geza Lore]
|
||||
* Add --reloop-limit argument (#2943) (#2960). [Geza Lore]
|
||||
* Add --expand-limit argument (#3005). [Julien Margetts]
|
||||
* Add TRACE_THREADS to CMake (#2934). [Jonathan Drolet]
|
||||
* Optimize large lookup tables to static data (#2925). [Geza Lore]
|
||||
* Optimize reloop to accept constant index offsets (#2939). [Geza Lore]
|
||||
* Split always blocks to better respect --output-split-cfuncs. [Geza Lore]
|
||||
* Support ignoring "`pragma protect ..." (#2886). [Udi Finkelstein]
|
||||
* Support --trace-fst for SystemC with CMake (#2927). [Jonathan Drolet]
|
||||
* Update cmake latest C++ Standard Compilation flag (#2951). [Ameya Vikram Singh]
|
||||
* Prep work towards better ccache hashing/performance. [Geza Lore]
|
||||
* Fix assertion failure in bitOpTree optimization (#2891) (#2899). [Raynard Qiao]
|
||||
* Fix DPI functions not seen as vpiModule (#2893). [Todd Strader]
|
||||
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski]
|
||||
* Fix slowdown in elaboration (#2911). [Nathan Graybeal]
|
||||
* Fix initialization of assoc in assoc array (#2914). [myftptoyman]
|
||||
* Fix make support for gmake 3.x (#2920) (#2921). [Philipp Wagner]
|
||||
* Fix VPI memory access for packed arrays (#2922). [Todd Strader]
|
||||
* Fix MCD close also closing stdout (#2931). [Alexander Grobman]
|
||||
* Fix split procedures to better respect --output-split-cfuncs (#2942). [Geza Lore]
|
||||
* Fix to emit 'else if' without nesting (#2944). [Geza Lore]
|
||||
* Fix part select issues in LATCH warning (#2948) (#2938). [Julien Margetts]
|
||||
* Fix to not emit empty files with low split limits (#2961). [Geza Lore]
|
||||
* Fix merging of assignments in C++ code (#2970). [Ruper Swarbrick]
|
||||
* Fix unused variable warnings (#2991). [Pieter Kapsenberg]
|
||||
* Fix --protect-ids when using SV classes (#2994). [Geza Lore]
|
||||
* Fix constant function calls with uninit value (#2995). [yanx21]
|
||||
* Fix Makefiles to support Windows EXEEXT usage (#3008). [Miodrag Milanovic]
|
||||
|
||||
|
||||
Verilator 4.202 2021-04-24
|
||||
==========================
|
||||
|
||||
@@ -331,7 +395,7 @@ Verilator 4.034 2020-05-03
|
||||
* Support event data type (with some restrictions).
|
||||
* Support $root. (#2150) [Keyi Zhang]
|
||||
* Add error if use SystemC 2.2 and earlier (pre-2011) as is deprecated.
|
||||
* Fix build of fast path tracing code to use OPT_FAST. (#2245) [Geza Lore]
|
||||
* Add support of --trace-structs for CMake (#2986). [Martin Schmidt]
|
||||
* Fix arrayed instances connecting to slices. (#2263) [Don/engr248]
|
||||
* Fix error on unpacked connecting to packed. (#2288) [Joseph Shaker]
|
||||
* Fix logical not optimization with empty begin. (#2291) [Baltazar Ortiz]
|
||||
|
||||
+25
-20
@@ -44,6 +44,7 @@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
HOST = @HOST@
|
||||
EXEEXT = @EXEEXT@
|
||||
|
||||
DOXYGEN = doxygen
|
||||
INSTALL = @INSTALL@
|
||||
@@ -130,6 +131,7 @@ DISTFILES1 = $(INFOS) .gitignore \
|
||||
verilator-config.cmake.in \
|
||||
verilator-config-version.cmake.in \
|
||||
bin/verilator \
|
||||
bin/verilator_ccache_report \
|
||||
bin/verilator_coverage \
|
||||
bin/verilator_difftree \
|
||||
bin/verilator_gantt \
|
||||
@@ -193,6 +195,7 @@ DISTFILES2 = \
|
||||
|
||||
INST_PROJ_FILES = \
|
||||
bin/verilator \
|
||||
bin/verilator_ccache_report \
|
||||
bin/verilator_coverage \
|
||||
bin/verilator_gantt \
|
||||
bin/verilator_includer \
|
||||
@@ -203,9 +206,9 @@ INST_PROJ_FILES = \
|
||||
include/vltstd/*.[chv]* \
|
||||
|
||||
INST_PROJ_BIN_FILES = \
|
||||
bin/verilator_bin \
|
||||
bin/verilator_bin_dbg \
|
||||
bin/verilator_coverage_bin_dbg \
|
||||
bin/verilator_bin$(EXEEXT) \
|
||||
bin/verilator_bin_dbg$(EXEEXT) \
|
||||
bin/verilator_coverage_bin_dbg$(EXEEXT) \
|
||||
|
||||
EXAMPLES_FIRST = \
|
||||
examples/make_hello_c \
|
||||
@@ -221,13 +224,13 @@ all: all_nomsg msg_test
|
||||
all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
|
||||
|
||||
.PHONY:verilator_exe
|
||||
.PHONY:verilator_bin
|
||||
.PHONY:verilator_bin_dbg
|
||||
.PHONY:verilator_coverage_bin_dbg
|
||||
verilator_exe verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg:
|
||||
.PHONY:verilator_bin$(EXEEXT)
|
||||
.PHONY:verilator_bin_dbg$(EXEEXT)
|
||||
.PHONY:verilator_coverage_bin_dbg$(EXEEXT)
|
||||
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
|
||||
@@ -280,8 +283,8 @@ verilator.pdf: Makefile
|
||||
$(MAKE) -C docs verilator.pdf
|
||||
|
||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||
VL_INST_BIN_FILES = verilator verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg \
|
||||
verilator_coverage verilator_gantt verilator_includer verilator_profcfunc
|
||||
VL_INST_BIN_FILES = verilator verilator_bin$(EXEEXT) verilator_bin_dbg$(EXEEXT) verilator_coverage_bin_dbg$(EXEEXT) \
|
||||
verilator_ccache_report verilator_coverage verilator_gantt verilator_includer verilator_profcfunc
|
||||
# Some scripts go into both the search path and pkgdatadir,
|
||||
# so they can be found by the user, and under $VERILATOR_ROOT.
|
||||
|
||||
@@ -305,11 +308,12 @@ installbin:
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage $(DESTDIR)$(bindir)/verilator_coverage )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_gantt $(DESTDIR)$(bindir)/verilator_gantt )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_profcfunc $(DESTDIR)$(bindir)/verilator_profcfunc )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin $(DESTDIR)$(bindir)/verilator_bin )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin_dbg $(DESTDIR)$(bindir)/verilator_bin_dbg )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin$(EXEEXT) )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin_dbg$(EXEEXT) )
|
||||
( cd bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg$(EXEEXT) )
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/bin
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_includer $(DESTDIR)$(pkgdatadir)/bin/verilator_includer )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_ccache_report $(DESTDIR)$(pkgdatadir)/bin/verilator_ccache_report )
|
||||
|
||||
# Man files can either be part of the original kit, or built in current directory
|
||||
# So important we use $^ so VPATH is searched
|
||||
@@ -458,7 +462,7 @@ clang-tidy: $(CLANGTIDY_DEP)
|
||||
|
||||
analyzer-src:
|
||||
-rm -rf src/obj_dbg
|
||||
scan-build $(MAKE) -k verilator_coverage_bin_dbg verilator_bin_dbg
|
||||
scan-build $(MAKE) -k verilator_coverage_bin_dbg$(EXEEXT) verilator_bin_dbg$(EXEEXT)
|
||||
|
||||
analyzer-include:
|
||||
-rm -rf examples/*/obj*
|
||||
@@ -466,28 +470,29 @@ 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 = \
|
||||
bin/verilator_ccache_report \
|
||||
examples/xml_py/vl_file_copy \
|
||||
examples/xml_py/vl_hier_graph \
|
||||
docs/guide/conf.py \
|
||||
docs/guide/vl_sphinx_extract \
|
||||
docs/guide/vl_sphinx_extract \
|
||||
docs/guide/vl_doxygen_filter \
|
||||
docs/bin/vl_sphinx_extract \
|
||||
docs/bin/vl_sphinx_fix \
|
||||
src/astgen \
|
||||
src/bisonpre \
|
||||
src/config_rev \
|
||||
src/cppcheck_filtered \
|
||||
src/flexfix \
|
||||
src/vlcovgen \
|
||||
test_regress/t/*.pf \
|
||||
nodist/code_coverage \
|
||||
nodist/dot_importer \
|
||||
nodist/fuzzer/actual_fail \
|
||||
|
||||
+4
-2
@@ -315,6 +315,7 @@ detailed descriptions of these arguments.
|
||||
-E Preprocess, but do not compile
|
||||
--error-limit <value> Abort after this number of errors
|
||||
--exe Link to create executable
|
||||
--expand-limit <value> Set expand optimization limit
|
||||
-F <file> Parse arguments from a file, relatively
|
||||
-f <file> Parse arguments from a file
|
||||
-FI <file> Force include of a file
|
||||
@@ -331,7 +332,6 @@ 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
|
||||
-LDFLAGS <flags> Linker pre-object arguments for makefile
|
||||
--l2-name <value> Verilog scope name of the top module
|
||||
@@ -343,6 +343,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
|
||||
@@ -366,6 +367,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
|
||||
@@ -377,7 +379,7 @@ 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
|
||||
--savable Enable model save-restore
|
||||
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0103,C0114,C0115,C0116,C0123,C0301,R0902,R0913,R0914,R0912,R0915,W0621
|
||||
######################################################################
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
allow_abbrev=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description="""Report ccache behavior of a Verilated model build.""",
|
||||
epilog=
|
||||
"""Copyright 2002-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""")
|
||||
|
||||
parser.add_argument('-o',
|
||||
type=argparse.FileType('w'),
|
||||
metavar="OUTFILE",
|
||||
required=True,
|
||||
help='output file')
|
||||
parser.add_argument('logdir', type=pathlib.Path, help='log directory')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
results = {}
|
||||
elapsed = {}
|
||||
|
||||
|
||||
def toDateTime(s):
|
||||
return datetime.strptime(s, "%Y-%m-%dT%H:%M:%S.%f")
|
||||
|
||||
|
||||
for logfile in args.logdir.iterdir():
|
||||
with logfile.open() as fh:
|
||||
start = None
|
||||
obj = None
|
||||
for line in fh:
|
||||
line = line.strip()
|
||||
match = re.match(r'\[(\S+)\s.*Object file: (.*)$', line)
|
||||
if match:
|
||||
start = toDateTime(match.group(1))
|
||||
obj = match.group(2)
|
||||
match = re.match(r'\[(\S+)\s.*Result: (.*)$', line)
|
||||
if match:
|
||||
assert obj is not None
|
||||
elapsed[obj] = toDateTime(match.group(1)) - start
|
||||
results[obj] = match.group(2)
|
||||
|
||||
args.o.write("#" * 80 + "\n")
|
||||
args.o.write("ccache report (from verilator_ccache_report) :\n")
|
||||
|
||||
if not results:
|
||||
args.o.write("\nAll object files up to date\n")
|
||||
else:
|
||||
args.o.write("\nCompiled object files:\n")
|
||||
wnames = max(len(_) for _ in results) + 1
|
||||
wresults = max(len(_) for _ in results.values()) + 1
|
||||
for k in sorted(results.keys()):
|
||||
args.o.write("{:{wnames}} : {:{wresults}} : {}s\n".format(
|
||||
k,
|
||||
results[k],
|
||||
elapsed[k].total_seconds(),
|
||||
wnames=wnames,
|
||||
wresults=wresults))
|
||||
|
||||
args.o.write("\nSummary:\n")
|
||||
counts = collections.Counter(_ for _ in results.values())
|
||||
total = sum(counts.values())
|
||||
for k in sorted(counts.keys()):
|
||||
c = counts[k]
|
||||
args.o.write("{:{width}}| {} ({:.2%})\n".format(k,
|
||||
c,
|
||||
c / total,
|
||||
width=wresults))
|
||||
|
||||
args.o.write("\nLongest:\n")
|
||||
longest = sorted(list(elapsed.items()),
|
||||
key=lambda kv: -kv[1].total_seconds())
|
||||
for i, (k, v) in enumerate(longest):
|
||||
args.o.write("{:{width}}| {}s\n".format(k,
|
||||
v.total_seconds(),
|
||||
width=wnames))
|
||||
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
|
||||
|
||||
+20
-4
@@ -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,19 +85,24 @@ 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/2
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/3
|
||||
;;
|
||||
dist-vlt-1)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/2
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/3
|
||||
;;
|
||||
dist-vlt-2)
|
||||
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=2/3
|
||||
;;
|
||||
vltmt-0)
|
||||
"$MAKE" -C test_regress SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=0/2
|
||||
@@ -166,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
|
||||
|
||||
+108
-18
@@ -7,7 +7,7 @@
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.202 2021-04-24],
|
||||
AC_INIT([Verilator],[4.210 2021-07-07],
|
||||
[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,15 +313,48 @@ 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
|
||||
# gnu++17 code is clean, but SystemC in 2018 doesn't link with it (bug1339)
|
||||
# gnu++14 is the newest that Verilator supports
|
||||
# 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)
|
||||
#_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)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++11)
|
||||
@@ -289,11 +365,13 @@ AC_SUBST(CFG_CXXFLAGS_STD_NEWEST)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++03)
|
||||
_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=std++17)
|
||||
#_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
|
||||
@@ -318,6 +396,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
|
||||
|
||||
@@ -6,6 +6,7 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
||||
Ahmed El-Mahmoudy
|
||||
Alex Chadwick
|
||||
Àlex Torregrosa
|
||||
Ameya Vikram Singh
|
||||
Andreas Kuster
|
||||
Chris Randall
|
||||
Conor McCullough
|
||||
@@ -19,6 +20,7 @@ Driss Hafdi
|
||||
Edgar E. Iglesias
|
||||
Eric Rippey
|
||||
Fan Shupei
|
||||
Felix Yan
|
||||
Garrett Smith
|
||||
Geza Lore
|
||||
Gianfranco Costamagna
|
||||
@@ -38,6 +40,7 @@ Jean Berniolles
|
||||
Jeremy Bennett
|
||||
John Coiner
|
||||
John Demme
|
||||
Jonathan Drolet
|
||||
Josh Redford
|
||||
Julien Margetts
|
||||
Kaleb Barrett
|
||||
@@ -53,9 +56,11 @@ Marco Widmer
|
||||
Markus Krause
|
||||
Marlon James
|
||||
Marshal Qiao
|
||||
Martin Schmidt
|
||||
Matthew Ballance
|
||||
Michael Killough
|
||||
Mike Popoloski
|
||||
Miodrag Milanović
|
||||
Morten Borup Petersen
|
||||
Nandu Raj
|
||||
Nathan Kohagen
|
||||
|
||||
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: "";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0112,C0114,C0116,C0301,R0903
|
||||
# pylint: disable=C0112,C0114,C0115,C0116,C0301,R0201,R0903
|
||||
# -*- Python -*- See copyright, etc below
|
||||
######################################################################
|
||||
|
||||
@@ -8,6 +8,7 @@ import re
|
||||
|
||||
#######################################################################
|
||||
|
||||
|
||||
class VlSphinxExtract:
|
||||
debug = 0
|
||||
SkipBasenames = {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0112,C0114,C0116,C0301,R0903
|
||||
# pylint: disable=C0112,C0114,C0115,C0116,C0301,R0903
|
||||
# -*- Python -*- See copyright, etc below
|
||||
######################################################################
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.. comment: generated by t_lint_multidriven_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2,5
|
||||
|
||||
always @(posedge clk) begin
|
||||
out2[7:0] <= d0; // <--- Warning
|
||||
end
|
||||
always @(negedge clk) begin
|
||||
out2[15:8] <= d0; // <--- Warning
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
.. comment: generated by t_lint_multidriven_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-MULTIDRIVEN: example.v:1:22 Signal has multiple driving blocks with different clocking: 'out2'
|
||||
example.v:1:7 ... Location of first driving block
|
||||
example.v:1:7 ... Location of other driving block
|
||||
+4
-5
@@ -1,4 +1,4 @@
|
||||
# pylint: disable=E402
|
||||
# pylint: disable=C0103,C0114,C0116,E0402,W0622
|
||||
#
|
||||
# Configuration file for Verilator's Sphinx documentation builder.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
@@ -12,11 +12,10 @@
|
||||
from datetime import datetime
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('./_ext'))
|
||||
|
||||
import sphinx_rtd_theme
|
||||
import sphinx_rtd_theme # pylint: disable=wrong-import-position,
|
||||
|
||||
|
||||
def get_vlt_version():
|
||||
@@ -94,7 +93,7 @@ today_fmt = datetime.now().strftime("%F")
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
# TODO could use this for internals<->guide references
|
||||
# Could use this for internals<->guide references
|
||||
# intersphinx_mapping = { 'sphinx': ('https://sphinx-doc.org/', None), }
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
@@ -208,7 +207,7 @@ spelling_ignore_contributor_names = True
|
||||
# ----------------------------------------------------------------------
|
||||
# -- Options for doxygen
|
||||
|
||||
#if shutil.which("doxygen"):
|
||||
# if shutil.which("doxygen"):
|
||||
# breathe_projects = {
|
||||
# "verilated": "../_build/doxygen/verilated/xml",
|
||||
# }
|
||||
|
||||
+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)
|
||||
|
||||
@@ -11,14 +11,6 @@ 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.
|
||||
|
||||
Configuration File -msg
|
||||
The :vlopt:`lint_off` "-msg" option has been replaced with the "-rule"
|
||||
option. "-msg" is planned for removal no sooner than January 2021.
|
||||
|
||||
@@ -403,6 +403,12 @@ Summary:
|
||||
files on the command line that implement the main loop for your
|
||||
simulation.
|
||||
|
||||
.. option:: --expand-limit <value>
|
||||
|
||||
Rarely needed. Fine-tune optimizations to set the maximum size of an
|
||||
expression in 32-bit words to expand into separate word-based
|
||||
statements.
|
||||
|
||||
.. option:: -F <file>
|
||||
|
||||
Read the specified file, and act as if all text inside it was specified
|
||||
@@ -418,8 +424,11 @@ Summary:
|
||||
fairly standard across Verilog tools.
|
||||
|
||||
The file may contain :code:`//` comments which are ignored to the end of
|
||||
the line. Any :code:`$VAR`, :code:`$(VAR)`, or :code:`${VAR}` will be
|
||||
replaced with the specified environment variable.
|
||||
the line. It may also contain :code:`/* .. */` comments which are
|
||||
ignored, be cautious that wildcards are not handled in -f files, and
|
||||
that :code:`directory/*` is the beginning of a comment, not a wildcard.
|
||||
Any :code:`$VAR`, :code:`$(VAR)`, or :code:`${VAR}` will be replaced
|
||||
with the specified environment variable.
|
||||
|
||||
.. option:: -FI <file>
|
||||
|
||||
@@ -518,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
|
||||
@@ -624,6 +625,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
|
||||
@@ -645,10 +653,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
|
||||
@@ -799,6 +803,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
|
||||
@@ -809,6 +820,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
|
||||
@@ -915,6 +928,15 @@ Summary:
|
||||
the path of the referencing file, instead of relative to the current
|
||||
directory.
|
||||
|
||||
.. option:: --reloop-limit
|
||||
|
||||
Rarely needed. Verilator attempts to turn some common sequences of
|
||||
statements into loops in the output. This argument specifies the minimum
|
||||
number of iterations the resulting loop needs to have in order to perform
|
||||
this transformation. Default limit is 40. A smaller number may slightly
|
||||
improve C++ compilation time on designs where these sequences are common,
|
||||
however effect on model performance requires benchmarking.
|
||||
|
||||
.. option:: --report-unoptflat
|
||||
|
||||
Extra diagnostics for UNOPTFLAT warnings. This includes for each loop,
|
||||
|
||||
@@ -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,
|
||||
|
||||
+9
-7
@@ -118,9 +118,9 @@ A. Pass the :vlopt:`--trace` option to Verilator, and in your top level C
|
||||
|
||||
B. Or, for finer-grained control, or C++ files with multiple Verilated
|
||||
modules you may also create the trace purely from C++. Create a
|
||||
VerilatedVcdC object, and in your main loop call
|
||||
``trace_object->dump(time)`` every time step, and finally call
|
||||
``trace_object->close()``.
|
||||
VerilatedVcdC object, and in your main loop right after ``eval()`` call
|
||||
``trace_object->dump(contextp->time())`` every time step, and finally
|
||||
call ``trace_object->close()``.
|
||||
|
||||
.. code-block:: C++
|
||||
:emphasize-lines: 1,5-8,12
|
||||
@@ -128,15 +128,17 @@ B. Or, for finer-grained control, or C++ files with multiple Verilated
|
||||
#include "verilated_vcd_c.h"
|
||||
...
|
||||
int main(int argc, char** argv, char** env) {
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
...
|
||||
Verilated::traceEverOn(true);
|
||||
VerilatedVcdC* tfp = new VerilatedVcdC;
|
||||
topp->trace(tfp, 99); // Trace 99 levels of hierarchy
|
||||
tfp->open("obj_dir/t_trace_ena_cc/simx.vcd");
|
||||
...
|
||||
while (Verilated::time() < sim_time && !Verilated::gotFinish()) {
|
||||
Verilated::timeInc(1);
|
||||
tfp->dump(main_time);
|
||||
while (contextp->time() < sim_time && !contextp->gotFinish()) {
|
||||
contextp->timeInc(1);
|
||||
topp->eval();
|
||||
tfp->dump(contextp->time());
|
||||
}
|
||||
tfp->close();
|
||||
}
|
||||
@@ -385,7 +387,7 @@ How do I get faster build times?
|
||||
identical source builds, even across different users. If ccache was
|
||||
installed when Verilator was built it is used, or see OBJCACHE
|
||||
environment variable to override this. Also see the
|
||||
:vlopt:`--output-split` option.
|
||||
:vlopt:`--output-split` option and :ref: `Profiling ccache efficiency`
|
||||
|
||||
* To reduce the compile time of classes that use a Verilated module (e.g. a
|
||||
top CPP file) you may wish to add a
|
||||
|
||||
+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)
|
||||
|
||||
|
||||
@@ -116,7 +116,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.
|
||||
@@ -316,6 +314,35 @@ statistics.
|
||||
|
||||
For more information see :command:`verilator_gantt`.
|
||||
|
||||
.. _Profiling ccache efficiency:
|
||||
|
||||
Profiling ccache efficiency
|
||||
===========================
|
||||
|
||||
The Verilator generated Makefile provides support for basic profiling of
|
||||
ccache behavior during the build. This can be used to track down files that
|
||||
might be unnecessarily rebuilt, though as of today even small code changes
|
||||
will usually require rebuilding a large number of files. Improving ccache
|
||||
efficiency during the edit/compile/test loop is an active area of
|
||||
development.
|
||||
|
||||
To get a basic report of how well ccache is doing, add the `ccache-report`
|
||||
target when invoking the generated Makefile:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
make -C obj_dir -f Vout.mk Vout ccache-report
|
||||
|
||||
This will print a report based on all executions of ccache during this
|
||||
invocation of Make. The report is also written to a file, in this example
|
||||
`obj_dir/Vout__cache_report.txt`.
|
||||
|
||||
To use the `ccache-report` target, at least one other explicit build target
|
||||
must be specified, and OBJCACHE must be set to 'ccache'.
|
||||
|
||||
This feature is currently experimental and might change in subsequent
|
||||
releases.
|
||||
|
||||
.. _Save/Restore:
|
||||
|
||||
Save/Restore
|
||||
|
||||
@@ -360,7 +360,8 @@ Verilate in CMake
|
||||
verilate(target SOURCES source ... [TOP_MODULE top] [PREFIX name]
|
||||
[TRACE] [TRACE_FST] [SYSTEMC] [COVERAGE]
|
||||
[INCLUDE_DIRS dir ...] [OPT_SLOW ...] [OPT_FAST ...]
|
||||
[OPT_GLOBAL ..] [DIRECTORY dir] [VERILATOR_ARGS ...])
|
||||
[OPT_GLOBAL ..] [DIRECTORY dir] [THREADS num]
|
||||
[TRACE_THREADS num] [VERILATOR_ARGS ...])
|
||||
|
||||
Lowercase and ... should be replaced with arguments, the uppercase parts
|
||||
delimit the arguments and can be passed in any order, or left out entirely
|
||||
@@ -429,6 +430,15 @@ SystemC include directories and link to the SystemC libraries.
|
||||
the SystemC library. This can be specified using the SYSTEMC_CXX_FLAGS
|
||||
environment variable.
|
||||
|
||||
.. describe:: THREADS
|
||||
|
||||
Optional. Generated a multi-threaded model, same as "--threads".
|
||||
|
||||
.. describe:: TRACE_THREADS
|
||||
|
||||
Optional. Generated multi-threaded trace dumping, same as
|
||||
"--trace-threads".
|
||||
|
||||
.. describe:: TOP_MODULE
|
||||
|
||||
Optional. Sets the name of the top module. Defaults to the name of the
|
||||
|
||||
+33
-6
@@ -128,6 +128,13 @@ List Of Warnings
|
||||
simulate correctly.
|
||||
|
||||
|
||||
.. option:: BADSTDPRAGMA
|
||||
|
||||
Error that a pragma is badly formed, when that pragma is defined by IEEE1800-2017.
|
||||
For example, an empty `pragma line, or an incorrect specified '`pragma protect'.
|
||||
Note that 3rd party pragmas not defined by IEEE1800-2017 are ignored.
|
||||
|
||||
|
||||
.. option:: BLKANDNBLK
|
||||
|
||||
.. TODO better example
|
||||
@@ -796,15 +803,25 @@ List Of Warnings
|
||||
|
||||
.. option:: MULTIDRIVEN
|
||||
|
||||
.. TODO better example
|
||||
Warns that the specified signal comes from multiple always blocks each
|
||||
with different clocking. This warning does not look at individual bits
|
||||
(see example below).
|
||||
|
||||
Warns that the specified signal comes from multiple always blocks. This
|
||||
is often unsupported by synthesis tools, and is considered bad style.
|
||||
It will also cause longer simulation runtimes due to reduced
|
||||
optimizations.
|
||||
This is considered bad style, as the consumer of a given signal may be
|
||||
unaware of the inconsistent clocking, causing clock domain crossing
|
||||
or timing bugs.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. include:: ../../docs/gen/ex_MULTIDRIVEN_faulty.rst
|
||||
|
||||
Results in:
|
||||
|
||||
.. include:: ../../docs/gen/ex_MULTIDRIVEN_msg.rst
|
||||
|
||||
Ignoring this warning will only slow simulations, it will simulate
|
||||
correctly.
|
||||
correctly. It may however cause longer simulation runtimes due to
|
||||
reduced optimizations.
|
||||
|
||||
|
||||
.. option:: MULTITOP
|
||||
@@ -988,6 +1005,16 @@ List Of Warnings
|
||||
a var/reg must be used as the target of procedural assignments.
|
||||
|
||||
|
||||
.. option:: PROTECTED
|
||||
|
||||
Warning that a '`pragma protected' section was encountered. The code
|
||||
inside the protected region will be partly checked for correctness, but is
|
||||
otherwise ignored.
|
||||
|
||||
Suppressing the warning may make Verilator differ from a simulator that
|
||||
accepts the protected code.
|
||||
|
||||
|
||||
.. option:: RANDC
|
||||
|
||||
Warns that the :code:`randc` keyword is currently unsupported, and that
|
||||
|
||||
+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
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
# cmake --build .
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(cmake_hello_sc)
|
||||
project(cmake_hello_sc CXX)
|
||||
|
||||
find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT})
|
||||
if (NOT verilator_FOUND)
|
||||
@@ -37,6 +37,11 @@ find_package(SystemCLanguage QUIET)
|
||||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_hello_sc/sc_main.cpp)
|
||||
|
||||
set_property(
|
||||
TARGET example
|
||||
PROPERTY CXX_STANDARD ${SystemC_CXX_STANDARD}
|
||||
)
|
||||
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example SYSTEMC
|
||||
INCLUDE_DIRS "../make_hello_sc"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
# cmake --build .
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(cmake_tracing_sc_example)
|
||||
project(cmake_tracing_sc_example CXX)
|
||||
|
||||
find_package(verilator HINTS $ENV{VERILATOR_ROOT} ${VERILATOR_ROOT})
|
||||
if (NOT verilator_FOUND)
|
||||
@@ -37,6 +37,11 @@ find_package(SystemCLanguage QUIET)
|
||||
# Create a new executable target that will contain all your sources
|
||||
add_executable(example ../make_tracing_sc/sc_main.cpp)
|
||||
|
||||
set_property(
|
||||
TARGET example
|
||||
PROPERTY CXX_STANDARD ${SystemC_CXX_STANDARD}
|
||||
)
|
||||
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example SYSTEMC COVERAGE TRACE
|
||||
INCLUDE_DIRS "../make_tracing_sc"
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2018 Tony Bybell.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef WIN_UNISTD_H
|
||||
#define WIN_UNISTD_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN64
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <sys/io.h>
|
||||
#endif
|
||||
|
||||
#include <process.h>
|
||||
|
||||
#define ftruncate _chsize_s
|
||||
#define unlink _unlink
|
||||
#define fileno _fileno
|
||||
#define lseek _lseeki64
|
||||
|
||||
#ifdef _WIN64
|
||||
#define ssize_t __int64
|
||||
#define SSIZE_MAX 9223372036854775807i64
|
||||
#else
|
||||
#define ssize_t long
|
||||
#define SSIZE_MAX 2147483647L
|
||||
#endif
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
#endif //WIN_UNISTD_H
|
||||
+56
-50
@@ -80,6 +80,12 @@
|
||||
#define PATH_MAX (4096)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef int64_t fst_off_t;
|
||||
#else
|
||||
typedef off_t fst_off_t;
|
||||
#endif
|
||||
|
||||
/* note that Judy versus Jenkins requires more experimentation: they are */
|
||||
/* functionally equivalent though it appears Jenkins is slightly faster. */
|
||||
/* in addition, Jenkins is not bound by the LGPL. */
|
||||
@@ -155,8 +161,8 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3
|
||||
#ifdef __MINGW32__
|
||||
#include <io.h>
|
||||
#ifndef HAVE_FSEEKO
|
||||
#define ftello ftell
|
||||
#define fseeko fseek
|
||||
#define ftello _ftelli64
|
||||
#define fseeko _fseeki64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -284,7 +290,7 @@ static size_t fstFwrite(const void *buf, size_t siz, size_t cnt, FILE *fp)
|
||||
return(fwrite(buf, siz, cnt, fp));
|
||||
}
|
||||
|
||||
static int fstFtruncate(int fd, off_t length)
|
||||
static int fstFtruncate(int fd, fst_off_t length)
|
||||
{
|
||||
return(ftruncate(fd, length));
|
||||
}
|
||||
@@ -329,12 +335,12 @@ return(NULL);
|
||||
#define fstMmap(__addr,__len,__prot,__flags,__fd,__off) fstMmap2((__len), (__fd), (__off))
|
||||
#define fstMunmap(__addr,__len) free(__addr)
|
||||
|
||||
static void *fstMmap2(size_t __len, int __fd, off_t __off)
|
||||
static void *fstMmap2(size_t __len, int __fd, fst_off_t __off)
|
||||
{
|
||||
(void)__off;
|
||||
|
||||
unsigned char *pnt = (unsigned char *)malloc(__len);
|
||||
off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
|
||||
fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
|
||||
size_t i;
|
||||
|
||||
lseek(__fd, 0, SEEK_SET);
|
||||
@@ -734,7 +740,7 @@ FILE *tchn_handle;
|
||||
|
||||
unsigned char *vchg_mem;
|
||||
|
||||
off_t hier_file_len;
|
||||
fst_off_t hier_file_len;
|
||||
|
||||
uint32_t *valpos_mem;
|
||||
unsigned char *curval_mem;
|
||||
@@ -754,7 +760,7 @@ unsigned fourpack : 1;
|
||||
unsigned fastpack : 1;
|
||||
|
||||
int64_t timezero;
|
||||
off_t section_header_truncpos;
|
||||
fst_off_t section_header_truncpos;
|
||||
uint32_t tchn_cnt, tchn_idx;
|
||||
uint64_t curtime;
|
||||
uint64_t firsttime;
|
||||
@@ -762,7 +768,7 @@ uint32_t vchg_siz;
|
||||
uint32_t vchg_alloc_siz;
|
||||
|
||||
uint32_t secnum;
|
||||
off_t section_start;
|
||||
fst_off_t section_start;
|
||||
|
||||
uint32_t numscopes;
|
||||
double nan; /* nan value for uninitialized doubles */
|
||||
@@ -820,7 +826,7 @@ fstEnumHandle max_enumhandle;
|
||||
};
|
||||
|
||||
|
||||
static int fstWriterFseeko(struct fstWriterContext *xc, FILE *stream, off_t offset, int whence)
|
||||
static int fstWriterFseeko(struct fstWriterContext *xc, FILE *stream, fst_off_t offset, int whence)
|
||||
{
|
||||
int rc = fseeko(stream, offset, whence);
|
||||
|
||||
@@ -987,7 +993,7 @@ if(pnt == MAP_FAILED)
|
||||
|
||||
static void fstWriterCreateMmaps(struct fstWriterContext *xc)
|
||||
{
|
||||
off_t curpos = ftello(xc->handle);
|
||||
fst_off_t curpos = ftello(xc->handle);
|
||||
|
||||
fflush(xc->hier_handle);
|
||||
|
||||
@@ -1041,7 +1047,7 @@ if(xc->curval_mem)
|
||||
{
|
||||
unsigned char *pnt = xc->curval_mem;
|
||||
int __fd = fileno(xc->curval_handle);
|
||||
off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
|
||||
fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
|
||||
size_t i;
|
||||
size_t __len = xc->maxvalpos;
|
||||
|
||||
@@ -1282,14 +1288,14 @@ int cnt = 0;
|
||||
unsigned int i;
|
||||
unsigned char *vchg_mem;
|
||||
FILE *f;
|
||||
off_t fpos, indxpos, endpos;
|
||||
fst_off_t fpos, indxpos, endpos;
|
||||
uint32_t prevpos;
|
||||
int zerocnt;
|
||||
unsigned char *scratchpad;
|
||||
unsigned char *scratchpnt;
|
||||
unsigned char *tmem;
|
||||
off_t tlen;
|
||||
off_t unc_memreq = 0; /* for reader */
|
||||
fst_off_t tlen;
|
||||
fst_off_t unc_memreq = 0; /* for reader */
|
||||
unsigned char *packmem;
|
||||
unsigned int packmemlen;
|
||||
uint32_t *vm4ip;
|
||||
@@ -1733,7 +1739,7 @@ if(tmem)
|
||||
unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen));
|
||||
int rc = compress2(dmem, &destlen, tmem, tlen, 9);
|
||||
|
||||
if((rc == Z_OK) && (((off_t)destlen) < tlen))
|
||||
if((rc == Z_OK) && (((fst_off_t)destlen) < tlen))
|
||||
{
|
||||
fstFwrite(dmem, destlen, 1, xc->handle);
|
||||
}
|
||||
@@ -1781,7 +1787,7 @@ fstWriterFseeko(xc, xc->handle, endpos, SEEK_SET);
|
||||
xc2->section_header_truncpos = endpos; /* cache in case of need to truncate */
|
||||
if(xc->dump_size_limit)
|
||||
{
|
||||
if(endpos >= ((off_t)xc->dump_size_limit))
|
||||
if(endpos >= ((fst_off_t)xc->dump_size_limit))
|
||||
{
|
||||
xc2->skip_writing_section_hdr = 1;
|
||||
xc2->size_limit_locked = 1;
|
||||
@@ -1931,7 +1937,7 @@ if(xc)
|
||||
if(xc && !xc->already_in_close && !xc->already_in_flush)
|
||||
{
|
||||
unsigned char *tmem = NULL;
|
||||
off_t fixup_offs, tlen, hlen;
|
||||
fst_off_t fixup_offs, tlen, hlen;
|
||||
|
||||
xc->already_in_close = 1; /* never need to zero this out as it is freed at bottom */
|
||||
|
||||
@@ -1991,7 +1997,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
||||
unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen));
|
||||
int rc = compress2(dmem, &destlen, tmem, tlen, 9);
|
||||
|
||||
if((rc != Z_OK) || (((off_t)destlen) > tlen))
|
||||
if((rc != Z_OK) || (((fst_off_t)destlen) > tlen))
|
||||
{
|
||||
destlen = tlen;
|
||||
}
|
||||
@@ -2002,7 +2008,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
||||
fstWriterUint64(xc->handle, tlen); /* uncompressed */
|
||||
/* compressed len is section length - 24 */
|
||||
fstWriterUint64(xc->handle, xc->maxhandle); /* maxhandle */
|
||||
fstFwrite((((off_t)destlen) != tlen) ? dmem : tmem, destlen, 1, xc->handle);
|
||||
fstFwrite((((fst_off_t)destlen) != tlen) ? dmem : tmem, destlen, 1, xc->handle);
|
||||
fflush(xc->handle);
|
||||
|
||||
fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET);
|
||||
@@ -2018,7 +2024,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
||||
if(xc->num_blackouts)
|
||||
{
|
||||
uint64_t cur_bl = 0;
|
||||
off_t bpos, eos;
|
||||
fst_off_t bpos, eos;
|
||||
uint32_t i;
|
||||
|
||||
fixup_offs = ftello(xc->handle);
|
||||
@@ -2051,7 +2057,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
||||
|
||||
if(xc->compress_hier)
|
||||
{
|
||||
off_t hl, eos;
|
||||
fst_off_t hl, eos;
|
||||
gzFile zhandle;
|
||||
int zfd;
|
||||
int fourpack_duo = 0;
|
||||
@@ -2174,7 +2180,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
||||
if(xc->repack_on_close)
|
||||
{
|
||||
FILE *fp;
|
||||
off_t offpnt, uclen;
|
||||
fst_off_t offpnt, uclen;
|
||||
int flen = strlen(xc->filename);
|
||||
char *hf = (char *)calloc(1, flen + 5);
|
||||
|
||||
@@ -2281,7 +2287,7 @@ struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
|
||||
if(xc)
|
||||
{
|
||||
char s[FST_HDR_DATE_SIZE];
|
||||
off_t fpos = ftello(xc->handle);
|
||||
fst_off_t fpos = ftello(xc->handle);
|
||||
int len = strlen(dat);
|
||||
|
||||
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_DATE, SEEK_SET);
|
||||
@@ -2300,7 +2306,7 @@ struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
|
||||
if(xc && vers)
|
||||
{
|
||||
char s[FST_HDR_SIM_VERSION_SIZE];
|
||||
off_t fpos = ftello(xc->handle);
|
||||
fst_off_t fpos = ftello(xc->handle);
|
||||
int len = strlen(vers);
|
||||
|
||||
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_SIM_VERSION, SEEK_SET);
|
||||
@@ -2320,7 +2326,7 @@ if(xc)
|
||||
{
|
||||
if(/*(filetype >= FST_FT_MIN) &&*/ (filetype <= FST_FT_MAX))
|
||||
{
|
||||
off_t fpos = ftello(xc->handle);
|
||||
fst_off_t fpos = ftello(xc->handle);
|
||||
|
||||
xc->filetype = filetype;
|
||||
|
||||
@@ -2461,7 +2467,7 @@ void fstWriterSetTimescale(void *ctx, int ts)
|
||||
struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
|
||||
if(xc)
|
||||
{
|
||||
off_t fpos = ftello(xc->handle);
|
||||
fst_off_t fpos = ftello(xc->handle);
|
||||
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMESCALE, SEEK_SET);
|
||||
fputc(ts & 255, xc->handle);
|
||||
fflush(xc->handle);
|
||||
@@ -2519,7 +2525,7 @@ void fstWriterSetTimezero(void *ctx, int64_t tim)
|
||||
struct fstWriterContext *xc = (struct fstWriterContext *)ctx;
|
||||
if(xc)
|
||||
{
|
||||
off_t fpos = ftello(xc->handle);
|
||||
fst_off_t fpos = ftello(xc->handle);
|
||||
fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMEZERO, SEEK_SET);
|
||||
fstWriterUint64(xc->handle, (xc->timezero = tim));
|
||||
fflush(xc->handle);
|
||||
@@ -3361,7 +3367,7 @@ char date[FST_HDR_DATE_SIZE + 1];
|
||||
int64_t timezero;
|
||||
|
||||
char *filename, *filename_unpacked;
|
||||
off_t hier_pos;
|
||||
fst_off_t hier_pos;
|
||||
|
||||
uint32_t num_blackouts;
|
||||
uint64_t *blackout_times;
|
||||
@@ -3376,10 +3382,10 @@ uint64_t *rvat_time_table;
|
||||
uint64_t rvat_beg_tim, rvat_end_tim;
|
||||
unsigned char *rvat_frame_data;
|
||||
uint64_t rvat_frame_maxhandle;
|
||||
off_t *rvat_chain_table;
|
||||
fst_off_t *rvat_chain_table;
|
||||
uint32_t *rvat_chain_table_lengths;
|
||||
uint64_t rvat_vc_maxhandle;
|
||||
off_t rvat_vc_start;
|
||||
fst_off_t rvat_vc_start;
|
||||
uint32_t *rvat_sig_offs;
|
||||
int rvat_packtype;
|
||||
|
||||
@@ -3418,7 +3424,7 @@ char *fh_nam;
|
||||
};
|
||||
|
||||
|
||||
int fstReaderFseeko(struct fstReaderContext *xc, FILE *stream, off_t offset, int whence)
|
||||
int fstReaderFseeko(struct fstReaderContext *xc, FILE *stream, fst_off_t offset, int whence)
|
||||
{
|
||||
int rc = fseeko(stream, offset, whence);
|
||||
|
||||
@@ -3911,11 +3917,11 @@ int pass_status = 1;
|
||||
|
||||
if(!xc->fh)
|
||||
{
|
||||
off_t offs_cache = ftello(xc->f);
|
||||
fst_off_t offs_cache = ftello(xc->f);
|
||||
char *fnam = (char *)malloc(strlen(xc->filename) + 6 + 16 + 32 + 1);
|
||||
unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN);
|
||||
off_t hl, uclen;
|
||||
off_t clen = 0;
|
||||
fst_off_t hl, uclen;
|
||||
fst_off_t clen = 0;
|
||||
gzFile zhandle = NULL;
|
||||
int zfd;
|
||||
int htyp = FST_BL_SKIP;
|
||||
@@ -4535,8 +4541,8 @@ return(1);
|
||||
*/
|
||||
int fstReaderInit(struct fstReaderContext *xc)
|
||||
{
|
||||
off_t blkpos = 0;
|
||||
off_t endfile;
|
||||
fst_off_t blkpos = 0;
|
||||
fst_off_t endfile;
|
||||
uint64_t seclen;
|
||||
int sectype;
|
||||
uint64_t vc_section_count_actual = 0;
|
||||
@@ -4548,7 +4554,7 @@ sectype = fgetc(xc->f);
|
||||
if(sectype == FST_BL_ZWRAPPER)
|
||||
{
|
||||
FILE *fcomp;
|
||||
off_t offpnt, uclen;
|
||||
fst_off_t offpnt, uclen;
|
||||
char gz_membuf[FST_GZIO_LEN];
|
||||
gzFile zhandle;
|
||||
int zfd;
|
||||
@@ -4981,15 +4987,15 @@ uint64_t *time_table = NULL;
|
||||
uint64_t tsec_nitems;
|
||||
unsigned int secnum = 0;
|
||||
int blocks_skipped = 0;
|
||||
off_t blkpos = 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;
|
||||
off_t vc_start;
|
||||
off_t indx_pntr, indx_pos;
|
||||
off_t *chain_table = NULL;
|
||||
fst_off_t vc_start;
|
||||
fst_off_t indx_pntr, indx_pos;
|
||||
fst_off_t *chain_table = NULL;
|
||||
uint32_t *chain_table_lengths = NULL;
|
||||
unsigned char *chain_cmem;
|
||||
unsigned char *pnt;
|
||||
@@ -5105,7 +5111,7 @@ for(;;)
|
||||
destlen = tsec_uclen;
|
||||
sourcelen = tsec_clen;
|
||||
|
||||
fstReaderFseeko(xc, xc->f, -24 - ((off_t)tsec_clen), SEEK_CUR);
|
||||
fstReaderFseeko(xc, xc->f, -24 - ((fst_off_t)tsec_clen), SEEK_CUR);
|
||||
|
||||
if(tsec_uclen != tsec_clen)
|
||||
{
|
||||
@@ -5346,11 +5352,11 @@ for(;;)
|
||||
}
|
||||
|
||||
free(mu);
|
||||
fstReaderFseeko(xc, xc->f, -((off_t)frame_clen), SEEK_CUR);
|
||||
fstReaderFseeko(xc, xc->f, -((fst_off_t)frame_clen), SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
fstReaderFseeko(xc, xc->f, (off_t)frame_clen, SEEK_CUR); /* skip past compressed data */
|
||||
fstReaderFseeko(xc, xc->f, (fst_off_t)frame_clen, SEEK_CUR); /* skip past compressed data */
|
||||
|
||||
vc_maxhandle = fstReaderVarint64(xc->f);
|
||||
vc_start = ftello(xc->f); /* points to '!' character */
|
||||
@@ -5380,7 +5386,7 @@ for(;;)
|
||||
free(chain_table_lengths);
|
||||
|
||||
vc_maxhandle_largest = vc_maxhandle;
|
||||
chain_table = (off_t *)calloc((vc_maxhandle+1), sizeof(off_t));
|
||||
chain_table = (fst_off_t *)calloc((vc_maxhandle+1), sizeof(fst_off_t));
|
||||
chain_table_lengths = (uint32_t *)calloc((vc_maxhandle+1), sizeof(uint32_t));
|
||||
}
|
||||
|
||||
@@ -6001,7 +6007,7 @@ return(buf);
|
||||
char *fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf)
|
||||
{
|
||||
struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
|
||||
off_t blkpos = 0, prev_blkpos;
|
||||
fst_off_t blkpos = 0, prev_blkpos;
|
||||
uint64_t beg_tim, end_tim, beg_tim2, end_tim2;
|
||||
int sectype;
|
||||
unsigned int secnum = 0;
|
||||
@@ -6012,7 +6018,7 @@ uint64_t frame_uclen, frame_clen;
|
||||
#ifdef FST_DEBUG
|
||||
uint64_t mem_required_for_traversal;
|
||||
#endif
|
||||
off_t indx_pntr, indx_pos;
|
||||
fst_off_t indx_pntr, indx_pos;
|
||||
long chain_clen;
|
||||
unsigned char *chain_cmem;
|
||||
unsigned char *pnt;
|
||||
@@ -6074,7 +6080,7 @@ for(;;)
|
||||
{
|
||||
if((tim == end_tim) && (tim != xc->end_time))
|
||||
{
|
||||
off_t cached_pos = ftello(xc->f);
|
||||
fst_off_t cached_pos = ftello(xc->f);
|
||||
fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET);
|
||||
|
||||
sectype = fgetc(xc->f);
|
||||
@@ -6136,7 +6142,7 @@ ucdata = (unsigned char *)malloc(tsec_uclen);
|
||||
destlen = tsec_uclen;
|
||||
sourcelen = tsec_clen;
|
||||
|
||||
fstReaderFseeko(xc, xc->f, -24 - ((off_t)tsec_clen), SEEK_CUR);
|
||||
fstReaderFseeko(xc, xc->f, -24 - ((fst_off_t)tsec_clen), SEEK_CUR);
|
||||
if(tsec_uclen != tsec_clen)
|
||||
{
|
||||
cdata = (unsigned char *)malloc(tsec_clen);
|
||||
@@ -6221,7 +6227,7 @@ chain_cmem = (unsigned char *)malloc(chain_clen);
|
||||
fstReaderFseeko(xc, xc->f, indx_pos, SEEK_SET);
|
||||
fstFread(chain_cmem, chain_clen, 1, xc->f);
|
||||
|
||||
xc->rvat_chain_table = (off_t *)calloc((xc->rvat_vc_maxhandle+1), sizeof(off_t));
|
||||
xc->rvat_chain_table = (fst_off_t *)calloc((xc->rvat_vc_maxhandle+1), sizeof(fst_off_t));
|
||||
xc->rvat_chain_table_lengths = (uint32_t *)calloc((xc->rvat_vc_maxhandle+1), sizeof(uint32_t));
|
||||
|
||||
pnt = chain_cmem;
|
||||
|
||||
@@ -35,7 +35,11 @@ extern "C" {
|
||||
#include <ctype.h>
|
||||
#include <zlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#if defined(_MSC_VER)
|
||||
#include "fst_win_unistd.h"
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
|
||||
#define FST_RDLOAD "FSTLOAD | "
|
||||
|
||||
+214
-130
@@ -215,7 +215,7 @@ std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
|
||||
char* bufp = new char[len + 1];
|
||||
VL_VSNPRINTF(bufp, len + 1, formatp, ap);
|
||||
|
||||
std::string out = std::string(bufp, len);
|
||||
const std::string out = std::string(bufp, len);
|
||||
delete[] bufp;
|
||||
return out;
|
||||
}
|
||||
@@ -246,7 +246,7 @@ void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE {
|
||||
// includes that otherwise would be required in every Verilated module
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
std::string out = _vl_string_vprintf(formatp, ap);
|
||||
const std::string out = _vl_string_vprintf(formatp, ap);
|
||||
va_end(ap);
|
||||
// printf("-imm-V{t%d,%" VL_PRI64 "d}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(),
|
||||
// out.c_str());
|
||||
@@ -261,7 +261,7 @@ void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE {
|
||||
void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
std::string out = _vl_string_vprintf(formatp, ap);
|
||||
const std::string out = _vl_string_vprintf(formatp, ap);
|
||||
va_end(ap);
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
|
||||
VL_PRINTF("%s", out.c_str());
|
||||
@@ -301,7 +301,7 @@ vluint64_t vl_rand64() VL_MT_SAFE {
|
||||
if (VL_COUNTONES_I(t_state[1]) < 10) t_state[1] = ~t_state[1];
|
||||
}
|
||||
// Xoroshiro128+ algorithm
|
||||
vluint64_t result = t_state[0] + t_state[1];
|
||||
const vluint64_t result = t_state[0] + t_state[1];
|
||||
t_state[1] ^= t_state[0];
|
||||
t_state[0] = (((t_state[0] << 55) | (t_state[0] >> 9)) ^ t_state[1] ^ (t_state[1] << 14));
|
||||
t_state[1] = (t_state[1] << 36) | (t_state[1] >> 28);
|
||||
@@ -356,7 +356,7 @@ WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
//===========================================================================
|
||||
// Debug
|
||||
|
||||
void _vl_debug_print_w(int lbits, WDataInP iwp) VL_MT_SAFE {
|
||||
void _vl_debug_print_w(int lbits, const WDataInP iwp) VL_MT_SAFE {
|
||||
VL_PRINTF_MT(" Data: w%d: ", lbits);
|
||||
for (int i = VL_WORDS_I(lbits) - 1; i >= 0; --i) VL_PRINTF_MT("%08x ", iwp[i]);
|
||||
VL_PRINTF_MT("\n");
|
||||
@@ -365,24 +365,24 @@ void _vl_debug_print_w(int lbits, WDataInP iwp) VL_MT_SAFE {
|
||||
//===========================================================================
|
||||
// Slow math
|
||||
|
||||
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
|
||||
WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WDataInP rwp,
|
||||
bool is_modulus) VL_MT_SAFE {
|
||||
// See Knuth Algorithm D. Computes u/v = q.r
|
||||
// This isn't massively tuned, as wide division is rare
|
||||
// for debug see V3Number version
|
||||
// Requires clean input
|
||||
int words = VL_WORDS_I(lbits);
|
||||
const int words = VL_WORDS_I(lbits);
|
||||
for (int i = 0; i < words; ++i) owp[i] = 0;
|
||||
// Find MSB and check for zero.
|
||||
int umsbp1 = VL_MOSTSETBITP1_W(words, lwp); // dividend
|
||||
int vmsbp1 = VL_MOSTSETBITP1_W(words, rwp); // divisor
|
||||
const int umsbp1 = VL_MOSTSETBITP1_W(words, lwp); // dividend
|
||||
const int vmsbp1 = VL_MOSTSETBITP1_W(words, rwp); // divisor
|
||||
if (VL_UNLIKELY(vmsbp1 == 0) // rwp==0 so division by zero. Return 0.
|
||||
|| VL_UNLIKELY(umsbp1 == 0)) { // 0/x so short circuit and return 0
|
||||
return owp;
|
||||
}
|
||||
|
||||
int uw = VL_WORDS_I(umsbp1); // aka "m" in the algorithm
|
||||
int vw = VL_WORDS_I(vmsbp1); // aka "n" in the algorithm
|
||||
const int uw = VL_WORDS_I(umsbp1); // aka "m" in the algorithm
|
||||
const int vw = VL_WORDS_I(vmsbp1); // aka "n" in the algorithm
|
||||
|
||||
if (vw == 1) { // Single divisor word breaks rest of algorithm
|
||||
vluint64_t k = 0;
|
||||
@@ -408,8 +408,8 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
|
||||
|
||||
// Algorithm requires divisor MSB to be set
|
||||
// Copy and shift to normalize divisor so MSB of vn[vw-1] is set
|
||||
int s = 31 - VL_BITBIT_I(vmsbp1 - 1); // shift amount (0...31)
|
||||
vluint32_t shift_mask = s ? 0xffffffff : 0; // otherwise >> 32 won't mask the value
|
||||
const int s = 31 - VL_BITBIT_I(vmsbp1 - 1); // shift amount (0...31)
|
||||
const vluint32_t shift_mask = s ? 0xffffffff : 0; // otherwise >> 32 won't mask the value
|
||||
for (int i = vw - 1; i > 0; --i) {
|
||||
vn[i] = (rwp[i] << s) | (shift_mask & (rwp[i - 1] >> (32 - s)));
|
||||
}
|
||||
@@ -429,8 +429,8 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
|
||||
// Main loop
|
||||
for (int j = uw - vw; j >= 0; --j) {
|
||||
// Estimate
|
||||
vluint64_t unw64 = (static_cast<vluint64_t>(un[j + vw]) << 32ULL
|
||||
| static_cast<vluint64_t>(un[j + vw - 1]));
|
||||
const vluint64_t unw64 = (static_cast<vluint64_t>(un[j + vw]) << 32ULL
|
||||
| static_cast<vluint64_t>(un[j + vw - 1]));
|
||||
vluint64_t qhat = unw64 / static_cast<vluint64_t>(vn[vw - 1]);
|
||||
vluint64_t rhat = unw64 - qhat * static_cast<vluint64_t>(vn[vw - 1]);
|
||||
|
||||
@@ -478,15 +478,15 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
|
||||
}
|
||||
}
|
||||
|
||||
WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp,
|
||||
WDataInP rwp) VL_MT_SAFE {
|
||||
WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp,
|
||||
const WDataInP rwp) VL_MT_SAFE {
|
||||
// obits==lbits, rbits can be different
|
||||
owp[0] = 1;
|
||||
for (int i = 1; i < VL_WORDS_I(obits); i++) owp[i] = 0;
|
||||
// cppcheck-suppress variableScope
|
||||
WData powstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
|
||||
WData lastpowstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
|
||||
WData lastoutstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
|
||||
VlWide<VL_MULS_MAX_WORDS> powstore; // Fixed size, as MSVC++ doesn't allow [words] here
|
||||
VlWide<VL_MULS_MAX_WORDS> lastpowstore; // Fixed size, as MSVC++ doesn't allow [words] here
|
||||
VlWide<VL_MULS_MAX_WORDS> lastoutstore; // Fixed size, as MSVC++ doesn't allow [words] here
|
||||
// cppcheck-suppress variableScope
|
||||
VL_ASSIGN_W(obits, powstore, lwp);
|
||||
for (int bit = 0; bit < rbits; bit++) {
|
||||
@@ -501,13 +501,13 @@ WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp,
|
||||
}
|
||||
return owp;
|
||||
}
|
||||
WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp,
|
||||
WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits, WDataOutP owp, const WDataInP lwp,
|
||||
QData rhs) VL_MT_SAFE {
|
||||
WData rhsw[VL_WQ_WORDS_E];
|
||||
VlWide<VL_WQ_WORDS_E> rhsw;
|
||||
VL_SET_WQ(rhsw, rhs);
|
||||
return VL_POW_WWW(obits, lbits, rbits, owp, lwp, rhsw);
|
||||
}
|
||||
QData VL_POW_QQW(int, int, int rbits, QData lhs, WDataInP rwp) VL_MT_SAFE {
|
||||
QData VL_POW_QQW(int, int, int rbits, QData lhs, const WDataInP rwp) VL_MT_SAFE {
|
||||
// Skip check for rhs == 0, as short-circuit doesn't save time
|
||||
if (VL_UNLIKELY(lhs == 0)) return 0;
|
||||
QData power = lhs;
|
||||
@@ -519,11 +519,11 @@ QData VL_POW_QQW(int, int, int rbits, QData lhs, WDataInP rwp) VL_MT_SAFE {
|
||||
return out;
|
||||
}
|
||||
|
||||
WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp,
|
||||
bool lsign, bool rsign) VL_MT_SAFE {
|
||||
WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp,
|
||||
const WDataInP rwp, bool lsign, bool rsign) VL_MT_SAFE {
|
||||
// obits==lbits, rbits can be different
|
||||
if (rsign && VL_SIGN_W(rbits, rwp)) {
|
||||
int words = VL_WORDS_I(obits);
|
||||
const int words = VL_WORDS_I(obits);
|
||||
VL_ZERO_W(obits, owp);
|
||||
EData lor = 0; // 0=all zeros, ~0=all ones, else mix
|
||||
for (int i = 1; i < (words - 1); ++i) { lor |= lwp[i]; }
|
||||
@@ -545,13 +545,13 @@ WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, W
|
||||
}
|
||||
return VL_POW_WWW(obits, rbits, rbits, owp, lwp, rwp);
|
||||
}
|
||||
WDataOutP VL_POWSS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, WDataInP lwp, QData rhs,
|
||||
bool lsign, bool rsign) VL_MT_SAFE {
|
||||
WData rhsw[VL_WQ_WORDS_E];
|
||||
WDataOutP VL_POWSS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, const WDataInP lwp,
|
||||
QData rhs, bool lsign, bool rsign) VL_MT_SAFE {
|
||||
VlWide<VL_WQ_WORDS_E> rhsw;
|
||||
VL_SET_WQ(rhsw, rhs);
|
||||
return VL_POWSS_WWW(obits, lbits, rbits, owp, lwp, rhsw, lsign, rsign);
|
||||
}
|
||||
QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP rwp, bool lsign,
|
||||
QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, const WDataInP rwp, bool lsign,
|
||||
bool rsign) VL_MT_SAFE {
|
||||
// Skip check for rhs == 0, as short-circuit doesn't save time
|
||||
if (rsign && VL_SIGN_W(rbits, rwp)) {
|
||||
@@ -571,23 +571,23 @@ QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP rwp, bool lsig
|
||||
return VL_POW_QQW(obits, rbits, rbits, lhs, rwp);
|
||||
}
|
||||
|
||||
double VL_ITOR_D_W(int lbits, WDataInP lwp) VL_PURE {
|
||||
double VL_ITOR_D_W(int lbits, const WDataInP lwp) VL_PURE {
|
||||
int ms_word = VL_WORDS_I(lbits) - 1;
|
||||
for (; !lwp[ms_word] && ms_word > 0;) --ms_word;
|
||||
if (ms_word == 0) return static_cast<double>(lwp[0]);
|
||||
if (ms_word == 1) return static_cast<double>(VL_SET_QW(lwp));
|
||||
// We need 53 bits of mantissa, which might mean looking at 3 words
|
||||
// namely ms_word, ms_word-1 and ms_word-2
|
||||
EData ihi = lwp[ms_word];
|
||||
EData imid = lwp[ms_word - 1];
|
||||
EData ilo = lwp[ms_word - 2];
|
||||
double hi = static_cast<double>(ihi) * std::exp2(2 * VL_EDATASIZE);
|
||||
double mid = static_cast<double>(imid) * std::exp2(VL_EDATASIZE);
|
||||
double lo = static_cast<double>(ilo);
|
||||
double d = (hi + mid + lo) * std::exp2(VL_EDATASIZE * (ms_word - 2));
|
||||
const EData ihi = lwp[ms_word];
|
||||
const EData imid = lwp[ms_word - 1];
|
||||
const EData ilo = lwp[ms_word - 2];
|
||||
const double hi = static_cast<double>(ihi) * std::exp2(2 * VL_EDATASIZE);
|
||||
const double mid = static_cast<double>(imid) * std::exp2(VL_EDATASIZE);
|
||||
const double lo = static_cast<double>(ilo);
|
||||
const double d = (hi + mid + lo) * std::exp2(VL_EDATASIZE * (ms_word - 2));
|
||||
return d;
|
||||
}
|
||||
double VL_ISTOR_D_W(int lbits, WDataInP lwp) VL_PURE {
|
||||
double VL_ISTOR_D_W(int lbits, const WDataInP lwp) VL_PURE {
|
||||
if (!VL_SIGN_W(lbits, lwp)) return VL_ITOR_D_W(lbits, lwp);
|
||||
vluint32_t pos[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here
|
||||
VL_NEGATE_W(VL_WORDS_I(lbits), pos, lwp);
|
||||
@@ -599,13 +599,13 @@ double VL_ISTOR_D_W(int lbits, WDataInP lwp) VL_PURE {
|
||||
// Formatting
|
||||
|
||||
// Output a string representation of a wide number
|
||||
std::string VL_DECIMAL_NW(int width, WDataInP lwp) VL_MT_SAFE {
|
||||
int maxdecwidth = (width + 3) * 4 / 3;
|
||||
std::string VL_DECIMAL_NW(int width, const WDataInP lwp) VL_MT_SAFE {
|
||||
const int maxdecwidth = (width + 3) * 4 / 3;
|
||||
// Or (maxdecwidth+7)/8], but can't have more than 4 BCD bits per word
|
||||
WData bcd[VL_VALUE_STRING_MAX_WIDTH / 4 + 2];
|
||||
VlWide<VL_VALUE_STRING_MAX_WIDTH / 4 + 2> bcd;
|
||||
VL_ZERO_RESET_W(maxdecwidth, bcd);
|
||||
WData tmp[VL_VALUE_STRING_MAX_WIDTH / 4 + 2];
|
||||
WData tmp2[VL_VALUE_STRING_MAX_WIDTH / 4 + 2];
|
||||
VlWide<VL_VALUE_STRING_MAX_WIDTH / 4 + 2> tmp;
|
||||
VlWide<VL_VALUE_STRING_MAX_WIDTH / 4 + 2> tmp2;
|
||||
int from_bit = width - 1;
|
||||
// Skip all leading zeros
|
||||
for (; from_bit >= 0 && !(VL_BITRSHIFT_W(lwp, from_bit) & 1); --from_bit) {}
|
||||
@@ -637,27 +637,80 @@ std::string VL_DECIMAL_NW(int width, WDataInP lwp) VL_MT_SAFE {
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string _vl_vsformat_time(char* tmp, double ld, bool left, size_t width) {
|
||||
// Double may lose precision, but sc_time_stamp has similar limit
|
||||
std::string suffix = Verilated::threadContextp()->impp()->timeFormatSuffix();
|
||||
int userUnits = Verilated::threadContextp()->impp()->timeFormatUnits(); // 0..-15
|
||||
int fracDigits = Verilated::threadContextp()->impp()->timeFormatPrecision(); // 0..N
|
||||
int prec = Verilated::threadContextp()->timeprecision(); // 0..-15
|
||||
int shift = prec - userUnits + fracDigits; // 0..-15
|
||||
double shiftd = vl_time_multiplier(shift);
|
||||
double scaled = ld * shiftd;
|
||||
QData fracDiv = static_cast<QData>(vl_time_multiplier(fracDigits));
|
||||
QData whole = static_cast<QData>(scaled) / fracDiv;
|
||||
QData fraction = static_cast<QData>(scaled) % fracDiv;
|
||||
template <typename T>
|
||||
std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t width) {
|
||||
const VerilatedContextImp* const ctxImpp = Verilated::threadContextp()->impp();
|
||||
const std::string suffix = ctxImpp->timeFormatSuffix();
|
||||
const int userUnits = ctxImpp->timeFormatUnits(); // 0..-15
|
||||
const int fracDigits = ctxImpp->timeFormatPrecision(); // 0..N
|
||||
const int shift = -userUnits + fracDigits + timeunit; // 0..-15
|
||||
int digits = 0;
|
||||
if (!fracDigits) {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u%s", whole,
|
||||
suffix.c_str());
|
||||
if (std::numeric_limits<T>::is_integer) {
|
||||
constexpr int b = 128;
|
||||
constexpr int w = VL_WORDS_I(b);
|
||||
VlWide<w> tmp0, tmp1, tmp2, tmp3;
|
||||
|
||||
WDataInP shifted = VL_EXTEND_WQ(b, 0, tmp0, static_cast<QData>(ld));
|
||||
if (shift < 0) {
|
||||
const WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(-shift));
|
||||
shifted = VL_DIV_WWW(b, tmp2, shifted, pow10);
|
||||
} else {
|
||||
const WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(shift));
|
||||
shifted = VL_MUL_W(w, tmp2, shifted, pow10);
|
||||
}
|
||||
|
||||
const WDataInP fracDigitsPow10 = VL_EXTEND_WQ(b, 0, tmp3, vl_time_pow10(fracDigits));
|
||||
const WDataInP integer = VL_DIV_WWW(b, tmp0, shifted, fracDigitsPow10);
|
||||
const WDataInP frac = VL_MODDIV_WWW(b, tmp1, shifted, fracDigitsPow10);
|
||||
const WDataInP max64Bit
|
||||
= VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits<vluint64_t>::max()); // breaks shifted
|
||||
if (VL_GT_W(w, integer, max64Bit)) {
|
||||
WDataOutP v = VL_ASSIGN_W(b, tmp3, integer); // breaks fracDigitsPow10
|
||||
VlWide<w> zero, ten;
|
||||
VL_ZERO_W(b, zero);
|
||||
VL_EXTEND_WI(b, 0, ten, 10);
|
||||
char buf[128]; // 128B is obviously long enough to represent 128bit integer in decimal
|
||||
char* ptr = buf + sizeof(buf) - 1;
|
||||
*ptr = '\0';
|
||||
while (VL_GT_W(w, v, zero)) {
|
||||
--ptr;
|
||||
const WDataInP mod = VL_MODDIV_WWW(b, tmp2, v, ten); // breaks max64Bit
|
||||
*ptr = "0123456789"[VL_SET_QW(mod)];
|
||||
VlWide<w> divided;
|
||||
VL_DIV_WWW(b, divided, v, ten);
|
||||
VL_ASSIGN_W(b, v, divided);
|
||||
}
|
||||
if (!fracDigits) {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%s%s", ptr, suffix.c_str());
|
||||
} else {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%s.%0*" VL_PRI64 "u%s", ptr,
|
||||
fracDigits, VL_SET_QW(frac), suffix.c_str());
|
||||
}
|
||||
} else {
|
||||
const vluint64_t integer64 = VL_SET_QW(integer);
|
||||
if (!fracDigits) {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u%s", integer64,
|
||||
suffix.c_str());
|
||||
} else {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH,
|
||||
"%" VL_PRI64 "u.%0*" VL_PRI64 "u%s", integer64, fracDigits,
|
||||
VL_SET_QW(frac), suffix.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u.%0*" VL_PRI64 "u%s",
|
||||
whole, fracDigits, fraction, suffix.c_str());
|
||||
double shiftd = vl_time_multiplier(shift);
|
||||
double scaled = ld * shiftd;
|
||||
const double fracDiv = vl_time_multiplier(fracDigits);
|
||||
const double whole = scaled / fracDiv;
|
||||
if (!fracDigits) {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%.0f%s", whole, suffix.c_str());
|
||||
} else {
|
||||
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%.*f%s", fracDigits, whole,
|
||||
suffix.c_str());
|
||||
}
|
||||
}
|
||||
int needmore = width - digits;
|
||||
|
||||
const int needmore = width - digits;
|
||||
std::string padding;
|
||||
if (needmore > 0) padding.append(needmore, ' '); // Pad with spaces
|
||||
return left ? (tmp + padding) : (padding + tmp);
|
||||
@@ -748,11 +801,12 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
case 'g':
|
||||
case '^': { // Realtime
|
||||
const int lbits = va_arg(ap, int);
|
||||
double d = va_arg(ap, double);
|
||||
const double d = va_arg(ap, double);
|
||||
if (lbits) {} // UNUSED - always 64
|
||||
if (fmt == '^') { // Realtime
|
||||
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
|
||||
output += _vl_vsformat_time(t_tmp, d, left, width);
|
||||
const int timeunit = va_arg(ap, int);
|
||||
output += _vl_vsformat_time(t_tmp, d, timeunit, left, width);
|
||||
} else {
|
||||
std::string fmts(pctp, pos - pctp + 1);
|
||||
VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d);
|
||||
@@ -764,7 +818,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
// Deal with all read-and-print somethings
|
||||
const int lbits = va_arg(ap, int);
|
||||
QData ld = 0;
|
||||
WData qlwp[VL_WQ_WORDS_E];
|
||||
VlWide<VL_WQ_WORDS_E> qlwp;
|
||||
WDataInP lwp = nullptr;
|
||||
if (lbits <= VL_QUADSIZE) {
|
||||
ld = VL_VA_ARG_Q_(ap, lbits);
|
||||
@@ -780,7 +834,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
}
|
||||
switch (fmt) {
|
||||
case 'c': {
|
||||
IData charval = ld & 0xff;
|
||||
const IData charval = ld & 0xff;
|
||||
output += static_cast<char>(charval);
|
||||
break;
|
||||
}
|
||||
@@ -788,7 +842,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
std::string field;
|
||||
for (; lsb >= 0; --lsb) {
|
||||
lsb = (lsb / 8) * 8; // Next digit
|
||||
IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
|
||||
const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
|
||||
field += (charval == 0) ? ' ' : charval;
|
||||
}
|
||||
std::string padding;
|
||||
@@ -806,7 +860,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
append = t_tmp;
|
||||
} else {
|
||||
if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) {
|
||||
WData neg[VL_VALUE_STRING_MAX_WIDTH / 4 + 2];
|
||||
VlWide<VL_VALUE_STRING_MAX_WIDTH / 4 + 2> neg;
|
||||
VL_NEGATE_W(VL_WORDS_I(lbits), neg, lwp);
|
||||
append = std::string("-") + VL_DECIMAL_NW(lbits, neg);
|
||||
} else {
|
||||
@@ -851,7 +905,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
}
|
||||
case 't': { // Time
|
||||
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
|
||||
output += _vl_vsformat_time(t_tmp, static_cast<double>(ld), left, width);
|
||||
const int timeunit = va_arg(ap, int);
|
||||
output += _vl_vsformat_time(t_tmp, ld, timeunit, left, width);
|
||||
break;
|
||||
}
|
||||
case 'b':
|
||||
@@ -902,7 +957,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
}
|
||||
break;
|
||||
default: { // LCOV_EXCL_START
|
||||
std::string msg = std::string("Unknown _vl_vsformat code: ") + pos[0];
|
||||
const std::string msg = std::string("Unknown _vl_vsformat code: ") + pos[0];
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
break;
|
||||
} // LCOV_EXCL_STOP
|
||||
@@ -927,11 +982,11 @@ static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
|
||||
floc -= 8;
|
||||
}
|
||||
}
|
||||
static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp,
|
||||
static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr) VL_MT_SAFE {
|
||||
// Get a character without advancing
|
||||
if (fp) {
|
||||
int data = std::fgetc(fp);
|
||||
const int data = std::fgetc(fp);
|
||||
if (data == EOF) return EOF;
|
||||
ungetc(data, fp);
|
||||
return data;
|
||||
@@ -945,16 +1000,17 @@ static inline int _vl_vsss_peek(FILE* fp, int& floc, WDataInP fromp,
|
||||
}
|
||||
}
|
||||
}
|
||||
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, WDataInP fromp,
|
||||
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr) VL_MT_SAFE {
|
||||
while (true) {
|
||||
int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
||||
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
|
||||
if (c == EOF || !std::isspace(c)) return;
|
||||
_vl_vsss_advance(fp, floc);
|
||||
}
|
||||
}
|
||||
static inline void _vl_vsss_read_str(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr,
|
||||
char* tmpp, const char* acceptp) VL_MT_SAFE {
|
||||
static inline void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr, char* tmpp,
|
||||
const char* acceptp) VL_MT_SAFE {
|
||||
// Read into tmp, consisting of characters from acceptp list
|
||||
char* cp = tmpp;
|
||||
while (true) {
|
||||
@@ -968,8 +1024,9 @@ static inline void _vl_vsss_read_str(FILE* fp, int& floc, WDataInP fromp, const
|
||||
*cp++ = '\0';
|
||||
// VL_DBG_MSGF(" _read got='"<<tmpp<<"'\n");
|
||||
}
|
||||
static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, WDataInP fromp, const std::string& fstr,
|
||||
char* beginp, std::size_t n, bool inhibit = false) {
|
||||
static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr, char* beginp, std::size_t n,
|
||||
bool inhibit = false) {
|
||||
// Variant of _vl_vsss_read_str using the same underlying I/O functions but optimized
|
||||
// specifically for block reads of N bytes (read operations are not demarcated by
|
||||
// whitespace). In the fp case, except descriptor to have been opened in binary mode.
|
||||
@@ -1019,7 +1076,7 @@ static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const
|
||||
}
|
||||
|
||||
IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
int fbits, WDataInP fromp, // Else if a sscanf
|
||||
int fbits, const WDataInP fromp, // Else if a sscanf
|
||||
const std::string& fstr, // if a sscanf to string
|
||||
const char* formatp, va_list ap) VL_MT_SAFE {
|
||||
// Read a Verilog $sscanf/$fscanf style format into the output list
|
||||
@@ -1064,7 +1121,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
// Deal with all read-and-scan somethings
|
||||
// Note LSBs are preserved if there's an overflow
|
||||
const int obits = inIgnore ? 0 : va_arg(ap, int);
|
||||
WData qowp[VL_WQ_WORDS_E];
|
||||
VlWide<VL_WQ_WORDS_E> qowp;
|
||||
VL_SET_WQ(qowp, 0ULL);
|
||||
WDataOutP owp = qowp;
|
||||
if (obits == -1) { // string
|
||||
@@ -1185,7 +1242,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
break;
|
||||
}
|
||||
default: { // LCOV_EXCL_START
|
||||
std::string msg = std::string("Unknown _vl_vsscanf code: ") + pos[0];
|
||||
const std::string msg = std::string("Unknown _vl_vsscanf code: ") + pos[0];
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
break;
|
||||
} // LCOV_EXCL_STOP
|
||||
@@ -1227,7 +1284,7 @@ FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE {
|
||||
return Verilated::threadContextp()->impp()->fdToFp(lhs);
|
||||
}
|
||||
|
||||
void _vl_vint_to_string(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE {
|
||||
void _vl_vint_to_string(int obits, char* destoutp, const WDataInP sourcep) VL_MT_SAFE {
|
||||
// See also VL_DATA_TO_STRING_NW
|
||||
int lsb = obits - 1;
|
||||
bool start = true;
|
||||
@@ -1248,7 +1305,7 @@ void _vl_vint_to_string(int obits, char* destoutp, WDataInP sourcep) VL_MT_SAFE
|
||||
|
||||
void _vl_string_to_vint(int obits, void* destp, size_t srclen, const char* srcp) VL_MT_SAFE {
|
||||
// Convert C string to Verilog format
|
||||
size_t bytes = VL_BYTES_I(obits);
|
||||
const size_t bytes = VL_BYTES_I(obits);
|
||||
char* op = reinterpret_cast<char*>(destp);
|
||||
if (srclen > bytes) srclen = bytes; // Don't overflow destination
|
||||
size_t i = 0;
|
||||
@@ -1260,7 +1317,7 @@ static IData getLine(std::string& str, IData fpi, size_t maxLen) VL_MT_SAFE {
|
||||
str.clear();
|
||||
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
FILE* fp = VL_CVT_I_FP(fpi);
|
||||
FILE* const fp = VL_CVT_I_FP(fpi);
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
|
||||
// We don't use fgets, as we must read \0s.
|
||||
@@ -1276,7 +1333,7 @@ static IData getLine(std::string& str, IData fpi, size_t maxLen) VL_MT_SAFE {
|
||||
IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
|
||||
std::string str;
|
||||
const IData bytes = VL_BYTES_I(obits);
|
||||
IData got = getLine(str, fpi, bytes);
|
||||
const IData got = getLine(str, fpi, bytes);
|
||||
|
||||
if (VL_UNLIKELY(str.empty())) return 0;
|
||||
|
||||
@@ -1296,7 +1353,7 @@ IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE {
|
||||
|
||||
IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE {
|
||||
// We ignore lhs/fpi - IEEE says "most recent error" so probably good enough
|
||||
IData ret = errno;
|
||||
const IData ret = errno;
|
||||
outputr = std::string(::std::strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
@@ -1419,47 +1476,47 @@ void VL_FWRITEF(IData fpi, const char* formatp, ...) VL_MT_SAFE {
|
||||
|
||||
IData VL_FSCANF_IX(IData fpi, const char* formatp, ...) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
FILE* fp = VL_CVT_I_FP(fpi);
|
||||
FILE* const fp = VL_CVT_I_FP(fpi);
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
IData got = _vl_vsscanf(fp, 0, nullptr, "", formatp, ap);
|
||||
const IData got = _vl_vsscanf(fp, 0, nullptr, "", formatp, ap);
|
||||
va_end(ap);
|
||||
return got;
|
||||
}
|
||||
|
||||
IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...) VL_MT_SAFE {
|
||||
WData fnw[VL_WQ_WORDS_E];
|
||||
VlWide<VL_WQ_WORDS_E> fnw;
|
||||
VL_SET_WI(fnw, ld);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
IData got = _vl_vsscanf(nullptr, lbits, fnw, "", formatp, ap);
|
||||
const IData got = _vl_vsscanf(nullptr, lbits, fnw, "", formatp, ap);
|
||||
va_end(ap);
|
||||
return got;
|
||||
}
|
||||
IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...) VL_MT_SAFE {
|
||||
WData fnw[VL_WQ_WORDS_E];
|
||||
VlWide<VL_WQ_WORDS_E> fnw;
|
||||
VL_SET_WQ(fnw, ld);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
IData got = _vl_vsscanf(nullptr, lbits, fnw, "", formatp, ap);
|
||||
const IData got = _vl_vsscanf(nullptr, lbits, fnw, "", formatp, ap);
|
||||
va_end(ap);
|
||||
return got;
|
||||
}
|
||||
IData VL_SSCANF_IWX(int lbits, WDataInP lwp, const char* formatp, ...) VL_MT_SAFE {
|
||||
IData VL_SSCANF_IWX(int lbits, const WDataInP lwp, const char* formatp, ...) VL_MT_SAFE {
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
IData got = _vl_vsscanf(nullptr, lbits, lwp, "", formatp, ap);
|
||||
const IData got = _vl_vsscanf(nullptr, lbits, lwp, "", formatp, ap);
|
||||
va_end(ap);
|
||||
return got;
|
||||
}
|
||||
IData VL_SSCANF_INX(int, const std::string& ld, const char* formatp, ...) VL_MT_SAFE {
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
IData got = _vl_vsscanf(nullptr, ld.length() * 8, nullptr, ld, formatp, ap);
|
||||
const IData got = _vl_vsscanf(nullptr, ld.length() * 8, nullptr, ld, formatp, ap);
|
||||
va_end(ap);
|
||||
return got;
|
||||
}
|
||||
@@ -1467,7 +1524,7 @@ IData VL_SSCANF_INX(int, const std::string& ld, const char* formatp, ...) VL_MT_
|
||||
IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi, IData start,
|
||||
IData count) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
FILE* fp = VL_CVT_I_FP(fpi);
|
||||
FILE* const fp = VL_CVT_I_FP(fpi);
|
||||
if (VL_UNLIKELY(!fp)) return 0;
|
||||
if (count > (array_size - (start - array_lsb))) count = array_size - (start - array_lsb);
|
||||
// Prep for reading
|
||||
@@ -1517,14 +1574,14 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi
|
||||
}
|
||||
|
||||
IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
|
||||
WData lhsw[VL_WQ_WORDS_E];
|
||||
VlWide<VL_WQ_WORDS_E> lhsw;
|
||||
VL_SET_WQ(lhsw, lhs);
|
||||
return VL_SYSTEM_IW(VL_WQ_WORDS_E, lhsw);
|
||||
}
|
||||
IData VL_SYSTEM_IW(int lhswords, WDataInP lhsp) VL_MT_SAFE {
|
||||
IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
|
||||
char filenamez[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
_vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp);
|
||||
int code = std::system(filenamez); // Yes, std::system() is threadsafe
|
||||
const int code = std::system(filenamez); // Yes, std::system() is threadsafe
|
||||
return code >> 8; // Want exit status
|
||||
}
|
||||
|
||||
@@ -1652,7 +1709,7 @@ std::string VL_TO_STRING(CData lhs) { return VL_SFORMATF_NX("'h%0x", 8, lhs); }
|
||||
std::string VL_TO_STRING(SData lhs) { return VL_SFORMATF_NX("'h%0x", 16, lhs); }
|
||||
std::string VL_TO_STRING(IData lhs) { return VL_SFORMATF_NX("'h%0x", 32, lhs); }
|
||||
std::string VL_TO_STRING(QData lhs) { return VL_SFORMATF_NX("'h%0x", 64, lhs); }
|
||||
std::string VL_TO_STRING_W(int words, WDataInP obj) {
|
||||
std::string VL_TO_STRING_W(int words, const WDataInP obj) {
|
||||
return VL_SFORMATF_NX("'h%0x", words * VL_EDATASIZE, obj);
|
||||
}
|
||||
|
||||
@@ -1667,7 +1724,7 @@ std::string VL_TOUPPER_NN(const std::string& ld) VL_MT_SAFE {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE {
|
||||
std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE {
|
||||
// See also _vl_vint_to_string
|
||||
char destout[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1];
|
||||
int obits = lwords * VL_EDATASIZE;
|
||||
@@ -1749,7 +1806,7 @@ static const char* formatBinary(int nBits, vluint32_t bits) {
|
||||
|
||||
static VL_THREAD_LOCAL char t_buf[64];
|
||||
for (int i = 0; i < nBits; i++) {
|
||||
bool isOne = bits & (1 << (nBits - 1 - i));
|
||||
const bool isOne = bits & (1 << (nBits - 1 - i));
|
||||
t_buf[i] = (isOne ? '1' : '0');
|
||||
}
|
||||
t_buf[nBits] = '\0';
|
||||
@@ -1830,7 +1887,7 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
||||
// Check for hex or binary digits as file format requests
|
||||
else if (std::isxdigit(c) || (!reading_addr && (c == 'x' || c == 'X'))) {
|
||||
c = std::tolower(c);
|
||||
int value
|
||||
const int value
|
||||
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
|
||||
if (reading_addr) {
|
||||
// Decode @ addresses
|
||||
@@ -1859,12 +1916,13 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
|
||||
return false; // EOF
|
||||
}
|
||||
void VlReadMem::setData(void* valuep, const std::string& rhs) {
|
||||
QData shift = m_hex ? 4ULL : 1ULL;
|
||||
const QData shift = m_hex ? 4ULL : 1ULL;
|
||||
bool innum = false;
|
||||
// Shift value in
|
||||
for (const auto& i : rhs) {
|
||||
char c = std::tolower(i);
|
||||
int value = (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
|
||||
const char c = std::tolower(i);
|
||||
const int value
|
||||
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
|
||||
if (m_bits <= 8) {
|
||||
CData* datap = reinterpret_cast<CData*>(valuep);
|
||||
if (!innum) *datap = 0;
|
||||
@@ -1946,9 +2004,9 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
|
||||
}
|
||||
} else if (m_bits <= 64) {
|
||||
const QData* datap = reinterpret_cast<const QData*>(valuep);
|
||||
vluint64_t value = VL_MASK_Q(m_bits) & *datap;
|
||||
vluint32_t lo = value & 0xffffffff;
|
||||
vluint32_t hi = value >> 32;
|
||||
const vluint64_t value = VL_MASK_Q(m_bits) & *datap;
|
||||
const vluint32_t lo = value & 0xffffffff;
|
||||
const vluint32_t hi = value >> 32;
|
||||
if (m_hex) {
|
||||
fprintf(m_fp, memhFormat(m_bits - 32), hi);
|
||||
fprintf(m_fp, "%08x\n", lo);
|
||||
@@ -1957,7 +2015,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
|
||||
fprintf(m_fp, "%s\n", formatBinary(32, lo));
|
||||
}
|
||||
} else {
|
||||
WDataInP datap = reinterpret_cast<WDataInP>(valuep);
|
||||
const WDataInP datap = reinterpret_cast<WDataInP>(valuep);
|
||||
// output as a sequence of VL_EDATASIZE'd words
|
||||
// from MSB to LSB. Mask off the MSB word which could
|
||||
// contain junk above the top of valid data.
|
||||
@@ -1967,7 +2025,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
|
||||
EData data = datap[word_idx];
|
||||
if (first) {
|
||||
data &= VL_MASK_E(m_bits);
|
||||
int top_word_nbits = VL_BITBIT_E(m_bits - 1) + 1;
|
||||
const int top_word_nbits = VL_BITBIT_E(m_bits - 1) + 1;
|
||||
if (m_hex) {
|
||||
fprintf(m_fp, memhFormat(top_word_nbits), data);
|
||||
} else {
|
||||
@@ -2053,7 +2111,7 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
|
||||
if (VL_UNLIKELY(!wmem.isOpen())) return;
|
||||
|
||||
for (QData addr = start; addr <= end; ++addr) {
|
||||
QData row_offset = addr - array_lsb;
|
||||
const QData row_offset = addr - array_lsb;
|
||||
if (bits <= 8) {
|
||||
const CData* datap = &(reinterpret_cast<const CData*>(memp))[row_offset];
|
||||
wmem.print(addr, false, datap);
|
||||
@@ -2067,8 +2125,8 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
|
||||
const QData* datap = &(reinterpret_cast<const QData*>(memp))[row_offset];
|
||||
wmem.print(addr, false, datap);
|
||||
} else {
|
||||
WDataInP memDatap = reinterpret_cast<WDataInP>(memp);
|
||||
WDataInP datap = &memDatap[row_offset * VL_WORDS_I(bits)];
|
||||
const WDataInP memDatap = reinterpret_cast<WDataInP>(memp);
|
||||
const WDataInP datap = &memDatap[row_offset * VL_WORDS_I(bits)];
|
||||
wmem.print(addr, false, datap);
|
||||
}
|
||||
}
|
||||
@@ -2154,6 +2212,30 @@ double vl_time_multiplier(int scale) VL_PURE {
|
||||
return pow10[scale];
|
||||
}
|
||||
}
|
||||
vluint64_t vl_time_pow10(int n) {
|
||||
static const vluint64_t pow10[20] = {
|
||||
1ULL,
|
||||
10ULL,
|
||||
100ULL,
|
||||
1000ULL,
|
||||
10000ULL,
|
||||
100000ULL,
|
||||
1000000ULL,
|
||||
10000000ULL,
|
||||
100000000ULL,
|
||||
1000000000ULL,
|
||||
10000000000ULL,
|
||||
100000000000ULL,
|
||||
1000000000000ULL,
|
||||
10000000000000ULL,
|
||||
100000000000000ULL,
|
||||
1000000000000000ULL,
|
||||
10000000000000000ULL,
|
||||
100000000000000000ULL,
|
||||
1000000000000000000ULL,
|
||||
};
|
||||
return pow10[n];
|
||||
}
|
||||
|
||||
void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
|
||||
const VerilatedContext* contextp) VL_MT_SAFE {
|
||||
@@ -2272,7 +2354,7 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock(m_mutex);
|
||||
m_s.m_timeprecision = value;
|
||||
#ifdef SYSTEMC_VERSION
|
||||
sc_time sc_res = sc_get_time_resolution();
|
||||
const sc_time sc_res = sc_get_time_resolution();
|
||||
int sc_prec = 99;
|
||||
if (sc_res == sc_time(1, SC_SEC)) {
|
||||
sc_prec = 0;
|
||||
@@ -2294,7 +2376,7 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
|
||||
<< ". Suggest use 'sc_set_time_resolution(" << vl_time_str(value)
|
||||
<< ")', or Verilator '--timescale-override " << vl_time_str(sc_prec) << "/"
|
||||
<< vl_time_str(sc_prec) << "'";
|
||||
std::string msgs = msg.str();
|
||||
const std::string msgs = msg.str();
|
||||
VL_FATAL_MT("", 0, "", msgs.c_str());
|
||||
}
|
||||
#endif
|
||||
@@ -2355,7 +2437,7 @@ std::string VerilatedContextImp::argPlusMatch(const char* prefixp)
|
||||
VL_MT_SAFE_EXCLUDES(m_argMutex) {
|
||||
const VerilatedLockGuard lock(m_argMutex);
|
||||
// Note prefixp does not include the leading "+"
|
||||
size_t len = std::strlen(prefixp);
|
||||
const size_t len = std::strlen(prefixp);
|
||||
if (VL_UNLIKELY(!m_args.m_argVecLoaded)) {
|
||||
m_args.m_argVecLoaded = true; // Complain only once
|
||||
VL_FATAL_MT("unknown", 0, "",
|
||||
@@ -2432,7 +2514,7 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||
}
|
||||
bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::string& prefix,
|
||||
std::string& valuer) {
|
||||
size_t len = prefix.length();
|
||||
const size_t len = prefix.length();
|
||||
if (0 == std::strncmp(prefix.c_str(), arg.c_str(), len)) {
|
||||
valuer = arg.substr(len);
|
||||
return true;
|
||||
@@ -2449,7 +2531,7 @@ void VerilatedContext::randSeed(int val) VL_MT_SAFE {
|
||||
// and so the rand seed's mutex must also be static
|
||||
const VerilatedLockGuard lock(VerilatedContextImp::s().s_randMutex);
|
||||
m_s.m_randSeed = val;
|
||||
vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
|
||||
const vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
|
||||
// Obververs must see new epoch AFTER seed updated
|
||||
#ifdef VL_THREADED
|
||||
std::atomic_signal_fence(std::memory_order_release);
|
||||
@@ -2544,7 +2626,7 @@ const char* Verilated::catName(const char* n1, const char* n2, int scopet,
|
||||
// Used by symbol table creation to make module names
|
||||
static VL_THREAD_LOCAL char* t_strp = nullptr;
|
||||
static VL_THREAD_LOCAL size_t t_len = 0;
|
||||
size_t newlen
|
||||
const size_t newlen
|
||||
= std::strlen(n1) + std::strlen(n2) + std::strlen(delimiter) + (scopet > 0 ? 2 : 1);
|
||||
if (VL_UNLIKELY(!t_strp || newlen > t_len)) {
|
||||
if (t_strp) delete[] t_strp;
|
||||
@@ -2647,8 +2729,8 @@ void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE {
|
||||
|
||||
void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
|
||||
// Slowpath - Called only when signal sets too high of a bit
|
||||
std::string msg = (std::string("Testbench C set input '") + signame
|
||||
+ "' to value that overflows what the signal's width can fit");
|
||||
const std::string msg = (std::string("Testbench C set input '") + signame
|
||||
+ "' to value that overflows what the signal's width can fit");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
VL_UNREACHABLE
|
||||
}
|
||||
@@ -2810,8 +2892,8 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo
|
||||
va_list ap;
|
||||
va_start(ap, dims);
|
||||
for (int i = 0; i < dims; ++i) {
|
||||
int msb = va_arg(ap, int);
|
||||
int lsb = va_arg(ap, int);
|
||||
const int msb = va_arg(ap, int);
|
||||
const int lsb = va_arg(ap, int);
|
||||
if (i == 0) {
|
||||
var.m_packed.m_left = msb;
|
||||
var.m_packed.m_right = lsb;
|
||||
@@ -2842,18 +2924,20 @@ VerilatedVar* VerilatedScope::varFind(const char* namep) const VL_MT_SAFE_POSTIN
|
||||
|
||||
void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
|
||||
// Slowpath - Called only when find has failed
|
||||
std::string msg = (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum)
|
||||
+ "' but scope wasn't set, perhaps due to dpi import call without "
|
||||
+ "'context', or missing svSetScope. See IEEE 1800-2017 35.5.3.");
|
||||
const std::string msg
|
||||
= (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum)
|
||||
+ "' but scope wasn't set, perhaps due to dpi import call without "
|
||||
+ "'context', or missing svSetScope. See IEEE 1800-2017 35.5.3.");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* VerilatedScope::exportFindError(int funcnum) const {
|
||||
// Slowpath - Called only when find has failed
|
||||
std::string msg = (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum)
|
||||
+ "' but this DPI export function exists only in other scopes, not scope '"
|
||||
+ name() + "'");
|
||||
const std::string msg
|
||||
= (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum)
|
||||
+ "' but this DPI export function exists only in other scopes, not scope '" + name()
|
||||
+ "'");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+255
-243
File diff suppressed because it is too large
Load Diff
+64
-10
@@ -9,16 +9,18 @@
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
######################################################################
|
||||
|
||||
PERL = @PERL@
|
||||
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)
|
||||
@@ -35,6 +37,7 @@ CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
|
||||
|
||||
VERILATOR_COVERAGE = $(PERL) $(VERILATOR_ROOT)/bin/verilator_coverage
|
||||
VERILATOR_INCLUDER = $(PERL) $(VERILATOR_ROOT)/bin/verilator_includer
|
||||
VERILATOR_CCACHE_REPORT = $(PYTHON3) $(VERILATOR_ROOT)/bin/verilator_ccache_report
|
||||
|
||||
######################################################################
|
||||
# Make checks
|
||||
@@ -102,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
|
||||
|
||||
@@ -202,16 +213,19 @@ else
|
||||
endif
|
||||
|
||||
# When archiving just objects (.o), use single $(AR) run
|
||||
# 1. Make .tmp file with list of objects so don't exceed commend line
|
||||
# 1. Make .verilator_deplist.tmp file with list of objects so don't exceed
|
||||
# the command line limits when calling $(AR).
|
||||
# The approach to write the dependency file is compatible with GNU Make 3,
|
||||
# and can be simplified using the file function once GNU Make 4.x becomes
|
||||
# the minimum supported version.
|
||||
# When merging objects (.o) and archives (.a) additionally:
|
||||
# 1. Extract object files from .a
|
||||
# 2. Create a new archive from extracted .o and given .o
|
||||
%.a:
|
||||
%.a: | %.verilator_deplist.tmp
|
||||
$(info Archive $(AR) -rcs $@ $^)
|
||||
$(file >$@.tmp)
|
||||
$(foreach L, $(filter-out %.a,$^), $(file >>[email protected], $L))
|
||||
$(foreach L, $(filter-out %.a,$^), $(shell echo $L >>[email protected]_deplist.tmp))
|
||||
@if test $(words $(filter %.a,$^)) -eq 0; then \
|
||||
$(AR) -rcs $@ @[email protected]; \
|
||||
$(AR) -rcs $@ @$@.verilator_deplist.tmp; \
|
||||
else \
|
||||
$(RM) -rf [email protected]; \
|
||||
for archive in $(filter %.a,$^); do \
|
||||
@@ -220,9 +234,13 @@ endif
|
||||
$(AR) -x ../../$${archive}; \
|
||||
cd ../..; \
|
||||
done; \
|
||||
$(AR) -rcs $@ @[email protected] [email protected]/*/*.o; \
|
||||
$(AR) -rcs $@ @$@.verilator_deplist.tmp [email protected]/*/*.o; \
|
||||
fi \
|
||||
; $(RM) -rf [email protected] [email protected]
|
||||
; $(RM) -rf $@.verilator_deplist.tmp [email protected]
|
||||
|
||||
# Truncate the dependency list file used in the %.a target above.
|
||||
%.verilator_deplist.tmp:
|
||||
echo "" > $@
|
||||
|
||||
$(VM_PREFIX)__ALL.a: $(VK_OBJS) $(VM_HIER_LIBS)
|
||||
|
||||
@@ -247,6 +265,42 @@ endif
|
||||
#.cpp.o:
|
||||
# $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
######################################################################
|
||||
### ccache report
|
||||
|
||||
ifneq ($(findstring ccache-report,$(MAKECMDGOALS)),)
|
||||
ifneq ($(OBJCACHE),ccache)
|
||||
$(error ccache-report requires OBJCACHE to equal 'ccache')
|
||||
endif
|
||||
VK_OTHER_GOALS := $(strip $(subst ccache-report,,$(MAKECMDGOALS)))
|
||||
ifeq ($(VK_OTHER_GOALS),)
|
||||
$(error ccache-report must be used with at least one other explicit target)
|
||||
endif
|
||||
|
||||
# Report ccache behaviour for this invocation of make
|
||||
VK_CCACHE_LOGDIR := ccache-logs
|
||||
VK_CCACHE_REPORT := $(VM_PREFIX)__ccache_report.txt
|
||||
# Remove previous logfiles and report
|
||||
$(shell rm -rf $(VK_CCACHE_LOGDIR) $(VK_CCACHE_REPORT))
|
||||
|
||||
$(VK_CCACHE_LOGDIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(VK_OBJS): | $(VK_CCACHE_LOGDIR)
|
||||
|
||||
$(VK_OBJS): export CCACHE_LOGFILE=$(VK_CCACHE_LOGDIR)/[email protected]
|
||||
|
||||
$(VK_CCACHE_REPORT): $(VK_OBJS)
|
||||
$(VERILATOR_CCACHE_REPORT) -o $@ $(VK_CCACHE_LOGDIR)
|
||||
|
||||
.PHONY: ccache-report
|
||||
ccache-report: $(VK_CCACHE_REPORT)
|
||||
@cat $<
|
||||
|
||||
# ccache-report runs last
|
||||
ccache-report: $(VK_OTHER_GOALS)
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
### Debugging
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ private:
|
||||
|
||||
// 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 std::string prefix = std::string(a, apre - a);
|
||||
|
||||
// Scan backward to last mismatch
|
||||
const char* apost = a + std::strlen(a) - 1;
|
||||
@@ -251,7 +251,7 @@ 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);
|
||||
m_forcePerInstance = flag;
|
||||
@@ -261,7 +261,7 @@ public:
|
||||
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);
|
||||
if (matchp && matchp[0]) {
|
||||
@@ -288,7 +288,7 @@ public:
|
||||
assert(!m_insertp);
|
||||
m_insertp = itemp;
|
||||
}
|
||||
void insertf(const char* filenamep, int lineno) VL_MT_SAFE_EXCLUDES(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;
|
||||
|
||||
+56
-56
@@ -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);
|
||||
@@ -279,7 +279,7 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
|
||||
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];
|
||||
@@ -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;
|
||||
@@ -460,13 +460,13 @@ void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int
|
||||
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;
|
||||
}
|
||||
@@ -492,13 +492,13 @@ void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
|
||||
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;
|
||||
}
|
||||
@@ -528,13 +528,13 @@ void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int 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;
|
||||
}
|
||||
@@ -559,13 +559,13 @@ void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int in
|
||||
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;
|
||||
}
|
||||
@@ -592,13 +592,13 @@ svBit svGetBitArrElem(const svOpenArrayHandle s, int 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;
|
||||
}
|
||||
@@ -625,13 +625,13 @@ svLogic svGetLogicArrElem(const svOpenArrayHandle s, int 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;
|
||||
}
|
||||
@@ -660,13 +660,13 @@ void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int 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;
|
||||
}
|
||||
@@ -691,13 +691,13 @@ void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int 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;
|
||||
}
|
||||
|
||||
+12
-11
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h" // Also presumably included by caller
|
||||
#include "verilated_heavy.h" // Also presumably included by caller
|
||||
#include "verilated_sym_props.h"
|
||||
|
||||
#include "svdpi.h"
|
||||
@@ -37,7 +38,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 +48,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 +72,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;
|
||||
|
||||
@@ -150,7 +150,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;
|
||||
}
|
||||
@@ -206,7 +206,7 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,8 @@ public:
|
||||
/// Flush dump
|
||||
void flush() VL_MT_SAFE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
/// Call with the current context's time just after eval'ed,
|
||||
/// e.g. ->dump(contextp->time())
|
||||
void dump(vluint64_t timeui) { m_sptrace.dump(timeui); }
|
||||
/// Write one cycle of dump data - backward compatible and to reduce
|
||||
/// conversion warnings. It's better to use a vluint64_t time instead.
|
||||
|
||||
@@ -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());
|
||||
|
||||
+36
-36
@@ -27,6 +27,7 @@
|
||||
#include "verilated.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -42,7 +43,7 @@ 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);
|
||||
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
|
||||
|
||||
//===================================================================
|
||||
// Shuffle RNG
|
||||
@@ -88,19 +89,24 @@ public:
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
/// Verilog wide unpacked bit container.
|
||||
/// 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.
|
||||
|
||||
template <std::size_t T_Words> class VlWide final {
|
||||
EData m_storage[T_Words];
|
||||
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
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// cppcheck-suppress uninitVar
|
||||
VlWide() = default;
|
||||
~VlWide() = default;
|
||||
@@ -110,10 +116,8 @@ public:
|
||||
// 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]; }
|
||||
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]; }
|
||||
@@ -127,7 +131,8 @@ public:
|
||||
|
||||
// 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>&) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -791,32 +796,27 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
|
||||
VlWriteMem wmem(hex, bits, filename, start, end);
|
||||
if (VL_UNLIKELY(!wmem.isOpen())) return;
|
||||
for (const auto& i : obj) {
|
||||
QData addr = i.first;
|
||||
const QData addr = i.first;
|
||||
if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
/// Verilog packed array container
|
||||
/// 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> class VlUnpacked final {
|
||||
private:
|
||||
// TYPES
|
||||
using Array = std::array<T_Value, T_Depth>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename Array::const_iterator;
|
||||
|
||||
private:
|
||||
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
|
||||
// MEMBERS
|
||||
Array m_array; // Contents of the packed array
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
T_Value m_storage[T_Depth]; // Contents of the unpacked array
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlUnpacked() = default;
|
||||
~VlUnpacked() = default;
|
||||
@@ -827,18 +827,18 @@ public:
|
||||
|
||||
// METHODS
|
||||
// Raw access
|
||||
WData* data() { return &m_array[0]; }
|
||||
const WData* data() const { return &m_array[0]; }
|
||||
WData* data() { return &m_storage[0]; }
|
||||
const WData* data() const { return &m_storage[0]; }
|
||||
|
||||
T_Value& operator[](size_t index) { return m_array[index]; };
|
||||
const T_Value& operator[](size_t index) const { return m_array[index]; };
|
||||
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_array[i]);
|
||||
out += comma + VL_TO_STRING(m_storage[i]);
|
||||
comma = ", ";
|
||||
}
|
||||
return out + "} ";
|
||||
@@ -877,16 +877,16 @@ static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
|
||||
//======================================================================
|
||||
// Conversion functions
|
||||
|
||||
extern std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE;
|
||||
extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE;
|
||||
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
|
||||
WData lw[VL_WQ_WORDS_E];
|
||||
VlWide<VL_WQ_WORDS_E> lw;
|
||||
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];
|
||||
VlWide<VL_WQ_WORDS_E> lw;
|
||||
VL_SET_WI(lw, lhs);
|
||||
return VL_CVT_PACK_STR_NW(1, lw);
|
||||
}
|
||||
@@ -925,31 +925,31 @@ extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suff
|
||||
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
|
||||
VlWide<2> rwp; // 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
|
||||
VlWide<2> rwp; // 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];
|
||||
VlWide<2> rwp;
|
||||
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];
|
||||
VlWide<2> rwp;
|
||||
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];
|
||||
VlWide<2> rwp;
|
||||
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
|
||||
if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp));
|
||||
return got;
|
||||
|
||||
+10
-5
@@ -245,7 +245,7 @@ public: // But only for verilated*.cpp
|
||||
static vluint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
|
||||
|
||||
// METHODS - timeformat
|
||||
int timeFormatUnits() VL_MT_SAFE {
|
||||
int timeFormatUnits() const VL_MT_SAFE {
|
||||
if (m_s.m_timeFormatUnits == VerilatedContext::Serialized::UNITS_NONE)
|
||||
return timeprecision();
|
||||
return m_s.m_timeFormatUnits;
|
||||
@@ -255,7 +255,7 @@ public: // But only for verilated*.cpp
|
||||
void timeFormatPrecision(int value) VL_MT_SAFE { m_s.m_timeFormatPrecision = value; }
|
||||
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() VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
const VerilatedLockGuard lock(m_timeDumpMutex);
|
||||
return m_timeFormatSuffix;
|
||||
}
|
||||
@@ -333,17 +333,20 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock(m_fdMutex);
|
||||
if ((fdi & (1 << 31)) != 0) {
|
||||
if (VL_BITISSET_I(fdi, 31)) {
|
||||
// Non-MCD case
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
if (VL_UNLIKELY(idx >= m_fdps.size())) return;
|
||||
if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr
|
||||
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
|
||||
std::fclose(m_fdps[idx]);
|
||||
m_fdps[idx] = (FILE*)0;
|
||||
m_fdFree.push_back(idx);
|
||||
} else {
|
||||
// MCD case
|
||||
for (int i = 0; (fdi != 0) && (i < 31); i++, fdi >>= 1) {
|
||||
// Starts at 1 to skip stdout
|
||||
fdi >>= 1;
|
||||
for (int i = 1; (fdi != 0) && (i < 31); i++, fdi >>= 1) {
|
||||
if (fdi & VL_MASK_I(1)) {
|
||||
std::fclose(m_fdps[i]);
|
||||
m_fdps[i] = nullptr;
|
||||
@@ -375,7 +378,9 @@ private:
|
||||
}
|
||||
} else {
|
||||
// MCD Case
|
||||
for (size_t i = 0; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
|
||||
if (fdi & 1) fp.push_back(stdout);
|
||||
fdi >>= 1;
|
||||
for (size_t i = 1; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
|
||||
if (fdi & VL_MASK_I(1)) fp.push_back(m_fdps[i]);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -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,8 +91,8 @@ 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
|
||||
const std::string fn = filename();
|
||||
const std::string msg
|
||||
= std::string(
|
||||
"Can't deserialize; file has wrong header signature, or file not found: ")
|
||||
+ filename();
|
||||
@@ -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,10 +194,10 @@ 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)) {
|
||||
@@ -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;
|
||||
|
||||
+10
-10
@@ -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;
|
||||
|
||||
@@ -74,7 +74,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 +93,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++) {
|
||||
@@ -149,7 +149,7 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed)
|
||||
const VerilatedLockGuard lk(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
|
||||
|
||||
+13
-13
@@ -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} {}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
m_ready.emplace_back(fnp, selfp, evenCycle);
|
||||
m_ready_size.fetch_add(1, std::memory_order_relaxed);
|
||||
notify = m_waiting;
|
||||
}
|
||||
|
||||
+14
-10
@@ -99,7 +99,6 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
|
||||
m_wrBufp = new char[m_wrChunkSize * 8];
|
||||
m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
|
||||
m_writep = m_wrBufp;
|
||||
m_suffixesp = nullptr;
|
||||
}
|
||||
|
||||
void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
@@ -114,9 +113,6 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
|
||||
dumpHeader();
|
||||
|
||||
// Get the direct access pointer to the code strings
|
||||
m_suffixesp = &m_suffixes[0]; // Note: C++11 m_suffixes.data();
|
||||
|
||||
// When using rollover, the first chunk contains the header only.
|
||||
if (m_rolloverMB) openNextImp(true);
|
||||
}
|
||||
@@ -133,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])) {
|
||||
@@ -303,10 +299,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;
|
||||
@@ -356,7 +352,7 @@ 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;
|
||||
@@ -586,8 +582,10 @@ void VerilatedVcd::declTriArray(vluint32_t code, const char* name, bool array, i
|
||||
//=============================================================================
|
||||
// Trace rendering prinitives
|
||||
|
||||
void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
|
||||
const char* const suffixp = m_suffixesp + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
|
||||
static inline void
|
||||
VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
|
||||
|
||||
static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
|
||||
// Copy the whole suffix (this avoid having hard to predict branches which
|
||||
// helps a lot). Note: The maximum length of the suffix is
|
||||
// VL_TRACE_MAX_VCD_CODE_SIZE + 2 == 7, but we unroll this here for speed.
|
||||
@@ -605,6 +603,12 @@ void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
|
||||
writep[5] = suffixp[5];
|
||||
writep[6] = '\n'; // The 6th index is always '\n' if it's relevant, no need to fetch it.
|
||||
#endif
|
||||
}
|
||||
|
||||
void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
|
||||
const char* const suffixp = m_suffixes.data() + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
|
||||
VerilatedVcdCCopyAndAppendNewLine(writep, suffixp);
|
||||
|
||||
// Now write back the write pointer incremented by the actual size of the
|
||||
// suffix, which was stored in the last byte of the suffix buffer entry.
|
||||
m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
|
||||
|
||||
@@ -80,7 +80,6 @@ private:
|
||||
vluint64_t m_wroteBytes = 0; // Number of bytes written to this file
|
||||
|
||||
std::vector<char> m_suffixes; // VCD line end string codes + metadata
|
||||
const char* m_suffixesp; // Pointer to first element of above
|
||||
|
||||
using NameMap = std::map<const std::string, const std::string>;
|
||||
NameMap* m_namemapp = nullptr; // List of names for the header
|
||||
@@ -234,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);
|
||||
@@ -246,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);
|
||||
@@ -271,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
|
||||
@@ -282,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);
|
||||
@@ -291,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);
|
||||
@@ -369,6 +368,8 @@ public:
|
||||
/// Flush dump
|
||||
void flush() VL_MT_SAFE { m_sptrace.flush(); }
|
||||
/// Write one cycle of dump data
|
||||
/// Call with the current context's time just after eval'ed,
|
||||
/// e.g. ->dump(contextp->time())
|
||||
void dump(vluint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
|
||||
/// Write one cycle of dump data - backward compatible and to reduce
|
||||
/// conversion warnings. It's better to use a vluint64_t time instead.
|
||||
|
||||
@@ -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());
|
||||
|
||||
+57
-57
@@ -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 {
|
||||
@@ -365,7 +365,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;
|
||||
@@ -564,7 +564,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 +587,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,7 +619,7 @@ 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;
|
||||
@@ -715,7 +715,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;
|
||||
@@ -1300,9 +1300,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 +1316,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 +1332,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,7 +1355,7 @@ 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;
|
||||
@@ -1373,7 +1373,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
|
||||
}
|
||||
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);
|
||||
@@ -1407,7 +1407,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
|
||||
// Memory words are not indexable
|
||||
VerilatedVpioMemoryWord* 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())) {
|
||||
@@ -1496,7 +1496,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) {
|
||||
@@ -1508,7 +1508,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
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
|
||||
@@ -1521,15 +1521,15 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
|
||||
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());
|
||||
}
|
||||
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());
|
||||
}
|
||||
@@ -1560,30 +1560,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 +1602,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) {
|
||||
@@ -1725,7 +1725,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
__FILE__, __LINE__, "",
|
||||
"vpi_get_value with more than VL_MULS_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;
|
||||
@@ -1756,7 +1756,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) {
|
||||
@@ -1770,7 +1770,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
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
|
||||
@@ -1832,7 +1832,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;
|
||||
@@ -1862,7 +1862,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
|
||||
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 : ' ';
|
||||
}
|
||||
@@ -1896,7 +1896,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
|
||||
if (VerilatedVpioVar* 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)) {
|
||||
@@ -1920,7 +1920,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 +1953,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 +1962,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 +1977,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 +2029,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 +2057,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 +2097,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 +2120,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,8 +2153,8 @@ 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];
|
||||
@@ -2162,9 +2162,9 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
|
||||
} else if (time_p->type == vpiScaledRealTime) {
|
||||
double dtime = VL_TIME_D();
|
||||
if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) {
|
||||
int scalePow10
|
||||
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 +2198,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 +2208,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 +2221,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 +2238,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 +2255,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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -43,6 +43,12 @@
|
||||
# define VL_ATTR_COLD __attribute__((cold))
|
||||
# define VL_ATTR_HOT __attribute__((hot))
|
||||
# define VL_ATTR_NORETURN __attribute__((noreturn))
|
||||
// clang and gcc-8.0+ support no_sanitize("string") style attribute
|
||||
# if defined(__clang__) || (__GNUC__ >= 8)
|
||||
# define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize("alignment")))
|
||||
#else // The entire undefined sanitizer has to be disabled for older gcc
|
||||
# define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize_undefined))
|
||||
#endif
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1)))
|
||||
# define VL_ATTR_PURE __attribute__((pure))
|
||||
# define VL_ATTR_UNUSED __attribute__((unused))
|
||||
@@ -85,6 +91,9 @@
|
||||
#ifndef VL_ATTR_NORETURN
|
||||
# define VL_ATTR_NORETURN ///< Attribute that function does not ever return
|
||||
#endif
|
||||
#ifndef VL_ATTR_NO_SANITIZE_ALIGN
|
||||
# define VL_ATTR_NO_SANITIZE_ALIGN ///< Attribute that the function contains intended unaligned access
|
||||
#endif
|
||||
#ifndef VL_ATTR_PRINTF
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) ///< Attribute for function with printf format checking
|
||||
#endif
|
||||
@@ -428,6 +437,7 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
(((nbits) & VL_SIZEBITS_Q) ? ((1ULL << ((nbits) & VL_SIZEBITS_Q)) - 1ULL) : ~0ULL)
|
||||
/// Return mask for EData with 1's where relevant bits are (0=all bits)
|
||||
#define VL_MASK_E(nbits) VL_MASK_I(nbits)
|
||||
|
||||
#define VL_EUL(n) VL_UL(n) // Make constant number EData sized
|
||||
|
||||
#define VL_BITWORD_I(bit) ((bit) / VL_IDATASIZE) ///< Word number for sv DPI vectors
|
||||
|
||||
+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]:
|
||||
|
||||
+9
-9
@@ -47,25 +47,25 @@ obj_dbg:
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
.PHONY: ../bin/verilator_bin ../bin/verilator_bin_dbg ../bin/verilator_coverage_bin_dbg
|
||||
.PHONY: ../bin/verilator_bin$(EXEEXT) ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
|
||||
|
||||
opt: ../bin/verilator_bin
|
||||
opt: ../bin/verilator_bin$(EXEEXT)
|
||||
ifeq ($(VERILATOR_NO_OPT_BUILD),1) # Faster laptop development... One build
|
||||
../bin/verilator_bin: ../bin/verilator_bin_dbg
|
||||
-cp -p $<$(EXEEXT) $@$(EXEEXT).tmp
|
||||
-mv -f $@$(EXEEXT).tmp $@$(EXEEXT)
|
||||
../bin/verilator_bin$(EXEEXT): ../bin/verilator_bin_dbg$(EXEEXT)
|
||||
-cp -p $< $@.tmp
|
||||
-mv -f $@.tmp $@
|
||||
else
|
||||
../bin/verilator_bin: obj_opt ../bin prefiles
|
||||
../bin/verilator_bin$(EXEEXT): obj_opt ../bin prefiles
|
||||
$(MAKE) -C obj_opt -j 1 TGT=../$@ -f ../Makefile_obj serial
|
||||
$(MAKE) -C obj_opt TGT=../$@ -f ../Makefile_obj
|
||||
endif
|
||||
|
||||
dbg: ../bin/verilator_bin_dbg ../bin/verilator_coverage_bin_dbg
|
||||
../bin/verilator_bin_dbg: obj_dbg ../bin prefiles
|
||||
dbg: ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
|
||||
../bin/verilator_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
|
||||
../bin/verilator_coverage_bin_dbg: obj_dbg ../bin prefiles
|
||||
../bin/verilator_coverage_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj
|
||||
|
||||
|
||||
+14
-10
@@ -72,18 +72,16 @@ CFG_LIBS = @CFG_LIBS@
|
||||
#### End of system configuration section. ####
|
||||
|
||||
VPATH += . $(bldsrc) $(srcdir)
|
||||
TGT = ../../verilator_bin
|
||||
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
|
||||
@@ -181,11 +178,16 @@ RAW_OBJS = \
|
||||
V3Depth.o \
|
||||
V3DepthBlock.o \
|
||||
V3Descope.o \
|
||||
V3DupFinder.o \
|
||||
V3EmitC.o \
|
||||
V3EmitCBase.o \
|
||||
V3EmitCConstPool.o \
|
||||
V3EmitCFunc.o \
|
||||
V3EmitCInlines.o \
|
||||
V3EmitCSyms.o \
|
||||
V3EmitCMake.o \
|
||||
V3EmitCMain.o \
|
||||
V3EmitCMake.o \
|
||||
V3EmitCModel.o \
|
||||
V3EmitCSyms.o \
|
||||
V3EmitMk.o \
|
||||
V3EmitV.o \
|
||||
V3EmitXml.o \
|
||||
@@ -202,7 +204,8 @@ RAW_OBJS = \
|
||||
V3GraphDfa.o \
|
||||
V3GraphPathChecker.o \
|
||||
V3GraphTest.o \
|
||||
V3Hashed.o \
|
||||
V3Hash.o \
|
||||
V3Hasher.o \
|
||||
V3HierBlock.o \
|
||||
V3Inline.o \
|
||||
V3Inst.o \
|
||||
@@ -221,6 +224,7 @@ RAW_OBJS = \
|
||||
V3MergeCond.o \
|
||||
V3Name.o \
|
||||
V3Number.o \
|
||||
V3OptionParser.o \
|
||||
V3Options.o \
|
||||
V3Order.o \
|
||||
V3Os.o \
|
||||
|
||||
+11
-12
@@ -309,16 +309,16 @@ private:
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) {
|
||||
LatchDetectGraphVertex* parentp = m_graph.currentp();
|
||||
LatchDetectGraphVertex* branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
|
||||
m_graph.addPathVertex(branchp, "IF");
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
m_graph.addPathVertex(branchp, "ELSE");
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
m_graph.currentp(parentp);
|
||||
if (!nodep->isBoundsCheck()) {
|
||||
LatchDetectGraphVertex* parentp = m_graph.currentp();
|
||||
LatchDetectGraphVertex* branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
|
||||
m_graph.addPathVertex(branchp, "IF");
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
m_graph.addPathVertex(branchp, "ELSE");
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
m_graph.currentp(parentp);
|
||||
}
|
||||
}
|
||||
// Empty visitors, speed things up
|
||||
virtual void visit(AstNodeMath* nodep) {}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) { iterateChildren(nodep); }
|
||||
|
||||
@@ -474,11 +474,10 @@ private:
|
||||
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);
|
||||
}
|
||||
|
||||
+4
-3
@@ -108,6 +108,7 @@ private:
|
||||
// Like newFireAssert() but omits the asserts-on check
|
||||
AstDisplay* dispp = new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR, message,
|
||||
nullptr, nullptr);
|
||||
dispp->fmtp()->timeunit(m_modp->timeunit());
|
||||
AstNode* bodysp = dispp;
|
||||
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
||||
bodysp->addNext(new AstStop(nodep->fileline(), true));
|
||||
@@ -162,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.");
|
||||
@@ -230,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());
|
||||
@@ -304,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))
|
||||
|
||||
+6
-18
@@ -119,7 +119,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 +932,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();
|
||||
@@ -970,20 +972,6 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
|
||||
&& (ignNext || sameTreeIter(node1p->m_nextp, node2p->m_nextp, false, gateOnly)));
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Static utilities
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) {
|
||||
return os << std::hex << std::setw(2) << std::setfill('0') << rhs.depth() << "_"
|
||||
<< std::setw(6) << std::setfill('0') << rhs.hshval();
|
||||
}
|
||||
|
||||
V3Hash::V3Hash(const string& name) {
|
||||
uint32_t val = 0;
|
||||
for (const auto& c : name) val = val * 31 + c;
|
||||
setBoth(1, val);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Debugging
|
||||
|
||||
@@ -1059,7 +1047,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
|
||||
|
||||
@@ -1192,7 +1180,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;
|
||||
|
||||
+136
-209
@@ -26,6 +26,7 @@
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "V3Ast__gen_classes.h" // From ./astgen
|
||||
@@ -155,6 +156,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; }
|
||||
@@ -457,6 +459,7 @@ public:
|
||||
// Internal types for mid-steps
|
||||
SCOPEPTR,
|
||||
CHARPTR,
|
||||
MTASKSTATE,
|
||||
// Unsigned and two state; fundamental types
|
||||
UINT32,
|
||||
UINT64,
|
||||
@@ -467,18 +470,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 +515,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 +554,13 @@ 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; }
|
||||
};
|
||||
inline bool operator==(const AstBasicDTypeKwd& lhs, const AstBasicDTypeKwd& rhs) {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
@@ -1136,10 +1143,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); }
|
||||
};
|
||||
@@ -1271,6 +1282,8 @@ public:
|
||||
virtual ~AstNVisitor() { doDeletes(); }
|
||||
/// Call visit()s on nodep
|
||||
void iterate(AstNode* nodep);
|
||||
/// Call visit()s on nodep
|
||||
void iterateNull(AstNode* nodep);
|
||||
/// Call visit()s on nodep's children
|
||||
void iterateChildren(AstNode* nodep);
|
||||
/// Call visit()s on nodep's children in backp() order
|
||||
@@ -1321,59 +1334,6 @@ inline std::ostream& operator<<(std::ostream& os, const AstNRelinker& rhs) {
|
||||
return os;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// V3Hash -- Node hashing for V3Combine
|
||||
|
||||
class V3Hash final {
|
||||
// A hash of a tree of nodes, consisting of 8 bits with the number of nodes in the hash
|
||||
// and 24 bit value hash of relevant information about the node.
|
||||
// A value of 0 is illegal
|
||||
uint32_t m_both;
|
||||
static const uint32_t M24 = ((1 << 24) - 1);
|
||||
void setBoth(uint32_t depth, uint32_t hshval) {
|
||||
if (depth == 0) depth = 1;
|
||||
if (depth > 255) depth = 255;
|
||||
m_both = (depth << 24) | (hshval & M24);
|
||||
}
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
bool isIllegal() const { return m_both == 0; }
|
||||
uint32_t fullValue() const { return m_both; }
|
||||
uint32_t depth() const { return (m_both >> 24) & 255; }
|
||||
uint32_t hshval() const { return m_both & M24; }
|
||||
// OPERATORS
|
||||
bool operator==(const V3Hash& rh) const { return m_both == rh.m_both; }
|
||||
bool operator!=(const V3Hash& rh) const { return m_both != rh.m_both; }
|
||||
bool operator<(const V3Hash& rh) const { return m_both < rh.m_both; }
|
||||
// CONSTRUCTORS
|
||||
class Illegal {}; // for creator type-overload selection
|
||||
class FullValue {}; // for creator type-overload selection
|
||||
explicit V3Hash(Illegal) { m_both = 0; }
|
||||
// Saving and restoring inside a userp
|
||||
explicit V3Hash(const VNUser& u) { m_both = u.toInt(); }
|
||||
V3Hash operator+=(const V3Hash& rh) {
|
||||
setBoth(depth() + rh.depth(), (hshval() * 31 + rh.hshval()));
|
||||
return *this;
|
||||
}
|
||||
// Creating from raw data (sameHash functions)
|
||||
V3Hash() { setBoth(1, 0); }
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
V3Hash(uint32_t val) { setBoth(1, val); }
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
V3Hash(const void* vp) { setBoth(1, cvtToHash(vp)); }
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
V3Hash(const string& name);
|
||||
V3Hash(V3Hash h1, V3Hash h2) { setBoth(1, h1.hshval() * 31 + h2.hshval()); }
|
||||
V3Hash(V3Hash h1, V3Hash h2, V3Hash h3) {
|
||||
setBoth(1, (h1.hshval() * 31 + h2.hshval()) * 31 + h3.hshval());
|
||||
}
|
||||
V3Hash(V3Hash h1, V3Hash h2, V3Hash h3, V3Hash h4) {
|
||||
setBoth(1, ((h1.hshval() * 31 + h2.hshval()) * 31 + h3.hshval()) * 31 + h4.hshval());
|
||||
}
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs);
|
||||
|
||||
//######################################################################
|
||||
// Callback base class to determine if node matches some formula
|
||||
|
||||
@@ -1738,6 +1698,7 @@ 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* findVoidDType() const;
|
||||
AstNodeDType* findQueueIndexDType() const;
|
||||
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
|
||||
@@ -1835,9 +1796,6 @@ public:
|
||||
// statement is unlikely to be taken
|
||||
virtual bool isUnlikely() const { return false; }
|
||||
virtual int instrCount() const { return 0; }
|
||||
virtual V3Hash sameHash() const {
|
||||
return V3Hash(V3Hash::Illegal()); // Not a node that supports it
|
||||
}
|
||||
virtual bool same(const AstNode*) const { return true; }
|
||||
// Iff has a data type; dtype() must be non null
|
||||
virtual bool hasDType() const { return false; }
|
||||
@@ -1912,15 +1870,17 @@ inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
|
||||
|
||||
class AstNodeMath VL_NOT_FINAL : public AstNode {
|
||||
// Math -- anything that's part of an expression tree
|
||||
public:
|
||||
protected:
|
||||
AstNodeMath(AstType t, FileLine* fl)
|
||||
: AstNode{t, fl} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeMath)
|
||||
// METHODS
|
||||
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
|
||||
@@ -1932,9 +1892,11 @@ public:
|
||||
|
||||
class AstNodeTermop VL_NOT_FINAL : public AstNodeMath {
|
||||
// Terminal operator -- a operator with no "inputs"
|
||||
public:
|
||||
protected:
|
||||
AstNodeTermop(AstType t, FileLine* fl)
|
||||
: AstNodeMath{t, fl} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeTermop)
|
||||
// Know no children, and hot function, so skip iterator for speed
|
||||
// See checkTreeIter also that asserts no children
|
||||
@@ -1945,12 +1907,14 @@ public:
|
||||
|
||||
class AstNodeUniop VL_NOT_FINAL : public AstNodeMath {
|
||||
// Unary math
|
||||
public:
|
||||
protected:
|
||||
AstNodeUniop(AstType t, FileLine* fl, AstNode* lhsp)
|
||||
: AstNodeMath{t, fl} {
|
||||
dtypeFrom(lhsp);
|
||||
setOp1p(lhsp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeUniop)
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
void lhsp(AstNode* nodep) { return setOp1p(nodep); }
|
||||
@@ -1965,18 +1929,19 @@ public:
|
||||
virtual bool signedFlavor() const { return false; }
|
||||
virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors?
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeBiop VL_NOT_FINAL : public AstNodeMath {
|
||||
// Binary math
|
||||
public:
|
||||
protected:
|
||||
AstNodeBiop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
|
||||
: AstNodeMath{t, fl} {
|
||||
setOp1p(lhs);
|
||||
setOp2p(rhs);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeBiop)
|
||||
// Clone single node, just get same type back.
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) = 0;
|
||||
@@ -1997,19 +1962,20 @@ public:
|
||||
virtual bool signedFlavor() const { return false; }
|
||||
virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors?
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeTriop VL_NOT_FINAL : public AstNodeMath {
|
||||
// Trinary math
|
||||
public:
|
||||
protected:
|
||||
AstNodeTriop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths)
|
||||
: AstNodeMath{t, fl} {
|
||||
setOp1p(lhs);
|
||||
setOp2p(rhs);
|
||||
setOp3p(ths);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeTriop)
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
@@ -2030,13 +1996,12 @@ public:
|
||||
virtual bool sizeMattersRhs() const = 0; // True if output result depends on rhs size
|
||||
virtual bool sizeMattersThs() const = 0; // True if output result depends on ths size
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeQuadop VL_NOT_FINAL : public AstNodeMath {
|
||||
// Quaternary math
|
||||
public:
|
||||
protected:
|
||||
AstNodeQuadop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths, AstNode* fhs)
|
||||
: AstNodeMath{t, fl} {
|
||||
setOp1p(lhs);
|
||||
@@ -2044,6 +2009,8 @@ public:
|
||||
setOp3p(ths);
|
||||
setOp4p(fhs);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeQuadop)
|
||||
AstNode* lhsp() const { return op1p(); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
@@ -2067,27 +2034,31 @@ public:
|
||||
virtual bool sizeMattersThs() const = 0; // True if output result depends on ths size
|
||||
virtual bool sizeMattersFhs() const = 0; // True if output result depends on ths size
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeBiCom VL_NOT_FINAL : public AstNodeBiop {
|
||||
// Binary math with commutative properties
|
||||
public:
|
||||
protected:
|
||||
AstNodeBiCom(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
|
||||
: AstNodeBiop{t, fl, lhs, rhs} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeBiCom)
|
||||
};
|
||||
|
||||
class AstNodeBiComAsv VL_NOT_FINAL : public AstNodeBiCom {
|
||||
// Binary math with commutative & associative properties
|
||||
public:
|
||||
protected:
|
||||
AstNodeBiComAsv(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
|
||||
: AstNodeBiCom{t, fl, lhs, rhs} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeBiComAsv)
|
||||
};
|
||||
|
||||
class AstNodeCond VL_NOT_FINAL : public AstNodeTriop {
|
||||
public:
|
||||
protected:
|
||||
AstNodeCond(AstType t, FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p)
|
||||
: AstNodeTriop{t, fl, condp, expr1p, expr2p} {
|
||||
if (expr1p) {
|
||||
@@ -2096,6 +2067,8 @@ public:
|
||||
dtypeFrom(expr2p);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeCond)
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
|
||||
const V3Number& ths) override;
|
||||
@@ -2124,13 +2097,15 @@ class AstNodeBlock VL_NOT_FINAL : public AstNode {
|
||||
private:
|
||||
string m_name; // Name of block
|
||||
bool m_unnamed; // Originally unnamed (name change does not affect this)
|
||||
public:
|
||||
protected:
|
||||
AstNodeBlock(AstType t, FileLine* fl, const string& name, AstNode* stmtsp)
|
||||
: AstNode{t, fl}
|
||||
, m_name{name} {
|
||||
addNOp1p(stmtsp);
|
||||
m_unnamed = (name == "");
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeBlock)
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual string name() const override { return m_name; } // * = Block name
|
||||
@@ -2143,13 +2118,15 @@ public:
|
||||
|
||||
class AstNodePreSel VL_NOT_FINAL : public AstNode {
|
||||
// Something that becomes an AstSel
|
||||
public:
|
||||
protected:
|
||||
AstNodePreSel(AstType t, FileLine* fl, AstNode* fromp, AstNode* rhs, AstNode* ths)
|
||||
: AstNode{t, fl} {
|
||||
setOp1p(fromp);
|
||||
setOp2p(rhs);
|
||||
setNOp3p(ths);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodePreSel)
|
||||
AstNode* fromp() const { return op1p(); }
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
@@ -2160,17 +2137,18 @@ public:
|
||||
void thsp(AstNode* nodep) { return setOp3p(nodep); }
|
||||
void attrp(AstAttrOf* nodep) { return setOp4p((AstNode*)nodep); }
|
||||
// METHODS
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeProcedure VL_NOT_FINAL : public AstNode {
|
||||
// IEEE procedure: initial, final, always
|
||||
public:
|
||||
protected:
|
||||
AstNodeProcedure(AstType t, FileLine* fl, AstNode* bodysp)
|
||||
: AstNode{t, fl} {
|
||||
addNOp2p(bodysp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeProcedure)
|
||||
// METHODS
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
@@ -2182,10 +2160,12 @@ public:
|
||||
class AstNodeStmt VL_NOT_FINAL : public AstNode {
|
||||
// Statement -- anything that's directly under a function
|
||||
bool m_statement; // Really a statement (e.g. not a function with return)
|
||||
public:
|
||||
protected:
|
||||
AstNodeStmt(AstType t, FileLine* fl, bool statement = true)
|
||||
: AstNode{t, fl}
|
||||
, m_statement{statement} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeStmt)
|
||||
// METHODS
|
||||
bool isStatement() const { return m_statement; } // Really a statement
|
||||
@@ -2198,13 +2178,15 @@ public:
|
||||
};
|
||||
|
||||
class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt {
|
||||
public:
|
||||
protected:
|
||||
AstNodeAssign(AstType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
||||
: AstNodeStmt{t, fl} {
|
||||
setOp1p(rhsp);
|
||||
setOp2p(lhsp);
|
||||
dtypeFrom(lhsp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeAssign)
|
||||
// Clone single node, just get same type back.
|
||||
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) = 0;
|
||||
@@ -2216,14 +2198,13 @@ public:
|
||||
virtual bool hasDType() const override { return true; }
|
||||
virtual bool cleanRhs() const { return true; }
|
||||
virtual int instrCount() const override { return widthInstrs(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode*) const override { return true; }
|
||||
virtual string verilogKwd() const override { return "="; }
|
||||
virtual bool brokeLhsMustBeLvalue() const = 0;
|
||||
};
|
||||
|
||||
class AstNodeFor VL_NOT_FINAL : public AstNodeStmt {
|
||||
public:
|
||||
protected:
|
||||
AstNodeFor(AstType t, FileLine* fl, AstNode* initsp, AstNode* condp, AstNode* incsp,
|
||||
AstNode* bodysp)
|
||||
: AstNodeStmt{t, fl} {
|
||||
@@ -2232,6 +2213,8 @@ public:
|
||||
addNOp3p(incsp);
|
||||
addNOp4p(bodysp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeFor)
|
||||
AstNode* initsp() const { return op1p(); } // op1 = initial statements
|
||||
AstNode* condp() const { return op2p(); } // op2 = condition to continue
|
||||
@@ -2239,20 +2222,23 @@ public:
|
||||
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
|
||||
class AstNodeIf VL_NOT_FINAL : public AstNodeStmt {
|
||||
private:
|
||||
VBranchPred m_branchPred; // Branch prediction as taken/untaken?
|
||||
public:
|
||||
bool m_isBoundsCheck; // True if this if node was inserted for array bounds checking
|
||||
protected:
|
||||
AstNodeIf(AstType t, FileLine* fl, AstNode* condp, AstNode* ifsp, AstNode* elsesp)
|
||||
: AstNodeStmt{t, fl} {
|
||||
setOp1p(condp);
|
||||
addNOp2p(ifsp);
|
||||
addNOp3p(elsesp);
|
||||
isBoundsCheck(false);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeIf)
|
||||
AstNode* condp() const { return op1p(); } // op1 = condition
|
||||
AstNode* ifsp() const { return op2p(); } // op2 = list of true statements
|
||||
@@ -2263,19 +2249,22 @@ public:
|
||||
virtual bool isGateOptimizable() const override { return false; }
|
||||
virtual bool isGateDedupable() const override { return true; }
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
void branchPred(VBranchPred flag) { m_branchPred = flag; }
|
||||
VBranchPred branchPred() const { return m_branchPred; }
|
||||
void isBoundsCheck(bool flag) { m_isBoundsCheck = flag; }
|
||||
bool isBoundsCheck() const { return m_isBoundsCheck; }
|
||||
};
|
||||
|
||||
class AstNodeCase VL_NOT_FINAL : public AstNodeStmt {
|
||||
public:
|
||||
protected:
|
||||
AstNodeCase(AstType t, FileLine* fl, AstNode* exprp, AstNode* casesp)
|
||||
: AstNodeStmt{t, fl} {
|
||||
setOp1p(exprp);
|
||||
addNOp2p(casesp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeCase)
|
||||
virtual int instrCount() const override { return instrCountBranch(); }
|
||||
AstNode* exprp() const { return op1p(); } // op1 = case condition <expression>
|
||||
@@ -2295,11 +2284,9 @@ 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')
|
||||
|
||||
public:
|
||||
protected:
|
||||
AstNodeVarRef(AstType t, FileLine* fl, const string& name, const VAccess& access)
|
||||
: AstNodeMath{t, fl}
|
||||
, m_access{access}
|
||||
@@ -2313,6 +2300,8 @@ public:
|
||||
// May have varp==nullptr
|
||||
this->varp(varp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeVarRef)
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual bool hasDType() const override { return true; }
|
||||
@@ -2327,13 +2316,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
|
||||
@@ -2346,15 +2331,16 @@ class AstNodeText VL_NOT_FINAL : public AstNode {
|
||||
private:
|
||||
string m_text;
|
||||
|
||||
public:
|
||||
protected:
|
||||
// Node that simply puts text into the output stream
|
||||
AstNodeText(AstType t, FileLine* fl, const string& textp)
|
||||
: AstNode{t, fl} {
|
||||
m_text = textp; // Copy it
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeText)
|
||||
virtual void dump(std::ostream& str = std::cout) const override;
|
||||
virtual V3Hash sameHash() const override { return V3Hash(text()); }
|
||||
virtual bool same(const AstNode* samep) const override {
|
||||
const AstNodeText* asamep = static_cast<const AstNodeText*>(samep);
|
||||
return text() == asamep->text();
|
||||
@@ -2375,7 +2361,7 @@ private:
|
||||
// Unique number assigned to each dtype during creation for IEEE matching
|
||||
static int s_uniqueNum;
|
||||
|
||||
public:
|
||||
protected:
|
||||
// CONSTRUCTORS
|
||||
AstNodeDType(AstType t, FileLine* fl)
|
||||
: AstNode{t, fl} {
|
||||
@@ -2383,6 +2369,8 @@ public:
|
||||
m_widthMin = 0;
|
||||
m_generic = false;
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeDType)
|
||||
// ACCESSORS
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
@@ -2473,16 +2461,21 @@ private:
|
||||
bool m_packed;
|
||||
bool m_isFourstate;
|
||||
MemberNameMap m_members;
|
||||
const int m_uniqueNum;
|
||||
|
||||
public:
|
||||
protected:
|
||||
AstNodeUOrStructDType(AstType t, FileLine* fl, VSigning numericUnpack)
|
||||
: AstNodeDType{t, fl} {
|
||||
: AstNodeDType{t, fl}
|
||||
, m_uniqueNum{uniqueNumInc()} {
|
||||
// VSigning::NOSIGN overloaded to indicate not packed
|
||||
m_packed = (numericUnpack != VSigning::NOSIGN);
|
||||
m_isFourstate = false; // V3Width computes
|
||||
numeric(VSigning::fromBool(numericUnpack.isSigned()));
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeUOrStructDType)
|
||||
int uniqueNum() const { return m_uniqueNum; }
|
||||
virtual const char* broken() const override;
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual bool isCompound() const override { return false; } // Because don't support unpacked
|
||||
@@ -2534,9 +2527,11 @@ class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
|
||||
private:
|
||||
AstNodeDType* m_refDTypep = nullptr; // Elements of this type (after widthing)
|
||||
AstNode* rangenp() const { return op2p(); } // op2 = Array(s) of variable
|
||||
public:
|
||||
protected:
|
||||
AstNodeArrayDType(AstType t, FileLine* fl)
|
||||
: AstNodeDType{t, fl} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeArrayDType)
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual void dumpSmall(std::ostream& str) const override;
|
||||
@@ -2559,9 +2554,6 @@ public:
|
||||
&& rangenp()->sameTree(asamep->rangenp())
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
|
||||
}
|
||||
virtual V3Hash sameHash() const override {
|
||||
return V3Hash(V3Hash(m_refDTypep), V3Hash(hi()), V3Hash(lo()));
|
||||
}
|
||||
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
|
||||
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||
@@ -2594,9 +2586,11 @@ public:
|
||||
|
||||
class AstNodeSel VL_NOT_FINAL : public AstNodeBiop {
|
||||
// Single bit range extraction, perhaps with non-constant selection or array selection
|
||||
public:
|
||||
protected:
|
||||
AstNodeSel(AstType t, FileLine* fl, AstNode* fromp, AstNode* bitp)
|
||||
: AstNodeBiop{t, fl, fromp, bitp} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeSel)
|
||||
AstNode* fromp() const {
|
||||
return op1p();
|
||||
@@ -2610,11 +2604,13 @@ public:
|
||||
|
||||
class AstNodeStream VL_NOT_FINAL : public AstNodeBiop {
|
||||
// Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
|
||||
public:
|
||||
protected:
|
||||
AstNodeStream(AstType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
||||
: AstNodeBiop{t, fl, lhsp, rhsp} {
|
||||
if (lhsp->dtypep()) dtypeSetLogicSized(lhsp->dtypep()->width(), VSigning::UNSIGNED);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeStream)
|
||||
};
|
||||
|
||||
@@ -2625,32 +2621,22 @@ 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_selfPointer; // Output code object pointer (e.g.: 'this')
|
||||
string m_argTypes;
|
||||
|
||||
public:
|
||||
protected:
|
||||
AstNodeCCall(AstType t, FileLine* fl, AstCFunc* funcp, AstNode* argsp = nullptr)
|
||||
: AstNodeStmt{t, fl, true}
|
||||
, m_funcp{funcp} {
|
||||
addNOp2p(argsp);
|
||||
}
|
||||
// Replacement form for V3Combine
|
||||
// Note this removes old attachments from the oldp
|
||||
AstNodeCCall(AstType t, AstNodeCCall* oldp, AstCFunc* funcp)
|
||||
: AstNodeStmt{t, oldp->fileline(), true}
|
||||
, m_funcp{funcp} {
|
||||
m_hiernameToProt = oldp->hiernameToProt();
|
||||
m_hiernameToUnprot = oldp->hiernameToUnprot();
|
||||
m_argTypes = oldp->argTypes();
|
||||
if (oldp->argsp()) addNOp2p(oldp->argsp()->unlinkFrBackWithNext());
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeCCall)
|
||||
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 V3Hash sameHash() const override { return V3Hash(funcp()); }
|
||||
virtual bool same(const AstNode* samep) const override {
|
||||
const AstNodeCCall* asamep = static_cast<const AstNodeCCall*>(samep);
|
||||
return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes());
|
||||
@@ -2661,11 +2647,9 @@ 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;
|
||||
string selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const string& value) { m_selfPointer = value; }
|
||||
string selfPointerProtect(bool useSelfForThis) const;
|
||||
void argTypes(const string& str) { m_argTypes = str; }
|
||||
string argTypes() const { return m_argTypes; }
|
||||
// op1p reserved for AstCMethodCall
|
||||
@@ -2696,7 +2680,7 @@ private:
|
||||
bool m_pureVirtual : 1; // Pure virtual
|
||||
bool m_virtual : 1; // Virtual method in class
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
public:
|
||||
protected:
|
||||
AstNodeFTask(AstType t, FileLine* fl, const string& name, AstNode* stmtsp)
|
||||
: AstNode{t, fl}
|
||||
, m_name{name}
|
||||
@@ -2720,6 +2704,8 @@ public:
|
||||
addNOp3p(stmtsp);
|
||||
cname(name); // Might be overridden by dpi import/export
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeFTask)
|
||||
virtual void dump(std::ostream& str = std::cout) const override;
|
||||
virtual string name() const override { return m_name; } // * = Var name
|
||||
@@ -2796,7 +2782,7 @@ private:
|
||||
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
|
||||
string m_inlinedDots; // Dotted hierarchy flattened out
|
||||
bool m_pli = false; // Pli system call ($name)
|
||||
public:
|
||||
protected:
|
||||
AstNodeFTaskRef(AstType t, FileLine* fl, bool statement, AstNode* namep, AstNode* pinsp)
|
||||
: AstNodeStmt{t, fl, statement} {
|
||||
setOp1p(namep);
|
||||
@@ -2807,6 +2793,8 @@ public:
|
||||
, m_name{name} {
|
||||
addNOp3p(pinsp);
|
||||
}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeFTaskRef)
|
||||
virtual const char* broken() const override;
|
||||
virtual void cloneRelink() override {
|
||||
@@ -2862,7 +2850,7 @@ private:
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
VTimescale m_timeunit; // Global time unit
|
||||
VOptionBool m_unconnectedDrive; // State of `unconnected_drive
|
||||
public:
|
||||
protected:
|
||||
AstNodeModule(AstType t, FileLine* fl, const string& name)
|
||||
: AstNode{t, fl}
|
||||
, m_name{name}
|
||||
@@ -2875,6 +2863,8 @@ public:
|
||||
, m_internal{false}
|
||||
, m_recursive{false}
|
||||
, m_recursiveClone{false} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeModule)
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
virtual bool maybePointedTo() const override { return true; }
|
||||
@@ -2922,21 +2912,22 @@ public:
|
||||
|
||||
class AstNodeRange VL_NOT_FINAL : public AstNode {
|
||||
// A range, sized or unsized
|
||||
public:
|
||||
protected:
|
||||
AstNodeRange(AstType t, FileLine* fl)
|
||||
: AstNode{t, fl} {}
|
||||
|
||||
public:
|
||||
ASTNODE_BASE_FUNCS(NodeRange)
|
||||
virtual void dump(std::ostream& str) const override;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
#include "V3AstNodes__gen.h"
|
||||
|
||||
//######################################################################
|
||||
// Inline AstNVisitor METHODS
|
||||
|
||||
inline void AstNVisitor::iterate(AstNode* nodep) { nodep->accept(*this); }
|
||||
inline void AstNVisitor::iterateNull(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->accept(*this);
|
||||
}
|
||||
inline void AstNVisitor::iterateChildren(AstNode* nodep) { nodep->iterateChildren(*this); }
|
||||
inline void AstNVisitor::iterateChildrenBackwards(AstNode* nodep) {
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
@@ -2955,71 +2946,7 @@ inline AstNode* AstNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) {
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Inline ACCESSORS
|
||||
|
||||
inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
|
||||
inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
|
||||
inline bool AstNode::width1() const { // V3Const uses to know it can optimize
|
||||
return dtypep() && dtypep()->width() == 1;
|
||||
}
|
||||
inline int AstNode::widthInstrs() const {
|
||||
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
|
||||
}
|
||||
inline bool AstNode::isDouble() const {
|
||||
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_CAST(dtypep(), BasicDType)->isDouble();
|
||||
}
|
||||
inline bool AstNode::isString() const {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
|
||||
}
|
||||
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
inline bool AstNode::isZero() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqZero());
|
||||
}
|
||||
inline bool AstNode::isNeqZero() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isNeqZero());
|
||||
}
|
||||
inline bool AstNode::isOne() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqOne());
|
||||
}
|
||||
inline bool AstNode::isAllOnes() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnes());
|
||||
}
|
||||
inline bool AstNode::isAllOnesV() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnesV());
|
||||
}
|
||||
inline bool AstNode::sameTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, false);
|
||||
}
|
||||
inline bool AstNode::sameGateTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, true);
|
||||
}
|
||||
|
||||
inline void AstNodeVarRef::varp(AstVar* varp) {
|
||||
m_varp = varp;
|
||||
dtypeFrom(varp);
|
||||
}
|
||||
|
||||
inline bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
|
||||
|
||||
inline void AstNodeArrayDType::rangep(AstRange* nodep) { setOp2p(nodep); }
|
||||
inline int AstNodeArrayDType::left() const { return rangep()->leftConst(); }
|
||||
inline int AstNodeArrayDType::right() const { return rangep()->rightConst(); }
|
||||
inline int AstNodeArrayDType::hi() const { return rangep()->hiConst(); }
|
||||
inline int AstNodeArrayDType::lo() const { return rangep()->loConst(); }
|
||||
inline int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
|
||||
inline VNumRange AstNodeArrayDType::declRange() const { return VNumRange{left(), right()}; }
|
||||
|
||||
inline const char* AstNodeFTaskRef::broken() const {
|
||||
BROKEN_RTN(m_taskp && !m_taskp->brokeExists());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void AstIfaceRefDType::cloneRelink() {
|
||||
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep();
|
||||
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep();
|
||||
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep();
|
||||
}
|
||||
#include "V3AstNodes.h"
|
||||
|
||||
#endif // Guard
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Ast node inline functions
|
||||
//
|
||||
// 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_V3ASTINLINES_H_
|
||||
#define VERILATOR_V3ASTINLINES_H_
|
||||
|
||||
#ifndef VERILATOR_V3ASTNODES_H_
|
||||
#error "Use V3Ast.h as the include"
|
||||
#include "V3AstNodes.h" // This helps code analysis tools pick up symbols in V3Ast.h and V3AstNodes.h
|
||||
#endif
|
||||
|
||||
//######################################################################
|
||||
// Inline ACCESSORS
|
||||
|
||||
inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
|
||||
inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
|
||||
inline bool AstNode::width1() const { // V3Const uses to know it can optimize
|
||||
return dtypep() && dtypep()->width() == 1;
|
||||
}
|
||||
inline int AstNode::widthInstrs() const {
|
||||
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
|
||||
}
|
||||
inline bool AstNode::isDouble() const {
|
||||
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_CAST(dtypep(), BasicDType)->isDouble();
|
||||
}
|
||||
inline bool AstNode::isString() const {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
|
||||
}
|
||||
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
inline bool AstNode::isZero() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqZero());
|
||||
}
|
||||
inline bool AstNode::isNeqZero() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isNeqZero());
|
||||
}
|
||||
inline bool AstNode::isOne() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqOne());
|
||||
}
|
||||
inline bool AstNode::isAllOnes() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnes());
|
||||
}
|
||||
inline bool AstNode::isAllOnesV() const {
|
||||
return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnesV());
|
||||
}
|
||||
inline bool AstNode::sameTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, false);
|
||||
}
|
||||
inline bool AstNode::sameGateTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, true);
|
||||
}
|
||||
|
||||
inline void AstNodeVarRef::varp(AstVar* varp) {
|
||||
m_varp = varp;
|
||||
dtypeFrom(varp);
|
||||
}
|
||||
|
||||
inline bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
|
||||
|
||||
inline void AstNodeArrayDType::rangep(AstRange* nodep) { setOp2p(nodep); }
|
||||
inline int AstNodeArrayDType::left() const { return rangep()->leftConst(); }
|
||||
inline int AstNodeArrayDType::right() const { return rangep()->rightConst(); }
|
||||
inline int AstNodeArrayDType::hi() const { return rangep()->hiConst(); }
|
||||
inline int AstNodeArrayDType::lo() const { return rangep()->loConst(); }
|
||||
inline int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
|
||||
inline VNumRange AstNodeArrayDType::declRange() const { return VNumRange{left(), right()}; }
|
||||
|
||||
inline const char* AstNodeFTaskRef::broken() const {
|
||||
BROKEN_RTN(m_taskp && !m_taskp->brokeExists());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void AstIfaceRefDType::cloneRelink() {
|
||||
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep();
|
||||
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep();
|
||||
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep();
|
||||
}
|
||||
|
||||
#endif // Guard
|
||||
+181
-39
@@ -18,14 +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>
|
||||
|
||||
//======================================================================
|
||||
@@ -53,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 {
|
||||
@@ -106,8 +121,10 @@ 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 AstNodeCCall::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,
|
||||
@@ -214,9 +231,9 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
}
|
||||
|
||||
AstExecGraph::AstExecGraph(FileLine* fileline)
|
||||
: AstNode{AstType::atExecGraph, 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) {
|
||||
@@ -258,8 +275,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;
|
||||
@@ -312,13 +337,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 ";
|
||||
|
||||
@@ -333,7 +360,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) {
|
||||
@@ -608,13 +635,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
|
||||
@@ -622,15 +642,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);
|
||||
}
|
||||
|
||||
@@ -670,10 +697,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) {
|
||||
@@ -862,6 +891,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
|
||||
@@ -956,6 +990,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 uint32_t hash = V3Hasher::uncachedHash(initp).value();
|
||||
const auto& er = m_tables.equal_range(hash);
|
||||
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 += cvtToHex(hash);
|
||||
name += "_";
|
||||
name += cvtToStr(std::distance(er.first, er.second));
|
||||
AstVarScope* const varScopep = createNewEntry(name, initp);
|
||||
m_tables.emplace(hash, 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 uint32_t hash = initp->num().toHash().value();
|
||||
const auto& er = m_consts.equal_range(hash);
|
||||
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 += cvtToHex(hash);
|
||||
name += "_";
|
||||
name += cvtToStr(std::distance(er.first, er.second));
|
||||
AstVarScope* const varScopep = createNewEntry(name, initp);
|
||||
m_consts.emplace(hash, varScopep);
|
||||
return varScopep;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Special walking tree inserters
|
||||
|
||||
@@ -1117,8 +1260,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());
|
||||
@@ -1314,10 +1458,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;
|
||||
}
|
||||
@@ -1362,7 +1506,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 {
|
||||
@@ -1677,14 +1822,11 @@ 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 (isConstructor()) str << " [CTOR]";
|
||||
if (isDestructor()) str << " [DTOR]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
|
||||
+543
-629
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,119 @@
|
||||
// -*- 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(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;
|
||||
}
|
||||
};
|
||||
|
||||
// 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
|
||||
+1
-1
@@ -117,7 +117,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);
|
||||
|
||||
+5
-5
@@ -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) {
|
||||
|
||||
+2
-2
@@ -72,7 +72,7 @@ public:
|
||||
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
|
||||
const int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
|
||||
s_nodes.emplace(nodep, flags);
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
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);
|
||||
const int or_flags = FLAG_IN_TREE | (linkable ? FLAG_LINKABLE : 0);
|
||||
if (iter == s_nodes.end()) {
|
||||
s_nodes.emplace(nodep, or_flags);
|
||||
} else {
|
||||
|
||||
+93
-64
@@ -32,63 +32,96 @@
|
||||
#include "V3CCtors.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
class VCtorType final {
|
||||
public:
|
||||
enum en : uint8_t { MODULE, CLASS, COVERAGE };
|
||||
|
||||
class V3CCtorsVisitor final {
|
||||
private:
|
||||
string m_basename;
|
||||
string m_argsp;
|
||||
string m_callargsp;
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_tlFuncp; // Top level function being built
|
||||
AstCFunc* m_funcp; // Current function
|
||||
enum en m_e;
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline VCtorType(en _e)
|
||||
: m_e{_e} {}
|
||||
bool isClass() const { return m_e == CLASS; }
|
||||
bool isCoverage() const { return m_e == COVERAGE; }
|
||||
};
|
||||
|
||||
class V3CCtorsBuilder final {
|
||||
private:
|
||||
AstNodeModule* const m_modp; // Current module/class
|
||||
const string m_basename;
|
||||
const VCtorType m_type; // What kind of constructor are we creating
|
||||
std::list<AstCFunc*> m_newFunctions; // Created functions, latest is at back
|
||||
int m_numStmts = 0; // Number of statements output
|
||||
int m_funcNum = 0; // Function number being built
|
||||
|
||||
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(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) {} // Prevent unused\n";
|
||||
} else if (m_type.isCoverage()) {
|
||||
funcp->argTypes("bool first");
|
||||
preventUnusedStmt = "if (false && first) {} // Prevent unused\n";
|
||||
}
|
||||
if (!preventUnusedStmt.empty()) {
|
||||
funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt));
|
||||
}
|
||||
m_modp->addStmtp(funcp);
|
||||
m_numStmts = 0;
|
||||
return funcp;
|
||||
}
|
||||
|
||||
public:
|
||||
void add(AstNode* nodep) {
|
||||
if (v3Global.opt.outputSplitCFuncs() && v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
m_funcp = nullptr;
|
||||
if (v3Global.opt.outputSplitCFuncs() && m_numStmts > v3Global.opt.outputSplitCFuncs()) {
|
||||
m_newFunctions.push_back(makeNewFunc());
|
||||
}
|
||||
if (!m_funcp) {
|
||||
m_funcp = new AstCFunc(m_modp->fileline(), m_basename + "_" + cvtToStr(++m_funcNum),
|
||||
nullptr, "void");
|
||||
m_funcp->isStatic(false);
|
||||
m_funcp->declPrivate(true);
|
||||
m_funcp->slow(!VN_IS(m_modp, Class)); // Only classes construct on fast path
|
||||
m_funcp->argTypes(m_argsp);
|
||||
m_modp->addStmtp(m_funcp);
|
||||
|
||||
// Add a top call to it
|
||||
AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp);
|
||||
callp->argTypes(m_callargsp);
|
||||
|
||||
m_tlFuncp->addStmtsp(callp);
|
||||
m_numStmts = 0;
|
||||
}
|
||||
m_funcp->addStmtsp(nodep);
|
||||
m_newFunctions.back()->addStmtsp(nodep);
|
||||
m_numStmts += 1;
|
||||
}
|
||||
|
||||
V3CCtorsVisitor(AstNodeModule* nodep, const string& basename, const string& argsp = "",
|
||||
const string& callargsp = "", const string& stmt = "") {
|
||||
m_basename = basename;
|
||||
m_argsp = argsp;
|
||||
m_callargsp = callargsp;
|
||||
m_modp = nodep;
|
||||
m_tlFuncp = new AstCFunc(nodep->fileline(), basename, nullptr, "void");
|
||||
m_tlFuncp->declPrivate(true);
|
||||
m_tlFuncp->isStatic(false);
|
||||
m_tlFuncp->slow(!VN_IS(m_modp, Class)); // Only classes construct on fast path
|
||||
m_tlFuncp->argTypes(m_argsp);
|
||||
if (stmt != "") m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||
m_funcp = m_tlFuncp;
|
||||
m_modp->addStmtp(m_tlFuncp);
|
||||
V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type)
|
||||
: m_modp(nodep)
|
||||
, m_basename{basename}
|
||||
, m_type(type) {
|
||||
// Note: The constructor is always called, even if empty, so we must always create at least
|
||||
// one.
|
||||
m_newFunctions.push_back(makeNewFunc());
|
||||
}
|
||||
~V3CCtorsVisitor() = default;
|
||||
|
||||
~V3CCtorsBuilder() {
|
||||
if (m_newFunctions.size() == 1) {
|
||||
// No split was necessary, rename the one function to the basename
|
||||
m_newFunctions.front()->name(m_basename);
|
||||
} else {
|
||||
// Split was necessary, create root function and call all others from that
|
||||
AstCFunc* const rootFuncp = makeNewFunc();
|
||||
rootFuncp->name(m_basename);
|
||||
for (AstCFunc* const funcp : m_newFunctions) {
|
||||
AstCCall* const callp = new AstCCall(m_modp->fileline(), funcp);
|
||||
if (m_type.isClass()) {
|
||||
callp->argTypes("vlSymsp");
|
||||
} else {
|
||||
if (m_type.isCoverage()) callp->argTypes("first");
|
||||
callp->selfPointer("this");
|
||||
}
|
||||
rootFuncp->addStmtsp(callp);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(V3CCtorsVisitor);
|
||||
VL_UNCOPYABLE(V3CCtorsBuilder);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -98,6 +131,7 @@ void V3CCtors::evalAsserts() {
|
||||
AstCFunc* 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);
|
||||
@@ -105,11 +139,14 @@ void V3CCtors::evalAsserts() {
|
||||
if (AstVar* 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(
|
||||
varp->fileline(), newp,
|
||||
@@ -140,32 +177,24 @@ void V3CCtors::cctorsAll() {
|
||||
modp = VN_CAST(modp->nextp(), NodeModule)) {
|
||||
// Process each module in turn
|
||||
{
|
||||
V3CCtorsVisitor var_reset(
|
||||
modp, "_ctor_var_reset",
|
||||
(VN_IS(modp, Class) ? EmitCBaseVisitor::symClassVar() : ""),
|
||||
(VN_IS(modp, Class) ? "vlSymsp" : ""),
|
||||
(VN_IS(modp, Class) ? "if (false && vlSymsp) {} // Prevent unused\n" : ""));
|
||||
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* varp = VN_CAST(np, Var)) {
|
||||
if (AstVar* const varp = VN_CAST(np, Var)) {
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) {
|
||||
var_reset.add(
|
||||
new AstCReset(varp->fileline(),
|
||||
new AstVarRef(varp->fileline(), varp, VAccess::WRITE)));
|
||||
const auto vrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE);
|
||||
var_reset.add(new AstCReset(varp->fileline(), vrefp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.coverage()) {
|
||||
V3CCtorsVisitor configure_coverage(
|
||||
modp, "_configure_coverage", EmitCBaseVisitor::symClassVar() + ", bool first",
|
||||
"vlSymsp, first", "if (false && vlSymsp && first) {} // Prevent unused\n");
|
||||
V3CCtorsBuilder configure_coverage(modp, "_configure_coverage", VCtorType::COVERAGE);
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) {
|
||||
AstNode* backp = coverp->backp();
|
||||
coverp->unlinkFrBack();
|
||||
configure_coverage.add(coverp);
|
||||
np = backp;
|
||||
if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) {
|
||||
np = coverp->backp();
|
||||
configure_coverage.add(coverp->unlinkFrBack());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -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:";
|
||||
@@ -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());
|
||||
@@ -506,7 +506,8 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt";
|
||||
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;
|
||||
|
||||
+4
-5
@@ -61,14 +61,13 @@ public:
|
||||
m_chgFuncp
|
||||
= new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
|
||||
m_scopetopp, "QData");
|
||||
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_chgFuncp->symProlog(true);
|
||||
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");
|
||||
|
||||
if (!m_tlChgFuncp->stmtsp()) {
|
||||
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
|
||||
@@ -251,8 +250,8 @@ private:
|
||||
// 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->isStatic(false);
|
||||
m_statep->m_tlChgFuncp->isLoose(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
|
||||
|
||||
+5
-4
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
+51
-73
@@ -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,55 +250,10 @@ 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);
|
||||
@@ -301,34 +294,20 @@ private:
|
||||
}
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
virtual void visit(AstAlwaysPost* nodep) override {
|
||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
nodep->addNextHere(stmtsp);
|
||||
}
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
virtual void visit(AstAlwaysPostponed* nodep) override {
|
||||
if (AstNode* stmtsp = nodep->bodysp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
nodep->addNextHere(stmtsp);
|
||||
}
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) override {
|
||||
// nodep->dumpTree(cout, "ct:");
|
||||
// COVERTOGGLE(INC, ORIG, CHANGE) ->
|
||||
// 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);
|
||||
}
|
||||
@@ -338,7 +317,6 @@ private:
|
||||
if (nodep->formCallTree()) {
|
||||
UINFO(4, " formCallTree " << nodep << endl);
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
|
||||
callp->argTypes("vlSymsp");
|
||||
m_finalFuncp->addStmtsp(callp);
|
||||
}
|
||||
}
|
||||
|
||||
+93
-347
@@ -15,19 +15,8 @@
|
||||
//*************************************************************************
|
||||
// V3Combine's Transformations:
|
||||
//
|
||||
// For every function that we spit out
|
||||
// Examine code to find largest common blocks
|
||||
// Hash each node depth first
|
||||
// Hash includes varp name and operator type, and constants
|
||||
// Form lookup table based on hash of each statement w/ nodep and next nodep
|
||||
// GO through table
|
||||
// Lookup in hash, while next of each statement match, grow that common block
|
||||
// Foreach common block
|
||||
// If common block large enough (> 20 statements) & used 2x or more
|
||||
// Make new function
|
||||
// Move common block to function
|
||||
// Replace each common block ref with funccall
|
||||
//
|
||||
// Combine identical CFuncs by retaining only a single copy
|
||||
// Also drop empty CFuncs
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
@@ -35,7 +24,7 @@
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Combine.h"
|
||||
#include "V3Hashed.h"
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
@@ -45,12 +34,6 @@
|
||||
|
||||
//######################################################################
|
||||
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
constexpr int COMBINE_MIN_STATEMENTS = 50; // Min # of statements to be worth making a function
|
||||
#endif
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CombBaseVisitor VL_NOT_FINAL : public AstNVisitor {
|
||||
protected:
|
||||
// STATE
|
||||
@@ -67,8 +50,7 @@ class CombCallVisitor final : CombBaseVisitor {
|
||||
// Find all CCALLS of each CFUNC, so that we can later rename them
|
||||
private:
|
||||
// NODE STATE
|
||||
using CallMmap = std::multimap<AstCFunc*, AstCCall*>;
|
||||
CallMmap m_callMmap; // Associative array of {function}{call}
|
||||
std::multimap<AstCFunc*, AstCCall*> m_callMmap; // Associative array of {function}{call}
|
||||
// METHODS
|
||||
public:
|
||||
void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
@@ -81,49 +63,45 @@ public:
|
||||
// Note: m_callMmap modified in loop, so not using equal_range.
|
||||
for (auto it = m_callMmap.find(oldfuncp); it != m_callMmap.end();
|
||||
it = m_callMmap.find(oldfuncp)) {
|
||||
AstCCall* callp = it->second;
|
||||
if (!callp->user3()) { // !already done
|
||||
UINFO(4, " Called " << callp << endl);
|
||||
UASSERT_OBJ(callp->funcp() == oldfuncp, callp,
|
||||
"Call list broken, points to call w/different func");
|
||||
if (newfuncp) {
|
||||
AstCCall* newp = new AstCCall(callp, newfuncp);
|
||||
// Special new AstCCall form above transfers children of callp to newfuncp
|
||||
callp->replaceWith(newp);
|
||||
addCall(newp); // Fix the table
|
||||
} else { // Just deleting empty function
|
||||
callp->unlinkFrBack();
|
||||
}
|
||||
callp->user3(true); // Dead now
|
||||
VL_DO_DANGLING(pushDeletep(callp), callp);
|
||||
AstCCall* const oldp = it->second;
|
||||
UINFO(4, " Called " << oldp << endl);
|
||||
UASSERT_OBJ(oldp->funcp() == oldfuncp, oldp,
|
||||
"Call list broken, points to call w/different func");
|
||||
if (newfuncp) {
|
||||
// Replace call to oldfuncp with call to newfuncp
|
||||
AstNode* const argsp
|
||||
= oldp->argsp() ? oldp->argsp()->unlinkFrBackWithNext() : nullptr;
|
||||
AstCCall* const newp = new AstCCall(oldp->fileline(), newfuncp, argsp);
|
||||
newp->selfPointer(oldp->selfPointer());
|
||||
newp->argTypes(oldp->argTypes());
|
||||
addCall(newp); // Fix the table, in case the newfuncp itself gets replaced
|
||||
oldp->replaceWith(newp);
|
||||
} else {
|
||||
// Just deleting empty function
|
||||
oldp->unlinkFrBack();
|
||||
}
|
||||
// It is safe to unconditionally remove this entry here as the above
|
||||
// 'if' would never be entered again for this entry (we set user3).
|
||||
// The only other place where m_callMmap is looked up is deleteCall
|
||||
// below, but that is only ever called straight after an addCall
|
||||
// of the node being deleted, so it won't miss this entry.
|
||||
m_callMmap.erase(it); // Fix the table
|
||||
VL_DO_DANGLING(pushDeletep(oldp), oldp);
|
||||
m_callMmap.erase(it); // Fix the table, This call has been replaced
|
||||
}
|
||||
}
|
||||
// METHODS
|
||||
void addCall(AstCCall* nodep) { m_callMmap.emplace(nodep->funcp(), nodep); }
|
||||
void deleteCall(AstCCall* nodep) {
|
||||
std::pair<CallMmap::iterator, CallMmap::iterator> eqrange
|
||||
= m_callMmap.equal_range(nodep->funcp());
|
||||
for (auto nextit = eqrange.first; nextit != eqrange.second;) {
|
||||
const auto eqit = nextit++;
|
||||
AstCCall* callp = eqit->second;
|
||||
if (callp == nodep) {
|
||||
m_callMmap.erase(eqit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
nodep->v3fatalSrc("deleteCall node not found in table");
|
||||
}
|
||||
|
||||
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 {}
|
||||
@@ -136,26 +114,6 @@ public:
|
||||
void main(AstNetlist* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Combine marking function
|
||||
|
||||
class CombMarkVisitor final : CombBaseVisitor {
|
||||
// Mark all nodes under specified one.
|
||||
private:
|
||||
// OUTPUT:
|
||||
// AstNode::user3() -> bool. True to indicate duplicated
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
nodep->user3(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CombMarkVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
virtual ~CombMarkVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Combine state, as a visitor of each AstNode
|
||||
|
||||
@@ -163,312 +121,100 @@ class CombineVisitor final : CombBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNodeStmt::user() -> bool. True if iterated already
|
||||
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
|
||||
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
|
||||
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is
|
||||
// illegal)
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser3InUse m_inuser3;
|
||||
// AstUser4InUse part of V3Hashed
|
||||
AstUser3InUse m_user3InUse; // Marks replaced AstCFuncs
|
||||
// AstUser4InUse part of V3Hasher in V3DupFinder
|
||||
|
||||
// STATE
|
||||
enum CombineState : uint8_t { STATE_IDLE, STATE_HASH, STATE_DUP };
|
||||
VDouble0 m_statCombs; // Statistic tracking
|
||||
CombineState m_state = STATE_IDLE; // Major state
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstCFunc* m_cfuncp = nullptr; // Current function
|
||||
VDouble0 m_cfuncsCombined; // Statistic tracking
|
||||
CombCallVisitor m_call; // Tracking of function call users
|
||||
int m_modNFuncs = 0; // Number of functions made
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
AstNode* m_walkLast1p = nullptr; // Final node that is the same in duplicate list
|
||||
#endif
|
||||
AstNode* m_walkLast2p = nullptr; // Final node that is the same in duplicate list
|
||||
V3Hashed m_hashed; // Hash for every node in module
|
||||
V3DupFinder m_dupFinder; // Duplicate finder for CFuncs in module
|
||||
|
||||
// METHODS
|
||||
void hashStatement(AstNode* nodep) {
|
||||
// Compute hash on entire tree of this statement
|
||||
m_hashed.hashAndInsert(nodep);
|
||||
// UINFO(9, " stmthash " << hex << nodep->user4() << " " << nodep << endl);
|
||||
}
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
void hashFunctions(AstCFunc* nodep) {
|
||||
// Compute hash of all statement trees in the function
|
||||
VL_RESTORER(m_state);
|
||||
{
|
||||
m_state = STATE_HASH;
|
||||
iterate(nodep);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
void walkEmptyFuncs() {
|
||||
for (const auto& itr : m_hashed) {
|
||||
AstNode* node1p = itr.second;
|
||||
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
|
||||
if (oldfuncp && oldfuncp->emptyBody() && !oldfuncp->dontCombine()) {
|
||||
UINFO(5, " EmptyFunc " << std::hex << V3Hash(oldfuncp->user4p()) << " "
|
||||
<< oldfuncp << endl);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
m_call.replaceFunc(oldfuncp, nullptr);
|
||||
oldfuncp->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
||||
}
|
||||
for (const auto& itr : m_dupFinder) {
|
||||
AstCFunc* const oldfuncp = VN_CAST(itr.second, CFunc);
|
||||
UASSERT_OBJ(oldfuncp, itr.second, "Not a CFunc in hash");
|
||||
if (!oldfuncp->emptyBody()) continue;
|
||||
UASSERT_OBJ(!oldfuncp->dontCombine(), oldfuncp,
|
||||
"dontCombine function should not be in hash");
|
||||
|
||||
// Remove calls to empty function
|
||||
UASSERT_OBJ(!oldfuncp->user3(), oldfuncp, "Should not be processed yet");
|
||||
UINFO(5, " Drop empty CFunc " << itr.first << " " << oldfuncp << endl);
|
||||
oldfuncp->user3SetOnce(); // Mark replaced
|
||||
m_call.replaceFunc(oldfuncp, nullptr);
|
||||
oldfuncp->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
||||
}
|
||||
}
|
||||
|
||||
void walkDupFuncs() {
|
||||
// Do non-slow first as then favors naming functions based on fast name
|
||||
for (int slow = 0; slow < 2; ++slow) {
|
||||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
AstNode* node1p = it->second;
|
||||
AstCFunc* cfunc1p = VN_CAST(node1p, CFunc);
|
||||
if (!cfunc1p) continue;
|
||||
// cppcheck-suppress compareBoolExpressionWithInt
|
||||
if (cfunc1p->slow() != slow) continue;
|
||||
V3Hash hashval = it->first;
|
||||
UASSERT_OBJ(!hashval.isIllegal(), node1p, "Illegal (unhashed) nodes");
|
||||
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (!(eqit->first == hashval)) break;
|
||||
if (node1p == node2p) continue; // Identical iterator
|
||||
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
|
||||
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
|
||||
// Replace AstCCall's that point here
|
||||
replaceFuncWFunc(VN_CAST(node2p, CFunc), cfunc1p);
|
||||
// Replacement may promote a slow routine to fast path
|
||||
if (!VN_CAST(node2p, CFunc)->slow()) cfunc1p->slow(false);
|
||||
}
|
||||
for (const bool slow : {false, true}) {
|
||||
for (auto newIt = m_dupFinder.begin(); newIt != m_dupFinder.end(); ++newIt) {
|
||||
AstCFunc* const newfuncp = VN_CAST(newIt->second, CFunc);
|
||||
UASSERT_OBJ(newfuncp, newIt->second, "Not a CFunc in hash");
|
||||
if (newfuncp->user3()) continue; // Already replaced
|
||||
if (newfuncp->slow() != slow) continue;
|
||||
auto oldIt = newIt;
|
||||
++oldIt; // Skip over current position
|
||||
for (; oldIt != m_dupFinder.end(); ++oldIt) {
|
||||
AstCFunc* const oldfuncp = VN_CAST(oldIt->second, CFunc);
|
||||
UASSERT_OBJ(oldfuncp, oldIt->second, "Not a CFunc in hash");
|
||||
UASSERT_OBJ(newfuncp != oldfuncp, newfuncp,
|
||||
"Same function hashed multiple times");
|
||||
if (newIt->first != oldIt->first) break; // Iterate over same hashes only
|
||||
if (oldfuncp->user3()) continue; // Already replaced
|
||||
if (!newfuncp->sameTree(oldfuncp)) continue; // Different functions
|
||||
|
||||
// Replace calls to oldfuncp with calls to newfuncp
|
||||
UINFO(5, " Replace CFunc " << newIt->first << " " << newfuncp << endl);
|
||||
UINFO(5, " with " << oldIt->first << " " << oldfuncp << endl);
|
||||
++m_cfuncsCombined;
|
||||
oldfuncp->user3SetOnce(); // Mark replaced
|
||||
m_call.replaceFunc(oldfuncp, newfuncp);
|
||||
oldfuncp->unlinkFrBack();
|
||||
// Replacement may promote a slow routine to fast path
|
||||
if (!oldfuncp->slow()) newfuncp->slow(false);
|
||||
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
UINFO(5, " DupFunc " << std::hex << V3Hash(newfuncp->user4p()) << " " << newfuncp
|
||||
<< endl);
|
||||
UINFO(5, " and " << std::hex << V3Hash(oldfuncp->user4p()) << " " << oldfuncp
|
||||
<< endl);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
++m_statCombs;
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
m_call.replaceFunc(oldfuncp, newfuncp);
|
||||
oldfuncp->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
||||
}
|
||||
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
void replaceOnlyCallFunc(AstCCall* nodep) {
|
||||
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
|
||||
// oldfuncp->dumpTree(cout, "MAYDEL: ");
|
||||
if (nodep->nextp() == nullptr && oldfuncp->initsp() == nullptr
|
||||
&& oldfuncp->stmtsp() == nodep && oldfuncp->finalsp() == nullptr) {
|
||||
UINFO(9, " Function only has call " << oldfuncp << endl);
|
||||
m_call.deleteCall(nodep);
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
VL_DO_DANGLING(replaceFuncWFunc(oldfuncp, nodep->funcp()), nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void walkDupCodeStart(AstNode* node1p) {
|
||||
V3Hash hashval(node1p->user4p());
|
||||
// UINFO(4," STMT " << hashval << " " << node1p << endl);
|
||||
//
|
||||
int bestDepth = 0; // Best substitution found in the search
|
||||
AstNode* bestNode2p = nullptr;
|
||||
AstNode* bestLast1p = nullptr;
|
||||
AstNode* bestLast2p = nullptr;
|
||||
//
|
||||
std::pair<V3Hashed::iterator, V3Hashed::iterator> eqrange
|
||||
= m_hashed.mmap().equal_range(hashval);
|
||||
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (node1p == node2p) continue;
|
||||
//
|
||||
// We need to mark iteration to prevent matching code inside
|
||||
// code (abab matching in ababab)
|
||||
AstNode::user1ClearTree(); // user1p() used on entire tree
|
||||
m_walkLast1p = nullptr;
|
||||
m_walkLast2p = nullptr;
|
||||
int depth = walkDupCodeNext(node1p, node2p, 1);
|
||||
if (depth > COMBINE_MIN_STATEMENTS && depth > bestDepth) {
|
||||
bestDepth = depth;
|
||||
bestNode2p = node2p;
|
||||
bestLast1p = m_walkLast1p;
|
||||
bestLast2p = m_walkLast2p;
|
||||
}
|
||||
}
|
||||
if (bestDepth) {
|
||||
// Found a replacement
|
||||
UINFO(5, " Duplicate of depth " << bestDepth << endl);
|
||||
UINFO(5, " DupFunc "
|
||||
<< " " << node1p << endl);
|
||||
UINFO(5, " and "
|
||||
<< " " << bestNode2p << endl);
|
||||
UINFO(5, " Through "
|
||||
<< " " << bestLast1p << endl);
|
||||
UINFO(5, " and "
|
||||
<< " " << bestLast2p << endl);
|
||||
//
|
||||
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
|
||||
}
|
||||
}
|
||||
|
||||
int walkDupCodeNext(AstNode* node1p, AstNode* node2p, int level) {
|
||||
// Find number of common statements between the two node1p_nextp's...
|
||||
if (node1p->user1p() || node2p->user1p()) return 0; // Already iterated
|
||||
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
|
||||
if (!m_hashed.sameNodes(node1p, node2p)) return 0; // walk of tree has same comparison
|
||||
V3Hash hashval(node1p->user4p());
|
||||
// UINFO(9, " wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
|
||||
// UINFO(9, " wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
|
||||
m_walkLast1p = node1p;
|
||||
m_walkLast2p = node2p;
|
||||
node1p->user1(true);
|
||||
node2p->user1(true);
|
||||
if (node1p->nextp() && node2p->nextp()) {
|
||||
return hashval.depth() + walkDupCodeNext(node1p->nextp(), node2p->nextp(), level + 1);
|
||||
}
|
||||
return hashval.depth();
|
||||
}
|
||||
|
||||
void walkReplace(AstNode* node1p, AstNode* node2p, AstNode* last1p,
|
||||
AstNode* last2p) { // Final node in linked list, maybe null if all statements
|
||||
// to be grabbed
|
||||
// Make new function
|
||||
string oldname = m_cfuncp->name();
|
||||
string::size_type pos;
|
||||
if ((pos = oldname.find("_common")) != string::npos) oldname.erase(pos);
|
||||
if ((pos = oldname.find("__")) != string::npos) oldname.erase(pos);
|
||||
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
|
||||
oldname + "_common" + cvtToStr(++m_modNFuncs), nullptr);
|
||||
m_modp->addStmtp(newfuncp);
|
||||
// Create calls
|
||||
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
|
||||
AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp);
|
||||
// Grab statement bodies
|
||||
AstNRelinker relink1Handle;
|
||||
AstNRelinker relink2Handle;
|
||||
for (AstNode *nextp, *walkp = node1p; true; walkp = nextp) {
|
||||
nextp = walkp->nextp();
|
||||
if (walkp == node1p) {
|
||||
walkp->unlinkFrBack(&relink1Handle);
|
||||
} else {
|
||||
walkp->unlinkFrBack();
|
||||
node1p->addNext(walkp);
|
||||
}
|
||||
if (walkp == last1p) break;
|
||||
}
|
||||
for (AstNode *nextp, *walkp = node2p; true; walkp = nextp) {
|
||||
nextp = walkp->nextp();
|
||||
if (walkp == node2p) {
|
||||
walkp->unlinkFrBack(&relink2Handle);
|
||||
} else {
|
||||
walkp->unlinkFrBack();
|
||||
node2p->addNext(walkp);
|
||||
}
|
||||
if (walkp == last2p) break;
|
||||
}
|
||||
// Move node1 statements to new function
|
||||
newfuncp->addStmtsp(node1p);
|
||||
// newfuncp->dumpTree(cout, " newfunctree: ");
|
||||
// Mark node2 statements as dead
|
||||
CombMarkVisitor visitor(node2p);
|
||||
pushDeletep(node2p); // Delete later
|
||||
// Link in new function
|
||||
relink1Handle.relink(call1p);
|
||||
relink2Handle.relink(call2p);
|
||||
// Hash the new function
|
||||
hashFunctions(newfuncp);
|
||||
m_call.addCall(call1p);
|
||||
m_call.addCall(call2p);
|
||||
// If either new statement makes a func with only a single call, replace
|
||||
// the above callers to call it directly
|
||||
VL_DO_DANGLING(replaceOnlyCallFunc(call1p), call1p);
|
||||
VL_DO_DANGLING(replaceOnlyCallFunc(call2p), call2p);
|
||||
}
|
||||
#endif
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) override {
|
||||
// Track all callers of each function
|
||||
m_call.main(nodep);
|
||||
//
|
||||
// In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
|
||||
// Iterate modules backwards, in bottom-up order.
|
||||
// Required so that a module instantiating another can benefit from collapsing.
|
||||
iterateChildrenBackwards(nodep);
|
||||
m_call.main(nodep); // Track all call sites of each function
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
m_modp = nodep;
|
||||
m_modNFuncs = 0;
|
||||
m_walkLast2p = nullptr;
|
||||
m_hashed.clear();
|
||||
// Compute hash of all statement trees in the function
|
||||
m_state = STATE_HASH;
|
||||
m_dupFinder.clear();
|
||||
// Compute hash of all CFuncs in the module
|
||||
iterateChildren(nodep);
|
||||
m_state = STATE_IDLE;
|
||||
if (debug() >= 9) m_hashed.dumpFilePrefixed("combine");
|
||||
if (debug() >= 9) m_dupFinder.dumpFilePrefixed("combine");
|
||||
// Walk the hashes removing empty functions
|
||||
walkEmptyFuncs();
|
||||
// Walk the hashes looking for duplicate functions
|
||||
walkDupFuncs();
|
||||
// Walk the statements looking for large replicated code sections
|
||||
// Note this is disabled, it still needed work
|
||||
// Also repair it for DPI functions; when make __common need to ensure proper
|
||||
// flags get inherited from the old to new AstCFunc, and that AstText doesn't
|
||||
// get split between functions causing the text to have a dangling reference.
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
{
|
||||
m_state = STATE_DUP;
|
||||
iterateChildren(nodep);
|
||||
m_state = STATE_IDLE;
|
||||
}
|
||||
#endif
|
||||
m_modp = nullptr;
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
m_cfuncp = nodep;
|
||||
if (!nodep->dontCombine()) {
|
||||
if (m_state == STATE_HASH) {
|
||||
hashStatement(nodep); // Hash the entire function - it might be identical
|
||||
}
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
else if (m_state == STATE_DUP) {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeStmt* nodep) override {
|
||||
if (!nodep->isStatement()) {
|
||||
iterateChildren(nodep);
|
||||
return;
|
||||
}
|
||||
if (m_state == STATE_HASH && m_cfuncp) {
|
||||
hashStatement(nodep);
|
||||
}
|
||||
#ifdef VL_COMBINE_STATEMENTS
|
||||
else if (m_state == STATE_DUP && m_cfuncp) {
|
||||
walkDupCodeStart(nodep);
|
||||
}
|
||||
#endif
|
||||
if (nodep->dontCombine()) return;
|
||||
// Hash the entire function
|
||||
m_dupFinder.insert(nodep);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
virtual void visit(AstVar*) override {}
|
||||
virtual void visit(AstTraceDecl*) override {}
|
||||
virtual void visit(AstTraceInc*) override {}
|
||||
virtual void visit(AstVar*) override {} // Accelerate
|
||||
virtual void visit(AstNodeStmt* nodep) override {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CombineVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~CombineVisitor() override { //
|
||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
|
||||
virtual ~CombineVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+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);
|
||||
|
||||
+229
-72
@@ -82,13 +82,58 @@ public:
|
||||
class ConstBitOpTreeVisitor final : public AstNVisitor {
|
||||
// TYPES
|
||||
|
||||
struct LeafInfo { // Leaf node (either AstConst or AstVarRef)
|
||||
struct LeafInfo final { // Leaf node (either AstConst or AstVarRef)
|
||||
bool m_polarity = true;
|
||||
int m_lsb = 0;
|
||||
int m_wordIdx = -1; // -1 means AstWordSel is not used.
|
||||
AstVarRef* m_refp = nullptr;
|
||||
AstConst* m_constp = nullptr;
|
||||
};
|
||||
|
||||
struct BitPolarityEntry final { // Found bit polarity during iterate()
|
||||
LeafInfo m_info;
|
||||
bool m_polarity;
|
||||
int m_bit;
|
||||
BitPolarityEntry(const LeafInfo& info, bool pol, int bit)
|
||||
: m_info(info)
|
||||
, m_polarity(pol)
|
||||
, m_bit(bit) {}
|
||||
BitPolarityEntry() = default;
|
||||
};
|
||||
|
||||
class Restorer final { // Restore the original state unless disableRestore() is called
|
||||
ConstBitOpTreeVisitor& m_visitor;
|
||||
const size_t m_polaritiesSize;
|
||||
const size_t m_frozenSize;
|
||||
const int m_ops;
|
||||
const bool m_polarity;
|
||||
bool m_restore;
|
||||
|
||||
public:
|
||||
explicit Restorer(ConstBitOpTreeVisitor& visitor)
|
||||
: m_visitor(visitor)
|
||||
, m_polaritiesSize(visitor.m_bitPolarities.size())
|
||||
, m_frozenSize(visitor.m_frozenNodes.size())
|
||||
, m_ops(visitor.m_ops)
|
||||
, m_polarity(visitor.m_polarity)
|
||||
, m_restore(true) {}
|
||||
~Restorer() {
|
||||
UASSERT(m_visitor.m_bitPolarities.size() >= m_polaritiesSize,
|
||||
"m_bitPolarities must grow monotorilaclly");
|
||||
UASSERT(m_visitor.m_frozenNodes.size() >= m_frozenSize,
|
||||
"m_frozenNodes must grow monotorilaclly");
|
||||
if (m_restore) restoreNow();
|
||||
}
|
||||
void disableRestore() { m_restore = false; }
|
||||
void restoreNow() {
|
||||
UASSERT(m_restore, "Can be called just once");
|
||||
m_visitor.m_bitPolarities.resize(m_polaritiesSize);
|
||||
m_visitor.m_frozenNodes.resize(m_frozenSize);
|
||||
m_visitor.m_ops = m_ops;
|
||||
m_visitor.m_polarity = m_polarity;
|
||||
m_restore = false;
|
||||
}
|
||||
};
|
||||
// Collect information for each Variable to transform as below
|
||||
class VarInfo final {
|
||||
// MEMBERS
|
||||
@@ -96,18 +141,37 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
||||
ConstBitOpTreeVisitor* m_parentp; // ConstBitOpTreeVisitor that holds this VarInfo
|
||||
AstVarRef* m_refp; // Points the variable that this VarInfo covers
|
||||
V3Number m_bitPolarity; // Coefficient of each bit
|
||||
static int widthOfRef(AstVarRef* refp) {
|
||||
if (AstWordSel* selp = VN_CAST(refp->backp(), WordSel)) return selp->width();
|
||||
if (AstCCast* castp = VN_CAST(refp->backp(), CCast)) return castp->width();
|
||||
return refp->width();
|
||||
}
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
bool hasConstantResult() const { return m_constResult >= 0; }
|
||||
bool sameVarAs(const AstNodeVarRef* otherp) const { return m_refp->sameGateTree(otherp); }
|
||||
bool sameVarAs(const AstNodeVarRef* otherp) const { return m_refp->same(otherp); }
|
||||
void setPolarity(bool compBit, int bit) {
|
||||
UASSERT_OBJ(!hasConstantResult(), m_refp, "Already has result of " << m_constResult);
|
||||
UASSERT_OBJ(bit < VL_QUADSIZE, m_refp,
|
||||
"bit:" << bit << " is too big after V3Expand"
|
||||
<< " back:" << m_refp->backp());
|
||||
if (bit >= m_bitPolarity.width()) { // Need to expand m_bitPolarity
|
||||
const V3Number oldPol = std::move(m_bitPolarity);
|
||||
// oldPol.width() is 8, 16, or 32 because this visitor is called after V3Expand
|
||||
// newWidth is increased by 2x because
|
||||
// - CCast will cast to such bitwidth anyway
|
||||
// - can avoid frequent expansion
|
||||
int newWidth = oldPol.width();
|
||||
while (bit >= newWidth) newWidth *= 2;
|
||||
m_bitPolarity = V3Number{m_refp, newWidth};
|
||||
UASSERT_OBJ(newWidth == 16 || newWidth == 32 || newWidth == 64, m_refp,
|
||||
"bit:" << bit << " newWidth:" << newWidth);
|
||||
m_bitPolarity.setAllBitsX();
|
||||
for (int i = 0; i < oldPol.width(); ++i) {
|
||||
if (oldPol.bitIs0(i))
|
||||
m_bitPolarity.setBit(i, '0');
|
||||
else if (oldPol.bitIs1(i))
|
||||
m_bitPolarity.setBit(i, '1');
|
||||
}
|
||||
}
|
||||
UASSERT_OBJ(bit < m_bitPolarity.width(), m_refp,
|
||||
"bit:" << bit << " width:" << m_bitPolarity.width() << m_refp);
|
||||
if (m_bitPolarity.bitIsX(bit)) { // The bit is not yet set
|
||||
m_bitPolarity.setBit(bit, compBit);
|
||||
} else { // Priviously set the bit
|
||||
@@ -129,7 +193,7 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
||||
FileLine* fl = m_refp->fileline();
|
||||
AstNode* srcp = VN_CAST(m_refp->backp(), WordSel);
|
||||
if (!srcp) srcp = m_refp;
|
||||
const int width = widthOfRef(m_refp);
|
||||
const int width = m_bitPolarity.width();
|
||||
|
||||
if (hasConstantResult())
|
||||
return new AstConst{fl,
|
||||
@@ -160,7 +224,7 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
||||
VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp)
|
||||
: m_parentp(parent)
|
||||
, m_refp(refp)
|
||||
, m_bitPolarity(refp, widthOfRef(refp)) {
|
||||
, m_bitPolarity(refp, refp->isWide() ? VL_EDATASIZE : refp->width()) {
|
||||
m_bitPolarity.setAllBitsX();
|
||||
}
|
||||
};
|
||||
@@ -176,7 +240,8 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
||||
|
||||
AstUser4InUse m_inuser4;
|
||||
std::vector<AstNode*> m_frozenNodes; // Nodes that cannot be optimized
|
||||
std::vector<VarInfo*> m_varInfos; // VarInfo for each variable, [0] is nullptr
|
||||
std::vector<BitPolarityEntry> m_bitPolarities; // Polarity of bits found during iterate()
|
||||
std::vector<std::unique_ptr<VarInfo>> m_varInfos; // VarInfo for each variable, [0] is nullptr
|
||||
|
||||
// NODE STATE
|
||||
// AstVarRef::user4u -> Base index of m_varInfos that points VarInfo
|
||||
@@ -216,14 +281,14 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
||||
baseIdx = m_varInfos.size();
|
||||
const int numWords
|
||||
= ref.m_refp->dtypep()->isWide() ? ref.m_refp->dtypep()->widthWords() : 1;
|
||||
m_varInfos.resize(m_varInfos.size() + numWords, nullptr);
|
||||
m_varInfos.resize(m_varInfos.size() + numWords);
|
||||
nodep->user4(baseIdx);
|
||||
}
|
||||
const size_t idx = baseIdx + std::max(0, ref.m_wordIdx);
|
||||
VarInfo* varInfop = m_varInfos[idx];
|
||||
VarInfo* varInfop = m_varInfos[idx].get();
|
||||
if (!varInfop) {
|
||||
varInfop = new VarInfo{this, ref.m_refp};
|
||||
m_varInfos[idx] = varInfop;
|
||||
m_varInfos[idx].reset(varInfop);
|
||||
} else {
|
||||
if (!varInfop->sameVarAs(ref.m_refp))
|
||||
CONST_BITOP_SET_FAILED("different var (scope?)", ref.m_refp);
|
||||
@@ -306,28 +371,30 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
||||
}
|
||||
|
||||
virtual void visit(AstRedXor* nodep) override { // Expect '^(mask & v)'
|
||||
Restorer restorer{*this};
|
||||
CONST_BITOP_RETURN_IF(!VN_IS(m_rootp, Xor), nodep);
|
||||
AstAnd* andp = VN_CAST(nodep->lhsp(), And);
|
||||
CONST_BITOP_RETURN_IF(!andp, nodep->lhsp());
|
||||
|
||||
auto mask = findLeaf(andp->lhsp(), true);
|
||||
const auto mask = findLeaf(andp->lhsp(), true);
|
||||
CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp());
|
||||
|
||||
LeafInfo leaf = findLeaf(andp->rhsp(), false);
|
||||
const LeafInfo leaf = findLeaf(andp->rhsp(), false);
|
||||
CONST_BITOP_RETURN_IF(!leaf.m_refp, andp->rhsp());
|
||||
|
||||
restorer.disableRestore(); // Now all subtree succeeded
|
||||
|
||||
incrOps(nodep, __LINE__);
|
||||
incrOps(andp, __LINE__);
|
||||
const V3Number& maskNum = mask.m_constp->num();
|
||||
VarInfo& varInfo = getVarInfo(leaf);
|
||||
for (int i = 0; i < maskNum.width(); ++i) {
|
||||
// Set true, m_treePolarity takes care of the entire parity
|
||||
if (maskNum.bitIs1(i)) varInfo.setPolarity(true, i + leaf.m_lsb);
|
||||
if (maskNum.bitIs1(i)) m_bitPolarities.emplace_back(leaf, true, i + leaf.m_lsb);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeBiop* nodep) override {
|
||||
auto isConst = [](AstNode* nodep, vluint64_t v) -> bool {
|
||||
const auto isConst = [](AstNode* nodep, vluint64_t v) -> bool {
|
||||
AstConst* constp = VN_CAST(nodep, Const);
|
||||
return constp && constp->toUQuad() == v;
|
||||
};
|
||||
@@ -338,29 +405,28 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
||||
VL_RESTORER(m_leafp);
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Restorer restorer{*this};
|
||||
LeafInfo leafInfo;
|
||||
m_leafp = &leafInfo;
|
||||
m_curOpp = i == 0 ? nodep->lhsp() : nodep->rhsp();
|
||||
const size_t origFrozens = m_frozenNodes.size();
|
||||
const int origOps = m_ops;
|
||||
const bool origFailed = m_failed;
|
||||
iterate(m_curOpp);
|
||||
if (leafInfo.m_constp || m_failed) {
|
||||
// Rvert changes in leaf
|
||||
if (m_frozenNodes.size() > origFrozens) m_frozenNodes.resize(origFrozens);
|
||||
restorer.restoreNow();
|
||||
m_frozenNodes.push_back(m_curOpp);
|
||||
m_ops = origOps;
|
||||
m_failed = origFailed;
|
||||
} else if (leafInfo.m_refp) {
|
||||
VarInfo& varInfo = getVarInfo(leafInfo);
|
||||
if (!varInfo.hasConstantResult()) {
|
||||
varInfo.setPolarity(isXorTree() || leafInfo.m_polarity, leafInfo.m_lsb);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
restorer.disableRestore(); // Now all checks passed
|
||||
if (leafInfo.m_refp)
|
||||
m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.m_polarity,
|
||||
leafInfo.m_lsb);
|
||||
}
|
||||
return;
|
||||
} else if (VN_IS(m_rootp, Xor) && VN_IS(nodep, Eq) && isConst(nodep->lhsp(), 0)
|
||||
&& VN_IS(nodep->rhsp(), And)) { // 0 == (1 & RedXor)
|
||||
Restorer restorer{*this};
|
||||
AstAnd* andp = static_cast<AstAnd*>(nodep->rhsp()); // already checked above
|
||||
CONST_BITOP_RETURN_IF(!isConst(andp->lhsp(), 1), andp->lhsp());
|
||||
AstRedXor* redXorp = VN_CAST(andp->rhsp(), RedXor);
|
||||
@@ -369,36 +435,43 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
||||
incrOps(andp, __LINE__);
|
||||
m_polarity = !m_polarity;
|
||||
iterate(redXorp);
|
||||
CONST_BITOP_RETURN_IF(m_failed, redXorp);
|
||||
|
||||
restorer.disableRestore(); // Now all checks passed
|
||||
return;
|
||||
} else if (VN_IS(m_rootp, Xor) && VN_IS(nodep, And) && isConst(nodep->lhsp(), 1)
|
||||
&& (VN_IS(nodep->rhsp(), Xor)
|
||||
|| VN_IS(nodep->rhsp(), RedXor))) { // 1 & (v[3] ^ v[2])
|
||||
Restorer restorer{*this};
|
||||
incrOps(nodep, __LINE__);
|
||||
iterate(nodep->rhsp());
|
||||
CONST_BITOP_RETURN_IF(m_failed, nodep->rhsp());
|
||||
restorer.disableRestore(); // Now all checks passed
|
||||
return;
|
||||
} else if ((isAndTree() && VN_IS(nodep, Eq)) || (isOrTree() && VN_IS(nodep, Neq))) {
|
||||
Restorer restorer{*this};
|
||||
CONST_BITOP_RETURN_IF(!m_polarity, nodep);
|
||||
const bool maskFlip = isOrTree();
|
||||
LeafInfo comp = findLeaf(nodep->lhsp(), true);
|
||||
const LeafInfo comp = findLeaf(nodep->lhsp(), true);
|
||||
CONST_BITOP_RETURN_IF(!comp.m_constp || comp.m_lsb != 0, nodep->lhsp());
|
||||
|
||||
AstAnd* andp = VN_CAST(nodep->rhsp(), And); // comp == (mask & v)
|
||||
CONST_BITOP_RETURN_IF(!andp, nodep->rhsp());
|
||||
|
||||
LeafInfo mask = findLeaf(andp->lhsp(), true);
|
||||
const LeafInfo mask = findLeaf(andp->lhsp(), true);
|
||||
CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp());
|
||||
|
||||
LeafInfo ref = findLeaf(andp->rhsp(), false);
|
||||
const LeafInfo ref = findLeaf(andp->rhsp(), false);
|
||||
CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp());
|
||||
|
||||
VarInfo& varInfo = getVarInfo(ref);
|
||||
restorer.disableRestore(); // Now all checks passed
|
||||
|
||||
const V3Number maskNum = mask.m_constp->num();
|
||||
const V3Number compNum = comp.m_constp->num();
|
||||
for (int i = 0; i < maskNum.width() && !varInfo.hasConstantResult(); ++i) {
|
||||
for (int i = 0; i < maskNum.width(); ++i) {
|
||||
const int bit = i + ref.m_lsb;
|
||||
if (maskNum.bitIs0(i)) continue;
|
||||
varInfo.setPolarity(compNum.bitIs1(i) ^ maskFlip, bit);
|
||||
m_bitPolarities.emplace_back(ref, compNum.bitIs1(i) != maskFlip, bit);
|
||||
}
|
||||
incrOps(nodep, __LINE__);
|
||||
incrOps(andp, __LINE__);
|
||||
@@ -421,13 +494,14 @@ class ConstBitOpTreeVisitor final : public AstNVisitor {
|
||||
incrOps(nodep, __LINE__);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
for (auto&& entry : m_bitPolarities) {
|
||||
VarInfo& info = getVarInfo(entry.m_info);
|
||||
if (info.hasConstantResult()) continue;
|
||||
info.setPolarity(entry.m_polarity, entry.m_bit);
|
||||
}
|
||||
UASSERT_OBJ(isXorTree() || m_polarity, nodep, "must be the original polarity");
|
||||
}
|
||||
virtual ~ConstBitOpTreeVisitor() {
|
||||
for (size_t i = 0; i < m_varInfos.size(); ++i) {
|
||||
VL_DO_DANGLING(delete m_varInfos[i], m_varInfos[i]);
|
||||
}
|
||||
}
|
||||
virtual ~ConstBitOpTreeVisitor() = default;
|
||||
#undef CONST_BITOP_RETURN_IF
|
||||
#undef CONST_BITOP_SET_FAILED
|
||||
|
||||
@@ -447,7 +521,7 @@ public:
|
||||
// Two ops for each varInfo. (And and Eq)
|
||||
const int vars = visitor.m_varInfos.size() - 1;
|
||||
int constTerms = 0;
|
||||
for (const VarInfo* v : visitor.m_varInfos) {
|
||||
for (auto&& v : visitor.m_varInfos) {
|
||||
if (v && v->hasConstantResult()) ++constTerms;
|
||||
}
|
||||
// Expected number of ops after this simplification
|
||||
@@ -467,7 +541,7 @@ public:
|
||||
AstNode* resultp = nullptr;
|
||||
// VarInfo in visitor.m_varInfos appears in deterministic order,
|
||||
// so the optimized AST is deterministic too.
|
||||
for (const VarInfo* varinfop : visitor.m_varInfos) {
|
||||
for (auto&& varinfop : visitor.m_varInfos) {
|
||||
if (!varinfop) continue;
|
||||
AstNode* partialresultp = varinfop->getResult();
|
||||
resultp = visitor.combineTree(resultp, partialresultp);
|
||||
@@ -691,6 +765,86 @@ private:
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return true;
|
||||
}
|
||||
bool matchMaskedOr(AstAnd* nodep) {
|
||||
// Masking an OR with terms that have no bits set under the mask is replaced with masking
|
||||
// only the remaining terms. Canonical example as generated by V3Expand is:
|
||||
// 0xff & (a << 8 | b >> 24) --> 0xff & (b >> 24)
|
||||
|
||||
// Compute how many significant bits are in the mask
|
||||
const AstConst* const constp = VN_CAST(nodep->lhsp(), Const);
|
||||
const uint32_t significantBits = constp->num().widthMin();
|
||||
|
||||
AstOr* const orp = VN_CAST(nodep->rhsp(), Or);
|
||||
|
||||
// Predicate for checking whether the bottom 'significantBits' bits of the given expression
|
||||
// are all zeroes.
|
||||
const auto checkBottomClear = [=](const AstNode* nodep) -> bool {
|
||||
if (const AstShiftL* const shiftp = VN_CAST_CONST(nodep, ShiftL)) {
|
||||
if (const AstConst* const scp = VN_CAST_CONST(shiftp->rhsp(), Const)) {
|
||||
return scp->num().toUInt() >= significantBits;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const bool orLIsRedundant = checkBottomClear(orp->lhsp());
|
||||
const bool orRIsRedundant = checkBottomClear(orp->rhsp());
|
||||
|
||||
if (orLIsRedundant && orRIsRedundant) {
|
||||
nodep->replaceWith(
|
||||
new AstConst(nodep->fileline(), AstConst::DtypedValue(), nodep->dtypep(), 0));
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return true;
|
||||
} else if (orLIsRedundant) {
|
||||
orp->replaceWith(orp->rhsp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(orp->deleteTree(), orp);
|
||||
return false; // input node is still valid, keep going
|
||||
} else if (orRIsRedundant) {
|
||||
orp->replaceWith(orp->lhsp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(orp->deleteTree(), orp);
|
||||
return false; // input node is still valid, keep going
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool matchMaskedShift(AstAnd* nodep) {
|
||||
// Drop redundant masking of right shift result. E.g: 0xff & ((uint32_t)a >> 24). This
|
||||
// commonly appears after V3Expand and the simplification in matchMaskedOr. Similarly,
|
||||
// drop redundant masking of left shift result. E.g.: 0xff000000 & ((uint32_t)a << 24).
|
||||
|
||||
const auto checkMask = [=](const V3Number& mask) -> bool {
|
||||
const AstConst* const constp = VN_CAST(nodep->lhsp(), Const);
|
||||
if (constp->num().isCaseEq(mask)) {
|
||||
AstNode* const rhsp = nodep->rhsp();
|
||||
rhsp->unlinkFrBack();
|
||||
nodep->replaceWith(rhsp);
|
||||
rhsp->dtypeFrom(nodep);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Check if masking is redundant
|
||||
if (AstShiftR* const shiftp = VN_CAST(nodep->rhsp(), ShiftR)) {
|
||||
if (const AstConst* scp = VN_CAST_CONST(shiftp->rhsp(), Const)) {
|
||||
// Check if mask is full over the non-zero bits
|
||||
V3Number maskLo(nodep, nodep->width());
|
||||
maskLo.setMask(nodep->width() - scp->num().toUInt());
|
||||
return checkMask(maskLo);
|
||||
}
|
||||
} else if (AstShiftL* const shiftp = VN_CAST(nodep->rhsp(), ShiftL)) {
|
||||
if (const AstConst* scp = VN_CAST_CONST(shiftp->rhsp(), Const)) {
|
||||
// Check if mask is full over the non-zero bits
|
||||
V3Number maskLo(nodep, nodep->width()), maskHi(nodep, nodep->width());
|
||||
maskLo.setMask(nodep->width() - scp->num().toUInt());
|
||||
maskHi.opShiftL(maskLo, scp->num());
|
||||
return checkMask(maskHi);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool matchBitOpTree(AstNode* nodep) {
|
||||
if (!v3Global.opt.oConstBitOpTree()) return false;
|
||||
|
||||
@@ -849,7 +1003,7 @@ private:
|
||||
|| lp->num().isFourState() || lp->num().isNegative()) {
|
||||
return false;
|
||||
}
|
||||
int newLsb = lp->toSInt() + bp->toSInt();
|
||||
const int newLsb = lp->toSInt() + bp->toSInt();
|
||||
if (newLsb + nodep->widthConst() > ap->width()) return false;
|
||||
//
|
||||
UINFO(9, "SEL(SHIFTR(a,b),l,w) -> SEL(a,l+b,w)\n");
|
||||
@@ -870,7 +1024,7 @@ private:
|
||||
AstExtend* extendp = VN_CAST(nodep->rhsp(), Extend);
|
||||
if (!extendp) return false;
|
||||
AstNode* smallerp = extendp->lhsp();
|
||||
int subsize = smallerp->width();
|
||||
const int subsize = smallerp->width();
|
||||
AstConst* constp = VN_CAST(nodep->lhsp(), Const);
|
||||
if (!constp) return false;
|
||||
if (!constp->num().isBitsZero(constp->width() - 1, subsize)) return false;
|
||||
@@ -895,7 +1049,7 @@ private:
|
||||
const AstExtend* extendp = VN_CAST_CONST(nodep->rhsp(), Extend);
|
||||
if (!extendp) return false;
|
||||
AstNode* smallerp = extendp->lhsp();
|
||||
int subsize = smallerp->width();
|
||||
const int subsize = smallerp->width();
|
||||
const AstConst* constp = VN_CAST_CONST(nodep->lhsp(), Const);
|
||||
if (!constp) return false;
|
||||
if (constp->num().isBitsZero(constp->width() - 1, subsize)) return false;
|
||||
@@ -926,10 +1080,10 @@ private:
|
||||
}
|
||||
// Find range of dtype we are selecting from
|
||||
// Similar code in V3Unknown::AstSel
|
||||
bool doit = true;
|
||||
const bool doit = true;
|
||||
if (m_warn && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const) && doit) {
|
||||
int maxDeclBit = nodep->declRange().hiMaxSelect() * nodep->declElWidth()
|
||||
+ (nodep->declElWidth() - 1);
|
||||
const int maxDeclBit = nodep->declRange().hiMaxSelect() * nodep->declElWidth()
|
||||
+ (nodep->declElWidth() - 1);
|
||||
if (VN_CAST(nodep->lsbp(), Const)->num().isFourState()
|
||||
|| VN_CAST(nodep->widthp(), Const)->num().isFourState()) {
|
||||
nodep->v3error("Selection index is constantly unknown or tristated: "
|
||||
@@ -1004,7 +1158,7 @@ private:
|
||||
const AstConst* lwidth = VN_CAST_CONST(lhsp->widthp(), Const);
|
||||
const AstConst* rwidth = VN_CAST_CONST(rhsp->widthp(), Const);
|
||||
if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated
|
||||
int rend = (rstart->toSInt() + rwidth->toSInt());
|
||||
const int rend = (rstart->toSInt() + rwidth->toSInt());
|
||||
return (rend == lstart->toSInt());
|
||||
}
|
||||
bool ifMergeAdjacent(AstNode* lhsp, AstNode* rhsp) {
|
||||
@@ -1037,7 +1191,7 @@ private:
|
||||
AstConst* lwidth = VN_CAST(lselp->widthp(), Const);
|
||||
AstConst* rwidth = VN_CAST(rselp->widthp(), Const);
|
||||
if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated
|
||||
int rend = (rstart->toSInt() + rwidth->toSInt());
|
||||
const int rend = (rstart->toSInt() + rwidth->toSInt());
|
||||
// a[i:j] a[j-1:k]
|
||||
if (rend == lstart->toSInt()) return true;
|
||||
// a[i:0] a[msb:j]
|
||||
@@ -1054,8 +1208,8 @@ private:
|
||||
const AstNodeBiop* rp = VN_CAST_CONST(rhsp, NodeBiop);
|
||||
if (!lp || !rp) return false;
|
||||
// {a[]&b[], a[]&b[]}
|
||||
bool lad = ifMergeAdjacent(lp->lhsp(), rp->lhsp());
|
||||
bool rad = ifMergeAdjacent(lp->rhsp(), rp->rhsp());
|
||||
const bool lad = ifMergeAdjacent(lp->lhsp(), rp->lhsp());
|
||||
const bool rad = ifMergeAdjacent(lp->rhsp(), rp->rhsp());
|
||||
if (lad && rad) return true;
|
||||
// {a[] & b[]&c[], a[] & b[]&c[]}
|
||||
if (lad && concatMergeable(lp->rhsp(), rp->rhsp())) return true;
|
||||
@@ -1296,10 +1450,10 @@ private:
|
||||
// {a[1], a[0]} -> a[1:0]
|
||||
AstSel* lselp = VN_CAST(nodep->lhsp()->unlinkFrBack(), Sel);
|
||||
AstSel* rselp = VN_CAST(nodep->rhsp()->unlinkFrBack(), Sel);
|
||||
int lstart = lselp->lsbConst();
|
||||
int lwidth = lselp->widthConst();
|
||||
int rstart = rselp->lsbConst();
|
||||
int rwidth = rselp->widthConst();
|
||||
const int lstart = lselp->lsbConst();
|
||||
const int lwidth = lselp->widthConst();
|
||||
const int rstart = rselp->lsbConst();
|
||||
const int rwidth = rselp->widthConst();
|
||||
|
||||
UASSERT_OBJ((rstart + rwidth) == lstart, nodep,
|
||||
"tried to merge two selects which are not adjacent");
|
||||
@@ -1360,7 +1514,7 @@ private:
|
||||
}
|
||||
void replaceMulShift(AstMul* nodep) { // Mul, but not MulS as not simple shift
|
||||
UINFO(5, "MUL(2^n,b)->SHIFTL(b,n) " << nodep << endl);
|
||||
int amount = VN_CAST(nodep->lhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
|
||||
const int amount = VN_CAST(nodep->lhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
|
||||
AstNode* opp = nodep->rhsp()->unlinkFrBack();
|
||||
AstShiftL* newp
|
||||
= new AstShiftL(nodep->fileline(), opp, new AstConst(nodep->fileline(), amount));
|
||||
@@ -1370,7 +1524,7 @@ private:
|
||||
}
|
||||
void replaceDivShift(AstDiv* nodep) { // Mul, but not MulS as not simple shift
|
||||
UINFO(5, "DIV(b,2^n)->SHIFTR(b,n) " << nodep << endl);
|
||||
int amount = VN_CAST(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
|
||||
const int amount = VN_CAST(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
|
||||
AstNode* opp = nodep->lhsp()->unlinkFrBack();
|
||||
AstShiftR* newp
|
||||
= new AstShiftR(nodep->fileline(), opp, new AstConst(nodep->fileline(), amount));
|
||||
@@ -1380,7 +1534,7 @@ private:
|
||||
}
|
||||
void replaceModAnd(AstModDiv* nodep) { // Mod, but not ModS as not simple shift
|
||||
UINFO(5, "MOD(b,2^n)->AND(b,2^n-1) " << nodep << endl);
|
||||
int amount = VN_CAST(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
|
||||
const int amount = VN_CAST(nodep->rhsp(), Const)->num().mostSetBitP1() - 1; // 2^n->n+1
|
||||
V3Number mask(nodep, nodep->width());
|
||||
mask.setMask(amount);
|
||||
AstNode* opp = nodep->lhsp()->unlinkFrBack();
|
||||
@@ -1423,7 +1577,7 @@ private:
|
||||
if (nodep->type() == lhsp->type()) {
|
||||
int shift1 = VN_CAST(shift1p, Const)->toUInt();
|
||||
int shift2 = VN_CAST(shift2p, Const)->toUInt();
|
||||
int newshift = shift1 + shift2;
|
||||
const int newshift = shift1 + shift2;
|
||||
VL_DO_DANGLING(shift1p->deleteTree(), shift1p);
|
||||
VL_DO_DANGLING(shift2p->deleteTree(), shift2p);
|
||||
nodep->lhsp(ap);
|
||||
@@ -1435,7 +1589,7 @@ private:
|
||||
if (VN_IS(lhsp, ShiftR)) shift1 = -shift1;
|
||||
int shift2 = VN_CAST(shift2p, Const)->toUInt();
|
||||
if (VN_IS(nodep, ShiftR)) shift2 = -shift2;
|
||||
int newshift = shift1 + shift2;
|
||||
const int newshift = shift1 + shift2;
|
||||
VL_DO_DANGLING(shift1p->deleteTree(), shift1p);
|
||||
VL_DO_DANGLING(shift2p->deleteTree(), shift2p);
|
||||
AstNode* newp;
|
||||
@@ -1503,7 +1657,7 @@ private:
|
||||
&& (con2p->toSInt() != con1p->toSInt() + sel1p->width())) {
|
||||
return false;
|
||||
}
|
||||
bool lsbFirstAssign = (con1p->toUInt() < con2p->toUInt());
|
||||
const bool lsbFirstAssign = (con1p->toUInt() < con2p->toUInt());
|
||||
UINFO(4, "replaceAssignMultiSel " << nodep << endl);
|
||||
UINFO(4, " && " << nextp << endl);
|
||||
// nodep->dumpTree(cout, "comb1: ");
|
||||
@@ -1662,8 +1816,8 @@ private:
|
||||
return true;
|
||||
} else if (m_doV && VN_IS(nodep->lhsp(), StreamL)) {
|
||||
// Push the stream operator to the rhs of the assignment statement
|
||||
int dWidth = VN_CAST(nodep->lhsp(), StreamL)->lhsp()->width();
|
||||
int sWidth = nodep->rhsp()->width();
|
||||
const int dWidth = VN_CAST(nodep->lhsp(), StreamL)->lhsp()->width();
|
||||
const int sWidth = nodep->rhsp()->width();
|
||||
// Unlink the stuff
|
||||
AstNode* dstp = VN_CAST(nodep->lhsp(), StreamL)->lhsp()->unlinkFrBack();
|
||||
AstNode* streamp = VN_CAST(nodep->lhsp(), StreamL)->unlinkFrBack();
|
||||
@@ -1683,8 +1837,8 @@ private:
|
||||
// The right stream operator on lhs of assignment statement does
|
||||
// not reorder bits. However, if the rhs is wider than the lhs,
|
||||
// then we select bits from the left-most, not the right-most.
|
||||
int dWidth = VN_CAST(nodep->lhsp(), StreamR)->lhsp()->width();
|
||||
int sWidth = nodep->rhsp()->width();
|
||||
const int dWidth = VN_CAST(nodep->lhsp(), StreamR)->lhsp()->width();
|
||||
const int sWidth = nodep->rhsp()->width();
|
||||
// Unlink the stuff
|
||||
AstNode* dstp = VN_CAST(nodep->lhsp(), StreamR)->lhsp()->unlinkFrBack();
|
||||
AstNode* sizep = VN_CAST(nodep->lhsp(), StreamR)->rhsp()->unlinkFrBack();
|
||||
@@ -1700,7 +1854,7 @@ private:
|
||||
VL_DO_DANGLING(streamp->deleteTree(), streamp);
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
} else if (replaceAssignMultiSel(nodep)) {
|
||||
} else if (m_doV && replaceAssignMultiSel(nodep)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -2513,7 +2667,7 @@ private:
|
||||
string fmt;
|
||||
bool inPct = false;
|
||||
AstNode* argp = nodep->exprsp();
|
||||
string text = nodep->text();
|
||||
const string text = nodep->text();
|
||||
for (const char ch : text) {
|
||||
if (!inPct && ch == '%') {
|
||||
inPct = true;
|
||||
@@ -2535,7 +2689,8 @@ private:
|
||||
if (argp) {
|
||||
AstNode* nextp = argp->nextp();
|
||||
if (VN_IS(argp, Const)) { // Convert it
|
||||
string out = VN_CAST(argp, Const)->num().displayed(nodep, fmt);
|
||||
const string out
|
||||
= VN_CAST(argp, Const)->num().displayed(nodep, fmt);
|
||||
UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
|
||||
<< argp << endl);
|
||||
// fmt = out w/ replace % with %% as it must be literal.
|
||||
@@ -2573,10 +2728,10 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstWhile* nodep) override {
|
||||
bool oldHasJumpDelay = m_hasJumpDelay;
|
||||
const bool oldHasJumpDelay = m_hasJumpDelay;
|
||||
m_hasJumpDelay = false;
|
||||
{ iterateChildren(nodep); }
|
||||
bool thisWhileHasJumpDelay = m_hasJumpDelay;
|
||||
const bool thisWhileHasJumpDelay = m_hasJumpDelay;
|
||||
m_hasJumpDelay = thisWhileHasJumpDelay || oldHasJumpDelay;
|
||||
if (m_doNConst) {
|
||||
if (nodep->condp()->isZero()) {
|
||||
@@ -2945,9 +3100,11 @@ private:
|
||||
TREEOPV("AstConcat{$lhsp.castSel, $rhsp.castSel, ifAdjacentSel(VN_CAST($lhsp,,Sel),,VN_CAST($rhsp,,Sel))}", "replaceConcatSel(nodep)");
|
||||
TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp)}", "replaceConcatMerge(nodep)");
|
||||
// Common two-level operations that can be simplified
|
||||
TREEOP ("AstAnd {$lhsp.castConst,matchAndCond(nodep)}", "DONE");
|
||||
TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
|
||||
TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
|
||||
TREEOP ("AstAnd {$lhsp.castConst,matchAndCond(nodep)}", "DONE");
|
||||
TREEOP ("AstAnd {$lhsp.castConst, $rhsp.castOr, matchMaskedOr(nodep)}", "DONE");
|
||||
TREEOPC("AstAnd {$lhsp.castConst, matchMaskedShift(nodep)}", "DONE");
|
||||
TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
|
||||
TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)");
|
||||
TREEOP ("AstOr {matchOrAndNot(nodep)}", "DONE");
|
||||
TREEOP ("AstAnd {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
||||
TREEOP ("AstOr {operandShiftSame(nodep)}", "replaceShiftSame(nodep)");
|
||||
|
||||
+14
-13
@@ -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;
|
||||
{
|
||||
|
||||
+8
-11
@@ -22,7 +22,7 @@
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3CoverageJoin.h"
|
||||
#include "V3Hashed.h"
|
||||
#include "V3DupFinder.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <vector>
|
||||
@@ -33,10 +33,7 @@
|
||||
class CoverageJoinVisitor final : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// V3Hashed
|
||||
// AstCoverToggle->VarRef::user4() // V3Hashed calculation
|
||||
|
||||
// AstUser4InUse In V3Hashed
|
||||
// AstUser4InUse In V3Hasher via V3DupFinder
|
||||
|
||||
// STATE
|
||||
std::vector<AstCoverToggle*> m_toggleps; // List of of all AstCoverToggle's
|
||||
@@ -49,9 +46,9 @@ private:
|
||||
void detectDuplicates() {
|
||||
UINFO(9, "Finding duplicates\n");
|
||||
// Note uses user4
|
||||
V3Hashed hashed; // Duplicate code detection
|
||||
V3DupFinder dupFinder; // Duplicate code detection
|
||||
// Hash all of the original signals we toggle cover
|
||||
for (AstCoverToggle* nodep : m_toggleps) hashed.hashAndInsert(nodep->origp());
|
||||
for (AstCoverToggle* nodep : m_toggleps) dupFinder.insert(nodep->origp());
|
||||
// Find if there are any duplicates
|
||||
for (AstCoverToggle* nodep : m_toggleps) {
|
||||
// nodep->backp() is null if we already detected it's a duplicate and unlinked it.
|
||||
@@ -60,10 +57,10 @@ private:
|
||||
// This prevents making chains where a->b, then c->d, then b->c, as we'll
|
||||
// find a->b, a->c, a->d directly.
|
||||
while (true) {
|
||||
const auto dupit = hashed.findDuplicate(nodep->origp());
|
||||
if (dupit == hashed.end()) break;
|
||||
const auto dupit = dupFinder.findDuplicate(nodep->origp());
|
||||
if (dupit == dupFinder.end()) break;
|
||||
//
|
||||
AstNode* duporigp = hashed.iteratorNodep(dupit);
|
||||
AstNode* duporigp = dupit->second;
|
||||
// Note hashed will point to the original variable (what's
|
||||
// duplicated), not the covertoggle, but we need to get back to the
|
||||
// covertoggle which is immediately above, so:
|
||||
@@ -82,7 +79,7 @@ private:
|
||||
removep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(removep), removep);
|
||||
// Remove node from comparison so don't hit it again
|
||||
hashed.erase(dupit);
|
||||
dupFinder.erase(dupit);
|
||||
++m_statToggleJoins;
|
||||
}
|
||||
}
|
||||
|
||||
+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(
|
||||
@@ -351,7 +351,7 @@ private:
|
||||
new AstVarRef(nodep->fileline(), setvscp, VAccess::READ),
|
||||
nullptr, nullptr);
|
||||
UINFO(9, " Created " << postLogicp << endl);
|
||||
finalp->addBodysp(postLogicp);
|
||||
finalp->addStmtp(postLogicp);
|
||||
finalp->user3p(setvscp); // Remember IF's vset variable
|
||||
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
+2
-2
@@ -52,7 +52,7 @@ private:
|
||||
UINFO(6, " Deep " << nodep << endl);
|
||||
// if (debug() >= 9) nodep->dumpTree(cout, "deep:");
|
||||
|
||||
string newvarname = (string("__Vdeeptemp") + cvtToStr(m_modp->varNumGetInc()));
|
||||
const string newvarname = (string("__Vdeeptemp") + cvtToStr(m_modp->varNumGetInc()));
|
||||
AstVar* varp
|
||||
= new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep());
|
||||
UASSERT_OBJ(m_cfuncp, nodep, "Deep expression not under a function");
|
||||
@@ -128,7 +128,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);
|
||||
}
|
||||
|
||||
+14
-10
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+47
-98
@@ -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,39 @@ 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);
|
||||
|
||||
// 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->";
|
||||
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 (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->"
|
||||
return "this";
|
||||
} else if (!m_modSingleton && relativeRefOk && 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 +107,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 +121,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 +129,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);
|
||||
@@ -252,16 +205,17 @@ 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()) { nodep->selfPointer(descopedSelfPointer(scopep)); }
|
||||
nodep->varScopep(nullptr);
|
||||
UINFO(9, " refout " << nodep << endl);
|
||||
}
|
||||
@@ -270,24 +224,20 @@ private:
|
||||
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();
|
||||
nodep->selfPointer(descopedSelfPointer(scopep));
|
||||
// Can't do this, as we may have more calls later
|
||||
// nodep->funcp()->scopep(nullptr);
|
||||
}
|
||||
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 +266,6 @@ public:
|
||||
|
||||
void V3Descope::descopeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
v3Global.assertScoped(false);
|
||||
{ DescopeVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("descope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Hashed common code into functions
|
||||
//
|
||||
// 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 "V3DupFinder.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3File.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
//######################################################################
|
||||
// V3DupFinder class functions
|
||||
|
||||
V3DupFinder::iterator V3DupFinder::findDuplicate(AstNode* nodep, V3DupFinderUserSame* checkp) {
|
||||
const auto& er = equal_range(m_hasher(nodep));
|
||||
for (iterator it = er.first; it != er.second; ++it) {
|
||||
AstNode* const node2p = it->second;
|
||||
if (nodep == node2p) continue; // Same node is not a duplicate
|
||||
if (checkp && !checkp->isSame(nodep, node2p)) continue; // User says it is not a duplicate
|
||||
if (!nodep->sameTree(node2p)) continue; // Not the same trees
|
||||
// Found duplicate!
|
||||
return it;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
void V3DupFinder::dumpFile(const string& filename, bool tree) {
|
||||
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;
|
||||
|
||||
V3Hash lasthash;
|
||||
int num_in_bucket = 0;
|
||||
for (auto it = cbegin(); true; ++it) {
|
||||
if (it == cend() || lasthash != it->first) {
|
||||
if (it != cend()) lasthash = it->first;
|
||||
if (num_in_bucket) {
|
||||
if (dist.find(num_in_bucket) == dist.end()) {
|
||||
dist.emplace(num_in_bucket, 1);
|
||||
} else {
|
||||
++dist[num_in_bucket];
|
||||
}
|
||||
}
|
||||
num_in_bucket = 0;
|
||||
}
|
||||
if (it == cend()) break;
|
||||
num_in_bucket++;
|
||||
}
|
||||
*logp << "\n*** STATS:\n\n";
|
||||
*logp << " #InBucket Occurrences\n";
|
||||
for (const auto& i : dist) {
|
||||
*logp << " " << std::setw(9) << i.first << " " << std::setw(12) << i.second << '\n';
|
||||
}
|
||||
|
||||
*logp << "\n*** Dump:\n\n";
|
||||
for (const auto& it : *this) {
|
||||
if (lasthash != it.first) {
|
||||
lasthash = it.first;
|
||||
*logp << " " << it.first << '\n';
|
||||
}
|
||||
*logp << "\t" << it.second << '\n';
|
||||
// Dumping the entire tree may make nearly N^2 sized dumps,
|
||||
// because the nodes under this one may also be in the hash table!
|
||||
if (tree) it.second->dumpTree(*logp, " ");
|
||||
}
|
||||
}
|
||||
|
||||
void V3DupFinder::dumpFilePrefixed(const string& nameComment, bool tree) {
|
||||
if (v3Global.opt.dumpTree()) { //
|
||||
dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Hash AST trees to find duplicates
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2005-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
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Datastructure for finding duplicate AstNode trees via hashing
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3DUPFINDER_H_
|
||||
#define VERILATOR_V3DUPFINDER_H_
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Hasher.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
//============================================================================
|
||||
|
||||
struct V3DupFinderUserSame {
|
||||
// Functor for V3DupFinder::findDuplicate
|
||||
virtual bool isSame(AstNode*, AstNode*) = 0;
|
||||
V3DupFinderUserSame() = default;
|
||||
virtual ~V3DupFinderUserSame() = default;
|
||||
};
|
||||
|
||||
// This really is just a multimap from 'node hash' to 'node pointer', with some minor extensions.
|
||||
class V3DupFinder final : private std::multimap<V3Hash, AstNode*> {
|
||||
using Super = std::multimap<V3Hash, AstNode*>;
|
||||
|
||||
// MEMBERS
|
||||
const V3Hasher m_hasher;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
V3DupFinder(){};
|
||||
~V3DupFinder() = default;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// Expose minimal set of superclass interface
|
||||
using Super::begin;
|
||||
using Super::cbegin;
|
||||
using Super::cend;
|
||||
using Super::clear;
|
||||
using Super::const_iterator;
|
||||
using Super::empty;
|
||||
using Super::end;
|
||||
using Super::erase;
|
||||
using Super::iterator;
|
||||
|
||||
// Insert node into data structure
|
||||
iterator insert(AstNode* nodep) { return emplace(m_hasher(nodep), nodep); }
|
||||
|
||||
// Return duplicate, if one was inserted, with optional user check for sameness
|
||||
iterator findDuplicate(AstNode* nodep, V3DupFinderUserSame* checkp = nullptr);
|
||||
|
||||
// Dump for debug
|
||||
void dumpFile(const string& filename, bool tree);
|
||||
void dumpFilePrefixed(const string& nameComment, bool tree = false);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
+211
-3036
File diff suppressed because it is too large
Load Diff
@@ -28,7 +28,9 @@
|
||||
class V3EmitC final {
|
||||
public:
|
||||
static void emitc();
|
||||
static void emitcConstPool();
|
||||
static void emitcInlines();
|
||||
static void emitcModel();
|
||||
static void emitcSyms(bool dpiHdrOnly = false);
|
||||
static void emitcTrace();
|
||||
static void emitcFiles();
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
// -*- 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"
|
||||
|
||||
//######################################################################
|
||||
// EmitCBaseVisitor implementation
|
||||
|
||||
string EmitCBaseVisitor::funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) {
|
||||
modp = modp ? modp : VN_CAST(nodep->user4p(), NodeModule);
|
||||
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 ";
|
||||
AstNodeModule* modp = VN_CAST(nodep->user4p(), NodeModule);
|
||||
args += prefixNameProtect(modp);
|
||||
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->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);
|
||||
if (funcp->slow()) puts(" VL_ATTR_COLD");
|
||||
puts(";\n");
|
||||
if (!funcp->ifdef().empty()) puts("#endif // " + funcp->ifdef() + "\n");
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, const string& prefixIfImp, 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, prefixIfImp, asRef));
|
||||
puts(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitModCUse(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);
|
||||
}
|
||||
+20
-43
@@ -43,6 +43,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 +53,35 @@ 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;
|
||||
}
|
||||
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 isConstPoolMod(AstNode* modp) {
|
||||
return modp == v3Global.rootp()->constPoolp()->modp();
|
||||
}
|
||||
|
||||
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, const string& prefixIfImp, bool asRef = false);
|
||||
void emitModCUse(AstNodeModule* modp, VUseType useType);
|
||||
|
||||
// CONSTRUCTORS
|
||||
EmitCBaseVisitor() = default;
|
||||
virtual ~EmitCBaseVisitor() override = default;
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
// -*- 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 "V3EmitCBase.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3String.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
||||
//######################################################################
|
||||
// Const pool emitter
|
||||
|
||||
class EmitCConstPool final : EmitCBaseVisitor {
|
||||
// MEMBERS
|
||||
bool m_inUnpacked = false;
|
||||
uint32_t m_unpackedWord = 0;
|
||||
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_heavy.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(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
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_END
|
||||
|
||||
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();
|
||||
m_outFileSize += 1;
|
||||
if (num.isString()) {
|
||||
// Note: putsQuoted does not track indentation, so we use this instead
|
||||
puts("\"");
|
||||
puts(num.toString());
|
||||
puts("\"");
|
||||
m_outFileSize += 9;
|
||||
} else if (dtypep->isWide()) {
|
||||
const uint32_t size = dtypep->widthWords();
|
||||
m_outFileSize += size - 1;
|
||||
// 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->isQuad()) {
|
||||
ofp()->printf("0x%016" PRIx64, static_cast<uint64_t>(num.toUQuad()));
|
||||
} else if (dtypep->widthMin() > 16) {
|
||||
ofp()->printf("0x%08" PRIx32, num.toUInt());
|
||||
} else if (dtypep->widthMin() > 8) {
|
||||
ofp()->printf("0x%04" PRIx32, num.toUInt());
|
||||
} else {
|
||||
ofp()->printf("0x%02" PRIx32, num.toUInt());
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
+1066
File diff suppressed because it is too large
Load Diff
+1257
File diff suppressed because it is too large
Load Diff
+1
-13
@@ -30,32 +30,20 @@ class EmitCInlines final : EmitCBaseVisitor {
|
||||
// STATE
|
||||
|
||||
// METHODS
|
||||
void checkHeavy(AstNode* nodep) {
|
||||
if (nodep->isHeavy()) v3Global.needHeavy(true);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstClass* nodep) override {
|
||||
checkHeavy(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCNew* nodep) override {
|
||||
checkHeavy(nodep);
|
||||
if (v3Global.opt.savable())
|
||||
v3warn(E_UNSUPPORTED, "Unsupported: --savable with dynamic new");
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstDumpCtl* nodep) override {
|
||||
checkHeavy(nodep);
|
||||
if (v3Global.opt.trace()) v3Global.needTraceDumper(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
checkHeavy(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
explicit EmitCInlines(AstNetlist* nodep) { iterate(nodep); }
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ public:
|
||||
private:
|
||||
// MAIN METHOD
|
||||
void emitInt() {
|
||||
string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__main.cpp";
|
||||
const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__main.cpp";
|
||||
newCFile(filename, false /*slow*/, true /*source*/);
|
||||
V3OutCFile cf(filename);
|
||||
m_ofp = &cf;
|
||||
|
||||
+6
-7
@@ -64,7 +64,7 @@ class CMakeEmitter final {
|
||||
|
||||
static void cmake_set(std::ofstream& of, const string& name, const string& value,
|
||||
const string& cache_type = "", const string& docstring = "") {
|
||||
string raw_value = "\"" + value + "\"";
|
||||
const string raw_value = "\"" + value + "\"";
|
||||
cmake_set_raw(of, name, raw_value, cache_type, docstring);
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ class CMakeEmitter final {
|
||||
static void emitOverallCMake() {
|
||||
const std::unique_ptr<std::ofstream> of(
|
||||
V3File::new_ofstream(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".cmake"));
|
||||
string name = v3Global.opt.prefix();
|
||||
const string name = v3Global.opt.prefix();
|
||||
|
||||
*of << "# Verilated -*- CMake -*-\n";
|
||||
*of << "# DESCR"
|
||||
@@ -113,6 +113,10 @@ class CMakeEmitter final {
|
||||
cmake_set_raw(*of, name + "_COVERAGE", v3Global.opt.coverage() ? "1" : "0");
|
||||
*of << "# Threaded output mode? 0/1/N threads (from --threads)\n";
|
||||
cmake_set_raw(*of, name + "_THREADS", cvtToStr(v3Global.opt.threads()));
|
||||
*of << "# Threaded tracing output mode? 0/1/N threads (from --trace-threads)\n";
|
||||
cmake_set_raw(*of, name + "_TRACE_THREADS", cvtToStr(v3Global.opt.traceThreads()));
|
||||
*of << "# Struct output mode? 0/1 (from --trace-structs)\n";
|
||||
cmake_set_raw(*of, name + "_TRACE_STRUCTS", cvtToStr(v3Global.opt.traceStructs()));
|
||||
*of << "# VCD Tracing output mode? 0/1 (from --trace)\n";
|
||||
cmake_set_raw(*of, name + "_TRACE_VCD",
|
||||
(v3Global.opt.trace() && (v3Global.opt.traceFormat() == TraceFormat::VCD))
|
||||
@@ -167,11 +171,6 @@ class CMakeEmitter final {
|
||||
global.emplace_back("${VERILATOR_ROOT}/include/" + v3Global.opt.traceSourceBase()
|
||||
+ "_c.cpp");
|
||||
if (v3Global.opt.systemC()) {
|
||||
if (v3Global.opt.traceFormat() != TraceFormat::VCD) {
|
||||
v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: This trace format is not supported in SystemC, "
|
||||
"use VCD format.");
|
||||
}
|
||||
global.emplace_back("${VERILATOR_ROOT}/include/" + v3Global.opt.traceSourceLang()
|
||||
+ ".cpp");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,629 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Emit C++ for model entry point class
|
||||
//
|
||||
// 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 "V3EmitCFunc.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
class EmitCModel final : public EmitCFunc {
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC;
|
||||
|
||||
void putSectionDelimiter(const string& name) {
|
||||
puts("\n");
|
||||
puts("//============================================================\n");
|
||||
puts("// " + name + "\n");
|
||||
}
|
||||
|
||||
void emitHeader(AstNodeModule* modp) {
|
||||
UASSERT(!m_ofp, "Output file should not be open");
|
||||
|
||||
const string filename = v3Global.opt.makeDir() + "/" + topClassName() + ".h";
|
||||
newCFile(filename, /* slow: */ false, /* source: */ false);
|
||||
m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) : new V3OutCFile(filename);
|
||||
|
||||
ofp()->putsHeader();
|
||||
puts("// DESCRIPTION: Verilator output: Primary model header\n");
|
||||
puts("//\n");
|
||||
puts("// This header should be included by all source files instantiating the design.\n");
|
||||
puts("// The class here is then constructed to instantiate the design.\n");
|
||||
puts("// See the Verilator manual for examples.\n");
|
||||
|
||||
ofp()->putsGuard();
|
||||
|
||||
// Include files
|
||||
puts("\n");
|
||||
ofp()->putsIntTopInclude();
|
||||
puts("#include \"verilated_heavy.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");
|
||||
if (v3Global.dpi()) puts("#include \"svdpi.h\"\n");
|
||||
|
||||
// Declare foreign instances up front to make C++ happy
|
||||
puts("\n");
|
||||
puts("class " + symClassName() + ";\n");
|
||||
puts("class " + prefixNameProtect(modp) + ";\n"); // For rootp pointer only
|
||||
if (v3Global.opt.trace()) puts("class " + v3Global.opt.traceClassLang() + ";\n");
|
||||
emitModCUse(modp, VUseType::INT_FWD_CLASS); // Note: This is needed for cell forwarding
|
||||
|
||||
puts("\n");
|
||||
|
||||
puts("// This class is the main interface to the Verilated model\n");
|
||||
if (optSystemC()) {
|
||||
puts("SC_MODULE(" + topClassName() + ") {\n");
|
||||
} else {
|
||||
puts("class " + topClassName() + " VL_NOT_FINAL {\n");
|
||||
}
|
||||
ofp()->resetPrivate();
|
||||
ofp()->putsPrivate(true); // private:
|
||||
|
||||
puts("// Symbol table holding complete model state (owned by this class)\n");
|
||||
puts(symClassName() + "* const vlSymsp;\n");
|
||||
|
||||
puts("\n");
|
||||
ofp()->putsPrivate(false); // public:
|
||||
// User accessible IO
|
||||
puts("\n// PORTS\n"
|
||||
"// The application code writes and reads these signals to\n"
|
||||
"// propagate new values into/out from the Verilated model.\n");
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) {
|
||||
if (varp->isPrimaryIO()) { //
|
||||
emitVarDecl(varp, "", /* asRef: */ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cells instantiated by the top level (for access to /* verilator public */)
|
||||
puts("\n// CELLS\n"
|
||||
"// Public to allow access to /* verilator public */ items.\n"
|
||||
"// Otherwise the application code can consider these internals.\n");
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstCell* const cellp = VN_CAST_CONST(nodep, Cell)) {
|
||||
puts(prefixNameProtect(cellp->modp()) + "* const " + cellp->nameProtect() + ";\n");
|
||||
}
|
||||
}
|
||||
|
||||
// root instance pointer (for access to internals, including public_flat items).
|
||||
puts("\n// Root instance pointer to allow access to model internals,\n"
|
||||
"// including inlined /* verilator public_flat_* */ items.\n");
|
||||
puts(prefixNameProtect(modp) + "* const rootp;\n");
|
||||
|
||||
puts("\n");
|
||||
ofp()->putsPrivate(false); // public:
|
||||
puts("// CONSTRUCTORS\n");
|
||||
if (optSystemC()) {
|
||||
puts("SC_CTOR(" + topClassName() + ");\n");
|
||||
puts("virtual ~" + topClassName() + "();\n");
|
||||
} else {
|
||||
puts("/// Construct the model; called by application code\n");
|
||||
puts("/// If contextp is null, then the model will use the default global "
|
||||
"context\n");
|
||||
puts("/// If name is \"\", then makes a wrapper with a\n");
|
||||
puts("/// single model invisible with respect to DPI scope names.\n");
|
||||
puts("explicit " + topClassName() + "(VerilatedContext* contextp,"
|
||||
+ " const char* name = \"TOP\");\n");
|
||||
puts("explicit " + topClassName() + "(const char* name = \"TOP\");\n");
|
||||
puts("/// Destroy the model; called (often implicitly) by application code\n");
|
||||
puts("virtual ~" + topClassName() + "();\n");
|
||||
}
|
||||
ofp()->putsPrivate(true);
|
||||
puts("VL_UNCOPYABLE(" + topClassName() + "); ///< Copying not allowed\n");
|
||||
|
||||
puts("\n");
|
||||
ofp()->putsPrivate(false); // public:
|
||||
puts("// API METHODS\n");
|
||||
string callEvalEndStep
|
||||
= (v3Global.needTraceDumper() && !optSystemC()) ? "eval_end_step(); " : "";
|
||||
if (optSystemC()) {
|
||||
ofp()->putsPrivate(true); ///< eval() is invoked by our sensitive() calls.
|
||||
}
|
||||
if (!optSystemC()) {
|
||||
puts("/// Evaluate the model. Application must call when inputs change.\n");
|
||||
}
|
||||
puts("void eval() { eval_step(); " + callEvalEndStep + "}\n");
|
||||
if (!optSystemC()) {
|
||||
puts("/// Evaluate when calling multiple units/models per time step.\n");
|
||||
}
|
||||
puts("void eval_step();\n");
|
||||
if (!optSystemC()) {
|
||||
puts("/// Evaluate at end of a timestep for tracing, when using eval_step().\n");
|
||||
puts("/// Application must call after all eval() and before time changes.\n");
|
||||
puts("void eval_end_step()");
|
||||
if (callEvalEndStep == "") {
|
||||
puts(" {}\n");
|
||||
} else {
|
||||
puts(";\n");
|
||||
}
|
||||
}
|
||||
if (!optSystemC()) {
|
||||
puts("/// Simulation complete, run final blocks. Application "
|
||||
"must call on completion.\n");
|
||||
}
|
||||
ofp()->putsPrivate(false); // public:
|
||||
puts("void final();\n");
|
||||
|
||||
if (v3Global.opt.trace()) {
|
||||
puts("/// Trace signals in the model; called by application code\n");
|
||||
puts("void trace(" + v3Global.opt.traceClassBase()
|
||||
+ "C* tfp, int levels, int options = 0);\n");
|
||||
if (optSystemC()) {
|
||||
puts("/// SC tracing; avoid overloaded virtual function lint warning\n");
|
||||
puts("virtual void trace(sc_trace_file* tfp) const override { "
|
||||
"::sc_core::sc_module::trace(tfp); }\n");
|
||||
}
|
||||
}
|
||||
|
||||
puts("/// Return current simulation context for this model.\n");
|
||||
puts("/// Used to get to e.g. simulation time via contextp()->time()\n");
|
||||
puts("VerilatedContext* contextp() const;\n");
|
||||
if (!optSystemC()) {
|
||||
puts("/// Retrieve name of this model instance (as passed to constructor).\n");
|
||||
puts("const char* name() const;\n");
|
||||
}
|
||||
|
||||
// Emit DPI export dispatcher declarations
|
||||
{
|
||||
std::vector<const AstCFunc*> funcps;
|
||||
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstCFunc* funcp = VN_CAST(nodep, CFunc)) {
|
||||
if (!funcp->dpiExportDispatcher()) continue;
|
||||
funcps.push_back(funcp);
|
||||
}
|
||||
}
|
||||
|
||||
stable_sort(funcps.begin(), funcps.end(), [](const AstNode* ap, const AstNode* bp) {
|
||||
return ap->name() < bp->name();
|
||||
});
|
||||
|
||||
if (!funcps.empty()) {
|
||||
puts("\n/// DPI Export functions\n");
|
||||
for (const AstCFunc* funcp : funcps) { emitCFuncDecl(funcp, modp); }
|
||||
}
|
||||
}
|
||||
|
||||
if (v3Global.opt.savable()) {
|
||||
puts("\n");
|
||||
puts("// Serialization functions\n");
|
||||
puts("friend VerilatedSerialize& operator<<(VerilatedSerialize& os, " //
|
||||
+ topClassName() + "& rhs);\n");
|
||||
puts("friend VerilatedDeserialize& operator>>(VerilatedDeserialize& os, "
|
||||
+ topClassName() + "& rhs);\n");
|
||||
}
|
||||
|
||||
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
|
||||
|
||||
ofp()->putsEndGuard();
|
||||
|
||||
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
||||
}
|
||||
|
||||
void emitConstructorImplementation(AstNodeModule* modp) {
|
||||
putSectionDelimiter("Constructors");
|
||||
|
||||
puts("\n");
|
||||
puts(topClassName() + "::" + topClassName());
|
||||
if (optSystemC()) {
|
||||
puts("(sc_module_name /* unused */)\n");
|
||||
puts(" : vlSymsp{new " + symClassName() + "(nullptr, name(), this)}\n");
|
||||
} else {
|
||||
puts(+"(VerilatedContext* _vcontextp__, const char* _vcname__)\n");
|
||||
puts(" : vlSymsp{new " + symClassName() + "(_vcontextp__, _vcname__, this)}\n");
|
||||
}
|
||||
|
||||
// Set up IO references
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) {
|
||||
if (varp->isPrimaryIO()) {
|
||||
const string protName = varp->nameProtect();
|
||||
puts(" , " + protName + "{vlSymsp->TOP." + protName + "}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup cell pointers
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstCell* const cellp = VN_CAST_CONST(nodep, Cell)) {
|
||||
const string protName = cellp->nameProtect();
|
||||
puts(" , " + protName + "{vlSymsp->TOP." + protName + "}\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Setup rootp root instance pointer,
|
||||
puts(" , rootp{&(vlSymsp->TOP)}\n");
|
||||
|
||||
puts("{\n");
|
||||
|
||||
if (optSystemC()) {
|
||||
// Create sensitivity list for when to evaluate the model.
|
||||
putsDecoration("// Sensitivities on all clocks and combinational inputs\n");
|
||||
puts("SC_METHOD(eval);\n");
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
|
||||
if (varp->isNonOutput() && (varp->isScSensitive() || varp->isUsedClock())) {
|
||||
int vects = 0;
|
||||
// This isn't very robust and may need cleanup for other data types
|
||||
for (AstUnpackArrayDType* arrayp
|
||||
= VN_CAST(varp->dtypeSkipRefp(), UnpackArrayDType);
|
||||
arrayp;
|
||||
arrayp = VN_CAST(arrayp->subDTypep()->skipRefp(), UnpackArrayDType)) {
|
||||
const int vecnum = vects++;
|
||||
UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp,
|
||||
"Should have swapped msb & lsb earlier.");
|
||||
const string ivar = string("__Vi") + cvtToStr(vecnum);
|
||||
puts("for (int __Vi" + cvtToStr(vecnum) + "="
|
||||
+ cvtToStr(arrayp->lo()));
|
||||
puts("; " + ivar + "<=" + cvtToStr(arrayp->hi()));
|
||||
puts("; ++" + ivar + ") {\n");
|
||||
}
|
||||
puts("sensitive << " + varp->nameProtect());
|
||||
for (int v = 0; v < vects; ++v) puts("[__Vi" + cvtToStr(v) + "]");
|
||||
puts(";\n");
|
||||
for (int v = 0; v < vects; ++v) puts("}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
puts("}\n");
|
||||
|
||||
if (!optSystemC()) {
|
||||
puts("\n");
|
||||
puts(topClassName() + "::" + topClassName() + "(const char* _vcname__)\n");
|
||||
puts(" : " + topClassName() + "(nullptr, _vcname__)\n{\n}\n");
|
||||
}
|
||||
}
|
||||
|
||||
void emitDestructorImplementation() {
|
||||
putSectionDelimiter("Destructor");
|
||||
|
||||
puts("\n");
|
||||
puts(topClassName() + "::~" + topClassName() + "() {\n");
|
||||
puts("delete vlSymsp;\n");
|
||||
puts("}\n");
|
||||
}
|
||||
|
||||
void emitSettleLoop(AstNodeModule* modp, bool initial) {
|
||||
const string topModNameProtected = prefixNameProtect(modp);
|
||||
|
||||
putsDecoration("// Evaluate till stable\n");
|
||||
puts("int __VclockLoop = 0;\n");
|
||||
puts("QData __Vchange = 1;\n");
|
||||
if (v3Global.opt.trace()) puts("vlSymsp->__Vm_activity = true;\n");
|
||||
puts("do {\n");
|
||||
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ ");
|
||||
puts(initial ? "Initial" : "Clock");
|
||||
puts(" loop\\n\"););\n");
|
||||
if (initial)
|
||||
puts(topModNameProtected + "__" + protect("_eval_settle") + "(&(vlSymsp->TOP));\n");
|
||||
puts(topModNameProtected + "__" + protect("_eval") + "(&(vlSymsp->TOP));\n");
|
||||
puts("if (VL_UNLIKELY(++__VclockLoop > " + cvtToStr(v3Global.opt.convergeLimit())
|
||||
+ ")) {\n");
|
||||
puts("// About to fail, so enable debug to see what's not settling.\n");
|
||||
puts("// Note you must run make with OPT=-DVL_DEBUG for debug prints.\n");
|
||||
puts("int __Vsaved_debug = Verilated::debug();\n");
|
||||
puts("Verilated::debug(1);\n");
|
||||
puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request")
|
||||
+ "(&(vlSymsp->TOP));\n");
|
||||
puts("Verilated::debug(__Vsaved_debug);\n");
|
||||
puts("VL_FATAL_MT(");
|
||||
putsQuoted(protect(modp->fileline()->filename()));
|
||||
puts(", ");
|
||||
puts(cvtToStr(modp->fileline()->lineno()));
|
||||
puts(", \"\",\n");
|
||||
puts("\"Verilated model didn't ");
|
||||
if (initial) puts("DC ");
|
||||
puts("converge\\n\"\n");
|
||||
puts("\"- See https://verilator.org/warn/DIDNOTCONVERGE\");\n");
|
||||
puts("} else {\n");
|
||||
puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request")
|
||||
+ "(&(vlSymsp->TOP));\n");
|
||||
puts("}\n");
|
||||
puts("} while (VL_UNLIKELY(__Vchange));\n");
|
||||
}
|
||||
|
||||
void emitStandardMethods(AstNodeModule* modp) {
|
||||
UASSERT_OBJ(modp->isTop(), modp, "Attempting to emitWrapEval for non-top class");
|
||||
|
||||
const string topModNameProtected = prefixNameProtect(modp);
|
||||
const string selfDecl = "(" + topModNameProtected + "* vlSelf)";
|
||||
|
||||
putSectionDelimiter("Evaluation loop");
|
||||
|
||||
// Forward declarations
|
||||
puts("\n");
|
||||
puts("void " + topModNameProtected + "__" + protect("_eval_initial") + selfDecl + ";\n");
|
||||
puts("void " + topModNameProtected + "__" + protect("_eval_settle") + selfDecl + ";\n");
|
||||
puts("void " + topModNameProtected + "__" + protect("_eval") + selfDecl + ";\n");
|
||||
puts("QData " + topModNameProtected + "__" + protect("_change_request") + selfDecl
|
||||
+ ";\n");
|
||||
puts("#ifdef VL_DEBUG\n");
|
||||
puts("void " + topModNameProtected + "__" + protect("_eval_debug_assertions") + selfDecl
|
||||
+ ";\n");
|
||||
puts("#endif // VL_DEBUG\n");
|
||||
puts("void " + topModNameProtected + "__" + protect("_final") + selfDecl + ";\n");
|
||||
|
||||
// _eval_initial_loop
|
||||
puts("\nstatic void " + protect("_eval_initial_loop") + "(" + symClassVar() + ")"
|
||||
+ " {\n");
|
||||
puts("vlSymsp->__Vm_didInit = true;\n");
|
||||
puts(topModNameProtected + "__" + protect("_eval_initial") + "(&(vlSymsp->TOP));\n");
|
||||
emitSettleLoop(modp, /* initial: */ true);
|
||||
ensureNewLine();
|
||||
puts("}\n");
|
||||
|
||||
// ::eval_step
|
||||
puts("\nvoid " + topClassName() + "::eval_step() {\n");
|
||||
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate " + topClassName()
|
||||
+ "::eval_step\\n\"); );\n");
|
||||
puts("#ifdef VL_DEBUG\n");
|
||||
putsDecoration("// Debug assertions\n");
|
||||
puts(topModNameProtected + "__" + protect("_eval_debug_assertions")
|
||||
+ "(&(vlSymsp->TOP));\n");
|
||||
puts("#endif // VL_DEBUG\n");
|
||||
putsDecoration("// Initialize\n");
|
||||
puts("if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) " + protect("_eval_initial_loop")
|
||||
+ "(vlSymsp);\n");
|
||||
|
||||
if (v3Global.opt.threads() == 1) {
|
||||
uint32_t mtaskId = 0;
|
||||
putsDecoration("// MTask " + cvtToStr(mtaskId) + " start\n");
|
||||
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"MTask" + cvtToStr(mtaskId) + " starting\\n\"););\n");
|
||||
puts("Verilated::mtaskId(" + cvtToStr(mtaskId) + ");\n");
|
||||
}
|
||||
|
||||
if (v3Global.opt.mtasks() && v3Global.opt.profThreads()) {
|
||||
puts("if (VL_UNLIKELY((vlSymsp->_vm_contextp__->profThreadsStart() != "
|
||||
"vlSymsp->__Vm_profile_time_finished)\n");
|
||||
puts(" && (VL_TIME_Q() > vlSymsp->_vm_contextp__->profThreadsStart())\n");
|
||||
puts(" && (vlSymsp->_vm_contextp__->profThreadsWindow() >= 1))) {\n");
|
||||
// Within a profile (either starting, middle, or end)
|
||||
puts("if (vlSymsp->__Vm_profile_window_ct == 0) {\n"); // Opening file?
|
||||
// Start profile on this cycle. We'll capture a window worth, then
|
||||
// only analyze the next window worth. The idea is that the first window
|
||||
// capture will hit some cache-cold stuff (eg printf) but it'll be warm
|
||||
// by the time we hit the second window, we hope.
|
||||
puts("vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
|
||||
// "* 2" as first half is warmup, second half is collection
|
||||
puts("vlSymsp->__Vm_profile_window_ct = vlSymsp->_vm_contextp__->profThreadsWindow() "
|
||||
"* 2 "
|
||||
"+ "
|
||||
"1;\n");
|
||||
puts("}\n");
|
||||
puts("--(vlSymsp->__Vm_profile_window_ct);\n");
|
||||
puts("if (vlSymsp->__Vm_profile_window_ct == "
|
||||
"vlSymsp->_vm_contextp__->profThreadsWindow()) {\n");
|
||||
// This barrier record in every threads' profile demarcates the
|
||||
// cache-warm-up cycles before the barrier from the actual profile
|
||||
// cycles afterward.
|
||||
puts("vlSymsp->__Vm_threadPoolp->profileAppendAll(");
|
||||
puts("VlProfileRec(VlProfileRec::Barrier()));\n");
|
||||
puts("vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
|
||||
puts("}\n");
|
||||
puts("else if (vlSymsp->__Vm_profile_window_ct == 0) {\n");
|
||||
// Ending file.
|
||||
puts("vluint64_t elapsed = VL_RDTSC_Q() - vlSymsp->__Vm_profile_cycle_start;\n");
|
||||
puts("vlSymsp->__Vm_threadPoolp->profileDump(vlSymsp->_vm_contextp__->"
|
||||
"profThreadsFilename().c_str(), elapsed);\n");
|
||||
// This turns off the test to enter the profiling code, but still
|
||||
// allows the user to collect another profile by changing
|
||||
// profThreadsStart
|
||||
puts("vlSymsp->__Vm_profile_time_finished = "
|
||||
"vlSymsp->_vm_contextp__->profThreadsStart();\n");
|
||||
puts("vlSymsp->__Vm_profile_cycle_start = 0;\n");
|
||||
puts("}\n");
|
||||
puts("}\n");
|
||||
}
|
||||
|
||||
emitSettleLoop(modp, /* initial: */ false);
|
||||
if (v3Global.opt.threads() == 1) {
|
||||
puts("Verilated::endOfThreadMTask(vlSymsp->__Vm_evalMsgQp);\n");
|
||||
}
|
||||
if (v3Global.opt.threads()) puts("Verilated::endOfEval(vlSymsp->__Vm_evalMsgQp);\n");
|
||||
puts("}\n");
|
||||
|
||||
// ::eval_end_step
|
||||
if (v3Global.needTraceDumper() && !optSystemC()) {
|
||||
puts("\nvoid " + topClassName() + "::eval_end_step() {\n");
|
||||
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+eval_end_step " + topClassName()
|
||||
+ "::eval_end_step\\n\"); );\n");
|
||||
puts("#ifdef VM_TRACE\n");
|
||||
putsDecoration("// Tracing\n");
|
||||
// SystemC's eval loop deals with calling trace, not us
|
||||
puts("if (VL_UNLIKELY(vlSymsp->__Vm_dumping)) vlSymsp->_traceDump();\n");
|
||||
puts("#endif // VM_TRACE\n");
|
||||
puts("}\n");
|
||||
}
|
||||
|
||||
putSectionDelimiter("Invoke final blocks");
|
||||
// ::final
|
||||
puts("\nvoid " + topClassName() + "::final() {\n");
|
||||
puts(topModNameProtected + "__" + protect("_final") + "(&(vlSymsp->TOP));\n");
|
||||
puts("}\n");
|
||||
|
||||
putSectionDelimiter("Utilities");
|
||||
// ::contextp
|
||||
puts("\nVerilatedContext* " + topClassName() + "::contextp() const {\n");
|
||||
puts(/**/ "return vlSymsp->_vm_contextp__;\n");
|
||||
puts("}\n");
|
||||
|
||||
if (!optSystemC()) {
|
||||
// ::name
|
||||
puts("\nconst char* " + topClassName() + "::name() const {\n");
|
||||
puts(/**/ "return vlSymsp->name();\n");
|
||||
puts("}\n");
|
||||
}
|
||||
}
|
||||
|
||||
void emitTraceMethods(AstNodeModule* modp) {
|
||||
const string topModNameProtected = prefixNameProtect(modp);
|
||||
|
||||
putSectionDelimiter("Trace configuration");
|
||||
|
||||
// Forward declaration
|
||||
puts("\nvoid " + topModNameProtected + "__" + protect("traceInitTop") + "("
|
||||
+ topModNameProtected + "* vlSelf, " + v3Global.opt.traceClassBase()
|
||||
+ "* tracep);\n");
|
||||
|
||||
// Static helper function
|
||||
puts("\nstatic void " + protect("traceInit") + "(void* voidSelf, "
|
||||
+ v3Global.opt.traceClassBase() + "* tracep, uint32_t code) {\n");
|
||||
putsDecoration("// Callback from tracep->open()\n");
|
||||
puts(voidSelfAssign(modp));
|
||||
puts(symClassAssign());
|
||||
puts("if (!vlSymsp->_vm_contextp__->calcUnusedSigs()) {\n");
|
||||
puts("VL_FATAL_MT(__FILE__, __LINE__, __FILE__,\n");
|
||||
puts("\"Turning on wave traces requires Verilated::traceEverOn(true) call before time "
|
||||
"0.\");\n");
|
||||
puts("}\n");
|
||||
puts("vlSymsp->__Vm_baseCode = code;\n");
|
||||
puts("tracep->module(vlSymsp->name());\n");
|
||||
puts("tracep->scopeEscape(' ');\n");
|
||||
puts(topModNameProtected + "__" + protect("traceInitTop") + "(vlSelf, tracep);\n");
|
||||
puts("tracep->scopeEscape('.');\n"); // Restore so later traced files won't break
|
||||
puts("}\n");
|
||||
|
||||
// Forward declaration
|
||||
puts("\nvoid " + topModNameProtected + "__" + protect("traceRegister") + "("
|
||||
+ topModNameProtected + "* vlSelf, " + v3Global.opt.traceClassBase()
|
||||
+ "* tracep);\n");
|
||||
|
||||
// ::trace
|
||||
puts("\nvoid " + topClassName() + "::trace(");
|
||||
puts(v3Global.opt.traceClassBase() + "C* tfp, int, int) {\n");
|
||||
puts("tfp->spTrace()->addInitCb(&" + protect("traceInit") + ", &(vlSymsp->TOP));\n");
|
||||
puts(topModNameProtected + "__" + protect("traceRegister")
|
||||
+ "(&(vlSymsp->TOP), tfp->spTrace());\n");
|
||||
puts("}\n");
|
||||
}
|
||||
|
||||
void emitSerializationFunctions() {
|
||||
putSectionDelimiter("Model serialization");
|
||||
|
||||
puts("\nVerilatedSerialize& operator<<(VerilatedSerialize& os, " + topClassName()
|
||||
+ "& rhs) {\n");
|
||||
puts("Verilated::quiesce();\n");
|
||||
puts("rhs.vlSymsp->" + protect("__Vserialize") + "(os);\n");
|
||||
puts("return os;\n");
|
||||
puts("}\n");
|
||||
|
||||
puts("\nVerilatedDeserialize& operator>>(VerilatedDeserialize& os, " + topClassName()
|
||||
+ "& rhs) {\n");
|
||||
puts("Verilated::quiesce();\n");
|
||||
puts("rhs.vlSymsp->" + protect("__Vdeserialize") + "(os);\n");
|
||||
puts("return os;\n");
|
||||
puts("}\n");
|
||||
}
|
||||
|
||||
void emitImplementation(AstNodeModule* modp) {
|
||||
UASSERT(!m_ofp, "Output file should not be open");
|
||||
|
||||
const string filename = v3Global.opt.makeDir() + "/" + topClassName() + ".cpp";
|
||||
newCFile(filename, /* slow: */ false, /* source: */ true);
|
||||
m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) : new V3OutCFile(filename);
|
||||
|
||||
ofp()->putsHeader();
|
||||
puts("// DESCRIPTION: Verilator output: "
|
||||
"Model implementation (design independent parts)\n");
|
||||
|
||||
puts("\n");
|
||||
puts("#include \"" + topClassName() + ".h\"\n");
|
||||
puts("#include \"" + symClassName() + ".h\"\n");
|
||||
if (v3Global.opt.trace()) {
|
||||
puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n");
|
||||
}
|
||||
if (v3Global.dpi()) { //
|
||||
puts("#include \"verilated_dpi.h\"\n");
|
||||
}
|
||||
|
||||
emitConstructorImplementation(modp);
|
||||
emitDestructorImplementation();
|
||||
emitStandardMethods(modp);
|
||||
if (v3Global.opt.trace()) { emitTraceMethods(modp); }
|
||||
if (v3Global.opt.savable()) { emitSerializationFunctions(); }
|
||||
|
||||
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
||||
}
|
||||
|
||||
void emitDpiExportDispatchers(AstNodeModule* modp) {
|
||||
UASSERT(!m_ofp, "Output file should not be open");
|
||||
|
||||
// Emit DPI Export dispatchers
|
||||
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
AstCFunc* const funcp = VN_CAST(nodep, CFunc);
|
||||
if (!funcp || !funcp->dpiExportDispatcher()) continue;
|
||||
|
||||
if (splitNeeded()) {
|
||||
// Splitting file, so using parallel build.
|
||||
v3Global.useParallelBuild(true);
|
||||
// Close old file
|
||||
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
||||
}
|
||||
|
||||
if (!m_ofp) {
|
||||
const string filename = v3Global.opt.makeDir() + "/" + topClassName()
|
||||
+ "__Dpi_Export_" + cvtToStr(splitFilenumInc() - 1)
|
||||
+ ".cpp";
|
||||
newCFile(filename, /* slow: */ false, /* source: */ true);
|
||||
m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename)
|
||||
: new V3OutCFile(filename);
|
||||
m_lazyDecls.reset();
|
||||
m_ofp->putsHeader();
|
||||
puts(
|
||||
"// DESCRIPTION: Verilator output: Implementation of DPI export functions.\n");
|
||||
puts("//\n");
|
||||
puts("#include \"" + topClassName() + ".h\"\n");
|
||||
puts("#include \"" + symClassName() + ".h\"\n");
|
||||
puts("#include \"verilated_dpi.h\"\n");
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
iterate(funcp);
|
||||
}
|
||||
|
||||
if (m_ofp) { VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); }
|
||||
}
|
||||
|
||||
void main(AstNodeModule* modp) {
|
||||
m_modp = modp;
|
||||
emitHeader(modp);
|
||||
emitImplementation(modp);
|
||||
if (v3Global.dpi()) { emitDpiExportDispatchers(modp); }
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
|
||||
public:
|
||||
explicit EmitCModel(AstNetlist* netlistp) { main(netlistp->topModulep()); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// EmitC class functions
|
||||
|
||||
void V3EmitC::emitcModel() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ EmitCModel emit(v3Global.rootp()); }
|
||||
}
|
||||
+240
-155
@@ -83,9 +83,9 @@ class EmitCSyms final : EmitCBaseVisitor {
|
||||
};
|
||||
struct CmpDpi {
|
||||
bool operator()(const AstCFunc* lhsp, const AstCFunc* rhsp) const {
|
||||
if (lhsp->dpiImport() != rhsp->dpiImport()) {
|
||||
if (lhsp->dpiImportPrototype() != rhsp->dpiImportPrototype()) {
|
||||
// cppcheck-suppress comparisonOfFuncReturningBoolError
|
||||
return lhsp->dpiImport() < rhsp->dpiImport();
|
||||
return lhsp->dpiImportPrototype() < rhsp->dpiImportPrototype();
|
||||
}
|
||||
return lhsp->name() < rhsp->name();
|
||||
}
|
||||
@@ -125,7 +125,7 @@ class EmitCSyms final : EmitCBaseVisitor {
|
||||
&& !(VN_IS(nodep, CFunc)
|
||||
&& (VN_CAST(nodep, CFunc)->isConstructor()
|
||||
|| VN_CAST(nodep, CFunc)->isDestructor()))) {
|
||||
string rsvd = V3LanguageWords::isKeyword(nodep->name());
|
||||
const string rsvd = V3LanguageWords::isKeyword(nodep->name());
|
||||
if (rsvd != "") {
|
||||
// Generally V3Name should find all of these and throw SYMRSVDWORD.
|
||||
// We'll still check here because the compiler errors
|
||||
@@ -170,7 +170,12 @@ class EmitCSyms final : EmitCBaseVisitor {
|
||||
const auto scpit = m_vpiScopeCandidates.find(scp);
|
||||
if ((scpit != m_vpiScopeCandidates.end())
|
||||
&& (m_scopeNames.find(scp) == m_scopeNames.end())) {
|
||||
m_scopeNames.emplace(scpit->second.m_symName, scpit->second);
|
||||
const auto scopeNameit = m_scopeNames.find(scpit->second.m_symName);
|
||||
if (scopeNameit == m_scopeNames.end()) {
|
||||
m_scopeNames.emplace(scpit->second.m_symName, scpit->second);
|
||||
} else {
|
||||
scopeNameit->second.m_type = scpit->second.m_type;
|
||||
}
|
||||
}
|
||||
string::size_type pos = scp.rfind("__DOT__");
|
||||
if (pos == string::npos) {
|
||||
@@ -211,9 +216,9 @@ class EmitCSyms final : EmitCBaseVisitor {
|
||||
}
|
||||
// UINFO(9,"For "<<scopep->name()<<" - "<<varp->name()<<" Scp "<<scpName<<"
|
||||
// Var "<<varBase<<endl);
|
||||
string varBasePretty = AstNode::prettyName(varBase);
|
||||
string scpPretty = AstNode::prettyName(scpName);
|
||||
string scpSym = scopeSymString(scpName);
|
||||
const string varBasePretty = AstNode::prettyName(varBase);
|
||||
const string scpPretty = AstNode::prettyName(scpName);
|
||||
const string scpSym = scopeSymString(scpName);
|
||||
// UINFO(9," scnameins sp "<<scpName<<" sp "<<scpPretty<<" ss "<<scpSym<<endl);
|
||||
if (v3Global.opt.vpi()) varHierarchyScopes(scpName);
|
||||
if (m_scopeNames.find(scpSym) == m_scopeNames.end()) {
|
||||
@@ -233,20 +238,20 @@ class EmitCSyms final : EmitCBaseVisitor {
|
||||
++it) {
|
||||
if (it->second.m_type != "SCOPE_MODULE") continue;
|
||||
|
||||
string name = it->second.m_prettyName;
|
||||
if (name.substr(0, 4) == "TOP.") name.replace(0, 4, "");
|
||||
const string symName = it->second.m_symName;
|
||||
string above = symName;
|
||||
if (above.substr(0, 4) == "TOP.") above.replace(0, 4, "");
|
||||
|
||||
string above = name;
|
||||
while (!above.empty()) {
|
||||
string::size_type pos = above.rfind('.');
|
||||
string::size_type pos = above.rfind("__");
|
||||
if (pos == string::npos) break;
|
||||
above.resize(pos);
|
||||
if (m_vpiScopeHierarchy.find(above) != m_vpiScopeHierarchy.end()) {
|
||||
m_vpiScopeHierarchy[above].push_back(name);
|
||||
m_vpiScopeHierarchy[above].push_back(symName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_vpiScopeHierarchy[name] = std::vector<string>();
|
||||
m_vpiScopeHierarchy[symName] = std::vector<string>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,6 +278,7 @@ class EmitCSyms final : EmitCBaseVisitor {
|
||||
if (!m_dpiHdrOnly) emitDpiImp();
|
||||
}
|
||||
}
|
||||
virtual void visit(AstConstPool* nodep) override {} // Ignore
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
nameCheck(nodep);
|
||||
VL_RESTORER(m_modp);
|
||||
@@ -283,10 +289,11 @@ class EmitCSyms final : EmitCBaseVisitor {
|
||||
}
|
||||
virtual void visit(AstCellInline* nodep) override {
|
||||
if (v3Global.opt.vpi()) {
|
||||
string type = (nodep->origModName() == "__BEGIN__") ? "SCOPE_OTHER" : "SCOPE_MODULE";
|
||||
string name = nodep->scopep()->name() + "__DOT__" + nodep->name();
|
||||
string name_dedot = AstNode::dedotName(name);
|
||||
int timeunit = m_modp->timeunit().powerOfTen();
|
||||
const string type
|
||||
= (nodep->origModName() == "__BEGIN__") ? "SCOPE_OTHER" : "SCOPE_MODULE";
|
||||
const string name = nodep->scopep()->name() + "__DOT__" + nodep->name();
|
||||
const string name_dedot = AstNode::dedotName(name);
|
||||
const int timeunit = m_modp->timeunit().powerOfTen();
|
||||
m_vpiScopeCandidates.insert(
|
||||
std::make_pair(name, ScopeData(scopeSymString(name), name_dedot, timeunit, type)));
|
||||
}
|
||||
@@ -298,19 +305,19 @@ class EmitCSyms final : EmitCBaseVisitor {
|
||||
m_scopes.emplace_back(std::make_pair(nodep, m_modp));
|
||||
|
||||
if (v3Global.opt.vpi() && !nodep->isTop()) {
|
||||
string type = VN_IS(nodep->modp(), Package) ? "SCOPE_OTHER" : "SCOPE_MODULE";
|
||||
string name_dedot = AstNode::dedotName(nodep->shortName());
|
||||
int timeunit = m_modp->timeunit().powerOfTen();
|
||||
const string type = VN_IS(nodep->modp(), Package) ? "SCOPE_OTHER" : "SCOPE_MODULE";
|
||||
const string name_dedot = AstNode::dedotName(nodep->shortName());
|
||||
const int timeunit = m_modp->timeunit().powerOfTen();
|
||||
m_vpiScopeCandidates.insert(
|
||||
std::make_pair(nodep->name(), ScopeData(scopeSymString(nodep->name()), name_dedot,
|
||||
timeunit, type)));
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScopeName* nodep) override {
|
||||
string name = nodep->scopeSymName();
|
||||
const string name = nodep->scopeSymName();
|
||||
// UINFO(9,"scnameins sp "<<nodep->name()<<" sp "<<nodep->scopePrettySymName()
|
||||
// <<" ss"<<name<<endl);
|
||||
int timeunit = m_modp ? m_modp->timeunit().powerOfTen() : 0;
|
||||
const int timeunit = m_modp ? m_modp->timeunit().powerOfTen() : 0;
|
||||
if (m_scopeNames.find(name) == m_scopeNames.end()) {
|
||||
m_scopeNames.emplace(
|
||||
name, ScopeData(name, nodep->scopePrettySymName(), timeunit, "SCOPE_OTHER"));
|
||||
@@ -342,7 +349,7 @@ class EmitCSyms final : EmitCBaseVisitor {
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
nameCheck(nodep);
|
||||
if (nodep->dpiImport() || nodep->dpiExportWrapper()) m_dpis.push_back(nodep);
|
||||
if (nodep->dpiImportPrototype() || nodep->dpiExportDispatcher()) m_dpis.push_back(nodep);
|
||||
VL_RESTORER(m_cfuncp);
|
||||
{
|
||||
m_cfuncp = nodep;
|
||||
@@ -363,7 +370,7 @@ public:
|
||||
|
||||
void EmitCSyms::emitSymHdr() {
|
||||
UINFO(6, __FUNCTION__ << ": " << endl);
|
||||
string filename = v3Global.opt.makeDir() + "/" + symClassName() + ".h";
|
||||
const string filename = v3Global.opt.makeDir() + "/" + symClassName() + ".h";
|
||||
newCFile(filename, true /*slow*/, false /*source*/);
|
||||
|
||||
if (v3Global.opt.systemC()) {
|
||||
@@ -373,8 +380,7 @@ void EmitCSyms::emitSymHdr() {
|
||||
}
|
||||
|
||||
ofp()->putsHeader();
|
||||
puts("// DESCR"
|
||||
"IPTION: Verilator output: Symbol table internal header\n");
|
||||
puts("// DESCRIPTION: Verilator output: Symbol table internal header\n");
|
||||
puts("//\n");
|
||||
puts("// Internal details; most calling programs do not need this header,\n");
|
||||
puts("// unless using verilator public meta comments.\n");
|
||||
@@ -383,12 +389,14 @@ void EmitCSyms::emitSymHdr() {
|
||||
|
||||
puts("\n");
|
||||
ofp()->putsIntTopInclude();
|
||||
if (v3Global.needHeavy()) {
|
||||
puts("#include \"verilated_heavy.h\"\n");
|
||||
} else {
|
||||
puts("#include \"verilated.h\"\n");
|
||||
puts("#include \"verilated_heavy.h\"\n");
|
||||
if (v3Global.needTraceDumper()) {
|
||||
puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n");
|
||||
}
|
||||
|
||||
puts("\n// INCLUDE MODEL CLASS\n");
|
||||
puts("\n#include \"" + topClassName() + ".h\"\n");
|
||||
|
||||
puts("\n// INCLUDE MODULE CLASSES\n");
|
||||
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep;
|
||||
nodep = VN_CAST(nodep->nextp(), NodeModule)) {
|
||||
@@ -401,49 +409,61 @@ void EmitCSyms::emitSymHdr() {
|
||||
std::map<const string, int> types; // Remove duplicates and sort
|
||||
for (const auto& itr : m_scopeFuncs) {
|
||||
AstCFunc* funcp = itr.second.m_cfuncp;
|
||||
if (funcp->dpiExport()) {
|
||||
string cbtype = protect(v3Global.opt.prefix() + "__Vcb_" + funcp->cname() + "_t");
|
||||
if (funcp->dpiExportImpl()) {
|
||||
const string cbtype
|
||||
= protect(v3Global.opt.prefix() + "__Vcb_" + funcp->cname() + "_t");
|
||||
types["using " + cbtype + " = void (*) (" + cFuncArgs(funcp) + ");\n"] = 1;
|
||||
}
|
||||
}
|
||||
for (const auto& i : types) puts(i.first);
|
||||
}
|
||||
|
||||
puts("\n// SYMS CLASS\n");
|
||||
puts(string("class ") + symClassName() + " : public VerilatedSyms {\n");
|
||||
puts("\n// SYMS CLASS (contains all model state)\n");
|
||||
puts("class " + symClassName() + " final : public VerilatedSyms {\n");
|
||||
ofp()->putsPrivate(false); // public:
|
||||
|
||||
puts("\n// LOCAL STATE\n");
|
||||
// Must be before subcells, as constructor order needed before _vlCoverInsert.
|
||||
puts("const char* __Vm_namep;\n");
|
||||
puts("// INTERNAL STATE\n");
|
||||
puts(topClassName() + "* const __Vm_modelp;\n");
|
||||
|
||||
if (v3Global.needTraceDumper()) {
|
||||
// __Vm_dumperp is local, otherwise we wouldn't know what design's eval()
|
||||
// should call a global dumpperp
|
||||
puts("bool __Vm_dumping; // Dumping is active\n");
|
||||
puts("bool __Vm_dumping = false; // Dumping is active\n");
|
||||
puts("VerilatedMutex __Vm_dumperMutex; // Protect __Vm_dumperp\n");
|
||||
puts(v3Global.opt.traceClassLang()
|
||||
+ "* __Vm_dumperp VL_GUARDED_BY(__Vm_dumperMutex); /// Trace class for $dump*\n");
|
||||
+ "* __Vm_dumperp VL_GUARDED_BY(__Vm_dumperMutex) = nullptr;"
|
||||
" /// Trace class for $dump*\n");
|
||||
}
|
||||
if (v3Global.opt.trace()) {
|
||||
puts("bool __Vm_activity; ///< Used by trace routines to determine change occurred\n");
|
||||
puts("uint32_t __Vm_baseCode; "
|
||||
"///< Used by trace routines when tracing multiple models\n");
|
||||
puts("bool __Vm_activity = false;"
|
||||
" ///< Used by trace routines to determine change occurred\n");
|
||||
puts("uint32_t __Vm_baseCode = 0;"
|
||||
" ///< Used by trace routines when tracing multiple models\n");
|
||||
}
|
||||
puts("bool __Vm_didInit;\n");
|
||||
puts("bool __Vm_didInit = false;\n");
|
||||
|
||||
puts("\n// SUBCELL STATE\n");
|
||||
if (v3Global.opt.mtasks()) {
|
||||
puts("VlThreadPool* const __Vm_threadPoolp;\n");
|
||||
puts("bool __Vm_even_cycle = false;\n");
|
||||
|
||||
if (v3Global.opt.profThreads()) {
|
||||
// rdtsc() at current cycle start
|
||||
puts("vluint64_t __Vm_profile_cycle_start = 0;\n");
|
||||
// Time we finished analysis
|
||||
puts("vluint64_t __Vm_profile_time_finished = 0;\n");
|
||||
// Track our position in the cache warmup and actual profile window
|
||||
puts("vluint32_t __Vm_profile_window_ct = 0;\n");
|
||||
}
|
||||
}
|
||||
|
||||
puts("\n// MODULE INSTANCE STATE\n");
|
||||
for (const auto& i : m_scopes) {
|
||||
AstScope* scopep = i.first;
|
||||
AstNodeModule* modp = i.second;
|
||||
if (VN_IS(modp, Class)) continue;
|
||||
if (modp->isTop()) {
|
||||
ofp()->printf("%-30s ", (prefixNameProtect(modp) + "*").c_str());
|
||||
puts(protectIf(scopep->nameDotless() + "p", scopep->protect()) + ";\n");
|
||||
} else {
|
||||
ofp()->printf("%-30s ", (prefixNameProtect(modp) + "").c_str());
|
||||
puts(protectIf(scopep->nameDotless(), scopep->protect()) + ";\n");
|
||||
}
|
||||
const string name = prefixNameProtect(modp);
|
||||
ofp()->printf("%-30s ", name.c_str());
|
||||
puts(protectIf(scopep->nameDotless(), scopep->protect()) + ";\n");
|
||||
}
|
||||
|
||||
if (m_coverBins) {
|
||||
@@ -466,28 +486,30 @@ void EmitCSyms::emitSymHdr() {
|
||||
puts("VerilatedHierarchy __Vhier;\n");
|
||||
}
|
||||
|
||||
puts("\n// CREATORS\n");
|
||||
puts(symClassName() + "(VerilatedContext* contextp, " + topClassName()
|
||||
+ "* topp, const char* namep);\n");
|
||||
puts("\n// CONSTRUCTORS\n");
|
||||
puts(symClassName() + "(VerilatedContext* contextp, const char* namep, " + topClassName()
|
||||
+ "* modelp);\n");
|
||||
puts(string("~") + symClassName() + "();\n");
|
||||
|
||||
for (const auto& i : m_usesVfinal) {
|
||||
puts("void " + symClassName() + "_" + cvtToStr(i.first) + "(");
|
||||
if (i.second) {
|
||||
puts("int __Vfinal");
|
||||
} else {
|
||||
puts(topClassName() + "* topp");
|
||||
}
|
||||
if (i.second) { puts("int __Vfinal"); }
|
||||
puts(");\n");
|
||||
}
|
||||
|
||||
puts("\n// METHODS\n");
|
||||
puts("inline const char* name() { return __Vm_namep; }\n");
|
||||
puts("const char* name() { return TOP.name(); }\n");
|
||||
|
||||
if (v3Global.needTraceDumper()) {
|
||||
if (!optSystemC()) puts("void _traceDump();\n");
|
||||
puts("void _traceDumpOpen();\n");
|
||||
puts("void _traceDumpClose();\n");
|
||||
}
|
||||
|
||||
if (v3Global.opt.savable()) {
|
||||
puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n");
|
||||
puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n");
|
||||
}
|
||||
puts("\n");
|
||||
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
|
||||
|
||||
ofp()->putsEndGuard();
|
||||
@@ -525,20 +547,12 @@ void EmitCSyms::checkSplit(bool usesVfinal) {
|
||||
}
|
||||
|
||||
m_ofpBase->puts(symClassName() + "_" + cvtToStr(m_funcNum) + "(");
|
||||
if (usesVfinal) {
|
||||
m_ofpBase->puts("__Vfinal");
|
||||
} else {
|
||||
m_ofpBase->puts("topp");
|
||||
}
|
||||
if (usesVfinal) { m_ofpBase->puts("__Vfinal"); }
|
||||
m_ofpBase->puts(");\n");
|
||||
|
||||
emitSymImpPreamble();
|
||||
puts("void " + symClassName() + "::" + symClassName() + "_" + cvtToStr(m_funcNum) + "(");
|
||||
if (usesVfinal) {
|
||||
puts("int __Vfinal");
|
||||
} else {
|
||||
puts(topClassName() + "* topp");
|
||||
}
|
||||
if (usesVfinal) { puts("int __Vfinal"); }
|
||||
puts(") {\n");
|
||||
}
|
||||
|
||||
@@ -550,21 +564,32 @@ void EmitCSyms::emitSymImpPreamble() {
|
||||
|
||||
// Includes
|
||||
puts("#include \"" + symClassName() + ".h\"\n");
|
||||
puts("#include \"" + topClassName() + ".h\"\n");
|
||||
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep;
|
||||
nodep = VN_CAST(nodep->nextp(), NodeModule)) {
|
||||
if (VN_IS(nodep, Class)) continue; // Class included earlier
|
||||
puts("#include \"" + prefixNameProtect(nodep) + ".h\"\n");
|
||||
}
|
||||
puts("\n");
|
||||
// Declarations for DPI Export implementation functions
|
||||
bool needsNewLine = false;
|
||||
for (const auto& pair : m_scopeFuncs) {
|
||||
const AstCFunc* const funcp = pair.second.m_cfuncp;
|
||||
if (!funcp->dpiExportImpl()) continue;
|
||||
emitCFuncDecl(funcp, pair.second.m_modp);
|
||||
needsNewLine = true;
|
||||
}
|
||||
if (needsNewLine) puts("\n");
|
||||
}
|
||||
|
||||
void EmitCSyms::emitScopeHier(bool destroy) {
|
||||
if (v3Global.opt.vpi()) {
|
||||
string verb = destroy ? "Tear down" : "Set up";
|
||||
string method = destroy ? "remove" : "add";
|
||||
const string verb = destroy ? "Tear down" : "Set up";
|
||||
const string method = destroy ? "remove" : "add";
|
||||
puts("\n// " + verb + " scope hierarchy\n");
|
||||
for (ScopeNames::const_iterator it = m_scopeNames.begin(); it != m_scopeNames.end();
|
||||
++it) {
|
||||
string name = it->second.m_prettyName;
|
||||
const string name = it->second.m_prettyName;
|
||||
if (it->first == "TOP") continue;
|
||||
if ((name.find('.') == string::npos) && (it->second.m_type == "SCOPE_MODULE")) {
|
||||
puts("__Vhier." + method + "(0, &" + protect("__Vscope_" + it->second.m_symName)
|
||||
@@ -575,8 +600,8 @@ void EmitCSyms::emitScopeHier(bool destroy) {
|
||||
for (auto it = m_vpiScopeHierarchy.cbegin(); it != m_vpiScopeHierarchy.cend(); ++it) {
|
||||
for (ScopeNameList::const_iterator lit = it->second.begin(); lit != it->second.end();
|
||||
++lit) {
|
||||
string fromname = scopeSymString(it->first);
|
||||
string toname = scopeSymString(*lit);
|
||||
const string fromname = scopeSymString(it->first);
|
||||
const string toname = scopeSymString(*lit);
|
||||
const auto from = vlstd::as_const(m_scopeNames).find(fromname);
|
||||
const auto to = vlstd::as_const(m_scopeNames).find(toname);
|
||||
UASSERT(from != m_scopeNames.end(), fromname + " not in m_scopeNames");
|
||||
@@ -592,7 +617,7 @@ void EmitCSyms::emitScopeHier(bool destroy) {
|
||||
|
||||
void EmitCSyms::emitSymImp() {
|
||||
UINFO(6, __FUNCTION__ << ": " << endl);
|
||||
string filename = v3Global.opt.makeDir() + "/" + symClassName() + ".cpp";
|
||||
const string filename = v3Global.opt.makeDir() + "/" + symClassName() + ".cpp";
|
||||
AstCFile* cfilep = newCFile(filename, true /*slow*/, true /*source*/);
|
||||
cfilep->support(true);
|
||||
|
||||
@@ -605,89 +630,120 @@ void EmitCSyms::emitSymImp() {
|
||||
m_ofpBase = m_ofp;
|
||||
emitSymImpPreamble();
|
||||
|
||||
// puts("\n// GLOBALS\n");
|
||||
|
||||
puts("\n");
|
||||
|
||||
if (v3Global.opt.savable()) {
|
||||
puts("\n");
|
||||
for (int de = 0; de < 2; ++de) {
|
||||
string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize";
|
||||
string funcname = de ? "__Vdeserialize" : "__Vserialize";
|
||||
string op = de ? ">>" : "<<";
|
||||
const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize";
|
||||
const string funcname = de ? "__Vdeserialize" : "__Vserialize";
|
||||
const string op = de ? ">>" : "<<";
|
||||
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
||||
puts("void " + symClassName() + "::" + protect(funcname) + "(" + classname
|
||||
+ "& os) {\n");
|
||||
puts("// LOCAL STATE\n");
|
||||
// __Vm_namep presumably already correct
|
||||
puts("// Internal state\n");
|
||||
if (v3Global.opt.trace()) puts("os" + op + "__Vm_activity;\n");
|
||||
puts("os" + op + "__Vm_didInit;\n");
|
||||
puts("// SUBCELL STATE\n");
|
||||
for (std::vector<ScopeModPair>::iterator it = m_scopes.begin(); it != m_scopes.end();
|
||||
++it) {
|
||||
AstScope* scopep = it->first;
|
||||
AstNodeModule* modp = it->second;
|
||||
if (!modp->isTop()) {
|
||||
puts(protectIf(scopep->nameDotless(), scopep->protect()) + "."
|
||||
+ protect(funcname) + "(os);\n");
|
||||
}
|
||||
puts("os " + op + " __Vm_didInit;\n");
|
||||
puts("// Module instance state\n");
|
||||
for (const auto& pair : m_scopes) {
|
||||
const AstScope* const scopep = pair.first;
|
||||
puts(protectIf(scopep->nameDotless(), scopep->protect()) + "." + protect(funcname)
|
||||
+ "(os);\n");
|
||||
}
|
||||
puts("}\n");
|
||||
}
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
puts("\n");
|
||||
|
||||
puts("\n// FUNCTIONS\n");
|
||||
puts("// FUNCTIONS\n");
|
||||
// Destructor
|
||||
puts(symClassName() + "::~" + symClassName() + "()\n");
|
||||
puts("{\n");
|
||||
emitScopeHier(true);
|
||||
puts("}\n\n");
|
||||
puts(symClassName() + "::" + symClassName() + "(VerilatedContext* contextp, " + topClassName()
|
||||
+ "* topp, const char* namep)\n");
|
||||
puts(" // Setup locals\n");
|
||||
puts(" : VerilatedSyms{contextp}\n");
|
||||
puts(" , __Vm_namep(namep)\n"); // No leak, as gets destroyed when the top is destroyed
|
||||
if (v3Global.needTraceDumper()) {
|
||||
puts(" , __Vm_dumping(false)\n");
|
||||
puts(" , __Vm_dumperp(nullptr)\n");
|
||||
puts("#ifdef VM_TRACE\n");
|
||||
puts("if (__Vm_dumping) _traceDumpClose();\n");
|
||||
puts("#endif // VM_TRACE\n");
|
||||
}
|
||||
if (v3Global.opt.trace()) {
|
||||
puts(" , __Vm_activity(false)\n");
|
||||
puts(" , __Vm_baseCode(0)\n");
|
||||
if (v3Global.opt.mtasks()) { puts("delete __Vm_threadPoolp;\n"); }
|
||||
puts("}\n\n");
|
||||
|
||||
// Constructor
|
||||
puts(symClassName() + "::" + symClassName() + "(VerilatedContext* contextp, const char* namep,"
|
||||
+ topClassName() + "* modelp)\n");
|
||||
puts(" : VerilatedSyms{contextp}\n");
|
||||
puts(" // Setup internal state of the Syms class\n");
|
||||
puts(" , __Vm_modelp(modelp)\n");
|
||||
|
||||
if (v3Global.opt.mtasks()) {
|
||||
// TODO -- For now each model creates its own ThreadPool here,
|
||||
// and deletes it in the destructor. If A and B are each top level
|
||||
// modules, each creates a separate thread pool. This allows
|
||||
// A.eval() and B.eval() to run concurrently without any
|
||||
// interference -- so long as the physical machine has enough cores
|
||||
// to support both pools and all testbench threads.
|
||||
//
|
||||
// In the future, we might want to let the client provide a
|
||||
// threadpool to the constructor. This would allow two or more
|
||||
// models to share a single threadpool.
|
||||
//
|
||||
// For example: suppose models A and B are each compiled to run on
|
||||
// 4 threads. The client might create a single thread pool with 3
|
||||
// threads and pass it to both models. If the client can ensure that
|
||||
// A.eval() and B.eval() do NOT run concurrently, there will be no
|
||||
// contention for the threads. This mode is missing for now. (Is
|
||||
// there demand for such a setup?)
|
||||
//
|
||||
// Note we create N-1 threads in the thread pool. The thread
|
||||
// that calls eval() becomes the final Nth thread for the
|
||||
// duration of the eval call.
|
||||
puts(" , __Vm_threadPoolp(new VlThreadPool(_vm_contextp__, "
|
||||
+ cvtToStr(v3Global.opt.threads() - 1) + ", " + cvtToStr(v3Global.opt.profThreads())
|
||||
+ "))\n");
|
||||
}
|
||||
puts(" , __Vm_didInit(false)\n");
|
||||
puts(" // Setup submodule names\n");
|
||||
char comma = ',';
|
||||
|
||||
puts(" // Setup module instances\n");
|
||||
for (const auto& i : m_scopes) {
|
||||
AstScope* scopep = i.first;
|
||||
AstNodeModule* modp = i.second;
|
||||
const AstScope* const scopep = i.first;
|
||||
const AstNodeModule* const modp = i.second;
|
||||
puts(" , ");
|
||||
puts(protect(scopep->nameDotless()));
|
||||
if (modp->isTop()) {
|
||||
puts("(namep)\n");
|
||||
} else {
|
||||
puts(string(" ") + comma + " " + protect(scopep->nameDotless()));
|
||||
puts("(Verilated::catName(topp->name(), ");
|
||||
// The "." is added by catName
|
||||
puts("(Verilated::catName(namep, ");
|
||||
putsQuoted(protectWordsIf(scopep->prettyName(), scopep->protect()));
|
||||
puts("))\n");
|
||||
comma = ',';
|
||||
++m_numStmts;
|
||||
}
|
||||
++m_numStmts;
|
||||
}
|
||||
puts("{\n");
|
||||
|
||||
puts("// Pointer to top level\n");
|
||||
puts("TOPp = topp;\n");
|
||||
puts("// Configure time unit / time precision\n");
|
||||
if (!v3Global.rootp()->timeunit().isNone()) {
|
||||
puts("_vm_contextp__->timeunit(");
|
||||
puts(cvtToStr(v3Global.rootp()->timeunit().powerOfTen()));
|
||||
puts(");\n");
|
||||
}
|
||||
if (!v3Global.rootp()->timeprecision().isNone()) {
|
||||
puts("_vm_contextp__->timeprecision(");
|
||||
puts(cvtToStr(v3Global.rootp()->timeprecision().powerOfTen()));
|
||||
puts(");\n");
|
||||
}
|
||||
|
||||
puts("// Setup each module's pointers to their submodules\n");
|
||||
for (const auto& i : m_scopes) {
|
||||
AstScope* scopep = i.first;
|
||||
AstNodeModule* modp = i.second;
|
||||
if (!modp->isTop()) {
|
||||
const AstScope* const scopep = i.first;
|
||||
const AstNodeModule* const modp = i.second;
|
||||
if (const AstScope* const aboveScopep = scopep->aboveScopep()) {
|
||||
checkSplit(false);
|
||||
string arrow = scopep->name();
|
||||
string::size_type pos;
|
||||
while ((pos = arrow.find('.')) != string::npos) arrow.replace(pos, 1, "->");
|
||||
if (arrow.substr(0, 5) == "TOP->") arrow.replace(0, 5, "TOPp->");
|
||||
ofp()->puts(protectWordsIf(arrow, scopep->protect()));
|
||||
const string protName = protectWordsIf(scopep->name(), scopep->protect());
|
||||
if (VN_IS(modp, ClassPackage)) {
|
||||
// ClassPackage modules seem to be a bit out of place, so hard code...
|
||||
puts("TOP");
|
||||
} else {
|
||||
puts(protectIf(aboveScopep->nameDotless(), aboveScopep->protect()));
|
||||
}
|
||||
puts(".");
|
||||
puts(protName.substr(protName.rfind(".") + 1));
|
||||
puts(" = &");
|
||||
puts(protectIf(scopep->nameDotless(), scopep->protect()) + ";\n");
|
||||
++m_numStmts;
|
||||
@@ -695,19 +751,16 @@ void EmitCSyms::emitSymImp() {
|
||||
}
|
||||
|
||||
puts("// Setup each module's pointer back to symbol table (for public functions)\n");
|
||||
puts("TOPp->" + protect("__Vconfigure") + "(this, true);\n");
|
||||
for (const auto& i : m_scopes) {
|
||||
AstScope* scopep = i.first;
|
||||
AstNodeModule* modp = i.second;
|
||||
if (!modp->isTop()) {
|
||||
checkSplit(false);
|
||||
// first is used by AstCoverDecl's call to __vlCoverInsert
|
||||
bool first = !modp->user1();
|
||||
modp->user1(true);
|
||||
puts(protectIf(scopep->nameDotless(), scopep->protect()) + "."
|
||||
+ protect("__Vconfigure") + "(this, " + (first ? "true" : "false") + ");\n");
|
||||
++m_numStmts;
|
||||
}
|
||||
checkSplit(false);
|
||||
// first is used by AstCoverDecl's call to __vlCoverInsert
|
||||
const bool first = !modp->user1();
|
||||
modp->user1(true);
|
||||
puts(protectIf(scopep->nameDotless(), scopep->protect()) + "." + protect("__Vconfigure")
|
||||
+ "(this, " + (first ? "true" : "false") + ");\n");
|
||||
++m_numStmts;
|
||||
}
|
||||
|
||||
if (!m_scopeNames.empty()) { // Setup scope names
|
||||
@@ -737,13 +790,13 @@ void EmitCSyms::emitSymImp() {
|
||||
AstScopeName* scopep = it->second.m_scopep;
|
||||
AstCFunc* funcp = it->second.m_cfuncp;
|
||||
AstNodeModule* modp = it->second.m_modp;
|
||||
if (funcp->dpiExport()) {
|
||||
if (funcp->dpiExportImpl()) {
|
||||
checkSplit(true);
|
||||
puts(protect("__Vscope_" + scopep->scopeSymName()) + ".exportInsert(__Vfinal, ");
|
||||
putsQuoted(funcp->cname()); // Not protected - user asked for import/export
|
||||
puts(", (void*)(&");
|
||||
puts(prefixNameProtect(modp));
|
||||
puts("::");
|
||||
puts("__");
|
||||
puts(funcp->nameProtect());
|
||||
puts("));\n");
|
||||
++m_numStmts;
|
||||
@@ -753,10 +806,10 @@ void EmitCSyms::emitSymImp() {
|
||||
// Someday. For now public isn't common.
|
||||
for (auto it = m_scopeVars.begin(); it != m_scopeVars.end(); ++it) {
|
||||
checkSplit(true);
|
||||
AstNodeModule* modp = it->second.m_modp;
|
||||
AstScope* scopep = it->second.m_scopep;
|
||||
AstVar* varp = it->second.m_varp;
|
||||
//
|
||||
int pwidth = 1;
|
||||
int pdim = 0;
|
||||
int udim = 0;
|
||||
string bounds;
|
||||
@@ -768,6 +821,7 @@ void EmitCSyms::emitSymImp() {
|
||||
bounds += ",";
|
||||
bounds += cvtToStr(basicp->lo());
|
||||
pdim++;
|
||||
pwidth *= basicp->elements();
|
||||
}
|
||||
for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) {
|
||||
dtypep
|
||||
@@ -779,6 +833,7 @@ void EmitCSyms::emitSymImp() {
|
||||
bounds += cvtToStr(adtypep->right());
|
||||
if (VN_IS(dtypep, PackArrayDType)) {
|
||||
pdim++;
|
||||
pwidth *= adtypep->elementsConst();
|
||||
} else {
|
||||
udim++;
|
||||
}
|
||||
@@ -788,19 +843,21 @@ void EmitCSyms::emitSymImp() {
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
if (udim > 1 && (pdim && udim)) {
|
||||
// TODO: actually expose packed arrays as vpiRegArray
|
||||
if (pdim > 1 && udim == 0) {
|
||||
bounds = ", ";
|
||||
bounds += cvtToStr(pwidth - 1);
|
||||
bounds += ",0";
|
||||
pdim = 1;
|
||||
}
|
||||
if (pdim > 1 || udim > 1) {
|
||||
puts("//UNSUP "); // VerilatedImp can't deal with >2d or packed arrays
|
||||
}
|
||||
puts(protect("__Vscope_" + it->second.m_scopeName) + ".varInsert(__Vfinal,");
|
||||
putsQuoted(protect(it->second.m_varBasePretty));
|
||||
|
||||
std::string varName;
|
||||
if (modp->isTop()) {
|
||||
varName += (protectIf(scopep->nameDotless() + "p", scopep->protect()) + "->");
|
||||
} else {
|
||||
varName += (protectIf(scopep->nameDotless(), scopep->protect()) + ".");
|
||||
}
|
||||
varName += (protectIf(scopep->nameDotless(), scopep->protect()) + ".");
|
||||
|
||||
if (varp->isParam()) {
|
||||
varName += protect("var_" + varp->name());
|
||||
@@ -839,6 +896,34 @@ void EmitCSyms::emitSymImp() {
|
||||
}
|
||||
|
||||
m_ofpBase->puts("}\n");
|
||||
|
||||
if (v3Global.needTraceDumper()) {
|
||||
if (!optSystemC()) {
|
||||
puts("\nvoid " + symClassName() + "::_traceDump() {\n");
|
||||
// Caller checked for __Vm_dumperp non-nullptr
|
||||
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
|
||||
puts("__Vm_dumperp->dump(VL_TIME_Q());\n");
|
||||
puts("}\n");
|
||||
}
|
||||
|
||||
puts("\nvoid " + symClassName() + "::_traceDumpOpen() {\n");
|
||||
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
|
||||
puts("if (VL_UNLIKELY(!__Vm_dumperp)) {\n");
|
||||
puts("__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n");
|
||||
puts("__Vm_modelp->trace(__Vm_dumperp, 0, 0);\n");
|
||||
puts("std::string dumpfile = _vm_contextp__->dumpfileCheck();\n");
|
||||
puts("__Vm_dumperp->open(dumpfile.c_str());\n");
|
||||
puts("__Vm_dumping = true;\n");
|
||||
puts("}\n");
|
||||
puts("}\n");
|
||||
|
||||
puts("\nvoid " + symClassName() + "::_traceDumpClose() {\n");
|
||||
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
|
||||
puts("__Vm_dumping = false;\n");
|
||||
puts("VL_DO_CLEAR(delete __Vm_dumperp, __Vm_dumperp = nullptr);\n");
|
||||
puts("}\n");
|
||||
}
|
||||
|
||||
closeSplit();
|
||||
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
||||
}
|
||||
@@ -847,7 +932,7 @@ void EmitCSyms::emitSymImp() {
|
||||
|
||||
void EmitCSyms::emitDpiHdr() {
|
||||
UINFO(6, __FUNCTION__ << ": " << endl);
|
||||
string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.h";
|
||||
const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.h";
|
||||
AstCFile* cfilep = newCFile(filename, false /*slow*/, false /*source*/);
|
||||
cfilep->support(true);
|
||||
V3OutCFile hf(filename);
|
||||
@@ -871,13 +956,13 @@ void EmitCSyms::emitDpiHdr() {
|
||||
int firstExp = 0;
|
||||
int firstImp = 0;
|
||||
for (AstCFunc* nodep : m_dpis) {
|
||||
if (nodep->dpiExportWrapper()) {
|
||||
if (nodep->dpiExportDispatcher()) {
|
||||
if (!firstExp++) puts("\n// DPI EXPORTS\n");
|
||||
putsDecoration("// DPI export" + ifNoProtect(" at " + nodep->fileline()->ascii())
|
||||
+ "\n");
|
||||
puts("extern " + nodep->rtnTypeVoid() + " " + nodep->nameProtect() + "("
|
||||
+ cFuncArgs(nodep) + ");\n");
|
||||
} else if (nodep->dpiImport()) {
|
||||
} else if (nodep->dpiImportPrototype()) {
|
||||
if (!firstImp++) puts("\n// DPI IMPORTS\n");
|
||||
putsDecoration("// DPI import" + ifNoProtect(" at " + nodep->fileline()->ascii())
|
||||
+ "\n");
|
||||
@@ -896,7 +981,7 @@ void EmitCSyms::emitDpiHdr() {
|
||||
|
||||
void EmitCSyms::emitDpiImp() {
|
||||
UINFO(6, __FUNCTION__ << ": " << endl);
|
||||
string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.cpp";
|
||||
const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.cpp";
|
||||
AstCFile* cfilep = newCFile(filename, false /*slow*/, true /*source*/);
|
||||
cfilep->support(true);
|
||||
V3OutCFile hf(filename);
|
||||
@@ -922,7 +1007,7 @@ void EmitCSyms::emitDpiImp() {
|
||||
puts("\n");
|
||||
|
||||
for (AstCFunc* nodep : m_dpis) {
|
||||
if (nodep->dpiExportWrapper()) {
|
||||
if (nodep->dpiExportDispatcher()) {
|
||||
// Prevent multi-definition if used by multiple models
|
||||
puts("#ifndef VL_DPIDECL_" + nodep->name() + "_\n");
|
||||
puts("#define VL_DPIDECL_" + nodep->name() + "_\n");
|
||||
|
||||
+4
-2
@@ -168,6 +168,8 @@ public:
|
||||
}
|
||||
|
||||
of.puts("\n### Switches...\n");
|
||||
of.puts("# C++ code coverage 0/1 (from --prof-c)\n");
|
||||
of.puts(string("VM_PROFC = ") + ((v3Global.opt.profC()) ? "1" : "0") + "\n");
|
||||
of.puts("# SystemC output mode? 0/1 (from --sc)\n");
|
||||
of.puts(string("VM_SC = ") + ((v3Global.opt.systemC()) ? "1" : "0") + "\n");
|
||||
of.puts("# Legacy or SystemC output mode? 0/1 (from --sc)\n");
|
||||
@@ -203,7 +205,7 @@ public:
|
||||
const V3StringSet& cppFiles = v3Global.opt.cppFiles();
|
||||
for (const auto& cppfile : cppFiles) {
|
||||
of.puts("\t" + V3Os::filenameNonExt(cppfile) + " \\\n");
|
||||
string dir = V3Os::filenameDir(cppfile);
|
||||
const string dir = V3Os::filenameDir(cppfile);
|
||||
dirs.insert(dir);
|
||||
}
|
||||
of.puts("\n");
|
||||
@@ -228,7 +230,7 @@ public:
|
||||
of.puts("VPATH += $(VM_USER_DIR)\n");
|
||||
of.puts("\n");
|
||||
for (const string& cppfile : cppFiles) {
|
||||
string basename = V3Os::filenameNonExt(cppfile);
|
||||
const string basename = V3Os::filenameNonExt(cppfile);
|
||||
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
||||
of.puts(basename + ".o: " + cppfile + "\n");
|
||||
of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $<\n");
|
||||
|
||||
+8
-4
@@ -624,9 +624,12 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
|
||||
if (nodep->varScopep()) {
|
||||
putfs(nodep, nodep->varScopep()->prettyName());
|
||||
} else {
|
||||
putfs(nodep, nodep->hiernameToUnprot());
|
||||
puts(nodep->hiernameToProt());
|
||||
puts(nodep->varp()->prettyName());
|
||||
if (nodep->selfPointer().empty()) {
|
||||
putfs(nodep, nodep->varp()->prettyName());
|
||||
} else {
|
||||
putfs(nodep, nodep->selfPointer() + "->");
|
||||
puts(nodep->varp()->prettyName());
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep) override {
|
||||
@@ -847,7 +850,8 @@ void V3EmitV::emitvFiles() {
|
||||
|
||||
void V3EmitV::debugEmitV(const string& stage) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__" + stage + ".v";
|
||||
const string filename
|
||||
= v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__" + stage + ".v";
|
||||
V3OutVFile of(filename);
|
||||
EmitVFileVisitor visitor(v3Global.rootp(), &of, true, true);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user