mirror of
https://github.com/verilator/verilator.git
synced 2026-10-04 17:13:40 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52d29d238c | ||
|
|
0675510eb9 | ||
|
|
99791ac8b3 | ||
|
|
ee26378394 | ||
|
|
54c4351c39 | ||
|
|
e504e9aced | ||
|
|
ef2776034e | ||
|
|
5c658f8cd5 | ||
|
|
0ed7aaeabd | ||
|
|
5f9b0929b4 | ||
|
|
65290afe0a | ||
|
|
ed93a111c2 | ||
|
|
3487701b04 | ||
|
|
d33a3d09f7 | ||
|
|
0716a28816 | ||
|
|
8e1901da10 | ||
|
|
ecfa385f13 | ||
|
|
63df87e220 | ||
|
|
495d52d30f | ||
|
|
2df886b2b0 | ||
|
|
fcf0d03cd4 | ||
|
|
4154584c4b | ||
|
|
a57a3579c0 | ||
|
|
203993b018 | ||
|
|
347e9b4ec8 | ||
|
|
9d02082801 | ||
|
|
785c51dd0b | ||
|
|
2e4f5c863f | ||
|
|
196f3292d5 | ||
|
|
627a144b83 | ||
|
|
5688d1a935 | ||
|
|
8b0d71994d | ||
|
|
79682e6072 | ||
|
|
7e1b92fa75 | ||
|
|
ca8ef7ce73 | ||
|
|
23d538fdaf | ||
|
|
aaadc3def7 | ||
|
|
444a4a760c | ||
|
|
b42799f3b5 | ||
|
|
7b07cf912e | ||
|
|
6a3ec17887 | ||
|
|
8c3ca30c91 | ||
|
|
0e4da3b0bf | ||
|
|
e6add5e0b8 | ||
|
|
22243d1e49 | ||
|
|
bec0b7d4d0 | ||
|
|
f6f13c7fda | ||
|
|
e7068369fe | ||
|
|
b930d0731a | ||
|
|
2723223884 | ||
|
|
b6c116d4bf | ||
|
|
54e3f15dce | ||
|
|
c057847760 | ||
|
|
46c5764383 | ||
|
|
22ce36012e | ||
|
|
5c65e0cfa1 | ||
|
|
840e26b69a | ||
|
|
5e79652922 | ||
|
|
cb7b024e8f | ||
|
|
f3292a3651 | ||
|
|
76ccd332a6 | ||
|
|
3cd2c8532d | ||
|
|
c0739e908c | ||
|
|
379a947379 | ||
|
|
916a3d9066 | ||
|
|
b16b607b98 | ||
|
|
732d5bea10 | ||
|
|
d9a0d0ade2 | ||
|
|
e32ff0e1a6 | ||
|
|
5957156dee | ||
|
|
14f58ed6c7 | ||
|
|
038d57070b | ||
|
|
8a347248f5 | ||
|
|
caed086516 | ||
|
|
68927d4fd3 | ||
|
|
b2070a9407 | ||
|
|
8dacbdec3a | ||
|
|
2a110c91cf | ||
|
|
ba052beccd | ||
|
|
6f9c585452 | ||
|
|
18c26b90af | ||
|
|
ff49f797e5 | ||
|
|
c033a0d7c8 | ||
|
|
461f3c1004 | ||
|
|
90447d54d1 | ||
|
|
439d30a953 | ||
|
|
a972230b3a | ||
|
|
29a080dd9b | ||
|
|
0570cb8d9f | ||
|
|
6fa14bf029 | ||
|
|
4f0158b5e0 | ||
|
|
97add4d57a | ||
|
|
5b742571d3 | ||
|
|
a83043d735 | ||
|
|
22fcd616aa | ||
|
|
1a8188e1b4 | ||
|
|
f87fe4c3b4 | ||
|
|
f23f3ca907 | ||
|
|
965d99f1bc | ||
|
|
56ac054fb2 | ||
|
|
ced82cbac4 | ||
|
|
2fc1746ef5 | ||
|
|
10fc1f757c | ||
|
|
90009b9ec7 | ||
|
|
4367e03e46 | ||
|
|
c9634695a7 | ||
|
|
2a12b052f2 | ||
|
|
84b9502af4 | ||
|
|
694bdbc130 | ||
|
|
880cac2fdd | ||
|
|
a204b24fcf | ||
|
|
526e6b9fc7 | ||
|
|
f1ba6cb517 | ||
|
|
159cf0429c | ||
|
|
46b8dca360 | ||
|
|
4db998d357 | ||
|
|
0b843ada03 | ||
|
|
746c7ea8f7 | ||
|
|
5ed882faf2 | ||
|
|
fa4b10b4d9 | ||
|
|
cc51966ad1 | ||
|
|
c9d6344f2f | ||
|
|
acebafcbc2 | ||
|
|
4a1a2def95 | ||
|
|
09e352ef66 | ||
|
|
17976d7401 | ||
|
|
f25a9a4c80 | ||
|
|
cd2a5771b8 | ||
|
|
9c2ead90d5 | ||
|
|
a999c73ce0 | ||
|
|
b92173bf3d | ||
|
|
91823c41c5 | ||
|
|
c6bce636ee | ||
|
|
75a70bee6d | ||
|
|
1b1907af49 | ||
|
|
1b18eee5dc | ||
|
|
4931e48016 | ||
|
|
1b17acdb01 | ||
|
|
9c1cc5465d | ||
|
|
d8b5359fcb | ||
|
|
f96454adcc | ||
|
|
9da012568c | ||
|
|
9a20a258f5 | ||
|
|
10796457d2 | ||
|
|
78e659a142 | ||
|
|
2f50642ecb | ||
|
|
47bce4157d | ||
|
|
3a8a314566 | ||
|
|
050060b139 | ||
|
|
ddb678cc5b | ||
|
|
63c694f65f | ||
|
|
12093e6939 | ||
|
|
9949a6cd17 | ||
|
|
4600932d8c | ||
|
|
79ded3bb61 | ||
|
|
95145038b4 | ||
|
|
ce03293128 | ||
|
|
72e7271a14 | ||
|
|
0a8cfb8d2c | ||
|
|
0b07679ff2 | ||
|
|
550a5111b7 | ||
|
|
d162619bd3 | ||
|
|
5df14627fd | ||
|
|
fc3ce29845 | ||
|
|
bba800f2d6 | ||
|
|
fe2a1e1749 | ||
|
|
fc4ffd454e | ||
|
|
8dae4ad93a | ||
|
|
8da261f302 | ||
|
|
7bc7b5372e | ||
|
|
7d88e63bab | ||
|
|
a214fd1f78 | ||
|
|
79be097e34 | ||
|
|
11b0d36ba2 | ||
|
|
1234ee5fd2 | ||
|
|
80b73859a2 | ||
|
|
a548a77f26 | ||
|
|
d6bfb37e6a | ||
|
|
af305bf280 | ||
|
|
38a8d7fb2e | ||
|
|
0c70a0dcbf | ||
|
|
d16619fe86 | ||
|
|
ab6e1c2399 | ||
|
|
2dc85a5acd | ||
|
|
dbe1348b4c | ||
|
|
22846df03e | ||
|
|
d74536a4dc | ||
|
|
da20da264b | ||
|
|
22b9dfb9c9 | ||
|
|
27031ed688 | ||
|
|
e43c089ab8 | ||
|
|
d85b909054 | ||
|
|
75fd71d7e5 | ||
|
|
9efd64ab98 | ||
|
|
7aa01625d8 | ||
|
|
a3c58d7b70 | ||
|
|
ae466b1703 | ||
|
|
2564484429 | ||
|
|
93a044f587 | ||
|
|
81fe35ee2e | ||
|
|
08b6bdddf9 | ||
|
|
4d49db48a3 | ||
|
|
9b2266f68c | ||
|
|
752f425025 | ||
|
|
47262cd4ec | ||
|
|
47e64535d6 | ||
|
|
45d622e4bc | ||
|
|
249feaae7c | ||
|
|
9a27004ae5 | ||
|
|
42283150d7 | ||
|
|
5a1bcf9794 | ||
|
|
361cef4633 | ||
|
|
90ab746a42 | ||
|
|
e94cdcf29c | ||
|
|
1af046986d | ||
|
|
1c9263a25b | ||
|
|
fd6275a62b | ||
|
|
6b6790fc50 | ||
|
|
fb931087ab | ||
|
|
a2e1b32a1c | ||
|
|
54f89bce42 | ||
|
|
8b19d02e3b | ||
|
|
da7ad35577 | ||
|
|
937e893b6d | ||
|
|
d42a2d6494 | ||
|
|
8e8f4b1e5c | ||
|
|
298f71f2b1 | ||
|
|
2ba39b25f1 | ||
|
|
5c828b7e60 | ||
|
|
4640bea31a | ||
|
|
875361d7ce | ||
|
|
849bb5590a | ||
|
|
8d0c06e570 | ||
|
|
c0f9b0d8f6 | ||
|
|
505bba14eb | ||
|
|
ebbe24966c | ||
|
|
881c3f6e40 | ||
|
|
546aeab9f2 | ||
|
|
8de21e9bb7 | ||
|
|
2ecda74471 | ||
|
|
819e8741cc | ||
|
|
24ec84851a | ||
|
|
2af5304884 | ||
|
|
5c356a4680 | ||
|
|
39af5d020e | ||
|
|
7cc89b8b42 | ||
|
|
ebb37b0156 | ||
|
|
10cf492946 | ||
|
|
1404319b28 | ||
|
|
a4fd6d38fb | ||
|
|
c266739e9f | ||
|
|
7403226a97 | ||
|
|
6fc25dae9e | ||
|
|
39d1a62f9e | ||
|
|
ba66fa7200 | ||
|
|
3c54d5df70 | ||
|
|
12925cd8b0 | ||
|
|
daac7cb90d | ||
|
|
4859f5e1fa | ||
|
|
ad2fbfe62d | ||
|
|
386401da60 | ||
|
|
31abe537a0 | ||
|
|
f9ecbdc70b | ||
|
|
c28bf9ce24 | ||
|
|
5a1f1796d7 | ||
|
|
ffc95fcf0e | ||
|
|
3773e2ef95 | ||
|
|
00c1f67c57 | ||
|
|
3f19ba1554 | ||
|
|
f37cc2353d | ||
|
|
6a7bda6910 | ||
|
|
ff1b9930fc | ||
|
|
582da6df9a | ||
|
|
c9ac9a75a6 | ||
|
|
b25b798dbe | ||
|
|
e7ca4a69e3 | ||
|
|
aca9fd3bed | ||
|
|
09f3f40462 | ||
|
|
e2525d8440 | ||
|
|
6769106881 | ||
|
|
0f324c8309 | ||
|
|
3af5e7e8da | ||
|
|
160f3ee4a7 | ||
|
|
3a310f19f0 | ||
|
|
d7a75dc026 | ||
|
|
f0a2c98376 | ||
|
|
282887d9c6 | ||
|
|
0e62cd11da | ||
|
|
599d23697d | ||
|
|
39b7c47e7b | ||
|
|
f6f03bc261 | ||
|
|
766fb56651 | ||
|
|
bec4610e12 | ||
|
|
184ebe72a2 | ||
|
|
2ad0bcbba9 | ||
|
|
88bb7cdca6 | ||
|
|
46de9460a6 | ||
|
|
92f64de115 | ||
|
|
f4baa86b71 |
@@ -56,7 +56,6 @@ DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
|
||||
|
||||
@@ -61,12 +61,12 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: repo
|
||||
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||
with:
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
|
||||
|
||||
- name: Upload tar archive
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
@@ -132,7 +132,7 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Download tar archive
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
path: ${{ github.workspace }}
|
||||
@@ -142,7 +142,7 @@ jobs:
|
||||
run: tar -x -z -f ${{ env.VERILATOR_ARCHIVE }}
|
||||
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||
with:
|
||||
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
Test:
|
||||
name: "'docs/CONTRIBUTORS' was signed"
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- run: test_regress/t/t_dist_contributors.pl
|
||||
|
||||
@@ -24,14 +24,14 @@ jobs:
|
||||
|
||||
|
||||
Build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CI_RUNS_ON: ubuntu-22.04
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: repo
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
|
||||
|
||||
- name: Upload tar archive
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
@@ -73,15 +73,15 @@ jobs:
|
||||
- 9
|
||||
include:
|
||||
- { test: dist, num: '' }
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
name: test-${{ matrix.test }}${{ matrix.num }}
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: test
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CI_RUNS_ON: ubuntu-22.04
|
||||
steps:
|
||||
|
||||
- name: Download tar archive
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
path: ${{ github.workspace }}
|
||||
|
||||
@@ -10,16 +10,16 @@ on:
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-20.04
|
||||
name: Ubuntu 20.04 | format
|
||||
runs-on: ubuntu-22.04
|
||||
name: Ubuntu 22.04 | format
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CI_RUNS_ON: ubuntu-22.04
|
||||
CI_COMMIT: ${{ github.sha }}
|
||||
CI_M32: 0
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Install packages for build
|
||||
@@ -27,14 +27,14 @@ jobs:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
run: |
|
||||
bash ci/ci-install.bash &&
|
||||
sudo apt-get install clang-format-11 yapf3 &&
|
||||
sudo apt-get install clang-format-14 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-11 &&
|
||||
make -j 2 format CLANGFORMAT=clang-format-14 &&
|
||||
git status
|
||||
- name: Push
|
||||
run: |
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
\#*
|
||||
.#*
|
||||
.gdb_history
|
||||
.nfs*
|
||||
*~
|
||||
*.tidy
|
||||
|
||||
@@ -8,36 +8,108 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.002 2022-10-29
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* This is a major new release.
|
||||
* Require C++20 for the new --timing features. Upgrading to a C++20 or
|
||||
newer compiler is strongly recommended.
|
||||
* Support the Active and NBA scheduling regions as defined by the
|
||||
SystemVerilog standard (IEEE 1800-2017 chapter 4). This means all generated
|
||||
clocks are now simulated correctly (#3278, #3384). [Geza Lore, Shunyao CAD]
|
||||
* Support timing controls (delays, event controls in any location, wait
|
||||
statements) and forks. [Krzysztof Bieganski, Antmicro Ltd]
|
||||
This may require adding --timing or --no-timing. See docs for details.
|
||||
* Introduce a new combinational logic optimizer (DFG), that can yield
|
||||
significant performance improvements on some designs. [Geza Lore, Shunyao CAD]
|
||||
* Add --binary option as alias of --main --exe --build --timing (#3625).
|
||||
For designs where C++ was only used to make a simple no-I/O testbench, we
|
||||
recommend abandoning that C++, and instead letting Verilator build it
|
||||
with --binary (or --main).
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Split UNUSED warning into genvar, param, and signal warnings (#3607). [Topa Topino]
|
||||
* Support standalone 'this' in classes (#2594) (#3248) (#3675). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Support tristate select/extend (#3604). [Ryszard Rozak, Antmicro Ltd>
|
||||
* Support linting for top module interfaces (#3635). [Kanad Kanhere]
|
||||
* Support virtual interfaces (#3654). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Support class type params without defaults (#3693). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Support empty generate_regions (#3695). [mpb27]
|
||||
* Support access to constructs inside type parameters (#3702). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Add --dump-tree-dot to enable dumping Ast Tree .dot files (#3636). [Marcel Chang]
|
||||
* Add --get-supported to determine what features are in Verilator.
|
||||
* Add error on real edge event control.
|
||||
* Fix false LATCH warning on 'unique if' (#3088). [Rachit Nigam]
|
||||
* Fix cell assigning integer array parameters (#3299). [Michael Platzer]
|
||||
* Fix LSB error on --hierarchical submodules (#3539). [danbone]
|
||||
* Fix $display of fixed-width numbers (#3565). [Iztok Jeras]
|
||||
* Fix foreach and pre/post increment in functions (#3613). [Nandu Raj]
|
||||
* Fix linker errors in user-facing timing functions (#3657). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix null access on optimized-out fork statements (#3658). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix VPI inline module naming mismatch (#3690) (#3694). [Jiuyang Liu]
|
||||
* Fix deadlock in timeprecision when using systemC (#3707). [Kamil Rakoczy, Antmicro Ltd]
|
||||
* Fix width mismatch on inside operator (#3714). [Alex Torregrosa]
|
||||
|
||||
|
||||
Verilator 4.228 2022-10-01
|
||||
==========================
|
||||
|
||||
**Announcement:**
|
||||
|
||||
* The next release is anticipated to premiere Verilator Version 5. Please
|
||||
consider beta-testing the github 'develop-v5' branch, which will soon
|
||||
merge into the github 'master' branch (#3383).
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Support some IEEE signal strengths (#3601) (#3629). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Add --main to generate main() C++ (previously was experimental only).
|
||||
* Add --build-jobs, and rework arguments for -j (#3623). [Kamil Rakoczy]
|
||||
* Rename --bin to --build-dep-bin.
|
||||
* Rename debug flags --dumpi-tree, --dumpi-graph, etc. [Geza Lore]
|
||||
* Fix thread safety in SystemC VL_ASSIGN_SBW/WSB (#3494) (#3513). [Mladen Slijepcevic]
|
||||
* Fix crash in gate optimization of circular logic (#3543). [Bill Flynn]
|
||||
* Fix arguments in non-static method call (#3547) (#3582). [Gustav Svensk]
|
||||
* Fix default --mod-prefix when --prefix is repeated (#3603). [Geza Lore]
|
||||
* Fix calling trace() after open() segfault (#3610) (#3627). [Yu-Sheng Lin]
|
||||
* Fix typedef'ed class conversion to Boolean (#3616). [Aleksander Kiryk]
|
||||
* Fix Verilation speed when disabled warnings (#3632). [Kamil Rakoczy, Antmicro Ltd]
|
||||
|
||||
|
||||
Verilator 4.226 2022-08-31
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add --future0 and --future1 options.
|
||||
* Support class parameters (#2231) (#3541). [Arkadiusz Kozdra/Antmicro]
|
||||
* Support wildcard index associative arrays (#3501). [Arkadiusz Kozdra/Antmicro]
|
||||
* Support class parameters (#2231) (#3541). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Support wildcard index associative arrays (#3501). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Support negated properties (#3572). [Aleksander Kiryk]
|
||||
* Support $test$plusargs(expr) (#3489).
|
||||
* Rename trace rolloverSize() (#3570).
|
||||
* Improve Verilation speed with --threads on large designs. [Geza Lore]
|
||||
* Improve Verilation memory by reducing V3Number (#3521). [Mariusz Glebocki, Antmicro Ltd]
|
||||
* Fix struct pattern assignment (#2328) (#3517). [Mostafa Gamal]
|
||||
* Fix public combo propagation issues (#2905). [Todd Strader]
|
||||
* Fix incorrect tristate logic (#3399) [shareefj, Vighnesh Iyer]
|
||||
* Fix incorrect bit op tree optimization (#3470). [algrobman]
|
||||
* Fix bisonpre for MSYS2 (#3471).
|
||||
* Fix max memory usage (#3483). [Kamil Rakoczy]
|
||||
* Fix max memory usage (#3483). [Kamil Rakoczy, Antmicro Ltd]
|
||||
* Fix empty string arguments to display (#3484). [Grulfen]
|
||||
* Fix table misoptimizing away display (#3488). [Stefan Post]
|
||||
* Fix table optimizing away display (#3488). [Stefan Post]
|
||||
* Fix unique_ptr memory header for MinGW64 (#3493).
|
||||
* Fix $dump systemtask with --output-split-cfuncs (#3495) (#3497). [Varun Koyyalagunta]
|
||||
* Fix $dump system task with --output-split-cfuncs (#3495) (#3497). [Varun Koyyalagunta]
|
||||
* Fix wrong bit op tree optimization (#3509). [Nathan Graybeal]
|
||||
* Fix nested default assignment for struct pattern (#3511) (#3524). [Mostafa Gamal]
|
||||
* Fix sformat string incorrectly cleared (#3515) (#3519). [Gustav Svensk]
|
||||
* Fix segfault exporting non-existant package (#3535).
|
||||
* Fix segfault exporting non-existent package (#3535).
|
||||
* Fix void-cast queue pop_front or pop_back (#3542) (#3364). [Drew Ranck]
|
||||
* Fix case statement comparing string literal (#3544). [Gustav Svensk]
|
||||
* Fix === with some tristate constants (#3551). [Ryszard Rozak]
|
||||
* Fix converting subclasses to string (#3552). [Arkadiusz Kozdra/Antmicro]
|
||||
* Fix === with some tristate constants (#3551). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix converting classes to string (#3552). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Fix --hierarchical with order-based pin connections (#3583) (#3585). [Kelin9298]
|
||||
|
||||
|
||||
@@ -59,7 +131,7 @@ Verilator 4.224 2022-06-19
|
||||
* Improve conditional merging optimization (#3125). [Geza Lore, Shunyao CAD]
|
||||
* Define VM_TRACE_VCD when tracing in VCD format. [Geza Lore, Shunyao CAD]
|
||||
* Add assert when VerilatedContext is mis-deleted (#3121). [Rupert Swarbrick]
|
||||
* Internal prep work towards timing control. [Krzysztof Bieganski/Antmicro]
|
||||
* Internal prep work towards timing control. [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix hang with large case statement optimization (#3405). [Mike Urbach]
|
||||
* Fix UNOPTFLAT warning from initial static var (#3406). [Kamil Rakoczy]
|
||||
* Fix compile error when enable VL_LEAK_CHECKS (#3411). [HungMingWu]
|
||||
@@ -226,7 +298,7 @@ Verilator 4.212 2021-09-01
|
||||
* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore]
|
||||
* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore]
|
||||
* Support timeunit/timeprecision in $unit.
|
||||
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski/Antmicro]
|
||||
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded
|
||||
model scheduling. Default value changed to 200 (#3068). [Yinan Xu]
|
||||
* Output files are split based on the set of headers required
|
||||
@@ -291,7 +363,7 @@ Verilator 4.204 2021-06-12
|
||||
* 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/Antmicro]
|
||||
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* 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]
|
||||
@@ -406,7 +478,7 @@ Verilator 4.108 2021-01-10
|
||||
**Major:**
|
||||
|
||||
* Many VPI changes for IEEE compatibility, which may alter behavior from previous releases.
|
||||
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski/Antmicro]
|
||||
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
|
||||
**Minor:**
|
||||
|
||||
@@ -466,7 +538,7 @@ Verilator 4.104 2020-11-14
|
||||
* Support queue and associative array 'with' statements (#2616).
|
||||
* Support queue slicing (#2326).
|
||||
* Support associative array pattern assignments and defaults.
|
||||
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski/Antmicro]
|
||||
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Add error on typedef referencing self (#2539). [Cody Piersall]
|
||||
* With --debug, turn off address space layout randomization.
|
||||
* Fix iteration over mutating list bug in VPI (#2588). [Kaleb Barrett]
|
||||
|
||||
+9
-7
@@ -107,6 +107,7 @@ SUBDIRS = docs src test_regress \
|
||||
examples/cmake_tracing_c \
|
||||
examples/cmake_tracing_sc \
|
||||
examples/cmake_protect_lib \
|
||||
examples/make_hello_binary \
|
||||
examples/make_hello_c \
|
||||
examples/make_hello_sc \
|
||||
examples/make_tracing_c \
|
||||
@@ -243,6 +244,7 @@ installdata:
|
||||
; for p in $(VL_INST_INC_SRCDIR_FILES) ; do \
|
||||
$(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \
|
||||
done
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_hello_binary
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_hello_c
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_hello_sc
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_tracing_c
|
||||
@@ -278,6 +280,7 @@ uninstall:
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/include/gtkwave
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/include/vltstd
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/include
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_hello_binary
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_hello_c
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_hello_sc
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_tracing_c
|
||||
@@ -326,8 +329,7 @@ CLANGTIDY_FLAGS = -config='' \
|
||||
-header-filter='.*' \
|
||||
-checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables,-cppcoreguidelines-avoid-goto,-modernize-avoid-c-arrays,-readability-magic-numbers,-readability-simplify-boolean-expr,-cppcoreguidelines-macro-usage' \
|
||||
|
||||
CLANGTIDY_DEP = $(subst .h,.h.tidy,$(CPPCHECK_H)) \
|
||||
$(subst .cpp,.cpp.tidy,$(CPPCHECK_CPP))
|
||||
CLANGTIDY_DEP = $(subst .cpp,.cpp.tidy,$(CPPCHECK_CPP))
|
||||
CLANGTIDY_DEFS = -DVL_DEBUG=1 -DVL_THREADED=1 -DVL_CPPCHECK=1
|
||||
|
||||
clang-tidy: $(CLANGTIDY_DEP)
|
||||
@@ -346,13 +348,13 @@ analyzer-include:
|
||||
|
||||
format: clang-format yapf format-pl-exec
|
||||
|
||||
CLANGFORMAT = clang-format-11
|
||||
CLANGFORMAT = clang-format-14
|
||||
CLANGFORMAT_FLAGS = -i
|
||||
CLANGFORMAT_FILES = $(CPPCHECK_CPP) $(CPPCHECK_H) $(CPPCHECK_YL) test_regress/t/*.c* test_regress/t/*.h
|
||||
|
||||
clang-format:
|
||||
@$(CLANGFORMAT) --version | egrep 11.0 > /dev/null \
|
||||
|| echo "*** You are not using clang-format 11.0, indents may differ from master's ***"
|
||||
@$(CLANGFORMAT) --version | egrep 14.0 > /dev/null \
|
||||
|| echo "*** You are not using clang-format-14, indents may differ from master's ***"
|
||||
$(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES)
|
||||
|
||||
PY_PROGRAMS = \
|
||||
@@ -391,10 +393,10 @@ yapf:
|
||||
FLAKE8 = flake8
|
||||
FLAKE8_FLAGS = \
|
||||
--extend-exclude=fastcov.py \
|
||||
--ignore=E123,E129,E251,E501,W503,W504,E701
|
||||
--ignore=E123,E129,E251,E402,E501,W503,W504,E701
|
||||
|
||||
PYLINT = pylint
|
||||
PYLINT_FLAGS = --disable=R0801
|
||||
PYLINT_FLAGS = --score=n --disable=R0801
|
||||
|
||||
lint-py:
|
||||
-$(FLAKE8) $(FLAKE8_FLAGS) $(PY_PROGRAMS)
|
||||
|
||||
+17
-16
@@ -28,7 +28,7 @@ Welcome to Verilator
|
||||
- |Logo|
|
||||
* - |verilator multithreaded performance|
|
||||
- **Fast**
|
||||
* Outperforms many commercial simulators
|
||||
* Outperforms many closed-source commercial simulators
|
||||
* Single- and multi-threaded output models
|
||||
* - **Widely Used**
|
||||
* Wide industry and academic deployment
|
||||
@@ -55,20 +55,19 @@ performing lint checks, and optionally inserting assertion checks and
|
||||
coverage-analysis points. It outputs single- or multi-threaded .cpp and .h
|
||||
files, the "Verilated" code.
|
||||
|
||||
The user writes a little C++/SystemC wrapper file, which instantiates the
|
||||
"Verilated" model of the user's top level module. These C++/SystemC files
|
||||
are then compiled by a C++ compiler (gcc/clang/MSVC++). Executing the
|
||||
resulting executable performs the design simulation. Verilator also
|
||||
supports linking Verilated generated libraries, optionally encrypted, into
|
||||
other simulators.
|
||||
These Verilated C++/SystemC files are then compiled by a C++ compiler
|
||||
(gcc/clang/MSVC++), optionally along with a user's own C++/SystemC wrapper
|
||||
file to instantiate the Verilated model. Executing the resulting executable
|
||||
performs the design simulation. Verilator also supports linking Verilated
|
||||
generated libraries, optionally encrypted, into other simulators.
|
||||
|
||||
Verilator may not be the best choice if you are expecting a full featured
|
||||
replacement for Incisive, ModelSim/Questa, VCS or another commercial
|
||||
Verilog simulator, or if you are looking for a behavioral Verilog simulator
|
||||
e.g. for a quick class project (we recommend `Icarus Verilog`_ for this.)
|
||||
However, if you are looking for a path to migrate SystemVerilog to C++ or
|
||||
SystemC, or your team is comfortable writing just a touch of C++ code,
|
||||
Verilator is the tool for you.
|
||||
replacement for a closed-source Verilog simulator, need SDF annotation,
|
||||
mixed-signal simulation, or are doing a quick class project (we recommend
|
||||
`Icarus Verilog`_ for classwork.) However, if you are looking for a path
|
||||
to migrate SystemVerilog to C++/SystemC, or want high speed simulation of
|
||||
synthesizable designs containing limited verification constructs, Verilator
|
||||
is the tool for you.
|
||||
|
||||
|
||||
Performance
|
||||
@@ -85,9 +84,11 @@ multithreading (yielding 200-1000x total over interpreted simulators).
|
||||
|
||||
Verilator has typically similar or better performance versus the
|
||||
closed-source Verilog simulators (Carbon Design Systems Carbonator,
|
||||
Modelsim, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and Pragmatic
|
||||
CVer/CVC). But, Verilator is open-sourced, so you can spend on computes
|
||||
rather than licenses. Thus Verilator gives you the best cycles/dollar.
|
||||
Modelsim/Questa, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and
|
||||
Pragmatic CVer/CVC). But, Verilator is open-sourced, so you can spend on
|
||||
computes rather than licenses. Thus Verilator gives you the best
|
||||
cycles/dollar.
|
||||
|
||||
|
||||
Installation & Documentation
|
||||
============================
|
||||
|
||||
+18
-5
@@ -244,6 +244,7 @@ Verilator - Translate and simulate SystemVerilog code using C++/SystemC
|
||||
|
||||
verilator --help
|
||||
verilator --version
|
||||
verilator --binary -j 0 [options] [source_files.v]... [opt_c_files.cpp/c/cc/a/o/so]
|
||||
verilator --cc [options] [source_files.v]... [opt_c_files.cpp/c/cc/a/o/so]
|
||||
verilator --sc [options] [source_files.v]... [opt_c_files.cpp/c/cc/a/o/so]
|
||||
verilator --lint-only -Wall [source_files.v]...
|
||||
@@ -282,8 +283,10 @@ detailed descriptions of these arguments.
|
||||
--autoflush Flush streams after all $displays
|
||||
--bbox-sys Blackbox unknown $system calls
|
||||
--bbox-unsup Blackbox unsupported language features
|
||||
--bin <filename> Override Verilator binary
|
||||
--binary Build model binary
|
||||
--build Build model executable/library after Verilation
|
||||
--build-dep-bin <filename> Override build dependency Verilator binary
|
||||
--build-jobs <jobs> Parallelism for --build
|
||||
--cc Create C++ output
|
||||
--cdc Clock domain crossing analysis
|
||||
-CFLAGS <flags> C++ compiler arguments for makefile
|
||||
@@ -308,10 +311,15 @@ detailed descriptions of these arguments.
|
||||
+define+<var>=<value> Set preprocessor define
|
||||
--dpi-hdr-only Only produce the DPI header file
|
||||
--dump-defines Show preprocessor defines with -E
|
||||
--dump-tree Enable dumping .tree files
|
||||
--dump-dfg Enable dumping DfgGraphs to .dot files
|
||||
--dump-graph Enable dumping V3Graphs to .dot files
|
||||
--dump-tree Enable dumping Ast .tree files
|
||||
--dump-tree-addrids Use short identifiers instead of addresses
|
||||
--dump-treei <level> Enable dumping .tree files at a level
|
||||
--dump-treei-<srcfile> <level> Enable dumping .tree file at a source file at a level
|
||||
--dump-<srcfile> Enable dumping everything in source file
|
||||
--dumpi-dfg <level> Enable dumping DfgGraphs to .dot files at level
|
||||
--dumpi-graph <level> Enable dumping V3Graphs to .dot files at level
|
||||
--dumpi-tree <level> Enable dumping Ast .tree files at level
|
||||
--dumpi-<srcfile> <level> Enable dumping everything in source file at level
|
||||
-E Preprocess, but do not compile
|
||||
--error-limit <value> Abort after this number of errors
|
||||
--exe Link to create executable
|
||||
@@ -327,6 +335,7 @@ detailed descriptions of these arguments.
|
||||
--gdbbt Run Verilator under GDB for backtrace
|
||||
--generate-key Create random key for --protect-key
|
||||
--getenv <var> Get environment variable with defaults
|
||||
--get-supported <feature> Get if feature is supported
|
||||
--help Display this help
|
||||
--hierarchical Enable hierarchical Verilation
|
||||
-I<dir> Directory to search for includes
|
||||
@@ -334,7 +343,7 @@ detailed descriptions of these arguments.
|
||||
+incdir+<dir> Directory to search for includes
|
||||
--inline-mult <value> Tune module inlining
|
||||
--instr-count-dpi <value> Assumed dynamic instruction count of DPI imports
|
||||
-j <jobs> Parallelism for --build
|
||||
-j <jobs> Parallelism for --build (alias to --build-jobs)
|
||||
--l2-name <value> Verilog scope name of the top module
|
||||
--language <lang> Default language standard to parse
|
||||
-LDFLAGS <flags> Linker pre-object arguments for makefile
|
||||
@@ -343,6 +352,7 @@ detailed descriptions of these arguments.
|
||||
--lint-only Lint, but do not make output
|
||||
--make <build-tool> Generate scripts for specified build tool
|
||||
-MAKEFLAGS <flags> Arguments to pass to make during --build
|
||||
--main Generate C++ main() file
|
||||
--max-num-width <value> Maximum number width (default: 64K)
|
||||
--Mdir <directory> Name of output object directory
|
||||
--MMD Create .d dependency files
|
||||
@@ -354,6 +364,7 @@ detailed descriptions of these arguments.
|
||||
-O<optimization-letter> Selectable optimizations
|
||||
-o <executable> Name of final executable
|
||||
--no-order-clock-delay Disable ordering clock enable assignments
|
||||
--no-verilate Skip Verilation, only compile previously Verilated code
|
||||
--output-split <statements> Split .cpp files into pieces
|
||||
--output-split-cfuncs <statements> Split model functions
|
||||
--output-split-ctrace <statements> Split tracing functions
|
||||
@@ -392,6 +403,8 @@ detailed descriptions of these arguments.
|
||||
--threads <threads> Enable multithreading
|
||||
--threads-dpi <mode> Enable multithreaded DPI
|
||||
--threads-max-mtasks <mtasks> Tune maximum mtask partitioning
|
||||
--timing Enable timing support
|
||||
--no-timing Disable timing support
|
||||
--timescale <timescale> Sets default timescale
|
||||
--timescale-override <timescale> Overrides all timescales
|
||||
--top <topname> Alias of --top-module
|
||||
|
||||
@@ -38,7 +38,7 @@ def diff_dir(a, b):
|
||||
|
||||
anyfile = False
|
||||
for base in sorted(files.keys()):
|
||||
if (not 'a' in files[base]) or (not 'b' in files[base]):
|
||||
if ('a' not in files[base]) or ('b' not in files[base]):
|
||||
continue
|
||||
a = files[base]['a']
|
||||
b = files[base]['b']
|
||||
|
||||
+2
-1
@@ -149,7 +149,8 @@ def report():
|
||||
for thread in Threads:
|
||||
# Make potentially multiple characters per column
|
||||
for start in Threads[thread]:
|
||||
if not Threads[thread][start]: continue
|
||||
if not Threads[thread][start]:
|
||||
continue
|
||||
cpu = Threads[thread][start]['cpu']
|
||||
elapsed = Threads[thread][start]['end'] - start
|
||||
if cpu not in Global['cpus']:
|
||||
|
||||
@@ -19,6 +19,10 @@ set -x
|
||||
|
||||
cd $(dirname "$0")/..
|
||||
|
||||
# Avoid occasional cpan failures "Issued certificate has expired."
|
||||
export PERL_LWP_SSL_VERIFY_HOSTNAME=0
|
||||
echo "check_certificate = off" >> ~/.wgetrc
|
||||
|
||||
fatal() {
|
||||
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:22.04
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive \
|
||||
|
||||
@@ -8,7 +8,7 @@ Verilator build. It uses the following parameters:
|
||||
|
||||
- Source revision (default: master)
|
||||
|
||||
- Compiler (GCC 9.3.0, clang 10.0.0, default: 9.3.0)
|
||||
- Compiler (GCC 10.3.0, clang 10.0.0, default: 10.3.0)
|
||||
|
||||
The container is published as ``verilator/verilator-buildenv`` on `docker
|
||||
hub
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:22.04
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive \
|
||||
|
||||
+31
-1
@@ -10,7 +10,7 @@
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.226 2022-08-31],
|
||||
AC_INIT([Verilator],[5.002 2022-10-29],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
@@ -374,6 +374,36 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wlogical-op)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wthread-safety)
|
||||
AC_SUBST(CFG_CXXFLAGS_WEXTRA)
|
||||
|
||||
# Flags for coroutine support for dynamic scheduling
|
||||
_MY_CXX_CHECK_IFELSE(
|
||||
-fcoroutines-ts,
|
||||
[CFG_CXXFLAGS_COROUTINES="-fcoroutines-ts"],
|
||||
[CFG_CXXFLAGS_COROUTINES="-fcoroutines"])
|
||||
AC_SUBST(CFG_CXXFLAGS_COROUTINES)
|
||||
|
||||
# HAVE_COROUTINES
|
||||
# Check if coroutines are supported at all
|
||||
AC_MSG_CHECKING([whether coroutines are supported by $CXX])
|
||||
ACO_SAVE_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $CFG_CXXFLAGS_COROUTINES"
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([
|
||||
#ifdef __clang__
|
||||
#define __cpp_impl_coroutine 1
|
||||
#endif
|
||||
#include <coroutine>
|
||||
],[[]])],
|
||||
[_my_result=yes
|
||||
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])],
|
||||
[AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([#include <experimental/coroutine>],[[]])],
|
||||
[_my_result=yes
|
||||
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])],
|
||||
[_my_result=no])])
|
||||
AC_MSG_RESULT($_my_result)
|
||||
CXXFLAGS="$ACO_SAVE_CXXFLAGS"
|
||||
AC_SUBST(HAVE_COROUTINES)
|
||||
|
||||
# Flags for compiling Verilator internals including parser always
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Qunused-arguments)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-faligned-new)
|
||||
|
||||
@@ -44,6 +44,7 @@ HyungKi Jeong
|
||||
Iru Cai
|
||||
Ivan Vnučec
|
||||
Iztok Jeras
|
||||
Jake Merdich
|
||||
James Hanlon
|
||||
James Hutchinson
|
||||
James Pallister
|
||||
@@ -52,6 +53,7 @@ Jamie Iles
|
||||
Jan Van Winkel
|
||||
Jean Berniolles
|
||||
Jeremy Bennett
|
||||
Jiuyang Liu
|
||||
John Coiner
|
||||
John Demme
|
||||
Jonathan Drolet
|
||||
@@ -70,6 +72,7 @@ Ludwig Rogiers
|
||||
Lukasz Dalek
|
||||
Maarten De Braekeleer
|
||||
Maciej Sobkowski
|
||||
Marcel Chang
|
||||
Marco Widmer
|
||||
Mariusz Glebocki
|
||||
Markus Krause
|
||||
@@ -82,6 +85,7 @@ Michael Killough
|
||||
Michaël Lefebvre
|
||||
Mike Popoloski
|
||||
Miodrag Milanović
|
||||
Mladen Slijepcevic
|
||||
Morten Borup Petersen
|
||||
Mostafa Gamal
|
||||
Nandu Raj
|
||||
@@ -89,6 +93,7 @@ Nathan Kohagen
|
||||
Nathan Myers
|
||||
Patrick Stewart
|
||||
Paul Wright
|
||||
Pawel Sagan
|
||||
Peter Horvath
|
||||
Peter Monsson
|
||||
Philipp Wagner
|
||||
@@ -114,6 +119,7 @@ Tobias Rosenkranz
|
||||
Tobias Wölfel
|
||||
Todd Strader
|
||||
Tomasz Gorochowik
|
||||
Topa Topino
|
||||
Tymoteusz Blazejczyk
|
||||
Udi Finkelstein
|
||||
Unai Martinez-Corral
|
||||
@@ -128,5 +134,6 @@ Yoda Lee
|
||||
Yossi Nivin
|
||||
Yuri Victorovich
|
||||
Yutetsu TAKATSUKASA
|
||||
Yu-Sheng Lin
|
||||
Yves Mathieu
|
||||
Zhanglei Wang
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
.. comment: generated by t_lint_didnotconverge_bad
|
||||
.. code-block::
|
||||
|
||||
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request
|
||||
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request_1
|
||||
-V{t#,#} CHANGE: t/t_lint_didnotconverge_bad.v:14: a
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge
|
||||
-V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] a)
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.. comment: generated by t_lint_didnotconverge_nodbg_bad
|
||||
.. code-block::
|
||||
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.. comment: generated by t_lint_stmtdly_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-STMTDLY: example.v:1:8 Unsupported: Ignoring delay on this delayed statement.
|
||||
%Warning-STMTDLY: example.v:1:7 Ignoring delay on this statement due to --no-timing
|
||||
|
||||
@@ -13,6 +13,7 @@ from datetime import datetime
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('./_ext'))
|
||||
|
||||
import sphinx_rtd_theme # pylint: disable=wrong-import-position,
|
||||
|
||||
@@ -94,7 +94,10 @@ commented example.
|
||||
Top level IO signals are read and written as members of the model. You
|
||||
call the model's :code:`eval()` method to evaluate the model. When the
|
||||
simulation is complete call the model's :code:`final()` method to execute
|
||||
any SystemVerilog final blocks, and complete any assertions. See
|
||||
any SystemVerilog final blocks, and complete any assertions. If using
|
||||
:vlopt:`--timing`, there are two additional functions for checking if
|
||||
there are any events pending in the simulation due to delays, and for
|
||||
retrieving the simulation time of the next delayed event. See
|
||||
:ref:`Evaluation Loop`.
|
||||
|
||||
|
||||
@@ -440,10 +443,25 @@ there is only a single design, you would call :code:`eval_step()` then
|
||||
:code:`eval_end_step()`; in fact :code:`eval()` described above is just a
|
||||
wrapper which calls these two functions.
|
||||
|
||||
3. If using delays and :vlopt:`--timing`, there are two additional methods
|
||||
the user should call:
|
||||
|
||||
* :code:`designp->eventsPending()`, which returns :code:`true` if there are
|
||||
any delayed events pending,
|
||||
* :code:`designp->nextTimeSlot()`, which returns the simulation time of the
|
||||
next delayed event. This method can only be called if
|
||||
:code:`designp->nextTimeSlot()` returned :code:`true`.
|
||||
|
||||
Call :code:`eventsPending()` to check if you should continue with the
|
||||
simulation, and then :code:`nextTimeSlot()` to move simulation time forward.
|
||||
:vlopt:`--main` can be used with :vlopt:`--timing` to generate a basic example
|
||||
of a timing-enabled eval loop.
|
||||
|
||||
When :code:`eval()` (or :code:`eval_step()`) is called Verilator looks for
|
||||
changes in clock signals and evaluates related sequential always blocks,
|
||||
such as computing always_ff @ (posedge...) outputs. Then Verilator
|
||||
evaluates combinatorial logic.
|
||||
such as computing always_ff @ (posedge...) outputs. With :vlopt:`--timing`, it
|
||||
resumes any delayed processes awaiting the current simulation time. Then
|
||||
Verilator evaluates combinational logic.
|
||||
|
||||
Note combinatorial logic is not computed before sequential always blocks
|
||||
are computed (for speed reasons). Therefore it is best to set any non-clock
|
||||
|
||||
+96
-80
@@ -23,7 +23,8 @@ Contributors
|
||||
Many people have provided ideas and other assistance with Verilator.
|
||||
|
||||
Verilator is receiving major development support from the `CHIPS Alliance
|
||||
<https://chipsalliance.org>`_ and `Shunyao CAD <https://shunyaocad.com>`_.
|
||||
<https://chipsalliance.org>`_, `Antmicro Ltd <https://antmicro.com>`_ and
|
||||
`Shunyao CAD <https://shunyaocad.com>`_.
|
||||
|
||||
Previous major corporate sponsors of Verilator, by providing significant
|
||||
contributions of time or funds included include: Atmel Corporation, Cavium
|
||||
@@ -32,90 +33,101 @@ Hicamp Systems, Intel Corporation, Mindspeed Technologies Inc., MicroTune
|
||||
Inc., picoChip Designs Ltd., Sun Microsystems Inc., Nauticus Networks Inc.,
|
||||
SiCortex Inc, and Shunyao CAD.
|
||||
|
||||
The people who have contributed major functionality are: Byron Bradley,
|
||||
Jeremy Bennett, Lane Brooks, John Coiner, Duane Galbi, Geza Lore, Todd
|
||||
Strader, Stefan Wallentowitz, Paul Wasson, Jie Xu, and Wilson Snyder.
|
||||
Major testers included Jeff Dutton, Jonathon Donaldson, Ralf Karge, David
|
||||
Hewson, Iztok Jeras, Wim Michiels, Alex Solomatnikov, Sebastien Van
|
||||
Cauwenberghe, Gene Weber, and Clifford Wolf.
|
||||
The people who have contributed major functionality are: Krzysztof
|
||||
Bieganski, Byron Bradley, Jeremy Bennett, Lane Brooks, John Coiner, Duane
|
||||
Galbi, Geza Lore, Todd Strader, Stefan Wallentowitz, Paul Wasson, Jie Xu,
|
||||
and Wilson Snyder. Major testers included Jeff Dutton, Jonathon Donaldson,
|
||||
Ralf Karge, David Hewson, Iztok Jeras, Wim Michiels, Alex Solomatnikov,
|
||||
Sebastien Van Cauwenberghe, Gene Weber, and Clifford Wolf.
|
||||
|
||||
Some of the people who have provided ideas, and feedback for Verilator
|
||||
include: David Addison, Nikana Anastasiadis, Vasu Arasanipalai, Jens Arm,
|
||||
Tariq B. Ahmad, Sharad Bagri, Matthew Ballance, Andrew Bardsley, Matthew
|
||||
Barr, Geoff Barrett, Kaleb Barrett, Julius Baxter, Jeremy Bennett, Michael
|
||||
Berman, Jean Berniolles, Victor Besyakov, Moinak Bhattacharyya, Krzysztof
|
||||
Bieganski, David Binderman, Piotr Binkowski, Johan Bjork, David Black,
|
||||
Tymoteusz Blazejczyk, Daniel Bone, Morten Borup Petersen, Gregg Bouchard,
|
||||
Christopher Boumenot, Nick Bowler, Byron Bradley, Bryan Brady, Charlie
|
||||
Brej, J Briquet, Lane Brooks, John Brownlee, Jeff Bush, Lawrence Butcher,
|
||||
Tony Bybell, Iru Cai, Ted Campbell, Chris Candler, Lauren Carlson, Donal
|
||||
Casey, Alex Chadwick, Terry Chen, Yi-Chung Chen, Enzo Chi, Robert A. Clark,
|
||||
Ryan Clarke, Allan Cochrane, John Coiner, Gianfranco Costamagna, Sean
|
||||
Cross, George Cuan, Joe DErrico, Lukasz Dalek, Gunter Dannoritzer, Ashutosh
|
||||
Das, Maarten De Braekeleer, Bernard Deadman, Alberto Del Rio, John Demme,
|
||||
Mike Denio, John Deroo, Philip Derrick, John Dickol, Ruben Diez, Danny
|
||||
Ding, Jacko Dirks, Ivan Djordjevic, Jonathon Donaldson, Sebastian Dressler,
|
||||
Alex Duller, Jeff Dutton, Tomas Dzetkulic, Richard E George, Edgar
|
||||
E. Iglesias, Usuario Eda, Charles Eddleston, Chandan Egbert, Jan Egil Ruud,
|
||||
Joe Eiler, Ahmed El-Mahmoudy, Trevor Elbourne, Mats Engstrom, Charles Eric
|
||||
LaForest, Robert Farrell, Eugen Fekete, Fabrizio Ferrandi, Udi Finkelstein,
|
||||
Brian Flachs, Andrea Foletto, Bob Fredieu, Duane Galbi, Benjamin Gartner,
|
||||
Christian Gelinek, Peter Gerst, Glen Gibb, Michael Gielda, Barbara Gigerl,
|
||||
Shankar Giri, Dan Gisselquist, Petr Gladkikh, Sam Gladstone, Andrew
|
||||
Goessling, Amir Gonnen, Chitlesh Goorah, Tomasz Gorochowik, Kai Gossner,
|
||||
Sergi Granell, Al Grant, Alexander Grobman, Xuan Guo, Driss Hafdi, Neil
|
||||
include:
|
||||
|
||||
David Addison, Tariq B. Ahmad, Nikana Anastasiadis, Vasu Arasanipalai, Jens
|
||||
Arm, Sharad Bagri, Matthew Ballance, Andrew Bardsley, Matthew Barr, Geoff
|
||||
Barrett, Kaleb Barrett, Julius Baxter, Jeremy Bennett, Michael Berman, Jean
|
||||
Berniolles, Victor Besyakov, Moinak Bhattacharyya, Krzysztof Bieganski,
|
||||
David Binderman, Piotr Binkowski, Johan Bjork, David Black, Tymoteusz
|
||||
Blazejczyk, Daniel Bone, Gregg Bouchard, Christopher Boumenot, Nick Bowler,
|
||||
Byron Bradley, Bryan Brady, Maarten De Braekeleer, Charlie Brej, J Briquet,
|
||||
Lane Brooks, John Brownlee, Jeff Bush, Lawrence Butcher, Tony Bybell, Iru
|
||||
Cai, Ted Campbell, Chris Candler, Lauren Carlson, Donal Casey, Alex
|
||||
Chadwick, Marcel Chang, Aliaksei Chapyzhenka, Guokai Chen, Terry Chen,
|
||||
Yi-Chung Chen, Enzo Chi, Robert A. Clark, Ryan Clarke, Allan Cochrane, John
|
||||
Coiner, Keith Colbert, Gianfranco Costamagna, Sean Cross, George Cuan, Joe
|
||||
DErrico, Lukasz Dalek, Gunter Dannoritzer, Ashutosh Das, Bernard Deadman,
|
||||
John Demme, Mike Denio, John Deroo, Philip Derrick, John Dickol, Ruben
|
||||
Diez, Danny Ding, Jacko Dirks, Ivan Djordjevic, Jonathon Donaldson, Larry
|
||||
Doolittle, Sebastian Dressler, Jonathan Drolet, Alex Duller, Jeff Dutton,
|
||||
Tomas Dzetkulic, Usuario Eda, Charles Eddleston, Chandan Egbert, Joe Eiler,
|
||||
Ahmed El-Mahmoudy, Trevor Elbourne, Mats Engstrom, Robert Farrell, Eugen
|
||||
Fekete, Fabrizio Ferrandi, Udi Finkelstein, Brian Flachs, Bill Flynn,
|
||||
Andrea Foletto, Bob Fredieu, Duane Galbi, Mostafa Gamal, Benjamin Gartner,
|
||||
Christian Gelinek, Richard E George, Peter Gerst, Glen Gibb, Michael
|
||||
Gielda, Barbara Gigerl, Shankar Giri, Dan Gisselquist, Petr Gladkikh, Sam
|
||||
Gladstone, Mariusz Glebocki, Andrew Goessling, Amir Gonnen, Chitlesh
|
||||
Goorah, Tomasz Gorochowik, Kai Gossner, Sergi Granell, Al Grant, Nathan
|
||||
Graybeal, Alexander Grobman, Graham Rushton, Xuan Guo, Driss Hafdi, Neil
|
||||
Hamilton, James Hanlon, Oyvind Harboe, Jannis Harder, Junji Hashimoto,
|
||||
Thomas Hawkins, Mitch Hayenga, Harald Heckmann, Robert Henry, Stephen
|
||||
Henry, David Hewson, Jamey Hicks, Joel Holdsworth, Andrew Holme, Hiroki
|
||||
Honda, Alex Hornung, Pierre-Henri Horrein, David Horton, Peter Horvath, Jae
|
||||
Hossell, Kuoping Hsu, Alan Hunter, James Hutchinson, Anderson Ignacio da
|
||||
Silva, Jamie Iles, Thomas J Whatson, Ben Jackson, Mark Jackson Pulver,
|
||||
Shareef Jalloq, Marlon James, Krzysztof Jankowski, HyungKi Jeong, Iztok
|
||||
Jeras, James Johnson, Christophe Joly, Franck Jullien, James Jung, Mike
|
||||
Kagen, Arthur Kahlich, Kaalia Kahn, Guy-Armand Kamendje, Vasu Kandadi,
|
||||
Kanad Kanhere, Patricio Kaplan, Pieter Kapsenberg, Rafal Kapuscik, Ralf
|
||||
Karge, Dan Katz, Sol Katzman, Ian Kennedy, Michael Killough, Jonathan
|
||||
Kimmitt, Olof Kindgren, Kevin Kiningham, Dan Kirkham, Sobhan Klnv, Gernot
|
||||
Koch, Jack Koenig, Soon Koh, Nathan Kohagen, Steve Kolecki, Brett Koonce,
|
||||
Will Korteland, Wojciech Koszek, Varun Koyyalagunta, Markus Krause, David
|
||||
Kravitz, Roland Kruse, Andreas Kuster, Sergey Kvachonok, Ed Lander, Steve
|
||||
Lang, Stephane Laurent, Walter Lavino, Christian Leber, Larry Lee, Igor
|
||||
Lesik, John Li, Eivind Liland, Charlie Lind, Andrew Ling, Jiuyang Liu, Paul
|
||||
Liu, Derek Lockhart, Jake Longo, Geza Lore, Arthur Low, Stefan Ludwig, Dan
|
||||
Lussier, Fred Ma, Duraid Madina, Odd Magne Reitan, Affe Mao, Julien
|
||||
Margetts, Mark Marshall, Alfonso Martinez, Unai Martinez-Corral, Yves
|
||||
Mathieu, Patrick Maupin, Conor McCullough, Jason McMullan, Elliot Mednick,
|
||||
Wim Michiels, Miodrag Milanovic, Peter Monsson, Sean Moore, Dennis
|
||||
Muhlestein, John Murphy, Matt Myers, Nathan Myers, Richard Myers, Dimitris
|
||||
Nalbantis, Peter Nelson, Bob Newgard, Paul Nitza, Yossi Nivin, Pete Nixon,
|
||||
Lisa Noack, Mark Nodine, Kuba Ober, Andreas Olofsson, Baltazar Ortiz,
|
||||
Aleksander Osman, Don Owen, James Pallister, Vassilis Papaefstathiou, Brad
|
||||
Parker, Dan Petrisko, Maciej Piechotka, David Pierce, Cody Piersall,
|
||||
Dominic Plunkett, David Poole, Mike Popoloski, Roman Popov, Rich Porter,
|
||||
Niranjan Prabhu, Usha Priyadharshini, Prateek Puri, Marshal Qiao, Nandu
|
||||
Raj, Danilo Ramos, Chris Randall, Anton Rapp, Josh Redford, Frederic
|
||||
Requin, Dustin Richmond, Samuel Riedel, Eric Rippey, Oleg Rodionov, Ludwig
|
||||
Rogiers, Paul Rolfe, Arjen Roodselaar, Tobias Rosenkranz, Huang Rui, Denis
|
||||
Rystsov, John Sanguinetti, Galen Seitz, Joseph Shaker, Salman Sheikh, Yu
|
||||
Sheng Lin, Hao Shi, Mike Shinkarovsky, Rafael Shirakawa, Jeffrey Short, Fan
|
||||
Shupei, Rodney Sinclair, Steven Slatter, Brian Small, Garrett Smith, Tim
|
||||
Snyder, Wilson Snyder, Maciej Sobkowski, Stan Sokorac, Alex Solomatnikov,
|
||||
Wei Song, Art Stamness, David Stanford, John Stevenson, Pete Stevenson,
|
||||
Patrick Stewart, Rob Stoddard, Todd Strader, John Stroebel, Sven Stucki,
|
||||
Howard Su, Emerson Suguimoto, Gene Sullivan, Wai Sum Mong, Qingyao Sun,
|
||||
Renga Sundararajan, Rupert Swarbrick, Yutetsu Takatsukasa, Thierry Tambe,
|
||||
Drew Taussig, Peter Tengstrand, Wesley Terpstra, Rui Terra, Stefan Thiede,
|
||||
Gary Thomas, Ian Thompson, Kevin Thompson, Mike Thyer, Hans Tichelaar,
|
||||
Viktor Tomov, Steve Tong, Alex Torregrosa, Michael Tresidder, David Turner,
|
||||
Neil Turton, Cong Van Nguyen, Hans Van Antwerpen, Jan Van Winkel, Sebastien
|
||||
Van Cauwenberghe, Laurens van Dam, Leendert van Doorn, Srini Vemuri, Yuri
|
||||
Hossell, Kuoping Hsu, Teng Huang, Steven Hugg, Alan Hunter, James
|
||||
Hutchinson, Ehab Ibrahim, Edgar E. Iglesias, Jamie Iles, Vighnesh Iyer, Ben
|
||||
Jackson, Shareef Jalloq, Marlon James, Krzysztof Jankowski, HyungKi Jeong,
|
||||
Iztok Jeras, Alexandre Joannou, James Johnson, Christophe Joly, Franck
|
||||
Jullien, James Jung, Mike Kagen, Arthur Kahlich, Kaalia Kahn, Guy-Armand
|
||||
Kamendje, Vasu Kandadi, Kanad Kanhere, Patricio Kaplan, Pieter Kapsenberg,
|
||||
Rafal Kapuscik, Ralf Karge, Per Karlsson, Dan Katz, Sol Katzman, Ian
|
||||
Kennedy, Michael Killough, Sun Kim, Jonathan Kimmitt, Olof Kindgren, Kevin
|
||||
Kiningham, Dan Kirkham, Aleksander Kiryk, Sobhan Klnv, Gernot Koch, Jack
|
||||
Koenig, Soon Koh, Nathan Kohagen, Steve Kolecki, Brett Koonce, Will
|
||||
Korteland, Wojciech Koszek, Varun Koyyalagunta, Arkadiusz Kozdra, Markus
|
||||
Krause, David Kravitz, Roland Kruse, Andreas Kuster, Sergey Kvachonok,
|
||||
Charles Eric LaForest, Ed Lander, Steve Lang, Stephane Laurent, Walter
|
||||
Lavino, Christian Leber, Larry Lee, Yoda Lee, Michaël Lefebvre, Igor Lesik,
|
||||
John Li, Eivind Liland, Yu Sheng Lin, Charlie Lind, Andrew Ling, Jiuyang
|
||||
Liu, Paul Liu, Derek Lockhart, Jake Longo, Geza Lore, Arthur Low, Stefan
|
||||
Ludwig, Dan Lussier, Fred Ma, Duraid Madina, Affe Mao, Julien Margetts,
|
||||
Mark Marshall, Alfonso Martinez, Unai Martinez-Corral, Adrien Le Masle,
|
||||
Yves Mathieu, Patrick Maupin, Conor McCullough, Jason McMullan, Elliot
|
||||
Mednick, David Metz, Wim Michiels, Miodrag Milanovic, Kevin Millis, Wai Sum
|
||||
Mong, Peter Monsson, Sean Moore, Dennis Muhlestein, John Murphy, Matt
|
||||
Myers, Nathan Myers, Richard Myers, Dimitris Nalbantis, Peter Nelson, Bob
|
||||
Newgard, Rachit Nigam, Paul Nitza, Yossi Nivin, Pete Nixon, Lisa Noack,
|
||||
Mark Nodine, Kuba Ober, Andreas Olofsson, Baltazar Ortiz, Aleksander Osman,
|
||||
Don Owen, James Pallister, Vassilis Papaefstathiou, Brad Parker, Morten
|
||||
Borup Petersen, Dan Petrisko, Maciej Piechotka, David Pierce, Cody
|
||||
Piersall, Michael Platzer, Dominic Plunkett, David Poole, Mike Popoloski,
|
||||
Roman Popov, Rich Porter, Stefan Post, Niranjan Prabhu, Damien Pretet, Usha
|
||||
Priyadharshini, Mark Jackson Pulver, Prateek Puri, Marshal Qiao, Nandu Raj,
|
||||
Kamil Rakoczy, Danilo Ramos, Drew Ranck, Chris Randall, Anton Rapp, Josh
|
||||
Redford, Odd Magne Reitan, Frederic Requin, Dustin Richmond, Samuel Riedel,
|
||||
Alberto Del Rio, Eric Rippey, Oleg Rodionov, Ludwig Rogiers, Paul Rolfe,
|
||||
Arjen Roodselaar, Tobias Rosenkranz, Ryszard Rozak, Huang Rui, Graham
|
||||
Rushton, Jan Egil Ruud, Denis Rystsov, John Sanguinetti, Martin Schmidt,
|
||||
Julie Schwartz, Galen Seitz, Joseph Shaker, Salman Sheikh, Hao Shi, Mike
|
||||
Shinkarovsky, Rafael Shirakawa, Jeffrey Short, Fan Shupei, Anderson Ignacio
|
||||
da Silva, Rodney Sinclair, Ameya Vikram Singh, Steven Slatter, Mladen
|
||||
Slijepcevic, Brian Small, Garrett Smith, Tim Snyder, Wilson Snyder, Maciej
|
||||
Sobkowski, Stan Sokorac, Alex Solomatnikov, Flavien Solt, Wei Song, Trefor
|
||||
Southwell, Martin Stadler, Art Stamness, David Stanford, John Stevenson,
|
||||
Pete Stevenson, Patrick Stewart, Rob Stoddard, Todd Strader, John Stroebel,
|
||||
Sven Stucki, Howard Su, Emerson Suguimoto, Gene Sullivan, Qingyao Sun,
|
||||
Renga Sundararajan, Gustav Svensk, Rupert Swarbrick, Yutetsu Takatsukasa,
|
||||
Thierry Tambe, Drew Taussig, Jose Tejada, Peter Tengstrand, Wesley
|
||||
Terpstra, Rui Terra, Stefan Thiede, Gary Thomas, Ian Thompson, Kevin
|
||||
Thompson, Mike Thyer, Hans Tichelaar, Viktor Tomov, Steve Tong, Topa
|
||||
Topino, Alex Torregrosa, Michael Tresidder, David Turner, Neil Turton, Mike
|
||||
Urbach, Hans Van Antwerpen, Sebastien Van Cauwenberghe, Laurens van Dam,
|
||||
Leendert van Doorn, Cong Van Nguyen, Jan Van Winkel, Srini Vemuri, Yuri
|
||||
Victorovich, Bogdan Vukobratovic, Holger Waechtler, Philipp Wagner, Stefan
|
||||
Wallentowitz, Shawn Wang, Paul Wasson, Greg Waters, Thomas Watts, Eugene
|
||||
Weber, David Welch, Martin Whitaker, Marco Widmer, Leon Wildman, Daniel
|
||||
Wilkerson, Gerald Williams, Trevor Williams, Jeff Winston, Joshua Wise,
|
||||
Clifford Wolf, Tobias Wolfel, Johan Wouters, Paul Wright, Junyi Xi, Ding
|
||||
Xiaoliang, Jie Xu, Mandy Xu, Yinan Xu, Luke Yang, Amir Yazdanbakhsh, and
|
||||
Keyi Zhang.
|
||||
Wallentowitz, Shawn Wang, Zhanglei Wang, Paul Wasson, Greg Waters, Thomas
|
||||
Watts, Eugene Weber, David Welch, Thomas J Whatson, Martin Whitaker, Marco
|
||||
Widmer, Leon Wildman, Daniel Wilkerson, Gerald Williams, Trevor Williams,
|
||||
Jeff Winston, Joshua Wise, Clifford Wolf, Tobias Wolfel, Johan Wouters,
|
||||
Paul Wright, Junyi Xi, Ding Xiaoliang, Jie Xu, Mandy Xu, Yinan Xu, Luke
|
||||
Yang, Amir Yazdanbakhsh, Keyi Zhang, and Xi Zhang.
|
||||
|
||||
Thanks to them, and all those we've missed including above, or wished to
|
||||
remain anonymous.
|
||||
@@ -151,6 +163,10 @@ In 2018, Verilator 4.000 was released with multithreaded support.
|
||||
In 2019, Verilator joined the `CHIPS Alliance
|
||||
<https://chipsalliance.org>`_.
|
||||
|
||||
In 2022, Verilator 5.000 was released with IEEE scheduling semantics,
|
||||
fork/join, delay handling, DFG performance optimizations, and other
|
||||
improvements.
|
||||
|
||||
Currently, various language features and performance enhancements are added
|
||||
as the need arises. Verilator is now about 3x faster than in 2002, and is
|
||||
faster than most (if not every) other simulator.
|
||||
as the need arises, with a focus towards getting to full Universal
|
||||
Verification Methodology (UVM, IEEE 1800.2-2017) support.
|
||||
|
||||
@@ -7,9 +7,15 @@ Deprecations
|
||||
The following deprecated items are scheduled for future removal:
|
||||
|
||||
C++11 compiler support
|
||||
Verilator currently requires C++11 or newer compilers. Verilator will
|
||||
require C++14 or newer compilers for both compiling Verilator and
|
||||
compiling Verilated models no sooner than January 2023.
|
||||
Verilator currently requires a C++20 or newer compiler for timing, and a
|
||||
C++11 or newer compiler for no-timing.
|
||||
|
||||
Verilator will require C++14 or newer compilers for both compiling
|
||||
Verilator and compiling Verilated models with --no-timing no sooner than
|
||||
January 2023.
|
||||
|
||||
Verilator will require C++20 or newer compilers for both compiling
|
||||
Verilator and compiling all Verilated models no sooner than January 2025.
|
||||
|
||||
Verilated_heavy.h
|
||||
The legacy "verilated_heavy.h" include was replaced with just including
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
.. Copyright 2003-2022 by Wilson Snyder.
|
||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
.. _Example Create-Binary Execution:
|
||||
|
||||
Example Create-Binary Execution
|
||||
===============================
|
||||
|
||||
We'll compile this SystemVerilog example into a Verilated simulation binary. For
|
||||
an example that discusses the next level of detail see :ref:`Example C++
|
||||
Execution`.
|
||||
|
||||
.. include:: example_common_install.rst
|
||||
|
||||
Now, let's create an example Verilog file:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir test_our
|
||||
cd test_our
|
||||
|
||||
cat >our.v <<'EOF'
|
||||
module our;
|
||||
initial begin $display("Hello World"); $finish; end
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
Now we run Verilator on our little example.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
verilator --binary -j 0 -Wall our.v
|
||||
|
||||
Breaking this command down:
|
||||
|
||||
#. :vlopt:`--binary` telling Verilator to do everything needed to create a
|
||||
simulation executable.
|
||||
|
||||
#. :vlopt:`-j` `0` to Verilate using use as many CPU threads as the machine
|
||||
has.
|
||||
|
||||
#. :vlopt:`-Wall` so Verilator has stronger lint warnings
|
||||
enabled.
|
||||
|
||||
#. An finally, :command:`our.v` which is our SystemVerilog design file.
|
||||
|
||||
And now we run it:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
obj_dir/Vour
|
||||
|
||||
And we get as output:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
Hello World
|
||||
- our.v:2: Verilog $finish
|
||||
|
||||
Really, you're better off using a Makefile to run the steps for you so when
|
||||
your source changes it will automatically run all of the appropriate steps.
|
||||
To aid this Verilator can create a makefile dependency file. For examples
|
||||
that do this see the :file:`examples` directory in the distribution.
|
||||
@@ -43,13 +43,10 @@ Now we run Verilator on our little example.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
verilator -Wall --cc --exe --build sim_main.cpp our.v
|
||||
verilator --cc --exe --build -j 0 -Wall sim_main.cpp our.v
|
||||
|
||||
Breaking this command down:
|
||||
|
||||
#. :vlopt:`-Wall` so Verilator has stronger lint warnings
|
||||
enabled.
|
||||
|
||||
#. :vlopt:`--cc` to get C++ output (versus e.g. SystemC
|
||||
or only linting).
|
||||
|
||||
@@ -61,6 +58,12 @@ Breaking this command down:
|
||||
own compile rules, and run make yourself as we show in :ref:`Example
|
||||
SystemC Execution`.)
|
||||
|
||||
#. :vlopt:`-j` `0' to Verilate using use as many CPU threads as the machine
|
||||
has.
|
||||
|
||||
#. :vlopt:`-Wall` so Verilator has stronger lint warnings
|
||||
enabled.
|
||||
|
||||
#. An finally, :command:`our.v` which is our SystemVerilog design file.
|
||||
|
||||
Once Verilator completes we can see the generated C++ code under the
|
||||
|
||||
@@ -10,6 +10,8 @@ See the ``examples/`` directory that is part of the distribution, and
|
||||
is installed (in a OS-specific place, often in e.g.
|
||||
``/usr/local/share/verilator/examples``). These examples include:
|
||||
|
||||
examples/make_hello_binary
|
||||
Example GNU-make simple Verilog->binary conversion
|
||||
examples/make_hello_c
|
||||
Example GNU-make simple Verilog->C++ conversion
|
||||
examples/make_hello_sc
|
||||
|
||||
@@ -43,7 +43,7 @@ Now we run Verilator on our little example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
verilator -Wall --sc --exe sc_main.cpp our.v
|
||||
verilator --sc --exe -Wall sc_main.cpp our.v
|
||||
|
||||
This example does not use --build, therefore we need to explicitly compile
|
||||
it:
|
||||
|
||||
@@ -17,6 +17,7 @@ This section covers the following examples:
|
||||
:maxdepth: 1
|
||||
:hidden:
|
||||
|
||||
example_binary.rst
|
||||
example_cc.rst
|
||||
example_sc.rst
|
||||
example_dist.rst
|
||||
|
||||
@@ -72,7 +72,7 @@ Summary:
|
||||
|
||||
.. option:: +verilator+prof+threads+window+<value>
|
||||
|
||||
Deprecated. Alias for :vlopt:`+verilator+prof+exec+window+\<filename\>`
|
||||
Deprecated. Alias for :vlopt:`+verilator+prof+exec+window+\<value\>`
|
||||
|
||||
.. option:: +verilator+prof+vlt+file+<filename>
|
||||
|
||||
|
||||
+173
-36
@@ -115,12 +115,12 @@ Summary:
|
||||
|
||||
Using this argument will likely cause incorrect simulation.
|
||||
|
||||
.. option:: --bin <filename>
|
||||
.. option:: --binary
|
||||
|
||||
Rarely needed. Override the default filename for Verilator itself.
|
||||
When a dependency (.d) file is created, this filename will become a
|
||||
source dependency, such that a change in this binary will have make
|
||||
rebuild the output files.
|
||||
Create a Verilated simulator binary. Alias for :vlopt:`--main`
|
||||
:vlopt:`--exe` :vlopt:`--build` :vlopt:`--timing`.
|
||||
|
||||
See also :vlopt:`-j`.
|
||||
|
||||
.. option:: --build
|
||||
|
||||
@@ -129,6 +129,24 @@ Summary:
|
||||
is also used). Verilator manages the build itself, and for this --build
|
||||
requires GNU Make to be available on the platform.
|
||||
|
||||
.. option:: --build-dep-bin <filename>
|
||||
|
||||
Rarely needed. When a dependency (.d) file is created, this filename
|
||||
will become a source dependency, such that a change in this binary will
|
||||
have make rebuild the output files. Defaults to the full path to the
|
||||
Verilator binary.
|
||||
|
||||
This option was named `--bin` prior to version 4.228.
|
||||
|
||||
.. option:: --build-jobs [<value>]
|
||||
|
||||
Specify the level of parallelism for :vlopt:`--build`. If zero, uses the
|
||||
number of threads in the current hardware. Otherwise, the <value> must
|
||||
be a positive integer specifying the maximum number of parallel build
|
||||
jobs.
|
||||
|
||||
See also :vlopt:`-j`.
|
||||
|
||||
.. option:: --cc
|
||||
|
||||
Specifies C++ without SystemC output mode; see also :vlopt:`--sc`
|
||||
@@ -158,10 +176,7 @@ Summary:
|
||||
|
||||
.. option:: --clk <signal-name>
|
||||
|
||||
With :vlopt:`--clk`, the specified signal-name is taken as a root clock
|
||||
into the model; Verilator will mark the signal as clocker and
|
||||
propagate the clocker attribute automatically to other signals downstream in
|
||||
that clock tree.
|
||||
With :vlopt:`--clk`, the specified signal is marked as a clock signal.
|
||||
|
||||
The provided signal-name is specified using a RTL hierarchy path. For
|
||||
example, v.foo.bar. If the signal is the input to top-module, then
|
||||
@@ -173,11 +188,11 @@ Summary:
|
||||
individual bits, Verilator will attempt to decompose the vector and
|
||||
connect the single-bit clock signals.
|
||||
|
||||
The clocker attribute is useful in cases where Verilator does not
|
||||
properly distinguish clock signals from other data signals. Using
|
||||
clocker will cause the signal indicated to be considered a clock, and
|
||||
remove it from the combinatorial logic reevaluation checking code. This
|
||||
may greatly improve performance.
|
||||
In versions prior to 5.000, the clocker attribute is useful in cases where
|
||||
Verilator does not properly distinguish clock signals from other data
|
||||
signals. Using clocker will cause the signal indicated to be considered a
|
||||
clock, and remove it from the combinatorial logic reevaluation checking
|
||||
code. This may greatly improve performance.
|
||||
|
||||
.. option:: --no-clk <signal-name>
|
||||
|
||||
@@ -255,8 +270,8 @@ Summary:
|
||||
generally is a less-optimized binary with symbols present (so GDB can be used on it).
|
||||
* Enable debugging messages (equivalent to :vlopt:`--debugi 3 <--debugi>`).
|
||||
* Enable internal assertions (equivalent to :vlopt:`--debug-check`).
|
||||
* Enable intermediate form dump files (equivalent to :vlopt:`--dump-treei 3
|
||||
<--dump-treei>`).
|
||||
* Enable intermediate form dump files (equivalent to :vlopt:`--dumpi-tree 3
|
||||
<--dumpi-tree>`).
|
||||
* Leak to make node numbers unique (equivalent to :vlopt:`--debug-leak
|
||||
<--no-debug-leak>`.
|
||||
* Call abort() instead of exit() if there are any errors (so GDB can see
|
||||
@@ -347,25 +362,31 @@ Summary:
|
||||
|
||||
touch foo.v ; verilator -E --dump-defines foo.v
|
||||
|
||||
.. option:: --dump-dfg
|
||||
|
||||
Rarely needed. Enable dumping DfgGraph .dot debug files with dumping
|
||||
level 3.
|
||||
|
||||
.. option:: --dump-graph
|
||||
|
||||
Rarely needed. Enable dumping V3Graph .dot debug files with dumping
|
||||
level 3. Before Verilator 4.228, :vlopt:`--dump-tree` used
|
||||
to include this option.
|
||||
|
||||
.. option:: --dump-tree
|
||||
|
||||
Rarely needed. Enable writing .tree debug files with dumping level 3,
|
||||
Rarely needed. Enable dumping Ast .tree debug files with dumping level 3,
|
||||
which dumps the standard critical stages. For details on the format see
|
||||
the Verilator Internals manual. :vlopt:`--dump-tree` is enabled
|
||||
automatically with :vlopt:`--debug`, so :vlopt:`--debug --no-dump-tree
|
||||
<--dump-tree>` may be useful if the dump files are large and not
|
||||
desired.
|
||||
|
||||
.. option:: --dump-treei <level>
|
||||
.. option:: --dump-tree-dot
|
||||
|
||||
.. option:: --dump-treei-<srcfile> <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal tree dumping level
|
||||
globally to a specific dumping level or set the specified Verilator
|
||||
source file to the specified tree dumping level (e.g.
|
||||
:vlopt:`--dump-treei-V3Order 9 <--dump-treei>`). Level 0 disables dumps
|
||||
and is equivalent to :vlopt:`--no-dump-tree <--dump-tree>`. Level 9
|
||||
enables dumping of every stage.
|
||||
Rarely needed. Enable dumping Ast .tree.dot debug files in Graphviz
|
||||
Dot format. This option implies :vlopt:`--dump-tree`, unless
|
||||
:vlopt:`--dumpi-tree` was passed explicitly.
|
||||
|
||||
.. option:: --dump-tree-addrids
|
||||
|
||||
@@ -376,6 +397,33 @@ Summary:
|
||||
by a newly allocated node after a node with the same address has been
|
||||
dumped then freed.
|
||||
|
||||
.. option:: --dump-<srcfile>
|
||||
|
||||
Rarely needed - for developer use. Enable all dumping in the given
|
||||
source file at level 3.
|
||||
|
||||
.. option:: --dumpi-dfg <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal DfgGraph dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-graph <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal V3Graph dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-tree <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal Ast dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-<srcfile> <level>
|
||||
|
||||
Rarely needed - for developer use. Set the dumping level in the
|
||||
specified Verilator source file to the specified value (e.g.
|
||||
`--dumpi-V3Order 9`). Level 0 disables dumps and is equivalent to
|
||||
`--no-dump-<srcfile>`. Level 9 enables dumping of everything.
|
||||
|
||||
.. option:: -E
|
||||
|
||||
Preprocess the source code, but do not compile, similar to C++
|
||||
@@ -453,10 +501,37 @@ Summary:
|
||||
|
||||
.. option:: -fno-const
|
||||
|
||||
.. options: -fno-const-before-dfg
|
||||
|
||||
Do not apply any global expression folding prior to the DFG pass. This
|
||||
option is solely for the purpose of DFG testing and should not be used
|
||||
otherwise.
|
||||
|
||||
.. option:: -fno-const-bit-op-tree
|
||||
|
||||
.. option:: -fno-dedup
|
||||
|
||||
.. option:: -fno-dfg
|
||||
|
||||
Disable all use of the DFG based combinational logic optimizer.
|
||||
Alias for :vlopt:`-fno-dfg-pre-inline` and :vlopt:`-fno-dfg-post-inline`.
|
||||
|
||||
.. option:: -fno-dfg-peephole
|
||||
|
||||
Disable the DFG peephole optimizer.
|
||||
|
||||
.. option:: -fno-dfg-peephole-<pattern>
|
||||
|
||||
Disable individual DFG peephole optimizer pattern.
|
||||
|
||||
.. option:: -fno-dfg-pre-inline
|
||||
|
||||
Do not apply the DFG optimizer before inlining.
|
||||
|
||||
.. option:: -fno-dfg-post-inline
|
||||
|
||||
Do not apply the DFG optimizer after inlining.
|
||||
|
||||
.. option:: -fno-expand
|
||||
|
||||
.. option:: -fno-gate
|
||||
@@ -573,6 +648,15 @@ Summary:
|
||||
a newline and exit immediately. This can be useful in makefiles. See
|
||||
also :vlopt:`-V`, and the various :file:`*.mk` files.
|
||||
|
||||
.. option:: --get-supported <feature>
|
||||
|
||||
If the given feature is supported, print "1" and exit
|
||||
immediately. Otherwise, print a newline and exit immediately. This can
|
||||
be useful in makefiles. See also :vlopt:`-V`, and the various
|
||||
:file:`*.mk` files.
|
||||
|
||||
Feature may be one of the following: COROUTINES, SYSTEMC.
|
||||
|
||||
.. option:: --help
|
||||
|
||||
Displays this message and program version and exits.
|
||||
@@ -615,11 +699,10 @@ Summary:
|
||||
|
||||
.. option:: -j [<value>]
|
||||
|
||||
Specify the level of parallelism for :vlopt:`--build`. The <value> must
|
||||
be a positive integer specifying the maximum number of parallel build
|
||||
jobs, or can be omitted. When <value> is omitted, the build will not try
|
||||
to limit the number of parallel build jobs but attempt to execute all
|
||||
independent build steps in parallel.
|
||||
Specify the level of parallelism for :vlopt:`--build` if
|
||||
:vlopt:`--build-jobs` isn't provided. If zero, uses the number of threads
|
||||
in the current hardware. Otherwise, the <value> must be a positive
|
||||
integer specifying the maximum number of parallel build jobs.
|
||||
|
||||
.. option:: --l2-name <value>
|
||||
|
||||
@@ -667,6 +750,8 @@ Summary:
|
||||
model has a time resolution that is always compatible with the time
|
||||
precision of the upper instantiating module.
|
||||
|
||||
Designs compiled using this option cannot use :vlopt:`--timing` with delays.
|
||||
|
||||
See also :vlopt:`--protect-lib`.
|
||||
|
||||
.. option:: +libext+<ext>[+<ext>][...]
|
||||
@@ -709,6 +794,24 @@ Summary:
|
||||
(e.g. ``-MAKEFLAGS -l -MAKEFLAGS -k``). Use of this option should not be
|
||||
required for simple builds using the host toolchain.
|
||||
|
||||
.. option:: --main
|
||||
|
||||
Generates a top-level C++ main() file that supports parsing arguments,
|
||||
but does not drive any inputs. This is sufficient to use for top-level
|
||||
SystemVerilog designs that has no inputs.
|
||||
|
||||
This option can also be used once to generate a main .cpp file as a
|
||||
starting point for editing. Copy it outside the obj directory, manually
|
||||
edit, and then pass the filename on later Verilator command line
|
||||
invocations.
|
||||
|
||||
Typically used with :vlopt:`--timing` to support delay-generated clocks,
|
||||
and :vlopt:`--build`.
|
||||
|
||||
Implies :vlopt:`--cc` if no other output mode was provided.
|
||||
|
||||
See also :vlopt:`--binary`.
|
||||
|
||||
.. option:: --max-num-width <value>
|
||||
|
||||
Set the maximum number literal width (e.g. in 1024'd22 this it the
|
||||
@@ -770,6 +873,10 @@ Summary:
|
||||
|
||||
.. option:: --no-order-clock-delay
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions prior to 5.000:
|
||||
|
||||
Rarely needed. Disables a bug fix for ordering of clock enables with
|
||||
delayed assignments. This option should only be used when suggested by
|
||||
the developers.
|
||||
@@ -972,6 +1079,8 @@ Summary:
|
||||
in the distribution for a demonstration of how to build and use the DPI
|
||||
library.
|
||||
|
||||
Designs compiled using this option cannot use :vlopt:`--timing` with delays.
|
||||
|
||||
.. option:: --public
|
||||
|
||||
This is only for historical debug use. Using it may result in
|
||||
@@ -1163,6 +1272,16 @@ Summary:
|
||||
module. As "1fs" is the finest time precision it may be desirable to
|
||||
always use a precision of "1fs".
|
||||
|
||||
.. option:: --timing
|
||||
|
||||
.. option:: --no-timing
|
||||
|
||||
Enables/disables support for timing constructs such as delays, event
|
||||
controls (unless it's at the top of a process), wait statements, and joins.
|
||||
When disabled, timing control constructs are ignored the same way as they
|
||||
were in earlier versions of Verilator. Enabling this feature requires a C++
|
||||
compiler with coroutine support (GCC 10, Clang 5, or newer).
|
||||
|
||||
.. option:: --top <topname>
|
||||
|
||||
.. option:: --top-module <topname>
|
||||
@@ -1326,8 +1445,7 @@ Summary:
|
||||
|
||||
Enable all code style warnings, including code style warnings that are
|
||||
normally disabled by default. Equivalent to :vlopt:`-Wwarn-lint`
|
||||
:vlopt:`-Wwarn-style`. Excludes some specialty warnings,
|
||||
i.e. IMPERFECTSCH.
|
||||
:vlopt:`-Wwarn-style`. Excludes some specialty warnings.
|
||||
|
||||
.. option:: -Werror-<message>
|
||||
|
||||
@@ -1370,7 +1488,8 @@ Summary:
|
||||
equivalent to ``-Wno-ALWCOMBORDER -Wno-BSSPACE -Wno-CASEINCOMPLETE
|
||||
-Wno-CASEOVERLAP -Wno-CASEX -Wno-CASTCONST -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS
|
||||
-Wno-ENDLABEL -Wno-IMPLICIT -Wno-LITENDIAN -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED -Wno-UNUSED
|
||||
-Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED
|
||||
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
|
||||
-Wno-WIDTH`` plus the list shown for Wno-style.
|
||||
|
||||
It is strongly recommended you cleanup your code rather than using this
|
||||
@@ -1382,7 +1501,8 @@ Summary:
|
||||
Disable all code style related warning messages (note by default they are
|
||||
already disabled). This is equivalent to ``-Wno-DECLFILENAME -Wno-DEFPARAM
|
||||
-Wno-EOFNEWLINE -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNUSED
|
||||
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN
|
||||
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
|
||||
-Wno-VARHIDDEN``.
|
||||
|
||||
.. option:: -Wpedantic
|
||||
@@ -1411,7 +1531,7 @@ Summary:
|
||||
Enable all code style related warning messages. This is equivalent to
|
||||
``-Wwarn ASSIGNDLY -Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-EOFNEWLINE
|
||||
-Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN
|
||||
-Wwarn-UNUSED -Wwarn-VARHIDDEN``.
|
||||
-Wwarn-UNUSEDGENVAR -Wwarn-UNUSEDPARAM -Wwarn-UNUSEDSIGNAL -Wwarn-VARHIDDEN``.
|
||||
|
||||
.. option:: --x-assign 0
|
||||
|
||||
@@ -1580,6 +1700,10 @@ The grammar of configuration commands is as follows:
|
||||
|
||||
.. option:: clock_enable -module "<modulename>" -var "<signame>"
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions prior to 5.000:
|
||||
|
||||
Indicate the signal is used to gate a clock, and the user takes
|
||||
responsibility for insuring there are no races related to it.
|
||||
|
||||
@@ -1743,6 +1867,19 @@ The grammar of configuration commands is as follows:
|
||||
|
||||
Same as :option:`/*verilator&32;split_var*/` metacomment.
|
||||
|
||||
.. option:: timing_on [-file "<filename>" [-lines <line> [ - <line>]]]
|
||||
|
||||
.. option:: timing_off [-file "<filename>" [-lines <line> [ - <line>]]]
|
||||
|
||||
Enables/disables timing constructs for the specified file and lines.
|
||||
When disabled, all timing control constructs in the specified source
|
||||
code locations are ignored the same way as with the
|
||||
:option:`--no-timing`, and code:`fork`/:code:`join*` blocks are
|
||||
converted into :code:`begin`/:code:`end` blocks.
|
||||
|
||||
Same as :option:`/*verilator&32;timing_on*/`,
|
||||
:option:`/*verilator&32;timing_off*/` metacomments.
|
||||
|
||||
.. option:: tracing_on [-file "<filename>" [-lines <line> [ - <line> ]]]
|
||||
|
||||
.. option:: tracing_off [-file "<filename>" [-lines <line> [ - <line> ]]]
|
||||
|
||||
@@ -151,6 +151,13 @@ or "`ifdef`"'s may break other tools.
|
||||
Take remaining text up to the next :option:`\`verilog` mode switch and
|
||||
treat it as Verilator configuration commands. See :ref:`Configuration Files`.
|
||||
|
||||
.. option:: `VERILATOR_TIMING
|
||||
|
||||
The VERILATOR_TIMING define is set when :vlopt:`--timing` is used to
|
||||
allow an "\`ifdef" of code dependent on this feature. Note this define
|
||||
is not affected by the :option:`timing_off` configuration file option
|
||||
nor timing metacomments.
|
||||
|
||||
.. option:: `verilog
|
||||
|
||||
Switch back to processing Verilog code after a
|
||||
@@ -160,6 +167,10 @@ or "`ifdef`"'s may break other tools.
|
||||
|
||||
.. option:: /*verilator&32;clock_enable*/
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions prior to 5.000:
|
||||
|
||||
Used after a signal declaration to indicate the signal is used to gate a
|
||||
clock, and the user takes responsibility for insuring there are no races
|
||||
related to it. (Typically by adding a latch, and running static timing
|
||||
@@ -174,9 +185,7 @@ or "`ifdef`"'s may break other tools.
|
||||
|
||||
The clock_enable attribute will cause the clock gate to be ignored in
|
||||
the scheduling algorithm, sometimes required for correct clock behavior,
|
||||
and always improving performance. It's also a good idea to enable the
|
||||
:option:`IMPERFECTSCH` warning, to ensure all clock enables are properly
|
||||
recognized.
|
||||
and always improving performance.
|
||||
|
||||
Same as :option:`clock_enable` configuration file option.
|
||||
|
||||
@@ -184,9 +193,7 @@ or "`ifdef`"'s may break other tools.
|
||||
|
||||
.. option:: /*verilator&32;no_clocker*/
|
||||
|
||||
Specifies that the signal is used as clock or not. This information is
|
||||
used by Verilator to mark the signal and any derived signals as
|
||||
clocker. See :vlopt:`--clk`.
|
||||
Specifies that the signal is used as clock or not. See :vlopt:`--clk`.
|
||||
|
||||
Same as :option:`clocker` and :option:`no_clocker` in configuration
|
||||
files.
|
||||
@@ -503,6 +510,22 @@ or "`ifdef`"'s may break other tools.
|
||||
Verilator) text that should be passed through to the XML output as a tag,
|
||||
for use by downstream applications.
|
||||
|
||||
.. option:: /*verilator&32;timing_off*/
|
||||
|
||||
Ignore all timing constructs after this metacomment. All timing controls
|
||||
behave as if they were not there (the same way as with
|
||||
:option:`--no-timing`), and :code:`fork`/:code:`join*` blocks are
|
||||
converted into :code:`begin`/:code:`end` blocks.
|
||||
|
||||
Same as :option:`timing_off` configuration file option.
|
||||
|
||||
.. option:: /*verilator&32;timing_on*/
|
||||
|
||||
Re-enable all timing constructs after this metacomment (only applicable
|
||||
after :option:`timing_off`).
|
||||
|
||||
Same as :option:`timing_on` configuration file option.
|
||||
|
||||
.. option:: /*verilator&32;trace_init_task*/
|
||||
|
||||
Attached to a DPI import to indicate that function should be called when
|
||||
|
||||
@@ -93,7 +93,7 @@ For --cc/--sc, it creates:
|
||||
* - *{prefix}{each_verilog_module}{__n}*\ .cpp
|
||||
- Additional lower C++ files
|
||||
* - *{prefix}{each_verilog_module}{__DepSet_hash__n}*\ .cpp
|
||||
- Additional lower C++ files (hased to reduce build times)
|
||||
- Additional lower C++ files (hashed to reduce build times)
|
||||
|
||||
For --hierarchy mode, it creates:
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ Those developing Verilator itself may also want these (see internals.rst):
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install gdb graphviz cmake clang clang-format-11 gprof lcov
|
||||
sudo apt-get install gdb graphviz cmake clang clang-format-14 gprof lcov
|
||||
sudo apt-get install yapf3
|
||||
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe
|
||||
cpan install Pod::Perldoc
|
||||
|
||||
+66
-17
@@ -95,6 +95,72 @@ keywords on case statement, as well as "unique" on if statements. However,
|
||||
"priority if" is currently ignored.
|
||||
|
||||
|
||||
Time
|
||||
====
|
||||
|
||||
With :vlopt:`--timing`, all timing controls are supported:
|
||||
|
||||
* delay statements,
|
||||
* event control statements not only at the top of a process,
|
||||
* intra-assignment timing controls,
|
||||
* net delays,
|
||||
* :code:`wait` statements,
|
||||
|
||||
as well as all flavors of :code:`fork`.
|
||||
|
||||
Compiling a verilated design that makes use of these features requires a
|
||||
compiler with C++20 coroutine support, e.g. Clang 5, GCC 10, or newer.
|
||||
|
||||
:code:`#0` delays cause Verilator to issue the :option:`ZERODLY` warning, as
|
||||
they work differently than described in the LRM. They do not schedule process
|
||||
resumption in the Inactive region, though the process will get resumed in the
|
||||
same time slot.
|
||||
|
||||
Rising/falling/turn-off delays are currently unsupported and cause the
|
||||
:option:`RISEFALLDLY` warning.
|
||||
|
||||
Minimum/typical/maximum delays are currently unsupported. The typical delay is
|
||||
always the one chosen. Such expressions cause the :option:`MINTYPMAX` warning.
|
||||
|
||||
Another consequence of using :vlopt:`--timing` is that the :vlopt:`--main`
|
||||
option generates a main file with a proper timing eval loop, eliminating the
|
||||
need for writing any driving C++ code. You can then simply compile the
|
||||
simulation (perhaps using :vlopt:`--build`) and run it.
|
||||
|
||||
With :vlopt:`--no-timing`, all timing controls cause the :option:`NOTIMING`
|
||||
error, with the exception of:
|
||||
|
||||
* delay statements – they are ignored (as they are in synthesis), though they
|
||||
do issue a :option:`STMTDLY` warning,
|
||||
* intra-assignment timing controls – they are ignored, though they do issue an
|
||||
:option:`ASSIGNDLY` warning,
|
||||
* net delays – they are ignored,
|
||||
* event controls at the top of the procedure,
|
||||
|
||||
Forks cause this error as well, with the exception of:
|
||||
|
||||
* forks with no statements,
|
||||
* :code:`fork..join` or :code:`fork..join_any` with one statement,
|
||||
* forks with :vlopt:`--bbox-unsup`.
|
||||
|
||||
If neither :vlopt:`--timing` nor :vlopt:`--no-timing` is specified, all timing
|
||||
controls cause the :option:`NEEDTIMINGOPT` error, with the exception of event
|
||||
controls at the top of the process. Forks cause this error as well, with the
|
||||
exception of:
|
||||
|
||||
* forks with no statements,
|
||||
* :code:`fork..join` or :code:`fork..join_any` with one statement,
|
||||
* forks with :vlopt:`--bbox-unsup`.
|
||||
|
||||
Timing controls and forks can also be ignored in specific files or parts of
|
||||
files. The :option:`/*verilator&32;timing_off*/` and
|
||||
:option:`/*verilator&32;timing_off*/` metacomments will make Verilator ignore
|
||||
the encompassed timing controls and forks, regardless of the chosen
|
||||
:vlopt:`--timing` or :vlopt:`--no-timing` option. This can also be achieved
|
||||
using the :option:`timing_off` and :option:`timing_off` options in Verilator
|
||||
configuration files.
|
||||
|
||||
|
||||
.. _Language Limitations:
|
||||
|
||||
Language Limitations
|
||||
@@ -180,12 +246,6 @@ structure from blocking, and another from non-blocking assignments is
|
||||
unsupported.
|
||||
|
||||
|
||||
Time
|
||||
----
|
||||
|
||||
All delays (#) are ignored, as they are in synthesis.
|
||||
|
||||
|
||||
.. _Unknown States:
|
||||
|
||||
Unknown States
|
||||
@@ -276,17 +336,6 @@ probably expect, what C does. The default behavior of Verilog is
|
||||
different.)
|
||||
|
||||
|
||||
Generated Clocks
|
||||
----------------
|
||||
|
||||
Verilator attempts to deal with generated and gated clocks correctly,
|
||||
however some cases cause problems in the scheduling algorithm which is
|
||||
optimized for performance. The safest option is to have all clocks as
|
||||
primary inputs to the model, or wires directly attached to primary inputs.
|
||||
For proper behavior clock enables may also need the
|
||||
:option:`/*verilator&32;clock_enable*/` metacomment.
|
||||
|
||||
|
||||
Gate Primitives
|
||||
---------------
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
Simulating (Verilated-Model Runtime)
|
||||
************************************
|
||||
|
||||
This section describes items related to simulating, that using a Verilated
|
||||
model's executable. For the runtime arguments to a simulated model, see
|
||||
:ref:`Simulation Runtime Arguments`.
|
||||
This section describes items related to simulating, that is, the use of a
|
||||
Verilated model's executable. For the runtime arguments to a simulated
|
||||
model, see :ref:`Simulation Runtime Arguments`.
|
||||
|
||||
|
||||
.. _Benchmarking & Optimization:
|
||||
|
||||
@@ -113,6 +113,8 @@ Hierarchy blocks have some limitations including:
|
||||
hierarchical model and pass up into another hierarchical model or the top
|
||||
module.
|
||||
|
||||
* Delays are not allowed in hierarchy blocks.
|
||||
|
||||
But, the following usage is supported:
|
||||
|
||||
* Nested hierarchy blocks. A hierarchy block may instantiate other
|
||||
|
||||
+121
-59
@@ -5,6 +5,7 @@
|
||||
Errors and Warnings
|
||||
*******************
|
||||
|
||||
.. _Disabling Warnings:
|
||||
|
||||
Disabling Warnings
|
||||
==================
|
||||
@@ -111,6 +112,8 @@ List Of Warnings
|
||||
simulators, however at one point this was a common style so disabled by
|
||||
default as a code style warning.
|
||||
|
||||
This warning is issued only if Verilator is run with :vlopt:`--no-timing`.
|
||||
|
||||
|
||||
.. option:: ASSIGNIN
|
||||
|
||||
@@ -315,16 +318,16 @@ List Of Warnings
|
||||
|
||||
.. option:: CLKDATA
|
||||
|
||||
.. TODO better example
|
||||
Historical, never issued since version 5.000.
|
||||
|
||||
Warns that clock signal is mixed used with/as data signal. The checking
|
||||
for this warning is enabled only if user has explicitly marked some
|
||||
signal as clocker using command line option or in-source meta comment
|
||||
(see :vlopt:`--clk`).
|
||||
Warned that clock signal was mixed used with/as data signal. The
|
||||
checking for this warning was enabled only if user has explicitly marked
|
||||
some signal as clocker using command line option or in-source meta
|
||||
comment (see :vlopt:`--clk`).
|
||||
|
||||
The warning can be disabled without affecting the simulation result. But
|
||||
it is recommended to check the warning as this may degrade the
|
||||
performance of the Verilated model.
|
||||
The warning could be disabled without affecting the simulation
|
||||
result. But it was recommended to check the warning as it may have
|
||||
degraded the performance of the Verilated model.
|
||||
|
||||
|
||||
.. option:: CMPCONST
|
||||
@@ -462,15 +465,11 @@ List Of Warnings
|
||||
|
||||
.. option:: DETECTARRAY
|
||||
|
||||
.. TODO better example
|
||||
Historical, never issued since version 3.862.
|
||||
|
||||
Error when Verilator tries to deal with a combinatorial loop that could
|
||||
not be flattened, and which involves a datatype which Verilator cannot
|
||||
handle, such as an unpacked struct or a large unpacked array. This
|
||||
typically occurs when :vlopt:`-Wno-UNOPTFLAT <UNOPTFLAT>` has been used
|
||||
to override an UNOPTFLAT warning (see below).
|
||||
|
||||
The solution is to break the loop, as described for UNOPTFLAT.
|
||||
Was an error when Verilator tried to deal with a combinatorial loop that
|
||||
could not be flattened, and which involves a datatype which Verilator
|
||||
could not handle, such as an unpacked struct or a large unpacked array.
|
||||
|
||||
|
||||
.. option:: DIDNOTCONVERGE
|
||||
@@ -492,7 +491,7 @@ List Of Warnings
|
||||
passing. Thus to prevent an infinite loop, the Verilated executable
|
||||
gives the DIDNOTCONVERGE error.
|
||||
|
||||
To debug this, first review any UNOPT or UNOPTFLAT warnings that were
|
||||
To debug this, first review any UNOPTFLAT warnings that were
|
||||
ignored. Though typically it is safe to ignore UNOPTFLAT (at a
|
||||
performance cost), at the time of issuing a UNOPTFLAT Verilator did not
|
||||
know if the logic would eventually converge and assumed it would.
|
||||
@@ -516,9 +515,10 @@ List Of Warnings
|
||||
constructs (e.g. always_ff and always_latch).
|
||||
|
||||
Another way DIDNOTCONVERGE may occur is if # delays are used to generate
|
||||
clocks. Verilator ignores the delays and gives an :option:`ASSIGNDLY`
|
||||
or :option:`STMTDLY` warning. If these were suppressed, due to the
|
||||
absence of the delay, the code may now oscillate.
|
||||
clocks if Verilator is run with :vlopt:`--no-timing`. In this mode,
|
||||
Verilator ignores the delays and gives an :option:`ASSIGNDLY` or
|
||||
:option:`STMTDLY` warning. If these were suppressed, due to the absence of
|
||||
the delay, the code may now oscillate.
|
||||
|
||||
Finally, rare, more difficult cases can be debugged like a C++ program;
|
||||
either enter :command:`gdb` and use its tracing facilities, or edit the
|
||||
@@ -589,9 +589,10 @@ List Of Warnings
|
||||
|
||||
.. option:: GENCLK
|
||||
|
||||
Deprecated and no longer used as a warning. Used to indicate that the
|
||||
specified signal was is generated inside the model, and also being used
|
||||
as a clock.
|
||||
Historical, never issued since version 5.000.
|
||||
|
||||
Indicated that the specified signal was generated inside the model, and
|
||||
was also being used as a clock.
|
||||
|
||||
|
||||
.. option:: HIERBLOCK
|
||||
@@ -653,12 +654,12 @@ List Of Warnings
|
||||
|
||||
.. option:: IMPERFECTSCH
|
||||
|
||||
.. TODO better example
|
||||
Historical, never issued since version 5.000.
|
||||
|
||||
Warns that the scheduling of the model is not absolutely perfect, and
|
||||
Warned that the scheduling of the model is not absolutely perfect, and
|
||||
some manual code edits may result in faster performance. This warning
|
||||
defaults to off, is not part of -Wall, and must be turned on explicitly
|
||||
before the top module statement is processed.
|
||||
defaulted to off, was not part of -Wall, and had to be turned on
|
||||
explicitly before the top module statement is processed.
|
||||
|
||||
|
||||
.. option:: IMPLICIT
|
||||
@@ -730,9 +731,9 @@ List Of Warnings
|
||||
Warns that a while or for statement has a condition that is always true.
|
||||
and thus results in an infinite loop if the statement ever executes.
|
||||
|
||||
This might be unintended behavior if the loop body contains statements
|
||||
that in other simulators would make time pass, which Verilator is
|
||||
ignoring due to e.g. ``STMTDLY`` warnings being disabled.
|
||||
This might be unintended behavior if Verilator is run with
|
||||
:vlopt:`--no-timing` and the loop body contains statements that would make
|
||||
time pass otherwise.
|
||||
|
||||
Ignoring this warning will only suppress the lint check, it will
|
||||
simulate correctly (i.e. hang due to the infinite loop).
|
||||
@@ -793,6 +794,16 @@ List Of Warnings
|
||||
simulate correctly.
|
||||
|
||||
|
||||
.. option:: MINTYPMAX
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
#(3:5:8) clk = ~clk;
|
||||
|
||||
Warns that minimum, typical, and maximum delay expressions are currently
|
||||
unsupported. Only the typical delay value is used by Verilator.
|
||||
|
||||
|
||||
.. option:: MODDUP
|
||||
|
||||
.. TODO better example
|
||||
@@ -863,6 +874,13 @@ List Of Warnings
|
||||
input.
|
||||
|
||||
|
||||
.. option:: NEEDTIMINGOPT
|
||||
|
||||
Error when a timing-related construct, such as an event control or delay,
|
||||
has been encountered, without specifying how Verilator should handle it
|
||||
(neither :vlopt:`--timing` nor :vlopt:`--no-timing` option was provided).
|
||||
|
||||
|
||||
.. option:: NOLATCH
|
||||
|
||||
.. TODO better example
|
||||
@@ -875,6 +893,13 @@ List Of Warnings
|
||||
simulate correctly.
|
||||
|
||||
|
||||
.. option:: NOTIMING
|
||||
|
||||
Error when a timing-related construct that requires :vlopt:`--timing` has
|
||||
been encountered. Issued only if Verilator is run with the
|
||||
:vlopt:`--no-timing` option.
|
||||
|
||||
|
||||
.. option:: NULLPORT
|
||||
|
||||
Warns that a null port was detected in the module definition port
|
||||
@@ -1112,6 +1137,16 @@ List Of Warnings
|
||||
"Duplicate macro arguments with name".
|
||||
|
||||
|
||||
.. option:: RISEFALLDLY
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
and #(1,2,3) AND (out, a, b);
|
||||
|
||||
Warns that rising, falling, and turn-off delays are currently unsupported.
|
||||
The first (rising) delay is used for all cases.
|
||||
|
||||
|
||||
.. option:: SELRANGE
|
||||
|
||||
Warns that a selection index will go out of bounds.
|
||||
@@ -1211,11 +1246,11 @@ List Of Warnings
|
||||
|
||||
.. include:: ../../docs/gen/ex_STMTDLY_msg.rst
|
||||
|
||||
This is a warning because Verilator does not support delayed statements.
|
||||
It will ignore all such delays. In many cases ignoring a delay might be
|
||||
harmless, but if the delayed statement is, as in this example, used to
|
||||
cause some important action at a later time, it might be an important
|
||||
difference.
|
||||
This warning is issued only if Verilator is run with :vlopt:`--no-timing`.
|
||||
All delays on statements are ignored in this mode. In many cases ignoring a
|
||||
delay might be harmless, but if the delayed statement is, as in this
|
||||
example, used to cause some important action at a later time, it might be an
|
||||
important difference.
|
||||
|
||||
Some possible workarounds:
|
||||
|
||||
@@ -1225,6 +1260,8 @@ List Of Warnings
|
||||
* Convert the statement into a FSM, or other statement that tests
|
||||
against $time.
|
||||
|
||||
* Run Verilator with :vlopt:`--timing`.
|
||||
|
||||
|
||||
.. option:: SYMRSVDWORD
|
||||
|
||||
@@ -1355,23 +1392,12 @@ List Of Warnings
|
||||
|
||||
.. option:: UNOPT
|
||||
|
||||
.. TODO better example
|
||||
Historical, never issued since version 5.000.
|
||||
|
||||
Warns that due to some construct, optimization of the specified signal
|
||||
or block is disabled. The construct should be cleaned up to improve
|
||||
simulation performance.
|
||||
Warned that due to some construct, optimization of the specified signal
|
||||
or block was disabled.
|
||||
|
||||
A less obvious case of this is when a module instantiates two
|
||||
submodules. Inside submodule A, signal I is input and signal O is
|
||||
output. Likewise in submodule B, signal O is an input and I is an
|
||||
output. A loop exists and a UNOPT warning will result if AI & AO both
|
||||
come from and go to combinatorial blocks in both submodules, even if
|
||||
they are unrelated always blocks. This affects performance because
|
||||
Verilator would have to evaluate each submodule multiple times to
|
||||
stabilize the signals crossing between the modules.
|
||||
|
||||
Ignoring this warning will only slow simulations, it will simulate
|
||||
correctly.
|
||||
Ignoring this warning only slowed simulations, it simulated correctly.
|
||||
|
||||
|
||||
.. option:: UNOPTFLAT
|
||||
@@ -1385,10 +1411,6 @@ List Of Warnings
|
||||
performance; two times better performance may be possible by fixing
|
||||
these warnings.
|
||||
|
||||
Unlike the ``UNOPT`` warning, this occurs after flattening the netlist,
|
||||
and indicates a more basic problem, as the less obvious case described
|
||||
under ``UNOPT`` does not apply.
|
||||
|
||||
Often UNOPTFLAT is caused by logic that isn't truly circular as viewed by
|
||||
synthesis which analyzes interconnection per-bit, but is circular to
|
||||
simulation which analyzes per-bus.
|
||||
@@ -1441,15 +1463,15 @@ List Of Warnings
|
||||
the conflict. If you run with `--report-unoptflat` Verilator will
|
||||
suggest possible candidates for :option:`/*verilator&32;split_var*/`.
|
||||
|
||||
The UNOPTFLAT warning may also be due to clock enables, identified from
|
||||
the reported path going through a clock gating instance. To fix these,
|
||||
use the clock_enable meta comment described above.
|
||||
|
||||
The UNOPTFLAT warning may also occur where outputs from a block of logic
|
||||
are independent, but occur in the same always block. To fix this, use
|
||||
the :option:`/*verilator&32;isolate_assignments*/` metacomment described
|
||||
above.
|
||||
|
||||
Prior to version 5.000, the UNOPTFLAT warning may also have been due to
|
||||
clock enables, identified from the reported path going through a clock
|
||||
gating instance. To fix these, the clock_enable meta comment was used.
|
||||
|
||||
To assist in resolving UNOPTFLAT, the option :vlopt:`--report-unoptflat`
|
||||
can be used, which will provide suggestions for variables that can be
|
||||
split up, and a graph of all the nodes connected in the loop. See the
|
||||
@@ -1520,9 +1542,31 @@ List Of Warnings
|
||||
|
||||
.. option:: UNUSED
|
||||
|
||||
Disabling/enabling UNUSED is equivalent to disabling/enabling the
|
||||
`UNUSEDGENVAR`, `UNUSEDPARAM` and `UNUSEDSIGNAL` warnings.
|
||||
|
||||
Never issued since version 5.000. Historically warned that a variable,
|
||||
parameter, or signal was unused.
|
||||
|
||||
.. option:: UNUSEDGENVAR
|
||||
|
||||
.. TODO better example
|
||||
|
||||
Warns that the specified signal or parameter is never used/consumed.
|
||||
Warns that the specified genvar is never used/consumed.
|
||||
|
||||
|
||||
.. option:: UNUSEDPARAM
|
||||
|
||||
.. TODO better example
|
||||
|
||||
Warns that the specified parameter is never used/consumed.
|
||||
|
||||
|
||||
.. option:: UNUSEDSIGNAL
|
||||
|
||||
.. TODO better example
|
||||
|
||||
Warns that the specified signal is never used/consumed.
|
||||
Verilator is fairly liberal in the usage calculations; making a signal
|
||||
public, a signal matching the :vlopt:`--unused-regexp` option (default
|
||||
"\*unused\*" or accessing only a single array element marks the entire
|
||||
@@ -1635,6 +1679,16 @@ List Of Warnings
|
||||
To resolve, rename the variable to a unique name.
|
||||
|
||||
|
||||
.. option:: WAITCONST
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
wait(0); // Blocks forever
|
||||
|
||||
Warns that a `wait` statement awaits a constant condition, which means it
|
||||
either blocks forever or never blocks.
|
||||
|
||||
|
||||
.. option:: WIDTH
|
||||
|
||||
Warns that based on width rules of Verilog:
|
||||
@@ -1702,3 +1756,11 @@ List Of Warnings
|
||||
The correct fix is to either size the 1 (:code:`32'h1`), or add the
|
||||
width to the parameter definition (:code:`parameter [31:0]`), or add the
|
||||
width to the parameter usage (:code:`{PAR[31:0], PAR[31:0]}`).
|
||||
|
||||
|
||||
.. option:: ZERODLY
|
||||
|
||||
Warns that `#0` delays do not schedule the process to be resumed in the
|
||||
Inactive region. Such processes do get resumed in the same time slot
|
||||
somewhere in the Active region. Issued only if Verilator is run with the
|
||||
:vlopt:`--timing` option.
|
||||
|
||||
+672
-13
@@ -96,10 +96,11 @@ this.
|
||||
Each ``AstNode`` has pointers to up to four children, accessed by the
|
||||
``op1p`` through ``op4p`` methods. These methods are then abstracted in a
|
||||
specific Ast\* node class to a more specific name. For example with the
|
||||
``AstIf`` node (for ``if`` statements), ``ifsp`` calls ``op2p`` to give the
|
||||
``AstIf`` node (for ``if`` statements), ``thensp`` calls ``op2p`` to give the
|
||||
pointer to the AST for the "then" block, while ``elsesp`` calls ``op3p`` to
|
||||
give the pointer to the AST for the "else" block, or NULL if there is not
|
||||
one.
|
||||
one. These accessors are automatically generated by ``astgen`` after
|
||||
parsing the ``@astgen`` directives in the specific ``AstNode`` subclasses.
|
||||
|
||||
``AstNode`` has the concept of a next and previous AST - for example the
|
||||
next and previous statements in a block. Pointers to the AST for these
|
||||
@@ -183,6 +184,568 @@ A number of predefined derived algorithm classes and access methods are
|
||||
provided and documented in ``V3GraphAlg.cpp``.
|
||||
|
||||
|
||||
``DfgGraph``
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The data-flow graph based combinational logic optimizer (DFG optimizer)
|
||||
converts an ``AstModule`` into a ``DfgGraph``. The graph represents the
|
||||
combinational equations (~continuous assignments) in the module, and for the
|
||||
duration of the DFG passes, it takes over the role of the represented
|
||||
``AstModule``. The ``DfgGraph`` keeps holds of the represented ``AstModule``,
|
||||
and the ``AstModule`` retains all other logic that is not representable as a
|
||||
data-flow graph. At the end of optimization, the combinational logic
|
||||
represented by the ``DfgGraph`` is converted back into AST form and is
|
||||
re-inserted into the corresponding ``AstModule``. The ``DfgGraph`` is distinct
|
||||
from ``V3Graph`` for efficiency and other desirable properties which make
|
||||
writing DFG passes easier.
|
||||
|
||||
|
||||
``DfgVertex``
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The ``DfgGraph`` represents combinational logic equations as a graph of
|
||||
``DfgVertex`` vertices. Each sub-class of ``DfgVertex`` corresponds to an
|
||||
expression (a sub-class of ``AstNodeMath``), a constanat, or a variable
|
||||
reference. LValues and RValues referencing the same storage location are
|
||||
represented by the same ``DfgVertex``. Consumers of such vertices read as the
|
||||
LValue, writers of such vertices write the RValue. The bulk of the final
|
||||
``DfgVertex`` sub-classes are generated by ``astgen`` from the corresponding
|
||||
``AstNode`` definitions.
|
||||
|
||||
|
||||
Scheduling
|
||||
----------
|
||||
|
||||
Verilator implements the Active and NBA regions of the SystemVerilog scheduling
|
||||
model as described in IEEE 1800-2017 chapter 4, and in particular sections
|
||||
4.5 and Figure 4.1. The static (verilation time) scheduling of SystemVerilog
|
||||
processes is performed by code in the ``V3Sched`` namespace. The single
|
||||
entry-point to the scheduling algorithm is ``V3Sched::schedule``. Some
|
||||
preparatory transformations important for scheduling are also performed in
|
||||
``V3Active`` and ``V3ActiveTop``. High level evaluation functions are
|
||||
constructed by ``V3Order``, which ``V3Sched`` invokes on subsets of the logic
|
||||
in the design.
|
||||
|
||||
Scheduling deals with the problem of evaluating 'logic' in the correct order
|
||||
and the correct number of times in order to compute the correct state of the
|
||||
SystemVerilog program. Throughout this section, we use the term 'logic' to
|
||||
refer to all SystemVerilog constructs that describe the evolution of the state
|
||||
of the program. In particular, all SystemVerilog processes and continuous
|
||||
assignments are considered 'logic', but not for example variable definitions
|
||||
without initialization or other miscellaneous constructs.
|
||||
|
||||
|
||||
Classes of logic
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
The first step in the scheduling algorithm is to gather all the logic present
|
||||
in the design, and classify it based on the conditions under which the logic
|
||||
needs to be evaluated.
|
||||
|
||||
The classes of logic we distinguish between are:
|
||||
|
||||
- SystemVerilog ``initial`` processes, that need to be executed once at
|
||||
startup.
|
||||
|
||||
- Static variable initializers. These are a separate class as they need to be
|
||||
executed before ``initial`` processes.
|
||||
|
||||
- SystemVerilog ``final`` processes.
|
||||
|
||||
- Combinational logic. Any process or construct that has an implicit
|
||||
sensitivity list with no explicit sensitivities is considered 'combinational'
|
||||
logic. This includes among other things, ``always @*`` and ``always_comb``
|
||||
processes, and continuous assignments. Verilator also converts some other
|
||||
``always`` processes to combinational logic in ``V3Active`` as described
|
||||
below.
|
||||
|
||||
- Clocked logic. Any process or construct that has an explicit sensitivity
|
||||
list, with no implicit sensitivities is considered 'clocked' (or
|
||||
'sequential') logic. This includes among other things ``always`` and
|
||||
``always_ff`` processes with an explicit sensitivity list.
|
||||
|
||||
Note that the distinction between clocked logic and combinational logic is only
|
||||
important for the scheduling algorithm within Verilator as we handle the two
|
||||
classes differently. It is possible to convert clocked logic into combinational
|
||||
logic if the explicit sensitivity list of the clocked logic is the same as the
|
||||
implicit sensitivity list of the equivalent combinational logic would be. The
|
||||
canonical examples are: ``always @(a) x = a;``, which is considered to be
|
||||
clocked logic by Verilator, and the equivalent ``assign x = a;``, which is
|
||||
considered to be combinational logic. ``V3Active`` in fact converts all clocked
|
||||
logic to combinational logic whenever possible, as this provides advantages for
|
||||
scheduling as described below.
|
||||
|
||||
There is also a 'hybrid' logic class, which has both explicit and implicit
|
||||
sensitivities. This kind of logic does not arise from a SystemVerilog
|
||||
construct, but is created during scheduling to break combinational cycles.
|
||||
Details of this process and the hybrid logic class are described below.
|
||||
|
||||
|
||||
Scheduling of simple classes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
SystemVerilog ``initial`` and ``final`` blocks can be scheduled (executed) in an
|
||||
arbitrary order.
|
||||
|
||||
Static variable initializers need to be executed in source code order in case
|
||||
there is a dependency between initializers, but the ordering of static variable
|
||||
initialization is otherwise not defined by the SystemVerilog standard
|
||||
(particularly, in the presence of hierarchical references in static variable
|
||||
initializers).
|
||||
|
||||
The scheduling algorithm handles all three of these classes the same way and
|
||||
schedules the logic in these classes in source code order. This step yields the
|
||||
``_eval_static``, ``_eval_initial`` and ``_eval_final`` functions which execute
|
||||
the corresponding logic constructs.
|
||||
|
||||
|
||||
Scheduling of clocked and combinational logic
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For performance, clocked and combinational logic needs to be ordered.
|
||||
Conceptually this minimizes the iterations through the evaluation loop
|
||||
presented in the reference algorithm in the SystemVerilog standard (IEEE
|
||||
1800-2017 section 4.5), by evaluating logic constructs in data-flow order.
|
||||
Without going into a lot of detail here, accept that well thought out ordering
|
||||
is crucial to good simulation performance, and also enables further
|
||||
optimizations later on.
|
||||
|
||||
At the highest level, ordering is performed by ``V3Order::order``, which is
|
||||
invoked by ``V3Sched::schedule`` on various subsets of the combinational and
|
||||
clocked logic as described below. The important thing to highlight now is that
|
||||
``V3Order::order`` operates by assuming that the state of all variables driven
|
||||
by combinational logic are consistent with that combinational logic. While this
|
||||
might seem subtle, it is very important, so here is an example:
|
||||
|
||||
::
|
||||
always_comb d = q + 2;
|
||||
always @(posedge clock) q <= d;
|
||||
|
||||
|
||||
During ordering, ``V3Order`` will assume that ``d`` equals ``q + 2`` at the
|
||||
beginning of an evaluation step. As a result it will order the clocked logic
|
||||
first, and all downstream combinational logic (like the assignment to ``d``)
|
||||
will execute after the clocked logic that drives inputs to the combinational
|
||||
logic, in data-flow (or dependency) order. At the end of the evaluation step,
|
||||
this ordering restores the invariant that variables driven by combinational
|
||||
logic are consistent with that combinational logic (i.e.: the circuit is in a
|
||||
settled/steady state).
|
||||
|
||||
One of the most important optimizations for performance is to only evaluate
|
||||
combinational logic, if its inputs might have changed. For example, there is no
|
||||
point in evaluating the above assignment to ``d`` on a negative edge of the
|
||||
clock signal. Verilator does this by pushing the combinational logic into the
|
||||
same (possibly multiple) event domains as the logic driving the inputs to that
|
||||
combinational logic, and only evaluating the combinational logic if at least
|
||||
one driving domains have been triggered. The impact of this activity gating is
|
||||
very high (observed 100x slowdown on large designs when turning it off), it is
|
||||
the reason we prefer to convert clocked logic to combinational logic in
|
||||
``V3Active`` whenever possible.
|
||||
|
||||
The ordering procedure described above works straight forward unless there are
|
||||
combinational logic constructs that are circularly dependent (a.k.a.: the
|
||||
UNOPTFLAT warning). Combinational scheduling loops can arise in sound
|
||||
(realizable) circuits as Verilator considers each SystemVerilog process as a
|
||||
unit of scheduling (albeit we do try to split processes into smaller ones to
|
||||
avoid this circularity problem whenever possible, this is not always possible).
|
||||
|
||||
|
||||
Breaking combinational loops
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Combinational loops are broken by the introduction of instances of the 'hybrid'
|
||||
logic class. As described in the previous section, combinational loops require
|
||||
iteration until the logic is settled, in order to restore the invariant that
|
||||
combinationally driven signals are consistent with the combinational logic.
|
||||
|
||||
To achieve this, ``V3Sched::schedule`` calls ``V3Sched::breakCycles``, which
|
||||
builds a dependency graph of all combinational logic in the design, and then
|
||||
breaks all combinational cycles by converting all combinational logic that
|
||||
consumes a variable driven via a 'back-edge' into hybrid logic. Here
|
||||
'back-edge' just means a graph edge that points from a higher rank vertex to a
|
||||
lower rank vertex in some consistent ranking of the directed graph. Variables
|
||||
driven via a back-edge in the dependency graph are marked, and all
|
||||
combinational logic that depends on such variables is converted into hybrid
|
||||
logic, with the back-edge driven variables listed as explicit 'changed'
|
||||
sensitivities.
|
||||
|
||||
Hybrid logic is handled by ``V3Order`` mostly in the same way as combinational
|
||||
logic, with two exceptions:
|
||||
|
||||
- Explicit sensitivities of hybrid logic are ignored for the purposes of
|
||||
data-flow ordering with respect to other combinational or hybrid logic. I.e.:
|
||||
an explicit sensitivity suppresses the implicit sensitivity on the same
|
||||
variable. This cold also be interpreted as ordering the hybrid logic as if
|
||||
all variables listed as explicit sensitivities were substituted as constants
|
||||
with their current values.
|
||||
|
||||
- The explicit sensitivities are included as an additional driving domain of
|
||||
the logic, and also cause evaluation when triggered.
|
||||
|
||||
This means that hybrid logic is evaluated when either any of its implicit
|
||||
sensitivities might have been updated (the same way as combinational logic, by
|
||||
pushing it into the domains that write those variables), or if any of its
|
||||
explicit sensitivities are triggered.
|
||||
|
||||
The effect of this transformation is that ``V3Order`` can proceed as if there
|
||||
are no combinational cycles (or alternatively, under the assumption that the
|
||||
back-edge driven variables don't change during one evaluation pass). The
|
||||
evaluation loop invoking the ordered code, will then re-invoke it on a follow
|
||||
on iteration, if any of the explicit sensitivities of hybrid logic have
|
||||
actually changed due to the previous invocation, iterating until all the
|
||||
combinational (including hybrid) logic have settled.
|
||||
|
||||
One might wonder if there can be a race condition between clocked logic
|
||||
triggered due to a combinational signal change from the previous evaluation
|
||||
pass, and a combinational loop settling due to hybrid logic, if the clocked
|
||||
logic reads the not yet settled combinationally driven signal. Such a race is
|
||||
indeed possible, but our evaluation is consistent with the SystemVerilog
|
||||
scheduling semantics (IEEE 1800-2017 chapter 4), and therefore any program that
|
||||
exhibits such a race has non-deterministic behaviour according to the
|
||||
SystemVerilog semantics, so we accept this.
|
||||
|
||||
|
||||
Settling combinational logic after initialization
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
At the beginning of simulation, once static initializer and ``initial`` blocks
|
||||
have been executed, we need to evaluate all combinational logic, in order to
|
||||
restore the invariant utilized by ``V3Order`` that the state of all
|
||||
combinationally driven variables are consistent with the combinational logic.
|
||||
|
||||
To achieve this, we invoke ``V3Order::order`` on all of the combinational and
|
||||
hybrid logic, and iterate the resulting evaluation function until no more
|
||||
hybrid logic is triggered. This yields the `_eval_settle` function which is
|
||||
invoked at the beginning of simulation, after the `_eval_initial`.
|
||||
|
||||
|
||||
Partitioning logic for correct NBA updates
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``V3Order`` can order logic corresponding to non-blocking assignments (NBAs) to
|
||||
yield correct simulation results, as long as all the sensitivity expressions of
|
||||
clocked logic triggered in the Active scheduling region of the current time
|
||||
step are known up front. I.e.: the ordering of NBA updates is only correct if
|
||||
derived clocks that are computed in an Active region update (that is, via a
|
||||
blocking or continuous assignment) are known up front.
|
||||
|
||||
We can ensure this by partitioning the logic into two regions. Note these
|
||||
regions are a concept of the Verilator scheduling algorithm and they do not
|
||||
directly correspond to the similarly named SystemVerilog scheduling regions
|
||||
as defined in the standard:
|
||||
|
||||
- All logic (clocked, combinational and hybrid) that transitively feeds into,
|
||||
or drives, via a non-blocking or continuous assignments (or via any update
|
||||
that SystemVerilog executes in the Active scheduling region), a variable that
|
||||
is used in the explicit sensitivity list of some clocked or hybrid logic, is
|
||||
assigned to the 'act' region.
|
||||
|
||||
- All other logic is assigned to the 'nba' region.
|
||||
|
||||
For completeness, note that a subset of the 'act' region logic, specifically,
|
||||
the logic related to the pre-assignments of NBA updates (i.e.: AstAssignPre
|
||||
nodes), is handled separately, but is executed as part of the 'act' region.
|
||||
|
||||
Also note that all logic representing the committing of an NBA (i.e.: Ast*Post)
|
||||
nodes) will be in the 'nba' region. This means that the evaluation of the 'act'
|
||||
region logic will not commit any NBA updates. As a result, the 'act' region
|
||||
logic can be iterated to compute all derived clock signals up front.
|
||||
|
||||
The correspondence between the SystemVerilog Active and NBA scheduling regions,
|
||||
and the internal 'act' and 'nba' regions, is that 'act' contains all Active
|
||||
region logic that can compute a clock signal, while 'nba' contains all other
|
||||
Active and NBA region logic. For example, if the only clocks in the design are
|
||||
top level inputs, then 'act' will be empty, and 'nba' will contain the whole of
|
||||
the design.
|
||||
|
||||
The partitioning described above is performed by ``V3Sched::partition``.
|
||||
|
||||
|
||||
Replication of combinational logic
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
We will separately invoke ``V3Order::order`` on the 'act' and 'nba' region
|
||||
logic.
|
||||
|
||||
Combinational logic that reads variables driven from both 'act' and 'nba'
|
||||
region logic has the problem of needing to be re-evaluated even if only one of
|
||||
the regions updates an input variable. We could pass additional trigger
|
||||
expressions between the regions to make sure combinational logic is always
|
||||
re-evaluated, or we can replicate combinational logic that is driven from
|
||||
multiple regions, by copying it into each region that drives it. Experiments
|
||||
show this simple replication works well performance-wise (and notably
|
||||
``V3Combine`` is good at combining the replicated code), so this is what we do
|
||||
in ``V3Sched::replicateLogic``.
|
||||
|
||||
In ``V3Sched::replicateLogic``, in addition to replicating logic into the 'act'
|
||||
and 'nba' regions, we also replicate combinational (and hybrid) logic that
|
||||
depends on top level inputs. These become a separate 'ico' region (Input
|
||||
Combinational logic), which we will always evaluate at the beginning of a
|
||||
time-step to ensure the combinational invariant holds even if input signals
|
||||
have changed. Note that this eliminates the need of changing data and clock
|
||||
signals on separate evaluations, as was necessary with earlier versions of
|
||||
Verilator).
|
||||
|
||||
|
||||
Constructing the top level `_eval` function
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To construct the top level `_eval` function, which updates the state of the
|
||||
circuit to the end of the current time step, we invoke ``V3Order::order``
|
||||
separately on the 'ico', 'act' and 'nba' logic, which yields the `_eval_ico`,
|
||||
`_eval_act`, and `_eval_nba` functions. We then put these all together with the
|
||||
corresponding functions that compute the respective trigger expressions into
|
||||
the top level `_eval` function, which on the high level has the form:
|
||||
|
||||
::
|
||||
void _eval() {
|
||||
// Update combinational logic dependent on top level inptus ('ico' region)
|
||||
while (true) {
|
||||
_eval__triggers__ico();
|
||||
// If no 'ico' region trigger is active
|
||||
if (!ico_triggers.any()) break;
|
||||
_eval_ico();
|
||||
}
|
||||
|
||||
|
||||
// Iterate 'act' and 'nba' regions together
|
||||
while (true) {
|
||||
|
||||
// Iterate 'act' region, this computes all derived clocks updaed in the
|
||||
// Active scheduling region, but does not commit any NBAs that executed
|
||||
// in 'act' region logic.
|
||||
while (true) {
|
||||
_eval__triggers__act();
|
||||
// If no 'act' region trigger is active
|
||||
if (!act_triggers.any()) break;
|
||||
// Remember what 'act' triggers were active, 'nba' uses the same
|
||||
latch_act_triggers_for_nba();
|
||||
_eval_act();
|
||||
}
|
||||
|
||||
|
||||
// If no 'nba' region trigger is active
|
||||
if (!nba_triggers.any()) break;
|
||||
|
||||
// Evaluate all other Active region logic, and commti NBAs
|
||||
_eval_nba();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timing
|
||||
------
|
||||
|
||||
Timing support in Verilator utilizes C++ coroutines, which is a new feature in
|
||||
C++20. The basic idea is to represent processes and tasks that await a certain
|
||||
event or simulation time as coroutines. These coroutines get suspended at the
|
||||
await, and resumed whenever the triggering event occurs, or at the expected
|
||||
simulation time.
|
||||
|
||||
There are several runtime classes used for managing such coroutines defined in
|
||||
``verilated_timing.h`` and ``verilated_timing.cpp``.
|
||||
|
||||
``VlCoroutineHandle``
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A thin wrapper around an ``std::coroutine_handle<>``. It forces move semantics,
|
||||
destroys the coroutine if it remains suspended at the end of the design's
|
||||
lifetime, and prevents multiple ``resume`` calls in the case of
|
||||
``fork..join_any``.
|
||||
|
||||
``VlCoroutine``
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Return value of all coroutines. Together with the promise type contained
|
||||
within, it allows for chaining coroutines – resuming coroutines from up the
|
||||
call stack. The calling coroutine's handle is saved in the promise object as a
|
||||
continuation, that is, the coroutine that must be resumed after the promise's
|
||||
coroutine finishes. This is necessary as C++ coroutines are stackless, meaning
|
||||
each one is suspended independently of others in the call graph.
|
||||
|
||||
``VlDelayScheduler``
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This class manages processes suspended by delays. There is one instance of this
|
||||
class per design. Coroutines ``co_await`` this object's ``delay`` function.
|
||||
Internally, they are stored in a heap structure sorted by simulation time in
|
||||
ascending order. When ``resume`` is called on the delay scheduler, all
|
||||
coroutines awaiting the current simulation time are resumed. The current
|
||||
simulation time is retrieved from a ``VerilatedContext`` object.
|
||||
|
||||
``VlTriggerScheduler``
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This class manages processes that await events (triggers). There is one such
|
||||
object per each trigger awaited by coroutines. Coroutines ``co_await`` this
|
||||
object's ``trigger`` function. They are stored in two stages – `uncommitted`
|
||||
and `ready`. First, they land in the `uncommitted` stage, and cannot be
|
||||
resumed. The ``resume`` function resumes all coroutines from the `ready` stage
|
||||
and moves `uncommitted` coroutines into `ready`. The ``commit`` function only
|
||||
moves `uncommitted` coroutines into `ready`.
|
||||
|
||||
This split is done to avoid self-triggering and triggering coroutines multiple
|
||||
times. See the `Scheduling with timing` section for details on how this is
|
||||
used.
|
||||
|
||||
``VlDynamicTriggerScheduler``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Like ``VlTriggerScheduler``, ``VlDynamicTriggerScheduler`` manages processes
|
||||
that await triggers. However, it does not rely on triggers evaluated externally
|
||||
by the 'act' trigger eval function. Instead, it is also responsible for trigger
|
||||
evaluation. Coroutines that make use of this scheduler must adhere to a certain
|
||||
procedure:
|
||||
|
||||
::
|
||||
__Vtrigger = 0;
|
||||
<locals and inits required for trigger eval>
|
||||
while (!__Vtrigger) {
|
||||
co_await __VdynSched.evaluation();
|
||||
<pre updates>;
|
||||
__Vtrigger = <trigger eval>;
|
||||
[optionally] co_await __VdynSched.postUpdate();
|
||||
<post updates>;
|
||||
}
|
||||
co_await __VdynSched.resumption();
|
||||
|
||||
The coroutines get resumed at trigger evaluation time, evaluate their local
|
||||
triggers, optionally await the post update step, and if the trigger is set,
|
||||
await proper resumption in the 'act' eval step.
|
||||
|
||||
``VlForkSync``
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Used for synchronizing ``fork..join`` and ``fork..join_any``. Forking
|
||||
coroutines ``co_await`` its ``join`` function, and forked ones call ``done``
|
||||
when they're finished. Once the required number of coroutines (set using
|
||||
``setCounter``) finish execution, the forking coroutine is resumed.
|
||||
|
||||
Awaitable utilities
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are also two small utility awaitable types:
|
||||
|
||||
* ``VlNow`` is an awaitable that suspends and immediately resumes coroutines.
|
||||
It is used for forcing a coroutine to be moved onto the heap. See the `Forks`
|
||||
section for more detail.
|
||||
* ``VlForever`` is used for blocking a coroutine forever. See the `Timing pass`
|
||||
section for more detail.
|
||||
|
||||
Timing pass
|
||||
^^^^^^^^^^^
|
||||
|
||||
The visitor in ``V3Timing.cpp`` transforms each timing control into a ``co_await``.
|
||||
|
||||
* event controls are turned into ``co_await`` on a trigger scheduler's
|
||||
``trigger`` method. The awaited trigger scheduler is the one corresponding to
|
||||
the sentree referenced by the event control. This sentree is also referenced
|
||||
by the ``AstCAwait`` node, to be used later by the static scheduling code.
|
||||
* if an event control waits on a local variable or class member, it uses a
|
||||
local trigger which it evaluates inline. It awaits a dynamic trigger
|
||||
scheduler multiple times: for trigger evaluation, updates, and resumption.
|
||||
The dynamic trigger scheduler is responsible for resuming the coroutine at
|
||||
the correct point of evaluation.
|
||||
* delays are turned into ``co_await`` on a delay scheduler's ``delay`` method.
|
||||
The created ``AstCAwait`` nodes also reference a special sentree related to
|
||||
delays, to be used later by the static scheduling code.
|
||||
* ``join`` and ``join_any`` are turned into ``co_await`` on a ``VlForkSync``'s
|
||||
``join`` method. Each forked process gets a ``VlForkSync::done`` call at the
|
||||
end.
|
||||
|
||||
Assignments with intra-assignment timing controls are simplified into
|
||||
assignments after those timing controls, with the LHS and RHS values evaluated
|
||||
before them and stored in temporary variables.
|
||||
|
||||
``wait`` statements are transformed into while loops that check the condition
|
||||
and then await changes in variables used in the condition. If the condition is
|
||||
always false, the ``wait`` statement is replaced by a ``co_await`` on a
|
||||
``VlForever``. This is done instead of a return in case the ``wait`` is deep in
|
||||
a call stack (otherwise the coroutine's caller would continue execution).
|
||||
|
||||
Each sub-statement of a ``fork`` is put in an ``AstBegin`` node for easier
|
||||
grouping. In a later step, each of these gets transformed into a new, separate
|
||||
function. See the `Forks` section for more detail.
|
||||
|
||||
Processes that use awaits are marked as suspendable. Later, during ``V3Sched``,
|
||||
they are transformed into coroutines. Functions that use awaits get the return
|
||||
type of ``VlCoroutine``. This immediately makes them coroutines. Note that if a
|
||||
process calls a function that is a coroutine, the call gets wrapped in an
|
||||
await, which means the process itself will be marked as suspendable. A virtual
|
||||
function is a coroutine if any of its overriding or overridden functions are
|
||||
coroutines. The visitor keeps a dependency graph of functions and processes to
|
||||
handle such cases.
|
||||
|
||||
Scheduling with timing
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Timing features in Verilator are built on top of the static scheduler. Triggers
|
||||
are used for determining which delay or trigger schedulers should resume. A
|
||||
special trigger is used for the delay scheduler. This trigger is set if there
|
||||
are any coroutines awaiting the current simulation time
|
||||
(``VlDelayScheduler::awaitingCurrentTime()``).
|
||||
|
||||
All triggers used by a suspendable process are mapped to variables written in
|
||||
that process. When ordering code using ``V3Order``, these triggers are provided
|
||||
as external domains of these variables. This ensures that the necessary
|
||||
combinational logic is triggered after a coroutine resumption.
|
||||
|
||||
There are two functions for managing timing logic called by ``_eval()``:
|
||||
|
||||
* ``_timing_commit()``, which commits all coroutines whose triggers were not set
|
||||
in the current iteration,
|
||||
* ``_timing_resume()``, which calls `resume()` on all trigger and delay
|
||||
schedulers whose triggers were set in the current iteration.
|
||||
|
||||
Thanks to this separation, a coroutine awaiting a trigger cannot be suspended
|
||||
and resumed in the same iteration, and it cannot be resumed before it suspends.
|
||||
|
||||
All coroutines are committed and resumed in the 'act' eval loop. With timing
|
||||
features enabled, the ``_eval()`` function takes this form:
|
||||
|
||||
::
|
||||
void _eval() {
|
||||
while (true) {
|
||||
_eval__triggers__ico();
|
||||
if (!ico_triggers.any()) break;
|
||||
_eval_ico();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
while (true) {
|
||||
_eval__triggers__act();
|
||||
|
||||
// Commit all non-triggered coroutines
|
||||
_timing_commit();
|
||||
|
||||
if (!act_triggers.any()) break;
|
||||
latch_act_triggers_for_nba();
|
||||
|
||||
// Resume all triggered coroutines
|
||||
_timing_resume();
|
||||
|
||||
_eval_act();
|
||||
}
|
||||
if (!nba_triggers.any()) break;
|
||||
_eval_nba();
|
||||
}
|
||||
}
|
||||
|
||||
Forks
|
||||
^^^^^
|
||||
|
||||
After the scheduling step, forks sub-statements are transformed into separate
|
||||
functions, and these functions are called in place of the sub-statements. These
|
||||
calls must be without ``co_await``, so that suspension of a forked process
|
||||
doesn't suspend the forking process.
|
||||
|
||||
In forked processes, references to local variables are only allowed in
|
||||
``fork..join``, as this is the only case that ensures the lifetime of these
|
||||
locals is at least as long as the execution of the forked processes. This is
|
||||
where ``VlNow`` is used, to ensure the locals are moved to the heap before they
|
||||
are passed by reference to the forked processes.
|
||||
|
||||
|
||||
Multithreaded Mode
|
||||
------------------
|
||||
|
||||
@@ -501,22 +1064,107 @@ code:
|
||||
The ``astgen`` Script
|
||||
---------------------
|
||||
|
||||
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.
|
||||
The ``astgen`` script is used to generate some of the repetitive C++ code
|
||||
related to the ``AstNode`` type hierarchy. An example is the abstract ``visit``
|
||||
methods in ``VNVisitor``. There are other uses, please see the ``*__gen*``
|
||||
files in the bulid directories and the ``astgen`` script itself for details. A
|
||||
description of the more advanced features of ``astgen`` are provided here.
|
||||
|
||||
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.
|
||||
|
||||
The original C source code is transformed into C code in the ``obj_opt``
|
||||
Generating ``AstNode`` members
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some of the member s of ``AstNode`` sub-classes are generated by ``astgen``.
|
||||
These are emitted as pre-processor macro definitions, which then need to be
|
||||
added to the ``AstNode`` sub-classes they correspond to. Specifically ``class
|
||||
AstFoo`` should contain an instance of ``ASTGEN_MEMBERS_AstFoo;`` at class
|
||||
scope. The ``astgen`` script checks and errors if this is not present. The
|
||||
method generated depends on whether the class is a concrete final class, or an
|
||||
abstract ``AstNode*`` base-class, and on ``@astgen`` directives present in
|
||||
comment sections in the body of the ``AstNode`` sub-class definitions.
|
||||
|
||||
|
||||
List of ``@astgen`` directives
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``@astgen`` directives in comments contained in the body of ``AstNode``
|
||||
sub-class definitions are parsed and contribute to the code generated by
|
||||
``astgen``. The general syntax is ``@astgen <keywords> := <description>``,
|
||||
where ``<keywords>`` determines what is being defined, and ``<description>`` is
|
||||
a ``<keywords>`` dependent description of the definition. The list of
|
||||
``@astgen`` directives is as follows:
|
||||
|
||||
|
||||
``op<N>`` operand directives
|
||||
"""""""""""""""""""""""""""""
|
||||
|
||||
The ``op1``, ``op2``, ``op3`` and ``op4`` directives are used to describe the
|
||||
name and type of the up to 4 child operands of a node. The syntax of the
|
||||
``<description>`` field is ``<identifier> : <type>``, where ``<identifier>``
|
||||
will be used as the base name of the generated operand accessors, and
|
||||
``<type>`` is one of:
|
||||
|
||||
1. An ``AstNode`` sub-class, defining the operand to be of that type, always
|
||||
no-null, and with an always null ``nextp()``. That is, the child node is
|
||||
always present, and is a single ``AstNode`` (as opposed to a list).
|
||||
|
||||
2. ``Optional[<AstNode sub-class>]``. This is just like in point 1 above, but
|
||||
defines the child node to be optional, meaning it may be null.
|
||||
|
||||
3. ``List[AstNode sub-class]`` describes a list operand, which means the child
|
||||
node may have a non-null ``nextp()`` and in addition the child itself may be
|
||||
null, representing an empty list.
|
||||
|
||||
|
||||
An example of the full syntax of the directive is
|
||||
``@astgen op1 := lhsp : AstNodeMath``.
|
||||
|
||||
``astnode`` generates accessors for the child nodes based on these directives.
|
||||
For non-list children, the names of the getter and setter both are that of the
|
||||
given ``<identifier>``. For list type children, the getter is ``<identifier>``,
|
||||
and instead of the setter, there an ``add<Identifier>`` method is generated
|
||||
that appends new nodes (or lists of nodes) to the child list.
|
||||
|
||||
|
||||
``alias op<N>`` operand alias directives
|
||||
""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
If a super-class already defined a name and type for a child node using the
|
||||
``op<N>`` directive, but a more appropriate name exists in the context of a
|
||||
sub-class, then the alias directive can be used to introduce an additional name
|
||||
for the child node. The is ``alias op<N> := <identifier>`` where
|
||||
``<identifier>`` is the new name. ``op<N>`` must have been defined in some
|
||||
super-class of the current node.
|
||||
|
||||
Example: ``@astgen alias op1 := condp``
|
||||
|
||||
|
||||
Generating ``DfgVertex`` sub-classes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Most of the ``DfgVertex`` sub-classes are generated by ``astgen``, from the
|
||||
definitions of the corresponding ``AstNode`` vertices.
|
||||
|
||||
|
||||
Additional features of ``astgen``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In addition to generating ``AstNode`` members as described above,
|
||||
``astgen`` is also use to handle some of the repetitive implementation code
|
||||
that is still variable enough not to be handled in C++ macros.
|
||||
|
||||
In particular, ``astgen`` is used to pre-process some of the C++ source
|
||||
files. For example in ``V3Const.cpp``, it is used to implement the
|
||||
``visit()`` functions for each binary operation using the ``TREEOP`` macro.
|
||||
|
||||
The original C++ source code is transformed into C++ code in the ``obj_opt``
|
||||
and ``obj_dbg`` sub-directories (the former for the optimized version of
|
||||
Verilator, the latter for the debug version). So for example
|
||||
``V3Const.cpp`` into ``V3Const__gen.cpp``.
|
||||
|
||||
|
||||
Visitor Functions -----------------
|
||||
Visitor Functions
|
||||
-----------------
|
||||
|
||||
Verilator uses the "Visitor" design pattern to implement its refinement and
|
||||
optimization passes. This allows separation of the pass algorithm from the
|
||||
@@ -1059,6 +1707,16 @@ Similarly the ``NETLIST`` has a list of modules referred to by its
|
||||
``op1p()`` pointer.
|
||||
|
||||
|
||||
.tree.dot Output
|
||||
----------------
|
||||
|
||||
``*.tree.dot`` files are dumps of the AST Tree in `Graphviz
|
||||
<https://www.graphviz.org>`__ dot format. This can be used to
|
||||
visualize the AST Tree. The vertices correspond to ``AstNode``
|
||||
instances, and the edges represent the pointers (``op1p``,
|
||||
``op2p``, etc) between the nodes.
|
||||
|
||||
|
||||
Debugging with GDB
|
||||
------------------
|
||||
|
||||
@@ -1153,8 +1811,9 @@ Generally what would you do to add a new feature?
|
||||
language and has a lot of back-and-forth with Verilator's grammar. Copy
|
||||
the appropriate rules to src/verilog.y and modify the productions.
|
||||
|
||||
4. If a new Ast type is needed, add it to V3AstNodes.h. Follow the
|
||||
convention described above about the AstNode type hierarchy.
|
||||
4. If a new Ast type is needed, add it to the appropriate V3AstNode*.h.
|
||||
Follow the convention described above about the AstNode type hierarchy.
|
||||
Ordering of definitions is enforced by ``astgen``.
|
||||
|
||||
5. Now you can run "test_regress/t/t_<newtestcase>.pl --debug" and it'll
|
||||
probably fail but you'll see a
|
||||
|
||||
@@ -7,12 +7,14 @@ Ami
|
||||
Amir
|
||||
Anastasiadis
|
||||
Anikin
|
||||
Antmicro
|
||||
Antonin
|
||||
Antwerpen
|
||||
Arasanipalai
|
||||
Arjen
|
||||
Asciidoc
|
||||
Ashutosh
|
||||
Ast
|
||||
Atmel
|
||||
Aurelien
|
||||
Bagri
|
||||
@@ -108,8 +110,10 @@ Goessling
|
||||
Gonnen
|
||||
Goorah
|
||||
Gossner
|
||||
Graphviz
|
||||
Graybeal
|
||||
Grobman
|
||||
Grulfen
|
||||
Gunter
|
||||
Guo
|
||||
Hao
|
||||
@@ -131,6 +135,7 @@ Iles
|
||||
Inlines
|
||||
Inout
|
||||
Iru
|
||||
Iyer
|
||||
Iztok
|
||||
Jacko
|
||||
Jae
|
||||
@@ -157,6 +162,7 @@ Karge
|
||||
Karlsson
|
||||
Katz
|
||||
Katzman
|
||||
Kelin
|
||||
Keren
|
||||
Keyi
|
||||
Kimmitt
|
||||
@@ -340,6 +346,7 @@ Verilating
|
||||
Verilation
|
||||
Verilator
|
||||
Verilog
|
||||
Vighnesh
|
||||
Viktor
|
||||
Vm
|
||||
Vukobratovic
|
||||
@@ -376,6 +383,7 @@ agrobman
|
||||
ahouska
|
||||
al
|
||||
ala
|
||||
algrobman
|
||||
andit
|
||||
ar
|
||||
architected
|
||||
@@ -397,6 +405,7 @@ bbox
|
||||
benchmarking
|
||||
biguint
|
||||
biops
|
||||
bisonpre
|
||||
bitOpTree
|
||||
bitOpTree
|
||||
bitop
|
||||
@@ -441,6 +450,7 @@ const
|
||||
constexpr
|
||||
constpool
|
||||
coredump
|
||||
coroutine
|
||||
countbits
|
||||
countones
|
||||
cout
|
||||
@@ -463,6 +473,7 @@ defenv
|
||||
defname
|
||||
defparam
|
||||
demangling
|
||||
dep
|
||||
der
|
||||
dereference
|
||||
desassign
|
||||
@@ -480,6 +491,7 @@ dsvf
|
||||
dtor
|
||||
dumpall
|
||||
dumpfile
|
||||
dumpi
|
||||
dumplimit
|
||||
dumpoff
|
||||
dumpon
|
||||
@@ -537,6 +549,7 @@ filesystem
|
||||
filt
|
||||
flto
|
||||
flushCall
|
||||
fno
|
||||
fopen
|
||||
forceable
|
||||
foreach
|
||||
@@ -600,6 +613,7 @@ inouts
|
||||
inserted
|
||||
instantiation
|
||||
instantiations
|
||||
intra
|
||||
iostream
|
||||
ish
|
||||
isunbounded
|
||||
@@ -725,6 +739,7 @@ profiler
|
||||
prototyptes
|
||||
ps
|
||||
pthread
|
||||
ptr
|
||||
pulldown
|
||||
pulldowns
|
||||
pullup
|
||||
@@ -750,6 +765,7 @@ reloop
|
||||
resetall
|
||||
respecified
|
||||
rodata
|
||||
rolloverSize
|
||||
rr
|
||||
rst
|
||||
runtime
|
||||
@@ -764,6 +780,7 @@ seg
|
||||
setuphold
|
||||
sformat
|
||||
sformatf
|
||||
shareefj
|
||||
shortint
|
||||
shortreal
|
||||
signame
|
||||
|
||||
@@ -39,30 +39,12 @@ ifneq ($(CMAKE_GT_3_8),true)
|
||||
TARGET := oldcmake
|
||||
else
|
||||
|
||||
# Test existence of SYSTEMC_INCLUDE and SYSTEMC_LIBDIR environment variabless
|
||||
ifneq (,$(SYSTEMC_INCLUDE))
|
||||
ifneq (,${SYSTEMC_LIBDIR})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
|
||||
# Test existence of SYSTEMC_ROOT environment variable
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,${SYSTEMC_ROOT})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
|
||||
# Test existence of SYSTEMC environment variable
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,${SYSTEMC})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
# Check if SC exists via a verilator call (empty if not)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
|
||||
|
||||
# Test whether SystemC is installed with CMake support
|
||||
# This will print a CMake error about processing arguments that can (currently) be ignored.
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,$(SYSTEMC_EXISTS))
|
||||
FINDSC := $(shell mkdir -p build && cd build && cmake --find-package -DNAME=SystemCLanguage -DCMAKE_USE_PTHREADS_INIT=ON -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST -DThreads_FOUND=ON)
|
||||
ifneq (,$(findstring SystemCLanguage found,$(FINDSC)))
|
||||
SYSTEMC_SET := true
|
||||
|
||||
@@ -39,30 +39,12 @@ ifneq ($(CMAKE_GT_3_8),true)
|
||||
TARGET := oldcmake
|
||||
else
|
||||
|
||||
# Test existence of SYSTEMC_INCLUDE and SYSTEMC_LIBDIR environment variabless
|
||||
ifneq (,$(SYSTEMC_INCLUDE))
|
||||
ifneq (,${SYSTEMC_LIBDIR})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
|
||||
# Test existence of SYSTEMC_ROOT environment variable
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,${SYSTEMC_ROOT})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
|
||||
# Test existence of SYSTEMC environment variable
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,${SYSTEMC})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
# Check if SC exists via a verilator call (empty if not)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
|
||||
|
||||
# Test whether SystemC is installed with CMake support
|
||||
# This will print a CMake error about processing arguments that can (currently) be ignored.
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,$(SYSTEMC_EXISTS))
|
||||
FINDSC := $(shell mkdir -p build && cd build && cmake --find-package -DNAME=SystemCLanguage -DCMAKE_USE_PTHREADS_INIT=ON -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST -DThreads_FOUND=ON)
|
||||
ifneq (,$(findstring SystemCLanguage found,$(FINDSC)))
|
||||
SYSTEMC_SET := true
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
*.dmp
|
||||
*.log
|
||||
*.csrc
|
||||
*.vcd
|
||||
obj_*
|
||||
logs
|
||||
@@ -0,0 +1,49 @@
|
||||
######################################################################
|
||||
#
|
||||
# DESCRIPTION: Verilator Example: Small Makefile
|
||||
#
|
||||
# This calls the object directory makefile. That allows the objects to
|
||||
# be placed in the "current directory" which simplifies the Makefile.
|
||||
#
|
||||
# This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
# any use, without warranty, 2020 by Wilson Snyder.
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
######################################################################
|
||||
# Check for sanity to avoid later confusion
|
||||
|
||||
ifneq ($(words $(CURDIR)),1)
|
||||
$(error Unsupported: GNU Make cannot build in directories containing spaces, build elsewhere: '$(CURDIR)')
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
|
||||
# This is intended to be a minimal example. Before copying this to start a
|
||||
# real project, it is better to start with a more complete example,
|
||||
# e.g. examples/make_tracing_c.
|
||||
|
||||
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
||||
# package install, and verilator is in your path. Otherwise find the
|
||||
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
||||
ifeq ($(VERILATOR_ROOT),)
|
||||
VERILATOR = verilator
|
||||
else
|
||||
export VERILATOR_ROOT
|
||||
VERILATOR = $(VERILATOR_ROOT)/bin/verilator
|
||||
endif
|
||||
|
||||
default:
|
||||
@echo "-- Verilator hello-world simple binary example"
|
||||
@echo "-- VERILATE & BUILD --------"
|
||||
$(VERILATOR) --binary -j 0 top.v
|
||||
@echo "-- RUN ---------------------"
|
||||
obj_dir/Vtop
|
||||
@echo "-- DONE --------------------"
|
||||
@echo "Note: Once this example is understood, see examples/make_hello_c."
|
||||
@echo "Note: See also https://verilator.org/guide/latest/examples.html"
|
||||
|
||||
######################################################################
|
||||
|
||||
maintainer-copy::
|
||||
clean mostlyclean distclean maintainer-clean::
|
||||
-rm -rf obj_dir *.log *.dmp *.vpd core
|
||||
@@ -0,0 +1,14 @@
|
||||
// DESCRIPTION: Verilator: Verilog example module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2017 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// See also https://verilator.org/guide/latest/examples.html"
|
||||
|
||||
module top;
|
||||
initial begin
|
||||
$display("Hello World!");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -33,7 +33,7 @@ VERILATOR = $(VERILATOR_ROOT)/bin/verilator
|
||||
endif
|
||||
|
||||
# Check if SC exists via a verilator call (empty if not)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --getenv SYSTEMC_INCLUDE)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
|
||||
|
||||
ifneq ($(SYSTEMC_EXISTS),)
|
||||
default: run
|
||||
|
||||
@@ -32,7 +32,7 @@ int main(int argc, char** argv, char** env) {
|
||||
// When tracing, the contents of the secret module will not be seen
|
||||
VerilatedVcdC* tfp = nullptr;
|
||||
const char* flag = contextp->commandArgsPlusMatch("trace");
|
||||
if (flag && 0 == strcmp(flag, "+trace")) {
|
||||
if (flag && 0 == std::strcmp(flag, "+trace")) {
|
||||
contextp->traceEverOn(true);
|
||||
VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n");
|
||||
tfp = new VerilatedVcdC;
|
||||
|
||||
@@ -55,7 +55,7 @@ VERILATOR_FLAGS += --coverage
|
||||
VERILATOR_INPUT = -f input.vc top.v sc_main.cpp
|
||||
|
||||
# Check if SC exists via a verilator call (empty if not)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --getenv SYSTEMC_INCLUDE)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
|
||||
|
||||
######################################################################
|
||||
|
||||
|
||||
@@ -82,14 +82,14 @@ int sc_main(int argc, char* argv[]) {
|
||||
|
||||
// You must do one evaluation before enabling waves, in order to allow
|
||||
// SystemC to interconnect everything for testing.
|
||||
sc_start(1, SC_NS);
|
||||
sc_start(SC_ZERO_TIME);
|
||||
|
||||
#if VM_TRACE
|
||||
// If verilator was invoked with --trace argument,
|
||||
// and if at run time passed the +trace argument, turn on tracing
|
||||
VerilatedVcdSc* tfp = nullptr;
|
||||
const char* flag = Verilated::commandArgsPlusMatch("trace");
|
||||
if (flag && 0 == strcmp(flag, "+trace")) {
|
||||
if (flag && 0 == std::strcmp(flag, "+trace")) {
|
||||
cout << "Enabling waves into logs/vlt_dump.vcd...\n";
|
||||
tfp = new VerilatedVcdSc;
|
||||
top->trace(tfp, 99); // Trace 99 levels of hierarchy
|
||||
|
||||
@@ -16,12 +16,13 @@ from shutil import copy2
|
||||
|
||||
|
||||
class VlFileCopy:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
debug=0,
|
||||
output_dir='copied'): # directory name we output file uses
|
||||
output_dir='copied'): # directory name we output file uses
|
||||
|
||||
self.debug = debug
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
class VlHierGraph:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
debug=0,
|
||||
output_filename='graph.dot'): # output filename
|
||||
output_filename='graph.dot'): # output filename
|
||||
self.debug = debug
|
||||
self.next_vertex_number = 0
|
||||
self.name_to_number = {}
|
||||
|
||||
@@ -140,7 +140,7 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(FST_MACOSX) || defined(__MINGW32__) || defined(__OpenBSD__) || defined(__FreeBSD__)
|
||||
#if defined(FST_MACOSX) || defined(__MINGW32__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
#define FST_UNBUFFERED_IO
|
||||
#endif
|
||||
|
||||
|
||||
+129
-65
@@ -905,15 +905,22 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
digits = append.length();
|
||||
}
|
||||
const int needmore = width - digits;
|
||||
std::string padding;
|
||||
if (needmore > 0) {
|
||||
if (pctp && pctp[0] && pctp[1] == '0') { // %0
|
||||
padding.append(needmore, '0'); // Pre-pad zero
|
||||
} else {
|
||||
std::string padding;
|
||||
if (left) {
|
||||
padding.append(needmore, ' '); // Pre-pad spaces
|
||||
output += append + padding;
|
||||
} else {
|
||||
if (pctp && pctp[0] && pctp[1] == '0') { // %0
|
||||
padding.append(needmore, '0'); // Pre-pad zero
|
||||
} else {
|
||||
padding.append(needmore, ' '); // Pre-pad spaces
|
||||
}
|
||||
output += padding + append;
|
||||
}
|
||||
} else {
|
||||
output += append;
|
||||
}
|
||||
output += left ? (append + padding) : (padding + append);
|
||||
break;
|
||||
}
|
||||
case '#': { // Unsigned decimal
|
||||
@@ -927,15 +934,22 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
digits = append.length();
|
||||
}
|
||||
const int needmore = width - digits;
|
||||
std::string padding;
|
||||
if (needmore > 0) {
|
||||
if (pctp && pctp[0] && pctp[1] == '0') { // %0
|
||||
padding.append(needmore, '0'); // Pre-pad zero
|
||||
} else {
|
||||
std::string padding;
|
||||
if (left) {
|
||||
padding.append(needmore, ' '); // Pre-pad spaces
|
||||
output += append + padding;
|
||||
} else {
|
||||
if (pctp && pctp[0] && pctp[1] == '0') { // %0
|
||||
padding.append(needmore, '0'); // Pre-pad zero
|
||||
} else {
|
||||
padding.append(needmore, ' '); // Pre-pad spaces
|
||||
}
|
||||
output += padding + append;
|
||||
}
|
||||
} else {
|
||||
output += append;
|
||||
}
|
||||
output += left ? (append + padding) : (padding + append);
|
||||
break;
|
||||
}
|
||||
case 't': { // Time
|
||||
@@ -944,21 +958,62 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
output += _vl_vsformat_time(t_tmp, ld, timeunit, left, width);
|
||||
break;
|
||||
}
|
||||
case 'b':
|
||||
for (; lsb >= 0; --lsb) output += (VL_BITRSHIFT_W(lwp, lsb) & 1) + '0';
|
||||
break;
|
||||
case 'o':
|
||||
for (; lsb >= 0; --lsb) {
|
||||
lsb = (lsb / 3) * 3; // Next digit
|
||||
// Octal numbers may span more than one wide word,
|
||||
// so we need to grab each bit separately and check for overrun
|
||||
// Octal is rare, so we'll do it a slow simple way
|
||||
output += static_cast<char>(
|
||||
'0' + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 0)) ? 1 : 0)
|
||||
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 1)) ? 2 : 0)
|
||||
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 2)) ? 4 : 0));
|
||||
case 'b': // FALLTHRU
|
||||
case 'o': // FALLTHRU
|
||||
case 'x': {
|
||||
if (widthSet || left) {
|
||||
lsb = VL_MOSTSETBITP1_W(VL_WORDS_I(lbits), lwp);
|
||||
lsb = (lsb < 1) ? 0 : (lsb - 1);
|
||||
}
|
||||
|
||||
std::string append;
|
||||
int digits;
|
||||
switch (fmt) {
|
||||
case 'b': {
|
||||
digits = lsb + 1;
|
||||
for (; lsb >= 0; --lsb) append += (VL_BITRSHIFT_W(lwp, lsb) & 1) + '0';
|
||||
break;
|
||||
}
|
||||
case 'o': {
|
||||
digits = (lsb + 1 + 2) / 3;
|
||||
for (; lsb >= 0; --lsb) {
|
||||
lsb = (lsb / 3) * 3; // Next digit
|
||||
// Octal numbers may span more than one wide word,
|
||||
// so we need to grab each bit separately and check for overrun
|
||||
// Octal is rare, so we'll do it a slow simple way
|
||||
append += static_cast<char>(
|
||||
'0' + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 0)) ? 1 : 0)
|
||||
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 1)) ? 2 : 0)
|
||||
+ ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 2)) ? 4 : 0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: { // 'x'
|
||||
digits = (lsb + 1 + 3) / 4;
|
||||
for (; lsb >= 0; --lsb) {
|
||||
lsb = (lsb / 4) * 4; // Next digit
|
||||
const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xf;
|
||||
append += "0123456789abcdef"[charval];
|
||||
}
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
|
||||
const int needmore = width - digits;
|
||||
if (needmore > 0) {
|
||||
std::string padding;
|
||||
if (left) {
|
||||
padding.append(needmore, ' '); // Pre-pad spaces
|
||||
output += append + padding;
|
||||
} else {
|
||||
padding.append(needmore, '0'); // Pre-pad zero
|
||||
output += padding + append;
|
||||
}
|
||||
} else {
|
||||
output += append;
|
||||
}
|
||||
break;
|
||||
} // b / o / x
|
||||
case 'u':
|
||||
case 'z': { // Packed 4-state
|
||||
const bool is_4_state = (fmt == 'z');
|
||||
@@ -984,13 +1039,6 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
for (; lsb >= 0; --lsb) {
|
||||
lsb = (lsb / 4) * 4; // Next digit
|
||||
const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xf;
|
||||
output += "0123456789abcdef"[charval];
|
||||
}
|
||||
break;
|
||||
default: { // LCOV_EXCL_START
|
||||
const std::string msg = std::string{"Unknown _vl_vsformat code: "} + pos[0];
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
@@ -1003,22 +1051,22 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE {
|
||||
static bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE {
|
||||
if (VL_LIKELY(fp)) {
|
||||
return std::feof(fp) ? true : false; // true : false to prevent MSVC++ warning
|
||||
} else {
|
||||
return floc < 0;
|
||||
}
|
||||
}
|
||||
static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
|
||||
static void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
|
||||
if (VL_LIKELY(fp)) {
|
||||
std::fgetc(fp);
|
||||
} else {
|
||||
floc -= 8;
|
||||
}
|
||||
}
|
||||
static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr) VL_MT_SAFE {
|
||||
static int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr) VL_MT_SAFE {
|
||||
// Get a character without advancing
|
||||
if (VL_LIKELY(fp)) {
|
||||
const int data = std::fgetc(fp);
|
||||
@@ -1035,17 +1083,16 @@ static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
|
||||
}
|
||||
}
|
||||
}
|
||||
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr) VL_MT_SAFE {
|
||||
static void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr) VL_MT_SAFE {
|
||||
while (true) {
|
||||
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, const WDataInP fromp,
|
||||
const std::string& fstr, char* tmpp,
|
||||
const char* acceptp) VL_MT_SAFE {
|
||||
static 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) {
|
||||
@@ -1059,9 +1106,8 @@ static inline void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp,
|
||||
*cp++ = '\0';
|
||||
// VL_DBG_MSGF(" _read got='"<<tmpp<<"'\n");
|
||||
}
|
||||
static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
|
||||
const std::string& fstr, char* beginp, std::size_t n,
|
||||
const bool inhibit = false) {
|
||||
static char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr,
|
||||
char* beginp, std::size_t n, const 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.
|
||||
@@ -1073,12 +1119,11 @@ static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
|
||||
}
|
||||
return beginp;
|
||||
}
|
||||
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits,
|
||||
IData ld) VL_MT_SAFE {
|
||||
static void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
|
||||
for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) { VL_ASSIGNBIT_WI(lsb, owp, ld & 1); }
|
||||
}
|
||||
static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
|
||||
size_t posstart, size_t posend) VL_MT_SAFE {
|
||||
static void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
|
||||
size_t posstart, size_t posend) VL_MT_SAFE {
|
||||
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
|
||||
int lsb = 0;
|
||||
for (int i = 0, pos = static_cast<int>(posend) - 1;
|
||||
@@ -2313,7 +2358,7 @@ std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMut
|
||||
std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
|
||||
std::string out = dumpfile();
|
||||
if (VL_UNLIKELY(out.empty())) {
|
||||
VL_PRINTF_MT("%%Warning: $dumpvar ignored as not proceeded by $dumpfile\n");
|
||||
VL_PRINTF_MT("%%Warning: $dumpvar ignored as not preceded by $dumpfile\n");
|
||||
return "";
|
||||
}
|
||||
return out;
|
||||
@@ -2381,23 +2426,25 @@ void VerilatedContext::timeunit(int value) VL_MT_SAFE {
|
||||
}
|
||||
void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
|
||||
if (value < 0) value = -value; // Stored as 0..15
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_timeprecision = value;
|
||||
#ifdef SYSTEMC_VERSION
|
||||
const sc_time sc_res = sc_get_time_resolution();
|
||||
int sc_prec = 99;
|
||||
if (sc_res == sc_time(1, SC_SEC)) {
|
||||
sc_prec = 0;
|
||||
} else if (sc_res == sc_time(1, SC_MS)) {
|
||||
sc_prec = 3;
|
||||
} else if (sc_res == sc_time(1, SC_US)) {
|
||||
sc_prec = 6;
|
||||
} else if (sc_res == sc_time(1, SC_NS)) {
|
||||
sc_prec = 9;
|
||||
} else if (sc_res == sc_time(1, SC_PS)) {
|
||||
sc_prec = 12;
|
||||
} else if (sc_res == sc_time(1, SC_FS)) {
|
||||
sc_prec = 15;
|
||||
{
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_timeprecision = value;
|
||||
#ifdef SYSTEMC_VERSION
|
||||
const sc_time sc_res = sc_get_time_resolution();
|
||||
if (sc_res == sc_time(1, SC_SEC)) {
|
||||
sc_prec = 0;
|
||||
} else if (sc_res == sc_time(1, SC_MS)) {
|
||||
sc_prec = 3;
|
||||
} else if (sc_res == sc_time(1, SC_US)) {
|
||||
sc_prec = 6;
|
||||
} else if (sc_res == sc_time(1, SC_NS)) {
|
||||
sc_prec = 9;
|
||||
} else if (sc_res == sc_time(1, SC_PS)) {
|
||||
sc_prec = 12;
|
||||
} else if (sc_res == sc_time(1, SC_FS)) {
|
||||
sc_prec = 15;
|
||||
}
|
||||
}
|
||||
if (value != sc_prec) {
|
||||
std::ostringstream msg;
|
||||
@@ -2409,6 +2456,8 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
|
||||
const std::string msgs = msg.str();
|
||||
VL_FATAL_MT("", 0, "", msgs.c_str());
|
||||
}
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
}
|
||||
const char* VerilatedContext::timeunitString() const VL_MT_SAFE { return vl_time_str(timeunit()); }
|
||||
@@ -2839,7 +2888,7 @@ const char* Verilated::productVersion() VL_PURE { return VERILATOR_VERSION; }
|
||||
void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE {
|
||||
// Slowpath - Called only on error
|
||||
VL_FATAL_MT(filename, linenum, "", "Null pointer dereferenced");
|
||||
VL_UNREACHABLE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
|
||||
@@ -2847,7 +2896,7 @@ void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
|
||||
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
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
void Verilated::mkdir(const char* dirname) VL_MT_UNSAFE {
|
||||
@@ -3098,3 +3147,18 @@ void VerilatedAssertOneThread::fatal_different() VL_MT_SAFE {
|
||||
#endif
|
||||
|
||||
//===========================================================================
|
||||
// VlDeleter:: Methods
|
||||
|
||||
void VlDeleter::deleteAll() {
|
||||
while (true) {
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
if (m_newGarbage.empty()) break;
|
||||
VerilatedLockGuard deleteLock{m_deleteMutex};
|
||||
std::swap(m_newGarbage, m_toDelete);
|
||||
lock.unlock(); // So destuctors can enqueue new objects
|
||||
for (VlClass* const objp : m_toDelete) delete objp;
|
||||
m_toDelete.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
||||
+15
-15
@@ -164,7 +164,7 @@ public:
|
||||
~VerilatedMutex() = default;
|
||||
const VerilatedMutex& operator!() const { return *this; } // For -fthread_safety
|
||||
/// Acquire/lock mutex
|
||||
void lock() VL_ACQUIRE() {
|
||||
void lock() VL_ACQUIRE() VL_MT_SAFE {
|
||||
// Try to acquire the lock by spinning. If the wait is short,
|
||||
// avoids a trap to the OS plus OS scheduler overhead.
|
||||
if (VL_LIKELY(try_lock())) return; // Short circuit loop
|
||||
@@ -176,9 +176,9 @@ public:
|
||||
m_mutex.lock();
|
||||
}
|
||||
/// Release/unlock mutex
|
||||
void unlock() VL_RELEASE() { m_mutex.unlock(); }
|
||||
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutex.unlock(); }
|
||||
/// Try to acquire mutex. Returns true on success, and false on failure.
|
||||
bool try_lock() VL_TRY_ACQUIRE(true) { return m_mutex.try_lock(); }
|
||||
bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE { return m_mutex.try_lock(); }
|
||||
};
|
||||
|
||||
/// Lock guard for mutex (ala std::unique_lock), wrapped to allow -fthread_safety checks
|
||||
@@ -190,16 +190,16 @@ private:
|
||||
|
||||
public:
|
||||
/// Construct and hold given mutex lock until destruction or unlock()
|
||||
explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr)
|
||||
explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE
|
||||
: m_mutexr(mutexr) { // Need () or GCC 4.8 false warning
|
||||
m_mutexr.lock();
|
||||
}
|
||||
/// Destruct and unlock the mutex
|
||||
~VerilatedLockGuard() VL_RELEASE() { m_mutexr.unlock(); }
|
||||
/// Unlock the mutex
|
||||
void lock() VL_ACQUIRE() { m_mutexr.lock(); }
|
||||
void lock() VL_ACQUIRE() VL_MT_SAFE { m_mutexr.lock(); }
|
||||
/// Lock the mutex
|
||||
void unlock() VL_RELEASE() { m_mutexr.unlock(); }
|
||||
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutexr.unlock(); }
|
||||
};
|
||||
|
||||
#else // !VL_THREADED
|
||||
@@ -274,7 +274,7 @@ protected:
|
||||
public:
|
||||
/// Returns the VerilatedContext this model is instantiated under
|
||||
/// Used to get to e.g. simulation time via contextp()->time()
|
||||
inline VerilatedContext* contextp() const { return &m_context; }
|
||||
VerilatedContext* contextp() const { return &m_context; }
|
||||
/// Returns the hierarchical name of this module instance.
|
||||
virtual const char* hierName() const = 0;
|
||||
/// Returns the name of this model (the name of the generated model class).
|
||||
@@ -457,9 +457,9 @@ public:
|
||||
VerilatedCovContext* coveragep() VL_MT_SAFE;
|
||||
/// Set debug level
|
||||
/// Debug is currently global, but for forward compatibility have a per-context method
|
||||
static void debug(int val) VL_MT_SAFE;
|
||||
static inline void debug(int val) VL_MT_SAFE;
|
||||
/// Return debug level
|
||||
static int debug() VL_MT_SAFE;
|
||||
static inline int debug() VL_MT_SAFE;
|
||||
/// Set current number of errors/assertions
|
||||
void errorCount(int val) VL_MT_SAFE;
|
||||
/// Increment current number of errors/assertions
|
||||
@@ -521,7 +521,7 @@ public:
|
||||
///
|
||||
/// * Else, time comes from the legacy 'double sc_time_stamp()' which
|
||||
/// must be a function defined by the user's wrapper.
|
||||
uint64_t time() const VL_MT_SAFE;
|
||||
inline uint64_t time() const VL_MT_SAFE;
|
||||
/// Set current simulation time. See time() for side effect details
|
||||
void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; }
|
||||
/// Advance current simulation time. See time() for side effect details
|
||||
@@ -652,13 +652,13 @@ public: // But internals only - called from VerilatedModule's
|
||||
const char* name() const { return m_namep; }
|
||||
const char* identifier() const { return m_identifierp; }
|
||||
int8_t timeunit() const { return m_timeunit; }
|
||||
inline VerilatedSyms* symsp() const { return m_symsp; }
|
||||
VerilatedSyms* symsp() const { return m_symsp; }
|
||||
VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT;
|
||||
VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; }
|
||||
void scopeDump() const;
|
||||
void* exportFindError(int funcnum) const;
|
||||
static void* exportFindNullError(int funcnum) VL_MT_SAFE;
|
||||
static inline void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
|
||||
static void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!scopep)) return exportFindNullError(funcnum);
|
||||
if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
|
||||
// m_callbacksp must be declared, as Max'es are > 0
|
||||
@@ -727,7 +727,7 @@ public:
|
||||
/// Return debug level
|
||||
/// When multithreaded this may not immediately react to another thread
|
||||
/// changing the level (no mutex)
|
||||
static inline int debug() VL_MT_SAFE { return s_debug; }
|
||||
static int debug() VL_MT_SAFE { return s_debug; }
|
||||
#else
|
||||
/// Return constant 0 debug level, so C++'s optimizer rips up
|
||||
static constexpr int debug() VL_PURE { return 0; }
|
||||
@@ -933,8 +933,8 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
inline void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
|
||||
inline int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
|
||||
void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
|
||||
int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
|
||||
|
||||
//=========================================================================
|
||||
// Data Types
|
||||
|
||||
@@ -27,6 +27,8 @@ CFG_CXXFLAGS_STD_NEWEST = @CFG_CXXFLAGS_STD_NEWEST@
|
||||
CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
|
||||
# Compiler flags that turn on extra warnings
|
||||
CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
|
||||
# Compiler flags that enable coroutine support
|
||||
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
|
||||
# Linker libraries for multithreading
|
||||
CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
|
||||
|
||||
@@ -142,6 +144,12 @@ ifneq ($(VM_THREADS),0)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VM_TIMING),0)
|
||||
ifneq ($(VM_TIMING),)
|
||||
CPPFLAGS += $(CFG_CXXFLAGS_COROUTINES)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VK_C11),0)
|
||||
ifneq ($(VK_C11),)
|
||||
# Need C++11 at least, so always default to newest
|
||||
|
||||
@@ -357,9 +357,6 @@ public:
|
||||
void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
#ifndef VM_COVERAGE
|
||||
VL_FATAL_MT("", 0, "", "%Error: Called VerilatedCov::write when VM_COVERAGE disabled");
|
||||
#endif
|
||||
selftest();
|
||||
|
||||
std::ofstream os{filename};
|
||||
@@ -518,7 +515,7 @@ VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!m_coveragep)) {
|
||||
const VerilatedLockGuard lock{s_mutex};
|
||||
// cppcheck-suppress identicalInnerCondition
|
||||
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
|
||||
if (VL_LIKELY(!m_coveragep)) { // LCOV_EXCL_LINE // Not redundant, prevents race
|
||||
m_coveragep.reset(new VerilatedCovImp);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-18
@@ -35,22 +35,6 @@
|
||||
|
||||
class VerilatedCovImp;
|
||||
|
||||
//=============================================================================
|
||||
/// Conditionally compile statements only when doing coverage (when
|
||||
/// VM_COVERAGE is defined)
|
||||
|
||||
// clang-format off
|
||||
#ifdef VM_COVERAGE
|
||||
# define VL_IF_COVER(stmts) \
|
||||
do { stmts; } while (false)
|
||||
#else
|
||||
# define VL_IF_COVER(stmts) \
|
||||
do { \
|
||||
if (false) { stmts; } \
|
||||
} while (false)
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
//=============================================================================
|
||||
/// Insert a item for coverage analysis.
|
||||
/// The first argument is a pointer to the count to be dumped.
|
||||
@@ -83,8 +67,11 @@ class VerilatedCovImp;
|
||||
/// }
|
||||
|
||||
#define VL_COVER_INSERT(covcontextp, countp, ...) \
|
||||
VL_IF_COVER(covcontextp->_inserti(countp); covcontextp->_insertf(__FILE__, __LINE__); \
|
||||
covcontextp->_insertp("hier", name(), __VA_ARGS__))
|
||||
do { \
|
||||
covcontextp->_inserti(countp); \
|
||||
covcontextp->_insertf(__FILE__, __LINE__); \
|
||||
covcontextp->_insertp("hier", name(), __VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
//=============================================================================
|
||||
// Convert VL_COVER_INSERT value arguments to strings, is \internal
|
||||
|
||||
@@ -181,7 +181,7 @@ void svPutPartselLogic(svLogicVecVal* dp, const svLogicVecVal s, int lbit, int w
|
||||
//======================================================================
|
||||
// Open array internals
|
||||
|
||||
static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) {
|
||||
static const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) {
|
||||
if (VL_UNLIKELY(!h)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"%%Error: DPI svOpenArrayHandle function called with nullptr handle");
|
||||
|
||||
@@ -161,13 +161,13 @@ class VerilatedFstBuffer VL_NOT_FINAL {
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
|
||||
// called from only one place (the full* methods), so always inline them.
|
||||
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE inline void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitSData(uint32_t code, SData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitIData(uint32_t code, IData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitQData(uint32_t code, QData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitDouble(uint32_t code, double newval);
|
||||
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -232,7 +232,7 @@ public:
|
||||
}
|
||||
|
||||
// Internal class access
|
||||
inline VerilatedFst* spTrace() { return &m_sptrace; }
|
||||
VerilatedFst* spTrace() { return &m_sptrace; }
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
||||
+12
-10
@@ -52,38 +52,39 @@ public:
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
}
|
||||
virtual ~VerilatedFstSc() /*override*/ { close(); }
|
||||
~VerilatedFstSc() override { close(); }
|
||||
|
||||
// METHODS
|
||||
/// Called by SystemC simulate()
|
||||
virtual void cycle(bool delta_cycle) {
|
||||
void cycle(bool delta_cycle) override {
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
}
|
||||
|
||||
// Override VerilatedFstC. Must be called after starting simulation.
|
||||
// cppcheck-suppress missingOverride // GCC won't accept override
|
||||
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
|
||||
// Note: this is not a virtual function in the base class, so no 'override'
|
||||
virtual void open(const char* filename) VL_MT_SAFE;
|
||||
|
||||
private:
|
||||
/// Fake outs for linker
|
||||
|
||||
#ifdef NC_SYSTEMC
|
||||
// Cadence Incisive has these as abstract functions so we must create them
|
||||
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
|
||||
void set_time_unit(int exponent10_seconds) override {} // deprecated
|
||||
#endif
|
||||
virtual void set_time_unit(double v, sc_time_unit tu) {} // LCOV_EXCL_LINE
|
||||
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
|
||||
|
||||
//--------------------------------------------------
|
||||
// SystemC 2.1.v1
|
||||
#define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const std::string& name);
|
||||
#define DECL_TRACE_METHOD_A(tp) void trace(const tp& object, const std::string& name) override;
|
||||
#define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace(const tp& object, const std::string& name, int width);
|
||||
void trace(const tp& object, const std::string& name, int width) override;
|
||||
|
||||
virtual void write_comment(const std::string&);
|
||||
virtual void trace(const unsigned int&, const std::string&, const char**);
|
||||
void write_comment(const std::string&) override;
|
||||
void trace(const unsigned int&, const std::string&, const char**) override;
|
||||
|
||||
// clang-format off
|
||||
// Formatting matches that of sc_trace.h
|
||||
// LCOV_EXCL_START
|
||||
#if (SYSTEMC_VERSION >= 20171012)
|
||||
DECL_TRACE_METHOD_A( sc_event )
|
||||
DECL_TRACE_METHOD_A( sc_time )
|
||||
@@ -118,6 +119,7 @@ private:
|
||||
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_bv_base )
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_lv_base )
|
||||
// LCOV_EXCL_STOP
|
||||
// clang-format on
|
||||
|
||||
#undef DECL_TRACE_METHOD_A
|
||||
|
||||
@@ -282,7 +282,7 @@ inline uint64_t vl_time_stamp64() {
|
||||
# endif
|
||||
#endif
|
||||
|
||||
inline uint64_t VerilatedContext::time() const VL_MT_SAFE {
|
||||
uint64_t VerilatedContext::time() const VL_MT_SAFE {
|
||||
// When using non-default context, fastest path is return time
|
||||
if (VL_LIKELY(m_s.m_time)) return m_s.m_time;
|
||||
#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY))
|
||||
@@ -448,10 +448,17 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
||||
{ \
|
||||
const int words = VL_WORDS_I(obits); \
|
||||
sc_biguint<(obits)> _butemp = (svar).read(); \
|
||||
for (int i = 0; i < words; ++i) { \
|
||||
int msb = ((i + 1) * VL_IDATASIZE) - 1; \
|
||||
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
|
||||
(owp)[i] = _butemp.range(msb, i * VL_IDATASIZE).to_uint(); \
|
||||
uint32_t* chunk = _butemp.get_raw(); \
|
||||
int32_t lsb = 0; \
|
||||
while (lsb < obits - BITS_PER_DIGIT) { \
|
||||
const uint32_t data = *chunk; \
|
||||
++chunk; \
|
||||
_vl_insert_WI(owp.data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \
|
||||
lsb += BITS_PER_DIGIT; \
|
||||
} \
|
||||
if (lsb < obits) { \
|
||||
const uint32_t msb_data = *chunk; \
|
||||
_vl_insert_WI(owp.data(), msb_data, obits - 1, lsb); \
|
||||
} \
|
||||
(owp)[words - 1] &= VL_MASK_E(obits); \
|
||||
}
|
||||
@@ -492,13 +499,23 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
||||
{ (svar).write(rd); }
|
||||
#define VL_ASSIGN_SBQ(obits, svar, rd) \
|
||||
{ (svar).write(rd); }
|
||||
#define VL_SC_BITS_PER_DIGIT 30 // This comes from sc_nbdefs.h BITS_PER_DIGIT
|
||||
#define VL_ASSIGN_SBW(obits, svar, rwp) \
|
||||
{ \
|
||||
sc_biguint<(obits)> _butemp; \
|
||||
for (int i = 0; i < VL_WORDS_I(obits); ++i) { \
|
||||
int msb = ((i + 1) * VL_IDATASIZE) - 1; \
|
||||
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
|
||||
_butemp.range(msb, i* VL_IDATASIZE) = (rwp)[i]; \
|
||||
int32_t lsb = 0; \
|
||||
uint32_t* chunk = _butemp.get_raw(); \
|
||||
while (lsb + VL_SC_BITS_PER_DIGIT < (obits)) { \
|
||||
static_assert(std::is_same<IData, EData>::value, "IData and EData missmatch"); \
|
||||
const uint32_t data = VL_SEL_IWII(lsb + VL_SC_BITS_PER_DIGIT + 1, (rwp).data(), lsb, \
|
||||
VL_SC_BITS_PER_DIGIT); \
|
||||
*chunk = data & VL_MASK_E(VL_SC_BITS_PER_DIGIT); \
|
||||
++chunk; \
|
||||
lsb += VL_SC_BITS_PER_DIGIT; \
|
||||
} \
|
||||
if (lsb < (obits)) { \
|
||||
const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \
|
||||
*chunk = msb_data & VL_MASK_E((obits)-lsb); \
|
||||
} \
|
||||
(svar).write(_butemp); \
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
}
|
||||
}
|
||||
inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
|
||||
@@ -466,7 +466,7 @@ public:
|
||||
// There's often many more scopes than userdata's and thus having a ~48byte
|
||||
// per map overhead * N scopes would take much more space and cache thrashing.
|
||||
// As scopep's are pointers, this implicitly handles multiple Context's
|
||||
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
|
||||
static void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
|
||||
if (it != s().m_userMap.end()) {
|
||||
@@ -475,7 +475,7 @@ public:
|
||||
s().m_userMap.emplace(std::make_pair(scopep, userKey), userData);
|
||||
}
|
||||
}
|
||||
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
static void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
|
||||
if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
|
||||
|
||||
@@ -45,8 +45,7 @@ class VlThreadPool;
|
||||
|
||||
//=============================================================================
|
||||
// Return high-precision counter for profiling, or 0x0 if not available
|
||||
VL_ATTR_ALWINLINE
|
||||
inline QData VL_CPU_TICK() {
|
||||
VL_ATTR_ALWINLINE QData VL_CPU_TICK() {
|
||||
uint64_t val;
|
||||
VL_GET_CPU_TICK(val);
|
||||
return val;
|
||||
@@ -171,9 +170,9 @@ public:
|
||||
// METHODS
|
||||
|
||||
// Is profiling enabled
|
||||
inline bool enabled() const { return m_enabled; }
|
||||
bool enabled() const { return m_enabled; }
|
||||
// Append a trace record to the trace buffer of the current thread
|
||||
static inline VlExecutionRecord& addRecord() {
|
||||
static VlExecutionRecord& addRecord() {
|
||||
t_trace.emplace_back();
|
||||
return t_trace.back();
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ VlWorkerThread::~VlWorkerThread() {
|
||||
m_cthread.join();
|
||||
}
|
||||
|
||||
static void shutdownTask(void*, bool) {
|
||||
static void shutdownTask(void*, bool) { // LCOV_EXCL_LINE
|
||||
// Deliberately empty, we use the address of this function as a magic number
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
// Upstream mtasks must call this when they complete.
|
||||
// Returns true when the current MTaskVertex becomes ready to execute,
|
||||
// false while it's still waiting on more dependencies.
|
||||
inline bool signalUpstreamDone(bool evenCycle) {
|
||||
bool signalUpstreamDone(bool evenCycle) {
|
||||
if (evenCycle) {
|
||||
const uint32_t upstreamDepsDone
|
||||
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
|
||||
@@ -116,11 +116,11 @@ public:
|
||||
return (upstreamDepsDone_prev == 1);
|
||||
}
|
||||
}
|
||||
inline bool areUpstreamDepsDone(bool evenCycle) const {
|
||||
bool areUpstreamDepsDone(bool evenCycle) const {
|
||||
const uint32_t target = evenCycle ? m_upstreamDepCount : 0;
|
||||
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
|
||||
}
|
||||
inline void waitUntilUpstreamDone(bool evenCycle) const {
|
||||
void waitUntilUpstreamDone(bool evenCycle) const {
|
||||
unsigned ct = 0;
|
||||
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
|
||||
VL_CPU_RELAX();
|
||||
@@ -171,7 +171,7 @@ public:
|
||||
|
||||
// METHODS
|
||||
template <bool SpinWait>
|
||||
inline void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Spin for a while, waiting for new data
|
||||
if VL_CONSTEXPR_CXX17 (SpinWait) {
|
||||
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
|
||||
@@ -191,7 +191,7 @@ public:
|
||||
m_ready.erase(m_ready.begin());
|
||||
m_ready_size.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle = false)
|
||||
void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle = false)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
bool notify;
|
||||
{
|
||||
@@ -223,8 +223,8 @@ public:
|
||||
~VlThreadPool() override;
|
||||
|
||||
// METHODS
|
||||
inline int numThreads() const { return m_workers.size(); }
|
||||
inline VlWorkerThread* workerp(int index) {
|
||||
int numThreads() const { return m_workers.size(); }
|
||||
VlWorkerThread* workerp(int index) {
|
||||
assert(index >= 0);
|
||||
assert(index < m_workers.size());
|
||||
return m_workers[index];
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2022 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU Lesser
|
||||
// General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//=========================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated timing implementation code
|
||||
///
|
||||
/// This file must be compiled and linked against all Verilated objects
|
||||
/// that use timing features.
|
||||
///
|
||||
/// See the internals documentation docs/internals.rst for details.
|
||||
///
|
||||
//=========================================================================
|
||||
|
||||
#include "verilated_timing.h"
|
||||
|
||||
//======================================================================
|
||||
// VlCoroutineHandle:: Methods
|
||||
|
||||
void VlCoroutineHandle::resume() {
|
||||
// Only null if we have a fork..join_any and one of the other child processes resumed the
|
||||
// main process
|
||||
if (VL_LIKELY(m_coro)) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Resuming: "); dump(););
|
||||
m_coro();
|
||||
m_coro = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
void VlCoroutineHandle::dump() const {
|
||||
VL_PRINTF("Process waiting at %s:%d\n", m_fileline.filename(), m_fileline.lineno());
|
||||
}
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// VlDelayScheduler:: Methods
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
void VlDelayScheduler::VlDelayedCoroutine::dump() const {
|
||||
VL_DBG_MSGF(" Awaiting time %" PRIu64 ": ", m_timestep);
|
||||
m_handle.dump();
|
||||
}
|
||||
#endif
|
||||
|
||||
void VlDelayScheduler::resume() {
|
||||
#ifdef VL_DEBUG
|
||||
VL_DEBUG_IF(dump(); VL_DBG_MSGF(" Resuming delayed processes\n"););
|
||||
#endif
|
||||
while (awaitingCurrentTime()) {
|
||||
if (m_queue.front().m_timestep != m_context.time()) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"%Error: Encountered process that should've been resumed at an "
|
||||
"earlier simulation time. Missed a time slot?");
|
||||
}
|
||||
// Move max element in the heap to the end
|
||||
std::pop_heap(m_queue.begin(), m_queue.end());
|
||||
VlCoroutineHandle handle = std::move(m_queue.back().m_handle);
|
||||
m_queue.pop_back();
|
||||
handle.resume();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t VlDelayScheduler::nextTimeSlot() const {
|
||||
if (empty()) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: There is no next time slot scheduled");
|
||||
}
|
||||
return m_queue.front().m_timestep;
|
||||
}
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
void VlDelayScheduler::dump() const {
|
||||
if (m_queue.empty()) {
|
||||
VL_DBG_MSGF(" No delayed processes:\n");
|
||||
} else {
|
||||
VL_DBG_MSGF(" Delayed processes:\n");
|
||||
for (const auto& susp : m_queue) susp.dump();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// VlTriggerScheduler:: Methods
|
||||
|
||||
void VlTriggerScheduler::resume(const char* eventDescription) {
|
||||
#ifdef VL_DEBUG
|
||||
VL_DEBUG_IF(dump(eventDescription);
|
||||
VL_DBG_MSGF(" Resuming processes waiting for %s\n", eventDescription););
|
||||
#endif
|
||||
for (auto& susp : m_ready) susp.resume();
|
||||
m_ready.clear();
|
||||
commit(eventDescription);
|
||||
}
|
||||
|
||||
void VlTriggerScheduler::commit(const char* eventDescription) {
|
||||
#ifdef VL_DEBUG
|
||||
if (!m_uncommitted.empty()) {
|
||||
VL_DEBUG_IF(
|
||||
VL_DBG_MSGF(" Committing processes waiting for %s:\n", eventDescription);
|
||||
for (const auto& susp
|
||||
: m_uncommitted) {
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
m_ready.reserve(m_ready.size() + m_uncommitted.size());
|
||||
m_ready.insert(m_ready.end(), std::make_move_iterator(m_uncommitted.begin()),
|
||||
std::make_move_iterator(m_uncommitted.end()));
|
||||
m_uncommitted.clear();
|
||||
}
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
void VlTriggerScheduler::dump(const char* eventDescription) const {
|
||||
if (m_ready.empty()) {
|
||||
VL_DBG_MSGF(" No ready processes waiting for %s\n", eventDescription);
|
||||
} else {
|
||||
for (const auto& susp : m_ready) {
|
||||
VL_DBG_MSGF(" Ready processes waiting for %s:\n", eventDescription);
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
}
|
||||
}
|
||||
if (!m_uncommitted.empty()) {
|
||||
VL_DBG_MSGF(" Uncommitted processes waiting for %s:\n", eventDescription);
|
||||
for (const auto& susp : m_uncommitted) {
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// VlDynamicTriggerScheduler:: Methods
|
||||
|
||||
bool VlDynamicTriggerScheduler::evaluate() {
|
||||
VL_DEBUG_IF(dump(););
|
||||
std::swap(m_suspended, m_evaluated);
|
||||
for (auto& coro : m_evaluated) coro.resume();
|
||||
m_evaluated.clear();
|
||||
return !m_triggered.empty();
|
||||
}
|
||||
|
||||
void VlDynamicTriggerScheduler::doPostUpdates() {
|
||||
VL_DEBUG_IF(if (!m_post.empty())
|
||||
VL_DBG_MSGF(" Doing post updates for processes:\n"); //
|
||||
for (const auto& susp
|
||||
: m_post) {
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
});
|
||||
for (auto& coro : m_post) coro.resume();
|
||||
m_post.clear();
|
||||
}
|
||||
|
||||
void VlDynamicTriggerScheduler::resume() {
|
||||
VL_DEBUG_IF(if (!m_triggered.empty()) VL_DBG_MSGF(" Resuming processes:\n"); //
|
||||
for (const auto& susp
|
||||
: m_triggered) {
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
});
|
||||
for (auto& coro : m_triggered) coro.resume();
|
||||
m_triggered.clear();
|
||||
}
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
void VlDynamicTriggerScheduler::dump() const {
|
||||
if (m_suspended.empty()) {
|
||||
VL_DBG_MSGF(" No suspended processes waiting for dynamic trigger evaluation\n");
|
||||
} else {
|
||||
for (const auto& susp : m_suspended) {
|
||||
VL_DBG_MSGF(" Suspended processes waiting for dynamic trigger evaluation:\n");
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// VlForkSync:: Methods
|
||||
|
||||
void VlForkSync::done(const char* filename, int lineno) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Process forked at %s:%d finished\n", filename, lineno););
|
||||
if (m_join->m_counter > 0) m_join->m_counter--;
|
||||
if (m_join->m_counter == 0) m_join->m_susp.resume();
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VlCoroutine:: Methods
|
||||
|
||||
VlCoroutine::VlPromise::~VlPromise() {
|
||||
// Indicate to the return object that the coroutine has finished or been destroyed
|
||||
if (m_corop) m_corop->m_promisep = nullptr;
|
||||
// If there is a continuation, destroy it
|
||||
if (m_continuation) m_continuation.destroy();
|
||||
}
|
||||
|
||||
std::suspend_never VlCoroutine::VlPromise::final_suspend() noexcept {
|
||||
// Indicate to the return object that the coroutine has finished
|
||||
if (m_corop) {
|
||||
m_corop->m_promisep = nullptr;
|
||||
// Forget the return value, we won't need it and it won't be able to let us know if
|
||||
// it's destroyed
|
||||
m_corop = nullptr;
|
||||
}
|
||||
// If there is a continuation, resume it
|
||||
if (m_continuation) {
|
||||
m_continuation();
|
||||
m_continuation = nullptr;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2022 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU Lesser
|
||||
// General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated timing header
|
||||
///
|
||||
/// This file is included automatically by Verilator in some of the C++ files
|
||||
/// it generates if timing features are used.
|
||||
///
|
||||
/// This file is not part of the Verilated public-facing API.
|
||||
/// It is only for internal use.
|
||||
///
|
||||
/// See the internals documentation docs/internals.rst for details.
|
||||
///
|
||||
//*************************************************************************
|
||||
#ifndef VERILATOR_VERILATED_TIMING_H_
|
||||
#define VERILATOR_VERILATED_TIMING_H_
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
// clang-format off
|
||||
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
|
||||
#if defined _LIBCPP_VERSION // libc++
|
||||
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
|
||||
# endif
|
||||
# include <experimental/coroutine>
|
||||
namespace std {
|
||||
using namespace experimental; // Bring std::experimental into the std namespace
|
||||
}
|
||||
#else
|
||||
# if defined __clang__ && defined __GLIBCXX__
|
||||
# define __cpp_impl_coroutine 1 // Clang doesn't define this, but it's needed for libstdc++
|
||||
# endif
|
||||
# include <coroutine>
|
||||
# if __clang_major__ < 14
|
||||
namespace std { // Bring coroutine library into std::experimental, as Clang < 14 expects it to be there
|
||||
namespace experimental {
|
||||
using namespace std;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
// Placeholder for compiling with --protect-ids
|
||||
#define VL_UNKNOWN "<unknown>"
|
||||
|
||||
//=============================================================================
|
||||
// VlFileLineDebug stores a SystemVerilog source code location. Used in VlCoroutineHandle for
|
||||
// debugging purposes.
|
||||
|
||||
class VlFileLineDebug final {
|
||||
// MEMBERS
|
||||
#ifdef VL_DEBUG
|
||||
const char* m_filename = nullptr;
|
||||
int m_lineno = 0;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// Construct
|
||||
VlFileLineDebug() = default;
|
||||
VlFileLineDebug(const char* filename, int lineno)
|
||||
#ifdef VL_DEBUG
|
||||
: m_filename{filename}
|
||||
, m_lineno{lineno}
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// METHODS
|
||||
#ifdef VL_DEBUG
|
||||
const char* filename() const { return m_filename; }
|
||||
int lineno() const { return m_lineno; }
|
||||
#endif
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
|
||||
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
|
||||
// suspended, another VlCoroutineHandle was created to manage it.
|
||||
|
||||
class VlCoroutineHandle final {
|
||||
VL_UNCOPYABLE(VlCoroutineHandle);
|
||||
|
||||
// MEMBERS
|
||||
std::coroutine_handle<> m_coro; // The wrapped coroutine handle
|
||||
VlFileLineDebug m_fileline;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// Construct
|
||||
VlCoroutineHandle()
|
||||
: m_coro{nullptr} {}
|
||||
VlCoroutineHandle(std::coroutine_handle<> coro, VlFileLineDebug fileline)
|
||||
: m_coro{coro}
|
||||
, m_fileline{fileline} {}
|
||||
// Move the handle, leaving a nullptr
|
||||
VlCoroutineHandle(VlCoroutineHandle&& moved)
|
||||
: m_coro{std::exchange(moved.m_coro, nullptr)}
|
||||
, m_fileline{moved.m_fileline} {}
|
||||
// Destroy if the handle isn't null
|
||||
~VlCoroutineHandle() {
|
||||
// Usually these coroutines should get resumed; we only need to clean up if we destroy a
|
||||
// model with some coroutines suspended
|
||||
if (VL_UNLIKELY(m_coro)) m_coro.destroy();
|
||||
}
|
||||
// METHODS
|
||||
// Move the handle, leaving a null handle
|
||||
auto& operator=(VlCoroutineHandle&& moved) {
|
||||
m_coro = std::exchange(moved.m_coro, nullptr);
|
||||
return *this;
|
||||
}
|
||||
// Resume the coroutine if the handle isn't null
|
||||
void resume();
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlDelayScheduler stores coroutines to be resumed at a certain simulation time. If the current
|
||||
// time is equal to a coroutine's resume time, the coroutine gets resumed.
|
||||
|
||||
class VlDelayScheduler final {
|
||||
// TYPES
|
||||
struct VlDelayedCoroutine {
|
||||
uint64_t m_timestep; // Simulation time when the coroutine should be resumed
|
||||
VlCoroutineHandle m_handle; // The suspended coroutine to be resumed
|
||||
|
||||
// Comparison operator for std::push_heap(), std::pop_heap()
|
||||
bool operator<(const VlDelayedCoroutine& other) const {
|
||||
return m_timestep > other.m_timestep;
|
||||
}
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
};
|
||||
using VlDelayedCoroutineQueue = std::vector<VlDelayedCoroutine>;
|
||||
|
||||
// MEMBERS
|
||||
VerilatedContext& m_context;
|
||||
VlDelayedCoroutineQueue m_queue; // Coroutines to be restored at a certain simulation time
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit VlDelayScheduler(VerilatedContext& context)
|
||||
: m_context{context} {}
|
||||
// METHODS
|
||||
// Resume coroutines waiting for the current simulation time
|
||||
void resume();
|
||||
// Returns the simulation time of the next time slot (aborts if there are no delayed
|
||||
// coroutines)
|
||||
uint64_t nextTimeSlot() const;
|
||||
// Are there no delayed coroutines awaiting?
|
||||
bool empty() const { return m_queue.empty(); }
|
||||
// Are there coroutines to resume at the current simulation time?
|
||||
bool awaitingCurrentTime() const {
|
||||
return !empty() && m_queue.front().m_timestep <= m_context.time();
|
||||
}
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
// Used by coroutines for co_awaiting a certain simulation time
|
||||
auto delay(uint64_t delay, const char* filename = VL_UNKNOWN, int lineno = 0) {
|
||||
struct Awaitable {
|
||||
VlDelayedCoroutineQueue& queue;
|
||||
uint64_t delay;
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
queue.push_back({delay, VlCoroutineHandle{coro, fileline}});
|
||||
// Move last element to the proper place in the max-heap
|
||||
std::push_heap(queue.begin(), queue.end());
|
||||
}
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{m_queue, m_context.time() + delay, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlTriggerScheduler stores coroutines to be resumed by a trigger. It does not keep track of its
|
||||
// trigger, relying on calling code to resume when appropriate. Coroutines are kept in two stages
|
||||
// - 'uncommitted' and 'ready'. Whenever a coroutine is suspended, it lands in the 'uncommited'
|
||||
// stage. Only when commit() is called, these coroutines get moved to the 'ready' stage. That's
|
||||
// when they can be resumed. This is done to avoid resuming processes before they start waiting.
|
||||
|
||||
class VlTriggerScheduler final {
|
||||
// TYPES
|
||||
using VlCoroutineVec = std::vector<VlCoroutineHandle>;
|
||||
|
||||
// MEMBERS
|
||||
VlCoroutineVec m_uncommitted; // Coroutines suspended before commit() was called
|
||||
// (not resumable)
|
||||
VlCoroutineVec m_ready; // Coroutines that can be resumed (all coros from m_uncommitted are
|
||||
// moved here in commit())
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
// Resumes all coroutines from the 'ready' stage
|
||||
void resume(const char* eventDescription = VL_UNKNOWN);
|
||||
// Moves all coroutines from m_uncommitted to m_ready
|
||||
void commit(const char* eventDescription = VL_UNKNOWN);
|
||||
// Are there no coroutines awaiting?
|
||||
bool empty() const { return m_ready.empty() && m_uncommitted.empty(); }
|
||||
#ifdef VL_DEBUG
|
||||
void dump(const char* eventDescription) const;
|
||||
#endif
|
||||
// Used by coroutines for co_awaiting a certain trigger
|
||||
auto trigger(const char* eventDescription = VL_UNKNOWN, const char* filename = VL_UNKNOWN,
|
||||
int lineno = 0) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
|
||||
eventDescription, filename, lineno););
|
||||
struct Awaitable {
|
||||
VlCoroutineVec& suspended; // Coros waiting on trigger
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
suspended.emplace_back(coro, fileline);
|
||||
}
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{m_uncommitted, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlDynamicTriggerScheduler is used for cases where triggers cannot be statically referenced and
|
||||
// evaluated. Coroutines that make use of this scheduler must adhere to a certain procedure:
|
||||
// __Vtrigger = 0;
|
||||
// <locals and inits required for trigger eval>
|
||||
// while (!__Vtrigger) {
|
||||
// co_await __VdynSched.evaluation();
|
||||
// <pre updates>;
|
||||
// __Vtrigger = <trigger eval>;
|
||||
// [optionally] co_await __VdynSched.postUpdate();
|
||||
// <post updates>;
|
||||
// }
|
||||
// co_await __VdynSched.resumption();
|
||||
// The coroutines get resumed at trigger evaluation time, evaluate their local triggers, optionally
|
||||
// await the post update step, and if the trigger is set, await proper resumption in the 'act' eval
|
||||
// step.
|
||||
|
||||
class VlDynamicTriggerScheduler final {
|
||||
// TYPES
|
||||
using VlCoroutineVec = std::vector<VlCoroutineHandle>;
|
||||
|
||||
// MEMBERS
|
||||
VlCoroutineVec m_suspended; // Suspended coroutines awaiting trigger evaluation
|
||||
VlCoroutineVec m_evaluated; // Coroutines currently being evaluated (for evaluate())
|
||||
VlCoroutineVec m_triggered; // Coroutines whose triggers were set, and are awaiting resumption
|
||||
VlCoroutineVec m_post; // Coroutines awaiting the post update step (only relevant for triggers
|
||||
// with destructive post updates, e.g. named events)
|
||||
|
||||
// METHODS
|
||||
auto awaitable(VlCoroutineVec& queue, const char* filename, int lineno) {
|
||||
struct Awaitable {
|
||||
VlCoroutineVec& suspended; // Coros waiting on trigger
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
suspended.emplace_back(coro, fileline);
|
||||
}
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{queue, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
|
||||
public:
|
||||
// Evaluates all dynamic triggers (resumed coroutines that co_await evaluation())
|
||||
bool evaluate();
|
||||
// Runs post updates for all dynamic triggers (resumes coroutines that co_await postUpdate())
|
||||
void doPostUpdates();
|
||||
// Resumes all coroutines whose triggers are set (those that co_await resumption())
|
||||
void resume();
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
// Used by coroutines for co_awaiting trigger evaluation
|
||||
auto evaluation(const char* eventDescription, const char* filename, int lineno) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
|
||||
eventDescription, filename, lineno););
|
||||
return awaitable(m_suspended, filename, lineno);
|
||||
}
|
||||
// Used by coroutines for co_awaiting the trigger post update step
|
||||
auto postUpdate(const char* eventDescription, const char* filename, int lineno) {
|
||||
VL_DEBUG_IF(
|
||||
VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting the post update step\n",
|
||||
eventDescription, filename, lineno););
|
||||
return awaitable(m_post, filename, lineno);
|
||||
}
|
||||
// Used by coroutines for co_awaiting the resumption step (in 'act' eval)
|
||||
auto resumption(const char* eventDescription, const char* filename, int lineno) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting resumption\n",
|
||||
eventDescription, filename, lineno););
|
||||
return awaitable(m_triggered, filename, lineno);
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlNow is a helper awaitable type that always suspends, and then immediately resumes a coroutine.
|
||||
// Allows forcing the move of coroutine locals to the heap.
|
||||
|
||||
struct VlNow {
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
bool await_suspend(std::coroutine_handle<>) const { return false; } // Resume immediately
|
||||
void await_resume() const {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlForever is a helper awaitable type for suspending coroutines forever. Used for constant
|
||||
// wait statements.
|
||||
|
||||
struct VlForever {
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) const { coro.destroy(); }
|
||||
void await_resume() const {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlForkSync is used to manage fork..join and fork..join_any constructs.
|
||||
|
||||
class VlForkSync final {
|
||||
// VlJoin stores the handle of a suspended coroutine that did a fork..join or fork..join_any.
|
||||
// If the counter reaches 0, the suspended coroutine shall be resumed.
|
||||
struct VlJoin {
|
||||
size_t m_counter = 0; // When reaches 0, resume suspended coroutine
|
||||
VlCoroutineHandle m_susp; // Coroutine to resume
|
||||
};
|
||||
|
||||
// The join info is shared among all forked processes
|
||||
std::shared_ptr<VlJoin> m_join;
|
||||
|
||||
public:
|
||||
// Create the join object and set the counter to the specified number
|
||||
void init(size_t count) { m_join.reset(new VlJoin{count, {}}); }
|
||||
// Called whenever any of the forked processes finishes. If the join counter reaches 0, the
|
||||
// main process gets resumed
|
||||
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
|
||||
// Used by coroutines for co_awaiting a join
|
||||
auto join(const char* filename = VL_UNKNOWN, int lineno = 0) {
|
||||
assert(m_join);
|
||||
VL_DEBUG_IF(
|
||||
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
|
||||
struct Awaitable {
|
||||
const std::shared_ptr<VlJoin> join; // Join to await on
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() { return join->m_counter == 0; } // Suspend if join still exists
|
||||
void await_suspend(std::coroutine_handle<> coro) { join->m_susp = {coro, fileline}; }
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{m_join, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoroutine
|
||||
// Return value of a coroutine. Used for chaining coroutine suspension/resumption.
|
||||
|
||||
class VlCoroutine final {
|
||||
private:
|
||||
// TYPES
|
||||
struct VlPromise {
|
||||
std::coroutine_handle<> m_continuation; // Coroutine to resume after this one finishes
|
||||
VlCoroutine* m_corop = nullptr; // Pointer to the coroutine return object
|
||||
|
||||
~VlPromise();
|
||||
|
||||
VlCoroutine get_return_object() { return {this}; }
|
||||
|
||||
// Never suspend at the start of the coroutine
|
||||
std::suspend_never initial_suspend() const { return {}; }
|
||||
|
||||
// Never suspend at the end of the coroutine (thanks to this, the coroutine will clean up
|
||||
// after itself)
|
||||
std::suspend_never final_suspend() noexcept;
|
||||
|
||||
void unhandled_exception() const { std::abort(); }
|
||||
void return_void() const {}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
VlPromise* m_promisep; // The promise created for this coroutine
|
||||
|
||||
public:
|
||||
// TYPES
|
||||
using promise_type = VlPromise; // promise_type has to be public
|
||||
|
||||
// CONSTRUCTORS
|
||||
// Construct
|
||||
VlCoroutine(VlPromise* promisep)
|
||||
: m_promisep{promisep} {
|
||||
m_promisep->m_corop = this;
|
||||
}
|
||||
// Move. Update the pointers each time the return object is moved
|
||||
VlCoroutine(VlCoroutine&& other)
|
||||
: m_promisep{std::exchange(other.m_promisep, nullptr)} {
|
||||
if (m_promisep) m_promisep->m_corop = this;
|
||||
}
|
||||
~VlCoroutine() {
|
||||
// Indicate to the promise that the return object is gone
|
||||
if (m_promisep) m_promisep->m_corop = nullptr;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
// Suspend the awaiter if the coroutine is suspended (the promise exists)
|
||||
bool await_ready() const noexcept { return !m_promisep; }
|
||||
// Set the awaiting coroutine as the continuation of the current coroutine
|
||||
void await_suspend(std::coroutine_handle<> coro) { m_promisep->m_continuation = coro; }
|
||||
void await_resume() const noexcept {}
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
+17
-17
@@ -318,8 +318,8 @@ protected:
|
||||
void flushBase();
|
||||
|
||||
#ifdef VL_THREADED
|
||||
inline bool offload() const { return m_offload; }
|
||||
inline bool parallel() const { return m_parallel; }
|
||||
bool offload() const { return m_offload; }
|
||||
bool parallel() const { return m_parallel; }
|
||||
#else
|
||||
static constexpr bool offload() { return false; }
|
||||
static constexpr bool parallel() { return false; }
|
||||
@@ -425,7 +425,7 @@ public:
|
||||
// duck-typed void emitWData(uint32_t code, const WData* newvalp, int bits) = 0;
|
||||
// duck-typed void emitDouble(uint32_t code, double newval) = 0;
|
||||
|
||||
VL_ATTR_ALWINLINE inline uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
|
||||
VL_ATTR_ALWINLINE uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
|
||||
|
||||
// Write to previous value buffer value and emit trace entry.
|
||||
void fullBit(uint32_t* oldp, CData newval);
|
||||
@@ -441,27 +441,27 @@ public:
|
||||
// thread and are called chg*Impl
|
||||
|
||||
// Check previous dumped value of signal. If changed, then emit trace entry
|
||||
VL_ATTR_ALWINLINE inline void chgBit(uint32_t* oldp, CData newval) {
|
||||
VL_ATTR_ALWINLINE void chgBit(uint32_t* oldp, CData newval) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgCData(uint32_t* oldp, CData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE void chgCData(uint32_t* oldp, CData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgSData(uint32_t* oldp, SData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE void chgSData(uint32_t* oldp, SData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgIData(uint32_t* oldp, IData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE void chgIData(uint32_t* oldp, IData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgQData(uint32_t* oldp, QData newval, int bits) {
|
||||
VL_ATTR_ALWINLINE void chgQData(uint32_t* oldp, QData newval, int bits) {
|
||||
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) {
|
||||
if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
|
||||
fullWData(oldp, newvalp, bits);
|
||||
@@ -469,7 +469,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
VL_ATTR_ALWINLINE inline void chgDouble(uint32_t* oldp, double newval) {
|
||||
VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
|
||||
}
|
||||
@@ -498,48 +498,48 @@ public:
|
||||
// Hot path internal interface to Verilator generated code
|
||||
|
||||
// Offloaded tracing. Just dump everything in the offload buffer
|
||||
inline void chgBit(uint32_t code, CData newval) {
|
||||
void chgBit(uint32_t code, CData newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgCData(uint32_t code, CData newval, int bits) {
|
||||
void chgCData(uint32_t code, CData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgSData(uint32_t code, SData newval, int bits) {
|
||||
void chgSData(uint32_t code, SData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgIData(uint32_t code, IData newval, int bits) {
|
||||
void chgIData(uint32_t code, IData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgQData(uint32_t code, QData newval, int bits) {
|
||||
void chgQData(uint32_t code, QData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
*reinterpret_cast<QData*>(m_offloadBufferWritep + 2) = newval;
|
||||
m_offloadBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
void chgWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_offloadBufferWritep++ = newvalp[i]; }
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
inline void chgDouble(uint32_t code, double newval) {
|
||||
void chgDouble(uint32_t code, double newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
|
||||
@@ -551,15 +551,15 @@ template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runOffloadedCallbacks(
|
||||
const std::vector<CallbackRecord>& cbVec) {
|
||||
// Fall back on sequential execution
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
#ifdef VL_THREADED
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
Buffer* traceBufferp = getTraceBuffer();
|
||||
cbr.m_dumpOffloadCb(cbr.m_userp, static_cast<OffloadBuffer*>(traceBufferp));
|
||||
commitTraceBuffer(traceBufferp);
|
||||
#else
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
if (!cbVec.empty()) VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -586,7 +586,9 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
||||
if (!preChangeDump()) return;
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
uint32_t* bufferp = nullptr;
|
||||
#endif
|
||||
if (offload()) {
|
||||
#ifdef VL_THREADED
|
||||
// Currently only incremental dumps run on the worker thread
|
||||
@@ -642,6 +644,10 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
|
||||
// Assert no buffer overflow
|
||||
assert(m_offloadBufferWritep - bufferp <= m_offloadBufferSize);
|
||||
|
||||
// Reset our pointers as we are giving up the buffer
|
||||
m_offloadBufferWritep = nullptr;
|
||||
m_offloadBufferEndp = nullptr;
|
||||
|
||||
// Pass it to the worker thread
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
}
|
||||
|
||||
+265
-2
@@ -29,6 +29,8 @@
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@@ -70,6 +72,72 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
|
||||
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
|
||||
#define VL_OUTW(name, msb, lsb, words) VlWide<words> name ///< Declare output signal, 65+ bits
|
||||
|
||||
//===================================================================
|
||||
// Activity trigger vector
|
||||
|
||||
template <std::size_t T_size> //
|
||||
class VlTriggerVec final {
|
||||
// TODO: static assert T_size > 0, and don't generate when empty
|
||||
private:
|
||||
// MEMBERS
|
||||
std::array<bool, T_size> m_flags; // State of the assoc array
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlTriggerVec() { clear(); }
|
||||
~VlTriggerVec() = default;
|
||||
|
||||
// METHODS
|
||||
|
||||
// Set all elements to false
|
||||
void clear() { m_flags.fill(false); }
|
||||
|
||||
// Reference to element at 'index'
|
||||
bool& at(size_t index) { return m_flags.at(index); }
|
||||
|
||||
// Return true iff at least one element is set
|
||||
bool any() const {
|
||||
for (size_t i = 0; i < m_flags.size(); ++i)
|
||||
if (m_flags[i]) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set all elements true in 'this' that are set in 'other'
|
||||
void set(const VlTriggerVec<T_size>& other) {
|
||||
for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] |= other.m_flags[i];
|
||||
}
|
||||
|
||||
// Set elements of 'this' to 'a & !b' element-wise
|
||||
void andNot(const VlTriggerVec<T_size>& a, const VlTriggerVec<T_size>& b) {
|
||||
for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] = a.m_flags[i] && !b.m_flags[i];
|
||||
}
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// SystemVerilog event type
|
||||
|
||||
class VlEvent final {
|
||||
// MEMBERS
|
||||
bool m_fired = false; // Fired on this scheduling iteration
|
||||
bool m_triggered = false; // Triggered state of event persisting until next time step
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlEvent() = default;
|
||||
~VlEvent() = default;
|
||||
|
||||
// METHODS
|
||||
void fire() { m_fired = m_triggered = true; }
|
||||
bool isFired() const { return m_fired; }
|
||||
bool isTriggered() const { return m_triggered; }
|
||||
void clearFired() { m_fired = false; }
|
||||
void clearTriggered() { m_triggered = false; }
|
||||
};
|
||||
|
||||
inline std::string VL_TO_STRING(const VlEvent& e) {
|
||||
return std::string("triggered=") + (e.isTriggered() ? "true" : "false");
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Shuffle RNG
|
||||
|
||||
@@ -146,6 +214,12 @@ struct VlWide final {
|
||||
// Default copy assignment operators are used.
|
||||
operator WDataOutP() { return &m_storage[0]; } // This also allows []
|
||||
operator WDataInP() const { return &m_storage[0]; } // This also allows []
|
||||
bool operator!=(const VlWide<T_Words>& that) const {
|
||||
for (size_t i = 0; i < T_Words; ++i) {
|
||||
if (m_storage[i] != that.m_storage[i]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
const EData& at(size_t index) const { return m_storage[index]; }
|
||||
@@ -891,6 +965,12 @@ struct VlUnpacked final {
|
||||
T_Value& operator[](size_t index) { return m_storage[index]; }
|
||||
const T_Value& operator[](size_t index) const { return m_storage[index]; }
|
||||
|
||||
// *this != that, which might be used for change detection/trigger computation, but avoid
|
||||
// operator overloading in VlUnpacked for safety in other contexts.
|
||||
bool neq(const VlUnpacked<T_Value, T_Depth>& that) const { return neq(*this, that); }
|
||||
// Similar to 'neq' above, *this = that used for change detection
|
||||
void assign(const VlUnpacked<T_Value, T_Depth>& that) { *this = that; }
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
std::string out = "'{";
|
||||
@@ -901,6 +981,22 @@ struct VlUnpacked final {
|
||||
}
|
||||
return out + "} ";
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T_Val, std::size_t T_Dep>
|
||||
static bool neq(const VlUnpacked<T_Val, T_Dep>& a, const VlUnpacked<T_Val, T_Dep>& b) {
|
||||
for (size_t i = 0; i < T_Dep; ++i) {
|
||||
// Recursive 'neq', in case T_Val is also a VlUnpacked<_, _>
|
||||
if (neq(a.m_storage[i], b.m_storage[i])) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T_Other> //
|
||||
static bool neq(const T_Other& a, const T_Other& b) {
|
||||
// Base case (T_Other is not VlUnpacked<_, _>), fall back on !=
|
||||
return a != b;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value, std::size_t T_Depth>
|
||||
@@ -908,12 +1004,179 @@ std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
class VlClass; // See below
|
||||
|
||||
//===================================================================
|
||||
// Class providing delayed deletion of garbage objects. Objects get deleted only when 'deleteAll()'
|
||||
// is called, or the deleter itself is destroyed.
|
||||
|
||||
class VlDeleter final {
|
||||
// MEMBERS
|
||||
// Queue of new objects that should be deleted
|
||||
std::vector<VlClass*> m_newGarbage VL_GUARDED_BY(m_mutex);
|
||||
// Queue of objects currently being deleted (only for deleteAll())
|
||||
std::vector<VlClass*> m_toDelete VL_GUARDED_BY(m_deleteMutex);
|
||||
mutable VerilatedMutex m_mutex; // Mutex protecting the 'new garbage' queue
|
||||
mutable VerilatedMutex m_deleteMutex; // Mutex protecting the delete queue
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlDeleter() = default;
|
||||
~VlDeleter() { deleteAll(); }
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(VlDeleter);
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
// Adds a new object to the 'new garbage' queue.
|
||||
void put(VlClass* const objp) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_newGarbage.push_back(objp);
|
||||
}
|
||||
|
||||
// Deletes all queued garbage objects.
|
||||
void deleteAll() VL_MT_SAFE;
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Base class for all verilated classes. Includes a reference counter, and a pointer to the deleter
|
||||
// object that should destroy it after the counter reaches 0. This allows for easy construction of
|
||||
// VlClassRefs from 'this'. Also declares a virtual constructor, so that the object can be deleted
|
||||
// using a base pointer.
|
||||
|
||||
class VlClass VL_NOT_FINAL {
|
||||
// TYPES
|
||||
template <typename T_Class>
|
||||
friend class VlClassRef; // Needed for access to the ref counter and deleter
|
||||
|
||||
// MEMBERS
|
||||
std::atomic<size_t> m_counter{0}; // Reference count for this object
|
||||
VlDeleter* m_deleter = nullptr; // The deleter that will delete this object
|
||||
|
||||
// METHODS
|
||||
// Atomically increments the reference counter
|
||||
void refCountInc() VL_MT_SAFE { ++m_counter; }
|
||||
// Atomically decrements the reference counter. Assuming VlClassRef semantics are sound, it
|
||||
// should never get called at m_counter == 0.
|
||||
void refCountDec() VL_MT_SAFE {
|
||||
if (!--m_counter) m_deleter->put(this);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlClass() = default;
|
||||
VlClass(const VlClass& copied) {}
|
||||
virtual ~VlClass() {}
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Represents the null pointer. Used for setting VlClassRef to null instead of
|
||||
// via nullptr_t, to prevent the implicit conversion of 0 to nullptr.
|
||||
struct VlNull {
|
||||
operator bool() const { return false; }
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Verilog class reference container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
|
||||
#define VlClassRef std::shared_ptr
|
||||
template <typename T_Class>
|
||||
class VlClassRef final {
|
||||
private:
|
||||
// TYPES
|
||||
template <typename T_OtherClass>
|
||||
friend class VlClassRef; // Needed for template copy/move assignments
|
||||
|
||||
// MEMBERS
|
||||
T_Class* m_objp = nullptr; // Object pointed to
|
||||
|
||||
// METHODS
|
||||
// Increase reference counter with null check
|
||||
void refCountInc() const VL_MT_SAFE {
|
||||
if (m_objp) m_objp->refCountInc();
|
||||
}
|
||||
// Decrease reference counter with null check
|
||||
void refCountDec() const VL_MT_SAFE {
|
||||
if (m_objp) m_objp->refCountDec();
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlClassRef() = default;
|
||||
// Init with nullptr
|
||||
VlClassRef(VlNull){};
|
||||
template <typename... T_Args>
|
||||
VlClassRef(VlDeleter& deleter, T_Args&&... args)
|
||||
: m_objp{new T_Class{std::forward<T_Args>(args)...}} {
|
||||
m_objp->m_deleter = &deleter;
|
||||
refCountInc();
|
||||
}
|
||||
// Explicit to avoid implicit conversion from 0
|
||||
explicit VlClassRef(T_Class* objp)
|
||||
: m_objp{objp} {
|
||||
refCountInc();
|
||||
}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlClassRef(const VlClassRef& copied)
|
||||
: m_objp{copied.m_objp} {
|
||||
refCountInc();
|
||||
}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlClassRef(VlClassRef&& moved)
|
||||
: m_objp{vlstd::exchange(moved.m_objp, nullptr)} {}
|
||||
~VlClassRef() { refCountDec(); }
|
||||
|
||||
// METHODS
|
||||
// Copy and move assignments
|
||||
VlClassRef& operator=(const VlClassRef& copied) {
|
||||
refCountDec();
|
||||
m_objp = copied.m_objp;
|
||||
refCountInc();
|
||||
return *this;
|
||||
}
|
||||
VlClassRef& operator=(VlClassRef&& moved) {
|
||||
refCountDec();
|
||||
m_objp = vlstd::exchange(moved.m_objp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
template <typename T_OtherClass>
|
||||
VlClassRef& operator=(const VlClassRef<T_OtherClass>& copied) {
|
||||
refCountDec();
|
||||
m_objp = copied.m_objp;
|
||||
refCountInc();
|
||||
return *this;
|
||||
}
|
||||
template <typename T_OtherClass>
|
||||
VlClassRef& operator=(VlClassRef<T_OtherClass>&& moved) {
|
||||
refCountDec();
|
||||
m_objp = vlstd::exchange(moved.m_objp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
// Assign with nullptr
|
||||
VlClassRef& operator=(VlNull) {
|
||||
refCountDec();
|
||||
m_objp = nullptr;
|
||||
return *this;
|
||||
}
|
||||
// Dynamic caster
|
||||
template <typename T_OtherClass>
|
||||
VlClassRef<T_OtherClass> dynamicCast() const {
|
||||
return VlClassRef<T_OtherClass>{dynamic_cast<T_OtherClass*>(m_objp)};
|
||||
}
|
||||
// Dereference operators
|
||||
T_Class& operator*() const { return *m_objp; }
|
||||
T_Class* operator->() const { return m_objp; }
|
||||
// For 'if (ptr)...'
|
||||
operator bool() const { return m_objp; }
|
||||
};
|
||||
|
||||
#define VL_NEW(Class, ...) \
|
||||
VlClassRef<Class> { vlSymsp->__Vm_deleter, __VA_ARGS__ }
|
||||
|
||||
#define VL_KEEP_THIS \
|
||||
VlClassRef<std::remove_pointer<decltype(this)>::type> __Vthisref { this }
|
||||
|
||||
template <class T> // T typically of type VlClassRef<x>
|
||||
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
|
||||
@@ -923,7 +1186,7 @@ inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
|
||||
|
||||
template <typename T, typename U>
|
||||
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
|
||||
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
|
||||
VlClassRef<U> casted = in.template dynamicCast<U>();
|
||||
if (VL_LIKELY(casted)) {
|
||||
outr = casted;
|
||||
return true;
|
||||
|
||||
@@ -632,10 +632,10 @@ void VerilatedVcd::commitTraceBuffer(VerilatedVcd::Buffer* bufp) {
|
||||
//=============================================================================
|
||||
// Trace rendering primitives
|
||||
|
||||
static inline void
|
||||
VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
|
||||
static void VerilatedVcdCCopyAndAppendNewLine(char* writep,
|
||||
const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
|
||||
|
||||
static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
|
||||
static 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.
|
||||
|
||||
@@ -73,7 +73,7 @@ private:
|
||||
|
||||
void bufferResize(size_t minsize);
|
||||
void bufferFlush() VL_MT_UNSAFE_ONE;
|
||||
inline void bufferCheck() {
|
||||
void bufferCheck() {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush();
|
||||
@@ -210,13 +210,13 @@ class VerilatedVcdBuffer VL_NOT_FINAL {
|
||||
// Implementation of VerilatedTraceBuffer interface
|
||||
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
|
||||
// called from only one place (the full* methods), so always inline them.
|
||||
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE inline void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitSData(uint32_t code, SData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitIData(uint32_t code, IData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitQData(uint32_t code, QData newval, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
VL_ATTR_ALWINLINE inline void emitDouble(uint32_t code, double newval);
|
||||
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -313,7 +313,7 @@ public:
|
||||
}
|
||||
|
||||
// Internal class access
|
||||
inline VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||
VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
||||
+11
-10
@@ -55,38 +55,38 @@ public:
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
}
|
||||
/// Destruct, flush, and close the dump
|
||||
virtual ~VerilatedVcdSc() /*override*/ { close(); }
|
||||
~VerilatedVcdSc() override { close(); }
|
||||
|
||||
// METHODS - for SC kernel
|
||||
// Called by SystemC simulate()
|
||||
virtual void cycle(bool delta_cycle) {
|
||||
void cycle(bool delta_cycle) override {
|
||||
if (!delta_cycle) this->dump(sc_time_stamp().to_double());
|
||||
}
|
||||
|
||||
// Override VerilatedVcdC. Must be called after starting simulation.
|
||||
// cppcheck-suppress missingOverride // GCC won't accept override
|
||||
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
|
||||
void open(const char* filename) override VL_MT_SAFE;
|
||||
|
||||
private:
|
||||
// METHODS - Fake outs for linker
|
||||
|
||||
#ifdef NC_SYSTEMC
|
||||
// Cadence Incisive has these as abstract functions so we must create them
|
||||
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
|
||||
void set_time_unit(int exponent10_seconds) override {} // deprecated
|
||||
#endif
|
||||
virtual void set_time_unit(double v, sc_time_unit tu) {} // LCOV_EXCL_LINE
|
||||
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
|
||||
|
||||
//--------------------------------------------------
|
||||
// SystemC 2.1.v1
|
||||
#define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const std::string& name);
|
||||
#define DECL_TRACE_METHOD_A(tp) void trace(const tp& object, const std::string& name) override;
|
||||
#define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace(const tp& object, const std::string& name, int width);
|
||||
void trace(const tp& object, const std::string& name, int width) override;
|
||||
|
||||
virtual void write_comment(const std::string&);
|
||||
virtual void trace(const unsigned int&, const std::string&, const char**);
|
||||
void write_comment(const std::string&) override;
|
||||
void trace(const unsigned int&, const std::string&, const char**) override;
|
||||
|
||||
// clang-format off
|
||||
// Formatting matches that of sc_trace.h
|
||||
// LCOV_EXCL_START
|
||||
#if (SYSTEMC_VERSION >= 20171012)
|
||||
DECL_TRACE_METHOD_A( sc_event )
|
||||
DECL_TRACE_METHOD_A( sc_time )
|
||||
@@ -121,6 +121,7 @@ private:
|
||||
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_bv_base )
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_lv_base )
|
||||
// LCOV_EXCL_STOP
|
||||
// clang-format on
|
||||
|
||||
#undef DECL_TRACE_METHOD_A
|
||||
|
||||
@@ -96,7 +96,7 @@ public:
|
||||
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
|
||||
return newp + 8;
|
||||
}
|
||||
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
|
||||
static void operator delete(void* obj, size_t /*size*/) VL_MT_SAFE {
|
||||
uint8_t* const oldp = (static_cast<uint8_t*>(obj)) - 8;
|
||||
if (VL_UNLIKELY(*(reinterpret_cast<uint32_t*>(oldp)) != activeMagic())) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
@@ -114,7 +114,7 @@ public:
|
||||
static VerilatedVpio* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpio*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
inline vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
|
||||
vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
|
||||
// ACCESSORS
|
||||
virtual const char* name() const { return "<null>"; }
|
||||
virtual const char* fullname() const { return "<null>"; }
|
||||
|
||||
+27
-8
@@ -39,7 +39,7 @@
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define VL_ATTR_ALIGNED(alignment) __attribute__((aligned(alignment)))
|
||||
# define VL_ATTR_ALWINLINE __attribute__((always_inline))
|
||||
# define VL_ATTR_ALWINLINE __attribute__((always_inline)) inline
|
||||
# define VL_ATTR_NOINLINE __attribute__((noinline))
|
||||
# define VL_ATTR_COLD __attribute__((cold))
|
||||
# define VL_ATTR_HOT __attribute__((hot))
|
||||
@@ -69,9 +69,9 @@
|
||||
# define VL_EXCLUDES(x) __attribute__((locks_excluded(x)))
|
||||
# define VL_SCOPED_CAPABILITY __attribute__((scoped_lockable))
|
||||
# endif
|
||||
# define VL_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
# define VL_UNREACHABLE __builtin_unreachable();
|
||||
# define VL_LIKELY(x) __builtin_expect(!!(x), 1) // Prefer over C++20 [[likely]]
|
||||
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0) // Prefer over C++20 [[unlikely]]
|
||||
# define VL_UNREACHABLE __builtin_unreachable() // C++23 std::unreachable()
|
||||
# define VL_PREFETCH_RD(p) __builtin_prefetch((p), 0)
|
||||
# define VL_PREFETCH_RW(p) __builtin_prefetch((p), 1)
|
||||
#endif
|
||||
@@ -163,14 +163,22 @@
|
||||
// Comment tag that Function is pure (and thus also VL_MT_SAFE)
|
||||
#define VL_PURE
|
||||
// Comment tag that function is threadsafe when VL_THREADED
|
||||
#define VL_MT_SAFE
|
||||
#if defined(__clang__)
|
||||
# define VL_MT_SAFE __attribute__((annotate("MT_SAFE")))
|
||||
#else
|
||||
# define VL_MT_SAFE
|
||||
#endif
|
||||
// Comment tag that function is threadsafe when VL_THREADED, only
|
||||
// during normal operation (post-init)
|
||||
#define VL_MT_SAFE_POSTINIT
|
||||
// Attribute that function is clang threadsafe and uses given mutex
|
||||
#define VL_MT_SAFE_EXCLUDES(mutex) VL_EXCLUDES(mutex)
|
||||
// Comment tag that function is not threadsafe when VL_THREADED
|
||||
#define VL_MT_UNSAFE
|
||||
#if defined(__clang__)
|
||||
# define VL_MT_UNSAFE __attribute__((annotate("MT_UNSAFE")))
|
||||
#else
|
||||
# define VL_MT_UNSAFE
|
||||
#endif
|
||||
// Comment tag that function is not threadsafe when VL_THREADED,
|
||||
// protected to make sure single-caller
|
||||
#define VL_MT_UNSAFE_ONE
|
||||
@@ -540,6 +548,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
//=========================================================================
|
||||
// Conversions
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace vlstd {
|
||||
|
||||
template <typename T>
|
||||
@@ -548,8 +558,8 @@ struct reverse_wrapper {
|
||||
|
||||
explicit reverse_wrapper(const T& a_v)
|
||||
: m_v(a_v) {}
|
||||
inline auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
|
||||
inline auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
|
||||
auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
|
||||
auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
|
||||
};
|
||||
|
||||
// C++20's std::ranges::reverse_view
|
||||
@@ -563,6 +573,15 @@ template <class T>
|
||||
T const& as_const(T& v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
// C++14's std::exchange
|
||||
template <class T, class U = T>
|
||||
T exchange(T& obj, U&& new_value) {
|
||||
T old_value = std::move(obj);
|
||||
obj = std::forward<U>(new_value);
|
||||
return old_value;
|
||||
}
|
||||
|
||||
}; // namespace vlstd
|
||||
|
||||
//=========================================================================
|
||||
|
||||
@@ -48,6 +48,7 @@ exclude_line_regexp(r'\bNUM_ASSERT')
|
||||
exclude_line_regexp(r'\bERROR_RSVD_WORD')
|
||||
exclude_line_regexp(r'\bV3ERROR_NA')
|
||||
exclude_line_regexp(r'\bUINFO\b')
|
||||
exclude_line_regexp(r'\bVL_DEFINE_DEBUG_FUNCTIONS\b')
|
||||
|
||||
# Exclude for branch coverage only
|
||||
exclude_branch_regexp(r'\bdebug\(\)')
|
||||
|
||||
+38
-10
@@ -79,7 +79,7 @@ ifeq ($(VL_NOOPT),1)
|
||||
CPPFLAGS += -O0
|
||||
else ifeq ($(VL_DEBUG),)
|
||||
# Optimize
|
||||
CPPFLAGS += -O2
|
||||
CPPFLAGS += -O3
|
||||
else
|
||||
# Debug
|
||||
CPPFLAGS += @CFG_CXXFLAGS_DEBUG@ -DVL_DEBUG -D_GLIBCXX_DEBUG
|
||||
@@ -166,7 +166,6 @@ RAW_OBJS = \
|
||||
V3Case.o \
|
||||
V3Cast.o \
|
||||
V3Cdc.o \
|
||||
V3Changed.o \
|
||||
V3Class.o \
|
||||
V3Clean.o \
|
||||
V3Clock.o \
|
||||
@@ -181,7 +180,15 @@ RAW_OBJS = \
|
||||
V3Depth.o \
|
||||
V3DepthBlock.o \
|
||||
V3Descope.o \
|
||||
V3Dfg.o \
|
||||
V3DfgAstToDfg.o \
|
||||
V3DfgDecomposition.o \
|
||||
V3DfgDfgToAst.o \
|
||||
V3DfgOptimizer.o \
|
||||
V3DfgPasses.o \
|
||||
V3DfgPeephole.o \
|
||||
V3DupFinder.o \
|
||||
V3Timing.o \
|
||||
V3EmitCBase.o \
|
||||
V3EmitCConstPool.o \
|
||||
V3EmitCFunc.o \
|
||||
@@ -201,7 +208,6 @@ RAW_OBJS = \
|
||||
V3FileLine.o \
|
||||
V3Force.o \
|
||||
V3Gate.o \
|
||||
V3GenClk.o \
|
||||
V3Global.o \
|
||||
V3Graph.o \
|
||||
V3GraphAlg.o \
|
||||
@@ -239,6 +245,11 @@ RAW_OBJS = \
|
||||
V3ProtectLib.o \
|
||||
V3Randomize.o \
|
||||
V3Reloop.o \
|
||||
V3Sched.o \
|
||||
V3SchedAcyclic.o \
|
||||
V3SchedPartition.o \
|
||||
V3SchedReplicate.o \
|
||||
V3SchedTiming.o \
|
||||
V3Scope.o \
|
||||
V3Scoreboard.o \
|
||||
V3Slice.o \
|
||||
@@ -276,13 +287,30 @@ VLCOV_OBJS = \
|
||||
|
||||
NON_STANDALONE_HEADERS = \
|
||||
V3AstInlines.h \
|
||||
V3AstNodes.h \
|
||||
V3AstNodeDType.h \
|
||||
V3AstNodeMath.h \
|
||||
V3AstNodeOther.h \
|
||||
V3DfgVertices.h \
|
||||
V3WidthCommit.h \
|
||||
|
||||
AST_DEFS := \
|
||||
V3AstNodeDType.h \
|
||||
V3AstNodeMath.h \
|
||||
V3AstNodeOther.h \
|
||||
|
||||
DFG_DEFS := \
|
||||
V3DfgVertices.h
|
||||
|
||||
#### astgen common flags
|
||||
|
||||
ASTGENFLAGS = -I $(srcdir)
|
||||
ASTGENFLAGS += $(foreach f,$(AST_DEFS),--astdef $f)
|
||||
ASTGENFLAGS += $(foreach f,$(DFG_DEFS),--dfgdef $f)
|
||||
|
||||
#### Linking
|
||||
|
||||
ifeq ($(VL_VLCOV),)
|
||||
PREDEP_H = V3Ast__gen_classes.h
|
||||
PREDEP_H = V3Ast__gen_forward_class_decls.h
|
||||
OBJS += $(RAW_OBJS) $(NC_OBJS)
|
||||
else
|
||||
PREDEP_H =
|
||||
@@ -301,8 +329,8 @@ V3Number_test: V3Number_test.o
|
||||
|
||||
#### Modules
|
||||
|
||||
%__gen.cpp: %.cpp $(ASTGEN) V3Ast.h V3AstNodes.h
|
||||
$(PYTHON3) $(ASTGEN) -I $(srcdir) $*.cpp
|
||||
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
|
||||
$(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) $*.cpp
|
||||
|
||||
%.o: %.cpp
|
||||
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $< -o $@
|
||||
@@ -324,7 +352,7 @@ V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
|
||||
#### Generated files
|
||||
|
||||
# Target rule called before parallel build to make generated files
|
||||
serial:: V3Ast__gen_classes.h V3ParseBison.c
|
||||
serial:: V3Ast__gen_forward_class_decls.h V3ParseBison.c
|
||||
|
||||
serial_vlcov:: vlcovgen.d
|
||||
|
||||
@@ -332,8 +360,8 @@ vlcovgen.d: $(VLCOVGEN) $(srcdir)/../include/verilated_cov_key.h
|
||||
$(PYTHON3) $(VLCOVGEN) --srcdir $(srcdir)
|
||||
touch $@
|
||||
|
||||
V3Ast__gen_classes.h : $(ASTGEN) V3Ast.h V3AstNodes.h
|
||||
$(PYTHON3) $(ASTGEN) -I $(srcdir) --classes
|
||||
V3Ast__gen_forward_class_decls.h: $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
|
||||
$(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) --classes
|
||||
|
||||
V3ParseBison.h: V3ParseBison.c
|
||||
|
||||
|
||||
+233
-210
@@ -38,6 +38,8 @@
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//***** See below for main transformation engine
|
||||
|
||||
//######################################################################
|
||||
@@ -66,8 +68,8 @@ public:
|
||||
: V3GraphVertex{graphp}
|
||||
, m_name{name}
|
||||
, m_type{type} {}
|
||||
virtual string name() const override { return m_name + " " + typestr(); }
|
||||
virtual string dotColor() const override { return user() ? "green" : "black"; }
|
||||
string name() const override { return m_name + " " + typestr(); }
|
||||
string dotColor() const override { return user() ? "green" : "black"; }
|
||||
virtual int type() const { return m_type; }
|
||||
};
|
||||
|
||||
@@ -79,8 +81,6 @@ protected:
|
||||
LatchDetectGraphVertex* m_curVertexp = nullptr; // Current latch detection graph vertex
|
||||
std::vector<AstVarRef*> m_outputs; // Vector of lvalues encountered on this pass
|
||||
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
static LatchDetectGraphVertex* castVertexp(void* vertexp) {
|
||||
return reinterpret_cast<LatchDetectGraphVertex*>(vertexp);
|
||||
}
|
||||
@@ -120,7 +120,7 @@ protected:
|
||||
|
||||
public:
|
||||
LatchDetectGraph() { clear(); }
|
||||
virtual ~LatchDetectGraph() override { clear(); }
|
||||
~LatchDetectGraph() override { clear(); }
|
||||
// ACCESSORS
|
||||
LatchDetectGraphVertex* currentp() { return m_curVertexp; }
|
||||
void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; }
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
// Start a new if/else tracking graph
|
||||
// See NODE STATE comment in ActiveLatchCheckVisitor
|
||||
AstNode::user1ClearTree();
|
||||
m_curVertexp = new LatchDetectGraphVertex(this, "ROOT");
|
||||
m_curVertexp = new LatchDetectGraphVertex{this, "ROOT"};
|
||||
}
|
||||
// Clear out userp field of referenced outputs on destruction
|
||||
// (occurs at the end of each combinational always block)
|
||||
@@ -165,7 +165,7 @@ public:
|
||||
} else {
|
||||
outVertexp = castVertexp(nodep->varp()->user1p());
|
||||
}
|
||||
new V3GraphEdge(this, m_curVertexp, outVertexp, 1);
|
||||
new V3GraphEdge{this, m_curVertexp, outVertexp, 1};
|
||||
}
|
||||
// Run latchCheckInternal on each variable assigned by the always block to see if all control
|
||||
// paths make an assignment. Detected latches are flagged in the variables AstVar
|
||||
@@ -183,7 +183,7 @@ public:
|
||||
<< " (not all control paths of combinational always assign a value)\n"
|
||||
<< nodep->warnMore()
|
||||
<< "... Suggest use of always_latch for intentional latches");
|
||||
if (debug() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
|
||||
if (dumpGraph() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
|
||||
}
|
||||
vertp->user(false); // Clear again (see above)
|
||||
vrp->varp()->isLatched(latch_detected);
|
||||
@@ -198,17 +198,14 @@ public:
|
||||
//######################################################################
|
||||
// Collect existing active names
|
||||
|
||||
class ActiveBaseVisitor VL_NOT_FINAL : public VNVisitor {
|
||||
protected:
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
class ActiveNamer final : public ActiveBaseVisitor {
|
||||
class ActiveNamer final : public VNVisitor {
|
||||
private:
|
||||
// STATE
|
||||
AstScope* m_scopep = nullptr; // Current scope to add statement to
|
||||
AstActive* m_iActivep = nullptr; // For current scope, the IActive we're building
|
||||
AstActive* m_cActivep = nullptr; // For current scope, the SActive(combo) we're building
|
||||
AstActive* m_sActivep = nullptr; // For current scope, the Static active we're building
|
||||
AstActive* m_iActivep = nullptr; // For current scope, the Initial active we're building
|
||||
AstActive* m_fActivep = nullptr; // For current scope, the Final active we're building
|
||||
AstActive* m_cActivep = nullptr; // For current scope, the Combo active we're building
|
||||
|
||||
// Map from AstSenTree (equivalence) to the corresponding AstActive created.
|
||||
std::unordered_map<VNRef<AstSenTree>, AstActive*> m_activeMap;
|
||||
@@ -216,49 +213,57 @@ private:
|
||||
// METHODS
|
||||
void addActive(AstActive* nodep) {
|
||||
UASSERT_OBJ(m_scopep, nodep, "nullptr scope");
|
||||
m_scopep->addActivep(nodep);
|
||||
m_scopep->addBlocksp(nodep);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
void visit(AstScope* nodep) override {
|
||||
m_scopep = nodep;
|
||||
m_sActivep = nullptr;
|
||||
m_iActivep = nullptr;
|
||||
m_fActivep = nullptr;
|
||||
m_cActivep = nullptr;
|
||||
m_activeMap.clear();
|
||||
iterateChildren(nodep);
|
||||
// Don't clear scopep, the namer persists beyond this visit
|
||||
}
|
||||
virtual void visit(AstSenTree* nodep) override {
|
||||
void visit(AstSenTree* nodep) override {
|
||||
// Simplify sensitivity list
|
||||
VL_DO_DANGLING(V3Const::constifyExpensiveEdit(nodep), nodep);
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNodeStmt*) override {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNodeStmt*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
// Specialized below for the special sensitivity classes
|
||||
template <typename SenItemKind>
|
||||
AstActive*& getSpecialActive();
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
AstScope* scopep() { return m_scopep; }
|
||||
AstActive* getCActive(FileLine* fl) {
|
||||
if (!m_cActivep) {
|
||||
m_cActivep = new AstActive(
|
||||
fl, "combo", new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Combo())));
|
||||
m_cActivep->sensesStorep(m_cActivep->sensesp());
|
||||
addActive(m_cActivep);
|
||||
}
|
||||
return m_cActivep;
|
||||
|
||||
// Make a new AstActive sensitive to the given special sensitivity class and return it
|
||||
template <typename SenItemKind>
|
||||
AstActive* makeSpecialActive(FileLine* const fl) {
|
||||
AstSenTree* const senTreep = new AstSenTree{fl, new AstSenItem{fl, SenItemKind{}}};
|
||||
auto* const activep = new AstActive{fl, "", senTreep};
|
||||
activep->sensesStorep(activep->sensesp());
|
||||
addActive(activep);
|
||||
return activep;
|
||||
}
|
||||
AstActive* getIActive(FileLine* fl) {
|
||||
if (!m_iActivep) {
|
||||
m_iActivep = new AstActive(
|
||||
fl, "initial", new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Initial())));
|
||||
m_iActivep->sensesStorep(m_iActivep->sensesp());
|
||||
addActive(m_iActivep);
|
||||
}
|
||||
return m_iActivep;
|
||||
|
||||
// Return an AstActive sensitive to the given special sensitivity class (possibly pre-created)
|
||||
template <typename SenItemKind>
|
||||
AstActive* getSpecialActive(FileLine* fl) {
|
||||
AstActive*& cachep = getSpecialActive<SenItemKind>();
|
||||
if (!cachep) cachep = makeSpecialActive<SenItemKind>(fl);
|
||||
return cachep;
|
||||
}
|
||||
|
||||
// Return an AstActive that is sensitive to a SenTree equivalent to the given sentreep.
|
||||
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
|
||||
UASSERT(sensesp, "Must be non-null");
|
||||
|
||||
auto it = m_activeMap.find(*sensesp);
|
||||
// If found matching AstActive, return it
|
||||
@@ -266,7 +271,7 @@ public:
|
||||
|
||||
// No such AstActive yet, creat it, and add to map.
|
||||
AstSenTree* const newsenp = sensesp->cloneTree(false);
|
||||
AstActive* const activep = new AstActive(fl, "sequent", newsenp);
|
||||
AstActive* const activep = new AstActive{fl, "sequent", newsenp};
|
||||
activep->sensesStorep(activep->sensesp());
|
||||
addActive(activep);
|
||||
m_activeMap.emplace(*newsenp, activep);
|
||||
@@ -275,14 +280,31 @@ public:
|
||||
|
||||
// CONSTRUCTORS
|
||||
ActiveNamer() = default;
|
||||
virtual ~ActiveNamer() override = default;
|
||||
~ActiveNamer() override = default;
|
||||
void main(AstScope* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
||||
template <>
|
||||
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Static>() {
|
||||
return m_sActivep;
|
||||
}
|
||||
template <>
|
||||
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Initial>() {
|
||||
return m_iActivep;
|
||||
}
|
||||
template <>
|
||||
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Final>() {
|
||||
return m_fActivep;
|
||||
}
|
||||
template <>
|
||||
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Combo>() {
|
||||
return m_cActivep;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Latch checking visitor
|
||||
|
||||
class ActiveLatchCheckVisitor final : public ActiveBaseVisitor {
|
||||
class ActiveLatchCheckVisitor final : public VNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Input:
|
||||
@@ -291,32 +313,32 @@ private:
|
||||
// STATE
|
||||
LatchDetectGraph m_graph; // Graph used to detect latches in combo always
|
||||
// VISITORS
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
void visit(AstVarRef* nodep) override {
|
||||
const AstVar* const varp = nodep->varp();
|
||||
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
|
||||
m_graph.addAssignment(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) override {
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
if (!nodep->isBoundsCheck()) {
|
||||
LatchDetectGraphVertex* const parentp = m_graph.currentp();
|
||||
LatchDetectGraphVertex* const branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
|
||||
m_graph.addPathVertex(branchp, "IF");
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
iterateAndNextNull(nodep->thensp());
|
||||
m_graph.addPathVertex(branchp, "ELSE");
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
m_graph.currentp(parentp);
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
ActiveLatchCheckVisitor(AstNode* nodep, VAlwaysKwd kwd) {
|
||||
ActiveLatchCheckVisitor(AstNode* nodep, bool expectLatch) {
|
||||
m_graph.begin();
|
||||
iterate(nodep);
|
||||
m_graph.latchCheck(nodep, kwd == VAlwaysKwd::ALWAYS_LATCH);
|
||||
m_graph.latchCheck(nodep, expectLatch);
|
||||
}
|
||||
~ActiveLatchCheckVisitor() override = default;
|
||||
};
|
||||
@@ -324,7 +346,7 @@ public:
|
||||
//######################################################################
|
||||
// Replace unsupported non-blocking assignments with blocking assignments
|
||||
|
||||
class ActiveDlyVisitor final : public ActiveBaseVisitor {
|
||||
class ActiveDlyVisitor final : public VNVisitor {
|
||||
public:
|
||||
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL };
|
||||
|
||||
@@ -333,7 +355,7 @@ private:
|
||||
const CheckType m_check; // Process type we are checking
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstAssignDly* nodep) override {
|
||||
void visit(AstAssignDly* nodep) override {
|
||||
// Non-blocking assignments are OK in sequential processes
|
||||
if (m_check == CT_SEQ) return;
|
||||
|
||||
@@ -351,17 +373,19 @@ private:
|
||||
}
|
||||
|
||||
// Convert to blocking assignment
|
||||
nodep->replaceWith(new AstAssign{nodep->fileline(), //
|
||||
nodep->lhsp()->unlinkFrBack(), //
|
||||
nodep->rhsp()->unlinkFrBack()});
|
||||
nodep->replaceWith(new AstAssign{
|
||||
nodep->fileline(), //
|
||||
nodep->lhsp()->unlinkFrBack(), //
|
||||
nodep->rhsp()->unlinkFrBack(), //
|
||||
nodep->timingControlp() ? nodep->timingControlp()->unlinkFrBack() : nullptr});
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstAssign* nodep) override {
|
||||
void visit(AstAssign* nodep) override {
|
||||
// Blocking assignments are always OK in combinational (and initial/final) processes
|
||||
if (m_check != CT_SEQ) return;
|
||||
|
||||
const bool ignore = nodep->lhsp()->forall<AstVarRef>([&](const AstVarRef* refp) {
|
||||
const bool ignore = nodep->lhsp()->forall([&](const AstVarRef* refp) {
|
||||
// Ignore reads (e.g.: index expressions)
|
||||
if (refp->access().isReadOnly()) return true;
|
||||
const AstVar* const varp = refp->varp();
|
||||
@@ -380,7 +404,7 @@ private:
|
||||
}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -394,91 +418,29 @@ public:
|
||||
//######################################################################
|
||||
// Active class functions
|
||||
|
||||
class ActiveVisitor final : public ActiveBaseVisitor {
|
||||
class ActiveVisitor final : public VNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Each call to V3Const::constify
|
||||
// AstVarScope::user1() bool: This VarScope is referenced in the sensitivity list
|
||||
// AstVarScope::user2() bool: This VarScope is written in the current process
|
||||
// AstNode::user4() Used by V3Const::constify, called below
|
||||
|
||||
// STATE
|
||||
ActiveNamer m_namer; // Tracking of active names
|
||||
AstCFunc* m_scopeFinalp = nullptr; // Final function for this scope
|
||||
bool m_itemCombo = false; // Found a SenItem combo
|
||||
bool m_itemSequent = false; // Found a SenItem sequential
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
// Create required actives and add to scope
|
||||
UINFO(4, " SCOPE " << nodep << endl);
|
||||
// Clear last scope's names, and collect this scope's existing names
|
||||
m_namer.main(nodep);
|
||||
m_scopeFinalp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstActive* nodep) override {
|
||||
// Actives are being formed, so we can ignore any already made
|
||||
}
|
||||
virtual void visit(AstInitialStatic* nodep) override {
|
||||
// Relink to IACTIVE, unless already under it
|
||||
UINFO(4, " INITIAL " << nodep << endl);
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
AstActive* const wantactivep = m_namer.getIActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) override {
|
||||
// Relink to IACTIVE, unless already under it
|
||||
UINFO(4, " INITIAL " << nodep << endl);
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
AstActive* const wantactivep = m_namer.getIActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) override {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4, " ASSIGNW " << nodep << endl);
|
||||
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) override {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4, " ASSIGNW " << nodep << endl);
|
||||
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) override {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4, " COVERTOGGLE " << nodep << endl);
|
||||
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstFinal* nodep) override {
|
||||
// Relink to CFUNC for the final
|
||||
UINFO(4, " FINAL " << nodep << endl);
|
||||
if (!nodep->bodysp()) { // Empty, Kill it.
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
if (!m_scopeFinalp) {
|
||||
m_scopeFinalp = new AstCFunc(
|
||||
nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep());
|
||||
m_scopeFinalp->dontCombine(true);
|
||||
m_scopeFinalp->isFinal(true);
|
||||
m_scopeFinalp->isStatic(false);
|
||||
m_scopeFinalp->isLoose(true);
|
||||
m_scopeFinalp->slow(true);
|
||||
m_namer.scopep()->addActivep(m_scopeFinalp);
|
||||
}
|
||||
nodep->unlinkFrBack();
|
||||
m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext());
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
bool m_clockedProcess = false; // Whether current process is a clocked process
|
||||
bool m_allChanged = false; // Whether all SenItem in the SenTree are ET_CHANGED
|
||||
bool m_walkingBody = false; // Walking body of a process
|
||||
bool m_canBeComb = false; // Whether current clocked process can be turned into a comb process
|
||||
|
||||
// METHODS
|
||||
template <typename T>
|
||||
void moveUnderSpecial(AstNode* nodep) {
|
||||
AstActive* const wantactivep = m_namer.getSpecialActive<T>(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
|
||||
void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
if (oldsensesp && oldsensesp->sensesp() && oldsensesp->sensesp()->isNever()) {
|
||||
@@ -489,112 +451,173 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
// Read sensitivities
|
||||
m_itemCombo = false;
|
||||
m_itemSequent = false;
|
||||
iterateAndNextNull(oldsensesp);
|
||||
bool combo = m_itemCombo;
|
||||
bool sequent = m_itemSequent;
|
||||
{
|
||||
const VNUser1InUse user1InUse;
|
||||
|
||||
if (!combo && !sequent) combo = true; // If no list, Verilog 2000: always @ (*)
|
||||
if (combo && sequent) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Mixed edge (pos/negedge) and activity "
|
||||
"(no edge) sensitive activity list");
|
||||
sequent = false;
|
||||
}
|
||||
|
||||
AstActive* wantactivep = nullptr;
|
||||
if (combo && !sequent) {
|
||||
// Combo: Relink to ACTIVE(combo)
|
||||
wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
} else {
|
||||
// Sequential: Build a ACTIVE(name)
|
||||
// OPTIMIZE: We could substitute a constant for things in the sense list, for example
|
||||
// always (posedge RESET) { if (RESET).... } we know RESET is true.
|
||||
// Summarize a long list of combo inputs as just "combo"
|
||||
#ifndef __COVERITY__ // Else dead code on next line.
|
||||
if (combo) {
|
||||
oldsensesp->addSensesp(new AstSenItem(nodep->fileline(), AstSenItem::Combo()));
|
||||
// Walk sensitivity list
|
||||
m_clockedProcess = false;
|
||||
m_allChanged = true;
|
||||
if (oldsensesp) {
|
||||
oldsensesp->unlinkFrBack();
|
||||
iterateChildrenConst(oldsensesp);
|
||||
}
|
||||
|
||||
// If all SenItems are ET_CHANGE, then walk the body to determine if this process
|
||||
// could be turned into a combinational process instead.
|
||||
if (m_allChanged) {
|
||||
const VNUser2InUse user2InUse;
|
||||
m_walkingBody = true;
|
||||
m_canBeComb = true;
|
||||
iterateChildrenConst(nodep);
|
||||
m_walkingBody = false;
|
||||
if (m_canBeComb) m_clockedProcess = false;
|
||||
}
|
||||
#endif
|
||||
wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp);
|
||||
}
|
||||
|
||||
AstActive* const wantactivep
|
||||
= !m_clockedProcess ? m_namer.getSpecialActive<AstSenItem::Combo>(nodep->fileline())
|
||||
: oldsensesp ? m_namer.getActive(nodep->fileline(), oldsensesp)
|
||||
: m_namer.getSpecialActive<AstSenItem::Initial>(nodep->fileline());
|
||||
|
||||
// Delete sensitivity list
|
||||
if (oldsensesp) {
|
||||
VL_DO_DANGLING(oldsensesp->unlinkFrBackWithNext()->deleteTree(), oldsensesp);
|
||||
}
|
||||
if (oldsensesp) VL_DO_DANGLING(oldsensesp->deleteTree(), oldsensesp);
|
||||
|
||||
// Move node to new active
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
|
||||
// Warn and/or convert any delayed assignments
|
||||
if (combo && !sequent) {
|
||||
{ ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB}; }
|
||||
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
|
||||
} else if (!combo && sequent) {
|
||||
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_SEQ};
|
||||
// Warn and convert any delayed assignments
|
||||
{
|
||||
ActiveDlyVisitor{nodep, m_clockedProcess ? ActiveDlyVisitor::CT_SEQ
|
||||
: ActiveDlyVisitor::CT_COMB};
|
||||
}
|
||||
|
||||
// check combinational processes for latches
|
||||
if (!m_clockedProcess || kwd == VAlwaysKwd::ALWAYS_LATCH) {
|
||||
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd == VAlwaysKwd::ALWAYS_LATCH};
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) override {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
UINFO(4, " ALW " << nodep << endl);
|
||||
// if (debug() >= 9) nodep->dumpTree(cout, " Alw: ");
|
||||
|
||||
if (!nodep->bodysp()) {
|
||||
// Empty always. Kill it.
|
||||
void visitSenItems(AstNode* nodep) {
|
||||
if (v3Global.opt.timing().isSetTrue()) {
|
||||
nodep->foreach([this](AstSenItem* senItemp) { visit(senItemp); });
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstScope* nodep) override {
|
||||
m_namer.main(nodep); // Clear last scope's names, and collect this scope's existing names
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstActive* nodep) override {
|
||||
// Actives are being formed, so we can ignore any already made
|
||||
}
|
||||
|
||||
void visit(AstInitialStatic* nodep) override { moveUnderSpecial<AstSenItem::Static>(nodep); }
|
||||
void visit(AstInitial* nodep) override {
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
visitSenItems(nodep);
|
||||
moveUnderSpecial<AstSenItem::Initial>(nodep);
|
||||
}
|
||||
void visit(AstFinal* nodep) override {
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
moveUnderSpecial<AstSenItem::Final>(nodep);
|
||||
}
|
||||
void visit(AstAssignAlias* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
|
||||
void visit(AstCoverToggle* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
|
||||
void visit(AstAssignW* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
|
||||
void visit(AstAlways* nodep) override {
|
||||
if (!nodep->stmtsp()) { // Empty always. Remove it now.
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
visitSenItems(nodep);
|
||||
visitAlways(nodep, nodep->sensesp(), nodep->keyword());
|
||||
}
|
||||
virtual void visit(AstAlwaysPostponed* nodep) override {
|
||||
UINFO(4, " ALW " << nodep << endl);
|
||||
if (!nodep->bodysp()) {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
visitAlways(nodep, nullptr, VAlwaysKwd::ALWAYS);
|
||||
void visit(AstAlwaysPostponed* nodep) override {
|
||||
// Might be empty with later optimizations, so this assertion can be removed,
|
||||
// but for now it is guaranteed to be not empty.
|
||||
UASSERT_OBJ(nodep->stmtsp(), nodep, "Should not be empty");
|
||||
// Make a new active for it, needs to be the only item under the active for V3Sched
|
||||
AstActive* const activep = m_namer.makeSpecialActive<AstSenItem::Combo>(nodep->fileline());
|
||||
activep->addStmtsp(nodep->unlinkFrBack());
|
||||
}
|
||||
virtual void visit(AstAlwaysPublic* nodep) override {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
UINFO(4, " ALWPub " << nodep << endl);
|
||||
// if (debug() >= 9) nodep->dumpTree(cout, " Alw: ");
|
||||
void visit(AstAlwaysPublic* nodep) override {
|
||||
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) override {
|
||||
if (nodep->varrefp()) {
|
||||
if (const AstBasicDType* const basicp = nodep->varrefp()->dtypep()->basicp()) {
|
||||
if (basicp->isEventValue()) {
|
||||
// Events need to be treated as active high so we only activate on event being
|
||||
// 1
|
||||
UINFO(8, "Demote event to HIGHEDGE " << nodep << endl);
|
||||
nodep->edgeType(VEdgeType::ET_HIGHEDGE);
|
||||
}
|
||||
void visit(AstCFunc* nodep) override { visitSenItems(nodep); }
|
||||
void visit(AstSenItem* nodep) override {
|
||||
UASSERT_OBJ(!m_walkingBody, nodep,
|
||||
"Should not reach here when walking body without --timing");
|
||||
if (!nodep->sensp()) return; // Ignore sequential items (e.g.: initial, comb, etc.)
|
||||
|
||||
m_clockedProcess = true;
|
||||
if (nodep->edgeType() != VEdgeType::ET_CHANGED) m_allChanged = false;
|
||||
|
||||
if (const auto* const dtypep = nodep->sensp()->dtypep()) {
|
||||
if (const auto* const basicp = dtypep->basicp()) {
|
||||
if (basicp->isEvent()) nodep->edgeType(VEdgeType::ET_EVENT);
|
||||
}
|
||||
}
|
||||
if (nodep->edgeType() == VEdgeType::ET_ANYEDGE) {
|
||||
m_itemCombo = true;
|
||||
// Delete the sensitivity
|
||||
// We'll add it as a generic COMBO SenItem in a moment.
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
} else if (nodep->varrefp()) {
|
||||
// V3LinkResolve should have cleaned most of these up
|
||||
if (!nodep->varrefp()->width1()) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Non-single bit wide signal pos/negedge sensitivity: "
|
||||
<< nodep->varrefp()->prettyNameQ());
|
||||
}
|
||||
m_itemSequent = true;
|
||||
nodep->varrefp()->varp()->usedClock(true);
|
||||
|
||||
nodep->sensp()->foreach([](const AstVarRef* refp) {
|
||||
refp->varp()->usedClock(true);
|
||||
refp->varScopep()->user1(true);
|
||||
});
|
||||
}
|
||||
|
||||
void visit(AstVarRef* nodep) override {
|
||||
AstVarScope* const vscp = nodep->varScopep();
|
||||
if (nodep->access().isWriteOnly()) {
|
||||
vscp->user2(true);
|
||||
} else {
|
||||
// If the variable is read before it is written (and is not a never-changing value),
|
||||
// and is not in the sensitivity list, then this cannot be optimized into a
|
||||
// combinational process
|
||||
// TODO: live variable analysis would be more precise
|
||||
if (!vscp->user2() && !vscp->varp()->valuep() && !vscp->user1()) m_canBeComb = false;
|
||||
}
|
||||
}
|
||||
void visit(AstAssignDly* nodep) override {
|
||||
m_canBeComb = false;
|
||||
if (nodep->isTimingControl()) m_clockedProcess = true;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstFireEvent* nodep) override {
|
||||
m_canBeComb = false;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstAssignForce* nodep) override {
|
||||
m_canBeComb = false;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstRelease* nodep) override {
|
||||
m_canBeComb = false;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstFork* nodep) override {
|
||||
if (nodep->isTimingControl()) {
|
||||
m_canBeComb = false;
|
||||
m_clockedProcess = true;
|
||||
}
|
||||
// Do not iterate children, technically not part of this process
|
||||
}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNodeMath*) override {} // Accelerate
|
||||
virtual void visit(AstVarScope*) override {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstVar*) override {} // Accelerate
|
||||
void visit(AstVarScope*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override {
|
||||
if (!v3Global.opt.timing().isSetTrue() && m_walkingBody && !m_canBeComb) {
|
||||
return; // Accelerate
|
||||
}
|
||||
if (!nodep->isPure()) m_canBeComb = false;
|
||||
if (nodep->isTimingControl()) {
|
||||
m_canBeComb = false;
|
||||
m_clockedProcess = true;
|
||||
return;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -608,5 +631,5 @@ public:
|
||||
void V3Active::activeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("active", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+46
-31
@@ -33,32 +33,41 @@
|
||||
#include "V3Global.h"
|
||||
#include "V3SenTree.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Active class functions
|
||||
|
||||
class ActiveTopVisitor final : public VNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist
|
||||
// AstNode::user() bool. True if processed
|
||||
// Each call to V3Const::constify
|
||||
// AstNode::user4() Used by V3Const::constify, called below
|
||||
const VNUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
static bool isInitial(AstNode* nodep) {
|
||||
const VNUser1InUse user1InUse;
|
||||
// Return true if no variables that read.
|
||||
return nodep->forall([&](const AstVarRef* refp) -> bool {
|
||||
AstVarScope* const vscp = refp->varScopep();
|
||||
// Note: Use same heuristic as ordering does to ignore written variables
|
||||
// TODO: Use live variable analysis.
|
||||
if (refp->access().isWriteOnly()) {
|
||||
vscp->user1(true);
|
||||
return true;
|
||||
}
|
||||
// Read or ReadWrite: OK if written before
|
||||
return vscp->user1();
|
||||
});
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
// Create required actives and add to module
|
||||
// We can start ordering at a module, or a scope
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstActive* nodep) override {
|
||||
void visit(AstActive* nodep) override {
|
||||
UINFO(4, " ACTIVE " << nodep << endl);
|
||||
// Remove duplicate clocks and such; sensesp() may change!
|
||||
V3Const::constifyExpensiveEdit(nodep);
|
||||
@@ -71,15 +80,6 @@ private:
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
// Copy combo tree to settlement tree with duplicated statements
|
||||
if (sensesp->hasCombo()) {
|
||||
AstSenTree* const newsentreep = new AstSenTree(
|
||||
nodep->fileline(), new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
|
||||
AstActive* const newp = new AstActive(nodep->fileline(), "settle", newsentreep);
|
||||
newp->sensesStorep(newsentreep);
|
||||
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
|
||||
nodep->addNextHere(newp);
|
||||
}
|
||||
// Move the SENTREE for each active up to the global level.
|
||||
// This way we'll easily see what clock domains are identical
|
||||
AstSenTree* const wantp = m_finder.getSenTree(sensesp);
|
||||
@@ -98,30 +98,45 @@ private:
|
||||
}
|
||||
nodep->sensesp(wantp);
|
||||
}
|
||||
// No need to do statements under it, they're already moved.
|
||||
// iterateChildren(nodep);
|
||||
|
||||
// If this is combinational logic that does not read any variables, then it really is an
|
||||
// initial block in disguise, so move such logic under an Initial AstActive, V3Order would
|
||||
// prune these otherwise.
|
||||
// TODO: we should warn for these if they were 'always @*' as some (including strictly
|
||||
// compliant) simulators will never execute these.
|
||||
if (nodep->sensesp()->hasCombo()) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstActive* initialp = nullptr;
|
||||
for (AstNode *logicp = nodep->stmtsp(), *nextp; logicp; logicp = nextp) {
|
||||
nextp = logicp->nextp();
|
||||
if (!isInitial(logicp)) continue;
|
||||
if (!initialp) initialp = new AstActive{flp, "", m_finder.getInitial()};
|
||||
initialp->addStmtsp(logicp->unlinkFrBack());
|
||||
}
|
||||
if (initialp) nodep->addHereThisAsNext(initialp);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeProcedure* nodep) override { // LCOV_EXCL_LINE
|
||||
void visit(AstNodeProcedure* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) override { // LCOV_EXCL_LINE
|
||||
void visit(AstAssignAlias* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) override { // LCOV_EXCL_LINE
|
||||
void visit(AstAssignW* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
virtual void visit(AstAlwaysPublic* nodep) override { // LCOV_EXCL_LINE
|
||||
void visit(AstAlwaysPublic* nodep) override { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNodeMath*) override {} // Accelerate
|
||||
virtual void visit(AstVarScope*) override {} // Accelerate
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNodeMath*) override {} // Accelerate
|
||||
void visit(AstVarScope*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit ActiveTopVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~ActiveTopVisitor() override = default;
|
||||
~ActiveTopVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -130,5 +145,5 @@ public:
|
||||
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveTopVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("activetop", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+110
-63
@@ -23,6 +23,8 @@
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Assert class functions
|
||||
|
||||
@@ -45,6 +47,7 @@ private:
|
||||
VDouble0 m_statAsNotImm; // Statistic tracking
|
||||
VDouble0 m_statAsImm; // Statistic tracking
|
||||
VDouble0 m_statAsFull; // Statistic tracking
|
||||
bool m_inSampled = false; // True inside a sampled expression
|
||||
|
||||
// METHODS
|
||||
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message) {
|
||||
@@ -56,7 +59,7 @@ private:
|
||||
nodep->displayType(VDisplayType::DT_WRITE);
|
||||
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
|
||||
// cppcheck-suppress nullPointer
|
||||
AstNode* const timenewp = new AstTime(nodep->fileline(), m_modp->timeunit());
|
||||
AstNode* const timenewp = new AstTime{nodep->fileline(), m_modp->timeunit()};
|
||||
if (AstNode* const timesp = nodep->fmtp()->exprsp()) {
|
||||
timesp->unlinkFrBackWithNext();
|
||||
timenewp->addNext(timesp);
|
||||
@@ -66,13 +69,18 @@ private:
|
||||
nodep->fmtp()->scopeNamep(new AstScopeName{nodep->fileline(), true});
|
||||
}
|
||||
}
|
||||
AstSampled* newSampledExpr(AstNode* nodep) {
|
||||
const auto sampledp = new AstSampled{nodep->fileline(), nodep};
|
||||
sampledp->dtypeFrom(nodep);
|
||||
return sampledp;
|
||||
}
|
||||
AstVarRef* newMonitorNumVarRefp(AstNode* nodep, VAccess access) {
|
||||
if (!m_monitorNumVarp) {
|
||||
m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
|
||||
nodep->findUInt64DType()};
|
||||
v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(m_monitorNumVarp);
|
||||
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp);
|
||||
}
|
||||
const auto varrefp = new AstVarRef(nodep->fileline(), m_monitorNumVarp, access);
|
||||
const auto varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access};
|
||||
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
||||
return varrefp;
|
||||
}
|
||||
@@ -80,9 +88,9 @@ private:
|
||||
if (!m_monitorOffVarp) {
|
||||
m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
|
||||
nodep->findBitDType()};
|
||||
v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(m_monitorOffVarp);
|
||||
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp);
|
||||
}
|
||||
const auto varrefp = new AstVarRef(nodep->fileline(), m_monitorOffVarp, access);
|
||||
const auto varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access};
|
||||
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
||||
return varrefp;
|
||||
}
|
||||
@@ -90,16 +98,17 @@ private:
|
||||
// Add a internal if to check assertions are on.
|
||||
// Don't make this a AND term, as it's unlikely to need to test this.
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNode* const newp = new AstIf(
|
||||
AstNodeIf* const newp = new AstIf{
|
||||
fl,
|
||||
(force ? new AstConst(fl, AstConst::BitTrue())
|
||||
(force ? new AstConst{fl, AstConst::BitTrue{}}
|
||||
: // If assertions are off, have constant propagation rip them out later
|
||||
// This allows syntax errors and such to be detected normally.
|
||||
(v3Global.opt.assertOn()
|
||||
? static_cast<AstNode*>(
|
||||
new AstCMath(fl, "vlSymsp->_vm_contextp__->assertOn()", 1))
|
||||
: static_cast<AstNode*>(new AstConst(fl, AstConst::BitFalse())))),
|
||||
nodep);
|
||||
new AstCMath{fl, "vlSymsp->_vm_contextp__->assertOn()", 1})
|
||||
: static_cast<AstNode*>(new AstConst{fl, AstConst::BitFalse{}}))),
|
||||
nodep};
|
||||
newp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
newp->user1(true); // Don't assert/cover this if
|
||||
return newp;
|
||||
}
|
||||
@@ -107,11 +116,11 @@ private:
|
||||
AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message) {
|
||||
// Like newFireAssert() but omits the asserts-on check
|
||||
AstDisplay* const dispp
|
||||
= new AstDisplay(nodep->fileline(), VDisplayType::DT_ERROR, message, nullptr, nullptr);
|
||||
= new AstDisplay{nodep->fileline(), VDisplayType::DT_ERROR, message, nullptr, nullptr};
|
||||
dispp->fmtp()->timeunit(m_modp->timeunit());
|
||||
AstNode* const bodysp = dispp;
|
||||
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
||||
bodysp->addNext(new AstStop(nodep->fileline(), true));
|
||||
bodysp->addNext(new AstStop{nodep->fileline(), true});
|
||||
return bodysp;
|
||||
}
|
||||
|
||||
@@ -147,7 +156,7 @@ private:
|
||||
selfDestruct = true;
|
||||
} else {
|
||||
// V3Coverage assigned us a bucket to increment.
|
||||
AstCoverInc* const covincp = VN_AS(snodep->coverincp(), CoverInc);
|
||||
AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc);
|
||||
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
|
||||
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
||||
if (message != "") covincp->declp()->comment(message);
|
||||
@@ -155,7 +164,8 @@ private:
|
||||
}
|
||||
|
||||
if (bodysp && passsp) bodysp = bodysp->addNext(passsp);
|
||||
ifp = new AstIf(nodep->fileline(), propp, bodysp);
|
||||
ifp = new AstIf{nodep->fileline(), propp, bodysp};
|
||||
ifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
bodysp = ifp;
|
||||
} else if (VN_IS(nodep, Assert) || VN_IS(nodep, AssertIntrinsic)) {
|
||||
if (nodep->immediate()) {
|
||||
@@ -167,7 +177,8 @@ private:
|
||||
if (passsp) passsp = newIfAssertOn(passsp, force);
|
||||
if (failsp) failsp = newIfAssertOn(failsp, force);
|
||||
if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
|
||||
ifp = new AstIf(nodep->fileline(), propp, passsp, failsp);
|
||||
ifp = new AstIf{nodep->fileline(), propp, passsp, failsp};
|
||||
ifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
// It's more LIKELY that we'll take the nullptr if clause
|
||||
// than the sim-killing else clause:
|
||||
ifp->branchPred(VBranchPred::BP_LIKELY);
|
||||
@@ -178,7 +189,7 @@ private:
|
||||
|
||||
AstNode* newp;
|
||||
if (sentreep) {
|
||||
newp = new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, bodysp);
|
||||
newp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, bodysp};
|
||||
} else {
|
||||
newp = bodysp;
|
||||
}
|
||||
@@ -196,7 +207,7 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstIf* nodep) override {
|
||||
void visit(AstIf* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||
const AstNodeIf* ifp = nodep;
|
||||
@@ -209,7 +220,7 @@ private:
|
||||
iterateAndNextNull(ifp->condp());
|
||||
|
||||
// Recurse into the true case.
|
||||
iterateAndNextNull(ifp->ifsp());
|
||||
iterateAndNextNull(ifp->thensp());
|
||||
|
||||
// If the last else is not an else if, recurse into that too.
|
||||
if (ifp->elsesp() && !nextifp) { //
|
||||
@@ -219,7 +230,7 @@ private:
|
||||
// Build a bitmask of the true predicates
|
||||
AstNode* const predp = ifp->condp()->cloneTree(false);
|
||||
if (propp) {
|
||||
propp = new AstConcat(nodep->fileline(), predp, propp);
|
||||
propp = new AstConcat{nodep->fileline(), predp, propp};
|
||||
} else {
|
||||
propp = predp;
|
||||
}
|
||||
@@ -234,17 +245,18 @@ private:
|
||||
const bool allow_none = nodep->unique0Pragma();
|
||||
|
||||
// Empty case means no property
|
||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::BitFalse());
|
||||
if (!propp) propp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
|
||||
|
||||
// Note: if this ends with an 'else', then we don't need to validate that one of the
|
||||
// predicates evaluates to true.
|
||||
AstNode* const ohot
|
||||
= ((allow_none || hasDefaultElse)
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||
? static_cast<AstNode*>(new AstOneHot0{nodep->fileline(), propp})
|
||||
: static_cast<AstNode*>(new AstOneHot{nodep->fileline(), propp}));
|
||||
AstIf* const checkifp
|
||||
= new AstIf(nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "'unique if' statement violated"), newifp);
|
||||
= new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
|
||||
newFireAssert(nodep, "'unique if' statement violated"), newifp};
|
||||
checkifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
nodep->replaceWith(checkifp);
|
||||
pushDeletep(nodep);
|
||||
@@ -254,7 +266,7 @@ private:
|
||||
}
|
||||
|
||||
//========== Case assertions
|
||||
virtual void visit(AstCase* nodep) override {
|
||||
void visit(AstCase* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
bool has_default = false;
|
||||
@@ -266,9 +278,9 @@ private:
|
||||
// Need to add a default if there isn't one already
|
||||
++m_statAsFull;
|
||||
if (!has_default) {
|
||||
nodep->addItemsp(new AstCaseItem(
|
||||
nodep->addItemsp(new AstCaseItem{
|
||||
nodep->fileline(), nullptr /*DEFAULT*/,
|
||||
newFireAssert(nodep, "synthesis full_case, but non-match found")));
|
||||
newFireAssert(nodep, "synthesis full_case, but non-match found")});
|
||||
}
|
||||
}
|
||||
if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||
@@ -297,24 +309,25 @@ private:
|
||||
icondp->cloneTree(false));
|
||||
}
|
||||
if (propp) {
|
||||
propp = new AstConcat(icondp->fileline(), onep, propp);
|
||||
propp = new AstConcat{icondp->fileline(), onep, propp};
|
||||
} else {
|
||||
propp = onep;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Empty case means no property
|
||||
if (!propp) propp = new AstConst(nodep->fileline(), AstConst::BitFalse());
|
||||
if (!propp) propp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
|
||||
|
||||
const bool allow_none = has_default || nodep->unique0Pragma();
|
||||
AstNode* const ohot
|
||||
= (allow_none
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||
AstIf* const ifp = new AstIf(
|
||||
nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
|
||||
? static_cast<AstNode*>(new AstOneHot0{nodep->fileline(), propp})
|
||||
: static_cast<AstNode*>(new AstOneHot{nodep->fileline(), propp}));
|
||||
AstIf* const ifp = new AstIf{
|
||||
nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
|
||||
newFireAssert(nodep,
|
||||
"synthesis parallel_case, but multiple matches found"));
|
||||
"synthesis parallel_case, but multiple matches found")};
|
||||
ifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
nodep->addNotParallelp(ifp);
|
||||
}
|
||||
@@ -323,7 +336,7 @@ private:
|
||||
}
|
||||
|
||||
//========== Past
|
||||
virtual void visit(AstPast* nodep) override {
|
||||
void visit(AstPast* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
uint32_t ticks = 1;
|
||||
if (nodep->ticksp()) {
|
||||
@@ -332,34 +345,66 @@ private:
|
||||
ticks = VN_AS(nodep->ticksp(), Const)->toUInt();
|
||||
}
|
||||
UASSERT_OBJ(ticks >= 1, nodep, "0 tick should have been checked in V3Width");
|
||||
AstNode* inp = nodep->exprp()->unlinkFrBack();
|
||||
AstNode* const exprp = nodep->exprp()->unlinkFrBack();
|
||||
AstNode* inp = newSampledExpr(exprp);
|
||||
AstVar* invarp = nullptr;
|
||||
AstSenTree* const sentreep = nodep->sentreep();
|
||||
sentreep->unlinkFrBack();
|
||||
AstAlways* const alwaysp
|
||||
= new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr);
|
||||
m_modp->addStmtp(alwaysp);
|
||||
= new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr};
|
||||
m_modp->addStmtsp(alwaysp);
|
||||
for (uint32_t i = 0; i < ticks; ++i) {
|
||||
AstVar* const outvarp = new AstVar(
|
||||
AstVar* const outvarp = new AstVar{
|
||||
nodep->fileline(), VVarType::MODULETEMP,
|
||||
"_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i), inp->dtypep());
|
||||
m_modp->addStmtp(outvarp);
|
||||
AstNode* const assp = new AstAssignDly(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), outvarp, VAccess::WRITE), inp);
|
||||
alwaysp->addStmtp(assp);
|
||||
"_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i), inp->dtypep()};
|
||||
m_modp->addStmtsp(outvarp);
|
||||
AstNode* const assp = new AstAssignDly{
|
||||
nodep->fileline(), new AstVarRef{nodep->fileline(), outvarp, VAccess::WRITE}, inp};
|
||||
alwaysp->addStmtsp(assp);
|
||||
// if (debug() >= 9) assp->dumpTree(cout, "-ass: ");
|
||||
invarp = outvarp;
|
||||
inp = new AstVarRef(nodep->fileline(), invarp, VAccess::READ);
|
||||
inp = new AstVarRef{nodep->fileline(), invarp, VAccess::READ};
|
||||
}
|
||||
nodep->replaceWith(inp);
|
||||
}
|
||||
virtual void visit(AstSampled* nodep) override {
|
||||
|
||||
//========== Move $sampled down to read-only variables
|
||||
void visit(AstSampled* nodep) override {
|
||||
if (nodep->user1()) return;
|
||||
VL_RESTORER(m_inSampled);
|
||||
{
|
||||
m_inSampled = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_inSampled) {
|
||||
if (!nodep->access().isReadOnly()) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Write to variable in sampled expression");
|
||||
} else {
|
||||
VNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
AstSampled* const newp = newSampledExpr(nodep);
|
||||
relinkHandle.relink(newp);
|
||||
newp->user1(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't sample sensitivities
|
||||
void visit(AstSenItem* nodep) override {
|
||||
VL_RESTORER(m_inSampled);
|
||||
{
|
||||
m_inSampled = false;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
//========== Statements
|
||||
virtual void visit(AstDisplay* nodep) override {
|
||||
void visit(AstDisplay* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Replace the special types with standard text
|
||||
if (nodep->displayType() == VDisplayType::DT_INFO) {
|
||||
@@ -385,9 +430,10 @@ private:
|
||||
new AstEq{fl, new AstConst{fl, monNum},
|
||||
newMonitorNumVarRefp(nodep, VAccess::READ)}},
|
||||
stmtsp};
|
||||
ifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
|
||||
m_modp->addStmtp(newp);
|
||||
AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, nullptr, ifp};
|
||||
m_modp->addStmtsp(newp);
|
||||
} else if (nodep->displayType() == VDisplayType::DT_STROBE) {
|
||||
nodep->displayType(VDisplayType::DT_DISPLAY);
|
||||
// Need one-shot
|
||||
@@ -395,7 +441,7 @@ private:
|
||||
const auto varp
|
||||
= new AstVar{fl, VVarType::MODULETEMP, "__Vstrobe" + cvtToStr(m_modStrobeNum++),
|
||||
nodep->findBitDType()};
|
||||
m_modp->addStmtp(varp);
|
||||
m_modp->addStmtsp(varp);
|
||||
// Where $strobe was we do "__Vstrobe = '1;"
|
||||
const auto newsetp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitTrue{}}};
|
||||
@@ -403,39 +449,40 @@ private:
|
||||
// Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end"
|
||||
AstNode* const stmtsp = nodep;
|
||||
AstIf* const ifp = new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp};
|
||||
ifp->isBoundsCheck(true); // To avoid LATCH warning
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
|
||||
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::BitFalse{}}});
|
||||
m_modp->addStmtp(newp);
|
||||
m_modp->addStmtsp(newp);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstMonitorOff* nodep) override {
|
||||
void visit(AstMonitorOff* nodep) override {
|
||||
const auto newp
|
||||
= new AstAssign(nodep->fileline(), newMonitorOffVarRefp(nodep, VAccess::WRITE),
|
||||
new AstConst(nodep->fileline(), AstConst::BitTrue{}, nodep->off()));
|
||||
= new AstAssign{nodep->fileline(), newMonitorOffVarRefp(nodep, VAccess::WRITE),
|
||||
new AstConst{nodep->fileline(), AstConst::BitTrue{}, nodep->off()}};
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
virtual void visit(AstAssert* nodep) override {
|
||||
void visit(AstAssert* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
newPslAssertion(nodep, nodep->failsp());
|
||||
}
|
||||
virtual void visit(AstAssertIntrinsic* nodep) override {
|
||||
void visit(AstAssertIntrinsic* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
newPslAssertion(nodep, nodep->failsp());
|
||||
}
|
||||
virtual void visit(AstCover* nodep) override {
|
||||
void visit(AstCover* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
newPslAssertion(nodep, nullptr);
|
||||
}
|
||||
virtual void visit(AstRestrict* nodep) override {
|
||||
void visit(AstRestrict* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// IEEE says simulator ignores these
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_modPastNum);
|
||||
VL_RESTORER(m_modStrobeNum);
|
||||
@@ -446,7 +493,7 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) override {
|
||||
void visit(AstBegin* nodep) override {
|
||||
// This code is needed rather than a visitor in V3Begin,
|
||||
// because V3Assert is called before V3Begin
|
||||
VL_RESTORER(m_beginp);
|
||||
@@ -456,12 +503,12 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit AssertVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~AssertVisitor() override {
|
||||
~AssertVisitor() override {
|
||||
V3Stats::addStat("Assertions, assert non-immediate statements", m_statAsNotImm);
|
||||
V3Stats::addStat("Assertions, assert immediate statements", m_statAsImm);
|
||||
V3Stats::addStat("Assertions, cover statements", m_statCover);
|
||||
@@ -475,5 +522,5 @@ public:
|
||||
void V3Assert::assertAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("assert", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+16
-15
@@ -25,6 +25,8 @@
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Assert class functions
|
||||
|
||||
@@ -45,7 +47,6 @@ private:
|
||||
AstNode* m_disablep = nullptr; // Last disable
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
AstSenTree* newSenTree(AstNode* nodep) {
|
||||
// Create sentree based on clocked or default clock
|
||||
@@ -69,7 +70,7 @@ private:
|
||||
|
||||
// VISITORS
|
||||
//========== Statements
|
||||
virtual void visit(AstClocking* nodep) override {
|
||||
void visit(AstClocking* nodep) override {
|
||||
UINFO(8, " CLOCKING" << nodep << endl);
|
||||
// Store the new default clock, reset on new module
|
||||
m_seniDefaultp = nodep->sensesp();
|
||||
@@ -81,14 +82,14 @@ private:
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) override {
|
||||
void visit(AstAlways* nodep) override {
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
if (nodep->sensesp()) m_seniAlwaysp = nodep->sensesp()->sensesp();
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
m_seniAlwaysp = nullptr;
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeCoverOrAssert* nodep) override {
|
||||
void visit(AstNodeCoverOrAssert* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
clearAssertInfo();
|
||||
// Find Clocking's buried under nodep->exprsp
|
||||
@@ -96,7 +97,7 @@ private:
|
||||
if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep));
|
||||
clearAssertInfo();
|
||||
}
|
||||
virtual void visit(AstFell* nodep) override {
|
||||
void visit(AstFell* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
@@ -110,12 +111,12 @@ private:
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
virtual void visit(AstPast* nodep) override {
|
||||
void visit(AstPast* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
}
|
||||
virtual void visit(AstRose* nodep) override {
|
||||
void visit(AstRose* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
@@ -129,7 +130,7 @@ private:
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
virtual void visit(AstStable* nodep) override {
|
||||
void visit(AstStable* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
iterateChildren(nodep);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
@@ -143,7 +144,7 @@ private:
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstImplication* nodep) override {
|
||||
void visit(AstImplication* nodep) override {
|
||||
if (nodep->sentreep()) return; // Already processed
|
||||
|
||||
FileLine* const fl = nodep->fileline();
|
||||
@@ -161,7 +162,7 @@ private:
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstPropClocked* nodep) override {
|
||||
void visit(AstPropClocked* nodep) override {
|
||||
// No need to iterate the body, once replace will get iterated
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
if (m_senip)
|
||||
@@ -183,12 +184,12 @@ private:
|
||||
nodep->replaceWith(blockp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Reset defaults
|
||||
m_seniDefaultp = nullptr;
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -197,7 +198,7 @@ public:
|
||||
// Process
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~AssertPreVisitor() override = default;
|
||||
~AssertPreVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -206,5 +207,5 @@ public:
|
||||
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertPreVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+69
-55
@@ -28,6 +28,8 @@
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//======================================================================
|
||||
// Statics
|
||||
|
||||
@@ -165,22 +167,22 @@ string AstNode::prettyName(const string& namein) {
|
||||
continue;
|
||||
}
|
||||
if (pos[0] == '_' && pos[1] == '_') { // Short-circuit
|
||||
if (0 == strncmp(pos, "__BRA__", 7)) {
|
||||
if (0 == std::strncmp(pos, "__BRA__", 7)) {
|
||||
pretty += "[";
|
||||
pos += 7;
|
||||
continue;
|
||||
}
|
||||
if (0 == strncmp(pos, "__KET__", 7)) {
|
||||
if (0 == std::strncmp(pos, "__KET__", 7)) {
|
||||
pretty += "]";
|
||||
pos += 7;
|
||||
continue;
|
||||
}
|
||||
if (0 == strncmp(pos, "__DOT__", 7)) {
|
||||
if (0 == std::strncmp(pos, "__DOT__", 7)) {
|
||||
pretty += ".";
|
||||
pos += 7;
|
||||
continue;
|
||||
}
|
||||
if (0 == strncmp(pos, "__PVT__", 7)) {
|
||||
if (0 == std::strncmp(pos, "__PVT__", 7)) {
|
||||
pretty += "";
|
||||
pos += 7;
|
||||
continue;
|
||||
@@ -212,8 +214,7 @@ string AstNode::prettyTypeName() const {
|
||||
//######################################################################
|
||||
// Insertion
|
||||
|
||||
inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int lineno,
|
||||
bool next){
|
||||
void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int lineno, bool next) {
|
||||
#ifdef VL_DEBUG
|
||||
// Called on all major tree changers.
|
||||
// Only for use for those really nasty bugs relating to internals
|
||||
@@ -234,7 +235,8 @@ inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, i
|
||||
#endif
|
||||
}
|
||||
|
||||
AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
|
||||
template <>
|
||||
AstNode* AstNode::addNext<AstNode, AstNode>(AstNode* nodep, AstNode* newp) {
|
||||
// Add to m_nextp, returns this
|
||||
UDEBUGONLY(UASSERT_OBJ(newp, nodep, "Null item passed to addNext"););
|
||||
debugTreeChange(nodep, "-addNextThs: ", __LINE__, false);
|
||||
@@ -272,11 +274,6 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
|
||||
return nodep;
|
||||
}
|
||||
|
||||
AstNode* AstNode::addNextNull(AstNode* nodep, AstNode* newp) {
|
||||
if (!newp) return nodep;
|
||||
return addNext(nodep, newp);
|
||||
}
|
||||
|
||||
void AstNode::addNextHere(AstNode* newp) {
|
||||
// Add to m_nextp on exact node passed, not at the end.
|
||||
// This could be at head, tail, or both (single)
|
||||
@@ -925,7 +922,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(VNVisitor& v) {
|
||||
} else if (!nodep->backp()) {
|
||||
// Calling on standalone tree; insert a shim node so we can keep
|
||||
// track, then delete it on completion
|
||||
AstBegin* const tempp = new AstBegin(nodep->fileline(), "[EditWrapper]", nodep);
|
||||
AstBegin* const tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", nodep};
|
||||
{
|
||||
VL_DO_DANGLING(tempp->stmtsp()->accept(v),
|
||||
nodep); // nodep to null as may be replaced
|
||||
@@ -1004,43 +1001,12 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
|
||||
//======================================================================
|
||||
// Debugging
|
||||
|
||||
void AstNode::checkTreeIter(AstNode* backp) {
|
||||
void AstNode::checkTreeIter(const AstNode* backp) const {
|
||||
// private: Check a tree and children
|
||||
UASSERT_OBJ(backp == this->backp(), this, "Back node inconsistent");
|
||||
if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) {
|
||||
// Termops have a short-circuited iterateChildren, so check usage
|
||||
UASSERT_OBJ(!(op1p() || op2p() || op3p() || op4p()), this,
|
||||
"Terminal operation with non-terminals");
|
||||
}
|
||||
if (m_op1p) m_op1p->checkTreeIterList(this);
|
||||
if (m_op2p) m_op2p->checkTreeIterList(this);
|
||||
if (m_op3p) m_op3p->checkTreeIterList(this);
|
||||
if (m_op4p) m_op4p->checkTreeIterList(this);
|
||||
}
|
||||
|
||||
void AstNode::checkTreeIterList(AstNode* backp) {
|
||||
// private: Check a (possible) list of nodes, this is always the head of the list
|
||||
// Audited to make sure this is never nullptr
|
||||
AstNode* const headp = this;
|
||||
const AstNode* tailp = this;
|
||||
for (AstNode* nodep = headp; nodep; nodep = nodep->nextp()) {
|
||||
nodep->checkTreeIter(backp);
|
||||
UASSERT_OBJ(headp == this || !nextp(), this,
|
||||
"Headtailp should be null in middle of lists");
|
||||
tailp = nodep;
|
||||
backp = nodep;
|
||||
}
|
||||
UASSERT_OBJ(headp->m_headtailp == tailp, headp, "Tail in headtailp is inconsistent");
|
||||
UASSERT_OBJ(tailp->m_headtailp == headp, tailp, "Head in headtailp is inconsistent");
|
||||
}
|
||||
|
||||
void AstNode::checkTree() {
|
||||
if (!debug()) return;
|
||||
if (this->backp()) {
|
||||
// Linked tree- check only the passed node
|
||||
this->checkTreeIter(this->backp());
|
||||
} else {
|
||||
this->checkTreeIterList(this->backp());
|
||||
switch (this->type()) {
|
||||
#include "V3Ast__gen_op_checks.h"
|
||||
default: VL_UNREACHABLE; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1109,9 +1075,10 @@ void AstNode::dumpPtrs(std::ostream& os) const {
|
||||
if (user5p()) os << " user5p=" << cvtToHex(user5p());
|
||||
if (m_iterpp) {
|
||||
os << " iterpp=" << cvtToHex(m_iterpp);
|
||||
os << "*=" << cvtToHex(*m_iterpp);
|
||||
// This may cause address sanitizer failures as iterpp can be stale
|
||||
// os << "*=" << cvtToHex(*m_iterpp);
|
||||
}
|
||||
os << endl;
|
||||
os << std::endl;
|
||||
}
|
||||
|
||||
void AstNode::dumpTree(std::ostream& os, const string& indent, int maxDepth) const {
|
||||
@@ -1156,7 +1123,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
|
||||
if (logsp->fail()) v3fatal("Can't write " << filename);
|
||||
*logsp << "Verilator Tree Dump (format 0x3900) from <e" << std::dec << editCountLast();
|
||||
*logsp << "> to <e" << std::dec << editCountGbl() << ">\n";
|
||||
if (editCountGbl() == editCountLast() && !(v3Global.opt.dumpTree() >= 9)) {
|
||||
if (editCountGbl() == editCountLast() && ::dumpTree() < 9) {
|
||||
*logsp << '\n';
|
||||
*logsp << "No changes since last dump!\n";
|
||||
} else {
|
||||
@@ -1166,7 +1133,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
|
||||
}
|
||||
}
|
||||
if (doDump && v3Global.opt.debugEmitV()) V3EmitV::debugEmitV(filename + ".v");
|
||||
if (doCheck && (v3Global.opt.debugCheck() || v3Global.opt.dumpTree())) {
|
||||
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTree())) {
|
||||
// Error check
|
||||
checkTree();
|
||||
// Broken isn't part of check tree because it can munge iterp's
|
||||
@@ -1175,10 +1142,53 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const {
|
||||
static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* childp,
|
||||
const std::string& childName) {
|
||||
if (childp) {
|
||||
os << "\tn" << cvtToHex(thisp) << " -> n" << cvtToHex(childp) << " ["
|
||||
<< "label=\"" << childName << "\" color=red];\n";
|
||||
for (const AstNode* nodep = childp; nodep; nodep = nodep->nextp()) {
|
||||
nodep->dumpTreeDot(os);
|
||||
if (nodep->nextp()) {
|
||||
os << "\tn" << cvtToHex(nodep) << " -> n" << cvtToHex(nodep->nextp()) << " ["
|
||||
<< "label=\"next\" color=red];\n";
|
||||
os << "\t{rank=same; n" << cvtToHex(nodep) << ", n" << cvtToHex(nodep->nextp())
|
||||
<< "}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::dumpTreeDot(std::ostream& os) const {
|
||||
os << "\tn" << cvtToHex(this) << "\t["
|
||||
<< "label=\"" << typeName() << "\\n"
|
||||
<< name() << "\"];\n";
|
||||
drawChildren(os, this, m_op1p, "op1");
|
||||
drawChildren(os, this, m_op2p, "op2");
|
||||
drawChildren(os, this, m_op3p, "op3");
|
||||
drawChildren(os, this, m_op4p, "op4");
|
||||
}
|
||||
|
||||
void AstNode::dumpTreeDotFile(const string& filename, bool append, bool doDump) {
|
||||
if (doDump) {
|
||||
UINFO(2, "Dumping " << filename << endl);
|
||||
const std::unique_ptr<std::ofstream> treedotp{V3File::new_ofstream(filename, append)};
|
||||
if (treedotp->fail()) v3fatal("Can't write " << filename);
|
||||
*treedotp << "digraph vTree{\n";
|
||||
*treedotp << "\tgraph\t[label=\"" << filename + ".dot"
|
||||
<< "\",\n";
|
||||
*treedotp << "\t\t labelloc=t, labeljust=l,\n";
|
||||
*treedotp << "\t\t //size=\"7.5,10\",\n"
|
||||
<< "];\n";
|
||||
dumpTreeDot(*treedotp);
|
||||
*treedotp << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_MT_SAFE {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
string AstNode::instanceStr() const {
|
||||
@@ -1217,7 +1227,11 @@ void AstNode::v3errorEnd(std::ostringstream& str) const {
|
||||
const_cast<AstNode*>(this)->dump(nsstr);
|
||||
nsstr << endl;
|
||||
}
|
||||
m_fileline->v3errorEnd(nsstr, instanceStr());
|
||||
// Don't look for instance name when warning is disabled.
|
||||
// In case of large number of warnings, this can
|
||||
// take significant amount of time
|
||||
m_fileline->v3errorEnd(nsstr,
|
||||
m_fileline->warnIsOff(V3Error::errorCode()) ? "" : instanceStr());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+708
-1620
File diff suppressed because it is too large
Load Diff
+133
-37
@@ -17,71 +17,167 @@
|
||||
#ifndef VERILATOR_V3ASTINLINES_H_
|
||||
#define VERILATOR_V3ASTINLINES_H_
|
||||
|
||||
#ifndef VERILATOR_V3ASTNODES_H_
|
||||
#ifndef VERILATOR_V3AST_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
|
||||
#include "V3Ast.h" // This helps code analysis tools pick up symbols in V3Ast.h and relaed
|
||||
#endif
|
||||
|
||||
//######################################################################
|
||||
// Inline ACCESSORS
|
||||
// Inline METHODS
|
||||
|
||||
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
|
||||
int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
|
||||
int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
|
||||
bool AstNode::width1() const { // V3Const uses to know it can optimize
|
||||
return dtypep() && dtypep()->width() == 1;
|
||||
}
|
||||
inline int AstNode::widthInstrs() const {
|
||||
int AstNode::widthInstrs() const {
|
||||
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
|
||||
}
|
||||
inline bool AstNode::isDouble() const {
|
||||
bool AstNode::isDouble() const VL_MT_SAFE {
|
||||
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_AS(dtypep(), BasicDType)->isDouble();
|
||||
}
|
||||
inline bool AstNode::isString() const {
|
||||
bool AstNode::isString() const VL_MT_SAFE {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
|
||||
}
|
||||
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
|
||||
bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
inline bool AstNode::isZero() const {
|
||||
bool AstNode::isZero() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
|
||||
}
|
||||
inline bool AstNode::isNeqZero() const {
|
||||
bool AstNode::isNeqZero() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNeqZero());
|
||||
}
|
||||
inline bool AstNode::isOne() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqOne());
|
||||
}
|
||||
inline bool AstNode::isAllOnes() const {
|
||||
bool AstNode::isOne() const { return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqOne()); }
|
||||
bool AstNode::isAllOnes() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnes());
|
||||
}
|
||||
inline bool AstNode::isAllOnesV() const {
|
||||
bool AstNode::isAllOnesV() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnesV());
|
||||
}
|
||||
inline bool AstNode::sameTree(const AstNode* node2p) const {
|
||||
bool AstNode::sameTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, false);
|
||||
}
|
||||
inline bool AstNode::sameGateTree(const AstNode* node2p) const {
|
||||
bool AstNode::sameGateTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, true);
|
||||
}
|
||||
|
||||
inline void AstNodeVarRef::varp(AstVar* varp) {
|
||||
m_varp = varp;
|
||||
int AstNodeArrayDType::left() const VL_MT_SAFE { return rangep()->leftConst(); }
|
||||
int AstNodeArrayDType::right() const VL_MT_SAFE { return rangep()->rightConst(); }
|
||||
int AstNodeArrayDType::hi() const VL_MT_SAFE { return rangep()->hiConst(); }
|
||||
int AstNodeArrayDType::lo() const VL_MT_SAFE { return rangep()->loConst(); }
|
||||
int AstNodeArrayDType::elementsConst() const VL_MT_SAFE { return rangep()->elementsConst(); }
|
||||
VNumRange AstNodeArrayDType::declRange() const VL_MT_SAFE { return VNumRange{left(), right()}; }
|
||||
|
||||
AstRange::AstRange(FileLine* fl, int left, int right)
|
||||
: ASTGEN_SUPER_Range(fl) {
|
||||
leftp(new AstConst{fl, static_cast<uint32_t>(left)});
|
||||
rightp(new AstConst{fl, static_cast<uint32_t>(right)});
|
||||
}
|
||||
AstRange::AstRange(FileLine* fl, const VNumRange& range)
|
||||
: ASTGEN_SUPER_Range(fl) {
|
||||
leftp(new AstConst{fl, static_cast<uint32_t>(range.left())});
|
||||
rightp(new AstConst{fl, static_cast<uint32_t>(range.right())});
|
||||
}
|
||||
int AstRange::leftConst() const {
|
||||
AstConst* const constp = VN_CAST(leftp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
int AstRange::rightConst() const {
|
||||
AstConst* const constp = VN_CAST(rightp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
int AstQueueDType::boundConst() const VL_MT_SAFE {
|
||||
AstConst* const constp = VN_CAST(boundp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
AstPin::AstPin(FileLine* fl, int pinNum, AstVarRef* varname, AstNode* exprp)
|
||||
: ASTGEN_SUPER_Pin(fl)
|
||||
, m_pinNum{pinNum}
|
||||
, m_name{varname->name()} {
|
||||
this->exprp(exprp);
|
||||
}
|
||||
|
||||
AstPackArrayDType::AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp,
|
||||
AstRange* rangep)
|
||||
: ASTGEN_SUPER_PackArrayDType(fl) {
|
||||
childDTypep(dtp); // Only for parser
|
||||
refDTypep(nullptr);
|
||||
this->rangep(rangep);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
const int width = subDTypep()->width() * rangep->elementsConst();
|
||||
widthForce(width, width);
|
||||
}
|
||||
AstPackArrayDType::AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep)
|
||||
: ASTGEN_SUPER_PackArrayDType(fl) {
|
||||
refDTypep(dtp);
|
||||
this->rangep(rangep);
|
||||
dtypep(this);
|
||||
const int width = subDTypep()->width() * rangep->elementsConst();
|
||||
widthForce(width, width);
|
||||
}
|
||||
|
||||
int AstBasicDType::hi() const { return (rangep() ? rangep()->hiConst() : m.m_nrange.hi()); }
|
||||
int AstBasicDType::lo() const { return (rangep() ? rangep()->loConst() : m.m_nrange.lo()); }
|
||||
int AstBasicDType::elements() const {
|
||||
return (rangep() ? rangep()->elementsConst() : m.m_nrange.elements());
|
||||
}
|
||||
bool AstBasicDType::littleEndian() const {
|
||||
return (rangep() ? rangep()->littleEndian() : m.m_nrange.littleEndian());
|
||||
}
|
||||
|
||||
bool AstActive::hasClocked() const { return m_sensesp->hasClocked(); }
|
||||
bool AstActive::hasCombo() const { return m_sensesp->hasCombo(); }
|
||||
|
||||
AstElabDisplay::AstElabDisplay(FileLine* fl, VDisplayType dispType, AstNode* exprsp)
|
||||
: ASTGEN_SUPER_ElabDisplay(fl) {
|
||||
addFmtp(new AstSFormatF{fl, AstSFormatF::NoFormat(), exprsp});
|
||||
m_displayType = dispType;
|
||||
}
|
||||
|
||||
AstCStmt::AstCStmt(FileLine* fl, const string& textStmt)
|
||||
: ASTGEN_SUPER_CStmt(fl) {
|
||||
addExprsp(new AstText{fl, textStmt, true});
|
||||
}
|
||||
|
||||
AstCMath::AstCMath(FileLine* fl, const string& textStmt, int setwidth, bool cleanOut)
|
||||
: ASTGEN_SUPER_CMath(fl)
|
||||
, m_cleanOut{cleanOut}
|
||||
, m_pure{true} {
|
||||
addExprsp(new AstText{fl, textStmt, true});
|
||||
if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
|
||||
}
|
||||
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varp->name(), varp, access) {}
|
||||
// This form only allowed post-link (see above)
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varscp->varp()->name(), varscp->varp(), access) {
|
||||
varScopep(varscp);
|
||||
}
|
||||
bool AstVarRef::same(const AstVarRef* samep) const {
|
||||
if (varScopep()) {
|
||||
return (varScopep() == samep->varScopep() && access() == samep->access());
|
||||
} else {
|
||||
return (selfPointer() == samep->selfPointer() && varp()->name() == samep->varp()->name()
|
||||
&& access() == samep->access());
|
||||
}
|
||||
}
|
||||
bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
|
||||
if (varScopep()) {
|
||||
return (varScopep() == samep->varScopep());
|
||||
} else {
|
||||
return (selfPointer() == samep->selfPointer()
|
||||
&& (!selfPointer().empty() || !samep->selfPointer().empty())
|
||||
&& varp()->name() == samep->varp()->name());
|
||||
}
|
||||
}
|
||||
|
||||
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, varp->name(), varp, access)
|
||||
, m_dotted{dotted} {
|
||||
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 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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+4301
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+422
-44
@@ -26,7 +26,7 @@
|
||||
#include "V3PartitionGraph.h" // Just for mtask dumping
|
||||
#include "V3String.h"
|
||||
|
||||
#include "V3AstNodes__gen_macros.h" // Generated by 'astgen'
|
||||
#include "V3Ast__gen_macros.h" // Generated by 'astgen'
|
||||
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
@@ -155,6 +155,51 @@ void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Numb
|
||||
}
|
||||
}
|
||||
|
||||
void AstBasicDType::init(VBasicDTypeKwd kwd, VSigning numer, int wantwidth, int wantwidthmin,
|
||||
AstRange* rangep) {
|
||||
// wantwidth=0 means figure it out, but if a widthmin is >=0
|
||||
// we allow width 0 so that {{0{x}},y} works properly
|
||||
// wantwidthmin=-1: default, use wantwidth if it is non zero
|
||||
m.m_keyword = kwd;
|
||||
// Implicitness: // "parameter X" is implicit and sized from initial
|
||||
// value, "parameter reg x" not
|
||||
if (keyword() == VBasicDTypeKwd::LOGIC_IMPLICIT) {
|
||||
if (rangep || wantwidth) m.m_keyword = VBasicDTypeKwd::LOGIC;
|
||||
}
|
||||
if (numer == VSigning::NOSIGN) {
|
||||
if (keyword().isSigned()) {
|
||||
numer = VSigning::SIGNED;
|
||||
} else if (keyword().isUnsigned()) {
|
||||
numer = VSigning::UNSIGNED;
|
||||
}
|
||||
}
|
||||
numeric(numer);
|
||||
if (!rangep && (wantwidth || wantwidthmin >= 0)) { // Constant width
|
||||
if (wantwidth > 1) m.m_nrange.init(wantwidth - 1, 0, false);
|
||||
const int wmin = wantwidthmin >= 0 ? wantwidthmin : wantwidth;
|
||||
widthForce(wantwidth, wmin);
|
||||
} else if (!rangep) { // Set based on keyword properties
|
||||
// V3Width will pull from this width
|
||||
if (keyword().width() > 1 && !isOpaque()) {
|
||||
m.m_nrange.init(keyword().width() - 1, 0, false);
|
||||
}
|
||||
widthForce(keyword().width(), keyword().width());
|
||||
} else {
|
||||
widthForce(rangep->elementsConst(),
|
||||
rangep->elementsConst()); // Maybe unknown if parameters underneath it
|
||||
}
|
||||
this->rangep(rangep);
|
||||
this->dtypep(this);
|
||||
}
|
||||
|
||||
void AstBasicDType::cvtRangeConst() {
|
||||
if (rangep() && VN_IS(rangep()->leftp(), Const) && VN_IS(rangep()->rightp(), Const)) {
|
||||
m.m_nrange = VNumRange{rangep()->leftConst(), rangep()->rightConst()};
|
||||
rangep()->unlinkFrBackWithNext()->deleteTree();
|
||||
rangep(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
int AstBasicDType::widthAlignBytes() const {
|
||||
if (width() <= 8) {
|
||||
return 1;
|
||||
@@ -334,7 +379,7 @@ string AstVar::verilogKwd() const {
|
||||
}
|
||||
|
||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
|
||||
bool asRef) const {
|
||||
bool asRef) const VL_MT_SAFE {
|
||||
UASSERT_OBJ(!forReturn, this,
|
||||
"Internal data is never passed as return, but as first argument");
|
||||
string ostatic;
|
||||
@@ -519,11 +564,11 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
|
||||
return dpiTypesToStringConverter{}.convert(this);
|
||||
} else {
|
||||
class converter final : public dpiTypesToStringConverter {
|
||||
virtual string bitLogicVector(const AstVar* varp, bool isBit) const override {
|
||||
string bitLogicVector(const AstVar* varp, bool isBit) const override {
|
||||
return string(varp->isReadOnly() ? "const " : "")
|
||||
+ dpiTypesToStringConverter::bitLogicVector(varp, isBit) + '*';
|
||||
}
|
||||
virtual string primitive(const AstVar* varp) const override {
|
||||
string primitive(const AstVar* varp) const override {
|
||||
string type = dpiTypesToStringConverter::primitive(varp);
|
||||
if (varp->isWritable() || VN_IS(varp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
if (!varp->isWritable() && varp->basicp()->keyword() != VBasicDTypeKwd::STRING)
|
||||
@@ -555,16 +600,16 @@ string AstVar::dpiTmpVarType(const string& varName) const {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
virtual string openArray(const AstVar* varp) const override {
|
||||
string openArray(const AstVar* varp) const override {
|
||||
return dpiTypesToStringConverter::openArray(varp) + ' ' + m_name
|
||||
+ arraySuffix(varp, 0);
|
||||
}
|
||||
virtual string bitLogicVector(const AstVar* varp, bool isBit) const override {
|
||||
string bitLogicVector(const AstVar* varp, bool isBit) const override {
|
||||
string type = dpiTypesToStringConverter::bitLogicVector(varp, isBit);
|
||||
type += ' ' + m_name + arraySuffix(varp, varp->widthWords());
|
||||
return type;
|
||||
}
|
||||
virtual string primitive(const AstVar* varp) const override {
|
||||
string primitive(const AstVar* varp) const override {
|
||||
string type = dpiTypesToStringConverter::primitive(varp);
|
||||
if (varp->isWritable() || VN_IS(varp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
if (!varp->isWritable() && varp->basicp()->keyword() == VBasicDTypeKwd::CHANDLE)
|
||||
@@ -615,34 +660,26 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (VN_IS(nodep, VarRef)) {
|
||||
if (VN_AS(nodep, VarRef)->varp()->isSc()) {
|
||||
return VN_AS(nodep, VarRef)->varp();
|
||||
} else if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) {
|
||||
if (vrefp->varp()->isSc()) {
|
||||
return vrefp->varp();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (VN_IS(nodep, ArraySel)) {
|
||||
if (nodep->op1p()) {
|
||||
if (AstVar* p = scVarRecurse(nodep->op1p())) return p;
|
||||
}
|
||||
if (nodep->op2p()) {
|
||||
if (AstVar* p = scVarRecurse(nodep->op2p())) return p;
|
||||
}
|
||||
if (nodep->op3p()) {
|
||||
if (AstVar* p = scVarRecurse(nodep->op3p())) return p;
|
||||
}
|
||||
if (nodep->op4p()) {
|
||||
if (AstVar* p = scVarRecurse(nodep->op4p())) return p;
|
||||
}
|
||||
} else if (AstArraySel* const arraySelp = VN_CAST(nodep, ArraySel)) {
|
||||
if (AstVar* const p = scVarRecurse(arraySelp->fromp())) return p;
|
||||
if (AstVar* const p = scVarRecurse(arraySelp->bitp())) return p;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
|
||||
|
||||
class AstNodeDType::CTypeRecursed final {
|
||||
public:
|
||||
string m_type; // The base type, e.g.: "Foo_t"s
|
||||
string m_dims; // Array dimensions, e.g.: "[3][2][1]"
|
||||
string render(const string& name, bool isRef) const {
|
||||
string render(const string& name, bool isRef) const VL_MT_SAFE {
|
||||
string out;
|
||||
out += m_type;
|
||||
if (!name.empty()) out += " ";
|
||||
@@ -659,7 +696,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const VL_MT_SAFE {
|
||||
const CTypeRecursed info = cTypeRecurse(false);
|
||||
return info.render(name, isRef);
|
||||
}
|
||||
@@ -687,6 +724,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
info.m_type += ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||
info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
||||
info.m_type = EmitCBaseVisitor::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
if (adtypep->isCompound()) compound = true;
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound);
|
||||
@@ -709,6 +748,18 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
info.m_type = "std::string";
|
||||
} else if (bdtypep->keyword().isMTaskState()) {
|
||||
info.m_type = "VlMTaskVertex";
|
||||
} else if (bdtypep->isTriggerVec()) {
|
||||
info.m_type = "VlTriggerVec<" + cvtToStr(dtypep->width()) + ">";
|
||||
} else if (bdtypep->isDelayScheduler()) {
|
||||
info.m_type = "VlDelayScheduler";
|
||||
} else if (bdtypep->isTriggerScheduler()) {
|
||||
info.m_type = "VlTriggerScheduler";
|
||||
} else if (bdtypep->isDynamicTriggerScheduler()) {
|
||||
info.m_type = "VlDynamicTriggerScheduler";
|
||||
} else if (bdtypep->isForkSync()) {
|
||||
info.m_type = "VlForkSync";
|
||||
} else if (bdtypep->isEvent()) {
|
||||
info.m_type = "VlEvent";
|
||||
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
|
||||
info.m_type = "CData" + bitvec;
|
||||
} else if (dtypep->widthMin() <= 16) {
|
||||
@@ -781,7 +832,7 @@ int AstNodeDType::widthPow2() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool AstNodeDType::isLiteralType() const {
|
||||
bool AstNodeDType::isLiteralType() const VL_MT_SAFE {
|
||||
if (const auto* const dtypep = VN_CAST(skipRefp(), BasicDType)) {
|
||||
return dtypep->keyword().isLiteralType();
|
||||
} else if (const auto* const dtypep = VN_CAST(skipRefp(), UnpackArrayDType)) {
|
||||
@@ -857,6 +908,29 @@ string AstScope::nameDotless() const {
|
||||
return out;
|
||||
}
|
||||
|
||||
AstVarScope* AstScope::createTemp(const string& name, unsigned width) {
|
||||
FileLine* const flp = fileline();
|
||||
AstVar* const varp
|
||||
= new AstVar{flp, VVarType::MODULETEMP, name, VFlagBitPacked{}, static_cast<int>(width)};
|
||||
modp()->addStmtsp(varp);
|
||||
AstVarScope* const vscp = new AstVarScope{flp, this, varp};
|
||||
addVarsp(vscp);
|
||||
return vscp;
|
||||
}
|
||||
|
||||
AstVarScope* AstScope::createTemp(const string& name, AstNodeDType* dtypep) {
|
||||
FileLine* const flp = fileline();
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtypep};
|
||||
modp()->addStmtsp(varp);
|
||||
AstVarScope* const vscp = new AstVarScope{flp, this, varp};
|
||||
addVarsp(vscp);
|
||||
return vscp;
|
||||
}
|
||||
|
||||
AstVarScope* AstScope::createTempLike(const string& name, AstVarScope* vscp) {
|
||||
return createTemp(name, vscp->dtypep());
|
||||
}
|
||||
|
||||
string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const {
|
||||
string out;
|
||||
for (AstText* textp = scopeTextp; textp; textp = VN_AS(textp->nextp(), Text)) {
|
||||
@@ -889,10 +963,10 @@ bool AstSenTree::hasClocked() const {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasSettle() const {
|
||||
bool AstSenTree::hasStatic() const {
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
|
||||
if (senp->isSettle()) return true;
|
||||
if (senp->isStatic()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -903,6 +977,13 @@ bool AstSenTree::hasInitial() const {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasFinal() const {
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
|
||||
if (senp->isFinal()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasCombo() const {
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
|
||||
@@ -910,6 +991,13 @@ bool AstSenTree::hasCombo() const {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool AstSenTree::hasHybrid() const {
|
||||
UASSERT_OBJ(sensesp(), this, "SENTREE without any SENITEMs under it");
|
||||
for (AstSenItem* senp = sensesp(); senp; senp = VN_AS(senp->nextp(), SenItem)) {
|
||||
if (senp->isHybrid()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AstTypeTable::AstTypeTable(FileLine* fl)
|
||||
: ASTGEN_SUPER_TypeTable(fl) {
|
||||
@@ -1023,8 +1111,13 @@ 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);
|
||||
this->modulep(m_modp);
|
||||
m_modp->addStmtsp(m_scopep);
|
||||
}
|
||||
const char* AstConstPool::broken() const {
|
||||
BROKEN_RTN(m_modp && !m_modp->brokeExists());
|
||||
BROKEN_RTN(m_scopep && !m_scopep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstVarScope* AstConstPool::createNewEntry(const string& name, AstNode* initp) {
|
||||
@@ -1033,9 +1126,9 @@ AstVarScope* AstConstPool::createNewEntry(const string& name, AstNode* initp) {
|
||||
varp->isConst(true);
|
||||
varp->isStatic(true);
|
||||
varp->valuep(initp->cloneTree(false));
|
||||
m_modp->addStmtp(varp);
|
||||
m_modp->addStmtsp(varp);
|
||||
AstVarScope* const varScopep = new AstVarScope(fl, m_scopep, varp);
|
||||
m_scopep->addVarp(varScopep);
|
||||
m_scopep->addVarsp(varScopep);
|
||||
return varScopep;
|
||||
}
|
||||
|
||||
@@ -1181,7 +1274,7 @@ void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) {
|
||||
} else if (belowp == condp()) {
|
||||
// Goes before condition, IE in preconditions
|
||||
addPrecondsp(newp);
|
||||
} else if (belowp == bodysp()) {
|
||||
} else if (belowp == stmtsp()) {
|
||||
// Was first statement in body, so new front
|
||||
belowp->addHereThisAsNext(newp);
|
||||
} else {
|
||||
@@ -1198,12 +1291,12 @@ void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
|
||||
belowp->addNextHere(newp);
|
||||
} else if (belowp == condp()) {
|
||||
// Becomes first statement in body, body may have been empty
|
||||
if (bodysp()) {
|
||||
bodysp()->addHereThisAsNext(newp);
|
||||
if (stmtsp()) {
|
||||
stmtsp()->addHereThisAsNext(newp);
|
||||
} else {
|
||||
addBodysp(newp);
|
||||
addStmtsp(newp);
|
||||
}
|
||||
} else if (belowp == bodysp()) {
|
||||
} else if (belowp == stmtsp()) {
|
||||
// Next statement in body
|
||||
belowp->addNextHere(newp);
|
||||
} else {
|
||||
@@ -1224,10 +1317,10 @@ static std::string nodeAddr(const AstNode* nodep) {
|
||||
}
|
||||
|
||||
void AstNode::dump(std::ostream& str) const {
|
||||
str << typeName() << " "
|
||||
<< nodeAddr(this)
|
||||
//<< " " << nodeAddr(m_backp)
|
||||
str << typeName() << " " << nodeAddr(this)
|
||||
#ifdef VL_DEBUG
|
||||
<< " <e" << std::dec << editCount() << ((editCount() >= editCountLast()) ? "#>" : ">")
|
||||
#endif
|
||||
<< " {" << fileline()->filenameLetters() << std::dec << fileline()->lastLineno()
|
||||
<< fileline()->firstColumnLetters() << "}";
|
||||
if (user1p()) str << " u1=" << nodeAddr(user1p());
|
||||
@@ -1255,7 +1348,10 @@ void AstNode::dump(std::ostream& str) const {
|
||||
}
|
||||
}
|
||||
|
||||
void AstNodeProcedure::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
void AstNodeProcedure::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isSuspendable()) str << " [SUSP]";
|
||||
}
|
||||
|
||||
void AstAlways::dump(std::ostream& str) const {
|
||||
this->AstNodeProcedure::dump(str);
|
||||
@@ -1297,11 +1393,19 @@ void AstCell::dump(std::ostream& str) const {
|
||||
str << " ->UNLINKED:" << modName();
|
||||
}
|
||||
}
|
||||
const char* AstCell::broken() const {
|
||||
BROKEN_RTN(m_modp && !m_modp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstCellInline::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> " << origModName();
|
||||
str << " [scopep=" << reinterpret_cast<const void*>(scopep()) << "]";
|
||||
}
|
||||
const char* AstCellInline::broken() const {
|
||||
BROKEN_RTN(m_scopep && !m_scopep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
const char* AstClassPackage::broken() const {
|
||||
BROKEN_BASE_RTN(AstNodeModule::broken());
|
||||
BROKEN_RTN(m_classp && !m_classp->brokeExists());
|
||||
@@ -1346,6 +1450,17 @@ void AstClass::dump(std::ostream& str) const {
|
||||
if (isExtended()) str << " [EXT]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
}
|
||||
const char* AstClass::broken() const {
|
||||
BROKEN_BASE_RTN(AstNodeModule::broken());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstClass::cloneRelink() {
|
||||
AstNodeModule::cloneRelink();
|
||||
if (m_classOrPackagep && m_classOrPackagep->clonep()) {
|
||||
m_classOrPackagep = m_classOrPackagep->clonep();
|
||||
}
|
||||
}
|
||||
AstClass* AstClassExtends::classp() const {
|
||||
const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
|
||||
if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.'
|
||||
@@ -1368,6 +1483,18 @@ void AstClassRefDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "class:" << name();
|
||||
}
|
||||
const char* AstClassRefDType::broken() const {
|
||||
BROKEN_RTN(m_classp && !m_classp->brokeExists());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstClassRefDType::cloneRelink() {
|
||||
if (m_classp && m_classp->clonep()) m_classp = m_classp->clonep();
|
||||
if (m_classOrPackagep && m_classOrPackagep->clonep()) {
|
||||
m_classOrPackagep = m_classOrPackagep->clonep();
|
||||
}
|
||||
}
|
||||
string AstClassRefDType::name() const { return classp() ? classp()->name() : "<unlinked>"; }
|
||||
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (immediate()) str << " [IMMEDIATE]";
|
||||
@@ -1385,6 +1512,11 @@ void AstEnumItemRef::dump(std::ostream& str) const {
|
||||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstEnumItemRef::broken() const {
|
||||
BROKEN_RTN(m_itemp && !m_itemp->brokeExists());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstIfaceRefDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (cellName() != "") str << " cell=" << cellName();
|
||||
@@ -1404,6 +1536,11 @@ void AstIfaceRefDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "iface";
|
||||
}
|
||||
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();
|
||||
}
|
||||
void AstInitArray::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
int n = 0;
|
||||
@@ -1416,6 +1553,42 @@ void AstInitArray::dump(std::ostream& str) const {
|
||||
str << " [" << itr.first << "]=" << reinterpret_cast<const void*>(itr.second);
|
||||
}
|
||||
}
|
||||
const char* AstInitArray::broken() const {
|
||||
for (KeyItemMap::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
|
||||
BROKEN_RTN(!it->second);
|
||||
BROKEN_RTN(!it->second->brokeExists());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void AstInitArray::cloneRelink() {
|
||||
for (KeyItemMap::iterator it = m_map.begin(); it != m_map.end(); ++it) {
|
||||
if (it->second->clonep()) it->second = it->second->clonep();
|
||||
}
|
||||
}
|
||||
void AstInitArray::addIndexValuep(uint64_t index, AstNode* newp) {
|
||||
const auto it = m_map.find(index);
|
||||
if (it != m_map.end()) {
|
||||
it->second->valuep(newp);
|
||||
} else {
|
||||
AstInitItem* const itemp = new AstInitItem(fileline(), newp);
|
||||
m_map.emplace(index, itemp);
|
||||
addInitsp(itemp);
|
||||
}
|
||||
}
|
||||
AstNode* AstInitArray::getIndexValuep(uint64_t index) const {
|
||||
const auto it = m_map.find(index);
|
||||
if (it == m_map.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return it->second->valuep();
|
||||
}
|
||||
}
|
||||
AstNode* AstInitArray::getIndexDefaultedValuep(uint64_t index) const {
|
||||
AstNode* valuep = getIndexValuep(index);
|
||||
if (!valuep) valuep = defaultp();
|
||||
return valuep;
|
||||
}
|
||||
|
||||
void AstJumpGo::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " -> ";
|
||||
@@ -1425,6 +1598,13 @@ void AstJumpGo::dump(std::ostream& str) const {
|
||||
str << "%Error:UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstJumpGo::broken() const {
|
||||
BROKEN_RTN(!labelp()->brokeExistsBelow());
|
||||
return nullptr;
|
||||
}
|
||||
void AstJumpGo::cloneRelink() {
|
||||
if (m_labelp->clonep()) m_labelp = m_labelp->clonep();
|
||||
}
|
||||
void AstJumpLabel::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " -> ";
|
||||
@@ -1447,6 +1627,13 @@ void AstMemberSel::dump(std::ostream& str) const {
|
||||
str << "%Error:UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstMemberSel::cloneRelink() {
|
||||
if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep();
|
||||
}
|
||||
const char* AstMemberSel::broken() const {
|
||||
BROKEN_RTN(m_varp && !m_varp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstMethodCall::dump(std::ostream& str) const {
|
||||
this->AstNodeFTaskRef::dump(str);
|
||||
if (isStatement()) str << " [STMT]";
|
||||
@@ -1468,6 +1655,13 @@ void AstModportFTaskRef::dump(std::ostream& str) const {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstModportFTaskRef::broken() const {
|
||||
BROKEN_RTN(m_ftaskp && !m_ftaskp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstModportFTaskRef::cloneRelink() {
|
||||
if (m_ftaskp && m_ftaskp->clonep()) m_ftaskp = m_ftaskp->clonep();
|
||||
}
|
||||
void AstModportVarRef::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (direction().isAny()) str << " " << direction();
|
||||
@@ -1478,6 +1672,13 @@ void AstModportVarRef::dump(std::ostream& str) const {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstModportVarRef::broken() const {
|
||||
BROKEN_RTN(m_varp && !m_varp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstModportVarRef::cloneRelink() {
|
||||
if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep();
|
||||
}
|
||||
void AstPin::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (modVarp()) {
|
||||
@@ -1488,6 +1689,17 @@ void AstPin::dump(std::ostream& str) const {
|
||||
}
|
||||
if (svImplicit()) str << " [.SV]";
|
||||
}
|
||||
const char* AstPin::broken() const {
|
||||
BROKEN_RTN(m_modVarp && !m_modVarp->brokeExists());
|
||||
BROKEN_RTN(m_modPTypep && !m_modPTypep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
string AstPin::prettyOperatorName() const {
|
||||
return modVarp()
|
||||
? ((modVarp()->direction().isAny() ? modVarp()->direction().prettyName() + " " : "")
|
||||
+ "port connection " + modVarp()->prettyNameQ())
|
||||
: "port connection";
|
||||
}
|
||||
void AstPrintTimeScale::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " " << timeunit();
|
||||
@@ -1509,12 +1721,25 @@ void AstTimeImport::dump(std::ostream& str) const {
|
||||
void AstTypedef::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (attrPublic()) str << " [PUBLIC]";
|
||||
if (subDTypep()) {
|
||||
str << " -> ";
|
||||
subDTypep()->dump(str);
|
||||
}
|
||||
}
|
||||
void AstNodeRange::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||
void AstRange::dump(std::ostream& str) const {
|
||||
this->AstNodeRange::dump(str);
|
||||
if (littleEndian()) str << " [LITTLE]";
|
||||
}
|
||||
void AstParamTypeDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (subDTypep()) {
|
||||
str << " -> ";
|
||||
subDTypep()->dump(str);
|
||||
} else {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstRefDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (typedefp() || subDTypep()) {
|
||||
@@ -1533,6 +1758,23 @@ void AstRefDType::dump(std::ostream& str) const {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstRefDType::broken() const {
|
||||
BROKEN_RTN(m_typedefp && !m_typedefp->brokeExists());
|
||||
BROKEN_RTN(m_refDTypep && !m_refDTypep->brokeExists());
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstRefDType::cloneRelink() {
|
||||
if (m_typedefp && m_typedefp->clonep()) m_typedefp = m_typedefp->clonep();
|
||||
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||
if (m_classOrPackagep && m_classOrPackagep->clonep()) {
|
||||
m_classOrPackagep = m_classOrPackagep->clonep();
|
||||
}
|
||||
}
|
||||
AstNodeDType* AstRefDType::subDTypep() const {
|
||||
if (typedefp()) return typedefp()->subDTypep();
|
||||
return refDTypep(); // Maybe nullptr
|
||||
}
|
||||
void AstNodeUOrStructDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (packed()) str << " [PACKED]";
|
||||
@@ -1603,6 +1845,33 @@ void AstNetlist::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << timeunit() << "/" << timeprecision() << "]";
|
||||
}
|
||||
const char* AstNetlist::broken() const {
|
||||
BROKEN_RTN(m_typeTablep && !m_typeTablep->brokeExists());
|
||||
BROKEN_RTN(m_constPoolp && !m_constPoolp->brokeExists());
|
||||
BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists());
|
||||
BROKEN_RTN(m_evalp && !m_evalp->brokeExists());
|
||||
BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists());
|
||||
BROKEN_RTN(m_topScopep && !m_topScopep->brokeExists());
|
||||
BROKEN_RTN(m_delaySchedulerp && !m_delaySchedulerp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
AstPackage* AstNetlist::dollarUnitPkgAddp() {
|
||||
if (!m_dollarUnitPkgp) {
|
||||
m_dollarUnitPkgp = new AstPackage(fileline(), AstPackage::dollarUnitName());
|
||||
// packages are always libraries; don't want to make them a "top"
|
||||
m_dollarUnitPkgp->inLibrary(true);
|
||||
m_dollarUnitPkgp->modTrace(false); // may reconsider later
|
||||
m_dollarUnitPkgp->internal(true);
|
||||
addModulesp(m_dollarUnitPkgp);
|
||||
}
|
||||
return m_dollarUnitPkgp;
|
||||
}
|
||||
void AstNetlist::createTopScope(AstScope* scopep) {
|
||||
UASSERT(scopep, "Must not be nullptr");
|
||||
UASSERT_OBJ(!m_topScopep, scopep, "TopScope already exits");
|
||||
m_topScopep = new AstTopScope{scopep->modp()->fileline(), scopep};
|
||||
scopep->modp()->addStmtsp(v3Global.rootp()->topScopep());
|
||||
}
|
||||
void AstNodeModule::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " L" << level();
|
||||
@@ -1620,10 +1889,24 @@ void AstPackageExport::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> " << packagep();
|
||||
}
|
||||
const char* AstPackageExport ::broken() const {
|
||||
BROKEN_RTN(!m_packagep || !m_packagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstPackageExport::cloneRelink() {
|
||||
if (m_packagep && m_packagep->clonep()) m_packagep = m_packagep->clonep();
|
||||
}
|
||||
void AstPackageImport::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " -> " << packagep();
|
||||
}
|
||||
const char* AstPackageImport::broken() const {
|
||||
BROKEN_RTN(!m_packagep || !m_packagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstPackageImport::cloneRelink() {
|
||||
if (m_packagep && m_packagep->clonep()) m_packagep = m_packagep->clonep();
|
||||
}
|
||||
void AstPatMember::dump(std::ostream& str) const {
|
||||
this->AstNodeMath::dump(str);
|
||||
if (isDefault()) str << " [DEFAULT]";
|
||||
@@ -1650,7 +1933,7 @@ void AstTypeTable::dump(std::ostream& str) const {
|
||||
for (int i = 0; i < static_cast<int>(VBasicDTypeKwd::_ENUM_MAX); ++i) {
|
||||
if (AstBasicDType* const subnodep = m_basicps[i]) {
|
||||
str << '\n'; // Newline from caller, so newline first
|
||||
str << "\t\t" << std::setw(8) << VBasicDTypeKwd(i).ascii();
|
||||
str << "\t\t" << std::setw(8) << VBasicDTypeKwd{i}.ascii();
|
||||
str << " -> ";
|
||||
subnodep->dump(str);
|
||||
}
|
||||
@@ -1691,10 +1974,30 @@ void AstWildcardArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[*]";
|
||||
}
|
||||
bool AstWildcardArrayDType::same(const AstNode* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
return (subDTypep() == asamep->subDTypep());
|
||||
}
|
||||
bool AstWildcardArrayDType::similarDType(AstNodeDType* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[]";
|
||||
}
|
||||
bool AstUnsizedArrayDType::same(const AstNode* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
if (!asamep->subDTypep()) return false;
|
||||
return (subDTypep() == asamep->subDTypep());
|
||||
}
|
||||
bool AstUnsizedArrayDType::similarDType(AstNodeDType* samep) const {
|
||||
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||
return type() == samep->type() && asamep->subDTypep()
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||
}
|
||||
void AstEmptyQueueDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "emptyq";
|
||||
@@ -1705,7 +2008,6 @@ void AstVoidDType::dumpSmall(std::ostream& str) const {
|
||||
}
|
||||
void AstVarScope::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isCircular()) str << " [CIRC]";
|
||||
if (isTrace()) str << " [T]";
|
||||
if (scopep()) str << " [scopep=" << reinterpret_cast<const void*>(scopep()) << "]";
|
||||
if (varp()) {
|
||||
@@ -1742,6 +2044,17 @@ void AstVarRef::dump(std::ostream& str) const {
|
||||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
bool AstVarRef::same(const AstNode* samep) const {
|
||||
return same(static_cast<const AstVarRef*>(samep));
|
||||
}
|
||||
int AstVarRef::instrCount() const {
|
||||
// Account for the target of hard-coded method calls as just an address computation
|
||||
if (const AstCMethodHard* const callp = VN_CAST(backp(), CMethodHard)) {
|
||||
if (callp->fromp() == this) return 1;
|
||||
}
|
||||
// Otherwise as a load/store
|
||||
return widthInstrs() * (access().isReadOrRW() ? INSTR_COUNT_LD : 1);
|
||||
}
|
||||
void AstVar::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isSc()) str << " [SC]";
|
||||
@@ -1754,7 +2067,6 @@ void AstVar::dump(std::ostream& str) const {
|
||||
if (isSigPublic()) str << " [P]";
|
||||
if (isLatched()) str << " [LATCHED]";
|
||||
if (isUsedLoopIdx()) str << " [LOOP]";
|
||||
if (attrClockEn()) str << " [aCLKEN]";
|
||||
if (attrIsolateAssign()) str << " [aISO]";
|
||||
if (attrFileDescr()) str << " [aFD]";
|
||||
if (isFuncReturn()) {
|
||||
@@ -1786,6 +2098,10 @@ void AstSenItem::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << edgeType().ascii() << "]";
|
||||
}
|
||||
void AstStrengthSpec::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " (" << m_s0.ascii() << ", " << m_s1.ascii() << ")";
|
||||
}
|
||||
void AstParseRef::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << expect().ascii() << "]";
|
||||
@@ -1800,6 +2116,14 @@ void AstClassOrPackageRef::dump(std::ostream& str) const {
|
||||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
AstNodeModule* AstClassOrPackageRef::classOrPackagep() const {
|
||||
AstNode* foundp = m_classOrPackageNodep;
|
||||
if (auto* const anodep = VN_CAST(foundp, Typedef)) foundp = anodep->subDTypep();
|
||||
if (auto* const anodep = VN_CAST(foundp, NodeDType)) foundp = anodep->skipRefp();
|
||||
if (auto* const anodep = VN_CAST(foundp, ClassRefDType)) foundp = anodep->classp();
|
||||
return VN_CAST(foundp, NodeModule);
|
||||
}
|
||||
|
||||
void AstDot::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (colon()) str << " [::]";
|
||||
@@ -1813,6 +2137,13 @@ void AstActive::dump(std::ostream& str) const {
|
||||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstActive::broken() const {
|
||||
BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
void AstActive::cloneRelink() {
|
||||
if (m_sensesp->clonep()) m_sensesp = m_sensesp->clonep();
|
||||
}
|
||||
void AstNodeFTaskRef::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
|
||||
@@ -1915,8 +2246,55 @@ void AstCFunc::dump(std::ostream& str) const {
|
||||
if (isConstructor()) str << " [CTOR]";
|
||||
if (isDestructor()) str << " [DTOR]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
if (isCoroutine()) str << " [CORO]";
|
||||
}
|
||||
void AstCAwait::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (sensesp()) {
|
||||
str << " => ";
|
||||
sensesp()->dump(str);
|
||||
}
|
||||
}
|
||||
int AstCMethodHard::instrCount() const {
|
||||
if (AstBasicDType* const basicp = fromp()->dtypep()->basicp()) {
|
||||
// TODO: add a more structured description of library methods, rather than using string
|
||||
// matching. See #3715.
|
||||
if (basicp->isTriggerVec() && m_name == "at") {
|
||||
// This is an important special case for scheduling so we compute it precisely,
|
||||
// it is simply a load.
|
||||
return INSTR_COUNT_LD;
|
||||
}
|
||||
}
|
||||
return AstNodeStmt::instrCount();
|
||||
}
|
||||
const char* AstCFunc::broken() const {
|
||||
BROKEN_RTN((m_scopep && !m_scopep->brokeExists()));
|
||||
return nullptr;
|
||||
}
|
||||
void AstCFunc::cloneRelink() {
|
||||
if (m_scopep && m_scopep->clonep()) m_scopep = m_scopep->clonep();
|
||||
}
|
||||
|
||||
void AstCUse::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [" << useType() << "]";
|
||||
}
|
||||
|
||||
AstAlways* AstAssignW::convertToAlways() {
|
||||
const bool hasTimingControl = isTimingControl();
|
||||
AstNode* const lhs1p = lhsp()->unlinkFrBack();
|
||||
AstNode* const rhs1p = rhsp()->unlinkFrBack();
|
||||
AstNode* const controlp = timingControlp() ? timingControlp()->unlinkFrBack() : nullptr;
|
||||
FileLine* const flp = fileline();
|
||||
AstNode* bodysp = new AstAssign{flp, lhs1p, rhs1p, controlp};
|
||||
if (hasTimingControl) {
|
||||
// If there's a timing control, put the assignment in a fork..join_none. This process won't
|
||||
// get marked as suspendable and thus will be scheduled normally
|
||||
auto* forkp = new AstFork{flp, "", bodysp};
|
||||
forkp->joinType(VJoinType::JOIN_NONE);
|
||||
bodysp = forkp;
|
||||
}
|
||||
AstAlways* const newp = new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, bodysp};
|
||||
replaceWith(newp); // User expected to then deleteTree();
|
||||
return newp;
|
||||
}
|
||||
|
||||
-9571
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,7 @@ class AstUserAllocatorBase VL_NOT_FINAL {
|
||||
private:
|
||||
std::vector<T_Data*> m_allocated;
|
||||
|
||||
inline T_Data* getUserp(const T_Node* nodep) const {
|
||||
T_Data* getUserp(const T_Node* nodep) const {
|
||||
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
|
||||
const VNUser user = nodep->user1u();
|
||||
return user.to<T_Data*>();
|
||||
@@ -54,17 +54,17 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
inline void setUserp(T_Node* nodep, T_Data* userp) const {
|
||||
void setUserp(T_Node* nodep, T_Data* userp) const {
|
||||
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
|
||||
nodep->user1u(VNUser(userp));
|
||||
nodep->user1u(VNUser{userp});
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
|
||||
nodep->user2u(VNUser(userp));
|
||||
nodep->user2u(VNUser{userp});
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
|
||||
nodep->user3u(VNUser(userp));
|
||||
nodep->user3u(VNUser{userp});
|
||||
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
|
||||
nodep->user4u(VNUser(userp));
|
||||
nodep->user4u(VNUser{userp});
|
||||
} else {
|
||||
nodep->user5u(VNUser(userp));
|
||||
nodep->user5u(VNUser{userp});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
-32
@@ -35,6 +35,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
class BeginState final {
|
||||
@@ -68,9 +70,9 @@ private:
|
||||
string m_namedScope; // Name of begin blocks above us
|
||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
int m_ifDepth = 0; // Current if depth
|
||||
bool m_underFork = false; // True if the current statement is directly under a fork
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
string dot(const string& a, const string& b) {
|
||||
if (a == "") return b;
|
||||
@@ -85,7 +87,7 @@ private:
|
||||
string::size_type pos;
|
||||
while ((pos = dottedname.find("__DOT__")) != string::npos) {
|
||||
const string ident = dottedname.substr(0, pos);
|
||||
dottedname = dottedname.substr(pos + strlen("__DOT__"));
|
||||
dottedname = dottedname.substr(pos + std::strlen("__DOT__"));
|
||||
if (nodep->name() != "") {
|
||||
m_displayScope = dot(m_displayScope, ident);
|
||||
m_namedScope = dot(m_namedScope, ident);
|
||||
@@ -115,19 +117,25 @@ private:
|
||||
}
|
||||
} else {
|
||||
// Move to module
|
||||
m_modp->addStmtp(nodep);
|
||||
m_modp->addStmtsp(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstFork* nodep) override {
|
||||
VL_RESTORER(m_underFork);
|
||||
m_underFork = true;
|
||||
dotNames(nodep, "__FORK__");
|
||||
nodep->name("");
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep) override {
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
UINFO(8, " " << nodep << endl);
|
||||
// Rename it
|
||||
if (m_unnamedScope != "") {
|
||||
@@ -162,26 +170,30 @@ private:
|
||||
m_ftaskp = nullptr;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstBegin* nodep) override {
|
||||
void visit(AstBegin* nodep) override {
|
||||
// Begin blocks were only useful in variable creation, change names and delete
|
||||
UINFO(8, " " << nodep << endl);
|
||||
VL_RESTORER(m_displayScope);
|
||||
VL_RESTORER(m_namedScope);
|
||||
VL_RESTORER(m_unnamedScope);
|
||||
{
|
||||
dotNames(nodep, "__BEGIN__");
|
||||
|
||||
{
|
||||
VL_RESTORER(m_underFork);
|
||||
m_underFork = false;
|
||||
dotNames(nodep, "__BEGIN__");
|
||||
}
|
||||
UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier");
|
||||
|
||||
// Cleanup
|
||||
if (m_underFork) {
|
||||
// If we're under a fork, keep this begin to group its statements together
|
||||
nodep->name("");
|
||||
return;
|
||||
}
|
||||
AstNode* addsp = nullptr;
|
||||
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
if (addsp) {
|
||||
addsp = addsp->addNextNull(stmtsp);
|
||||
} else {
|
||||
addsp = stmtsp;
|
||||
}
|
||||
addsp = AstNode::addNext(addsp, stmtsp);
|
||||
}
|
||||
if (addsp) {
|
||||
nodep->replaceWith(addsp);
|
||||
@@ -191,7 +203,7 @@ private:
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVar* nodep) override {
|
||||
void visit(AstVar* nodep) override {
|
||||
if (m_unnamedScope != "") {
|
||||
// Rename it
|
||||
nodep->name(dot(m_unnamedScope, nodep->name()));
|
||||
@@ -200,7 +212,7 @@ private:
|
||||
liftNode(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) override {
|
||||
void visit(AstTypedef* nodep) override {
|
||||
if (m_unnamedScope != "") {
|
||||
// Rename it
|
||||
nodep->name(dot(m_unnamedScope, nodep->name()));
|
||||
@@ -209,7 +221,7 @@ private:
|
||||
liftNode(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
void visit(AstCell* nodep) override {
|
||||
UINFO(8, " CELL " << nodep << endl);
|
||||
if (m_namedScope != "") {
|
||||
m_statep->userMarkChanged(nodep);
|
||||
@@ -218,18 +230,18 @@ private:
|
||||
UINFO(8, " rename to " << nodep->name() << endl);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
m_modp->addStmtp(nodep);
|
||||
m_modp->addStmtsp(nodep);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep) override {
|
||||
void visit(AstVarXRef* nodep) override {
|
||||
UINFO(9, " VARXREF " << nodep << endl);
|
||||
if (m_namedScope != "" && nodep->inlinedDots() == "" && !m_ftaskp) {
|
||||
nodep->inlinedDots(m_namedScope);
|
||||
UINFO(9, " rescope to " << nodep << endl);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScopeName* nodep) override {
|
||||
void visit(AstScopeName* nodep) override {
|
||||
// If there's a %m in the display text, we add a special node that will contain the name()
|
||||
// Similar code in V3Inline
|
||||
if (nodep->user1SetOnce()) return; // Don't double-add text's
|
||||
@@ -237,20 +249,22 @@ private:
|
||||
const string scname = nodep->forFormat() ? m_displayScope : m_namedScope;
|
||||
if (!scname.empty()) {
|
||||
// To keep correct visual order, must add before other Text's
|
||||
AstNode* const afterp = nodep->scopeAttrp();
|
||||
AstText* const afterp = nodep->scopeAttrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeAttrp(new AstText{nodep->fileline(), string("__DOT__") + scname});
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
nodep->addScopeAttrp(new AstText{nodep->fileline(), string("__DOT__") + scname});
|
||||
if (afterp) nodep->addScopeAttrp(afterp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverDecl* nodep) override {
|
||||
void visit(AstCoverDecl* nodep) override {
|
||||
// Don't need to fix path in coverage statements, they're not under
|
||||
// any BEGINs, but V3Coverage adds them all under the module itself.
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
// VISITORS - LINT CHECK
|
||||
virtual void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
||||
void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
||||
VL_RESTORER(m_underFork);
|
||||
m_underFork = false;
|
||||
// Check IFDEPTH warning - could be in other transform files if desire
|
||||
VL_RESTORER(m_ifDepth);
|
||||
if (m_ifDepth == -1 || v3Global.opt.ifDepth() < 1) { // Turned off
|
||||
@@ -264,7 +278,11 @@ private:
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override {
|
||||
VL_RESTORER(m_underFork);
|
||||
m_underFork = false;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -272,7 +290,7 @@ public:
|
||||
: m_statep{statep} {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~BeginVisitor() override = default;
|
||||
~BeginVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -285,21 +303,21 @@ private:
|
||||
// AstNodeFTask::user1p // Node replaced, rename it
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeFTaskRef* nodep) override {
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
if (nodep->taskp()->user1()) { // It was converted
|
||||
UINFO(9, " relinkFTask " << nodep << endl);
|
||||
nodep->name(nodep->taskp()->name());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (nodep->varp()->user1()) { // It was converted
|
||||
UINFO(9, " relinVarRef " << nodep << endl);
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstIfaceRefDType* nodep) override {
|
||||
void visit(AstIfaceRefDType* nodep) override {
|
||||
// May have changed cell names
|
||||
// TypeTable is always after all modules, so names are stable
|
||||
UINFO(8, " IFACEREFDTYPE " << nodep << endl);
|
||||
@@ -308,12 +326,12 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
BeginRelinkVisitor(AstNetlist* nodep, BeginState*) { iterate(nodep); }
|
||||
virtual ~BeginRelinkVisitor() override = default;
|
||||
~BeginRelinkVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -326,5 +344,5 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
|
||||
{ BeginVisitor{nodep, &state}; }
|
||||
if (state.anyFuncInBegin()) { BeginRelinkVisitor{nodep, &state}; }
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("begin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("begin", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+8
-7
@@ -33,6 +33,8 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Branch state, as a visitor of each AstNode
|
||||
|
||||
@@ -49,7 +51,6 @@ private:
|
||||
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void reset() {
|
||||
m_likely = false;
|
||||
@@ -63,14 +64,14 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeIf* nodep) override {
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
UINFO(4, " IF: " << nodep << endl);
|
||||
VL_RESTORER(m_likely);
|
||||
VL_RESTORER(m_unlikely);
|
||||
{
|
||||
// Do if
|
||||
reset();
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
iterateAndNextNull(nodep->thensp());
|
||||
const int ifLikely = m_likely;
|
||||
const int ifUnlikely = m_unlikely;
|
||||
// Do else
|
||||
@@ -87,17 +88,17 @@ private:
|
||||
} // else leave unknown
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeCCall* nodep) override {
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
checkUnlikely(nodep);
|
||||
nodep->funcp()->user1Inc();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
void visit(AstCFunc* nodep) override {
|
||||
checkUnlikely(nodep);
|
||||
m_cfuncsp.push_back(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
checkUnlikely(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -116,7 +117,7 @@ public:
|
||||
iterateChildren(nodep);
|
||||
calc_tasks();
|
||||
}
|
||||
virtual ~BranchVisitor() override = default;
|
||||
~BranchVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
+18
-16
@@ -36,6 +36,8 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Generation counter for AstNode::m_brokenState
|
||||
|
||||
@@ -49,7 +51,7 @@ static class BrokenCntGlobal {
|
||||
uint8_t m_count = MIN_VALUE;
|
||||
|
||||
public:
|
||||
uint8_t get() const {
|
||||
uint8_t get() const VL_MT_SAFE {
|
||||
UASSERT(MIN_VALUE <= m_count && m_count <= MAX_VALUE, "Invalid generation number");
|
||||
return m_count;
|
||||
}
|
||||
@@ -132,8 +134,8 @@ private:
|
||||
public:
|
||||
// METHODS
|
||||
void clear() { m_linkable.clear(); }
|
||||
inline void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); }
|
||||
inline bool isLinkable(const AstNode* nodep) const { return m_linkable.count(nodep) != 0; }
|
||||
void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); }
|
||||
bool isLinkable(const AstNode* nodep) const { return m_linkable.count(nodep) != 0; }
|
||||
} s_linkableTable;
|
||||
|
||||
bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinkable(nodep); }
|
||||
@@ -214,27 +216,27 @@ private:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) override {
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && nodep->brokeLhsMustBeLvalue()
|
||||
&& VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
|
||||
nodep, "Assignment LHS is not an lvalue");
|
||||
}
|
||||
virtual void visit(AstRelease* nodep) override {
|
||||
void visit(AstRelease* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
|
||||
nodep, "Release LHS is not an lvalue");
|
||||
}
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
void visit(AstScope* nodep) override {
|
||||
VL_RESTORER(m_inScope);
|
||||
{
|
||||
m_inScope = true;
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeVarRef* nodep) override {
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
// m_inScope because some Vars have initial variable references without scopes
|
||||
// This might false fire with some debug flags, as not certain we don't have temporary
|
||||
@@ -252,7 +254,7 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override {
|
||||
void visit(AstCFunc* nodep) override {
|
||||
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
|
||||
m_cfuncp = nodep;
|
||||
m_localVars.clear();
|
||||
@@ -270,14 +272,14 @@ private:
|
||||
|
||||
m_cfuncp = nullptr;
|
||||
}
|
||||
virtual void visit(AstNodeIf* nodep) override {
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
// Each branch is a separate local variable scope
|
||||
pushLocalScope();
|
||||
processEnter(nodep);
|
||||
processAndIterate(nodep->condp());
|
||||
if (AstNode* const ifsp = nodep->ifsp()) {
|
||||
if (AstNode* const thensp = nodep->thensp()) {
|
||||
pushLocalScope();
|
||||
processAndIterateList(ifsp);
|
||||
processAndIterateList(thensp);
|
||||
popLocalScope();
|
||||
}
|
||||
if (AstNode* const elsesp = nodep->elsesp()) {
|
||||
@@ -288,21 +290,21 @@ private:
|
||||
processExit(nodep);
|
||||
popLocalScope();
|
||||
}
|
||||
virtual void visit(AstNodeStmt* nodep) override {
|
||||
void visit(AstNodeStmt* nodep) override {
|
||||
// For local variable checking act as if any statement introduces a new scope.
|
||||
// This is aggressive but conservatively correct.
|
||||
pushLocalScope();
|
||||
processAndIterate(nodep);
|
||||
popLocalScope();
|
||||
}
|
||||
virtual void visit(AstVar* nodep) override {
|
||||
void visit(AstVar* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
if (m_cfuncp) {
|
||||
m_localVars.insert(nodep);
|
||||
m_localsStack.back().insert(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
// Process not just iterate
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
@@ -310,7 +312,7 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit BrokenCheckVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~BrokenCheckVisitor() override = default;
|
||||
~BrokenCheckVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -327,7 +329,7 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
|
||||
|
||||
// Mark every node in the tree
|
||||
const uint8_t brokenCntCurrent = s_brokenCntGlobal.get();
|
||||
nodep->foreach<AstNode>([brokenCntCurrent](AstNode* nodep) {
|
||||
nodep->foreach([brokenCntCurrent](AstNode* nodep) {
|
||||
#ifdef VL_LEAK_CHECKS
|
||||
UASSERT_OBJ(s_allocTable.isAllocated(nodep), nodep,
|
||||
"AstNode is in tree, but not allocated");
|
||||
|
||||
+9
-5
@@ -35,6 +35,8 @@
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
class VCtorType final {
|
||||
public:
|
||||
enum en : uint8_t { MODULE, CLASS, COVERAGE };
|
||||
@@ -44,7 +46,7 @@ private:
|
||||
|
||||
public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
inline VCtorType(en _e)
|
||||
constexpr VCtorType(en _e)
|
||||
: m_e{_e} {}
|
||||
bool isClass() const { return m_e == CLASS; }
|
||||
bool isCoverage() const { return m_e == COVERAGE; }
|
||||
@@ -77,7 +79,7 @@ private:
|
||||
if (!preventUnusedStmt.empty()) {
|
||||
funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt});
|
||||
}
|
||||
m_modp->addStmtp(funcp);
|
||||
m_modp->addStmtsp(funcp);
|
||||
m_numStmts = 0;
|
||||
return funcp;
|
||||
}
|
||||
@@ -136,7 +138,7 @@ void V3CCtors::evalAsserts() {
|
||||
funcp->isLoose(true);
|
||||
funcp->slow(false);
|
||||
funcp->ifdef("VL_DEBUG");
|
||||
modp->addStmtp(funcp);
|
||||
modp->addStmtsp(funcp);
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* const varp = VN_CAST(np, Var)) {
|
||||
if (varp->isPrimaryInish() && !varp->isSc()) {
|
||||
@@ -186,7 +188,9 @@ void V3CCtors::cctorsAll() {
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* const varp = VN_CAST(np, Var)) {
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()
|
||||
&& !varp->isParam()) {
|
||||
&& !varp->isParam()
|
||||
&& !(varp->basicp()
|
||||
&& (varp->basicp()->isEvent() || varp->basicp()->isTriggerVec()))) {
|
||||
const auto vrefp = new AstVarRef{varp->fileline(), varp, VAccess::WRITE};
|
||||
var_reset.add(new AstCReset{varp->fileline(), vrefp});
|
||||
}
|
||||
@@ -209,7 +213,7 @@ void V3CCtors::cctorsAll() {
|
||||
// If can be referred to by base pointer, need virtual delete
|
||||
funcp->isVirtual(classp->isExtended());
|
||||
funcp->slow(false);
|
||||
modp->addStmtp(funcp);
|
||||
modp->addStmtsp(funcp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-7
@@ -32,6 +32,8 @@
|
||||
|
||||
#include <set>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
// Visit within a module all nodes and data types they reference, finding
|
||||
@@ -51,13 +53,13 @@ class CUseVisitor final : public VNVisitor {
|
||||
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
|
||||
if (m_didUse.emplace(useType, name).second) {
|
||||
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
|
||||
m_modp->addStmtp(newp);
|
||||
m_modp->addStmtsp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstClassRefDType* nodep) override {
|
||||
void visit(AstClassRefDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
|
||||
// Need to include extends() when we implement, but no need for pointers to know
|
||||
@@ -67,17 +69,17 @@ class CUseVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep->classp()); // This also gets all extend classes
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep) override {
|
||||
void visit(AstNodeDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
|
||||
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstCell* nodep) override {
|
||||
void visit(AstCell* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
// Currently no IMP_INCLUDE because we include __Syms which has them all
|
||||
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name());
|
||||
@@ -90,7 +92,7 @@ public:
|
||||
: m_modp(modp) {
|
||||
iterate(modp);
|
||||
}
|
||||
virtual ~CUseVisitor() override = default;
|
||||
~CUseVisitor() override = default;
|
||||
VL_UNCOPYABLE(CUseVisitor);
|
||||
};
|
||||
|
||||
@@ -106,5 +108,5 @@ void V3CUse::cUseAll() {
|
||||
// for each output file and put under that
|
||||
CUseVisitor{modp};
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("cuse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("cuse", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+15
-15
@@ -45,6 +45,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
#define CASE_OVERLAP_WIDTH 16 // Maximum width we can check for overlaps in
|
||||
#define CASE_BARF 999999 // Magic width when non-constant
|
||||
#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree
|
||||
@@ -57,9 +59,8 @@ private:
|
||||
= nullptr; // Under a CASE value node, if so the relevant case statement
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
virtual void visit(AstNodeCase* nodep) override {
|
||||
void visit(AstNodeCase* nodep) override {
|
||||
if (VN_IS(nodep, Case) && VN_AS(nodep, Case)->casex()) {
|
||||
nodep->v3warn(CASEX, "Suggest casez (with ?'s) in place of casex (with X's)");
|
||||
}
|
||||
@@ -86,7 +87,7 @@ private:
|
||||
m_caseExprp = nullptr;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstConst* nodep) override {
|
||||
void visit(AstConst* nodep) override {
|
||||
// See also neverItem
|
||||
if (m_caseExprp && nodep->num().isFourState()) {
|
||||
if (VN_IS(m_caseExprp, GenCase)) {
|
||||
@@ -107,12 +108,12 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CaseLintVisitor(AstNodeCase* nodep) { iterate(nodep); }
|
||||
virtual ~CaseLintVisitor() override = default;
|
||||
~CaseLintVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -138,7 +139,6 @@ private:
|
||||
std::array<AstNode*, 1 << CASE_OVERLAP_WIDTH> m_valueItem;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
bool isCaseTreeFast(AstCase* nodep) {
|
||||
int width = 0;
|
||||
@@ -245,7 +245,7 @@ private:
|
||||
// Convert valueItem from AstCaseItem* to the expression
|
||||
// Not done earlier, as we may now have a nullptr because it's just a ";" NOP branch
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
m_valueItem[i] = VN_AS(m_valueItem[i], CaseItem)->bodysp();
|
||||
m_valueItem[i] = VN_AS(m_valueItem[i], CaseItem)->stmtsp();
|
||||
}
|
||||
return true; // All is fine
|
||||
}
|
||||
@@ -346,7 +346,7 @@ private:
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
if (!itemp->condsp()) {
|
||||
// Default clause. Just make true, we'll optimize it away later
|
||||
itemp->condsp(new AstConst(itemp->fileline(), AstConst::BitTrue()));
|
||||
itemp->addCondsp(new AstConst(itemp->fileline(), AstConst::BitTrue()));
|
||||
hadDefault = true;
|
||||
} else {
|
||||
// Expressioned clause
|
||||
@@ -397,7 +397,7 @@ private:
|
||||
}
|
||||
}
|
||||
// Replace expression in tree
|
||||
itemp->condsp(ifexprp);
|
||||
itemp->addCondsp(ifexprp);
|
||||
}
|
||||
}
|
||||
VL_DO_DANGLING(cexprp->deleteTree(), cexprp);
|
||||
@@ -420,7 +420,7 @@ private:
|
||||
AstIf* itemnextp = nullptr;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
AstNode* const istmtsp = itemp->bodysp(); // Maybe null -- no action.
|
||||
AstNode* const istmtsp = itemp->stmtsp(); // Maybe null -- no action.
|
||||
if (istmtsp) istmtsp->unlinkFrBackWithNext();
|
||||
// Expressioned clause
|
||||
AstNode* const ifexprp = itemp->condsp()->unlinkFrBack();
|
||||
@@ -452,7 +452,7 @@ private:
|
||||
if (itemnextp) {
|
||||
itemnextp->addElsesp(newp);
|
||||
} else {
|
||||
groupnextp->addIfsp(newp); // First in a new group
|
||||
groupnextp->addThensp(newp); // First in a new group
|
||||
}
|
||||
itemnextp = newp;
|
||||
}
|
||||
@@ -493,7 +493,7 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstCase* nodep) override {
|
||||
void visit(AstCase* nodep) override {
|
||||
V3Case::caseLint(nodep);
|
||||
iterateChildren(nodep);
|
||||
if (debug() >= 9) nodep->dumpTree(cout, " case_old: ");
|
||||
@@ -510,7 +510,7 @@ private:
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
if (VN_IS(nodep, Always)) m_alwaysp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -521,7 +521,7 @@ public:
|
||||
for (auto& itr : m_valueItem) itr = nullptr;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CaseVisitor() override {
|
||||
~CaseVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
|
||||
V3Stats::addStat("Optimizations, Cases complex", m_statCaseSlow);
|
||||
}
|
||||
@@ -533,7 +533,7 @@ public:
|
||||
void V3Case::caseAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CaseVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("case", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("case", 0, dumpTree() >= 3);
|
||||
}
|
||||
void V3Case::caseLint(AstNodeCase* nodep) {
|
||||
UINFO(4, __FUNCTION__ << ": " << endl);
|
||||
|
||||
+21
-18
@@ -47,6 +47,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Cast state, as a visitor of each AstNode
|
||||
|
||||
@@ -60,7 +62,6 @@ private:
|
||||
// STATE
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
|
||||
UINFO(4, " NeedCast " << nodep << endl);
|
||||
@@ -111,25 +112,25 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeUniop* nodep) override {
|
||||
void visit(AstNodeUniop* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
|
||||
}
|
||||
virtual void visit(AstNodeBiop* nodep) override {
|
||||
void visit(AstNodeBiop* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
|
||||
}
|
||||
virtual void visit(AstNodeTriop* nodep) override {
|
||||
void visit(AstNodeTriop* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
|
||||
if (nodep->sizeMattersThs()) ensureCast(nodep->thsp());
|
||||
}
|
||||
virtual void visit(AstNodeQuadop* nodep) override {
|
||||
void visit(AstNodeQuadop* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1()
|
||||
| nodep->fhsp()->user1());
|
||||
@@ -138,12 +139,12 @@ private:
|
||||
if (nodep->sizeMattersThs()) ensureCast(nodep->thsp());
|
||||
if (nodep->sizeMattersFhs()) ensureCast(nodep->fhsp());
|
||||
}
|
||||
virtual void visit(AstCCast* nodep) override {
|
||||
void visit(AstCCast* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureLower32Cast(nodep);
|
||||
nodep->user1(1);
|
||||
}
|
||||
virtual void visit(AstNegate* nodep) override {
|
||||
void visit(AstNegate* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->lhsp()->widthMin() == 1) {
|
||||
@@ -155,18 +156,20 @@ private:
|
||||
ensureCast(nodep->lhsp());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) override {
|
||||
void visit(AstVarRef* nodep) override {
|
||||
const AstNode* const backp = nodep->backp();
|
||||
if (nodep->access().isReadOnly() && !VN_IS(backp, CCast) && VN_IS(backp, NodeMath)
|
||||
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, RedXor) && backp->width()
|
||||
&& castSize(nodep) != castSize(nodep->varp())) {
|
||||
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, RedXor)
|
||||
&& (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec()
|
||||
&& !nodep->varp()->basicp()->isForkSync())
|
||||
&& backp->width() && castSize(nodep) != castSize(nodep->varp())) {
|
||||
// Cast vars to IData first, else below has upper bits wrongly set
|
||||
// CData x=3; out = (QData)(x<<30);
|
||||
insertCast(nodep, castSize(nodep));
|
||||
}
|
||||
nodep->user1(1);
|
||||
}
|
||||
virtual void visit(AstConst* nodep) override {
|
||||
void visit(AstConst* nodep) override {
|
||||
// Constants are of unknown size if smaller than 33 bits, because
|
||||
// we're too lazy to wrap every constant in the universe in
|
||||
// ((IData)#).
|
||||
@@ -174,29 +177,29 @@ private:
|
||||
}
|
||||
|
||||
// Null dereference protection
|
||||
virtual void visit(AstNullCheck* nodep) override {
|
||||
void visit(AstNullCheck* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
}
|
||||
virtual void visit(AstCMethodCall* nodep) override {
|
||||
void visit(AstCMethodCall* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureNullChecked(nodep->fromp());
|
||||
}
|
||||
virtual void visit(AstMemberSel* nodep) override {
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureNullChecked(nodep->fromp());
|
||||
}
|
||||
|
||||
// NOPs
|
||||
virtual void visit(AstVar*) override {}
|
||||
void visit(AstVar*) override {}
|
||||
|
||||
//--------------------
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CastVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~CastVisitor() override = default;
|
||||
~CastVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -205,5 +208,5 @@ public:
|
||||
void V3Cast::castAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CastVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("cast", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
V3Global::dumpCheckGlobalTree("cast", 0, dumpTree() >= 3);
|
||||
}
|
||||
|
||||
+46
-49
@@ -38,13 +38,14 @@
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
constexpr int CDC_WEIGHT_ASYNC = 0x1000; // Weight for edges that feed async logic
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CdcBaseVisitor VL_NOT_FINAL : public VNVisitor {
|
||||
public:
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -67,9 +68,9 @@ public:
|
||||
, m_srcDomainSet{false}
|
||||
, m_dstDomainSet{false}
|
||||
, m_asyncPath{false} {}
|
||||
virtual ~CdcEitherVertex() override = default;
|
||||
~CdcEitherVertex() override = default;
|
||||
// ACCESSORS
|
||||
virtual FileLine* fileline() const override { return nodep()->fileline(); }
|
||||
FileLine* fileline() const override { return nodep()->fileline(); }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
AstSenTree* srcDomainp() const { return m_srcDomainp; }
|
||||
@@ -93,11 +94,11 @@ public:
|
||||
CdcVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: CdcEitherVertex{graphp, scopep, varScp}
|
||||
, m_varScp{varScp} {}
|
||||
virtual ~CdcVarVertex() override = default;
|
||||
~CdcVarVertex() override = default;
|
||||
// ACCESSORS
|
||||
AstVarScope* varScp() const { return m_varScp; }
|
||||
virtual string name() const override { return (cvtToHex(m_varScp) + " " + varScp()->name()); }
|
||||
virtual string dotColor() const override {
|
||||
string name() const override { return (cvtToHex(m_varScp) + " " + varScp()->name()); }
|
||||
string dotColor() const override {
|
||||
return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue";
|
||||
}
|
||||
int cntAsyncRst() const { return m_cntAsyncRst; }
|
||||
@@ -118,12 +119,10 @@ public:
|
||||
srcDomainp(sensenodep);
|
||||
dstDomainp(sensenodep);
|
||||
}
|
||||
virtual ~CdcLogicVertex() override = default;
|
||||
~CdcLogicVertex() override = default;
|
||||
// ACCESSORS
|
||||
virtual string name() const override {
|
||||
return (cvtToHex(nodep()) + "@" + scopep()->prettyName());
|
||||
}
|
||||
virtual string dotColor() const override { return hazard() ? "black" : "yellow"; }
|
||||
string name() const override { return (cvtToHex(nodep()) + "@" + scopep()->prettyName()); }
|
||||
string dotColor() const override { return hazard() ? "black" : "yellow"; }
|
||||
bool hazard() const { return m_hazard; }
|
||||
void setHazard(AstNode* nodep) {
|
||||
m_hazard = true;
|
||||
@@ -144,7 +143,7 @@ private:
|
||||
std::ofstream* const m_ofp = nullptr; // Output file
|
||||
string m_prefix;
|
||||
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
*m_ofp << m_prefix;
|
||||
if (nodep->user3()) {
|
||||
*m_ofp << " %%";
|
||||
@@ -171,7 +170,7 @@ public:
|
||||
, m_prefix{prefix} {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CdcDumpVisitor() override = default;
|
||||
~CdcDumpVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -181,7 +180,7 @@ private:
|
||||
int m_maxLineno = 0;
|
||||
size_t m_maxFilenameLen = 0;
|
||||
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
void visit(AstNode* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Keeping line+filename lengths separate is much faster than calling ascii().length()
|
||||
if (nodep->fileline()->lineno() >= m_maxLineno) {
|
||||
@@ -195,7 +194,7 @@ private:
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CdcWidthVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
virtual ~CdcWidthVisitor() override = default;
|
||||
~CdcWidthVisitor() override = default;
|
||||
// ACCESSORS
|
||||
int maxWidth() const {
|
||||
size_t width = 1;
|
||||
@@ -325,13 +324,11 @@ private:
|
||||
|
||||
void analyze() {
|
||||
UINFO(3, __FUNCTION__ << ": " << endl);
|
||||
// if (debug() > 6) m_graph.dump();
|
||||
if (debug() > 6) m_graph.dumpDotFilePrefixed("cdc_pre");
|
||||
if (dumpGraph() > 6) m_graph.dumpDotFilePrefixed("cdc_pre");
|
||||
// This will MAX across edge weights
|
||||
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
|
||||
//
|
||||
m_graph.removeRedundantEdges(
|
||||
&V3GraphEdge::followAlwaysTrue); // This will MAX across edge weights
|
||||
//
|
||||
m_graph.dumpDotFilePrefixed("cdc_simp");
|
||||
if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("cdc_simp");
|
||||
//
|
||||
analyzeReset();
|
||||
}
|
||||
@@ -628,33 +625,33 @@ private:
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) override {
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScope* nodep) override {
|
||||
void visit(AstScope* nodep) override {
|
||||
UINFO(4, " SCOPE " << nodep << endl);
|
||||
m_scopep = nodep;
|
||||
m_logicVertexp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
m_scopep = nullptr;
|
||||
}
|
||||
virtual void visit(AstActive* nodep) override {
|
||||
void visit(AstActive* nodep) override {
|
||||
// Create required blocks and add to module
|
||||
UINFO(4, " BLOCK " << nodep << endl);
|
||||
AstNode::user2ClearTree();
|
||||
m_domainp = nodep->sensesp();
|
||||
if (!m_domainp || m_domainp->hasCombo()
|
||||
|| m_domainp->hasClocked()) { // IE not hasSettle/hasInitial
|
||||
// Ignore static initializers, initial and final blocks
|
||||
if (!m_domainp || m_domainp->hasCombo() || m_domainp->hasClocked()) {
|
||||
iterateNewStmt(nodep);
|
||||
}
|
||||
m_domainp = nullptr;
|
||||
AstNode::user2ClearTree();
|
||||
}
|
||||
virtual void visit(AstNodeVarRef* nodep) override {
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
if (m_scopep) {
|
||||
UASSERT_OBJ(m_logicVertexp, nodep, "Var ref not under a logic block");
|
||||
AstVarScope* const varscp = nodep->varScopep();
|
||||
@@ -682,53 +679,53 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAssignDly* nodep) override {
|
||||
void visit(AstAssignDly* nodep) override {
|
||||
m_inDly = true;
|
||||
iterateChildren(nodep);
|
||||
m_inDly = false;
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) override {
|
||||
void visit(AstSenItem* nodep) override {
|
||||
m_inSenItem = true;
|
||||
iterateChildren(nodep);
|
||||
m_inSenItem = false;
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) override { iterateNewStmt(nodep); }
|
||||
virtual void visit(AstAlwaysPublic* nodep) override {
|
||||
void visit(AstAlways* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAlwaysPublic* nodep) override {
|
||||
// CDC doesn't care about public variables
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) override { iterateNewStmt(nodep); }
|
||||
virtual void visit(AstAssignAlias* nodep) override { iterateNewStmt(nodep); }
|
||||
virtual void visit(AstAssignW* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstCFunc* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAssignAlias* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAssignW* nodep) override { iterateNewStmt(nodep); }
|
||||
|
||||
// Math that shouldn't cause us to clear hazard
|
||||
virtual void visit(AstConst*) override {}
|
||||
virtual void visit(AstReplicate* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstConcat* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstNot* nodep) override { iterateChildren(nodep); }
|
||||
virtual void visit(AstSel* nodep) override {
|
||||
void visit(AstConst*) override {}
|
||||
void visit(AstReplicate* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstConcat* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNot* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstSel* nodep) override {
|
||||
if (!VN_IS(nodep->lsbp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeSel* nodep) override {
|
||||
void visit(AstNodeSel* nodep) override {
|
||||
if (!VN_IS(nodep->bitp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Ignores
|
||||
virtual void visit(AstInitial*) override {}
|
||||
virtual void visit(AstInitialAutomatic*) override {}
|
||||
virtual void visit(AstInitialStatic*) override {}
|
||||
virtual void visit(AstTraceDecl*) override {}
|
||||
virtual void visit(AstCoverToggle*) override {}
|
||||
virtual void visit(AstNodeDType*) override {}
|
||||
void visit(AstInitial*) override {}
|
||||
void visit(AstInitialAutomatic*) override {}
|
||||
void visit(AstInitialStatic*) override {}
|
||||
void visit(AstTraceDecl*) override {}
|
||||
void visit(AstCoverToggle*) override {}
|
||||
void visit(AstNodeDType*) override {}
|
||||
|
||||
//--------------------
|
||||
// Default
|
||||
virtual void visit(AstNodeMath* nodep) override {
|
||||
void visit(AstNodeMath* nodep) override {
|
||||
setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -754,7 +751,7 @@ public:
|
||||
*m_ofp << '\n';
|
||||
}
|
||||
}
|
||||
virtual ~CdcVisitor() override {
|
||||
~CdcVisitor() override {
|
||||
if (m_ofp) VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Add temporaries, such as for changed nodes
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2022 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
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Changed's Transformations:
|
||||
//
|
||||
// Each module:
|
||||
// Each combo block
|
||||
// For each variable that comes from combo block and is generated AFTER a usage
|
||||
// Add __Vlast_{var} to local section, init to current value (just use array?)
|
||||
// Change = if any var != last.
|
||||
// If a signal is used as a clock in this module or any
|
||||
// module *below*, and it isn't a input to this module,
|
||||
// we need to indicate a new clock has been created.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Changed.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
//######################################################################
|
||||
|
||||
class ChangedState final {
|
||||
public:
|
||||
// STATE
|
||||
AstNodeModule* m_topModp = nullptr; // Top module
|
||||
AstScope* m_scopetopp = nullptr; // Scope under TOPSCOPE
|
||||
AstCFunc* m_chgFuncp = nullptr; // Change function we're building
|
||||
AstCFunc* m_tlChgFuncp = nullptr; // Top level change function we're building
|
||||
int m_numStmts = 0; // Number of statements added to m_chgFuncp
|
||||
int m_funcNum = 0; // Number of change functions emitted
|
||||
bool m_madeTopChg = false;
|
||||
|
||||
ChangedState() = default;
|
||||
~ChangedState() = default;
|
||||
|
||||
void maybeCreateChgFuncp() {
|
||||
maybeCreateTopChg();
|
||||
maybeCreateMidChg();
|
||||
}
|
||||
void maybeCreateTopChg() {
|
||||
if (m_madeTopChg) return;
|
||||
m_madeTopChg = true;
|
||||
v3Global.rootp()->changeRequest(true);
|
||||
|
||||
// Create a wrapper change detection function that calls each change detection function
|
||||
m_tlChgFuncp
|
||||
= new AstCFunc{m_scopetopp->fileline(), "_change_request", m_scopetopp, "QData"};
|
||||
m_tlChgFuncp->isStatic(false);
|
||||
m_tlChgFuncp->isLoose(true);
|
||||
m_tlChgFuncp->declPrivate(true);
|
||||
m_scopetopp->addActivep(m_tlChgFuncp);
|
||||
// Each change detection function needs at least one AstChangeDet
|
||||
// to ensure that V3EmitC outputs the necessary code.
|
||||
maybeCreateMidChg();
|
||||
m_chgFuncp->addStmtsp(new AstChangeDet{m_scopetopp->fileline(), nullptr, nullptr});
|
||||
}
|
||||
void maybeCreateMidChg() {
|
||||
// Don't create an extra function call if splitting is disabled
|
||||
if (!v3Global.opt.outputSplitCFuncs()) {
|
||||
m_chgFuncp = m_tlChgFuncp;
|
||||
return;
|
||||
}
|
||||
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
m_chgFuncp
|
||||
= new AstCFunc{m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
|
||||
m_scopetopp, "QData"};
|
||||
m_chgFuncp->isStatic(false);
|
||||
m_chgFuncp->isLoose(true);
|
||||
m_chgFuncp->declPrivate(true);
|
||||
m_scopetopp->addActivep(m_chgFuncp);
|
||||
|
||||
// Add a top call to it
|
||||
AstCCall* const callp = new AstCCall{m_scopetopp->fileline(), m_chgFuncp};
|
||||
|
||||
if (!m_tlChgFuncp->stmtsp()) {
|
||||
m_tlChgFuncp->addStmtsp(new AstCReturn{m_scopetopp->fileline(), callp});
|
||||
} else {
|
||||
AstCReturn* const returnp = VN_AS(m_tlChgFuncp->stmtsp(), CReturn);
|
||||
UASSERT_OBJ(returnp, m_scopetopp, "Lost CReturn in top change function");
|
||||
// This is currently using AstLogOr which will shortcut the
|
||||
// evaluation if any function returns true. This is likely what
|
||||
// we want and is similar to the logic already in use inside
|
||||
// V3EmitC, however, it also means that verbose logging may
|
||||
// miss to print change detect variables.
|
||||
AstNode* const newp = new AstCReturn{
|
||||
m_scopetopp->fileline(),
|
||||
new AstLogOr{m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()}};
|
||||
returnp->replaceWith(newp);
|
||||
VL_DO_DANGLING(returnp->deleteTree(), returnp);
|
||||
}
|
||||
m_numStmts = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Utility visitor to find elements to be compared
|
||||
|
||||
class ChangedInsertVisitor final : public VNVisitor {
|
||||
private:
|
||||
// STATE
|
||||
ChangedState& m_state; // Shared state across visitors
|
||||
AstVarScope* const m_vscp; // Original (non-change) variable we're change-detecting
|
||||
AstVarScope* m_newvscp = nullptr; // New (change detect) variable we're change-detecting
|
||||
AstNode* m_varEqnp = nullptr; // Original var's equation to get var value
|
||||
AstNode* m_newLvEqnp = nullptr; // New var's equation to read value
|
||||
AstNode* m_newRvEqnp = nullptr; // New var's equation to set value
|
||||
uint32_t m_detects = 0; // # detects created
|
||||
|
||||
// CONSTANTS
|
||||
// How many indexes before error. Ok to increase this, but may result in much slower model
|
||||
static constexpr uint32_t DETECTARRAY_MAX_INDEXES = 256;
|
||||
|
||||
void newChangeDet() {
|
||||
if (++m_detects > DETECTARRAY_MAX_INDEXES) {
|
||||
m_vscp->v3warn(E_DETECTARRAY,
|
||||
"Unsupported: Can't detect more than "
|
||||
<< DETECTARRAY_MAX_INDEXES
|
||||
<< " array indexes (probably with UNOPTFLAT warning suppressed): "
|
||||
<< m_vscp->prettyName() << '\n'
|
||||
<< m_vscp->warnMore()
|
||||
<< "... Could recompile with DETECTARRAY_MAX_INDEXES increased");
|
||||
return;
|
||||
}
|
||||
m_state.maybeCreateChgFuncp();
|
||||
|
||||
AstChangeDet* const changep = new AstChangeDet{
|
||||
m_vscp->fileline(), m_varEqnp->cloneTree(true), m_newRvEqnp->cloneTree(true)};
|
||||
m_state.m_chgFuncp->addStmtsp(changep);
|
||||
AstAssign* const initp = new AstAssign{m_vscp->fileline(), m_newLvEqnp->cloneTree(true),
|
||||
m_varEqnp->cloneTree(true)};
|
||||
m_state.m_chgFuncp->addFinalsp(initp);
|
||||
|
||||
// Later code will expand words which adds to GCC compile time,
|
||||
// so add penalty based on word width also
|
||||
m_state.m_numStmts += initp->nodeCount() + m_varEqnp->widthWords();
|
||||
}
|
||||
|
||||
virtual void visit(AstBasicDType*) override { //
|
||||
newChangeDet();
|
||||
}
|
||||
virtual void visit(AstPackArrayDType*) override { //
|
||||
newChangeDet();
|
||||
}
|
||||
virtual void visit(AstUnpackArrayDType* nodep) override {
|
||||
for (int index = 0; index < nodep->elementsConst(); ++index) {
|
||||
VL_RESTORER(m_varEqnp);
|
||||
VL_RESTORER(m_newLvEqnp);
|
||||
VL_RESTORER(m_newRvEqnp);
|
||||
{
|
||||
m_varEqnp = new AstArraySel{nodep->fileline(), m_varEqnp->cloneTree(true), index};
|
||||
m_newLvEqnp
|
||||
= new AstArraySel{nodep->fileline(), m_newLvEqnp->cloneTree(true), index};
|
||||
m_newRvEqnp
|
||||
= new AstArraySel{nodep->fileline(), m_newRvEqnp->cloneTree(true), index};
|
||||
|
||||
iterate(nodep->subDTypep()->skipRefp());
|
||||
|
||||
m_varEqnp->deleteTree();
|
||||
m_newLvEqnp->deleteTree();
|
||||
m_newRvEqnp->deleteTree();
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeUOrStructDType* nodep) override {
|
||||
if (nodep->packedUnsup()) {
|
||||
newChangeDet();
|
||||
} else {
|
||||
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-class-");
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||
" (probably with UNOPTFLAT warning suppressed): "
|
||||
<< m_vscp->varp()->prettyNameQ());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-general-");
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||
" (probably with UNOPTFLAT warning suppressed): "
|
||||
<< m_vscp->varp()->prettyNameQ());
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
ChangedInsertVisitor(AstVarScope* vscp, ChangedState& state)
|
||||
: m_state{state}
|
||||
, m_vscp{vscp} {
|
||||
// DPI export trigger should never need change detect. See similar assertions in V3Order
|
||||
// (OrderVisitor::nodeMarkCircular), and V3GenClk (GenClkRenameVisitor::genInpClk).
|
||||
UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp,
|
||||
"DPI export trigger should not need change detect");
|
||||
{
|
||||
AstVar* const varp = m_vscp->varp();
|
||||
const string newvarname{"__Vchglast__" + m_vscp->scopep()->nameDotless() + "__"
|
||||
+ varp->shortName()};
|
||||
// Create: VARREF(_last)
|
||||
// ASSIGN(VARREF(_last), VARREF(var))
|
||||
// ...
|
||||
// CHANGEDET(VARREF(_last), VARREF(var))
|
||||
AstVar* const newvarp
|
||||
= new AstVar{varp->fileline(), VVarType::MODULETEMP, newvarname, varp};
|
||||
m_state.m_topModp->addStmtp(newvarp);
|
||||
m_newvscp = new AstVarScope{m_vscp->fileline(), m_state.m_scopetopp, newvarp};
|
||||
m_state.m_scopetopp->addVarp(m_newvscp);
|
||||
|
||||
m_varEqnp = new AstVarRef{m_vscp->fileline(), m_vscp, VAccess::READ};
|
||||
m_newLvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::WRITE};
|
||||
m_newRvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::READ};
|
||||
}
|
||||
iterate(vscp->dtypep()->skipRefp());
|
||||
m_varEqnp->deleteTree();
|
||||
m_newLvEqnp->deleteTree();
|
||||
m_newRvEqnp->deleteTree();
|
||||
}
|
||||
virtual ~ChangedInsertVisitor() override = default;
|
||||
VL_UNCOPYABLE(ChangedInsertVisitor);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Changed class functions
|
||||
|
||||
void V3Changed::changedAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
|
||||
ChangedState state;
|
||||
state.m_scopetopp = nodep->topScopep()->scopep();
|
||||
state.m_topModp = nodep->topModulep();
|
||||
nodep->foreach<AstVarScope>([&state](AstVarScope* vscp) {
|
||||
if (vscp->isCircular()) {
|
||||
vscp->v3warn(IMPERFECTSCH,
|
||||
"Imperfect scheduling of variable: " << vscp->prettyNameQ());
|
||||
ChangedInsertVisitor{vscp, state};
|
||||
}
|
||||
});
|
||||
|
||||
V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user