mirror of
https://github.com/verilator/verilator.git
synced 2026-10-02 16:13:34 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57c816f906 | ||
|
|
8f43e74288 | ||
|
|
82e1dbe162 | ||
|
|
cef3960a4e | ||
|
|
783b7ecdab | ||
|
|
8bd1c63b32 | ||
|
|
e6fb7e970d | ||
|
|
891cc0f9b6 | ||
|
|
10fbe74cef | ||
|
|
05d04a3959 | ||
|
|
19f7279542 | ||
|
|
aa608472ae | ||
|
|
26ed2a06a0 | ||
|
|
07013da13d | ||
|
|
d840c612d4 | ||
|
|
188cf85b3f | ||
|
|
c52ba28dd0 | ||
|
|
10c1653e72 | ||
|
|
2fbd41e13b | ||
|
|
96857c5f95 | ||
|
|
131eb319e0 | ||
|
|
6e589377f4 | ||
|
|
2c84b37bf7 | ||
|
|
0d67caff77 | ||
|
|
3dde57d539 | ||
|
|
ec2e3ec0e4 | ||
|
|
9fe459c820 | ||
|
|
823e0723fb | ||
|
|
8bd6d7c5b1 | ||
|
|
839a8fa4d9 | ||
|
|
24917a187a | ||
|
|
6c6f03cf7c | ||
|
|
5676443139 | ||
|
|
e77d847671 | ||
|
|
0baee84e96 | ||
|
|
c446cc2596 | ||
|
|
b66c4153b1 | ||
|
|
d72f1b89fc | ||
|
|
b5b278d072 | ||
|
|
cb5466a90b | ||
|
|
4de25369da | ||
|
|
cdd8953085 | ||
|
|
2f2f0164ff | ||
|
|
6e977e1024 | ||
|
|
70b11f91b4 | ||
|
|
014301587f | ||
|
|
139e93d371 | ||
|
|
fb1fc46b06 | ||
|
|
9882ab6c67 | ||
|
|
1a1f919882 | ||
|
|
91227d26bb | ||
|
|
11b5dae88d | ||
|
|
0c7d485417 | ||
|
|
c40e34b134 | ||
|
|
6cdf8da3f7 | ||
|
|
c65265477f | ||
|
|
de77f1ee5c | ||
|
|
c8c980c49d | ||
|
|
ad329612fd | ||
|
|
c324895cbc | ||
|
|
679a4314dc | ||
|
|
36932d31c4 | ||
|
|
ed25aaaad9 | ||
|
|
147aa41ba5 | ||
|
|
5b7e32581b | ||
|
|
2f90fb2bec | ||
|
|
8c480fd39e | ||
|
|
ffbbd438ae | ||
|
|
96ee81fa3f | ||
|
|
3d2399ea0f | ||
|
|
6d5dde8645 | ||
|
|
048c9f3fe2 | ||
|
|
373265752f | ||
|
|
5e6519c1b0 | ||
|
|
ccdb52c1d9 | ||
|
|
b4b74d72f0 | ||
|
|
ca6ab584d0 | ||
|
|
799a502cac | ||
|
|
8d1570db28 | ||
|
|
2a30bbe493 | ||
|
|
cf6566b9bc | ||
|
|
c3e19f2821 | ||
|
|
e24197fd16 | ||
|
|
63db60f646 | ||
|
|
e66b28823d | ||
|
|
77bd565ef6 | ||
|
|
00d63883f8 | ||
|
|
10dd675371 | ||
|
|
2daa32b98b | ||
|
|
e8e7912871 | ||
|
|
90079c2974 | ||
|
|
967a8530dd | ||
|
|
ef4794e36d | ||
|
|
7c7c92d2dd | ||
|
|
95f3dd0535 | ||
|
|
768b78e7d0 | ||
|
|
4370254e73 | ||
|
|
436f72582b | ||
|
|
af3aba7821 | ||
|
|
7479db6ffc | ||
|
|
e9cc2786b7 | ||
|
|
cbdee5a804 | ||
|
|
0f66262fa1 | ||
|
|
6c80457262 | ||
|
|
16008acdf1 | ||
|
|
5447ed2629 | ||
|
|
9eba61018a | ||
|
|
bc97319402 | ||
|
|
709db5c648 | ||
|
|
b752faa107 | ||
|
|
8d512c3187 | ||
|
|
2d9bc73709 | ||
|
|
b0942ed8c7 | ||
|
|
e6b0bdd4df | ||
|
|
b1c1aa53a9 | ||
|
|
f79749e7b2 | ||
|
|
1a77030c9b | ||
|
|
817ade27fb | ||
|
|
4afa14b612 | ||
|
|
9caa79a7ea | ||
|
|
446f21d2a0 | ||
|
|
06521f6cf1 | ||
|
|
75336670d7 | ||
|
|
106a9fe67f | ||
|
|
a5eccc199b | ||
|
|
837d9f9d77 | ||
|
|
959387b69f | ||
|
|
fd0703b8c8 | ||
|
|
3ff608b9cb | ||
|
|
edc0a0e982 | ||
|
|
1549ff9185 | ||
|
|
3454641244 | ||
|
|
5dedf25e3b | ||
|
|
7214c180ae | ||
|
|
aa0ef554b3 | ||
|
|
a6015863d8 | ||
|
|
5a694ccb8a | ||
|
|
f7d09c671a | ||
|
|
4de1b22672 | ||
|
|
4bdda3f240 | ||
|
|
ced1dfeffd | ||
|
|
0774f825cf | ||
|
|
23fe5c1b93 | ||
|
|
bd4afeea69 | ||
|
|
3ee9b33f8a | ||
|
|
5be0813893 | ||
|
|
f94c1b32e5 | ||
|
|
ff4923cf67 | ||
|
|
5788bc8048 | ||
|
|
5749662194 | ||
|
|
d5d1cc47e0 | ||
|
|
da043ca16d | ||
|
|
9aa90569bf | ||
|
|
294d78e0e2 | ||
|
|
8182c3fe2c | ||
|
|
6f7bc38088 | ||
|
|
dcbc0ccfdc | ||
|
|
399319dc1c | ||
|
|
35d6da391b | ||
|
|
fcbd7c87af | ||
|
|
97feba6898 | ||
|
|
9249ffdb84 | ||
|
|
71df9e3381 | ||
|
|
3c964147be | ||
|
|
54b4b6b378 | ||
|
|
cff37f0775 | ||
|
|
8b1cc3b303 | ||
|
|
65b6121186 | ||
|
|
3d30527860 | ||
|
|
24a9f7c650 | ||
|
|
242f661644 | ||
|
|
32019d2bc4 | ||
|
|
b252bb048f | ||
|
|
ba47c01a01 | ||
|
|
51266898ec | ||
|
|
f9164ab0d2 | ||
|
|
c43e707f43 | ||
|
|
85b7f828b3 | ||
|
|
7005a65d32 | ||
|
|
776d4e6ac2 | ||
|
|
1c88c3083c | ||
|
|
4522834f7a | ||
|
|
9ee5fd0585 | ||
|
|
63b2dbb827 | ||
|
|
5bee8ef15b | ||
|
|
e7de3d368f | ||
|
|
d45deccc0a | ||
|
|
d5b03ab721 | ||
|
|
d1ff81a41c | ||
|
|
43fa6858f6 | ||
|
|
db7eb45433 | ||
|
|
21c01ba97b | ||
|
|
b517fb5ee3 | ||
|
|
9dbc669199 | ||
|
|
4f13c4d1bb | ||
|
|
7d2d32420a | ||
|
|
48e98ea2bd | ||
|
|
01d0f0327b | ||
|
|
0dc887720d | ||
|
|
e08bbcdb07 | ||
|
|
70d26ec123 | ||
|
|
a06f260f42 | ||
|
|
d42f76c346 | ||
|
|
66c3d6d2d1 | ||
|
|
81affad2dc | ||
|
|
2488af9066 | ||
|
|
5cf9d78516 | ||
|
|
3a9eeabdb2 | ||
|
|
ac4315e145 | ||
|
|
5094e94df1 | ||
|
|
4b7b185d05 | ||
|
|
6cb0335d28 | ||
|
|
c248318958 | ||
|
|
c3e5db5f04 | ||
|
|
058233fc87 | ||
|
|
186c851695 | ||
|
|
5aa36357f6 | ||
|
|
191d30cf1c | ||
|
|
d8c2ecaaf8 | ||
|
|
a7b0551d23 | ||
|
|
dc34968fe7 | ||
|
|
45f064f7cb | ||
|
|
545caba720 | ||
|
|
0e24f36fef | ||
|
|
db7935faf3 | ||
|
|
9cc218db3e | ||
|
|
15f8ebc562 | ||
|
|
205cae963b | ||
|
|
df2746de71 | ||
|
|
7f471d862e | ||
|
|
338acabe2b | ||
|
|
4f1f487ae4 | ||
|
|
77502aeb97 | ||
|
|
ba82d43ca1 | ||
|
|
e9135598b3 | ||
|
|
1c0739db10 | ||
|
|
dfd3907787 | ||
|
|
1069652701 | ||
|
|
70b82f1aec | ||
|
|
5573bdb219 | ||
|
|
cf68578261 | ||
|
|
f407d442d4 | ||
|
|
7f12ad9d91 | ||
|
|
5efe9367d2 | ||
|
|
cfce92645f | ||
|
|
519792d02b | ||
|
|
bfa1f2d7ce | ||
|
|
42beaf467c | ||
|
|
8303938d0e | ||
|
|
0ece155418 | ||
|
|
4f7e155e59 | ||
|
|
ff324625e4 | ||
|
|
fd7515b046 | ||
|
|
607f853bc7 | ||
|
|
09fb1174f1 | ||
|
|
542eaab3dd | ||
|
|
a301a498f5 | ||
|
|
d66e749ec8 | ||
|
|
c8ca54ea31 | ||
|
|
5704552053 | ||
|
|
426069a4dd | ||
|
|
19d0aabe7a | ||
|
|
fb0d735f68 | ||
|
|
167a30be1c | ||
|
|
5982528274 | ||
|
|
de3095e3b4 | ||
|
|
8e0682f442 | ||
|
|
140994d2c4 | ||
|
|
950e29d56e | ||
|
|
65667356b6 | ||
|
|
6b393e9d7c | ||
|
|
4818130e4f | ||
|
|
9da3aacd08 | ||
|
|
729f8b9334 | ||
|
|
279216048b | ||
|
|
2ce7a348df | ||
|
|
46f719ceaa | ||
|
|
949be301de | ||
|
|
be429a5800 | ||
|
|
e7714e0902 | ||
|
|
4835ed6967 | ||
|
|
0198a3fc52 | ||
|
|
e095bf1af0 | ||
|
|
d5de67c6dc | ||
|
|
2267db093f | ||
|
|
5aecfa98a1 | ||
|
|
485e230dd8 | ||
|
|
befb415f27 | ||
|
|
c0490627bf | ||
|
|
d269fbb446 | ||
|
|
444020f7c7 | ||
|
|
6188083baa | ||
|
|
c2a524d80b | ||
|
|
e6f5a0495f | ||
|
|
0606a29f13 | ||
|
|
c6052830e1 | ||
|
|
4bb876aee5 | ||
|
|
a3640c1767 | ||
|
|
250c6950cf | ||
|
|
76f5de6e54 | ||
|
|
1a1c5c5d44 | ||
|
|
4ae80f9a9f | ||
|
|
8a3cb8daa8 | ||
|
|
64ab537b68 | ||
|
|
fdea386727 | ||
|
|
28944ed862 | ||
|
|
3250ee5707 | ||
|
|
d308a561e4 | ||
|
|
584f8cc9e7 | ||
|
|
61e1483b74 | ||
|
|
3bb4e34044 | ||
|
|
832b17d4d2 | ||
|
|
76c5875912 | ||
|
|
266dc7679a | ||
|
|
d693dc85e4 | ||
|
|
add68130b8 | ||
|
|
d9b5680a45 | ||
|
|
dc25be536c | ||
|
|
7708c88e32 | ||
|
|
707f230353 | ||
|
|
49d2eb9a08 | ||
|
|
128186ea68 | ||
|
|
949be9d0a1 | ||
|
|
3e986517c3 | ||
|
|
c37ec19751 | ||
|
|
5d3a9eec7f | ||
|
|
3d21a79148 | ||
|
|
e15f5266a3 | ||
|
|
9130eb8b99 | ||
|
|
09e856d2f3 | ||
|
|
ee5c0a2902 | ||
|
|
621b7e63cf | ||
|
|
c6bf9fb89e | ||
|
|
f794180865 | ||
|
|
0e769d42a1 | ||
|
|
cbeb9d39ff | ||
|
|
5a98c1a3b1 | ||
|
|
cac634d39b | ||
|
|
08330bad0b | ||
|
|
84a46939b3 | ||
|
|
65a484e00b | ||
|
|
843fdd3e57 | ||
|
|
a10b51705f | ||
|
|
df86e39845 | ||
|
|
7f49b6c102 | ||
|
|
754a0b8320 | ||
|
|
7d3b3761b2 | ||
|
|
d1b80ffa29 | ||
|
|
f95ce886f1 | ||
|
|
fa16eff003 | ||
|
|
23f90b13f3 | ||
|
|
37e7b5dfc7 | ||
|
|
e1683afb31 | ||
|
|
e38b359d75 | ||
|
|
b6dcec2710 | ||
|
|
0307d59c1f | ||
|
|
3aa013c8db | ||
|
|
fc70876aa9 | ||
|
|
722ab9306a | ||
|
|
2ab34b5eeb | ||
|
|
e3bdae77a0 | ||
|
|
663d6a1c8d | ||
|
|
bf5cbb512e | ||
|
|
d4bb58630e | ||
|
|
fff0eb5d88 | ||
|
|
7ae0c5d546 | ||
|
|
114d93b212 | ||
|
|
13a87e5514 | ||
|
|
d67d75282c | ||
|
|
827cbf22c9 | ||
|
|
2530cda507 | ||
|
|
38000a3da0 | ||
|
|
6c21b21ecc | ||
|
|
8caf9be3e6 | ||
|
|
cdb61842d6 | ||
|
|
05660d1118 | ||
|
|
a3f9221a67 | ||
|
|
7e1980af7a | ||
|
|
921fd919b5 | ||
|
|
fc5a5ea53d | ||
|
|
86156dd05b | ||
|
|
3219ffddd4 | ||
|
|
3a01c32bf9 | ||
|
|
9ffd0a4e70 | ||
|
|
90e049c674 | ||
|
|
69121633cf | ||
|
|
28eadded87 | ||
|
|
93c598dd19 | ||
|
|
d012563ab1 | ||
|
|
0b96789e65 | ||
|
|
947402bc57 | ||
|
|
d4107dce52 | ||
|
|
990b19e048 | ||
|
|
2290e6ccf2 | ||
|
|
14c8f072f1 | ||
|
|
c55df02b1a | ||
|
|
5b86248b54 | ||
|
|
449ac44131 | ||
|
|
f439a7927f | ||
|
|
277bd67f72 | ||
|
|
5de8ccbf32 | ||
|
|
577e0700ba | ||
|
|
71a37484f5 | ||
|
|
daf157e6c0 | ||
|
|
2aa6a229ca | ||
|
|
9b869edd90 | ||
|
|
2b21697b86 | ||
|
|
895ab8789b | ||
|
|
5f02f95ce6 | ||
|
|
f2aac8c49a | ||
|
|
82e653a739 | ||
|
|
053f760e2a | ||
|
|
ba9e141fae | ||
|
|
d60a3e2b66 | ||
|
|
70c2d1eded | ||
|
|
80cbf81b3b | ||
|
|
798d7346cf | ||
|
|
d6c5d40f9b | ||
|
|
371b8310d9 | ||
|
|
1ac721af8f | ||
|
|
85fd88ace2 | ||
|
|
ed1e377bb1 | ||
|
|
4240c29f4b | ||
|
|
51ba4a1531 | ||
|
|
d1b55cb7aa | ||
|
|
bbf53bd5af | ||
|
|
b2ced6ff1d | ||
|
|
a9b07fe7d7 | ||
|
|
66e4656a8e | ||
|
|
8f4d4f07a4 | ||
|
|
8e55580cde | ||
|
|
8ae79066a2 | ||
|
|
36da6a3563 | ||
|
|
45690faea7 | ||
|
|
046fecbb35 | ||
|
|
0f6024ef3c | ||
|
|
2488b5a97f | ||
|
|
56de6f1a9f | ||
|
|
0fc5d37901 | ||
|
|
ea4cc4e076 | ||
|
|
9e25c21fed | ||
|
|
fc3fdcc71c | ||
|
|
80b7b8bbdb | ||
|
|
a8ce272e4a | ||
|
|
72963c4e20 | ||
|
|
4e5bd361e0 | ||
|
|
ed1a9309d4 | ||
|
|
1d0a06438c | ||
|
|
2c62714a30 | ||
|
|
f13e753b52 | ||
|
|
39a5bce8a6 | ||
|
|
ba953bfa94 | ||
|
|
1debd0405b | ||
|
|
1f1d9312d2 | ||
|
|
259201b352 | ||
|
|
bb45bd048e | ||
|
|
5489ec6eff | ||
|
|
e55e81bcd0 | ||
|
|
5c5c758718 | ||
|
|
b87669039e | ||
|
|
60a6ed2a20 | ||
|
|
81e8388c3f | ||
|
|
4532680e5f | ||
|
|
fc17ab280a | ||
|
|
621cd0f008 | ||
|
|
13c9877099 | ||
|
|
ff889fde18 | ||
|
|
f68c16a5e6 | ||
|
|
cc45f64280 | ||
|
|
725ec088ea | ||
|
|
87c3de5aab | ||
|
|
6d7c04a3de | ||
|
|
d0f5ce0cc4 | ||
|
|
cb89ef9885 | ||
|
|
90360d40fd | ||
|
|
15dcd326f8 | ||
|
|
ae1eb56695 | ||
|
|
a47fd07585 | ||
|
|
19bc257002 | ||
|
|
a1d4cd658e | ||
|
|
aaaf8e75af | ||
|
|
8cc31c6cf1 | ||
|
|
8468af1a20 | ||
|
|
db39d70c75 | ||
|
|
04fff7ebd8 | ||
|
|
b154af87a1 | ||
|
|
8b7480806a | ||
|
|
e29f5ad559 | ||
|
|
4618fd5a43 | ||
|
|
ed979c6aa1 | ||
|
|
141c5da3f9 | ||
|
|
0185ee5df3 | ||
|
|
27c08a65e1 | ||
|
|
de408a5eb7 | ||
|
|
fa3c072dd1 | ||
|
|
aac4b7090a | ||
|
|
f0d664530d | ||
|
|
702140bf93 | ||
|
|
2075db3db0 | ||
|
|
b7fec513cf | ||
|
|
78e7758022 | ||
|
|
818318ddd5 | ||
|
|
39eea781bc | ||
|
|
c4f7bd6a04 | ||
|
|
dd03d0f3c9 | ||
|
|
369ce6af2c | ||
|
|
f83817510d | ||
|
|
bfcd779cfb | ||
|
|
e69c380205 | ||
|
|
61e14aa827 | ||
|
|
0c6c83e278 | ||
|
|
6f52208b68 |
@@ -36,12 +36,14 @@ jobs:
|
||||
m32: [0, 1]
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-22.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-22.04
|
||||
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
|
||||
# - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-22.04 clang++
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-20.04, m32: 1}
|
||||
- compiler: { cc: gcc, cxx: g++ }
|
||||
m32: 1
|
||||
include:
|
||||
# Build GCC 10 on ubuntu-20.04
|
||||
- os: ubuntu-20.04
|
||||
@@ -105,12 +107,14 @@ jobs:
|
||||
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-22.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
# - os: ${{ github.event_name == 'pull_request' && 'ubuntu-20.04' || 'do-not-exclude' }}
|
||||
# - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-22.04
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-20.04, m32: 1}
|
||||
- compiler: { cc: gcc, cxx: g++ }
|
||||
m32: 1
|
||||
include:
|
||||
# Test with GCC 10 on ubuntu-20.04 without m32
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-0}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# Build and push verilator docker image when tags are pushed to the repository.
|
||||
# The following variable(s) must be configured in the github repository:
|
||||
# DOCKER_HUB_NAMESPACE: docker hub namespace.
|
||||
# The following secrets must be configured in the github repository:
|
||||
# DOCKER_HUB_USER: user name for logging into docker hub
|
||||
# DOCKER_HUB_ACCESS_TOKEN: docker hub access token.
|
||||
name: Build Verilator Container
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [ 'v*' ]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
manual_tag:
|
||||
description: 'Git tag to use for image build'
|
||||
required: true
|
||||
type: string
|
||||
add_latest_tag:
|
||||
description: 'Tag workflow_dispatch docker image as "latest"'
|
||||
required: true
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
contexts:
|
||||
- "ci/docker/run:verilator"
|
||||
# - "ci/docker/buildenv:verilator-buildenv"
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Extract context variables
|
||||
run: |
|
||||
echo "${{ matrix.contexts }}" | sed -r 's/(.*):.*/build_context=\1/' >> "$GITHUB_ENV"
|
||||
echo "${{ matrix.contexts }}" | sed -r 's/.*:(.*)/image_name=\1/' >> "$GITHUB_ENV"
|
||||
echo "git_tag=${GITHUB_REF#refs/*/}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Use manual tag
|
||||
if: ${{ inputs.manual_tag }}
|
||||
run: |
|
||||
echo "git_tag=${{ inputs.manual_tag }}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
|
||||
tags: |
|
||||
type=match,pattern=(v.*),group=1,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
type=raw,value=${{ inputs.manual_tag }},enable=${{ inputs.manual_tag != '' }}
|
||||
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
buildkitd-flags: --debug
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Build and Push to Docker
|
||||
uses: docker/build-push-action@v4
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
with:
|
||||
context: ${{ env.build_context }}
|
||||
build-args: SOURCE_COMMIT=${{ env.git_tag }}
|
||||
platforms: linux/arm64,linux/amd64
|
||||
push: ${{ !env.ACT && startsWith(github.ref, 'refs/tags/v') }}
|
||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||
@@ -41,3 +41,5 @@ verilator-config-version.cmake
|
||||
**/_build/*
|
||||
**/obj_dir/*
|
||||
/.vscode/
|
||||
/.idea/
|
||||
/cmake-build-*/
|
||||
|
||||
+5
-11
@@ -15,7 +15,7 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||
project(Verilator
|
||||
VERSION 5.008
|
||||
VERSION 5.016
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
@@ -23,7 +23,8 @@ project(Verilator
|
||||
option(DEBUG_AND_RELEASE_AND_COVERAGE
|
||||
"Builds both the debug and release binaries, overriding CMAKE_BUILD_TYPE. Not supported under MSBuild.")
|
||||
|
||||
set(PYTHON3 python)
|
||||
find_package(Python3 COMPONENTS Interpreter)
|
||||
set(PYTHON3 Python3::Interpreter)
|
||||
set(CMAKE_INSTALL_DATADIR ${CMAKE_INSTALL_PREFIX})
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
@@ -44,15 +45,7 @@ if (MSVC)
|
||||
if (NOT WIN_FLEX_BISON)
|
||||
message(FATAL_ERROR "Please install https://github.com/lexxmark/winflexbison and set WIN_FLEX_BISON environment variable. Please use install cmake target after a successful build.")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++20")
|
||||
else()
|
||||
if (NOT DEFINED ENV{FLEX_INCLUDE})
|
||||
message(FATAL_ERROR "Please set environment variable FLEX_INCLUDE to the directory containing FlexLexer.h")
|
||||
endif()
|
||||
set(WIN_FLEX_BISON "$ENV{FLEX_INCLUDE}")
|
||||
if (WIN32 AND NOT MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20")
|
||||
endif()
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
endif()
|
||||
|
||||
find_package(BISON)
|
||||
@@ -99,6 +92,7 @@ configure_package_config_file(verilator-config-version.cmake.in verilator-config
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/verilator-config-version.cmake DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
foreach (program
|
||||
verilator
|
||||
verilator_gantt
|
||||
verilator_ccache_report
|
||||
verilator_difftree
|
||||
|
||||
@@ -8,6 +8,236 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.016 2023-09-16
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add prepareClone and atClone APIs for Verilated models (#3503) (#4444). [Yinan Xu]
|
||||
* Add check for conflicting options e.g. binary and lint-only (#4409). [Ethan Sifferman]
|
||||
* Add --no-trace-top to not trace top signals (#4412) (#4422). [Frans Skarman]
|
||||
* Support recursive function calls (#3267).
|
||||
* Support assignments of packed values to stream expressions on queues (#4401). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support no-parentheses calls to static methods (#4432). [Krzysztof Boroński]
|
||||
* Support block_item_declaration in forks (#4455). [Krzysztof Boroński]
|
||||
* Support assignments of stream expressions on queues to packed values (#4458). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support function non-constant default arguments (#4470).
|
||||
* Support 'let'.
|
||||
* Optimize Verilator executable size by refactoring error reporting routines (#4446). [Anthony Donlon]
|
||||
* Optimize Verilation runtime pointers and graphs (#4396) (#4397) (#4398). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Optimize preparations towards multithreaded Verilation (#4291) (#4463) (#4476) (#4477) (#4479). [Kamil Rakoczy, Antmicro Ltd]
|
||||
* Fix Windows filename format, etc (#3873) (#4421). [Anthony Donlon].
|
||||
* Fix t_dist_cppstyle Perl performance issue (#4085). [Srinivasan Venkataramanan]
|
||||
* Fix using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon]
|
||||
* Fix false INFINITELOOP on forever..mailbox.get() (#4323). [Srinivasan Venkataramanan]
|
||||
* Fix data type of condition operation on class objects (#4345) (#4352). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix variables mutated under fork..join_none/join_any blocks into anonymous objects (#4356). [Krzysztof Boroński]
|
||||
* Fix V3CUse, do not consider implementations (.cpp) at all (#4386). [Krzysztof Boroński]
|
||||
* Fix ++/-- under statements (#4399). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix detection of mixed blocking and nonblocking assignment in nested assignments (#4404). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix jumping over object initialization (#4411). [Krzysztof Boroński]
|
||||
* Fix multiple issues towards short circuit support (#4413) (#4460). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix variable lifetimes in extern methods (#4414). [Krzysztof Boroński]
|
||||
* Fix multiple function definitions in V3Sched (#4416). [Hennadii Chernyshchyk]
|
||||
* Fix false UNUSEDPARAM on generate localparam (#4427). [Bill Pringlemeir]
|
||||
* Fix checking for parameter and port connections in the wrong place (#4428). [Anthony Donlon]
|
||||
* Fix coroutine handle movement during queue manipulation (#4431). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix nested assignments on the LHS (#4435). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix false MULTITOP on bound interfaces (#4438). [Alex Solomatnikov]
|
||||
* Fix internal error on real conversion (#4447). [vdhotre-ventana]
|
||||
* Fix lifetime unknown error on enum.name (#4448). [jwoutersymatra]
|
||||
* Fix unstable output of VHashSha256 (#4453). [Anthony Donlon]
|
||||
* Fix static cast from a stream type (#4469) (#4485). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix error on enum with VARHIDDEN of cell (#4482). [Michail Rontionov]
|
||||
* Fix lint of case statements with enum and wildcard bits (#4464) (#4487). [Anthony Donlon]
|
||||
* Fix reference to extended class in parameterized class (#4466).
|
||||
* Fix recursive display causing segfault (#4480). [Kuoping Hsu]
|
||||
* Fix the error message when the type of ref argument is wrong (#4490). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix display %x formatting of real.
|
||||
* Fix mis-warning on #() in classes' own functions.
|
||||
* Fix IGNOREDRETURN to not warn on void-cast static function calls.
|
||||
* Fix ZERODLY to not warn on 'wait(0)'.
|
||||
|
||||
|
||||
Verilator 5.014 2023-08-06
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Deprecation planned for 32-bit pointer -m32 mode (#4268).
|
||||
* Deprecate CMake config below version 3.13 (#4389) (#4390). [Vito Gamberini]
|
||||
* Support some stream operations on queues (#4292). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support property declaration with empty parentheses (#4313) (#4317). [Anthony Donlon]
|
||||
* Support locator methods with "with" on assoc arrays (#4335). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support string replication with variable (#4341). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Support more types in wait (#4374). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Support static method calls as default values of function arguments (#4378). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Add GENUNNAMED lint warning. [Srinivasan Venkataramanan, Deepa Palaniappan]
|
||||
* Add MISINDENT lint warning for misleading indentation.
|
||||
* Fix 'VlForkSync' redeclaration (#4277). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix processes that can outlive their parents (#4253). [Krzysztof Boronski, Antmicro Ltd]
|
||||
* Fix duplicate fork names (#4295). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix splitting coroutines (#4297) (#4307). [Jiamin Zhu]
|
||||
* Fix error when multiple duplicate DPI exports (#4301).
|
||||
* Fix class reference assignment checking (#4296). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix handling of ref types in initial values of type parameters (#4304). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix comparison of string parameters (#4308). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix state update for always processes (#4311). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix multiple edge timing controls in class methods (#4318) (#4320) (#4344). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix implicit calls of base class constructors with optional arguments (#4319). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix propagation of process requirement (#4321). [Krzysztof Boroński]
|
||||
* Fix unhandled overloads in V3InstrCount (#4324). [Krzysztof Boroński]
|
||||
* Fix selects of static members (#4326). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix references to members of results of static methods (#4327). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix unique..with method on queues of class objects (#4328). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix queue slicing (#4329). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix wildcard referring types (#4336) (#4342). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix comparison of class objects (#4346). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix unexpected RefDType on assoc arrays (#4337). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix cmake astgen for Rocky Linux 8.7 (#4343). [Julian Daube]
|
||||
* Fix class timescale in class packages (#4348). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix string concatenations (#4354). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix unlinked task error from broken context (#4355) (#4402). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix selects on unpacked structs (#4359). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix select operation on assoc array with wide keys (#4360). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix non-public methods with wide output (#4364). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix handling of super.new calls (#4366). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix assign to input var in methods (#4367). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix VlProcess not found (#4368). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix order of evaluation of function calls in statements (#4375). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix config_build.h issues (#4380) (#4381). [Andrew Miloradovsky]
|
||||
|
||||
|
||||
Verilator 5.012 2023-06-13
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* With -j or --build-jobs, multithread Verilator's emit phase of Verilation. [Kamil Rakoczy, Antmicro Ltd]
|
||||
Additional Verilator-internal stages will become multithreaded over time.
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add --main-top-name option for C main TOP name (#4235) (#4249). [Don Williamson]
|
||||
* Add creating __inputs.vpp file with --debug (#4177). [Tudor Timi]
|
||||
* Add NEWERSTD warning when using feature in newer language standard (#4168) (#4172). [Ethan Sifferman]
|
||||
* Add warning that timing controls in DPI exports are unsupported (#4238). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Support std::process class (#4212). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Support inside expressions with strings and doubles (#4138) (#4139). [Krzysztof Boroński]
|
||||
* Support get_randstate/set_randstate class method functions.
|
||||
* Support for condition operator on class objects (#4214). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support array max (#4275). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Optimize VPI callValueCbs (#4155). [Hennadii Chernyshchyk]
|
||||
* Configure for faster C++ linking using 'mold', if it is installed.
|
||||
* Fix crash on duplicate imported modules (#3231). [Robert Balas]
|
||||
* Fix false WIDTHEXPAND on array declarations (#3959). [JOTEGO]
|
||||
* Fix marking overridden methods as coroutines (#4120) (#4169). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix SystemC signal copy macro use (#4135). [Josep Sans]
|
||||
* Fix duplicate static names in blocks in functions (#4144) (#4160). [Stefan Wallentowitz]
|
||||
* Fix initialization order of initial static after function/task (#4159). [Kamil Rakoczy, Antmicro Ltd]
|
||||
* Fix linking AstRefDType if it has parameterized class ref (#4164) (#4170). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix crash caused by $display() optimization (#4165) (#4166). [Tudor Timi]
|
||||
* Fix arrays of unpacked structs (#4173). [Risto Pejašinović]
|
||||
* Fix $fscanf of decimals overflowing variables (#4174). [Ahmed El-Mahmoudy]
|
||||
* Fix super.new missing data type (#4147). [Tudor Timi]
|
||||
* Fix missing class forward declarations (#4151). [Krzysztof Boroński]
|
||||
* Fix hashes of instances of parameterized classes (#4182). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix forced assignments that override non-continuous assignments (#4183) (#4192). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix wide structure VL_TOSTRING_W generation (#4188) (#4189). [Aylon Chaim Porat]
|
||||
* Fix references to members of parameterized base classes (#4196). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix tracing undefined alignment (#4201) (#4288) [John Wehle]
|
||||
* Fix class specific same methods for AstVarScope, AstVar, and AstScope (#4203) (#4250). [John Wehle]
|
||||
* Fix dotted references in parameterized classes (#4206). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix bit selections under parameterized classes (#4210). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix duplicate std:: declaration with -I (#4215). [Harald Pretl]
|
||||
* Fix deep traversal of class inheritance timing (#4216). [Krzysztof Boroński]
|
||||
* Fix class parameters of enum types (#4219). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix static methods with prototypes (#4220). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix LATCH warning on function local variables (#4221) (#4284) [Julien Margetts]
|
||||
* Fix VCD scope types (#4227) (#4282). [Àlex Torregrosa]
|
||||
* Fix incorrect multi-driven lint warning (#4231) (#4248). [Adrien Le Masle]
|
||||
* Fix missing assignment for wide unpacked structs (#4233). [Jiamin Zhu]
|
||||
* Fix unpacked struct == and != operators (#4234) (#4240). [Risto Pejašinović]
|
||||
* Fix AstStructSel clean when data type is structure (#4241) (#4244). [Risto Pejašinović]
|
||||
* Fix function calls in with statements (#4245). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix operator == for unpacked struct, if elements are VlUnpacked arrays (#4247). [Risto Pejašinović]
|
||||
* Fix STATIC lifetime for variables created from clocking items (#4262). [Krzysztof Boroński]
|
||||
* Fix names of foreach blocks (#4264). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix iterated variables in foreach loops to have VAUTOM lifetimes (#4265). [Krzysztof Boroński]
|
||||
* Fix missing assignment for wide class members (#4267). [Jiamin Zhu]
|
||||
* Fix the global uses timing flag when forks exist (#4274). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix struct redefinition (#4276). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix detection of wire/reg duplicates.
|
||||
* Fix false IMPLICITSTATIC on package functions.
|
||||
* Fix method calls on function return values.
|
||||
|
||||
|
||||
Verilator 5.010 2023-04-30
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add --public-depth to force public to a certain instance depth (#3952). [Andrew Nolte]
|
||||
* Add --public-params flag (#3990). [Andrew Nolte]
|
||||
* Add CONSTRAINTIGN warning when constraint ignored.
|
||||
* Add STATICVAR warning and convert to automatic (#4018) (#4027) (#4030). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Add error if class types don't match (#4064). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support class extends of package::class.
|
||||
* Support class srandom and class random stability.
|
||||
* Support class method calls without parenthesis (#3902) (#4082). [Srinivasan Venkataramanan]
|
||||
* Support method calls without parenthesis (#4034). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support parameterized return types of methods (#4122). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support parameterized class references in extends statement (#4146). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support complicated IEEE 'for' assignments.
|
||||
* Support $fopen as an expression.
|
||||
* Support ++/-- on dotted member variables.
|
||||
* Optimize static trigger evaluation (#4142). [Geza Lore, X-EPIC]
|
||||
* Optimize more xor trees (#4071). [Yutetsu TAKATSUKASA]
|
||||
* Change range order warning from LITENDIAN to ASCRANGE (#4010). [Iztok Jeras]
|
||||
* Change ZERODLY to a warning.
|
||||
* Fix random internal crashes (#666). [Dag Lem]
|
||||
* Fix install, standardization in cmake CMakeLists.txt (#3974). [Yu-Sheng Lin]
|
||||
* Fix UNDRIVEN warning seg fault (#3989). [Felix Neumärker]
|
||||
* Fix symbol entries when inheriting classes (#3995) (#3996). [Krzysztof Boroński]
|
||||
* Fix event controls reusing same variable (#4014). Kamil Rakoczy <[email protected]>
|
||||
* Fix push to dynamic queue in struct (#4015). [ezchi]
|
||||
* Fix names for blocks in do..while loop (#4019). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix randomize on null field (#4023). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix rand fields in base classes (#4025). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix large return blocks with --comp-limit-blocks (#4028). [tenghtt]
|
||||
* Fix clocking block scope internal error (#4032). [Srinivasan Venkataramanan]
|
||||
* Fix false LATCH warning on --assert 'unique else if' (#4033) ($4054). [Jesse Taube]
|
||||
* Fix characters from DEFENV literals for Conda (#4035) (#4044). [Tim Snyder]
|
||||
* Fix info message prints under --assert (#4036) (#4053). [Srinivasan Venkataramanan]
|
||||
* Fix C++ compile errors when passing class refs as task argument (#4063). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix NBAs inside fork-joins (#4050). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix task calls as fork statements (#4055). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix _Vilp used before declaration (#4057) (#4062). [Josep Sans]
|
||||
* Fix incorrect optimization of bit op tree (#4059) (#4070). [Yutetsu TAKATSUKASA]
|
||||
* Fix parameters in a class body to be localparam (#4061). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix interface generate begin (#4065). [Srinivasan Venkataramanan]
|
||||
* Fix tracing with awaits at end of block (#4075) (#4076). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix sense expression variable naming (#4081). [Kamil Rakoczy]
|
||||
* Fix importing symbols from base class (#4084). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix false error on new const assignment (#4098). [Tudor Timi]
|
||||
* Fix unpacked structs under classes (#4102). [Tudor Timi]
|
||||
* Fix variables in class methods to be automatic (#4111) (#4137). [Peter Monsson]
|
||||
* Fix to use parallel build for projects with a lot of files (#4116). [Krzysztof Boroński]
|
||||
* Fix including __Syms header in generated C++ files (#4123). [Krzysztof Boroński]
|
||||
* Fix systemc namespace issues (#4126) (#4127). [Eyck Jentzsch]
|
||||
* Fix class param extends A=B (#4128). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix missing begin block hierarchy in --xml-only cells section (#4129) (#4133). [Risto Pejašinović]
|
||||
* Fix resolution of class lvalues after parameterization (#4131). [Krzysztof Boroński]
|
||||
* Fix DFG error on $countbits (#4101) (#4143). [Paul Donahue]
|
||||
* Fix duplicating parameter class types (#4115). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix class extend param references (#4136). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix -CFLAGS to allow overriding optimization levels (#4140). [Peter Monsson]
|
||||
* Fix DPI function type alias (#4148) (#4149). [Toru Niina]
|
||||
* Fix deleting unused parameterized classes (#4150). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix false ENUMVALUE on expressions and arrays.
|
||||
* Fix unnecessary verilated_std.sv waivers in --waiver-output.
|
||||
|
||||
|
||||
Verilator 5.008 2023-03-04
|
||||
==========================
|
||||
|
||||
@@ -31,7 +261,6 @@ Verilator 5.008 2023-03-04
|
||||
* Support function calls without parenthesis (#3903) (#3902). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support class extending its parameter (#3904). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support static function variables (#3830). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support vpiDefName (#3906) (#3931). [Andrew Nolte]
|
||||
* Support recursive methods (#3987). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix real parameters of infinity and NaN.
|
||||
* Fix pattern assignment to unpacked structs (#3510). [Mostafa Garnal]
|
||||
@@ -41,8 +270,8 @@ Verilator 5.008 2023-03-04
|
||||
* Fix namespace fallback resolution (#3863) (#3942). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix std:: to be parsed first (#3864) (#3928). [Aleksander Kiryk, Antmicro Ltd]
|
||||
* Fix cmake warning if multiple SOURCES w/o PREFIX (#3916) (#3927). [Yoda Lee]
|
||||
* Fix paramaterized class function linkage (#3917). [Ryszard Rozak]
|
||||
* Fix static members of type aliases of a parametrized class (#3922). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix parameterized class function linkage (#3917). [Ryszard Rozak]
|
||||
* Fix static members of type aliases of a parameterized class (#3922). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix class extend parameter dot case (#3926). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix MsWin missing directory exception, and ::std (#3928) (#3933) (#3935). [Kritik Bhimani]
|
||||
* Fix very long VPI signal names (#3929). [Marlon James]
|
||||
@@ -54,13 +283,13 @@ Verilator 5.008 2023-03-04
|
||||
* Fix CMake bad C identifiers (#3948) (#3951). [Zixi Li]
|
||||
* Fix build on HP PA architecture (#3954). [John David Anglin]
|
||||
* Fix date on the front page of verilator.pdf (#3956) (#3957). [Larry Doolittle]
|
||||
* Fix dicts declared with ref type (#3960). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix associative arrays declared with ref type (#3960). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix missing error on negative replicate (#3963). [Benjamin Menküc]
|
||||
* Fix self references to parameterized classes (#3962). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix LITENDIAN warning is backwards (#3966) (#3967). [Cameron Kirk]
|
||||
* Fix subsequent parameter declarations (#3969). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix timing delays to not truncate below 64 bits (#3973) (#3982). [Felix Neumärker]
|
||||
* Fix cmake on macOS to mark weak symbols with -U linker flag (#3978) (#3979). [Peter Debacker]
|
||||
* Fix cmake on MacOS to mark weak symbols with -U linker flag (#3978) (#3979). [Peter Debacker]
|
||||
* Fix UNDRIVEN warning seg fault (#3989). [Felix Neumärker]
|
||||
* Fix coverage of class methods (#3998). [Tim Paine]
|
||||
* Fix packed array structure replication.
|
||||
@@ -80,7 +309,7 @@ Verilator 5.006 2023-01-22
|
||||
* Support import/export lists in modport (#3886). [Gökçe Aydos]
|
||||
* Support class queue equality (#3895). [Ilya Barkov]
|
||||
* Support type case and type equality comparisons.
|
||||
* Add IMPLICITSTATIC warning when a ftask/function is implicitly static (#3839). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Add IMPLICITSTATIC warning when a task/function is implicitly static (#3839). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Add VL_VALUE_STRING_MAX_WORDS override (#3869). [Andrew Nolte]
|
||||
* Optimize expansion of extend operators.
|
||||
* Internal multithreading tests. [Mariusz Glebocki, et al, Antmicro Ltd]
|
||||
@@ -94,7 +323,7 @@ Verilator 5.006 2023-01-22
|
||||
* Fix self references when param class instantiated (#3833). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix memory leak in V3Sched, etc. (#3834). [Geza Lore]
|
||||
* Fix compatibility with musl libc / Alpine Linux (#3845). [Sören Tempel]
|
||||
* Fix empty case items crash (#3851). [rporter]
|
||||
* Fix empty case items crash (#3851). [Rich Porter]
|
||||
* Fix VL_CPU_RELAX on MIPS/Armel/s390/sparc (#3843) (#3891). [Kamil Rakoczy]
|
||||
* Fix module parameter name collision (#3854) (#3855). [James Shi]
|
||||
* Fix unpacked array expansion (#3861). [Joey Liu]
|
||||
|
||||
+6
-8
@@ -424,17 +424,15 @@ YAPF_FLAGS = -i
|
||||
yapf:
|
||||
$(YAPF) $(YAPF_FLAGS) $(PY_FILES)
|
||||
|
||||
FLAKE8 = flake8
|
||||
FLAKE8_FLAGS = \
|
||||
--extend-exclude=fastcov.py \
|
||||
--ignore=E123,E129,E251,E402,E501,W503,W504,E701
|
||||
|
||||
PYLINT = pylint
|
||||
PYLINT_FLAGS = --score=n --disable=R0801
|
||||
|
||||
RUFF = ruff
|
||||
RUFF_FLAGS = check --ignore=E402,E501,E701
|
||||
|
||||
lint-py:
|
||||
-$(FLAKE8) $(FLAKE8_FLAGS) $(PY_PROGRAMS)
|
||||
-$(PYLINT) $(PYLINT_FLAGS) $(PY_PROGRAMS)
|
||||
-$(RUFF) $(RUFF_FLAGS) $(PY_PROGRAMS)
|
||||
|
||||
format-pl-exec:
|
||||
-chmod a+x test_regress/t/*.pl
|
||||
@@ -451,8 +449,8 @@ install-msg:
|
||||
|
||||
IN_WILD := ${srcdir}/*.in ${srcdir}/*/*.in
|
||||
|
||||
# autoheader might not change config_build.h.in, so touch it
|
||||
${srcdir}/config_build.h: ${srcdir}/config_build.h.in configure
|
||||
# autoheader might not change config_package.h.in, so touch it
|
||||
${srcdir}/config_package.h: ${srcdir}/config_package.h.in configure
|
||||
cd ${srcdir} && autoheader
|
||||
touch $@
|
||||
Makefile: Makefile.in config.status $(IN_WILD)
|
||||
|
||||
+11
-8
@@ -1,12 +1,16 @@
|
||||
.. Github doesn't render images unless absolute URL
|
||||
.. Do not know of a conditional tag, "only: github" nor "github display" works
|
||||
|
||||
.. image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|
||||
:target: https://verilator.org
|
||||
.. image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0]
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0
|
||||
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
|
||||
:target: https://opensource.org/licenses/Artistic-2.0
|
||||
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
|
||||
:target: https://repology.org/project/verilator/versions
|
||||
.. image:: https://img.shields.io/docker/pulls/verilator/verilator
|
||||
:target: https://hub.docker.com/r/verilator/verilator
|
||||
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
|
||||
:target: https://www.codacy.com/gh/verilator/verilator
|
||||
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
|
||||
@@ -57,17 +61,16 @@ files, the "Verilated" code.
|
||||
|
||||
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.
|
||||
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 a closed-source Verilog simulator, needs 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.
|
||||
designs, Verilator is the tool for you.
|
||||
|
||||
|
||||
Performance
|
||||
@@ -83,11 +86,11 @@ as `Icarus Verilog`_. Another 2-10x speedup might be gained from
|
||||
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,
|
||||
closed-source Verilog simulators (e.g., Carbon Design Systems Carbonator,
|
||||
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.
|
||||
simulation cycles/dollar.
|
||||
|
||||
|
||||
Installation & Documentation
|
||||
|
||||
@@ -372,6 +372,7 @@ detailed descriptions of these arguments.
|
||||
--make <build-tool> Generate scripts for specified build tool
|
||||
-MAKEFLAGS <flags> Arguments to pass to make during --build
|
||||
--main Generate C++ main() file
|
||||
--main-top-name Specify top name passed to Verilated model in generated C++ main
|
||||
--max-num-width <value> Maximum number width (default: 64K)
|
||||
--Mdir <directory> Name of output object directory
|
||||
--MMD Create .d dependency files
|
||||
@@ -404,6 +405,7 @@ detailed descriptions of these arguments.
|
||||
--protect-key <key> Key for symbol protection
|
||||
--protect-lib <name> Create a DPI protected library
|
||||
--public Debugging; see docs
|
||||
--public-params Mark all parameters as public_flat
|
||||
--public-flat-rw Mark all variables, etc as public_flat_rw
|
||||
-pvalue+<name>=<value> Overwrite toplevel parameter
|
||||
--quiet-exit Don't print the command on failure
|
||||
@@ -438,6 +440,7 @@ detailed descriptions of these arguments.
|
||||
--trace-params Enable tracing of parameters
|
||||
--trace-structs Enable tracing structure names
|
||||
--trace-threads <threads> Enable FST waveform creation on separate threads
|
||||
--no-trace-top Do not emit traces for signals in the top module generated by verilator
|
||||
--trace-underscore Enable tracing of _signals
|
||||
-U<var> Undefine preprocessor define
|
||||
--no-unlimited-stack Don't disable stack size limit
|
||||
|
||||
+7
-3
@@ -61,7 +61,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
sudo apt-get update
|
||||
sudo apt-get install ccache help2man libfl-dev ||
|
||||
sudo apt-get install ccache help2man libfl-dev
|
||||
if [ "$CI_RUNS_ON" != "ubuntu-22.04" ]; then
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
|
||||
# Some conflict of libunwind verison on 22.04, can live without it for now
|
||||
sudo apt-get install libgoogle-perftools-dev ||
|
||||
sudo apt-get install libgoogle-perftools-dev
|
||||
@@ -70,6 +70,10 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
sudo apt-get install libsystemc libsystemc-dev ||
|
||||
sudo apt-get install libsystemc libsystemc-dev
|
||||
fi
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install bear mold ||
|
||||
sudo apt-get install bear mold
|
||||
fi
|
||||
if [ "$COVERAGE" = 1 ]; then
|
||||
yes yes | sudo cpan -fi Parallel::Forker
|
||||
fi
|
||||
@@ -102,8 +106,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache
|
||||
# Required for test_regress/t/t_dist_attributes.pl
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install libclang-dev ||
|
||||
sudo apt-get install libclang-dev
|
||||
sudo apt-get install libclang-dev mold ||
|
||||
sudo apt-get install libclang-dev mold
|
||||
pip3 install clang==14.0
|
||||
fi
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
|
||||
@@ -19,10 +19,16 @@ RUN apt-get update \
|
||||
ccache \
|
||||
flex \
|
||||
git \
|
||||
help2man \
|
||||
libfl2 \
|
||||
libfl-dev \
|
||||
libgoogle-perftools-dev \
|
||||
numactl \
|
||||
perl \
|
||||
perl-doc \
|
||||
python3 \
|
||||
zlib1g \
|
||||
zlib1g-dev \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@@ -39,7 +45,7 @@ RUN git clone "${REPO}" verilator && \
|
||||
git checkout "${SOURCE_COMMIT}" && \
|
||||
autoconf && \
|
||||
./configure && \
|
||||
make -j "$(nproc)" && \
|
||||
make && \
|
||||
make install && \
|
||||
cd .. && \
|
||||
rm -r verilator
|
||||
|
||||
+9
-4
@@ -5,16 +5,16 @@
|
||||
# General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
# When releasing, also update header of Changes file,
|
||||
# When releasing, also update header of Changes file, and CmakeLists.txt,
|
||||
# and commit using "devel release" or "Version bump" message
|
||||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.008 2023-03-04],
|
||||
AC_INIT([Verilator],[5.016 2023-09-16],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
AC_CONFIG_HEADERS(src/config_build.h)
|
||||
AC_CONFIG_HEADERS(src/config_package.h)
|
||||
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc verilator-config.cmake verilator-config-version.cmake)
|
||||
|
||||
# Version
|
||||
@@ -68,7 +68,7 @@ AC_MSG_CHECKING(whether to use -m32)
|
||||
AC_ARG_ENABLE([m32],
|
||||
[AS_HELP_STRING([--enable-m32],
|
||||
[Use -m32 for all compilation and link,
|
||||
including Verialtor and generated models.])],
|
||||
including Verilator and generated models.])],
|
||||
[case "${enableval}" in
|
||||
yes) CFG_ENABLE_M32=yes ;;
|
||||
no) CFG_ENABLE_M32=no ;;
|
||||
@@ -470,6 +470,10 @@ m4_foreach([ldflag], [
|
||||
AC_SUBST(CFG_LDLIBS_THREADS)
|
||||
AC_SUBST(CFG_LDFLAGS_THREADS_CMAKE)
|
||||
|
||||
# If 'mold' is installed, use it to link for faster buildtimes
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -fuse-ld=mold)
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_VERILATED, -fuse-ld=mold)
|
||||
|
||||
# When linking partially statically
|
||||
if test "$CFG_ENABLE_PARTIAL_STATIC" = "yes"; then
|
||||
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -static-libgcc)
|
||||
@@ -480,6 +484,7 @@ else
|
||||
LTCMALLOC=-ltcmalloc_minimal
|
||||
fi
|
||||
AC_SUBST(CFG_LDFLAGS_SRC)
|
||||
AC_SUBST(CFG_LDFLAGS_VERILATED)
|
||||
|
||||
# The pthread library is required by tcmalloc, so add it if it exists. If it
|
||||
# does not, the tcmalloc check below will fail anyway, and linking against
|
||||
|
||||
+21
-14
@@ -10,9 +10,12 @@ contributions flow more efficiently.
|
||||
Did you find a bug?
|
||||
-------------------
|
||||
|
||||
- Please **Ensure the bug was not already reported** by searching
|
||||
- Please **ensure the bug was not already reported** by searching
|
||||
`Verilator Issues <https://verilator.org/issues>`__.
|
||||
|
||||
- Please **download the latest development GitHub version**, build, and see
|
||||
if the issue has been fixed.
|
||||
|
||||
- If you're unable to find an open issue addressing the problem, `open a
|
||||
new Verilator issue <https://verilator.org/issues/new>`__.
|
||||
|
||||
@@ -20,21 +23,28 @@ Did you find a bug?
|
||||
demonstrating the bug and expected behavior that is not occurring.
|
||||
|
||||
- The ideal example works against other simulators, and is in the
|
||||
test_regress/t test format, as described in `docs/internals
|
||||
<internals.rst>`__.
|
||||
test_regress/t test format, as described in
|
||||
`Verilator Internals Documentation
|
||||
<https://github.com/verilator/verilator/blob/master/docs/internals.rst>`__.
|
||||
|
||||
|
||||
Did you write a patch that fixes a bug?
|
||||
---------------------------------------
|
||||
|
||||
- Please `Open a new issue <https://verilator.org/issues/new>`__.
|
||||
- Please `Open a new Verilator issue <https://verilator.org/issues/new>`__
|
||||
if there is not one already describing the bug.
|
||||
|
||||
- You may attach a patch to the issue, or (preferred) may request a
|
||||
GitHub pull request.
|
||||
- Please `Open a Verilator pull request
|
||||
<https://github.com/verilator/verilator/pulls>`__.
|
||||
|
||||
- Verilator uses GitHub Actions to provide continuous integration. You
|
||||
may want to enable Actions on your GitHub branch to ensure your changes
|
||||
keep the tests passing. See `docs/internals <internals.rst>`__.
|
||||
- See the coding conventions, and other developer information in
|
||||
``docs/internals.rst`` in the distribution, or as rendered at
|
||||
`Verilator Internals Documentation
|
||||
<https://github.com/verilator/verilator/blob/master/docs/internals.rst>`__.
|
||||
|
||||
- Verilator uses GitHub Actions to provide continuous integration. You
|
||||
may want to enable Actions on your GitHub branch to ensure your changes
|
||||
keep the tests passing.
|
||||
|
||||
- Your source-code contributions must be certified as open source,
|
||||
under the `Developer Certificate of
|
||||
@@ -56,13 +66,10 @@ Did you write a patch that fixes a bug?
|
||||
contribution.
|
||||
|
||||
- Your test contributions are generally considered released into the
|
||||
Creative Commons Public Domain (CC0), unless you request otherwise or
|
||||
Creative Commons Public Domain (CC0), unless you request otherwise, or
|
||||
put a GNU/Artistic license on your file.
|
||||
|
||||
- Most important is we get your patch. If you'd like to clean up
|
||||
indentation and related issues ahead of our feedback, that is
|
||||
appreciated; please see the coding conventions in `docs/internals
|
||||
<internals.rst>`__.
|
||||
- Most important is we get your patch.
|
||||
|
||||
|
||||
Do you have questions?
|
||||
|
||||
+29
-3
@@ -12,8 +12,12 @@ Alex Chadwick
|
||||
Aliaksei Chapyzhenka
|
||||
Ameya Vikram Singh
|
||||
Andreas Kuster
|
||||
Andrei Kostovski
|
||||
Andrew Miloradovsky
|
||||
Andrew Nolte
|
||||
Anthony Donlon
|
||||
Arkadiusz Kozdra
|
||||
Aylon Chaim Porat
|
||||
Cameron Kirk
|
||||
Chris Randall
|
||||
Chuxuan Wang
|
||||
@@ -24,14 +28,20 @@ David Horton
|
||||
David Metz
|
||||
David Stanford
|
||||
David Turner
|
||||
Don Williamson
|
||||
Drew Ranck
|
||||
Drew Taussig
|
||||
Driss Hafdi
|
||||
Edgar E. Iglesias
|
||||
Eric Rippey
|
||||
Ethan Sifferman
|
||||
Eyck Jentzsch
|
||||
Fan Shupei
|
||||
february cozzocrea
|
||||
Felix Neumärker
|
||||
Felix Yan
|
||||
Frans Skarman
|
||||
G-A. Kamendje
|
||||
Garrett Smith
|
||||
Geza Lore
|
||||
Gianfranco Costamagna
|
||||
@@ -40,16 +50,16 @@ Gökçe Aydos
|
||||
Graham Rushton
|
||||
Guokai Chen
|
||||
Gustav Svensk
|
||||
G-A. Kamendje
|
||||
Harald Heckmann
|
||||
Hennadii Chernyshchyk
|
||||
Howard Su
|
||||
Huang Rui
|
||||
Huanghuang Zhou
|
||||
HungMingWu
|
||||
HyungKi Jeong
|
||||
Ilya Barkov
|
||||
Iru Cai
|
||||
Ivan Vnučec
|
||||
Ilya Barkov
|
||||
Iztok Jeras
|
||||
Jake Merdich
|
||||
James Hanlon
|
||||
@@ -61,15 +71,21 @@ Jamie Iles
|
||||
Jan Van Winkel
|
||||
Jean Berniolles
|
||||
Jeremy Bennett
|
||||
Jesse Taube
|
||||
Jevin Sweval
|
||||
Jiacheng Qian
|
||||
Jiamin Zhu
|
||||
Jiuyang Liu
|
||||
Joey Liu
|
||||
John Coiner
|
||||
John Demme
|
||||
John Wehle
|
||||
Jonathan Drolet
|
||||
Jose Loyola
|
||||
Josep Sans
|
||||
Joseph Nwabueze
|
||||
Josh Redford
|
||||
Julian Daube
|
||||
Julie Schwartz
|
||||
Julien Margetts
|
||||
Kaleb Barrett
|
||||
@@ -79,6 +95,7 @@ Keith Colbert
|
||||
Kevin Kiningham
|
||||
Kritik Bhimani
|
||||
Krzysztof Bieganski
|
||||
Krzysztof Boronski
|
||||
Krzysztof Boroński
|
||||
Kuba Ober
|
||||
Larry Doolittle
|
||||
@@ -105,6 +122,7 @@ Mostafa Gamal
|
||||
Nandu Raj
|
||||
Nathan Kohagen
|
||||
Nathan Myers
|
||||
Oleh Maksymenko
|
||||
Patrick Stewart
|
||||
Paul Wright
|
||||
Pawel Sagan
|
||||
@@ -119,23 +137,29 @@ Qingyao Sun
|
||||
Rafal Kapuscik
|
||||
Raynard Qiao
|
||||
Richard Myers
|
||||
Risto Pejašinović
|
||||
Robert Balas
|
||||
Rupert Swarbrick
|
||||
Ryszard Rozak
|
||||
Samuel Riedel
|
||||
Sean Cross
|
||||
Sebastien Van Cauwenberghe
|
||||
Sergi Granell
|
||||
Srinivasan Venkataramanan
|
||||
Stefan Wallentowitz
|
||||
Stephen Henry
|
||||
Steven Hugg
|
||||
Sören Tempel
|
||||
Teng Huang
|
||||
Tim Hutt
|
||||
Tim Snyder
|
||||
Tobias Rosenkranz
|
||||
Tobias Wölfel
|
||||
Todd Strader
|
||||
Tomasz Gorochowik
|
||||
Topa Topino
|
||||
Toru Niina
|
||||
Tudor Timi
|
||||
Tymoteusz Blazejczyk
|
||||
Udi Finkelstein
|
||||
Unai Martinez-Corral
|
||||
@@ -143,15 +167,17 @@ Varun Koyyalagunta
|
||||
Vassilis Papaefstathiou
|
||||
Veripool API Bot
|
||||
Victor Besyakov
|
||||
Vito Gamberini
|
||||
William D. Jones
|
||||
Wilson Snyder
|
||||
Xi Zhang
|
||||
Yinan Xu
|
||||
Yoda Lee
|
||||
Yossi Nivin
|
||||
Yu-Sheng Lin
|
||||
Yuri Victorovich
|
||||
Yutetsu TAKATSUKASA
|
||||
Yu-Sheng Lin
|
||||
Yves Mathieu
|
||||
Zhanglei Wang
|
||||
Zixi Li
|
||||
أحمد المحمودي
|
||||
|
||||
+5
-1
@@ -18,12 +18,16 @@
|
||||
RST2HTML = rst2html
|
||||
PYTHON3 = python3
|
||||
DOXYGEN = doxygen
|
||||
SPHINXOPTS ?= -c guide
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
|
||||
SOURCEDIR = guide
|
||||
BUILDDIR = _build
|
||||
|
||||
SPHINXOPTS ?= -c guide
|
||||
ifneq ($(VERILATOR_ANALYTICS_ID),)
|
||||
SPHINXOPTS += -D html_theme_options.analytics_id=$(VERILATOR_ANALYTICS_ID)
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
+1
-2
@@ -31,7 +31,7 @@ def get_vlt_version():
|
||||
if match:
|
||||
try:
|
||||
data = os.popen('git log -n 1 --pretty=%cs').read()
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
data = "" # fallback, and Sphinx will fill in today's date
|
||||
return "Devel " + match.group(1), data
|
||||
return "unknown", "unknown"
|
||||
@@ -115,7 +115,6 @@ html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
html_theme_options = {
|
||||
'analytics_id': 'G-D27B0C9QEB',
|
||||
'logo_only': True,
|
||||
'style_nav_header_background': "#45acf8", # Default is #2980B9
|
||||
# 'canonical_url':
|
||||
|
||||
@@ -120,6 +120,52 @@ code. This is a feature, as using the SystemC pin interconnect scheme
|
||||
everywhere would reduce performance by an order of magnitude.
|
||||
|
||||
|
||||
Verilated API
|
||||
=============
|
||||
|
||||
The API to a Verilated model is the C++ headers in the include/ directory
|
||||
in the distribution. These headers use Doxygen comments, `///` and `//<`,
|
||||
to indicate and document those functions that are part of the Verilated
|
||||
public API.
|
||||
|
||||
Process-Level Clone APIs
|
||||
--------------------------
|
||||
|
||||
Modern operating systems support process-level clone (a.k.a copying, forking)
|
||||
with system call interfaces in C/C++, e.g., :code:`fork()` in Linux.
|
||||
|
||||
However, after cloning a parent process, some resources cannot be inherited
|
||||
in the child process. For example, in POSIX systems, when you fork a process,
|
||||
the child process inherits all the memory of the parent process. However,
|
||||
only the thread that called fork is replicated in the child process. Other
|
||||
threads are not.
|
||||
|
||||
Therefore, to support the process-level clone mechanisms, Verilator supports
|
||||
:code:`prepareClone()` and :code:`atClone()` APIs to allow the user to manually
|
||||
re-construct the model in the child process. The two APIs handle all necessary
|
||||
resources required for releasing and re-initializing before and after cloning.
|
||||
|
||||
The two APIs are supported in the verilated models. Here is an example of usage
|
||||
with Linux :code:`fork()` and :code:`pthread_atfork` APIs:
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
// static function pointers to fit pthread_atfork
|
||||
static auto prepareClone = [](){ topp->prepareClone(); };
|
||||
static auto atClone = [](){ topp->atClone(); };
|
||||
|
||||
// in main function, register the handlers:
|
||||
pthread_atfork(prepareClone, atClone, atClone);
|
||||
|
||||
For better flexibility, you can also manually call the handlers before and
|
||||
after :code:`fork()`.
|
||||
|
||||
With the process-level clone APIs, users can create process-level snapshots
|
||||
for the verilated models. While the Verilator save/restore option provides
|
||||
persistent and circuit-dependent snapshots, the process-level clone APIs
|
||||
enable in-memory, circuit-transparent, and highly efficient snapshots.
|
||||
|
||||
|
||||
Direct Programming Interface (DPI)
|
||||
==================================
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@ C++11 compiler support
|
||||
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
|
||||
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
|
||||
July 2022.
|
||||
32-bit compiler support
|
||||
Verilator currently regresses both 64-bit and 32-bit pointer modes (GCC's
|
||||
`-m64` and `-m32`). Support for 32-bit `-m32` mode will be deprecated no
|
||||
sooner than January 2024.
|
||||
|
||||
Option `-O<letter>`
|
||||
The debug `-O<letter>` options have been replaced with
|
||||
|
||||
@@ -91,9 +91,29 @@ associated programs.
|
||||
|
||||
.. option:: VERILATOR_ROOT
|
||||
|
||||
Specifies the directory containing the distribution kit. This is used
|
||||
to find the executable, Perl library, and include files. If not
|
||||
specified, it will come from a default optionally specified at configure
|
||||
time (before Verilator was compiled). It should not be specified if
|
||||
using a pre-compiled Verilator package as the hard-coded value should be
|
||||
correct. See :ref:`Installation`.
|
||||
The ``VERILATOR_ROOT`` environment variable is used in several places:
|
||||
|
||||
* At ``./configure`` time: If set, it is embedded into the binary, and
|
||||
at runtime if ``VERILATOR_ROOT`` is not set, the embedded value is
|
||||
used for the runtime default.
|
||||
|
||||
* When ``verilator`` is run: If ``VERILATOR_ROOT`` is set it will be
|
||||
used to find the ``verilator_bin`` executable (this is the actual
|
||||
Verilator binary; ``verilator`` is a Perl wrapper). If not set, the
|
||||
``verilator`` script uses other methods to find ``verilator_bin``
|
||||
(looking in the same directory and falling back to ``$PATH``).
|
||||
|
||||
* When ``make`` is run on the Makefile generated by ``verilator``: The
|
||||
value of ``VERILATOR_ROOT`` (falling back to the value embedded in the
|
||||
binary if not set) is used to find the include files
|
||||
(``include/verilated.mk``).
|
||||
|
||||
If you are using a pre-compiled Verilator package, you should not need
|
||||
to set ``VERILATOR_ROOT`` - the value embedded in the binary should be
|
||||
correct. In fact this option *does not work* with Verilator packages
|
||||
that have been installed with ``make install``. If a Verilator package
|
||||
has been installed using ``./configure --prefix=/some/path && make
|
||||
install`` and then moved to another location, you cannot use
|
||||
``VERILATOR_ROOT`` to point to the new version.
|
||||
|
||||
See :ref:`Installation` for more details.
|
||||
|
||||
@@ -125,9 +125,12 @@ Summary:
|
||||
|
||||
After generating the SystemC/C++ code, Verilator will invoke the
|
||||
toolchain to build the model library (and executable when :vlopt:`--exe`
|
||||
is also used). Verilator manages the build itself, and for this --build
|
||||
is also used). Verilator manages the build itself, and for this --build
|
||||
requires GNU Make to be available on the platform.
|
||||
|
||||
:vlopt:`--build` cannot be specified when using :vlopt:`-E`,
|
||||
:vlopt:`--dpi-hdr-only`, :vlopt:`--lint-only`, or :vlopt:`--xml-only`.
|
||||
|
||||
.. option:: --build-dep-bin <filename>
|
||||
|
||||
Rarely needed. When a dependency (.d) file is created, this filename
|
||||
@@ -808,6 +811,13 @@ Summary:
|
||||
|
||||
See also :vlopt:`--binary`.
|
||||
|
||||
.. option:: --main-top-name <string>
|
||||
|
||||
Specify the name passed to the Verilated model being constructed, in the
|
||||
generated C++ main() function.
|
||||
|
||||
If the string ``"-"`` is used, no top level scope is added.
|
||||
|
||||
.. option:: --max-num-width <value>
|
||||
|
||||
Set the maximum number literal width (e.g., in 1024'd22 this
|
||||
@@ -1101,6 +1111,18 @@ Summary:
|
||||
marking only those signals that need public_flat_rw is typically
|
||||
significantly better performing.
|
||||
|
||||
.. option:: --public-depth <level>
|
||||
|
||||
Enables public as with :vlopt:`--public-flat-rw`, but only to the specified depth of modules.
|
||||
It operates at the module maximum level, so if a module's cells are A.B.X and A.X, the
|
||||
a --public-depth 3 must be used to make module X public, and both A.B.X and A.X will be public.
|
||||
|
||||
.. option:: --public-params
|
||||
|
||||
Declares all parameters public as if they had
|
||||
:code:`/*verilator public_flat_rd*/`
|
||||
metacomments.
|
||||
|
||||
.. option:: -pvalue+<name>=<value>
|
||||
|
||||
Overwrites the given parameter(s) of the top-level module. See
|
||||
@@ -1295,6 +1317,7 @@ Summary:
|
||||
This is not needed with standard designs with only one top. See also
|
||||
:option:`MULTITOP` warning.
|
||||
|
||||
|
||||
.. option:: --trace
|
||||
|
||||
Adds waveform tracing code to the model using VCD format. This overrides
|
||||
@@ -1373,6 +1396,17 @@ Summary:
|
||||
This option is accepted, but has absolutely no effect with
|
||||
:vlopt:`--trace`, which respects :vlopt:`--threads` instead.
|
||||
|
||||
.. option:: --no-trace-top
|
||||
|
||||
Disables tracing for the input and output signals in the top wrapper which
|
||||
Verilator adds to the design. The signals are still traced in the original
|
||||
verilog top modules.
|
||||
|
||||
When combined with :option:`--main-top-name` set to "-" or when the name of
|
||||
the top module is set to "" in its constructor, the generated trace file
|
||||
will have the verilog top module as its root, rather than another module
|
||||
added by Verilator.
|
||||
|
||||
.. option:: --trace-underscore
|
||||
|
||||
Enable tracing of signals or modules that start with an
|
||||
@@ -1455,8 +1489,11 @@ Summary:
|
||||
them systematically.
|
||||
|
||||
The generated file is in the Verilator Configuration format, see
|
||||
:ref:`Configuration Files`, and can directly be consumed by
|
||||
Verilator. The standard file extension is ".vlt".
|
||||
:ref:`Configuration Files`. The standard file extension is ".vlt".
|
||||
These files can directly be consumed by Verilator, typically by placing
|
||||
the filename as part of the Verilator command line options. Waiver files
|
||||
need to be listed on the command line before listing the files they are
|
||||
waiving.
|
||||
|
||||
.. option:: -Wall
|
||||
|
||||
@@ -1501,13 +1538,15 @@ Summary:
|
||||
|
||||
.. option:: -Wno-lint
|
||||
|
||||
Disable all lint-related warning messages, and all style warnings. This is
|
||||
equivalent to ``-Wno-ALWCOMBORDER -Wno-BSSPACE -Wno-CASEINCOMPLETE
|
||||
-Wno-CASEOVERLAP -Wno-CASEX -Wno-CASTCONST -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS
|
||||
-Wno-IMPLICIT -Wno-IMPLICITSTATIC -Wno-LITENDIAN -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED
|
||||
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
|
||||
-Wno-WIDTH`` plus the list shown for Wno-style.
|
||||
Disable all lint-related warning messages, and all style warnings. This
|
||||
is equivalent to ``-Wno-ALWCOMBORDER`` ``-Wno-ASCRANGE``
|
||||
``-Wno-BSSPACE`` ``-Wno-CASEINCOMPLETE`` ``-Wno-CASEOVERLAP``
|
||||
``-Wno-CASEX`` ``-Wno-CASTCONST`` ``-Wno-CASEWITHX`` ``-Wno-CMPCONST``
|
||||
``-Wno-COLONPLUS`` ``-Wno-IMPLICIT`` ``-Wno-IMPLICITSTATIC``
|
||||
``-Wno-PINCONNECTEMPTY`` ``-Wno-PINMISSING`` ``-Wno-STATICVAR``
|
||||
``-Wno-SYNCASYNCNET`` ``-Wno-UNDRIVEN`` ``-Wno-UNSIGNED``
|
||||
``-Wno-UNUSEDGENVAR`` ``-Wno-UNUSEDPARAM`` ``-Wno-UNUSEDSIGNAL``
|
||||
``-Wno-WIDTH``, plus the list shown for :vlopt:`-Wno-style`.
|
||||
|
||||
It is strongly recommended that you clean up your code rather than using this
|
||||
option; it is only intended to be used when running test-cases of code
|
||||
@@ -1515,12 +1554,13 @@ Summary:
|
||||
|
||||
.. option:: -Wno-style
|
||||
|
||||
Disable all code style related warning messages (note that by default, they are
|
||||
already disabled). This is equivalent to ``-Wno-DECLFILENAME -Wno-DEFPARAM
|
||||
-Wno-EOFNEWLINE -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN
|
||||
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
|
||||
-Wno-VARHIDDEN``.
|
||||
Disable all code style related warning messages (note that by default,
|
||||
they are already disabled). This is equivalent to ``-Wno-DECLFILENAME``
|
||||
``-Wno-DEFPARAM`` ``-Wno-EOFNEWLINE`` ``-Wno-GENUNNAMED``
|
||||
``-Wno-IMPORTSTAR`` ``-Wno-INCABSPATH`` ``-Wno-PINCONNECTEMPTY``
|
||||
``-Wno-PINNOCONNECT`` ``-Wno-SYNCASYNCNET`` ``-Wno-UNDRIVEN``
|
||||
``-Wno-UNUSEDGENVAR`` ``-Wno-UNUSEDPARAM`` ``-Wno-UNUSEDSIGNAL``
|
||||
``-Wno-VARHIDDEN``.
|
||||
|
||||
.. option:: -Wpedantic
|
||||
|
||||
@@ -1538,19 +1578,25 @@ Summary:
|
||||
|
||||
.. option:: -Wwarn-lint
|
||||
|
||||
Enable all lint-related warning messages (note that by default, they are already
|
||||
enabled), but do not affect style messages. This is equivalent to
|
||||
``-Wwarn-ALWCOMBORDER -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE
|
||||
-Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASTCONST -Wwarn-CASEWITHX -Wwarn-CMPCONST
|
||||
-Wwarn-COLONPLUS -Wwarn-IMPLICIT -Wwarn-LITENDIAN
|
||||
-Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED -Wwarn-WIDTH``.
|
||||
Enable all lint-related warning messages (note that by default, they are
|
||||
already enabled), but do not affect style messages. This is equivalent
|
||||
to ``-Wwarn-ALWCOMBORDER`` ``-Wwarn-ASCRANGE`` ``-Wwarn-BSSPACE``
|
||||
``-Wwarn-CASEINCOMPLETE`` ``-Wwarn-CASEOVERLAP`` ``-Wwarn-CASEWITHX``
|
||||
``-Wwarn-CASEX`` ``-Wwarn-CASTCONST`` ``-Wwarn-CMPCONST``
|
||||
``-Wwarn-COLONPLUS`` ``-Wwarn-IMPLICIT`` ``-Wwarn-IMPLICITSTATIC``
|
||||
``-Wwarn-LATCH`` ``-Wwarn-MISINDENT`` ``-Wwarn-NEWERSTD``
|
||||
``-Wwarn-PINMISSING`` ``-Wwarn-REALCVT`` ``-Wwarn-STATICVAR``
|
||||
``-Wwarn-UNSIGNED`` ``-Wwarn-WIDTHTRUNC`` ``-Wwarn-WIDTHEXPAND``
|
||||
``-Wwarn-WIDTHXZEXPAND``.
|
||||
|
||||
.. option:: -Wwarn-style
|
||||
|
||||
Enable all code style-related warning messages. This is equivalent to
|
||||
``-Wwarn ASSIGNDLY -Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-EOFNEWLINE
|
||||
-Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN
|
||||
-Wwarn-UNUSEDGENVAR -Wwarn-UNUSEDPARAM -Wwarn-UNUSEDSIGNAL -Wwarn-VARHIDDEN``.
|
||||
``-Wwarn-ASSIGNDLY`` ``-Wwarn-DECLFILENAME`` ``-Wwarn-DEFPARAM``
|
||||
``-Wwarn-EOFNEWLINE`` ``-Wwarn-GENUNNAMED`` ``-Wwarn-INCABSPATH``
|
||||
``-Wwarn-PINNOCONNECT`` ``-Wwarn-SYNCASYNCNET`` ``-Wwarn-UNDRIVEN``
|
||||
``-Wwarn-UNUSEDGENVAR`` ``-Wwarn-UNUSEDPARAM`` ``-Wwarn-UNUSEDSIGNAL``
|
||||
``-Wwarn-VARHIDDEN``.
|
||||
|
||||
.. option:: --x-assign 0
|
||||
|
||||
|
||||
+3
-3
@@ -356,8 +356,8 @@ Why do I get "undefined reference to \`VL_RAND_RESET_I' or \`Verilated::...'"?
|
||||
You need to link your compiled Verilated code against the
|
||||
:code:`verilated.cpp` file found in the include directory of the Verilator
|
||||
kit. This is one target in the ``$(VK_GLOBAL_OBJS)`` make variable, which
|
||||
should be part of your Makefile's link rule. If you use :vlopt:`--exe`,
|
||||
this is done for you.
|
||||
should be part of your Makefile's link rule. If you use :vlopt:`--exe` or
|
||||
:vlopt:`--binary`, this is done for you.
|
||||
|
||||
|
||||
Is the PLI supported?
|
||||
@@ -486,7 +486,7 @@ From the sc_main.cpp file, you'd then:
|
||||
|
||||
#include "Vour.h"
|
||||
#include "Vour_our.h"
|
||||
cout << "clock is " << top->our->clk << endl;
|
||||
std::cout << "clock is " << top->our->clk << std::endl;
|
||||
|
||||
|
||||
In this example, clk is a bool you can read or set as any other variable.
|
||||
|
||||
@@ -126,8 +126,10 @@ In specific debug and other modes, it also creates:
|
||||
- Debugging graph files (from --debug)
|
||||
* - *{prefix}{misc}*\ .tree
|
||||
- Debugging files (from --debug)
|
||||
* - {mod_prefix}_{each_verilog_base_filename}*\ .vpp
|
||||
- Pre-processed verilog (from --debug)
|
||||
* - *{prefix}*\ __inputs\ .vpp
|
||||
- Pre-processed verilog for all files (from --debug)
|
||||
* - *{prefix}*\ _ *{each_verilog_base_filename}*\ .vpp
|
||||
- Pre-processed verilog for each file (from --debug)
|
||||
|
||||
After running Make, the C++ compiler may produce the following:
|
||||
|
||||
|
||||
+22
-34
@@ -39,7 +39,7 @@ In brief, to install from git:
|
||||
::
|
||||
|
||||
# Prerequisites:
|
||||
#sudo apt-get install git perl python3 make autoconf g++ flex bison ccache
|
||||
#sudo apt-get install git help2man perl python3 make autoconf g++ flex bison ccache
|
||||
#sudo apt-get install libgoogle-perftools-dev numactl perl-doc
|
||||
#sudo apt-get install libfl2 # Ubuntu only (ignore if gives error)
|
||||
#sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error)
|
||||
@@ -104,6 +104,7 @@ for good performance:
|
||||
::
|
||||
|
||||
sudo apt-get install ccache # If present at build, needed for run
|
||||
sudo apt-get install mold # If present at build, needed for run
|
||||
sudo apt-get install libgoogle-perftools-dev numactl
|
||||
|
||||
The following is optional but is recommended for nicely rendered command line
|
||||
@@ -124,9 +125,9 @@ Those developing Verilator itself may also want these (see internals.rst):
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install gdb graphviz cmake clang clang-format-14 gprof lcov
|
||||
sudo apt-get install libclang-dev yapf3
|
||||
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe
|
||||
sudo apt-get install clang clang-format-14 cmake gdb gprof graphviz lcov
|
||||
sudo apt-get install libclang-dev yapf3 bear
|
||||
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
|
||||
cpan install Pod::Perldoc
|
||||
cpan install Parallel::Forker
|
||||
|
||||
@@ -201,8 +202,9 @@ These are the installation options:
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Our personal favorite is to always run Verilator in-place from its Git
|
||||
directory. This allows the easiest experimentation and upgrading, and
|
||||
allows many versions of Verilator to co-exist on a system.
|
||||
directory (don't run ``make install``). This allows the easiest
|
||||
experimentation and upgrading, and allows many versions of Verilator to
|
||||
co-exist on a system.
|
||||
|
||||
::
|
||||
|
||||
@@ -211,17 +213,18 @@ allows many versions of Verilator to co-exist on a system.
|
||||
./configure
|
||||
# Running will use files from $VERILATOR_ROOT, so no install needed
|
||||
|
||||
Note after installing (below steps), a calling program or shell must set
|
||||
the environment variable :option:`VERILATOR_ROOT` to point to this Git
|
||||
directory, then execute ``$VERILATOR_ROOT/bin/verilator``, which will find
|
||||
the path to all needed files.
|
||||
Note after installing (see `Installation`_), a calling program or shell
|
||||
must set the environment variable :option:`VERILATOR_ROOT` to point to this
|
||||
Git directory, then execute ``$VERILATOR_ROOT/bin/verilator``, which will
|
||||
find the path to all needed files.
|
||||
|
||||
|
||||
2. Install into a specific location
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
2. Install into a Specific Prefix
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You may eventually be installing onto a project/company-wide "CAD" tools
|
||||
disk that may support multiple versions of every tool. Tell configure the
|
||||
You may be an OS package maintainer building a Verilator package, or you
|
||||
may eventually be installing onto a project/company-wide "CAD" tools disk
|
||||
that may support multiple versions of every tool. Tell configure the
|
||||
eventual destination directory name. We recommend that the destination
|
||||
location include the Verilator version name:
|
||||
|
||||
@@ -232,7 +235,8 @@ location include the Verilator version name:
|
||||
# For the tarball, use the version number instead of git describe
|
||||
./configure --prefix /CAD_DISK/verilator/`git describe | sed "s/verilator_//"`
|
||||
|
||||
Note after installing (below steps), if you use `modulecmd
|
||||
Note after installing (see `Installation`_), you need to add the path to
|
||||
the ``bin`` directory to your ``PATH``. Or, if you use `modulecmd
|
||||
<http://modules.sourceforge.net/>`__, you'll want a module file like the
|
||||
following:
|
||||
|
||||
@@ -245,23 +249,7 @@ following:
|
||||
prepend-path PKG_CONFIG_PATH $install_root/share/pkgconfig
|
||||
|
||||
|
||||
3. Install into a Specific Path
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You may eventually install Verilator into a specific installation prefix,
|
||||
as most GNU tools support:
|
||||
|
||||
::
|
||||
|
||||
unset VERILATOR_ROOT # if your shell is bash
|
||||
unsetenv VERILATOR_ROOT # if your shell is csh
|
||||
./configure --prefix /opt/verilator-VERSION
|
||||
|
||||
Then after installing (below steps), you will need to add
|
||||
``/opt/verilator-VERSION/bin`` to your ``$PATH`` environment variable.
|
||||
|
||||
|
||||
4. Install System Globally
|
||||
3. Install System Globally
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The final option is to eventually install Verilator globally, using
|
||||
@@ -273,8 +261,8 @@ configure's default system paths:
|
||||
unsetenv VERILATOR_ROOT # if your shell is csh
|
||||
./configure
|
||||
|
||||
Then after installing (below), the binaries should be in a location
|
||||
already in your ``$PATH`` environment variable.
|
||||
Then after installing (see `Installation`_), the binaries should be in a
|
||||
location already in your ``$PATH`` environment variable.
|
||||
|
||||
|
||||
Configure
|
||||
|
||||
@@ -44,7 +44,7 @@ The best place to get started is to try the :ref:`Examples`.
|
||||
.. [#] Verilog is defined by the `Institute of Electrical and Electronics
|
||||
Engineers (IEEE) Standard for Verilog Hardware Description
|
||||
Language`, Std. 1364, released in 1995, 2001, and 2005. The
|
||||
Verilator documentation uses the shorthand, e.g., "IEEE 1394-2005",
|
||||
Verilator documentation uses the shorthand, e.g., "IEEE 1364-2005",
|
||||
to refer to the, e.g., 2005 version of this standard.
|
||||
|
||||
.. [#] SystemVerilog is defined by the `Institute of Electrical and
|
||||
|
||||
@@ -143,7 +143,7 @@ Functional Coverage
|
||||
-------------------
|
||||
|
||||
With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will
|
||||
translate functional coverage points the user has inserted manually win
|
||||
translate functional coverage points the user has inserted manually in
|
||||
SystemVerilog code through into the Verilated model.
|
||||
|
||||
Currently, all functional coverage points are specified using SystemVerilog
|
||||
@@ -498,3 +498,20 @@ Clang. The process in GCC 10 was as follows:
|
||||
Clang and GCC also support -fauto-profile, which uses sample-based
|
||||
feedback-directed optimization. See the appropriate compiler
|
||||
documentation.
|
||||
|
||||
|
||||
.. _Runtime Debugging:
|
||||
|
||||
Runtime Debugging
|
||||
=================
|
||||
|
||||
To debug a Verilated executable, use the standard GNU debugger ``gdb`` or a
|
||||
similar tool. Typically you will want to have debugger symbols inserted by
|
||||
the compiler, assertions enabled in the C library, and assertions enabled
|
||||
in the Verilated library. (These options slow down the executable, so do
|
||||
this only when debugging.) To enable this, Verilate with:
|
||||
|
||||
-CFLAGS -ggdb -LDFLAGS -ggdb -CFLAGS -DVL_DEBUG=1 -CFLAGS -D_GLIBCXX_DEBUG
|
||||
|
||||
The :vlopt:`-CFLAGS` and/or :vlopt:`-LDFLAGS` options pass arguments
|
||||
directly to the compiler or linker.
|
||||
|
||||
@@ -165,7 +165,7 @@ compiled on the different target system.
|
||||
|
||||
To support this, none of the files that Verilator produces will reference
|
||||
any configure-generated build-system-specific files, such as
|
||||
:file:`config.h` (which is renamed in Verilator to :file:`config_build.h`
|
||||
:file:`config.h` (which is renamed in Verilator to :file:`config_package.h`
|
||||
to reduce confusion.) The disadvantage of this approach is that
|
||||
:file:`include/verilatedos.h` must self-detect the requirements of the
|
||||
target system, rather than using configure.
|
||||
|
||||
+259
-19
@@ -97,6 +97,24 @@ List Of Warnings
|
||||
simulate correctly.
|
||||
|
||||
|
||||
.. option:: ASCRANGE
|
||||
|
||||
.. TODO better example
|
||||
|
||||
Warns that a packed vector is declared with ascending bit range
|
||||
(i.e. [0:7]). Descending bit range is now the overwhelming standard,
|
||||
and ascending ranges are now thus often due to simple oversight
|
||||
instead of intent (a notable exception is the OpenPOWER code base).
|
||||
|
||||
It also warns that an instance is declared with ascending range
|
||||
(i.e. [0:7] or [7]) and is connected to an N-wide signal.
|
||||
The bits will likely be in the reversed order from what people may expect
|
||||
(i.e., instance [0] will connect to signal bit [N-1] not bit [0]).
|
||||
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
|
||||
|
||||
.. option:: ASSIGNDLY
|
||||
|
||||
.. TODO better example
|
||||
@@ -378,6 +396,16 @@ List Of Warnings
|
||||
simulators.
|
||||
|
||||
|
||||
.. option:: CONSTRAINTIGN
|
||||
|
||||
Warns that Verilator does not support :code:`constraint`,
|
||||
:code:`constraint_mode`, or :code:`rand_mode`, and the construct was are
|
||||
ignored.
|
||||
|
||||
Ignoring this warning may make Verilator randomize() simulations differ
|
||||
from other simulators.
|
||||
|
||||
|
||||
.. option:: CONTASSREG
|
||||
|
||||
.. TODO better example
|
||||
@@ -642,6 +670,52 @@ List Of Warnings
|
||||
used as a clock.
|
||||
|
||||
|
||||
.. option:: GENUNNAMED
|
||||
|
||||
Warns that a generate block was unnamed and "genblk" will be used per
|
||||
IEEE.
|
||||
|
||||
The potential issue is that adding additional generate blocks will
|
||||
renumber the assigned names, which may cause eventual problems with
|
||||
synthesis constraints or other tools that depend on hierarchical paths
|
||||
remaining consistent.
|
||||
|
||||
Blocks that are empty may not be reported with this warning, as no
|
||||
scopes are created for empty blocks, so there is no harm in having them
|
||||
unnamed.
|
||||
|
||||
Disabled by default as this is a code-style warning; it will simulate
|
||||
correctly.
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
generate
|
||||
if (PARAM == 1) begin //<--- Warning
|
||||
end
|
||||
|
||||
Results in:
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-GENUNNAMED: example.v:2:9: Unnamed generate block (IEEE 1800-2017 27.6)
|
||||
|
||||
To fix this assign a label (often with the naming convention prefix of
|
||||
:code:`gen_` or :code:`g_`), for example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
generate
|
||||
if (PARAM == 1) begin : gen_param_1 //<--- Repaired
|
||||
end
|
||||
|
||||
Other tools with similar warnings: Verible's generate-label, "All
|
||||
generate block statements must have a label."
|
||||
|
||||
|
||||
.. option:: HIERBLOCK
|
||||
|
||||
Warns that the top module is marked as a hierarchy block by the
|
||||
@@ -738,14 +812,17 @@ List Of Warnings
|
||||
|
||||
This is a warning because the static default differs from C++, differs
|
||||
from class member function/tasks. Static is a more dangerous default
|
||||
then automatic as static prevents the function from being reinterant,
|
||||
then automatic as static prevents the function from being reentrant,
|
||||
which may be a source of bugs, and/or performance issues.
|
||||
|
||||
If the function does not require static behavior, change it to "function
|
||||
automatic".
|
||||
If the function is in a module, and does not require static behavior,
|
||||
change it to "function automatic".
|
||||
|
||||
If the function requires static behavior, change it to "function
|
||||
static".
|
||||
If the function is in a module, and requires static behavior, change it
|
||||
to "function static".
|
||||
|
||||
If the function is in a package, it defaults to static, and label the
|
||||
function's variables as static.
|
||||
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
@@ -845,23 +922,111 @@ List Of Warnings
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
|
||||
.. option:: LIFETIME
|
||||
|
||||
Error when a variable is referenced in a process that can outlive the process
|
||||
in which it was declared. This can happen when using 'fork..join_none' or
|
||||
'fork..join_any' blocks, which spawn process that can outlive their parents.
|
||||
This error occurs only when Verilator can't replace the reference with a
|
||||
reference to copy of this variable, local to the forked process. For example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 3
|
||||
|
||||
task foo(int local_var);
|
||||
fork
|
||||
#10 local_var++;
|
||||
#20 $display("local_var = %d", local_var);
|
||||
join_none
|
||||
endtask
|
||||
|
||||
In the example above 'local_var' exists only within scope of 'foo', once foo
|
||||
finishes, the stack frame containing 'i' gets removed. However, the process
|
||||
forked from foo continues, as it contains a delay. After 10 units of time
|
||||
pass, this process attempts to modify 'local_var'. However, this variable no
|
||||
longer exits. It can't be made local to the forked process upon spawning, because
|
||||
it's modified and can be referenced somewhere else, for example in the other
|
||||
forked process, that was delayed by 20 units of time in this example. Thus,
|
||||
there's no viable stack allocation for it.
|
||||
|
||||
In order to fix it, if the intent is not to share the variable's state outside
|
||||
of the process, then create a local copy of the variable.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 4
|
||||
|
||||
task foo(int local_var);
|
||||
fork
|
||||
#10 begin
|
||||
int forked_var = local_var;
|
||||
forked_var++;
|
||||
end
|
||||
#20 begin
|
||||
// Note that we are going to print the original value here,
|
||||
// as `forked_var`is a local copy that was initialized while
|
||||
// `foo` was still alive.
|
||||
int forked_var = local_var;
|
||||
$display("forked_var = %d", forked_var)
|
||||
end
|
||||
join_none
|
||||
endtask
|
||||
|
||||
If you need to share its state, another strategy is to ensure it's allocated
|
||||
statically:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 1
|
||||
|
||||
int static_var;
|
||||
|
||||
task foo();
|
||||
fork
|
||||
#10 static_var++;
|
||||
#20 $display("static_var = %d", static_var);
|
||||
join_none
|
||||
endtask
|
||||
|
||||
However, if you need to be able to instantiate at runtime, the solution would be to
|
||||
wrap it in an object, since the forked process can hold a reference to that object
|
||||
and ensure that the variable stays alive this way:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
class Wrapper;
|
||||
int m_var;
|
||||
|
||||
// Here we implicitly hold a reference to `this`
|
||||
task foo();
|
||||
fork
|
||||
#10 m_var++;
|
||||
#20 $display("this.m_var = %d", m_var);
|
||||
join_none
|
||||
endtask
|
||||
endclass
|
||||
|
||||
// Here we explicitly hold a handle to an object
|
||||
task bar(Wrapper wrapper);
|
||||
fork
|
||||
#10 wrapper.m_var++;
|
||||
#20 $display("wrapper.m_var = %d", wrapper.m_var);
|
||||
join_none
|
||||
endtask
|
||||
|
||||
.. option:: LITENDIAN
|
||||
|
||||
.. TODO better example
|
||||
|
||||
Warns that a packed vector is declared with big endian bit numbering
|
||||
(i.e. [0:7]). Little endian bit numbering is now the overwhelming
|
||||
standard, and big numbering is now thus often due to simple oversight
|
||||
instead of intent.
|
||||
|
||||
It also warns that an instance is declared with big endian range
|
||||
(i.e. [0:7] or [7]) and is connected to an N-wide signal.
|
||||
The bits will likely be backward from what people may expect
|
||||
(i.e., instance [0] will connect to signal bit [N-1] not bit [0]).
|
||||
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
The naming of this warning is in contradiction with the common
|
||||
interpretation of little endian. It was therefore renamed to
|
||||
:option:`ASCRANGE`. While :option:`LITENDIAN` remains for
|
||||
backwards compatibility, new projects should use :option:`ASCRANGE`.
|
||||
|
||||
|
||||
.. option:: MINTYPMAX
|
||||
@@ -874,6 +1039,51 @@ List Of Warnings
|
||||
unsupported. Verilator uses only the typical delay value.
|
||||
|
||||
|
||||
.. option:: MISINDENT
|
||||
|
||||
Warns that the indentation of a statement is misleading, suggesting the
|
||||
statement is part of a previous :code:`if` or :code:`while` block while
|
||||
it is not.
|
||||
|
||||
Verilator suppresses this check when there is an inconsistent mix of
|
||||
spaces and tabs, as it cannot ensure the width of tabs. Verilator also
|
||||
ignores blocks with :code:`begin`/:code:`end`, as the :code:`end`
|
||||
visually indicates the earlier statement's end.
|
||||
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
|
||||
For example
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 3
|
||||
|
||||
if (something)
|
||||
statement_in_if;
|
||||
statement_not_in_if; //<--- Warning
|
||||
|
||||
Results in:
|
||||
|
||||
.. code-block::
|
||||
|
||||
%Warning-MISINDENT: example.v:3:9: Misleading indentation
|
||||
|
||||
To fix this repair the indentation to match the correct earlier
|
||||
statement, for example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 3
|
||||
|
||||
if (something)
|
||||
statement_in_if;
|
||||
statement_not_in_if; //<--- Repaired
|
||||
|
||||
Other tools with similar warnings: GCC -Wmisleading-indentation,
|
||||
clang-tidy readability-misleading-indentation.
|
||||
|
||||
|
||||
.. option:: MODDUP
|
||||
|
||||
.. TODO better example
|
||||
@@ -952,6 +1162,21 @@ List Of Warnings
|
||||
(neither :vlopt:`--timing` nor :vlopt:`--no-timing` option was provided).
|
||||
|
||||
|
||||
.. option:: NEWERSTD
|
||||
|
||||
Warns that a feature requires a newer standard of Verilog or SystemVerilog
|
||||
than the one specified by the :vlopt:`--language` option. For example, unsized
|
||||
unbased literals (`'0`, `'1`, `'z`, `'x`) require 1800-2005 or later.
|
||||
|
||||
To avoid this warning, use a Verilog or SystemVerilog standard that
|
||||
supports the feature. Alternatively, modify your code to use a different
|
||||
syntax that is supported by the Verilog/SystemVerilog standard specified
|
||||
by the :vlopt:`--language` option.
|
||||
|
||||
Ignoring this warning will only suppress the lint check; it will
|
||||
simulate correctly.
|
||||
|
||||
|
||||
.. option:: NOLATCH
|
||||
|
||||
.. TODO better example
|
||||
@@ -1302,6 +1527,17 @@ List Of Warnings
|
||||
* The variable is tristate or bidirectional. (e.g., :code:`inout`).
|
||||
|
||||
|
||||
.. option:: STATICVAR
|
||||
|
||||
Warns that a static variable declared in a loop with declaration assignment
|
||||
was converted to automatic. Often such variables were intended to
|
||||
instead be declared "automatic".
|
||||
|
||||
Ignoring this warning may make Verilator differ from other simulators,
|
||||
which will treat the variable as static. Verilator may in future versions also
|
||||
treat the variable as static.
|
||||
|
||||
|
||||
.. option:: STMTDLY
|
||||
|
||||
Warns that the code has a statement with a delayed time in front of it.
|
||||
@@ -1754,11 +1990,15 @@ List Of Warnings
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
wait(0); // Blocks forever
|
||||
wait(1); // Blocks forever
|
||||
|
||||
Warns that a `wait` statement awaits a constant condition, which means it
|
||||
either blocks forever or never blocks.
|
||||
|
||||
As a special case `wait(0)` with the literal constant `0` (as opposed to
|
||||
something that elaborates to zero), does not warn, as it is presumed the
|
||||
code is making the intent clear.
|
||||
|
||||
|
||||
.. option:: WIDTH
|
||||
|
||||
@@ -1813,7 +2053,7 @@ List Of Warnings
|
||||
|
||||
.. option:: WIDTHXZEXPAND
|
||||
|
||||
A more granular WIDTH warning, for when a value is xz expanded
|
||||
A more granular WIDTH warning, for when a value is X/Z expanded
|
||||
|
||||
.. option:: WIDTHCONCAT
|
||||
|
||||
|
||||
+39
-26
@@ -628,21 +628,32 @@ 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
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
``VlForever``
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
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.
|
||||
A small utility awaitable type. It allows for blocking a coroutine forever. It
|
||||
is currently only used for ``wait`` statements that await a constant false
|
||||
condition. See the `Timing Pass` section for more details.
|
||||
|
||||
Timing Pass
|
||||
~~~~~~~~~~~
|
||||
|
||||
The visitor in ``V3Timing.cpp`` transforms each timing control into a ``co_await``.
|
||||
There are two visitors in ``V3Timing.cpp``.
|
||||
|
||||
The first one, ``TimingSuspendableVisitor``, does not perform any AST
|
||||
transformations. It is responsible for marking processes and C++ functions that
|
||||
contain timing controls as suspendable. Processes that call suspendable
|
||||
functions are also marked as suspendable. Functions that call, are overridden
|
||||
by, or override suspendable functions are marked as suspendable as well.
|
||||
|
||||
The visitor keeps a dependency graph of functions and processes to handle such
|
||||
cases. A function or process is dependent on a function if it calls it. A
|
||||
virtual class method is dependent on another class method if it calls it,
|
||||
overrides it, or is overriden by it.
|
||||
|
||||
The second visitor in ``V3Timing.cpp``, ``TimingControlVisitor``, uses the
|
||||
information provided by ``TimingSuspendableVisitor`` and 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
|
||||
@@ -674,14 +685,9 @@ 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.
|
||||
Suspendable functions get the return type of ``VlCoroutine``, which makes them
|
||||
coroutines. Later, during ``V3Sched``, suspendable processes are also
|
||||
transformed into coroutines.
|
||||
|
||||
Scheduling with timing
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -749,9 +755,7 @@ 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 are 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.
|
||||
locals are at least as long as the execution of the forked processes.
|
||||
|
||||
|
||||
Multithreaded Mode
|
||||
@@ -1509,8 +1513,19 @@ debug level 5, with the V3Width.cpp file at level 9.
|
||||
--debug
|
||||
-------
|
||||
|
||||
When you run with ``--debug``, there are two primary output file types
|
||||
placed into the obj_dir, .tree and .dot files.
|
||||
When you run with ``--debug``, there are three primary output file types
|
||||
placed into the obj_dir, .vpp, .tree and .dot files.
|
||||
|
||||
.vpp Output
|
||||
-----------
|
||||
|
||||
Verilator creates a *{mod_prefix}*\ __inputs\ .vpp file containing all the
|
||||
files that were read, filtered by preprocessing. This file can be fed back
|
||||
into Verilator, replacing on the command line all of the previous input
|
||||
files, to enable simplification of test cases.
|
||||
|
||||
Verilator also creates .vpp files for each individual file passed on the
|
||||
command line.
|
||||
|
||||
|
||||
.dot Output
|
||||
@@ -1807,8 +1822,6 @@ IEEE 1800-2017 6.12 "shortreal"
|
||||
Little/no tool support, and easily promoted to real.
|
||||
IEEE 1800-2017 11.11 Min, typ, max
|
||||
No SDF support, so will always use typical.
|
||||
IEEE 1800-2017 11.12 "let"
|
||||
Little/no tool support, makes it difficult to implement parsers.
|
||||
IEEE 1800-2017 20.16 Stochastic analysis
|
||||
Little industry use.
|
||||
IEEE 1800-2017 20.17 PLA modeling
|
||||
@@ -2110,7 +2123,7 @@ Or in a hand-written C++ wrapper:
|
||||
::
|
||||
|
||||
#ifdef TEST_VERBOSE
|
||||
cout << "Read a=" << a << endl;
|
||||
std::cout << "Read a=" << a << std::endl;
|
||||
#endif
|
||||
|
||||
A filename that should be used to check the output results is given with
|
||||
|
||||
+46
-1
@@ -6,6 +6,7 @@ Alexandre
|
||||
Ami
|
||||
Amir
|
||||
Anastasiadis
|
||||
Anglin
|
||||
Anikin
|
||||
Antmicro
|
||||
Antonin
|
||||
@@ -42,6 +43,8 @@ Chandan
|
||||
Chitlesh
|
||||
Christophe
|
||||
Cochrane
|
||||
Conda
|
||||
Corteggiani
|
||||
Cuan
|
||||
Cygwin
|
||||
DErrico
|
||||
@@ -53,6 +56,7 @@ Das
|
||||
Dcache
|
||||
Deadman
|
||||
Debacker
|
||||
Deepa
|
||||
Defparams
|
||||
Denio
|
||||
Deprecations
|
||||
@@ -96,6 +100,7 @@ Fredieu
|
||||
GTKWave
|
||||
Galbi
|
||||
Gantt
|
||||
Garnal
|
||||
Gartner
|
||||
Gelinek
|
||||
Geoff
|
||||
@@ -188,6 +193,7 @@ Laurens
|
||||
Lavino
|
||||
Leber
|
||||
Leendert
|
||||
Lem
|
||||
Lesik
|
||||
Liland
|
||||
Liu
|
||||
@@ -207,6 +213,8 @@ Matveyenko
|
||||
Maupin
|
||||
Mdir
|
||||
Mednick
|
||||
Menküc
|
||||
Michail
|
||||
Michiels
|
||||
Microsystems
|
||||
Milanovic
|
||||
@@ -223,8 +231,10 @@ Muhlestein
|
||||
Multithreaded
|
||||
Multithreading
|
||||
NOUNOPTFLAT
|
||||
NaN
|
||||
Nalbantis
|
||||
Narayan
|
||||
Nassim
|
||||
Nauticus
|
||||
Newgard
|
||||
Nigam
|
||||
@@ -238,6 +248,7 @@ Olof
|
||||
Olofsson
|
||||
Oyvind
|
||||
PLI
|
||||
Palaniappan
|
||||
Patricio
|
||||
Petr
|
||||
Piechotka
|
||||
@@ -252,6 +263,8 @@ Prateek
|
||||
Pre
|
||||
Preprocess
|
||||
Pretet
|
||||
Pretl
|
||||
Pringlemeir
|
||||
Priyadharshini
|
||||
Pullup
|
||||
Pulver
|
||||
@@ -266,6 +279,7 @@ Renga
|
||||
Requin
|
||||
Rodionov
|
||||
Rolfe
|
||||
Rontionov
|
||||
Roodselaar
|
||||
Runtime
|
||||
Ruud
|
||||
@@ -295,6 +309,7 @@ Solomatnikov
|
||||
Solt
|
||||
Southwell
|
||||
Srini
|
||||
Srinivasan
|
||||
Stamness
|
||||
Stephane
|
||||
Stoddard
|
||||
@@ -319,6 +334,7 @@ Thiede
|
||||
Thierry
|
||||
Thyer
|
||||
Tichelaar
|
||||
Timi
|
||||
Tomov
|
||||
Tood
|
||||
Topa
|
||||
@@ -343,6 +359,7 @@ Vdeeptemp
|
||||
Vdly
|
||||
Vemumtab
|
||||
Vemuri
|
||||
Venkataramanan
|
||||
Veriable
|
||||
Verialted
|
||||
Verilate
|
||||
@@ -353,6 +370,7 @@ Verilator
|
||||
Verilog
|
||||
Vighnesh
|
||||
Viktor
|
||||
Vilp
|
||||
Vm
|
||||
Vukobratovic
|
||||
Wai
|
||||
@@ -363,6 +381,7 @@ Wfuture
|
||||
Whatson
|
||||
Wildman
|
||||
Wim
|
||||
Wmisleading
|
||||
Wno
|
||||
Wojciech
|
||||
Wolfel
|
||||
@@ -376,6 +395,7 @@ Xuan
|
||||
Xuanqi
|
||||
Yazdanbakhsh
|
||||
Yi
|
||||
Yike
|
||||
Yinan
|
||||
Yosys
|
||||
Yu
|
||||
@@ -396,7 +416,9 @@ architected
|
||||
args
|
||||
arrarys
|
||||
assertOn
|
||||
astgen
|
||||
async
|
||||
atClone
|
||||
ato
|
||||
atoi
|
||||
autoconf
|
||||
@@ -452,12 +474,14 @@ commandArgsPlusMatch
|
||||
compilable
|
||||
concat
|
||||
concats
|
||||
conf
|
||||
config
|
||||
const
|
||||
constexpr
|
||||
constpool
|
||||
coredump
|
||||
coroutine
|
||||
coroutines
|
||||
countbits
|
||||
countones
|
||||
cout
|
||||
@@ -512,6 +536,7 @@ dumpportson
|
||||
dumpvars
|
||||
dut
|
||||
dx
|
||||
dynarray
|
||||
elab
|
||||
elike
|
||||
elsif
|
||||
@@ -520,6 +545,7 @@ endcelldefine
|
||||
endfunction
|
||||
endgenerate
|
||||
endian
|
||||
endianness
|
||||
endif
|
||||
endmodule
|
||||
endprotect
|
||||
@@ -541,6 +567,7 @@ exe
|
||||
executables
|
||||
expr
|
||||
extern
|
||||
ezchi
|
||||
fanin
|
||||
fasttrace
|
||||
fauto
|
||||
@@ -585,6 +612,7 @@ genvar
|
||||
genvars
|
||||
getenv
|
||||
getline
|
||||
ggdb
|
||||
gmake
|
||||
gmon
|
||||
gotFinish
|
||||
@@ -629,12 +657,14 @@ ish
|
||||
isunbounded
|
||||
isunknown
|
||||
jobserver
|
||||
jwoutersymatra
|
||||
killua
|
||||
lang
|
||||
lcov
|
||||
ld
|
||||
leavinel
|
||||
len
|
||||
libc
|
||||
libext
|
||||
libgoogle
|
||||
libsystemc
|
||||
@@ -653,7 +683,9 @@ logicals
|
||||
longint
|
||||
lossy
|
||||
lsb
|
||||
lubc
|
||||
lvalue
|
||||
lvalues
|
||||
lxt
|
||||
macromodule
|
||||
makefile
|
||||
@@ -678,12 +710,15 @@ mtasks
|
||||
mulithreaded
|
||||
mult
|
||||
multidim
|
||||
multidriven
|
||||
multiinterfaces
|
||||
multiline
|
||||
multipling
|
||||
multipoint
|
||||
multithread
|
||||
multithreaded
|
||||
multithreading
|
||||
musl
|
||||
mutexes
|
||||
mux
|
||||
myftptoyman
|
||||
@@ -714,6 +749,7 @@ ortegon
|
||||
oversubscription
|
||||
parallelized
|
||||
param
|
||||
parameterization
|
||||
parameterized
|
||||
params
|
||||
parens
|
||||
@@ -741,6 +777,7 @@ pragmas
|
||||
pre
|
||||
precisions
|
||||
predefines
|
||||
prepareClone
|
||||
prepend
|
||||
prepended
|
||||
preprocess
|
||||
@@ -762,10 +799,12 @@ pulldowns
|
||||
pullup
|
||||
pvalue
|
||||
pwd
|
||||
py
|
||||
qrq
|
||||
radix
|
||||
randc
|
||||
randcase
|
||||
randstate
|
||||
rarr
|
||||
rdtsc
|
||||
reStructuredText
|
||||
@@ -807,6 +846,7 @@ specparam
|
||||
splitme
|
||||
spp
|
||||
sqrt
|
||||
srandom
|
||||
src
|
||||
srcdir
|
||||
srcfile
|
||||
@@ -838,6 +878,7 @@ svdpi
|
||||
swrite
|
||||
sys
|
||||
systemc
|
||||
tenghtt
|
||||
testbench
|
||||
threadsafe
|
||||
threashold
|
||||
@@ -869,10 +910,12 @@ typedefs
|
||||
typename
|
||||
uint
|
||||
un
|
||||
unbased
|
||||
undef
|
||||
undefineall
|
||||
undriven
|
||||
ungetc
|
||||
unhandled
|
||||
uniquified
|
||||
unistd
|
||||
unlink
|
||||
@@ -893,7 +936,9 @@ vc
|
||||
vcd
|
||||
vcddiff
|
||||
vcoverage
|
||||
vdhotre
|
||||
vec
|
||||
ventana
|
||||
ver
|
||||
verFiles
|
||||
verible
|
||||
@@ -909,6 +954,7 @@ vlt
|
||||
vltstd
|
||||
vluint
|
||||
vpi
|
||||
vpiDefName
|
||||
vpiLeftRange
|
||||
vpiModule
|
||||
vpiSize
|
||||
@@ -938,4 +984,3 @@ ypq
|
||||
yurivict
|
||||
zdave
|
||||
Øyvind
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//======================================================================
|
||||
|
||||
// SystemC global header
|
||||
#include <systemc.h>
|
||||
#include <systemc>
|
||||
|
||||
// Include common routines
|
||||
#include <verilated.h>
|
||||
@@ -15,6 +15,8 @@
|
||||
// Include model header, generated from Verilating "top.v"
|
||||
#include "Vtop.h"
|
||||
|
||||
using namespace sc_core;
|
||||
|
||||
int sc_main(int argc, char* argv[]) {
|
||||
// See a similar example walkthrough in the verilator manpage.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <memory>
|
||||
|
||||
// SystemC global header
|
||||
#include <systemc.h>
|
||||
#include <systemc>
|
||||
|
||||
// Include common routines
|
||||
#include <verilated.h>
|
||||
@@ -23,6 +23,8 @@
|
||||
// Include model header, generated from Verilating "top.v"
|
||||
#include "Vtop.h"
|
||||
|
||||
using namespace sc_core;
|
||||
|
||||
int sc_main(int argc, char* argv[]) {
|
||||
// This is a more complicated example, please also see the simpler examples/make_hello_c.
|
||||
|
||||
|
||||
+68
-72
@@ -58,6 +58,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
@@ -131,7 +132,7 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3
|
||||
#define FST_GZIO_LEN (32768)
|
||||
#define FST_HDR_FOURPACK_DUO_SIZE (4*1024*1024)
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(_AIX)
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(_AIX) || defined(__aarch64__)
|
||||
#define FST_DO_MISALIGNED_OPS
|
||||
#endif
|
||||
|
||||
@@ -334,26 +335,22 @@ return(NULL);
|
||||
/*
|
||||
* mmap compatibility
|
||||
*/
|
||||
#if defined __CYGWIN__ || defined __MINGW32__
|
||||
#if defined __MINGW32__
|
||||
#include <limits.h>
|
||||
#define fstMmap(__addr,__len,__prot,__flags,__fd,__off) fstMmap2((__len), (__fd), (__off))
|
||||
#define fstMunmap(__addr,__len) free(__addr)
|
||||
#define fstMunmap(__addr,__len) UnmapViewOfFile((LPCVOID)__addr)
|
||||
|
||||
static void *fstMmap2(size_t __len, int __fd, fst_off_t __off)
|
||||
{
|
||||
(void)__off;
|
||||
HANDLE handle = CreateFileMapping((HANDLE)_get_osfhandle(__fd), NULL,
|
||||
PAGE_READWRITE, (DWORD)(__len >> 32),
|
||||
(DWORD)__len, NULL);
|
||||
if (!handle) { return NULL; }
|
||||
|
||||
unsigned char *pnt = (unsigned char *)malloc(__len);
|
||||
fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
|
||||
size_t i;
|
||||
|
||||
lseek(__fd, 0, SEEK_SET);
|
||||
for(i=0;i<__len;i+=SSIZE_MAX)
|
||||
{
|
||||
read(__fd, pnt + i, ((__len - i) >= SSIZE_MAX) ? SSIZE_MAX : (__len - i));
|
||||
}
|
||||
lseek(__fd, cur_offs, SEEK_SET);
|
||||
return(pnt);
|
||||
void *ptr = MapViewOfFileEx(handle, FILE_MAP_READ | FILE_MAP_WRITE,
|
||||
0, (DWORD)__off, (SIZE_T)__len, (LPVOID)NULL);
|
||||
CloseHandle(handle);
|
||||
return ptr;
|
||||
}
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
@@ -984,14 +981,26 @@ fflush(xc->handle);
|
||||
*/
|
||||
static void fstWriterMmapSanity(void *pnt, const char *file, int line, const char *usage)
|
||||
{
|
||||
#if !defined(__CYGWIN__) && !defined(__MINGW32__)
|
||||
if(pnt == MAP_FAILED)
|
||||
if(pnt == NULL
|
||||
#ifdef MAP_FAILED
|
||||
|| pnt == MAP_FAILED
|
||||
#endif
|
||||
)
|
||||
{
|
||||
fprintf(stderr, "fstMmap() assigned to %s failed: errno: %d, file %s, line %d.\n", usage, errno, file, line);
|
||||
#if !defined(__MINGW32__)
|
||||
perror("Why");
|
||||
#else
|
||||
LPSTR mbuf = NULL;
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
|
||||
| FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPSTR)&mbuf, 0, NULL);
|
||||
fprintf(stderr, "%s", mbuf);
|
||||
LocalFree(mbuf);
|
||||
#endif
|
||||
pnt = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1037,34 +1046,11 @@ if(!xc->curval_mem)
|
||||
|
||||
static void fstDestroyMmaps(struct fstWriterContext *xc, int is_closing)
|
||||
{
|
||||
#if !defined __CYGWIN__ && !defined __MINGW32__
|
||||
(void)is_closing;
|
||||
#endif
|
||||
|
||||
fstMunmap(xc->valpos_mem, xc->maxhandle * 4 * sizeof(uint32_t));
|
||||
xc->valpos_mem = NULL;
|
||||
|
||||
#if defined __CYGWIN__ || defined __MINGW32__
|
||||
if(xc->curval_mem)
|
||||
{
|
||||
if(!is_closing) /* need to flush out for next emulated mmap() read */
|
||||
{
|
||||
unsigned char *pnt = xc->curval_mem;
|
||||
int __fd = fileno(xc->curval_handle);
|
||||
fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR);
|
||||
size_t i;
|
||||
size_t __len = xc->maxvalpos;
|
||||
|
||||
lseek(__fd, 0, SEEK_SET);
|
||||
for(i=0;i<__len;i+=SSIZE_MAX)
|
||||
{
|
||||
write(__fd, pnt + i, ((__len - i) >= SSIZE_MAX) ? SSIZE_MAX : (__len - i));
|
||||
}
|
||||
lseek(__fd, cur_offs, SEEK_SET);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
fstMunmap(xc->curval_mem, xc->maxvalpos);
|
||||
xc->curval_mem = NULL;
|
||||
}
|
||||
@@ -2066,7 +2052,8 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
||||
int zfd;
|
||||
int fourpack_duo = 0;
|
||||
#ifndef __MINGW32__
|
||||
char *fnam = (char *)malloc(strlen(xc->filename) + 5 + 1);
|
||||
int fnam_len = strlen(xc->filename) + 5 + 1;
|
||||
char *fnam = (char *)malloc(fnam_len);
|
||||
#endif
|
||||
|
||||
fixup_offs = ftello(xc->handle);
|
||||
@@ -2156,7 +2143,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush)
|
||||
fflush(xc->handle);
|
||||
|
||||
#ifndef __MINGW32__
|
||||
sprintf(fnam, "%s.hier", xc->filename);
|
||||
snprintf(fnam, fnam_len, "%s.hier", xc->filename);
|
||||
unlink(fnam);
|
||||
free(fnam);
|
||||
#endif
|
||||
@@ -2835,7 +2822,7 @@ if(ctx && name && literal_arr && val_arr && (elem_count != 0))
|
||||
uint32_t i;
|
||||
|
||||
name_len = strlen(name);
|
||||
elem_count_len = sprintf(elem_count_buf, "%" PRIu32, elem_count);
|
||||
elem_count_len = snprintf(elem_count_buf, 16, "%" PRIu32, elem_count);
|
||||
|
||||
literal_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int));
|
||||
val_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int));
|
||||
@@ -3412,6 +3399,7 @@ int flat_hier_alloc_len;
|
||||
unsigned do_rewind : 1;
|
||||
char str_scope_nam[FST_ID_NAM_SIZ+1];
|
||||
char str_scope_comp[FST_ID_NAM_SIZ+1];
|
||||
char *str_scope_attr;
|
||||
|
||||
unsigned fseek_failed : 1;
|
||||
|
||||
@@ -3922,7 +3910,8 @@ int pass_status = 1;
|
||||
if(!xc->fh)
|
||||
{
|
||||
fst_off_t offs_cache = ftello(xc->f);
|
||||
char *fnam = (char *)malloc(strlen(xc->filename) + 6 + 16 + 32 + 1);
|
||||
int fnam_len = strlen(xc->filename) + 6 + 16 + 32 + 1;
|
||||
char *fnam = (char *)malloc(fnam_len);
|
||||
unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN);
|
||||
fst_off_t hl, uclen;
|
||||
fst_off_t clen = 0;
|
||||
@@ -3941,7 +3930,7 @@ if(!xc->fh)
|
||||
htyp = xc->contains_hier_section_lz4duo ? FST_BL_HIER_LZ4DUO : FST_BL_HIER_LZ4;
|
||||
}
|
||||
|
||||
sprintf(fnam, "%s.hier_%d_%p", xc->filename, getpid(), (void *)xc);
|
||||
snprintf(fnam, fnam_len, "%s.hier_%d_%p", xc->filename, getpid(), (void *)xc);
|
||||
fstReaderFseeko(xc, xc->f, xc->hier_pos, SEEK_SET);
|
||||
uclen = fstReaderUint64(xc->f);
|
||||
#ifndef __MINGW32__
|
||||
@@ -4140,7 +4129,7 @@ if(!(isfeof=feof(xc->fh)))
|
||||
cl = 0;
|
||||
while((ch = fgetc(xc->fh)))
|
||||
{
|
||||
if(cl <= FST_ID_NAM_SIZ)
|
||||
if(cl < FST_ID_NAM_SIZ)
|
||||
{
|
||||
pnt[cl++] = ch;
|
||||
}
|
||||
@@ -4152,7 +4141,7 @@ if(!(isfeof=feof(xc->fh)))
|
||||
cl = 0;
|
||||
while((ch = fgetc(xc->fh)))
|
||||
{
|
||||
if(cl <= FST_ID_NAM_SIZ)
|
||||
if(cl < FST_ID_NAM_SIZ)
|
||||
{
|
||||
pnt[cl++] = ch;
|
||||
}
|
||||
@@ -4169,15 +4158,19 @@ if(!(isfeof=feof(xc->fh)))
|
||||
xc->hier.htyp = FST_HT_ATTRBEGIN;
|
||||
xc->hier.u.attr.typ = fgetc(xc->fh);
|
||||
xc->hier.u.attr.subtype = fgetc(xc->fh);
|
||||
xc->hier.u.attr.name = pnt = xc->str_scope_nam;
|
||||
if(!xc->str_scope_attr)
|
||||
{
|
||||
xc->str_scope_attr = (char *)calloc(1, FST_ID_NAM_ATTR_SIZ+1);
|
||||
}
|
||||
xc->hier.u.attr.name = pnt = xc->str_scope_attr;
|
||||
cl = 0;
|
||||
while((ch = fgetc(xc->fh)))
|
||||
{
|
||||
if(cl <= FST_ID_NAM_SIZ)
|
||||
if(cl < FST_ID_NAM_ATTR_SIZ)
|
||||
{
|
||||
pnt[cl++] = ch;
|
||||
}
|
||||
}; /* scopename */
|
||||
}; /* attrname */
|
||||
pnt[cl] = 0;
|
||||
xc->hier.u.attr.name_length = cl;
|
||||
|
||||
@@ -4188,7 +4181,7 @@ if(!(isfeof=feof(xc->fh)))
|
||||
if((xc->hier.u.attr.subtype == FST_MT_SOURCESTEM)||(xc->hier.u.attr.subtype == FST_MT_SOURCEISTEM))
|
||||
{
|
||||
int sidx_skiplen_dummy = 0;
|
||||
xc->hier.u.attr.arg_from_name = fstGetVarint64((unsigned char *)xc->str_scope_nam, &sidx_skiplen_dummy);
|
||||
xc->hier.u.attr.arg_from_name = fstGetVarint64((unsigned char *)xc->str_scope_attr, &sidx_skiplen_dummy);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4237,7 +4230,7 @@ if(!(isfeof=feof(xc->fh)))
|
||||
cl = 0;
|
||||
while((ch = fgetc(xc->fh)))
|
||||
{
|
||||
if(cl <= FST_ID_NAM_SIZ)
|
||||
if(cl < FST_ID_NAM_SIZ)
|
||||
{
|
||||
pnt[cl++] = ch;
|
||||
}
|
||||
@@ -4376,7 +4369,7 @@ while(!feof(xc->fh))
|
||||
cl = 0;
|
||||
while((ch = fgetc(xc->fh)))
|
||||
{
|
||||
if(cl <= FST_ID_NAM_ATTR_SIZ)
|
||||
if(cl < FST_ID_NAM_ATTR_SIZ)
|
||||
{
|
||||
pnt[cl++] = ch;
|
||||
}
|
||||
@@ -4398,7 +4391,7 @@ while(!feof(xc->fh))
|
||||
cl = 0;
|
||||
while((ch = fgetc(xc->fh)))
|
||||
{
|
||||
if(cl <= FST_ID_NAM_ATTR_SIZ)
|
||||
if(cl < FST_ID_NAM_ATTR_SIZ)
|
||||
{
|
||||
pnt[cl++] = ch;
|
||||
}
|
||||
@@ -4487,7 +4480,7 @@ while(!feof(xc->fh))
|
||||
cl = 0;
|
||||
while((ch = fgetc(xc->fh)))
|
||||
{
|
||||
if(cl <= FST_ID_NAM_ATTR_SIZ)
|
||||
if(cl < FST_ID_NAM_ATTR_SIZ)
|
||||
{
|
||||
pnt[cl++] = ch;
|
||||
}
|
||||
@@ -4594,15 +4587,17 @@ if(sectype == FST_BL_ZWRAPPER)
|
||||
int zfd;
|
||||
int flen = strlen(xc->filename);
|
||||
char *hf;
|
||||
int hf_len;
|
||||
|
||||
seclen = fstReaderUint64(xc->f);
|
||||
uclen = fstReaderUint64(xc->f);
|
||||
|
||||
if(!seclen) return(0); /* not finished compressing, this is a failed read */
|
||||
|
||||
hf = (char *)calloc(1, flen + 16 + 32 + 1);
|
||||
hf_len = flen + 16 + 32 + 1;
|
||||
hf = (char *)calloc(1, hf_len);
|
||||
|
||||
sprintf(hf, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc);
|
||||
snprintf(hf, hf_len, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc);
|
||||
fcomp = fopen(hf, "w+b");
|
||||
if(!fcomp)
|
||||
{
|
||||
@@ -4974,6 +4969,7 @@ if(xc)
|
||||
free(xc->signal_typs); xc->signal_typs = NULL;
|
||||
free(xc->signal_lens); xc->signal_lens = NULL;
|
||||
free(xc->filename); xc->filename = NULL;
|
||||
free(xc->str_scope_attr); xc->str_scope_attr = NULL;
|
||||
|
||||
if(xc->fh)
|
||||
{
|
||||
@@ -5197,16 +5193,16 @@ for(;;)
|
||||
|
||||
if(beg_tim)
|
||||
{
|
||||
if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
|
||||
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", beg_tim);
|
||||
if(dumpvars_state == 1) { wx_len = snprintf(wx_buf, 32, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
|
||||
wx_len = snprintf(wx_buf, 32, "#%" PRIu64 "\n", beg_tim);
|
||||
fstWritex(xc, wx_buf, wx_len);
|
||||
if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
|
||||
if(!dumpvars_state) { wx_len = snprintf(wx_buf, 32, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
|
||||
}
|
||||
if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts))
|
||||
{
|
||||
if(beg_tim == xc->blackout_times[cur_blackout])
|
||||
{
|
||||
wx_len = sprintf(wx_buf, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
|
||||
wx_len = snprintf(wx_buf, 32, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
|
||||
fstWritex(xc, wx_buf, wx_len);
|
||||
}
|
||||
}
|
||||
@@ -5340,7 +5336,7 @@ for(;;)
|
||||
clone_d[j] = srcdata[7-j];
|
||||
}
|
||||
}
|
||||
sprintf((char *)xc->temp_signal_value_buf, "%.16g", d);
|
||||
snprintf((char *)xc->temp_signal_value_buf, xc->longest_signal_value_len + 1, "%.16g", d);
|
||||
value_change_callback(user_callback_data_pointer, beg_tim, idx+1, xc->temp_signal_value_buf);
|
||||
}
|
||||
}
|
||||
@@ -5368,7 +5364,7 @@ for(;;)
|
||||
}
|
||||
|
||||
fstVcdID(vcdid_buf, idx+1);
|
||||
wx_len = sprintf(wx_buf, "r%.16g %s\n", d, vcdid_buf);
|
||||
wx_len = snprintf(wx_buf, 64, "r%.16g %s\n", d, vcdid_buf);
|
||||
fstWritex(xc, wx_buf, wx_len);
|
||||
}
|
||||
}
|
||||
@@ -5633,16 +5629,16 @@ for(;;)
|
||||
}
|
||||
}
|
||||
|
||||
if(dumpvars_state == 1) { wx_len = sprintf(wx_buf, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
|
||||
wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", time_table[i]);
|
||||
if(dumpvars_state == 1) { wx_len = snprintf(wx_buf, 32, "$end\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 2; }
|
||||
wx_len = snprintf(wx_buf, 32, "#%" PRIu64 "\n", time_table[i]);
|
||||
fstWritex(xc, wx_buf, wx_len);
|
||||
if(!dumpvars_state) { wx_len = sprintf(wx_buf, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
|
||||
if(!dumpvars_state) { wx_len = snprintf(wx_buf, 32, "$dumpvars\n"); fstWritex(xc, wx_buf, wx_len); dumpvars_state = 1; }
|
||||
|
||||
if((xc->num_blackouts)&&(cur_blackout != xc->num_blackouts))
|
||||
{
|
||||
if(time_table[i] == xc->blackout_times[cur_blackout])
|
||||
{
|
||||
wx_len = sprintf(wx_buf, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
|
||||
wx_len = snprintf(wx_buf, 32, "$dump%s $end\n", (xc->blackout_activity[cur_blackout++]) ? "on" : "off");
|
||||
fstWritex(xc, wx_buf, wx_len);
|
||||
}
|
||||
}
|
||||
@@ -5892,7 +5888,7 @@ for(;;)
|
||||
clone_d[j] = srcdata[7-j];
|
||||
}
|
||||
}
|
||||
sprintf((char *)xc->temp_signal_value_buf, "%.16g", d);
|
||||
snprintf((char *)xc->temp_signal_value_buf, xc->longest_signal_value_len + 1, "%.16g", d);
|
||||
value_change_callback(user_callback_data_pointer, time_table[i], idx+1, xc->temp_signal_value_buf);
|
||||
}
|
||||
}
|
||||
@@ -5918,7 +5914,7 @@ for(;;)
|
||||
}
|
||||
}
|
||||
|
||||
wx_len = sprintf(wx_buf, "r%.16g", d);
|
||||
wx_len = snprintf(wx_buf, 32, "r%.16g", d);
|
||||
fstWritex(xc, wx_buf, wx_len);
|
||||
}
|
||||
}
|
||||
@@ -6024,7 +6020,7 @@ if(xc->signal_lens[facidx] == 1)
|
||||
}
|
||||
}
|
||||
|
||||
sprintf((char *)buf, "%.16g", d);
|
||||
snprintf((char *)buf, 32, "%.16g", d); /* this will write 18 bytes */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6616,7 +6612,7 @@ if(xc->signal_lens[facidx] == 1)
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(buf, "r%.16g", d);
|
||||
snprintf(buf, 32, "r%.16g", d); /* this will write 19 bytes */
|
||||
return(buf);
|
||||
}
|
||||
}
|
||||
|
||||
+124
-45
@@ -232,9 +232,9 @@ std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
|
||||
char* const bufp = new char[len + 1];
|
||||
VL_VSNPRINTF(bufp, len + 1, formatp, ap);
|
||||
|
||||
std::string out{bufp, len}; // Not const to allow move optimization
|
||||
std::string result{bufp, len}; // Not const to allow move optimization
|
||||
delete[] bufp;
|
||||
return out;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t _vl_dbg_sequence_number() VL_MT_SAFE {
|
||||
@@ -255,30 +255,89 @@ void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE {
|
||||
// includes that otherwise would be required in every Verilated module
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
const std::string out = _vl_string_vprintf(formatp, ap);
|
||||
const std::string result = _vl_string_vprintf(formatp, ap);
|
||||
va_end(ap);
|
||||
// printf("-imm-V{t%d,%" PRId64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(),
|
||||
// out.c_str());
|
||||
// result.c_str());
|
||||
|
||||
// Using VL_PRINTF not VL_PRINTF_MT so that we can call VL_DBG_MSGF
|
||||
// from within the guts of the thread execution machinery (and it goes
|
||||
// to the screen and not into the queues we're debugging)
|
||||
VL_PRINTF("-V{t%u,%" PRIu64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), out.c_str());
|
||||
VL_PRINTF("-V{t%u,%" PRIu64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), result.c_str());
|
||||
}
|
||||
|
||||
void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
|
||||
va_list ap;
|
||||
va_start(ap, formatp);
|
||||
const std::string out = _vl_string_vprintf(formatp, ap);
|
||||
const std::string result = _vl_string_vprintf(formatp, ap);
|
||||
va_end(ap);
|
||||
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
|
||||
VL_PRINTF("%s", out.c_str());
|
||||
VL_PRINTF("%s", result.c_str());
|
||||
}});
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Random -- Mostly called at init time, so not inline.
|
||||
|
||||
VlRNG::VlRNG() VL_MT_SAFE {
|
||||
// Starting point for this new class comes from the global RNG
|
||||
VlRNG& fromr = vl_thread_rng();
|
||||
m_state = fromr.m_state;
|
||||
// Advance the *source* so it can later generate a new number
|
||||
// Xoroshiro128+ algorithm
|
||||
fromr.m_state[1] ^= fromr.m_state[0];
|
||||
fromr.m_state[0] = (((fromr.m_state[0] << 55) | (fromr.m_state[0] >> 9)) ^ fromr.m_state[1]
|
||||
^ (fromr.m_state[1] << 14));
|
||||
fromr.m_state[1] = (fromr.m_state[1] << 36) | (fromr.m_state[1] >> 28);
|
||||
}
|
||||
uint64_t VlRNG::rand64() VL_MT_UNSAFE {
|
||||
// Xoroshiro128+ algorithm
|
||||
const uint64_t result = m_state[0] + m_state[1];
|
||||
m_state[1] ^= m_state[0];
|
||||
m_state[0] = (((m_state[0] << 55) | (m_state[0] >> 9)) ^ m_state[1] ^ (m_state[1] << 14));
|
||||
m_state[1] = (m_state[1] << 36) | (m_state[1] >> 28);
|
||||
return result;
|
||||
}
|
||||
uint64_t VlRNG::vl_thread_rng_rand64() VL_MT_SAFE {
|
||||
VlRNG& fromr = vl_thread_rng();
|
||||
const uint64_t result = fromr.m_state[0] + fromr.m_state[1];
|
||||
fromr.m_state[1] ^= fromr.m_state[0];
|
||||
fromr.m_state[0] = (((fromr.m_state[0] << 55) | (fromr.m_state[0] >> 9)) ^ fromr.m_state[1]
|
||||
^ (fromr.m_state[1] << 14));
|
||||
fromr.m_state[1] = (fromr.m_state[1] << 36) | (fromr.m_state[1] >> 28);
|
||||
return result;
|
||||
}
|
||||
void VlRNG::srandom(uint64_t n) VL_MT_UNSAFE {
|
||||
m_state[0] = n;
|
||||
m_state[1] = m_state[0];
|
||||
// Fix state as algorithm is slow to randomize if many zeros
|
||||
// This causes a loss of ~ 1 bit of seed entropy, no big deal
|
||||
if (VL_COUNTONES_I(m_state[0]) < 10) m_state[0] = ~m_state[0];
|
||||
if (VL_COUNTONES_I(m_state[1]) < 10) m_state[1] = ~m_state[1];
|
||||
}
|
||||
std::string VlRNG::get_randstate() const VL_MT_UNSAFE {
|
||||
// Though not stated in IEEE, assumption is the string must be printable
|
||||
const char* const stateCharsp = reinterpret_cast<const char*>(&m_state);
|
||||
static_assert(sizeof(m_state) == 16, "");
|
||||
std::string result{"R00112233445566770011223344556677"};
|
||||
for (size_t i = 0; i < sizeof(m_state); ++i) {
|
||||
result[1 + i * 2] = 'a' + ((stateCharsp[i] >> 4) & 15);
|
||||
result[1 + i * 2 + 1] = 'a' + (stateCharsp[i] & 15);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE {
|
||||
if (VL_UNLIKELY((state.length() != 1 + 2 * sizeof(m_state)) || (state[0] != 'R'))) {
|
||||
VL_PRINTF_MT("%%Warning: set_randstate ignored as state string not from get_randstate\n");
|
||||
return;
|
||||
}
|
||||
char* const stateCharsp = reinterpret_cast<char*>(&m_state);
|
||||
for (size_t i = 0; i < sizeof(m_state); ++i) {
|
||||
stateCharsp[i]
|
||||
= (((state[1 + i * 2] - 'a') & 15) << 4) | ((state[1 + i * 2 + 1] - 'a') & 15);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t vl_sys_rand32() VL_MT_SAFE {
|
||||
// Return random 32-bits using system library.
|
||||
// Used only to construct seed for Verilator's PRNG.
|
||||
@@ -292,27 +351,23 @@ static uint32_t vl_sys_rand32() VL_MT_SAFE {
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t vl_rand64() VL_MT_SAFE {
|
||||
static thread_local uint64_t t_state[2];
|
||||
VlRNG& VlRNG::vl_thread_rng() VL_MT_SAFE {
|
||||
static thread_local VlRNG t_rng{0};
|
||||
static thread_local uint32_t t_seedEpoch = 0;
|
||||
// For speed, we use a thread-local epoch number to know when to reseed
|
||||
// A thread always belongs to a single context, so this works out ok
|
||||
if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) {
|
||||
// Set epoch before state, to avoid race case with new seeding
|
||||
t_seedEpoch = VerilatedContextImp::randSeedEpoch();
|
||||
t_state[0] = Verilated::threadContextp()->impp()->randSeedDefault64();
|
||||
t_state[1] = t_state[0];
|
||||
// Same as srandom() but here as needs to be VL_MT_SAFE
|
||||
t_rng.m_state[0] = Verilated::threadContextp()->impp()->randSeedDefault64();
|
||||
t_rng.m_state[1] = t_rng.m_state[0];
|
||||
// Fix state as algorithm is slow to randomize if many zeros
|
||||
// This causes a loss of ~ 1 bit of seed entropy, no big deal
|
||||
if (VL_COUNTONES_I(t_state[0]) < 10) t_state[0] = ~t_state[0];
|
||||
if (VL_COUNTONES_I(t_state[1]) < 10) t_state[1] = ~t_state[1];
|
||||
if (VL_COUNTONES_I(t_rng.m_state[0]) < 10) t_rng.m_state[0] = ~t_rng.m_state[0];
|
||||
if (VL_COUNTONES_I(t_rng.m_state[1]) < 10) t_rng.m_state[1] = ~t_rng.m_state[1];
|
||||
}
|
||||
// Xoroshiro128+ algorithm
|
||||
const uint64_t result = t_state[0] + t_state[1];
|
||||
t_state[1] ^= t_state[0];
|
||||
t_state[0] = (((t_state[0] << 55) | (t_state[0] >> 9)) ^ t_state[1] ^ (t_state[1] << 14));
|
||||
t_state[1] = (t_state[1] << 36) | (t_state[1] >> 28);
|
||||
return result;
|
||||
return t_rng;
|
||||
}
|
||||
|
||||
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
@@ -321,6 +376,12 @@ WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||
return outwp;
|
||||
}
|
||||
|
||||
WDataOutP VL_RANDOM_RNG_W(VlRNG& rngr, int obits, WDataOutP outwp) VL_MT_UNSAFE {
|
||||
for (int i = 0; i < VL_WORDS_I(obits); ++i) outwp[i] = rngr.rand64();
|
||||
// Last word is unclean
|
||||
return outwp;
|
||||
}
|
||||
|
||||
IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE {
|
||||
// $random - seed is a new seed to apply, then we return new seed
|
||||
Verilated::threadContextp()->randSeed(static_cast<int>(seedr));
|
||||
@@ -518,12 +579,12 @@ QData VL_POW_QQW(int, int, int rbits, QData lhs, const WDataInP rwp) VL_MT_SAFE
|
||||
// Skip check for rhs == 0, as short-circuit doesn't save time
|
||||
if (VL_UNLIKELY(lhs == 0)) return 0;
|
||||
QData power = lhs;
|
||||
QData out = 1ULL;
|
||||
QData result = 1ULL;
|
||||
for (int bit = 0; bit < rbits; ++bit) {
|
||||
if (bit > 0) power = power * power;
|
||||
if (VL_BITISSET_W(rwp, bit)) out *= power;
|
||||
if (VL_BITISSET_W(rwp, bit)) result *= power;
|
||||
}
|
||||
return out;
|
||||
return result;
|
||||
}
|
||||
|
||||
WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp,
|
||||
@@ -645,7 +706,7 @@ std::string VL_DECIMAL_NW(int width, const WDataInP lwp) VL_MT_SAFE {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t width) {
|
||||
std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t width) VL_MT_SAFE {
|
||||
const VerilatedContextImp* const ctxImpp = Verilated::threadContextp()->impp();
|
||||
const std::string suffix = ctxImpp->timeFormatSuffix();
|
||||
const int userUnits = ctxImpp->timeFormatUnits(); // 0..-15
|
||||
@@ -1312,16 +1373,18 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
||||
*p = t_tmp;
|
||||
} else if (obits <= VL_BYTESIZE) {
|
||||
CData* const p = va_arg(ap, CData*);
|
||||
*p = owp[0];
|
||||
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
||||
} else if (obits <= VL_SHORTSIZE) {
|
||||
SData* const p = va_arg(ap, SData*);
|
||||
*p = owp[0];
|
||||
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
||||
} else if (obits <= VL_IDATASIZE) {
|
||||
IData* const p = va_arg(ap, IData*);
|
||||
*p = owp[0];
|
||||
*p = VL_CLEAN_II(obits, obits, owp[0]);
|
||||
} else if (obits <= VL_QUADSIZE) {
|
||||
QData* const p = va_arg(ap, QData*);
|
||||
*p = VL_SET_QW(owp);
|
||||
*p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp));
|
||||
} else {
|
||||
_vl_clean_inplace_w(obits, owp);
|
||||
}
|
||||
}
|
||||
} // switch
|
||||
@@ -1651,15 +1714,15 @@ std::string VL_STACKTRACE_N() VL_MT_SAFE {
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!strings) return "Unable to backtrace\n";
|
||||
|
||||
std::string out = "Backtrace:\n";
|
||||
for (int j = 0; j < nptrs; j++) out += std::string{strings[j]} + std::string{"\n"};
|
||||
std::string result = "Backtrace:\n";
|
||||
for (int j = 0; j < nptrs; j++) result += std::string{strings[j]} + std::string{"\n"};
|
||||
free(strings);
|
||||
return out;
|
||||
return result;
|
||||
}
|
||||
|
||||
void VL_STACKTRACE() VL_MT_SAFE {
|
||||
const std::string out = VL_STACKTRACE_N();
|
||||
VL_PRINTF("%s", out.c_str());
|
||||
const std::string result = VL_STACKTRACE_N();
|
||||
VL_PRINTF("%s", result.c_str());
|
||||
}
|
||||
|
||||
IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
|
||||
@@ -1805,14 +1868,14 @@ std::string VL_TO_STRING_W(int words, const WDataInP obj) {
|
||||
}
|
||||
|
||||
std::string VL_TOLOWER_NN(const std::string& ld) VL_PURE {
|
||||
std::string out = ld;
|
||||
for (auto& cr : out) cr = std::tolower(cr);
|
||||
return out;
|
||||
std::string result = ld;
|
||||
for (auto& cr : result) cr = std::tolower(cr);
|
||||
return result;
|
||||
}
|
||||
std::string VL_TOUPPER_NN(const std::string& ld) VL_PURE {
|
||||
std::string out = ld;
|
||||
for (auto& cr : out) cr = std::toupper(cr);
|
||||
return out;
|
||||
std::string result = ld;
|
||||
for (auto& cr : result) cr = std::toupper(cr);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE {
|
||||
@@ -1833,6 +1896,12 @@ std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE {
|
||||
return std::string{destout, len};
|
||||
}
|
||||
|
||||
std::string VL_CVT_PACK_STR_ND(const VlQueue<std::string>& q) VL_PURE {
|
||||
std::string output;
|
||||
for (const std::string& s : q) output += s;
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
|
||||
std::string lstring = lhs;
|
||||
const int32_t rhs_s = rhs; // To signed value
|
||||
@@ -1871,7 +1940,6 @@ IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
|
||||
IData VL_NTOI_I(int obits, const std::string& str) VL_PURE { return VL_NTOI_Q(obits, str); }
|
||||
QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE {
|
||||
QData out = 0;
|
||||
const size_t procLen = std::min(str.length(), static_cast<size_t>(8));
|
||||
const char* const datap = str.data();
|
||||
int pos = static_cast<int>(str.length()) - 1;
|
||||
int bit = 0;
|
||||
@@ -2540,6 +2608,14 @@ VerilatedVirtualBase* VerilatedContext::threadPoolp() {
|
||||
return m_threadPool.get();
|
||||
}
|
||||
|
||||
void VerilatedContext::prepareClone() { delete m_threadPool.release(); }
|
||||
|
||||
VerilatedVirtualBase* VerilatedContext::threadPoolpOnClone() {
|
||||
if (VL_UNLIKELY(m_threadPool)) m_threadPool.release();
|
||||
m_threadPool = std::unique_ptr<VlThreadPool>(new VlThreadPool{this, m_threads - 1});
|
||||
return m_threadPool.get();
|
||||
}
|
||||
|
||||
VerilatedVirtualBase*
|
||||
VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
|
||||
if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
|
||||
@@ -3166,15 +3242,18 @@ void VerilatedAssertOneThread::fatal_different() VL_MT_SAFE {
|
||||
//===========================================================================
|
||||
// VlDeleter:: Methods
|
||||
|
||||
void VlDeleter::deleteAll() {
|
||||
void VlDeleter::deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE {
|
||||
while (true) {
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
if (m_newGarbage.empty()) break;
|
||||
VerilatedLockGuard deleteLock{m_deleteMutex};
|
||||
std::swap(m_newGarbage, m_toDelete);
|
||||
lock.unlock(); // So destructors can enqueue new objects
|
||||
{
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
if (m_newGarbage.empty()) break;
|
||||
m_deleteMutex.lock();
|
||||
std::swap(m_newGarbage, m_toDelete);
|
||||
// m_mutex is unlocked here, so destructors can enqueue new objects
|
||||
}
|
||||
for (VlDeletable* const objp : m_toDelete) delete objp;
|
||||
m_toDelete.clear();
|
||||
m_deleteMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+28
-21
@@ -30,6 +30,13 @@
|
||||
#ifndef VERILATOR_VERILATED_H_
|
||||
#define VERILATOR_VERILATED_H_
|
||||
#define VERILATOR_VERILATED_H_INTERNAL_
|
||||
#ifdef VERILATOR_INTERNAL_
|
||||
// This file contains definition of VerilationMutex that should
|
||||
// only be used by verilated code. Verilator itself should use
|
||||
// mutex from V3Mutex.h. Make sure this file isn't included in
|
||||
// verilator code.
|
||||
#error "verilated.h should only be included in verilated code"
|
||||
#endif
|
||||
|
||||
// clang-format off
|
||||
#include "verilatedos.h"
|
||||
@@ -142,6 +149,15 @@ enum VerilatedVarFlags {
|
||||
VLVF_DPI_CLAY = (1 << 10) // DPI compatible C standard layout
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// Utility functions
|
||||
|
||||
template <size_t N>
|
||||
inline constexpr size_t roundUpToMultipleOf(size_t value) {
|
||||
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
|
||||
return (value + N - 1) & ~(N - 1);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Mutex and threading support
|
||||
|
||||
@@ -159,6 +175,7 @@ public:
|
||||
/// Construct mutex (without locking it)
|
||||
VerilatedMutex() = default;
|
||||
~VerilatedMutex() = default;
|
||||
VL_UNCOPYABLE(VerilatedMutex);
|
||||
const VerilatedMutex& operator!() const { return *this; } // For -fthread_safety
|
||||
/// Acquire/lock mutex
|
||||
void lock() VL_ACQUIRE() VL_MT_SAFE {
|
||||
@@ -202,23 +219,10 @@ public:
|
||||
/// Construct and hold given mutex lock until destruction or unlock()
|
||||
explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE
|
||||
: m_mutexr(mutexr) { // Need () or GCC 4.8 false warning
|
||||
m_mutexr.lock();
|
||||
mutexr.lock();
|
||||
}
|
||||
/// Destruct and unlock the mutex
|
||||
~VerilatedLockGuard() VL_RELEASE() { m_mutexr.unlock(); }
|
||||
/// Lock the mutex
|
||||
void lock() VL_ACQUIRE() VL_MT_SAFE { m_mutexr.lock(); }
|
||||
/// Unlock the mutex
|
||||
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutexr.unlock(); }
|
||||
/// Acquire/lock mutex and check for stop request.
|
||||
/// It tries to lock the mutex and if it fails, it check if stop request was send.
|
||||
/// It returns after locking mutex.
|
||||
/// This function should be extracted to V3ThreadPool, but due to clang thread-safety
|
||||
/// limitations it needs to be placed here.
|
||||
void lockCheckStopRequest(std::function<void()> checkStopRequestFunction)
|
||||
VL_ACQUIRE() VL_MT_SAFE {
|
||||
m_mutexr.lockCheckStopRequest(checkStopRequestFunction);
|
||||
}
|
||||
};
|
||||
|
||||
// Internals: Remember the calling thread at construction time, and make
|
||||
@@ -564,6 +568,8 @@ public:
|
||||
void addModel(VerilatedModel*);
|
||||
|
||||
VerilatedVirtualBase* threadPoolp();
|
||||
void prepareClone();
|
||||
VerilatedVirtualBase* threadPoolpOnClone();
|
||||
VerilatedVirtualBase*
|
||||
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
|
||||
|
||||
@@ -607,6 +613,7 @@ public: // But for internal use only
|
||||
VerilatedEvalMsgQueue* __Vm_evalMsgQp;
|
||||
explicit VerilatedSyms(VerilatedContext* contextp); // Pass null for default context
|
||||
~VerilatedSyms();
|
||||
VL_UNCOPYABLE(VerilatedSyms);
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
@@ -945,18 +952,18 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_timeprecision = value;
|
||||
#if VM_SC
|
||||
const sc_time sc_res = sc_get_time_resolution();
|
||||
if (sc_res == sc_time(1, SC_SEC)) {
|
||||
const sc_core::sc_time sc_res = sc_core::sc_get_time_resolution();
|
||||
if (sc_res == sc_core::sc_time(1, sc_core::SC_SEC)) {
|
||||
sc_prec = 0;
|
||||
} else if (sc_res == sc_time(1, SC_MS)) {
|
||||
} else if (sc_res == sc_core::sc_time(1, sc_core::SC_MS)) {
|
||||
sc_prec = 3;
|
||||
} else if (sc_res == sc_time(1, SC_US)) {
|
||||
} else if (sc_res == sc_core::sc_time(1, sc_core::SC_US)) {
|
||||
sc_prec = 6;
|
||||
} else if (sc_res == sc_time(1, SC_NS)) {
|
||||
} else if (sc_res == sc_core::sc_time(1, sc_core::SC_NS)) {
|
||||
sc_prec = 9;
|
||||
} else if (sc_res == sc_time(1, SC_PS)) {
|
||||
} else if (sc_res == sc_core::sc_time(1, sc_core::SC_PS)) {
|
||||
sc_prec = 12;
|
||||
} else if (sc_res == sc_time(1, SC_FS)) {
|
||||
} else if (sc_res == sc_core::sc_time(1, sc_core::SC_FS)) {
|
||||
sc_prec = 15;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -31,6 +31,8 @@ CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
|
||||
CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
|
||||
# Compiler flags that enable coroutine support
|
||||
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
|
||||
# Linker flags
|
||||
CFG_LDFLAGS_VERILATED = @CFG_LDFLAGS_VERILATED@
|
||||
# Linker libraries for multithreading
|
||||
CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
|
||||
|
||||
@@ -77,6 +79,8 @@ VPATH += ..
|
||||
VPATH += $(VERILATOR_ROOT)/include
|
||||
VPATH += $(VERILATOR_ROOT)/include/vltstd
|
||||
|
||||
LDFLAGS += $(CFG_LDFLAGS_VERILATED)
|
||||
|
||||
#OPT = -ggdb -DPRINTINITSTR -DDETECTCHANGE
|
||||
#OPT = -ggdb -DPRINTINITSTR
|
||||
CPPFLAGS += $(OPT)
|
||||
@@ -235,14 +239,16 @@ $(VM_PREFIX)__ALL.a: $(VK_OBJS) $(VM_HIER_LIBS)
|
||||
ifneq ($(VM_DEFAULT_RULES),0)
|
||||
# Anything not in $(VK_SLOW_OBJS) or $(VK_GLOBAL_OBJS), including verilated.o
|
||||
# and user files passed on the Verilator command line use this rule.
|
||||
# We put OPT_FAST/OPT_SLOW/OPT_GLOBAL before the other flags to
|
||||
# allow USER_CPPFLAGS to override them
|
||||
%.o: %.cpp
|
||||
$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $<
|
||||
$(OBJCACHE) $(CXX) $(OPT_FAST) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
$(VK_SLOW_OBJS): %.o: %.cpp
|
||||
$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_SLOW) -c -o $@ $<
|
||||
$(OBJCACHE) $(CXX) $(OPT_SLOW) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
$(VK_GLOBAL_OBJS): %.o: %.cpp
|
||||
$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_GLOBAL) -c -o $@ $<
|
||||
$(OBJCACHE) $(CXX) $(OPT_GLOBAL) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
endif
|
||||
|
||||
#Default rule embedded in make:
|
||||
|
||||
@@ -208,11 +208,11 @@ private:
|
||||
// Forward to . so we have a whole word
|
||||
const std::string suffix = *bpost ? std::string{bpost + 1} : "";
|
||||
|
||||
std::string out = prefix + "*" + suffix;
|
||||
std::string result = prefix + "*" + suffix;
|
||||
|
||||
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
|
||||
// <<"\ncho="<<out<<endl;
|
||||
return out;
|
||||
// std::cout << "\nch pre=" << prefix << " s=" << suffix << "\nch a="
|
||||
// << old << "\nch b=" << add << "\ncho=" << result << std::endl;
|
||||
return result;
|
||||
}
|
||||
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match)
|
||||
VL_REQUIRES(m_mutex) {
|
||||
@@ -316,7 +316,7 @@ public:
|
||||
valps[2] = page_default.c_str();
|
||||
|
||||
// Keys -> strings
|
||||
std::string keys[VerilatedCovConst::MAX_KEYS];
|
||||
std::array<std::string, VerilatedCovConst::MAX_KEYS> keys;
|
||||
for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
|
||||
if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i];
|
||||
}
|
||||
@@ -337,7 +337,7 @@ public:
|
||||
const std::string key = keys[i];
|
||||
if (!keys[i].empty()) {
|
||||
const std::string val = valps[i];
|
||||
// cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
|
||||
// std::cout << " " << __FUNCTION__ << " " << key << " = " << val << std::endl;
|
||||
m_insertp->m_keys[addKeynum] = valueIndex(key);
|
||||
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
||||
++addKeynum;
|
||||
|
||||
+11
-11
@@ -19,8 +19,8 @@
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#ifndef VERILATOR_VERILATED_VCD_SC_H_
|
||||
#define VERILATOR_VERILATED_VCD_SC_H_
|
||||
#ifndef VERILATOR_VERILATED_FST_SC_H_
|
||||
#define VERILATOR_VERILATED_FST_SC_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
@@ -36,23 +36,23 @@
|
||||
/// with the SystemC simulation kernel, just like a SystemC-documented
|
||||
/// trace format.
|
||||
|
||||
class VerilatedFstSc final : sc_trace_file, public VerilatedFstC {
|
||||
class VerilatedFstSc final : sc_core::sc_trace_file, public VerilatedFstC {
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedFstSc);
|
||||
|
||||
public:
|
||||
/// Construct a SC trace object, and register with the SystemC kernel
|
||||
VerilatedFstSc() {
|
||||
sc_get_curr_simcontext()->add_trace_file(this);
|
||||
sc_core::sc_get_curr_simcontext()->add_trace_file(this);
|
||||
// We want to avoid a depreciated warning, but still be back compatible.
|
||||
// Turning off the message just for this still results in an
|
||||
// annoying "to turn off" message.
|
||||
const sc_time t1sec{1, SC_SEC};
|
||||
const sc_core::sc_time t1sec{1, sc_core::SC_SEC};
|
||||
if (t1sec.to_default_time_units() != 0) {
|
||||
const sc_time tunits{1.0 / t1sec.to_default_time_units(), SC_SEC};
|
||||
const sc_core::sc_time tunits{1.0 / t1sec.to_default_time_units(), sc_core::SC_SEC};
|
||||
spTrace()->set_time_unit(tunits.to_string());
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
spTrace()->set_time_resolution(sc_core::sc_get_time_resolution().to_string());
|
||||
}
|
||||
/// Destruct, flush, and close the dump
|
||||
~VerilatedFstSc() override { close(); }
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
// METHODS - for SC kernel
|
||||
// Called by SystemC simulate()
|
||||
void cycle(bool delta_cycle) override {
|
||||
if (!delta_cycle) this->dump(sc_time_stamp().to_double());
|
||||
if (!delta_cycle) this->dump(sc_core::sc_time_stamp().to_double());
|
||||
}
|
||||
|
||||
// Override VerilatedFstC. Must be called after starting simulation.
|
||||
@@ -79,7 +79,7 @@ private:
|
||||
// Cadence Incisive has these as abstract functions so we must create them
|
||||
void set_time_unit(int exponent10_seconds) override {} // deprecated
|
||||
#endif
|
||||
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
|
||||
void set_time_unit(double v, sc_core::sc_time_unit tu) override {} // LCOV_EXCL_LINE
|
||||
|
||||
//--------------------------------------------------
|
||||
// SystemC 2.1.v1
|
||||
@@ -96,8 +96,8 @@ private:
|
||||
// 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 )
|
||||
DECL_TRACE_METHOD_A( sc_core::sc_event )
|
||||
DECL_TRACE_METHOD_A( sc_core::sc_time )
|
||||
#endif
|
||||
|
||||
DECL_TRACE_METHOD_A( bool )
|
||||
|
||||
@@ -152,9 +152,11 @@ extern const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE; // PLIi
|
||||
// Base macros
|
||||
|
||||
// Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
|
||||
// Arguments must not have side effects
|
||||
#define VL_BITISSETLIMIT_W(data, width, bit) (((bit) < (width)) && VL_BITISSET_W(data, bit))
|
||||
|
||||
// Shift appropriate word by bit. Does not account for wrapping between two words
|
||||
// Argument 'bit' must not have side effects
|
||||
#define VL_BITRSHIFT_W(data, bit) ((data)[VL_BITWORD_E(bit)] >> VL_BITBIT_E(bit))
|
||||
|
||||
// Create two 32-bit words from quadword
|
||||
@@ -260,7 +262,7 @@ extern void _vl_debug_print_w(int lbits, WDataInP const iwp) VL_MT_SAFE;
|
||||
#if defined(SYSTEMC_VERSION)
|
||||
/// Return current simulation time
|
||||
// Already defined: extern sc_time sc_time_stamp();
|
||||
inline uint64_t vl_time_stamp64() VL_MT_SAFE { return sc_time_stamp().value(); }
|
||||
inline uint64_t vl_time_stamp64() VL_MT_SAFE { return sc_core::sc_time_stamp().value(); }
|
||||
#else // Non-SystemC
|
||||
# if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY)
|
||||
# ifdef VL_TIME_STAMP64
|
||||
@@ -758,6 +760,7 @@ static inline IData VL_ONEHOT0_W(int words, WDataInP const lwp) VL_PURE {
|
||||
|
||||
static inline IData VL_CLOG2_I(IData lhs) VL_PURE {
|
||||
// There are faster algorithms, or fls GCC4 builtins, but rarely used
|
||||
// In C++20 there will be std::bit_width(lhs) - 1
|
||||
if (VL_UNLIKELY(!lhs)) return 0;
|
||||
--lhs;
|
||||
int shifts = 0;
|
||||
@@ -953,11 +956,19 @@ static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE
|
||||
// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean;
|
||||
// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean;
|
||||
// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean;
|
||||
#define VL_DIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs))
|
||||
#define VL_DIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs))
|
||||
static inline IData VL_DIV_III(int lbits, IData lhs, IData rhs) {
|
||||
return (rhs == 0) ? 0 : lhs / rhs;
|
||||
}
|
||||
static inline QData VL_DIV_QQQ(int lbits, QData lhs, QData rhs) {
|
||||
return (rhs == 0) ? 0 : lhs / rhs;
|
||||
}
|
||||
#define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0))
|
||||
#define VL_MODDIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs))
|
||||
#define VL_MODDIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs))
|
||||
static inline IData VL_MODDIV_III(int lbits, IData lhs, IData rhs) {
|
||||
return (rhs == 0) ? 0 : lhs % rhs;
|
||||
}
|
||||
static inline QData VL_MODDIV_QQQ(int lbits, QData lhs, QData rhs) {
|
||||
return (rhs == 0) ? 0 : lhs % rhs;
|
||||
}
|
||||
#define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1))
|
||||
|
||||
static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP const lwp,
|
||||
@@ -1515,6 +1526,28 @@ static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const
|
||||
return owp;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void VL_ASSIGN_DYN_Q(VlQueue<T>& q, int elem_size, int lbits, QData from) {
|
||||
const int size = (lbits + elem_size - 1) / elem_size;
|
||||
q.renew(size);
|
||||
const QData mask = VL_MASK_Q(elem_size);
|
||||
for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline IData VL_DYN_TO_I(const VlQueue<T>& q, int elem_size) {
|
||||
IData ret = 0;
|
||||
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline QData VL_DYN_TO_Q(const VlQueue<T>& q, int elem_size) {
|
||||
QData ret = 0;
|
||||
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Because concats are common and wide, it's valuable to always have a clean output.
|
||||
// Thus we specify inputs must be clean, so we don't need to clean the output.
|
||||
// Note the bit shifts are always constants, so the adds in these constify out.
|
||||
@@ -2185,6 +2218,7 @@ extern IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE;
|
||||
// Conversion functions
|
||||
|
||||
extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE;
|
||||
extern std::string VL_CVT_PACK_STR_ND(const VlQueue<std::string>& q) VL_PURE;
|
||||
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
|
||||
VlWide<VL_WQ_WORDS_E> lw;
|
||||
VL_SET_WQ(lw, lhs);
|
||||
@@ -2201,10 +2235,10 @@ inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs
|
||||
return lhs + rhs;
|
||||
}
|
||||
inline std::string VL_REPLICATEN_NNQ(const std::string& lhs, IData rep) VL_PURE {
|
||||
std::string out;
|
||||
out.reserve(lhs.length() * rep);
|
||||
for (unsigned times = 0; times < rep; ++times) out += lhs;
|
||||
return out;
|
||||
std::string result;
|
||||
result.reserve(lhs.length() * rep);
|
||||
for (unsigned times = 0; times < rep; ++times) result += lhs;
|
||||
return result;
|
||||
}
|
||||
inline std::string VL_REPLICATEN_NNI(const std::string& lhs, IData rep) VL_PURE {
|
||||
return VL_REPLICATEN_NNQ(lhs, rep);
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2010-2023 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated old string and data-type header
|
||||
///
|
||||
/// This file is deprecated, and provided for backwards compatibility.
|
||||
/// Include verilated.h instead.
|
||||
///
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_VERILATED_HEAVY_H_
|
||||
#define VERILATOR_VERILATED_HEAVY_H_
|
||||
|
||||
#ifdef VL_NO_LEGACY
|
||||
#error "Include <verilated.h> instead of <verilated_heavy.h>"
|
||||
#endif
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#endif // Guard
|
||||
@@ -148,7 +148,7 @@ public:
|
||||
private:
|
||||
VL_UNCOPYABLE(VerilatedThreadMsgQueue);
|
||||
// METHODS
|
||||
static VerilatedThreadMsgQueue& threadton() {
|
||||
static VerilatedThreadMsgQueue& threadton() VL_MT_SAFE {
|
||||
static thread_local VerilatedThreadMsgQueue t_s;
|
||||
return t_s;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "systemc.h"
|
||||
#include <systemc>
|
||||
|
||||
//=============================================================================
|
||||
// For \internal use, get a pointer to m_data in the sc_bv_base class,
|
||||
@@ -37,9 +37,9 @@
|
||||
// use cast magic to get at it. Saves patching get_datap in SystemC.
|
||||
#define VL_SC_BV_DATAP(bv) (VlScBvExposer::sp_datap(bv))
|
||||
// This class is thread safe (though most of SystemC is not).
|
||||
class VlScBvExposer final : public sc_bv_base {
|
||||
class VlScBvExposer final : public sc_dt::sc_bv_base {
|
||||
public:
|
||||
static const uint32_t* sp_datap(const sc_bv_base& base) VL_MT_SAFE {
|
||||
static const uint32_t* sp_datap(const sc_dt::sc_bv_base& base) VL_MT_SAFE {
|
||||
return static_cast<const VlScBvExposer*>(&base)->sp_datatp();
|
||||
}
|
||||
const uint32_t* sp_datatp() const { return reinterpret_cast<uint32_t*>(m_data); }
|
||||
|
||||
+145
-71
@@ -26,93 +26,167 @@
|
||||
// verilator lint_off TIMESCALEMOD
|
||||
// verilator lint_off UNUSEDSIGNAL
|
||||
package std;
|
||||
// The process class is not implemented, but it's predeclared here,
|
||||
// so the linter accepts references to it.
|
||||
typedef class process;
|
||||
class mailbox #(type T);
|
||||
protected int m_bound;
|
||||
protected T m_queue[$];
|
||||
|
||||
class mailbox #(type T);
|
||||
protected int m_bound;
|
||||
protected T m_queue[$];
|
||||
function new(int bound = 0);
|
||||
m_bound = bound;
|
||||
endfunction
|
||||
|
||||
function new(int bound = 0);
|
||||
m_bound = bound;
|
||||
endfunction
|
||||
function int num();
|
||||
return m_queue.size();
|
||||
endfunction
|
||||
|
||||
function int num();
|
||||
return m_queue.size();
|
||||
endfunction
|
||||
task put(T message);
|
||||
`ifdef VERILATOR_TIMING
|
||||
if (m_bound != 0)
|
||||
wait (m_queue.size() < m_bound);
|
||||
m_queue.push_back(message);
|
||||
`endif
|
||||
endtask
|
||||
|
||||
task put(T message);
|
||||
`ifdef VERILATOR_TIMING
|
||||
if (m_bound != 0)
|
||||
wait (m_queue.size() < m_bound);
|
||||
function int try_put(T message);
|
||||
if (num() < m_bound) begin
|
||||
m_queue.push_back(message);
|
||||
`endif
|
||||
endtask
|
||||
return 1;
|
||||
end
|
||||
return 0;
|
||||
endfunction
|
||||
|
||||
function int try_put(T message);
|
||||
if (num() < m_bound) begin
|
||||
m_queue.push_back(message);
|
||||
return 1;
|
||||
end
|
||||
return 0;
|
||||
endfunction
|
||||
task get(ref T message);
|
||||
`ifdef VERILATOR_TIMING
|
||||
wait (m_queue.size() > 0);
|
||||
message = m_queue.pop_front();
|
||||
`endif
|
||||
endtask
|
||||
|
||||
task get(ref T message);
|
||||
`ifdef VERILATOR_TIMING
|
||||
wait (m_queue.size() > 0);
|
||||
function int try_get(ref T message);
|
||||
if (num() > 0) begin
|
||||
message = m_queue.pop_front();
|
||||
`endif
|
||||
endtask
|
||||
return 1;
|
||||
end
|
||||
return 0;
|
||||
endfunction
|
||||
|
||||
function int try_get(ref T message);
|
||||
if (num() > 0) begin
|
||||
message = m_queue.pop_front();
|
||||
return 1;
|
||||
end
|
||||
return 0;
|
||||
endfunction
|
||||
task peek(ref T message);
|
||||
`ifdef VERILATOR_TIMING
|
||||
wait (m_queue.size() > 0);
|
||||
message = m_queue[0];
|
||||
`endif
|
||||
endtask
|
||||
|
||||
task peek(ref T message);
|
||||
`ifdef VERILATOR_TIMING
|
||||
wait (m_queue.size() > 0);
|
||||
function int try_peek(ref T message);
|
||||
if (num() > 0) begin
|
||||
message = m_queue[0];
|
||||
`endif
|
||||
endtask
|
||||
return 1;
|
||||
end
|
||||
return 0;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
function int try_peek(ref T message);
|
||||
if (num() > 0) begin
|
||||
message = m_queue[0];
|
||||
return 1;
|
||||
end
|
||||
return 0;
|
||||
endfunction
|
||||
endclass
|
||||
class semaphore;
|
||||
protected int m_keyCount;
|
||||
|
||||
class semaphore;
|
||||
protected int m_keyCount;
|
||||
function new(int keyCount = 0);
|
||||
m_keyCount = keyCount;
|
||||
endfunction
|
||||
|
||||
function new(int keyCount = 0);
|
||||
m_keyCount = keyCount;
|
||||
endfunction
|
||||
function void put(int keyCount = 1);
|
||||
m_keyCount += keyCount;
|
||||
endfunction
|
||||
|
||||
function void put(int keyCount = 1);
|
||||
m_keyCount += keyCount;
|
||||
endfunction
|
||||
task get(int keyCount = 1);
|
||||
`ifdef VERILATOR_TIMING
|
||||
wait (m_keyCount >= keyCount);
|
||||
m_keyCount -= keyCount;
|
||||
`endif
|
||||
endtask
|
||||
|
||||
task get(int keyCount = 1);
|
||||
`ifdef VERILATOR_TIMING
|
||||
wait (m_keyCount >= keyCount);
|
||||
function int try_get(int keyCount = 1);
|
||||
if (m_keyCount >= keyCount) begin
|
||||
m_keyCount -= keyCount;
|
||||
`endif
|
||||
endtask
|
||||
return 1;
|
||||
end
|
||||
return 0;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
function int try_get(int keyCount = 1);
|
||||
if (m_keyCount >= keyCount) begin
|
||||
m_keyCount -= keyCount;
|
||||
return 1;
|
||||
end
|
||||
return 0;
|
||||
endfunction
|
||||
endclass
|
||||
class process;
|
||||
typedef enum {
|
||||
FINISHED = 0,
|
||||
RUNNING = 1,
|
||||
WAITING = 2,
|
||||
SUSPENDED = 3,
|
||||
KILLED = 4
|
||||
} state;
|
||||
|
||||
`ifdef VERILATOR_TIMING
|
||||
// Width visitor changes it to VlProcessRef
|
||||
protected chandle m_process;
|
||||
`endif
|
||||
|
||||
static function process self();
|
||||
process p = new;
|
||||
`ifdef VERILATOR_TIMING
|
||||
$c(p.m_process, " = vlProcess;");
|
||||
`endif
|
||||
return p;
|
||||
endfunction
|
||||
|
||||
protected function void set_status(state s);
|
||||
`ifdef VERILATOR_TIMING
|
||||
$c(m_process, "->state(", s, ");");
|
||||
`endif
|
||||
endfunction
|
||||
|
||||
function state status();
|
||||
`ifdef VERILATOR_TIMING
|
||||
return state'($c(m_process, "->state()"));
|
||||
`else
|
||||
return RUNNING;
|
||||
`endif
|
||||
endfunction
|
||||
|
||||
function void kill();
|
||||
set_status(KILLED);
|
||||
endfunction
|
||||
|
||||
function void suspend();
|
||||
$error("std::process::suspend() not supported");
|
||||
endfunction
|
||||
|
||||
function void resume();
|
||||
set_status(RUNNING);
|
||||
endfunction
|
||||
|
||||
task await();
|
||||
`ifdef VERILATOR_TIMING
|
||||
wait (status() == FINISHED || status() == KILLED);
|
||||
`endif
|
||||
endtask
|
||||
|
||||
// When really implemented, srandom must operate on the process, but for
|
||||
// now rely on the srandom() that is automatically generated for all
|
||||
// classes.
|
||||
//
|
||||
// function void srandom(int seed);
|
||||
// endfunction
|
||||
|
||||
// The methods below work only if set_randstate is never applied to
|
||||
// a state string created before another such string. Full support
|
||||
// could use VlRNG class to store the state per process in VlProcess
|
||||
// objects.
|
||||
function string get_randstate();
|
||||
string s;
|
||||
|
||||
s.itoa($random); // Get a random number
|
||||
set_randstate(s); // Pretend it's the state of RNG
|
||||
return s;
|
||||
endfunction
|
||||
|
||||
function void set_randstate(string s);
|
||||
$urandom(s.atoi()); // Set the seed using a string
|
||||
endfunction
|
||||
endclass
|
||||
endpackage
|
||||
|
||||
@@ -173,7 +173,7 @@ public:
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
while (m_ready.empty()) {
|
||||
m_waiting = true;
|
||||
m_cv.wait(lock);
|
||||
m_cv.wait(m_mutex);
|
||||
}
|
||||
m_waiting = false;
|
||||
// As noted above this is inefficient if our ready list is ever
|
||||
|
||||
@@ -30,7 +30,16 @@ void VlCoroutineHandle::resume() {
|
||||
// main process
|
||||
if (VL_LIKELY(m_coro)) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Resuming: "); dump(););
|
||||
m_coro();
|
||||
if (m_process) { // If process state is managed with std::process
|
||||
if (m_process->state() == VlProcess::KILLED) {
|
||||
m_coro.destroy();
|
||||
} else {
|
||||
m_process->state(VlProcess::RUNNING);
|
||||
m_coro();
|
||||
}
|
||||
} else {
|
||||
m_coro();
|
||||
}
|
||||
m_coro = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -95,8 +104,9 @@ void VlTriggerScheduler::resume(const char* eventDescription) {
|
||||
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();
|
||||
std::swap(m_ready, m_resumeQueue);
|
||||
for (VlCoroutineHandle& coro : m_resumeQueue) coro.resume();
|
||||
m_resumeQueue.clear();
|
||||
commit(eventDescription);
|
||||
}
|
||||
|
||||
|
||||
+61
-36
@@ -96,33 +96,52 @@ class VlCoroutineHandle final {
|
||||
|
||||
// MEMBERS
|
||||
std::coroutine_handle<> m_coro; // The wrapped coroutine handle
|
||||
VlProcessRef m_process; // Data of the suspended process, null if not needed
|
||||
VlFileLineDebug m_fileline;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// Construct
|
||||
VlCoroutineHandle()
|
||||
: m_coro{nullptr} {}
|
||||
VlCoroutineHandle(std::coroutine_handle<> coro, VlFileLineDebug fileline)
|
||||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlCoroutineHandle(VlProcessRef process)
|
||||
: m_coro{nullptr}
|
||||
, m_process{process} {
|
||||
if (m_process) m_process->state(VlProcess::WAITING);
|
||||
}
|
||||
VlCoroutineHandle(std::coroutine_handle<> coro, VlProcessRef process, VlFileLineDebug fileline)
|
||||
: m_coro{coro}
|
||||
, m_fileline{fileline} {}
|
||||
, m_process{process}
|
||||
, m_fileline{fileline} {
|
||||
if (m_process) m_process->state(VlProcess::WAITING);
|
||||
}
|
||||
// Move the handle, leaving a nullptr
|
||||
// non-explicit:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlCoroutineHandle(VlCoroutineHandle&& moved)
|
||||
: m_coro{std::exchange(moved.m_coro, nullptr)}
|
||||
, m_process{std::exchange(moved.m_process, 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();
|
||||
if (VL_UNLIKELY(m_coro)) {
|
||||
m_coro.destroy();
|
||||
if (m_process && m_process->state() != VlProcess::KILLED) {
|
||||
m_process->state(VlProcess::FINISHED);
|
||||
}
|
||||
}
|
||||
}
|
||||
// METHODS
|
||||
// Move the handle, leaving a null handle
|
||||
auto& operator=(VlCoroutineHandle&& moved) {
|
||||
m_coro = std::exchange(moved.m_coro, nullptr);
|
||||
m_process = std::exchange(moved.m_process, nullptr);
|
||||
m_fileline = moved.m_fileline;
|
||||
return *this;
|
||||
}
|
||||
// Resume the coroutine if the handle isn't null
|
||||
// Resume the coroutine if the handle isn't null and the process isn't killed
|
||||
void resume();
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
@@ -173,21 +192,24 @@ public:
|
||||
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) {
|
||||
auto delay(uint64_t delay, VlProcessRef process, const char* filename = VL_UNKNOWN,
|
||||
int lineno = 0) {
|
||||
struct Awaitable {
|
||||
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||
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}});
|
||||
queue.push_back({delay, VlCoroutineHandle{coro, process, 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}};
|
||||
return Awaitable{process, m_queue, m_context.time() + delay,
|
||||
VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -207,6 +229,10 @@ class VlTriggerScheduler final {
|
||||
// (not resumable)
|
||||
VlCoroutineVec m_ready; // Coroutines that can be resumed (all coros from m_uncommitted are
|
||||
// moved here in commit())
|
||||
VlCoroutineVec m_resumeQueue; // Coroutines being resumed by resume(); kept as a field to
|
||||
// avoid reallocation. Resumed coroutines are moved to
|
||||
// m_resumeQueue to allow adding coroutines to m_ready
|
||||
// during resume(). Outside of resume() should always be empty.
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
@@ -220,21 +246,23 @@ public:
|
||||
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) {
|
||||
auto trigger(bool commit, VlProcessRef process, 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
|
||||
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
suspended.emplace_back(coro, fileline);
|
||||
suspended.emplace_back(coro, process, fileline);
|
||||
}
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{m_uncommitted, VlFileLineDebug{filename, lineno}};
|
||||
return Awaitable{commit ? m_ready : m_uncommitted, process,
|
||||
VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -267,18 +295,19 @@ class VlDynamicTriggerScheduler final {
|
||||
// with destructive post updates, e.g. named events)
|
||||
|
||||
// METHODS
|
||||
auto awaitable(VlCoroutineVec& queue, const char* filename, int lineno) {
|
||||
auto awaitable(VlProcessRef process, VlCoroutineVec& queue, const char* filename, int lineno) {
|
||||
struct Awaitable {
|
||||
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||
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);
|
||||
suspended.emplace_back(coro, process, fileline);
|
||||
}
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{queue, VlFileLineDebug{filename, lineno}};
|
||||
return Awaitable{process, queue, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -292,36 +321,29 @@ public:
|
||||
void dump() const;
|
||||
#endif
|
||||
// Used by coroutines for co_awaiting trigger evaluation
|
||||
auto evaluation(const char* eventDescription, const char* filename, int lineno) {
|
||||
auto evaluation(VlProcessRef process, 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);
|
||||
return awaitable(process, 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) {
|
||||
auto postUpdate(VlProcessRef process, 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);
|
||||
return awaitable(process, 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) {
|
||||
auto resumption(VlProcessRef process, 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);
|
||||
return awaitable(process, 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.
|
||||
@@ -348,24 +370,27 @@ class VlForkSync final {
|
||||
|
||||
public:
|
||||
// Create the join object and set the counter to the specified number
|
||||
void init(size_t count) { m_join.reset(new VlJoin{count, {}}); }
|
||||
void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); }
|
||||
// Called whenever any of the forked processes finishes. If the join counter reaches 0, the
|
||||
// main process gets resumed
|
||||
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
|
||||
// Used by coroutines for co_awaiting a join
|
||||
auto join(const char* filename = VL_UNKNOWN, int lineno = 0) {
|
||||
auto join(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) {
|
||||
assert(m_join);
|
||||
VL_DEBUG_IF(
|
||||
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
|
||||
struct Awaitable {
|
||||
VlProcessRef process; // Data of the suspended process, null if not needed
|
||||
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_suspend(std::coroutine_handle<> coro) {
|
||||
join->m_susp = {coro, process, fileline};
|
||||
}
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{m_join, VlFileLineDebug{filename, lineno}};
|
||||
return Awaitable{process, m_join, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
// Get an element from the front of the queue. Blocks if none available
|
||||
T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
|
||||
m_cv.wait(m_mutex, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
|
||||
assert(!m_queue.empty());
|
||||
T value = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
@@ -447,7 +447,9 @@ public:
|
||||
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE void chgQData(uint32_t* oldp, QData newval, int bits) {
|
||||
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
|
||||
QData old;
|
||||
std::memcpy(&old, oldp, sizeof(old));
|
||||
const uint64_t diff = old ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
|
||||
}
|
||||
VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
@@ -460,8 +462,9 @@ public:
|
||||
}
|
||||
VL_ATTR_ALWINLINE void chgEvent(uint32_t* oldp, VlEvent newval) { fullEvent(oldp, 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);
|
||||
double old;
|
||||
std::memcpy(&old, oldp, sizeof(old));
|
||||
if (VL_UNLIKELY(old != newval)) fullDouble(oldp, newval);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -482,7 +482,7 @@ VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wa
|
||||
// We have been spinning for a while, so yield the thread
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
m_waiting = true;
|
||||
m_cv.wait(lock, [this] { return m_ready.load(std::memory_order_relaxed); });
|
||||
m_cv.wait(m_mutex, [this] { return m_ready.load(std::memory_order_relaxed); });
|
||||
m_waiting = false;
|
||||
}
|
||||
|
||||
@@ -872,7 +872,7 @@ void VerilatedTraceBuffer<VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*reinterpret_cast<QData*>(oldp) = newval;
|
||||
std::memcpy(oldp, &newval, sizeof(newval));
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
emitQData(code, newval, bits);
|
||||
}
|
||||
@@ -888,7 +888,7 @@ void VerilatedTraceBuffer<VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newv
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*reinterpret_cast<double*>(oldp) = newval;
|
||||
std::memcpy(oldp, &newval, sizeof(newval));
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
emitDouble(code, newval);
|
||||
|
||||
+332
-21
@@ -73,15 +73,45 @@ 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
|
||||
|
||||
//===================================================================
|
||||
// VlProcess stores metadata of running processes
|
||||
|
||||
class VlProcess final {
|
||||
// MEMBERS
|
||||
int m_state; // Current state of the process
|
||||
|
||||
public:
|
||||
// TYPES
|
||||
enum : int { // Type int for compatibility with $c
|
||||
FINISHED = 0,
|
||||
RUNNING = 1,
|
||||
WAITING = 2,
|
||||
SUSPENDED = 3,
|
||||
KILLED = 4,
|
||||
};
|
||||
|
||||
// CONSTRUCTORS
|
||||
VlProcess()
|
||||
: m_state{RUNNING} {}
|
||||
|
||||
// METHODS
|
||||
int state() { return m_state; }
|
||||
void state(int s) { m_state = s; }
|
||||
};
|
||||
|
||||
using VlProcessRef = std::shared_ptr<VlProcess>;
|
||||
|
||||
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
|
||||
|
||||
//===================================================================
|
||||
// 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
|
||||
alignas(16) std::array<uint64_t, roundUpToMultipleOf<64>(T_size) / 64> m_flags; // The flags
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
@@ -91,10 +121,18 @@ public:
|
||||
// METHODS
|
||||
|
||||
// Set all elements to false
|
||||
void clear() { m_flags.fill(false); }
|
||||
void clear() { m_flags.fill(0); }
|
||||
|
||||
// Reference to element at 'index'
|
||||
bool& at(size_t index) { return m_flags.at(index); }
|
||||
// Word at given 'wordIndex'
|
||||
uint64_t word(size_t wordIndex) const { return m_flags[wordIndex]; }
|
||||
|
||||
// Set specified flag to given value
|
||||
void set(size_t index, bool value) {
|
||||
uint64_t& w = m_flags[index / 64];
|
||||
const size_t bitIndex = index % 64;
|
||||
w &= ~(1ULL << bitIndex);
|
||||
w |= (static_cast<uint64_t>(value) << bitIndex);
|
||||
}
|
||||
|
||||
// Return true iff at least one element is set
|
||||
bool any() const {
|
||||
@@ -104,13 +142,13 @@ public:
|
||||
}
|
||||
|
||||
// Set all elements true in 'this' that are set in 'other'
|
||||
void set(const VlTriggerVec<T_size>& other) {
|
||||
void thisOr(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];
|
||||
for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] = a.m_flags[i] & ~b.m_flags[i];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -136,14 +174,34 @@ public:
|
||||
};
|
||||
|
||||
inline std::string VL_TO_STRING(const VlEvent& e) {
|
||||
return std::string("triggered=") + (e.isTriggered() ? "true" : "false");
|
||||
return std::string{"triggered="} + (e.isTriggered() ? "true" : "false");
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Shuffle RNG
|
||||
// Random
|
||||
|
||||
extern uint64_t vl_rand64() VL_MT_SAFE;
|
||||
// Random Number Generator with internal state
|
||||
class VlRNG final {
|
||||
std::array<uint64_t, 2> m_state;
|
||||
|
||||
public:
|
||||
// The default constructor simply sets state, to avoid vl_rand64()
|
||||
// having to check for construction at each call
|
||||
// Alternative: seed with zero and check on rand64() call
|
||||
VlRNG() VL_MT_SAFE;
|
||||
explicit VlRNG(uint64_t seed0) VL_MT_SAFE : m_state{0x12341234UL, seed0} {}
|
||||
void srandom(uint64_t n) VL_MT_UNSAFE;
|
||||
std::string get_randstate() const VL_MT_UNSAFE;
|
||||
void set_randstate(const std::string& state) VL_MT_UNSAFE;
|
||||
uint64_t rand64() VL_MT_UNSAFE;
|
||||
// Threadsafe, but requires use on vl_thread_rng
|
||||
static uint64_t vl_thread_rng_rand64() VL_MT_SAFE;
|
||||
static VlRNG& vl_thread_rng() VL_MT_SAFE;
|
||||
};
|
||||
|
||||
inline uint64_t vl_rand64() VL_MT_SAFE { return VlRNG::vl_thread_rng_rand64(); }
|
||||
|
||||
// RNG for shuffle()
|
||||
class VlURNG final {
|
||||
public:
|
||||
using result_type = size_t;
|
||||
@@ -152,6 +210,11 @@ public:
|
||||
size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
|
||||
};
|
||||
|
||||
// These require the class object to have the thread safety lock
|
||||
inline IData VL_RANDOM_RNG_I(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); }
|
||||
inline QData VL_RANDOM_RNG_Q(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); }
|
||||
extern WDataOutP VL_RANDOM_RNG_W(VlRNG& rngr, int obits, WDataOutP outwp) VL_MT_UNSAFE;
|
||||
|
||||
//===================================================================
|
||||
// Readmem/Writemem operation classes
|
||||
|
||||
@@ -370,18 +433,17 @@ public:
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(int32_t index) {
|
||||
static thread_local T_Value s_throwAway;
|
||||
static thread_local T_Value t_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
s_throwAway = atDefault();
|
||||
return s_throwAway;
|
||||
t_throwAway = atDefault();
|
||||
return t_throwAway;
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(int32_t index) const {
|
||||
static thread_local T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
|
||||
return atDefault();
|
||||
@@ -389,6 +451,10 @@ public:
|
||||
return m_deque[index];
|
||||
}
|
||||
}
|
||||
// Access with an index counted from end (e.g. q[$])
|
||||
T_Value& atBack(int32_t index) { return at(m_deque.size() - 1 - index); }
|
||||
const T_Value& atBack(int32_t index) const { return at(m_deque.size() - 1 - index); }
|
||||
|
||||
// function void q.insert(index, value);
|
||||
void insert(int32_t index, const T_Value& value) {
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
|
||||
@@ -404,6 +470,12 @@ public:
|
||||
for (int32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
|
||||
return out;
|
||||
}
|
||||
VlQueue sliceFrontBack(int32_t lsb, int32_t msb) const {
|
||||
return slice(lsb, m_deque.size() - 1 - msb);
|
||||
}
|
||||
VlQueue sliceBackBack(int32_t lsb, int32_t msb) const {
|
||||
return slice(m_deque.size() - 1 - lsb, m_deque.size() - 1 - msb);
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_deque.begin(); }
|
||||
@@ -445,7 +517,7 @@ public:
|
||||
template <typename Func>
|
||||
VlQueue unique(Func with_func) const {
|
||||
VlQueue out;
|
||||
std::set<T_Value> saw;
|
||||
std::set<decltype(with_func(0, m_deque[0]))> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
const auto i_mapped = with_func(0, i);
|
||||
const auto it = saw.find(i_mapped);
|
||||
@@ -474,7 +546,7 @@ public:
|
||||
VlQueue<IData> unique_index(Func with_func) const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
std::unordered_set<T_Value> saw;
|
||||
std::set<decltype(with_func(0, m_deque[0]))> saw;
|
||||
for (const auto& i : m_deque) {
|
||||
const auto i_mapped = with_func(index, i);
|
||||
auto it = saw.find(i_mapped);
|
||||
@@ -788,6 +860,22 @@ public:
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> unique(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
T_Key default_key;
|
||||
using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
|
||||
std::set<WithType> saw;
|
||||
for (const auto& i : m_map) {
|
||||
const auto i_mapped = with_func(default_key, i.second);
|
||||
const auto it = saw.find(i_mapped);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i_mapped);
|
||||
out.push_back(i.second);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<T_Key> unique_index() const {
|
||||
VlQueue<T_Key> out;
|
||||
std::set<T_Key> saw;
|
||||
@@ -801,6 +889,21 @@ public:
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> unique_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
|
||||
std::set<WithType> saw;
|
||||
for (const auto& i : m_map) {
|
||||
const auto i_mapped = with_func(i.first, i.second);
|
||||
auto it = saw.find(i_mapped);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i_mapped);
|
||||
out.push_back(i.first);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
for (const auto& i : m_map)
|
||||
@@ -861,6 +964,16 @@ public:
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> min(Func with_func) const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::min_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return with_func(a.first, a.second) < with_func(b.first, b.second);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
VlQueue<T_Value> max() const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
@@ -870,6 +983,16 @@ public:
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> max(Func with_func) const {
|
||||
if (m_map.empty()) return VlQueue<T_Value>();
|
||||
const auto it = std::max_element(
|
||||
m_map.begin(), m_map.end(),
|
||||
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
|
||||
return with_func(a.first, a.second) < with_func(b.first, b.second);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
|
||||
T_Value r_sum() const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
@@ -998,9 +1121,15 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
|
||||
|
||||
template <class T_Value, std::size_t T_Depth>
|
||||
struct VlUnpacked final {
|
||||
private:
|
||||
// TYPES
|
||||
using T_Key = IData; // Index type, for uniformity with other containers
|
||||
using Unpacked = T_Value[T_Depth];
|
||||
|
||||
public:
|
||||
// MEMBERS
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
T_Value m_storage[T_Depth]; // Contents of the unpacked array
|
||||
Unpacked m_storage; // Contents of the unpacked array
|
||||
|
||||
// CONSTRUCTORS
|
||||
// Default constructors and destructor are used. Note however that C++20 requires that
|
||||
@@ -1011,6 +1140,7 @@ struct VlUnpacked final {
|
||||
// Default copy assignment operators are used.
|
||||
|
||||
// METHODS
|
||||
public:
|
||||
// Raw access
|
||||
WData* data() { return &m_storage[0]; }
|
||||
const WData* data() const { return &m_storage[0]; }
|
||||
@@ -1023,6 +1153,170 @@ struct VlUnpacked final {
|
||||
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; }
|
||||
bool operator==(const VlUnpacked<T_Value, T_Depth>& that) const { return !neq(that); }
|
||||
bool operator!=(const VlUnpacked<T_Value, T_Depth>& that) { return neq(that); }
|
||||
|
||||
void sort() { std::sort(std::begin(m_storage), std::end(m_storage)); }
|
||||
template <typename Func>
|
||||
void sort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
std::sort(std::begin(m_storage), std::end(m_storage),
|
||||
[=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaningless with sort, as it changes
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
}
|
||||
// std::rbegin/std::rend not available until C++14
|
||||
void rsort() {
|
||||
std::sort(std::begin(m_storage), std::end(m_storage), std::greater<T_Value>());
|
||||
}
|
||||
template <typename Func>
|
||||
void rsort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
// std::rbegin/std::rend not available until C++14, so using > below
|
||||
std::sort(std::begin(m_storage), std::end(m_storage),
|
||||
[=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaningless with sort, as it changes
|
||||
return with_func(0, a) > with_func(0, b);
|
||||
});
|
||||
}
|
||||
void reverse() { std::reverse(std::begin(m_storage), std::end(m_storage)); }
|
||||
void shuffle() { std::shuffle(std::begin(m_storage), std::end(m_storage), VlURNG{}); }
|
||||
VlQueue<T_Value> unique() const {
|
||||
VlQueue<T_Value> out;
|
||||
std::set<T_Value> saw;
|
||||
for (const auto& i : m_storage) {
|
||||
const auto it = saw.find(i);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i);
|
||||
out.push_back(i);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> unique(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
std::set<T_Value> saw;
|
||||
for (const auto& i : m_storage) {
|
||||
const auto i_mapped = with_func(0, i);
|
||||
const auto it = saw.find(i_mapped);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i_mapped);
|
||||
out.push_back(i);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
VlQueue<T_Key> unique_index() const {
|
||||
VlQueue<T_Key> out;
|
||||
IData index = 0;
|
||||
std::set<T_Value> saw;
|
||||
for (const auto& i : m_storage) {
|
||||
const auto it = saw.find(i);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i);
|
||||
out.push_back(index);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> unique_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
IData index = 0;
|
||||
std::unordered_set<T_Value> saw;
|
||||
for (const auto& i : m_storage) {
|
||||
const auto i_mapped = with_func(index, i);
|
||||
auto it = saw.find(i_mapped);
|
||||
if (it == saw.end()) {
|
||||
saw.insert(it, i_mapped);
|
||||
out.push_back(index);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_storage) {
|
||||
if (with_func(index, i)) out.push_back(i);
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_storage) {
|
||||
if (with_func(index, i)) out.push_back(index);
|
||||
++index;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find_first(Func with_func) const {
|
||||
// Can't use std::find_if as need index number
|
||||
IData index = 0;
|
||||
for (const auto& i : m_storage) {
|
||||
if (with_func(index, i)) return VlQueue<T_Value>::cons(i);
|
||||
++index;
|
||||
}
|
||||
return VlQueue<T_Value>{};
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_first_index(Func with_func) const {
|
||||
IData index = 0;
|
||||
for (const auto& i : m_storage) {
|
||||
if (with_func(index, i)) return VlQueue<IData>::cons(index);
|
||||
++index;
|
||||
}
|
||||
return VlQueue<T_Key>{};
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find_last(Func with_func) const {
|
||||
for (int i = T_Depth - 1; i >= 0; i--) {
|
||||
if (with_func(i, m_storage[i])) return VlQueue<T_Value>::cons(m_storage[i]);
|
||||
}
|
||||
return VlQueue<T_Value>{};
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_last_index(Func with_func) const {
|
||||
for (int i = T_Depth - 1; i >= 0; i--) {
|
||||
if (with_func(i, m_storage[i])) return VlQueue<IData>::cons(i);
|
||||
}
|
||||
return VlQueue<T_Key>{};
|
||||
}
|
||||
|
||||
// Reduction operators
|
||||
VlQueue<T_Value> min() const {
|
||||
const auto it = std::min_element(std::begin(m_storage), std::end(m_storage));
|
||||
return VlQueue<T_Value>::cons(*it);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> min(Func with_func) const {
|
||||
const auto it = std::min_element(std::begin(m_storage), std::end(m_storage),
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(*it);
|
||||
}
|
||||
VlQueue<T_Value> max() const {
|
||||
const auto it = std::max_element(std::begin(m_storage), std::end(m_storage));
|
||||
return VlQueue<T_Value>::cons(*it);
|
||||
}
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> max(Func with_func) const {
|
||||
const auto it = std::max_element(std::begin(m_storage), std::end(m_storage),
|
||||
[&with_func](const IData& a, const IData& b) {
|
||||
return with_func(0, a) < with_func(0, b);
|
||||
});
|
||||
return VlQueue<T_Value>::cons(*it);
|
||||
}
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
@@ -1096,7 +1390,7 @@ public:
|
||||
}
|
||||
|
||||
// Deletes all queued garbage objects.
|
||||
void deleteAll() VL_MT_SAFE;
|
||||
void deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE;
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
@@ -1166,7 +1460,8 @@ public:
|
||||
// CONSTRUCTORS
|
||||
VlClassRef() = default;
|
||||
// Init with nullptr
|
||||
explicit VlClassRef(VlNull){};
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlClassRef(VlNull){};
|
||||
template <typename... T_Args>
|
||||
VlClassRef(VlDeleter& deleter, T_Args&&... args)
|
||||
// () required here to avoid narrowing conversion warnings,
|
||||
@@ -1188,6 +1483,16 @@ public:
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlClassRef(VlClassRef&& moved)
|
||||
: m_objp{vlstd::exchange(moved.m_objp, nullptr)} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
template <typename T_OtherClass>
|
||||
VlClassRef(const VlClassRef<T_OtherClass>& copied)
|
||||
: m_objp{copied.m_objp} {
|
||||
refCountInc();
|
||||
}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
template <typename T_OtherClass>
|
||||
VlClassRef(VlClassRef<T_OtherClass>&& moved)
|
||||
: m_objp{vlstd::exchange(moved.m_objp, nullptr)} {}
|
||||
~VlClassRef() { refCountDec(); }
|
||||
|
||||
// METHODS
|
||||
@@ -1233,8 +1538,14 @@ public:
|
||||
// For 'if (ptr)...'
|
||||
operator bool() const { return m_objp; }
|
||||
// In SV A == B iff both are handles to the same object (IEEE 1800-2017 8.4)
|
||||
bool operator==(const VlClassRef& rhs) const { return m_objp == rhs.m_objp; };
|
||||
bool operator!=(const VlClassRef& rhs) const { return m_objp != rhs.m_objp; };
|
||||
template <typename T_OtherClass>
|
||||
bool operator==(const VlClassRef<T_OtherClass>& rhs) const {
|
||||
return m_objp == rhs.m_objp;
|
||||
};
|
||||
template <typename T_OtherClass>
|
||||
bool operator!=(const VlClassRef<T_OtherClass>& rhs) const {
|
||||
return m_objp != rhs.m_objp;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
|
||||
@@ -61,16 +61,6 @@ constexpr unsigned VL_TRACE_MAX_VCD_CODE_SIZE = 5; // Maximum length of a VCD s
|
||||
// cache-lines.
|
||||
constexpr unsigned VL_TRACE_SUFFIX_ENTRY_SIZE = 8; // Size of a suffix entry
|
||||
|
||||
//=============================================================================
|
||||
// Utility functions: TODO: put these in a common place and share them.
|
||||
|
||||
template <size_t N>
|
||||
static size_t roundUpToMultipleOf(size_t value) {
|
||||
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
|
||||
size_t mask = N - 1;
|
||||
return (value + mask) & ~mask;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Specialization of the generics for this trace format
|
||||
|
||||
@@ -420,28 +410,14 @@ void VerilatedVcd::dumpHeader() {
|
||||
if (*np == ' ') np++;
|
||||
if (*np == '\t') break; // tab means signal name starts
|
||||
printIndent(1);
|
||||
// Find character after name end
|
||||
const char* sp = np;
|
||||
while (*sp && *sp != ' ' && *sp != '\t' && !(*sp & '\x80')) sp++;
|
||||
|
||||
printStr("$scope ");
|
||||
if (*sp & '\x80') {
|
||||
switch (*sp & 0x7f) {
|
||||
case VLT_TRACE_SCOPE_STRUCT: printStr("struct "); break;
|
||||
case VLT_TRACE_SCOPE_INTERFACE: printStr("interface "); break;
|
||||
case VLT_TRACE_SCOPE_UNION: printStr("union "); break;
|
||||
default: printStr("module ");
|
||||
}
|
||||
} else {
|
||||
printStr("module ");
|
||||
}
|
||||
printStr("$scope module ");
|
||||
|
||||
for (; *np && *np != ' ' && *np != '\t'; np++) {
|
||||
if (*np == '[') {
|
||||
printStr("[");
|
||||
} else if (*np == ']') {
|
||||
printStr("]");
|
||||
} else if (!(*np & '\x80')) {
|
||||
} else {
|
||||
*m_writep++ = *np;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,23 +36,23 @@
|
||||
/// with the SystemC simulation kernel, just like a SystemC-documented
|
||||
/// trace format.
|
||||
|
||||
class VerilatedVcdSc final : sc_trace_file, public VerilatedVcdC {
|
||||
class VerilatedVcdSc final : sc_core::sc_trace_file, public VerilatedVcdC {
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcdSc);
|
||||
|
||||
public:
|
||||
/// Construct a SC trace object, and register with the SystemC kernel
|
||||
VerilatedVcdSc() {
|
||||
sc_get_curr_simcontext()->add_trace_file(this);
|
||||
sc_core::sc_get_curr_simcontext()->add_trace_file(this);
|
||||
// We want to avoid a depreciated warning, but still be back compatible.
|
||||
// Turning off the message just for this still results in an
|
||||
// annoying "to turn off" message.
|
||||
const sc_time t1sec{1, SC_SEC};
|
||||
const sc_core::sc_time t1sec{1, sc_core::SC_SEC};
|
||||
if (t1sec.to_default_time_units() != 0) {
|
||||
const sc_time tunits{1.0 / t1sec.to_default_time_units(), SC_SEC};
|
||||
const sc_core::sc_time tunits{1.0 / t1sec.to_default_time_units(), sc_core::SC_SEC};
|
||||
spTrace()->set_time_unit(tunits.to_string());
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
spTrace()->set_time_resolution(sc_core::sc_get_time_resolution().to_string());
|
||||
}
|
||||
/// Destruct, flush, and close the dump
|
||||
~VerilatedVcdSc() override { close(); }
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
// METHODS - for SC kernel
|
||||
// Called by SystemC simulate()
|
||||
void cycle(bool delta_cycle) override {
|
||||
if (!delta_cycle) this->dump(sc_time_stamp().to_double());
|
||||
if (!delta_cycle) this->dump(sc_core::sc_time_stamp().to_double());
|
||||
}
|
||||
|
||||
// Override VerilatedVcdC. Must be called after starting simulation.
|
||||
@@ -78,7 +78,7 @@ private:
|
||||
// Cadence Incisive has these as abstract functions so we must create them
|
||||
void set_time_unit(int exponent10_seconds) override {} // deprecated
|
||||
#endif
|
||||
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
|
||||
void set_time_unit(double v, sc_core::sc_time_unit tu) override {} // LCOV_EXCL_LINE
|
||||
|
||||
//--------------------------------------------------
|
||||
// SystemC 2.1.v1
|
||||
@@ -95,8 +95,8 @@ private:
|
||||
// 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 )
|
||||
DECL_TRACE_METHOD_A( sc_core::sc_event )
|
||||
DECL_TRACE_METHOD_A( sc_core::sc_time )
|
||||
#endif
|
||||
|
||||
DECL_TRACE_METHOD_A( bool )
|
||||
|
||||
+21
-26
@@ -165,6 +165,7 @@ class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio {
|
||||
protected:
|
||||
const VerilatedVar* m_varp = nullptr;
|
||||
const VerilatedScope* m_scopep = nullptr;
|
||||
std::string m_fullname;
|
||||
const VerilatedRange& get_range() const {
|
||||
// Determine number of dimensions and return outermost
|
||||
return (m_varp->dims() > 1) ? m_varp->unpacked() : m_varp->packed();
|
||||
@@ -173,11 +174,13 @@ protected:
|
||||
public:
|
||||
VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: m_varp{varp}
|
||||
, m_scopep{scopep} {}
|
||||
, m_scopep{scopep}
|
||||
, m_fullname{std::string{m_scopep->name()} + '.' + m_varp->name()} {}
|
||||
explicit VerilatedVpioVarBase(const VerilatedVpioVarBase* varp) {
|
||||
if (varp) {
|
||||
m_varp = varp->m_varp;
|
||||
m_scopep = varp->m_scopep;
|
||||
m_fullname = varp->m_fullname;
|
||||
}
|
||||
}
|
||||
static VerilatedVpioVarBase* castp(vpiHandle h) {
|
||||
@@ -188,11 +191,7 @@ public:
|
||||
uint32_t size() const override { return get_range().elements(); }
|
||||
const VerilatedRange* rangep() const override { return &get_range(); }
|
||||
const char* name() const override { return m_varp->name(); }
|
||||
const char* fullname() const override {
|
||||
static thread_local std::string t_out;
|
||||
t_out = std::string{m_scopep->name()} + "." + name();
|
||||
return t_out.c_str();
|
||||
}
|
||||
const char* fullname() const override { return m_fullname.c_str(); }
|
||||
};
|
||||
|
||||
class VerilatedVpioParam final : public VerilatedVpioVarBase {
|
||||
@@ -646,23 +645,21 @@ public:
|
||||
continue;
|
||||
}
|
||||
VerilatedVpiCbHolder& ho = *it++;
|
||||
if (VerilatedVpioVar* const varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) {
|
||||
void* const newDatap = varop->varDatap();
|
||||
void* const prevDatap
|
||||
= varop->prevDatap(); // Was malloced when we added the callback
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
|
||||
varop->fullname(), *(static_cast<CData*>(newDatap)),
|
||||
*(static_cast<CData*>(prevDatap)), newDatap,
|
||||
prevDatap););
|
||||
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
|
||||
ho.id(), varop->fullname(),
|
||||
*(static_cast<CData*>(newDatap))););
|
||||
update.insert(varop);
|
||||
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
||||
(ho.cb_rtnp())(ho.cb_datap());
|
||||
called = true;
|
||||
}
|
||||
VerilatedVpioVar* const varop
|
||||
= reinterpret_cast<VerilatedVpioVar*>(ho.cb_datap()->obj);
|
||||
void* const newDatap = varop->varDatap();
|
||||
void* const prevDatap = varop->prevDatap(); // Was malloced when we added the callback
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
|
||||
varop->fullname(), *(static_cast<CData*>(newDatap)),
|
||||
*(static_cast<CData*>(prevDatap)), newDatap, prevDatap););
|
||||
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
|
||||
ho.id(), varop->fullname(),
|
||||
*(static_cast<CData*>(newDatap))););
|
||||
update.insert(varop);
|
||||
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
|
||||
(ho.cb_rtnp())(ho.cb_datap());
|
||||
called = true;
|
||||
}
|
||||
if (was_last) break;
|
||||
}
|
||||
@@ -791,7 +788,6 @@ void VerilatedVpiImp::dumpCbs() VL_MT_UNSAFE_ONE {
|
||||
}
|
||||
for (auto& ifuture : s().m_nextCbs) {
|
||||
const QData time = ifuture.first.first;
|
||||
const uint64_t id = ifuture.first.second;
|
||||
VerilatedVpiCbHolder& ho = ifuture.second;
|
||||
if (VL_UNLIKELY(!ho.invalid())) {
|
||||
VL_DBG_MSGF("- vpi: time=%" PRId64 "(NEXT) reason=%d=%s id=%" PRId64 "\n", time,
|
||||
@@ -802,7 +798,6 @@ void VerilatedVpiImp::dumpCbs() VL_MT_UNSAFE_ONE {
|
||||
}
|
||||
for (auto& ifuture : s().m_futureCbs) {
|
||||
const QData time = ifuture.first.first;
|
||||
const uint64_t id = ifuture.first.second;
|
||||
VerilatedVpiCbHolder& ho = ifuture.second;
|
||||
if (VL_UNLIKELY(!ho.invalid())) {
|
||||
VL_DBG_MSGF("- vpi: time=%" PRId64 " reason=%d=%s id=%" PRId64 "\n", time,
|
||||
@@ -2312,7 +2307,7 @@ PLI_INT32 vpi_flush(void) {
|
||||
VerilatedVpiImp::assertOneCheck();
|
||||
VL_VPI_ERROR_RESET_();
|
||||
Verilated::runFlushCallbacks();
|
||||
return 0;
|
||||
return 0; // Gcc coverage bug // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
|
||||
|
||||
+106
-70
@@ -37,8 +37,13 @@
|
||||
//=========================================================================
|
||||
// Compiler pragma abstraction
|
||||
|
||||
#if defined(__clang__)
|
||||
# define VL_CLANG_ATTR(attr) __attribute__(( attr ))
|
||||
#else
|
||||
# define VL_CLANG_ATTR(attr)
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define VL_ATTR_ALIGNED(alignment) __attribute__((aligned(alignment)))
|
||||
# define VL_ATTR_ALWINLINE __attribute__((always_inline)) inline
|
||||
# define VL_ATTR_NOINLINE __attribute__((noinline))
|
||||
# define VL_ATTR_COLD __attribute__((cold))
|
||||
@@ -57,19 +62,6 @@
|
||||
// All VL_ATTR_WEAK symbols must be marked with the macOS -U linker flag in verilated.mk.in
|
||||
# define VL_ATTR_WEAK __attribute__((weak))
|
||||
# endif
|
||||
# if defined(__clang__)
|
||||
# define VL_ACQUIRE(...) __attribute__((annotate("ACQUIRE"))) __attribute__((acquire_capability(__VA_ARGS__)))
|
||||
# define VL_ACQUIRE_SHARED(...) __attribute__((annotate("ACQUIRE_SHARED"))) __attribute__((acquire_shared_capability(__VA_ARGS__)))
|
||||
# define VL_RELEASE(...) __attribute__((annotate("RELEASE"))) __attribute__((release_capability(__VA_ARGS__)))
|
||||
# define VL_RELEASE_SHARED(...) __attribute__((annotate("RELEASE_SHARED"))) __attribute__((release_shared_capability(__VA_ARGS__)))
|
||||
# define VL_TRY_ACQUIRE(...) __attribute__((try_acquire_capability(__VA_ARGS__)))
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) __attribute__((try_acquire_shared_capability(__VA_ARGS__)))
|
||||
# define VL_CAPABILITY(x) __attribute__((capability(x)))
|
||||
# define VL_REQUIRES(x) __attribute__((annotate("REQUIRES"))) __attribute__((requires_capability(x)))
|
||||
# define VL_GUARDED_BY(x) __attribute__((annotate("GUARDED_BY"))) __attribute__((guarded_by(x)))
|
||||
# define VL_EXCLUDES(x) __attribute__((annotate("EXCLUDES"))) __attribute__((locks_excluded(x)))
|
||||
# define VL_SCOPED_CAPABILITY __attribute__((scoped_lockable))
|
||||
# endif
|
||||
# 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()
|
||||
@@ -77,10 +69,74 @@
|
||||
# define VL_PREFETCH_RW(p) __builtin_prefetch((p), 1)
|
||||
#endif
|
||||
|
||||
// Defaults for unsupported compiler features
|
||||
#ifndef VL_ATTR_ALIGNED
|
||||
# define VL_ATTR_ALIGNED(alignment) ///< Attribute to align structure to byte alignment
|
||||
// Function acquires a capability/lock (-fthread-safety)
|
||||
#define VL_ACQUIRE(...) \
|
||||
VL_CLANG_ATTR(annotate("ACQUIRE")) \
|
||||
VL_CLANG_ATTR(acquire_capability(__VA_ARGS__))
|
||||
// Function acquires a shared capability/lock (-fthread-safety)
|
||||
#define VL_ACQUIRE_SHARED(...) \
|
||||
VL_CLANG_ATTR(annotate("ACQUIRE_SHARED")) \
|
||||
VL_CLANG_ATTR(acquire_shared_capability(__VA_ARGS__))
|
||||
// Function releases a capability/lock (-fthread-safety)
|
||||
#define VL_RELEASE(...) \
|
||||
VL_CLANG_ATTR(annotate("RELEASE")) \
|
||||
VL_CLANG_ATTR(release_capability(__VA_ARGS__))
|
||||
// Function releases a shared capability/lock (-fthread-safety)
|
||||
#define VL_RELEASE_SHARED(...) \
|
||||
VL_CLANG_ATTR(annotate("RELEASE_SHARED")) \
|
||||
VL_CLANG_ATTR(release_shared_capability(__VA_ARGS__))
|
||||
// Function returns bool if acquired a capability (-fthread-safety)
|
||||
#define VL_TRY_ACQUIRE(...) \
|
||||
VL_CLANG_ATTR(try_acquire_capability(__VA_ARGS__))
|
||||
// Function returns bool if acquired shared (-fthread-safety)
|
||||
#define VL_TRY_ACQUIRE_SHARED(...) \
|
||||
VL_CLANG_ATTR(try_acquire_shared_capability(__VA_ARGS__))
|
||||
// Function requires a capability inbound (-fthread-safety)
|
||||
#define VL_CAPABILITY(x) \
|
||||
VL_CLANG_ATTR(capability(x))
|
||||
// Name of mutex protecting this variable (-fthread-safety)
|
||||
#define VL_EXCLUDES(x) \
|
||||
VL_CLANG_ATTR(annotate("EXCLUDES")) \
|
||||
VL_CLANG_ATTR(locks_excluded(x))
|
||||
// Scoped threaded capability/lock (-fthread-safety)
|
||||
#define VL_SCOPED_CAPABILITY \
|
||||
VL_CLANG_ATTR(scoped_lockable)
|
||||
// Annotated function returns reference to the given capability.
|
||||
// Allowed on: function, method. (-fthread-safety)
|
||||
#define VL_RETURN_CAPABILITY(x) \
|
||||
VL_CLANG_ATTR(lock_returned(x))
|
||||
// Assert that capability is already held.
|
||||
// Allowed on: function, method. (-fthread-safety)
|
||||
#define VL_ASSERT_CAPABILITY(x) \
|
||||
VL_CLANG_ATTR(assert_capability(x))
|
||||
|
||||
// Require mutex locks only in code units which work with enabled multi-threading.
|
||||
#if !defined(VL_MT_DISABLED_CODE_UNIT)
|
||||
// Function requires not having a capability inbound (-fthread-safety)
|
||||
# define VL_REQUIRES(x) \
|
||||
VL_CLANG_ATTR(annotate("REQUIRES")) \
|
||||
VL_CLANG_ATTR(requires_capability(x))
|
||||
// Name of capability/lock (-fthread-safety)
|
||||
# define VL_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("GUARDED_BY")) \
|
||||
VL_CLANG_ATTR(guarded_by(x))
|
||||
// The data that the annotated pointer points to is protected by the given capability.
|
||||
// The pointer itself is not protected.
|
||||
// Allowed on: pointer data member. (-fthread-safety)
|
||||
# define VL_PT_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \
|
||||
VL_CLANG_ATTR(pt_guarded_by(x))
|
||||
#else
|
||||
// Keep annotations for clang_check_attributes
|
||||
# define VL_REQUIRES(x) \
|
||||
VL_CLANG_ATTR(annotate("REQUIRES"))
|
||||
# define VL_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("GUARDED_BY"))
|
||||
# define VL_PT_GUARDED_BY(x) \
|
||||
VL_CLANG_ATTR(annotate("PT_GUARDED_BY"))
|
||||
#endif
|
||||
|
||||
// Defaults for unsupported compiler features
|
||||
#ifndef VL_ATTR_ALWINLINE
|
||||
# define VL_ATTR_ALWINLINE ///< Attribute to inline, even when not optimizing
|
||||
#endif
|
||||
@@ -111,19 +167,6 @@
|
||||
#ifndef VL_ATTR_WEAK
|
||||
# define VL_ATTR_WEAK ///< Attribute that function external that is optionally defined
|
||||
#endif
|
||||
#ifndef VL_CAPABILITY
|
||||
# define VL_ACQUIRE(...) ///< Function acquires a capability/lock (-fthread-safety)
|
||||
# define VL_ACQUIRE_SHARED(...) ///< Function acquires a shared capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE(...) ///< Function releases a capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE_SHARED(...) ///< Function releases a shared capability/lock (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE(...) ///< Function returns bool if acquired a capability (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) ///< Function returns bool if acquired shared (-fthread-safety)
|
||||
# define VL_REQUIRES(x) ///< Function requires a capability inbound (-fthread-safety)
|
||||
# define VL_EXCLUDES(x) ///< Function requires not having a capability inbound (-fthread-safety)
|
||||
# define VL_CAPABILITY(x) ///< Name of capability/lock (-fthread-safety)
|
||||
# define VL_GUARDED_BY(x) ///< Name of mutex protecting this variable (-fthread-safety)
|
||||
# define VL_SCOPED_CAPABILITY ///< Scoped threaded capability/lock (-fthread-safety)
|
||||
#endif
|
||||
#ifndef VL_LIKELY
|
||||
# define VL_LIKELY(x) (!!(x)) ///< Return boolean expression that is more often true
|
||||
# define VL_UNLIKELY(x) (!!(x)) ///< Return boolean expression that is more often false
|
||||
@@ -142,6 +185,7 @@
|
||||
|
||||
|
||||
#ifndef VL_NO_LEGACY
|
||||
# define VL_ATTR_ALIGNED(alignment) // Deprecated
|
||||
# define VL_FUNC __func__ // Deprecated
|
||||
# define VL_THREAD // Deprecated
|
||||
# define VL_THREAD_LOCAL thread_local // Deprecated
|
||||
@@ -149,49 +193,24 @@
|
||||
#endif
|
||||
|
||||
// Comment tag that Function is pure (and thus also VL_MT_SAFE)
|
||||
#if defined(__clang__)
|
||||
# define VL_PURE __attribute__((annotate("PURE")))
|
||||
#else
|
||||
# define VL_PURE
|
||||
#endif
|
||||
#define VL_PURE VL_CLANG_ATTR(annotate("PURE"))
|
||||
// Comment tag that function is threadsafe
|
||||
#if defined(__clang__)
|
||||
# define VL_MT_SAFE __attribute__((annotate("MT_SAFE")))
|
||||
#else
|
||||
# define VL_MT_SAFE
|
||||
#endif
|
||||
#define VL_MT_SAFE VL_CLANG_ATTR(annotate("MT_SAFE"))
|
||||
// Comment tag that function is threadsafe, only if
|
||||
// other threads doesn't change tree topology
|
||||
#define VL_MT_STABLE VL_CLANG_ATTR(annotate("MT_STABLE"))
|
||||
// Comment tag that function is threadsafe, only
|
||||
// during normal operation (post-init)
|
||||
#if defined(__clang__)
|
||||
# define VL_MT_SAFE_POSTINIT __attribute__((annotate("MT_SAFE_POSTINIT")))
|
||||
#else
|
||||
# define VL_MT_SAFE_POSTINIT
|
||||
#endif
|
||||
#define VL_MT_SAFE_POSTINIT VL_CLANG_ATTR(annotate("MT_SAFE_POSTINIT"))
|
||||
// Attribute that function is clang threadsafe and uses given mutex
|
||||
#if defined(__clang__)
|
||||
# define VL_MT_SAFE_EXCLUDES(mutex) __attribute__((annotate("MT_SAFE_EXCLUDES"))) VL_EXCLUDES(mutex)
|
||||
#else
|
||||
# define VL_MT_SAFE_EXCLUDES(mutex) VL_EXCLUDES(mutex)
|
||||
#endif
|
||||
#define VL_MT_SAFE_EXCLUDES(mutex) VL_CLANG_ATTR(annotate("MT_SAFE_EXCLUDES")) VL_EXCLUDES(mutex)
|
||||
// Comment tag that function is not threadsafe
|
||||
#if defined(__clang__)
|
||||
# define VL_MT_UNSAFE __attribute__((annotate("MT_UNSAFE")))
|
||||
#else
|
||||
# define VL_MT_UNSAFE
|
||||
#endif
|
||||
#define VL_MT_UNSAFE VL_CLANG_ATTR(annotate("MT_UNSAFE"))
|
||||
// Comment tag that function is not threadsafe
|
||||
// protected to make sure single-caller
|
||||
#if defined(__clang__)
|
||||
# define VL_MT_UNSAFE_ONE __attribute__((annotate("MT_UNSAFE_ONE")))
|
||||
#else
|
||||
# define VL_MT_UNSAFE_ONE
|
||||
#endif
|
||||
#define VL_MT_UNSAFE_ONE VL_CLANG_ATTR(annotate("MT_UNSAFE_ONE"))
|
||||
// Comment tag that function is entry point of parallelization
|
||||
#if defined(__clang__)
|
||||
# define VL_MT_START __attribute__((annotate("MT_START")))
|
||||
#else
|
||||
# define VL_MT_START
|
||||
#endif
|
||||
#define VL_MT_START VL_CLANG_ATTR(annotate("MT_START"))
|
||||
|
||||
#ifndef VL_NO_LEGACY
|
||||
# define VL_ULL(c) (c##ULL) // Add appropriate suffix to 64-bit constant (deprecated)
|
||||
@@ -317,6 +336,7 @@ extern "C" void __gcov_dump();
|
||||
// Now that C++ requires these standard types the vl types are deprecated
|
||||
#include <cstdint>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
|
||||
#ifndef VL_NO_LEGACY
|
||||
using vluint8_t = uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
@@ -422,6 +442,11 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
Type(const Type& other) = delete; \
|
||||
Type& operator=(const Type&) = delete
|
||||
|
||||
// Declare a class as unmovable; put after a private:
|
||||
#define VL_UNMOVABLE(Type) \
|
||||
Type(Type&& other) = delete; \
|
||||
Type& operator=(Type&&) = delete
|
||||
|
||||
//=========================================================================
|
||||
// Verilated function size macros
|
||||
|
||||
@@ -441,11 +466,14 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
#define VL_SIZEBITS_E (VL_EDATASIZE - 1) ///< Bit mask for bits in a quad
|
||||
|
||||
/// Return mask for words with 1's where relevant bits are (0=all bits)
|
||||
/// Arguments must not have side effects
|
||||
#define VL_MASK_I(nbits) (((nbits) & VL_SIZEBITS_I) ? ((1U << ((nbits) & VL_SIZEBITS_I)) - 1) : ~0)
|
||||
/// Return mask for quads with 1's where relevant bits are (0=all bits)
|
||||
/// Arguments must not have side effects
|
||||
#define VL_MASK_Q(nbits) \
|
||||
(((nbits) & VL_SIZEBITS_Q) ? ((1ULL << ((nbits) & VL_SIZEBITS_Q)) - 1ULL) : ~0ULL)
|
||||
/// Return mask for EData with 1's where relevant bits are (0=all bits)
|
||||
/// Arguments must not have side effects
|
||||
#define VL_MASK_E(nbits) VL_MASK_I(nbits)
|
||||
|
||||
#define VL_EUL(n) VL_UL(n) // Make constant number EData sized
|
||||
@@ -467,8 +495,12 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
// #defines, to avoid requiring math.h on all compile runs
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define VL_TRUNC(n) (((n) < 0) ? std::ceil((n)) : std::floor((n)))
|
||||
# define VL_ROUND(n) (((n) < 0) ? std::ceil((n)-0.5) : std::floor((n) + 0.5))
|
||||
static inline double VL_TRUNC(double n) {
|
||||
return (n < 0) ? std::ceil(n) : std::floor(n);
|
||||
}
|
||||
static inline double VL_ROUND(double n) {
|
||||
return (n < 0) ? std::ceil(n-0.5) : std::floor(n + 0.5);
|
||||
}
|
||||
#else
|
||||
# define VL_TRUNC(n) std::trunc(n)
|
||||
# define VL_ROUND(n) std::round(n)
|
||||
@@ -562,8 +594,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
|
||||
//=========================================================================
|
||||
// Stringify macros
|
||||
|
||||
#define VL_STRINGIFY(x) VL_STRINGIFY2(x)
|
||||
#define VL_STRINGIFY2(x) #x
|
||||
#define VL_STRINGIFY(...) VL_STRINGIFY2(__VA_ARGS__)
|
||||
#define VL_STRINGIFY2(...) #__VA_ARGS__
|
||||
|
||||
//=========================================================================
|
||||
// Offset of field in type
|
||||
@@ -596,8 +628,12 @@ reverse_wrapper<T> reverse_view(const T& v) {
|
||||
}
|
||||
|
||||
// C++17's std::as_const
|
||||
// `VL_MT_SAFE` annotation only applies to this function.
|
||||
// Object that is returned by this function is not considered
|
||||
// as MT_SAFE and any function call on this object still
|
||||
// needs to be `VL_MT_SAFE`.
|
||||
template <class T>
|
||||
T const& as_const(T& v) {
|
||||
T const& as_const(T& v) VL_MT_SAFE {
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
+403
-93
@@ -10,16 +10,36 @@ import argparse
|
||||
import os
|
||||
import sys
|
||||
import shlex
|
||||
from typing import Callable, Iterable, Optional, Union
|
||||
from typing import Callable, Iterable, Optional, Union, TYPE_CHECKING
|
||||
import dataclasses
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
from enum import Enum
|
||||
import multiprocessing
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
import clang.cindex
|
||||
from clang.cindex import CursorKind, Index, TranslationUnitSaveError, TranslationUnitLoadError
|
||||
from clang.cindex import (
|
||||
Index,
|
||||
TranslationUnitSaveError,
|
||||
TranslationUnitLoadError,
|
||||
CompilationDatabase,
|
||||
)
|
||||
|
||||
if not TYPE_CHECKING:
|
||||
from clang.cindex import CursorKind
|
||||
else:
|
||||
# Workaround for missing support for members defined out-of-class in Pylance:
|
||||
# https://github.com/microsoft/pylance-release/issues/2365#issuecomment-1035803067
|
||||
|
||||
class CursorKindMeta(type):
|
||||
|
||||
def __getattr__(cls, name: str) -> clang.cindex.CursorKind:
|
||||
return getattr(clang.cindex.CursorKind, name)
|
||||
|
||||
class CursorKind(clang.cindex.CursorKind, metaclass=CursorKindMeta):
|
||||
pass
|
||||
|
||||
|
||||
def fully_qualified_name(node):
|
||||
@@ -33,12 +53,34 @@ def fully_qualified_name(node):
|
||||
return [node.displayname] if node.displayname else []
|
||||
|
||||
|
||||
# Returns True, if `class_node` contains node
|
||||
# that matches `member` spelling
|
||||
def check_class_member_exists(class_node, member):
|
||||
for child in class_node.get_children():
|
||||
if member.spelling == child.spelling:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# Returns Base class (if found) of `class_node`
|
||||
# that is of type `base_type`
|
||||
def get_base_class(class_node, base_type):
|
||||
for child in class_node.get_children():
|
||||
if child.kind is CursorKind.CXX_BASE_SPECIFIER:
|
||||
base_class = child.type
|
||||
if base_type.spelling == base_class.spelling:
|
||||
return base_class
|
||||
return None
|
||||
|
||||
|
||||
@dataclass
|
||||
class VlAnnotations:
|
||||
mt_start: bool = False
|
||||
mt_safe: bool = False
|
||||
stable_tree: bool = False
|
||||
mt_safe_postinit: bool = False
|
||||
mt_unsafe: bool = False
|
||||
mt_disabled: bool = False
|
||||
mt_unsafe_one: bool = False
|
||||
pure: bool = False
|
||||
guarded: bool = False
|
||||
@@ -48,14 +90,19 @@ class VlAnnotations:
|
||||
release: bool = False
|
||||
|
||||
def is_mt_safe_context(self):
|
||||
return (not (self.mt_unsafe or self.mt_unsafe_one)
|
||||
and (self.mt_safe or self.mt_start))
|
||||
return self.mt_safe and not (self.mt_unsafe or self.mt_unsafe_one)
|
||||
|
||||
def is_pure_context(self):
|
||||
return self.pure
|
||||
|
||||
def is_stabe_tree_context(self):
|
||||
# stable tree context requires calls to be marked
|
||||
# as MT_SAFE or MT_STABLE
|
||||
# Functions in MT_START needs to be MT_SAFE or MT_STABLE
|
||||
return self.stable_tree or self.mt_start
|
||||
|
||||
def is_mt_unsafe_call(self):
|
||||
return self.mt_unsafe or self.mt_unsafe_one
|
||||
return self.mt_unsafe or self.mt_unsafe_one or self.mt_disabled
|
||||
|
||||
def is_mt_safe_call(self):
|
||||
return (not self.is_mt_unsafe_call()
|
||||
@@ -66,6 +113,9 @@ class VlAnnotations:
|
||||
def is_pure_call(self):
|
||||
return self.pure
|
||||
|
||||
def is_stabe_tree_call(self):
|
||||
return self.stable_tree
|
||||
|
||||
def __or__(self, other: "VlAnnotations"):
|
||||
result = VlAnnotations()
|
||||
for key, value in dataclasses.asdict(self).items():
|
||||
@@ -94,12 +144,16 @@ class VlAnnotations:
|
||||
result.mt_start = True
|
||||
elif node.displayname == "MT_SAFE":
|
||||
result.mt_safe = True
|
||||
elif node.displayname == "MT_STABLE":
|
||||
result.stable_tree = True
|
||||
elif node.displayname == "MT_SAFE_POSTINIT":
|
||||
result.mt_safe_postinit = True
|
||||
elif node.displayname == "MT_UNSAFE":
|
||||
result.mt_unsafe = True
|
||||
elif node.displayname == "MT_UNSAFE_ONE":
|
||||
result.mt_unsafe_one = True
|
||||
elif node.displayname == "MT_DISABLED":
|
||||
result.mt_disabled = True
|
||||
elif node.displayname == "PURE":
|
||||
result.pure = True
|
||||
elif node.displayname in ["ACQUIRE", "ACQUIRE_SHARED"]:
|
||||
@@ -172,6 +226,19 @@ class FunctionInfo:
|
||||
def copy(self, /, **changes):
|
||||
return dataclasses.replace(self, **changes)
|
||||
|
||||
@staticmethod
|
||||
def from_decl_file_line_and_refd_node(file: str, line: int,
|
||||
refd: clang.cindex.Cursor,
|
||||
annotations: VlAnnotations):
|
||||
file = os.path.abspath(file)
|
||||
refd = refd.canonical
|
||||
assert refd is not None
|
||||
name_parts = fully_qualified_name(refd)
|
||||
usr = refd.get_usr()
|
||||
ftype = FunctionType.from_node(refd)
|
||||
|
||||
return FunctionInfo(name_parts, usr, file, line, annotations, ftype)
|
||||
|
||||
@staticmethod
|
||||
def from_node(node: clang.cindex.Cursor,
|
||||
refd: Optional[clang.cindex.Cursor] = None,
|
||||
@@ -196,6 +263,8 @@ class DiagnosticKind(Enum):
|
||||
ANNOTATIONS_DEF_DECL_MISMATCH = enum.auto()
|
||||
NON_PURE_CALL_IN_PURE_CTX = enum.auto()
|
||||
NON_MT_SAFE_CALL_IN_MT_SAFE_CTX = enum.auto()
|
||||
NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX = enum.auto()
|
||||
MISSING_MT_DISABLED_ANNOTATION = enum.auto()
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.value < other.value
|
||||
@@ -233,13 +302,109 @@ class CallAnnotationsValidator:
|
||||
|
||||
self._index = Index.create()
|
||||
|
||||
self._processed_headers: set[str] = set()
|
||||
# Map key represents translation unit initial defines
|
||||
# (from command line and source's lines before any include)
|
||||
self._processed_headers: dict[str, set[str]] = {}
|
||||
self._external_decls: dict[str, set[tuple[str, int]]] = {}
|
||||
|
||||
# Current context
|
||||
self._main_source_file: str = ""
|
||||
self._defines: dict[str, str] = {}
|
||||
self._call_location: Optional[FunctionInfo] = None
|
||||
self._caller: Optional[FunctionInfo] = None
|
||||
self._constructor_context: list[clang.cindex.Cursor] = []
|
||||
self._level: int = 0
|
||||
self._constructor_context: int = 0
|
||||
|
||||
def is_mt_disabled_code_unit(self):
|
||||
return "VL_MT_DISABLED_CODE_UNIT" in self._defines
|
||||
|
||||
def is_constructor_context(self):
|
||||
return len(self._constructor_context) > 0
|
||||
|
||||
# Parses all lines in a form: `#define KEY VALUE` located before any `#include` line.
|
||||
# The parsing is very simple, there is no support for line breaks, etc.
|
||||
@staticmethod
|
||||
def parse_initial_defines(source_file: str) -> dict[str, str]:
|
||||
defs: dict[str, str] = {}
|
||||
with open(source_file, "r", encoding="utf-8") as file:
|
||||
for line in file:
|
||||
line = line.strip()
|
||||
match = re.fullmatch(
|
||||
r"^#\s*(define\s+(\w+)(?:\s+(.*))?|include\s+.*)$", line)
|
||||
if match:
|
||||
if match.group(1).startswith("define"):
|
||||
key = match.group(2)
|
||||
value = match.groups("1")[2]
|
||||
defs[key] = value
|
||||
elif match.group(1).startswith("include"):
|
||||
break
|
||||
return defs
|
||||
|
||||
@staticmethod
|
||||
def filter_out_unsupported_compiler_args(
|
||||
args: list[str]) -> tuple[list[str], dict[str, str]]:
|
||||
filtered_args = []
|
||||
defines = {}
|
||||
args_iter = iter(args)
|
||||
try:
|
||||
while arg := next(args_iter):
|
||||
# Skip positional arguments (input file name).
|
||||
if not arg.startswith("-") and (arg.endswith(".cpp")
|
||||
or arg.endswith(".c")
|
||||
or arg.endswith(".h")):
|
||||
continue
|
||||
|
||||
# Skipped options with separate value argument.
|
||||
if arg in ["-o", "-T", "-MT", "-MQ", "-MF"
|
||||
"-L"]:
|
||||
next(args_iter)
|
||||
continue
|
||||
|
||||
# Skipped options without separate value argument.
|
||||
if arg == "-c" or arg.startswith("-W") or arg.startswith("-L"):
|
||||
continue
|
||||
|
||||
# Preserved options with separate value argument.
|
||||
if arg in [
|
||||
"-x"
|
||||
"-Xclang", "-I", "-isystem", "-iquote", "-include",
|
||||
"-include-pch"
|
||||
]:
|
||||
filtered_args += [arg, next(args_iter)]
|
||||
continue
|
||||
|
||||
kv_str = None
|
||||
d_or_u = None
|
||||
# Preserve define/undefine with separate value argument.
|
||||
if arg in ["-D", "-U"]:
|
||||
filtered_args.append(arg)
|
||||
d_or_u = arg[1]
|
||||
kv_str = next(args_iter)
|
||||
filtered_args.append(kv_str)
|
||||
# Preserve define/undefine without separate value argument.
|
||||
elif arg[0:2] in ["-D", "-U"]:
|
||||
filtered_args.append(arg)
|
||||
kv_str = arg[2:]
|
||||
d_or_u = arg[1]
|
||||
# Preserve everything else.
|
||||
else:
|
||||
filtered_args.append(arg)
|
||||
continue
|
||||
|
||||
# Keep track of defines for class' internal purposes.
|
||||
key_value = kv_str.split("=", 1)
|
||||
key = key_value[0]
|
||||
val = "1" if len(key_value) == 1 else key_value[1]
|
||||
|
||||
if d_or_u == "D":
|
||||
defines[key] = val
|
||||
elif d_or_u == "U" and key in defines:
|
||||
del defines[key]
|
||||
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
return (filtered_args, defines)
|
||||
|
||||
def compile_and_analyze_file(self, source_file: str,
|
||||
compiler_args: list[str],
|
||||
@@ -247,18 +412,32 @@ class CallAnnotationsValidator:
|
||||
filename = os.path.abspath(source_file)
|
||||
initial_cwd = "."
|
||||
|
||||
filtered_args, defines = self.filter_out_unsupported_compiler_args(
|
||||
compiler_args)
|
||||
defines.update(self.parse_initial_defines(source_file))
|
||||
|
||||
if build_dir:
|
||||
initial_cwd = os.getcwd()
|
||||
os.chdir(build_dir)
|
||||
translation_unit = self._index.parse(filename, compiler_args)
|
||||
has_errors = False
|
||||
for diag in translation_unit.diagnostics:
|
||||
if diag.severity > clang.cindex.Diagnostic.Error:
|
||||
has_errors = True
|
||||
if translation_unit and not has_errors:
|
||||
try:
|
||||
translation_unit = self._index.parse(filename, filtered_args)
|
||||
except TranslationUnitLoadError:
|
||||
translation_unit = None
|
||||
errors = []
|
||||
if translation_unit:
|
||||
for diag in translation_unit.diagnostics:
|
||||
if diag.severity >= clang.cindex.Diagnostic.Error:
|
||||
errors.append(str(diag))
|
||||
if translation_unit and len(errors) == 0:
|
||||
self._defines = defines
|
||||
self._main_source_file = filename
|
||||
self.process_translation_unit(translation_unit)
|
||||
self._main_source_file = ""
|
||||
self._defines = {}
|
||||
else:
|
||||
print(f"%Error: parsing failed: {filename}", file=sys.stderr)
|
||||
for error in errors:
|
||||
print(f" {error}", file=sys.stderr)
|
||||
if build_dir:
|
||||
os.chdir(initial_cwd)
|
||||
|
||||
@@ -329,7 +508,7 @@ class CallAnnotationsValidator:
|
||||
return None
|
||||
|
||||
refn = find_object_ref(node)
|
||||
if self._constructor_context and not refn:
|
||||
if self.is_constructor_context() and not refn:
|
||||
# we are in constructor and no object reference means
|
||||
# we are calling local method. It is MT safe
|
||||
# only if this method is also only calling local methods or
|
||||
@@ -341,20 +520,44 @@ class CallAnnotationsValidator:
|
||||
elif refn and refn.kind == CursorKind.MEMBER_REF_EXPR and refn.referenced:
|
||||
refn = refn.referenced
|
||||
refna = VlAnnotations.from_nodes_list(refn.get_children())
|
||||
if refna.guarded or self._constructor_context:
|
||||
if refna.guarded:
|
||||
is_mt_safe = True
|
||||
if self.is_constructor_context() and refn.semantic_parent:
|
||||
# we are in constructor, so calling local members is MT_SAFE,
|
||||
# make sure object that we are calling is local to the constructor
|
||||
constructor_class = self._constructor_context[
|
||||
-1].semantic_parent
|
||||
if refn.semantic_parent.spelling == constructor_class.spelling:
|
||||
if check_class_member_exists(constructor_class, refn):
|
||||
is_mt_safe = True
|
||||
else:
|
||||
# check if this class inherits from some base class
|
||||
base_class = get_base_class(constructor_class,
|
||||
refn.semantic_parent)
|
||||
if base_class:
|
||||
if check_class_member_exists(
|
||||
base_class.get_declaration(), refn):
|
||||
is_mt_safe = True
|
||||
# variable
|
||||
elif refn and refn.kind == CursorKind.DECL_REF_EXPR and refn.referenced:
|
||||
# This is probably a local or an argument.
|
||||
# Calling methods on local pointers or references is MT-safe,
|
||||
# but on argument pointers or references is not.
|
||||
if "*" not in refn.type.spelling and "&" not in refn.type.spelling:
|
||||
is_mt_safe = True
|
||||
# local variable
|
||||
if refn.referenced.kind == CursorKind.VAR_DECL:
|
||||
is_mt_safe = True
|
||||
if refn.get_definition():
|
||||
if refn.referenced.semantic_parent:
|
||||
if refn.referenced.semantic_parent.kind in [
|
||||
CursorKind.FUNCTION_DECL, CursorKind.CXX_METHOD
|
||||
]:
|
||||
# This is a local or an argument.
|
||||
# Calling methods on local pointers or references is MT-safe,
|
||||
# but on argument pointers or references is not.
|
||||
if "*" not in refn.type.spelling and "&" not in refn.type.spelling:
|
||||
is_mt_safe = True
|
||||
# local variable
|
||||
if refn.referenced.kind == CursorKind.VAR_DECL:
|
||||
is_mt_safe = True
|
||||
else:
|
||||
# Global variable in different translation unit, unsafe
|
||||
pass
|
||||
elif refn and refn.kind == CursorKind.CALL_EXPR:
|
||||
if self._constructor_context:
|
||||
if self.is_constructor_context():
|
||||
# call to local function from constructor context
|
||||
# safe if this function also calling local methods or
|
||||
# MT-safe methods
|
||||
@@ -377,6 +580,17 @@ class CallAnnotationsValidator:
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_MT_SAFE_CALL_IN_MT_SAFE_CTX)
|
||||
|
||||
# stable tree context
|
||||
if ctx.is_stabe_tree_context():
|
||||
if annotations.is_mt_unsafe_call() or not (
|
||||
annotations.is_stabe_tree_call()
|
||||
or annotations.is_pure_call()
|
||||
or self.check_mt_safe_call(node, refd, annotations)):
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX)
|
||||
|
||||
# pure context
|
||||
if ctx.is_pure_context():
|
||||
if not annotations.is_pure_call():
|
||||
self.emit_diagnostic(
|
||||
@@ -394,6 +608,17 @@ class CallAnnotationsValidator:
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_MT_SAFE_CALL_IN_MT_SAFE_CTX)
|
||||
|
||||
# stable tree context
|
||||
if ctx.is_stabe_tree_context():
|
||||
if annotations.is_mt_unsafe_call() or not (
|
||||
annotations.is_pure_call()
|
||||
or annotations.is_mt_safe_call()
|
||||
or annotations.is_stabe_tree_call()):
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_node(refd, refd, annotations),
|
||||
DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX)
|
||||
|
||||
# pure context
|
||||
if ctx.is_pure_context():
|
||||
if not annotations.is_pure_call():
|
||||
@@ -408,11 +633,18 @@ class CallAnnotationsValidator:
|
||||
|
||||
# Constructors are OK in MT-safe context
|
||||
# only if they call local methods or MT-safe functions.
|
||||
if ctx.is_mt_safe_context() or self._constructor_context:
|
||||
self._constructor_context += 1
|
||||
if ctx.is_mt_safe_context() or self.is_constructor_context():
|
||||
self._constructor_context.append(refd)
|
||||
self.iterate_children(refd.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
self._constructor_context -= 1
|
||||
self._constructor_context.pop()
|
||||
|
||||
# stable tree context
|
||||
if ctx.is_stabe_tree_context():
|
||||
self._constructor_context.append(refd)
|
||||
self.iterate_children(refd.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
self._constructor_context.pop()
|
||||
|
||||
# pure context
|
||||
if ctx.is_pure_context():
|
||||
@@ -428,11 +660,19 @@ class CallAnnotationsValidator:
|
||||
if not supported:
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return
|
||||
return True
|
||||
|
||||
assert refd is not None
|
||||
if self._is_ignored_call(refd):
|
||||
return
|
||||
return True
|
||||
|
||||
if "std::function" in refd.displayname:
|
||||
# Workaroud for missing support for lambda annotations
|
||||
# in c++11.
|
||||
# If function takes std::function as argument,
|
||||
# assume, that this std::function will be called inside it.
|
||||
self.process_function_definition(node)
|
||||
return False
|
||||
|
||||
assert self._call_location is not None
|
||||
node_file = os.path.abspath(node.location.file.name)
|
||||
@@ -444,47 +684,52 @@ class CallAnnotationsValidator:
|
||||
or refd.kind == CursorKind.CXX_METHOD
|
||||
and refd.is_static_method()):
|
||||
self.process_function_call(refd, annotations)
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return
|
||||
# Function pointer
|
||||
if refd.kind in [
|
||||
elif refd.kind in [
|
||||
CursorKind.VAR_DECL, CursorKind.FIELD_DECL,
|
||||
CursorKind.PARM_DECL
|
||||
]:
|
||||
self.process_function_call(refd, annotations)
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return
|
||||
# Non-static class methods
|
||||
if refd.kind == CursorKind.CXX_METHOD:
|
||||
elif refd.kind == CursorKind.CXX_METHOD:
|
||||
self.process_method_call(node, refd, annotations)
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return
|
||||
# Conversion method (e.g. `operator int()`)
|
||||
if refd.kind == CursorKind.CONVERSION_FUNCTION:
|
||||
elif refd.kind == CursorKind.CONVERSION_FUNCTION:
|
||||
self.process_method_call(node, refd, annotations)
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return
|
||||
# Constructors
|
||||
if refd.kind == CursorKind.CONSTRUCTOR:
|
||||
elif refd.kind == CursorKind.CONSTRUCTOR:
|
||||
self.process_constructor_call(refd, annotations)
|
||||
self.iterate_children(node.get_children(),
|
||||
self.dispatch_node_inside_definition)
|
||||
return
|
||||
else:
|
||||
# Ignore other callables, but report them
|
||||
print("Unknown callable: "
|
||||
f"{refd.location.file.name}:{refd.location.line}: "
|
||||
f"{refd.displayname} {refd.kind}\n"
|
||||
f" from: {node.location.file.name}:{node.location.line}")
|
||||
return True
|
||||
|
||||
# Ignore other callables
|
||||
print(f"{refd.location.file.name}:{refd.location.line}: "
|
||||
f"{refd.displayname} {refd.kind}\n"
|
||||
f" from: {node.location.file.name}:{node.location.line}")
|
||||
def process_function_declaration(self, node: clang.cindex.Cursor):
|
||||
# Ignore declarations in main .cpp file
|
||||
if node.location.file.name != self._main_source_file:
|
||||
children = list(node.get_children())
|
||||
annotations = VlAnnotations.from_nodes_list(children)
|
||||
if not annotations.mt_disabled:
|
||||
self._external_decls.setdefault(node.get_usr(), set()).add(
|
||||
(str(node.location.file.name), int(node.location.line)))
|
||||
return self.iterate_children(children, self.dispatch_node)
|
||||
|
||||
return self.iterate_children(node.get_children(), self.dispatch_node)
|
||||
|
||||
# Definition handling
|
||||
|
||||
def dispatch_node_inside_definition(self, node: clang.cindex.Cursor):
|
||||
if node.kind == CursorKind.CALL_EXPR:
|
||||
self.dispatch_call_node(node)
|
||||
if self.dispatch_call_node(node) is False:
|
||||
return None
|
||||
elif node.is_definition() and node.kind in [
|
||||
CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL,
|
||||
CursorKind.CONSTRUCTOR, CursorKind.CONVERSION_FUNCTION
|
||||
]:
|
||||
self.process_function_definition(node)
|
||||
return None
|
||||
|
||||
return self.iterate_children(node.get_children(),
|
||||
@@ -504,6 +749,18 @@ class CallAnnotationsValidator:
|
||||
assert refd is not None
|
||||
|
||||
def_annotations = VlAnnotations.from_nodes_list(node_children)
|
||||
# Implicitly mark definitions in VL_MT_DISABLED_CODE_UNIT .cpp files as
|
||||
# VL_MT_DISABLED. Existence of the annotation on declarations in .h
|
||||
# files is verified below.
|
||||
# Also sets VL_REQUIRES, as this annotation is added together with
|
||||
# explicit VL_MT_DISABLED.
|
||||
if self.is_mt_disabled_code_unit():
|
||||
if node.location.file.name == self._main_source_file:
|
||||
annotations.mt_disabled = True
|
||||
annotations.requires = True
|
||||
if refd.location.file.name == self._main_source_file:
|
||||
def_annotations.mt_disabled = True
|
||||
def_annotations.requires = True
|
||||
|
||||
if not (def_annotations.is_empty() or def_annotations == annotations):
|
||||
# Use definition's annotations for the diagnostic
|
||||
@@ -516,49 +773,67 @@ class CallAnnotationsValidator:
|
||||
DiagnosticKind.ANNOTATIONS_DEF_DECL_MISMATCH)
|
||||
|
||||
# Use concatenation of definition and declaration annotations
|
||||
# for callees validation.
|
||||
# for calls validation.
|
||||
self._caller = FunctionInfo.from_node(node, refd,
|
||||
def_annotations | annotations)
|
||||
prev_call_location = self._call_location
|
||||
self._call_location = self._caller
|
||||
|
||||
if self.is_mt_disabled_code_unit():
|
||||
# Report declarations of this functions that don't have MT_DISABLED annotation
|
||||
# and are located in headers.
|
||||
if node.location.file.name == self._main_source_file:
|
||||
usr = node.get_usr()
|
||||
declarations = self._external_decls.get(usr, set())
|
||||
for file, line in declarations:
|
||||
self.emit_diagnostic(
|
||||
FunctionInfo.from_decl_file_line_and_refd_node(
|
||||
file, line, refd, def_annotations),
|
||||
DiagnosticKind.MISSING_MT_DISABLED_ANNOTATION)
|
||||
if declarations:
|
||||
del self._external_decls[usr]
|
||||
|
||||
self.iterate_children(node_children,
|
||||
self.dispatch_node_inside_definition)
|
||||
|
||||
self._call_location = None
|
||||
self._call_location = prev_call_location
|
||||
self._caller = prev_call_location
|
||||
|
||||
return None
|
||||
|
||||
# Nodes not located inside definition
|
||||
|
||||
def dispatch_node(self, node: clang.cindex.Cursor):
|
||||
if node.is_definition() and node.kind in [
|
||||
if node.kind in [
|
||||
CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL,
|
||||
CursorKind.CONSTRUCTOR, CursorKind.CONVERSION_FUNCTION
|
||||
]:
|
||||
return self.process_function_definition(node)
|
||||
if node.is_definition() and node.kind in [
|
||||
CursorKind.NAMESPACE, CursorKind.STRUCT_DECL,
|
||||
CursorKind.UNION_DECL, CursorKind.CLASS_DECL
|
||||
]:
|
||||
return self.iterate_children(node.get_children(),
|
||||
self.dispatch_node)
|
||||
if node.is_definition():
|
||||
return self.process_function_definition(node)
|
||||
# else:
|
||||
return self.process_function_declaration(node)
|
||||
|
||||
return self.iterate_children(node.get_children(), self.dispatch_node)
|
||||
|
||||
def process_translation_unit(
|
||||
self, translation_unit: clang.cindex.TranslationUnit):
|
||||
self._level += 1
|
||||
kv_defines = sorted([f"{k}={v}" for k, v in self._defines.items()])
|
||||
concat_defines = '\n'.join(kv_defines)
|
||||
# List of headers already processed in a TU with specified set of defines.
|
||||
tu_processed_headers = self._processed_headers.setdefault(
|
||||
concat_defines, set())
|
||||
for child in translation_unit.cursor.get_children():
|
||||
if self._is_ignored_top_level(child):
|
||||
continue
|
||||
if self._processed_headers:
|
||||
if tu_processed_headers:
|
||||
filename = os.path.abspath(child.location.file.name)
|
||||
if filename in self._processed_headers:
|
||||
if filename in tu_processed_headers:
|
||||
continue
|
||||
self.dispatch_node(child)
|
||||
self._level -= 1
|
||||
|
||||
self._processed_headers.update([
|
||||
tu_processed_headers.update([
|
||||
os.path.abspath(str(hdr.source))
|
||||
for hdr in translation_unit.get_includes()
|
||||
])
|
||||
@@ -620,34 +895,40 @@ def get_filter_funcs(verilator_root: str):
|
||||
|
||||
|
||||
def precompile_header(compile_command: CompileCommand, tmp_dir: str) -> str:
|
||||
initial_cwd = os.getcwd()
|
||||
errors = []
|
||||
try:
|
||||
initial_cwd = os.getcwd()
|
||||
os.chdir(compile_command.directory)
|
||||
|
||||
index = Index.create()
|
||||
translation_unit = index.parse(compile_command.filename,
|
||||
compile_command.args)
|
||||
for diag in translation_unit.diagnostics:
|
||||
if diag.severity > clang.cindex.Diagnostic.Error:
|
||||
pch_file = None
|
||||
break
|
||||
else:
|
||||
if diag.severity >= clang.cindex.Diagnostic.Error:
|
||||
errors.append(str(diag))
|
||||
|
||||
if len(errors) == 0:
|
||||
pch_file = os.path.join(
|
||||
tmp_dir,
|
||||
f"{compile_command.refid:02}_{os.path.basename(compile_command.filename)}.pch"
|
||||
)
|
||||
translation_unit.save(pch_file)
|
||||
|
||||
if pch_file:
|
||||
return pch_file
|
||||
|
||||
except (TranslationUnitSaveError, TranslationUnitLoadError,
|
||||
OSError) as exception:
|
||||
print(f"%Warning: {exception}", file=sys.stderr)
|
||||
|
||||
finally:
|
||||
os.chdir(initial_cwd)
|
||||
|
||||
if pch_file:
|
||||
return pch_file
|
||||
|
||||
except (TranslationUnitSaveError, TranslationUnitLoadError, OSError):
|
||||
pass
|
||||
|
||||
print(
|
||||
f"%Warning: Precompiling failed, skipping: {compile_command.filename}")
|
||||
f"%Warning: Precompilation failed, skipping: {compile_command.filename}",
|
||||
file=sys.stderr)
|
||||
for error in errors:
|
||||
print(f" {error}", file=sys.stderr)
|
||||
return ""
|
||||
|
||||
|
||||
@@ -670,7 +951,7 @@ def run_analysis(ccl: Iterable[CompileCommand], pccl: Iterable[CompileCommand],
|
||||
is_ignored_def, is_ignored_call)
|
||||
for compile_command in ccl:
|
||||
cav.compile_and_analyze_file(compile_command.filename,
|
||||
compile_command.args + extra_args,
|
||||
extra_args + compile_command.args,
|
||||
compile_command.directory)
|
||||
|
||||
|
||||
@@ -788,6 +1069,12 @@ class TopDownSummaryPrinter():
|
||||
name += "is mtsafe but calls non-mtsafe function(s)"
|
||||
elif func.reason == DiagnosticKind.NON_PURE_CALL_IN_PURE_CTX:
|
||||
name += "is pure but calls non-pure function(s)"
|
||||
elif func.reason == DiagnosticKind.NON_STABLE_TREE_CALL_IN_STABLE_TREE_CTX:
|
||||
name += "is stable_tree but calls non-stable_tree or non-mtsafe"
|
||||
elif func.reason == DiagnosticKind.MISSING_MT_DISABLED_ANNOTATION:
|
||||
name += ("defined in a file marked as " +
|
||||
"VL_MT_DISABLED_CODE_UNIT has declaration(s) " +
|
||||
"without VL_MT_DISABLED annotation")
|
||||
else:
|
||||
name += "for unknown reason (please add description)"
|
||||
|
||||
@@ -854,10 +1141,15 @@ def main():
|
||||
type=int,
|
||||
default=0,
|
||||
help="Number of parallel jobs to use.")
|
||||
parser.add_argument(
|
||||
"--compile-commands-dir",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Path to directory containing compile_commands.json.")
|
||||
parser.add_argument("--cxxflags",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Flags passed to clang++.")
|
||||
help="Extra flags passed to clang++.")
|
||||
parser.add_argument(
|
||||
"--compilation-root",
|
||||
type=str,
|
||||
@@ -882,34 +1174,52 @@ def main():
|
||||
cmdline.compilation_root = cmdline.verilator_root
|
||||
|
||||
verilator_root = os.path.abspath(cmdline.verilator_root)
|
||||
compilation_root = os.path.abspath(cmdline.compilation_root)
|
||||
default_compilation_root = os.path.abspath(cmdline.compilation_root)
|
||||
|
||||
compdb: Optional[CompilationDatabase] = None
|
||||
if cmdline.compile_commands_dir:
|
||||
compdb = CompilationDatabase.fromDirectory(
|
||||
cmdline.compile_commands_dir)
|
||||
|
||||
default_cxx_flags = [
|
||||
f"-I{verilator_root}/src",
|
||||
f"-I{verilator_root}/include",
|
||||
f"-I{verilator_root}/src/obj_opt",
|
||||
"-fcoroutines-ts",
|
||||
]
|
||||
if cmdline.cxxflags is not None:
|
||||
cxxflags = shlex.split(cmdline.cxxflags)
|
||||
common_cxxflags = shlex.split(cmdline.cxxflags)
|
||||
else:
|
||||
cxxflags = default_cxx_flags
|
||||
common_cxxflags = []
|
||||
|
||||
precompile_commands_list = []
|
||||
|
||||
if cmdline.precompile:
|
||||
hdr_cxxflags = ['-xc++-header'] + cxxflags
|
||||
hdr_cxxflags = ['-xc++-header'] + common_cxxflags
|
||||
for refid, file in enumerate(cmdline.precompile):
|
||||
filename = os.path.abspath(file)
|
||||
compile_command = CompileCommand(refid, filename, hdr_cxxflags,
|
||||
compilation_root)
|
||||
default_compilation_root)
|
||||
precompile_commands_list.append(compile_command)
|
||||
|
||||
compile_commands_list = []
|
||||
for refid, file in enumerate(cmdline.file):
|
||||
filename = os.path.abspath(file)
|
||||
compile_command = CompileCommand(refid, filename, cxxflags,
|
||||
compilation_root)
|
||||
root = default_compilation_root
|
||||
cxxflags = []
|
||||
if compdb:
|
||||
entry = compdb.getCompileCommands(filename)
|
||||
entry_list = list(entry)
|
||||
# Compilation database can contain multiple entries for single file,
|
||||
# e.g. when it has been updated by appending new entries.
|
||||
# Use last entry for the file, if it exists, as it is the newest one.
|
||||
if len(entry_list) > 0:
|
||||
last_entry = entry_list[-1]
|
||||
root = last_entry.directory
|
||||
entry_args = list(last_entry.arguments)
|
||||
# First argument in compile_commands.json arguments list is
|
||||
# compiler executable name/path. CIndex (libclang) always
|
||||
# implicitly prepends executable name, so it shouldn't be passed
|
||||
# here.
|
||||
cxxflags = common_cxxflags + entry_args[1:]
|
||||
else:
|
||||
cxxflags = common_cxxflags[:]
|
||||
|
||||
compile_command = CompileCommand(refid, filename, cxxflags, root)
|
||||
compile_commands_list.append(compile_command)
|
||||
|
||||
summary_printer = TopDownSummaryPrinter()
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
*~
|
||||
*.old
|
||||
config_build.h
|
||||
config_package.h
|
||||
Makefile
|
||||
Makefile_obj
|
||||
.objcache*
|
||||
|
||||
+7
-2
@@ -82,6 +82,7 @@ set(HEADERS
|
||||
V3File.h
|
||||
V3FileLine.h
|
||||
V3Force.h
|
||||
V3Fork.h
|
||||
V3FunctionTraits.h
|
||||
V3Gate.h
|
||||
V3Global.h
|
||||
@@ -150,6 +151,7 @@ set(HEADERS
|
||||
V3Table.h
|
||||
V3Task.h
|
||||
V3ThreadPool.h
|
||||
V3ThreadSafety.h
|
||||
V3Timing.h
|
||||
V3Trace.h
|
||||
V3TraceDecl.h
|
||||
@@ -227,6 +229,7 @@ set(COMMON_SOURCES
|
||||
V3File.cpp
|
||||
V3FileLine.cpp
|
||||
V3Force.cpp
|
||||
V3Fork.cpp
|
||||
V3Gate.cpp
|
||||
V3Global.cpp
|
||||
V3Graph.cpp
|
||||
@@ -322,7 +325,7 @@ file(TO_NATIVE_PATH ${srcdir}/../bin/verilator_includer VERILATOR_INCLUDER)
|
||||
file(TO_NATIVE_PATH ${srcdir}/vlcovgen VLCOVGEN)
|
||||
file(TO_NATIVE_PATH ${srcdir}/config_rev CONFIG_REV)
|
||||
|
||||
configure_file(config_build.h.in config_build.h @ONLY)
|
||||
configure_file(config_package.h.in config_package.h @ONLY)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT V3Ast__gen_forward_class_decls.h V3Dfg__gen_forward_class_decls.h
|
||||
@@ -414,7 +417,7 @@ foreach(astgen_name ${ASTGENERATED_NAMES})
|
||||
MAIN_DEPENDENCY ${astgen_name}.cpp
|
||||
DEPENDS ${ASTGEN} V3Ast.h
|
||||
COMMAND ${PYTHON3} ARGS
|
||||
${ASTGEN} -I"${srcdir}" --astdef V3AstNodeDType.h --astdef V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h ${astgen_name}.cpp
|
||||
${ASTGEN} -I "${srcdir}" --astdef V3AstNodeDType.h --astdef V3AstNodeExpr.h --astdef V3AstNodeOther.h --dfgdef V3DfgVertices.h ${astgen_name}.cpp
|
||||
)
|
||||
list(APPEND GENERATED_FILES ${astgen_name}__gen.cpp)
|
||||
endforeach()
|
||||
@@ -488,11 +491,13 @@ target_include_directories(${verilator}
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_compile_options(${verilator} PRIVATE /bigobj)
|
||||
target_compile_definitions(${verilator} PRIVATE
|
||||
YY_NO_UNISTD_H
|
||||
)
|
||||
target_include_directories(${verilator} PRIVATE ../platform/win32)
|
||||
target_link_libraries(${verilator} PRIVATE bcrypt psapi)
|
||||
target_link_options(${verilator} PRIVATE /STACK:10000000)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${verilator})
|
||||
|
||||
+24
-5
@@ -18,14 +18,13 @@
|
||||
#### Start of system configuration section. ####
|
||||
|
||||
srcdir = @srcdir@
|
||||
PYTHON3 = @PYTHON3@
|
||||
EXEEXT = @EXEEXT@
|
||||
PYTHON3 = @PYTHON3@
|
||||
# VPATH only does sources; fix install_test's not creating ../bin
|
||||
vpath %.h @srcdir@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
|
||||
default: dbg opt
|
||||
debug: dbg
|
||||
optimize: opt
|
||||
@@ -36,6 +35,26 @@ endif
|
||||
|
||||
UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
|
||||
|
||||
ifeq (,$(wildcard obj_dbg/bear.o))
|
||||
ifneq (, $(shell which bear 2>/dev/null))
|
||||
BEAR := $(shell which bear)
|
||||
ifeq (, $(shell $(BEAR) --output obj_dbg/comptest.json -- true))
|
||||
$(shell which bear 2>/dev/null >obj_dbg/bear.o)
|
||||
else
|
||||
# unsupported version
|
||||
BEAR :=
|
||||
endif
|
||||
endif
|
||||
else
|
||||
BEAR := $(shell cat obj_dbg/bear.o)
|
||||
endif
|
||||
|
||||
ifneq ($(BEAR),)
|
||||
BEAR_OBJ_OPT := $(BEAR) --append --output obj_dbg/compile_commands.json --
|
||||
else
|
||||
BEAR_OBJ_OPT :=
|
||||
endif
|
||||
|
||||
#*********************************************************************
|
||||
|
||||
obj_opt:
|
||||
@@ -62,8 +81,8 @@ endif
|
||||
|
||||
dbg: ../bin/verilator_bin_dbg$(EXEEXT) ../bin/verilator_coverage_bin_dbg$(EXEEXT)
|
||||
../bin/verilator_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
$(BEAR_OBJ_OPT) $(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
$(BEAR_OBJ_OPT) $(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
|
||||
../bin/verilator_coverage_bin_dbg$(EXEEXT): obj_dbg ../bin prefiles
|
||||
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
|
||||
@@ -95,7 +114,7 @@ clean mostlyclean distclean maintainer-clean::
|
||||
-rm -f .objcache*
|
||||
|
||||
distclean maintainer-clean::
|
||||
-rm -f Makefile Makefile_obj config_build.h
|
||||
-rm -f Makefile Makefile_obj config_package.h
|
||||
|
||||
maintainer-clean::
|
||||
-rm -f config_rev.h
|
||||
|
||||
@@ -92,6 +92,7 @@ endif
|
||||
# -lfl not needed as Flex invoked with %nowrap option
|
||||
LIBS = $(CFG_LIBS) -lm
|
||||
|
||||
CPPFLAGS += -DVERILATOR_INTERNAL_
|
||||
CPPFLAGS += -MMD
|
||||
CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
|
||||
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
|
||||
@@ -204,6 +205,7 @@ RAW_OBJS = \
|
||||
V3File.o \
|
||||
V3FileLine.o \
|
||||
V3Force.o \
|
||||
V3Fork.o \
|
||||
V3Gate.o \
|
||||
V3Global.o \
|
||||
V3Graph.o \
|
||||
@@ -287,6 +289,7 @@ NON_STANDALONE_HEADERS = \
|
||||
V3AstNodeExpr.h \
|
||||
V3AstNodeOther.h \
|
||||
V3DfgVertices.h \
|
||||
V3ThreadPool.h \
|
||||
V3WidthCommit.h \
|
||||
|
||||
AST_DEFS := \
|
||||
|
||||
+14
-10
@@ -47,6 +47,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
// Extend V3GraphVertex class for use in latch detection graph
|
||||
|
||||
class LatchDetectGraphVertex final : public V3GraphVertex {
|
||||
VL_RTTI_IMPL(LatchDetectGraphVertex, V3GraphVertex)
|
||||
public:
|
||||
enum VertexType : uint8_t { VT_BLOCK, VT_BRANCH, VT_OUTPUT };
|
||||
|
||||
@@ -54,7 +55,7 @@ private:
|
||||
const string m_name; // Only used for .dot file generation
|
||||
const VertexType m_type; // Vertex type (BLOCK/BRANCH/OUTPUT)
|
||||
|
||||
string typestr() const { // "
|
||||
string typestr() const VL_MT_SAFE { // "
|
||||
switch (m_type) {
|
||||
case VT_BLOCK: return "(||)"; // basic block node
|
||||
case VT_BRANCH: return "(&&)"; // if/else branch mode
|
||||
@@ -68,7 +69,7 @@ public:
|
||||
: V3GraphVertex{graphp}
|
||||
, m_name{name}
|
||||
, m_type{type} {}
|
||||
string name() const override { return m_name + " " + typestr(); }
|
||||
string name() const override VL_MT_STABLE { return m_name + " " + typestr(); }
|
||||
string dotColor() const override { return user() ? "green" : "black"; }
|
||||
virtual int type() const { return m_type; }
|
||||
};
|
||||
@@ -183,7 +184,7 @@ public:
|
||||
<< " (not all control paths of combinational always assign a value)\n"
|
||||
<< nodep->warnMore()
|
||||
<< "... Suggest use of always_latch for intentional latches");
|
||||
if (dumpGraph() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
|
||||
if (dumpGraphLevel() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
|
||||
}
|
||||
vertp->user(false); // Clear again (see above)
|
||||
vrp->varp()->isLatched(latch_detected);
|
||||
@@ -309,7 +310,7 @@ AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Combo>() {
|
||||
//######################################################################
|
||||
// Latch checking visitor
|
||||
|
||||
class ActiveLatchCheckVisitor final : public VNVisitor {
|
||||
class ActiveLatchCheckVisitor final : public VNVisitorConst {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Input:
|
||||
@@ -320,7 +321,8 @@ private:
|
||||
// VISITORS
|
||||
void visit(AstVarRef* nodep) override {
|
||||
const AstVar* const varp = nodep->varp();
|
||||
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
|
||||
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()
|
||||
&& !varp->isFuncLocalSticky()) {
|
||||
m_graph.addAssignment(nodep);
|
||||
}
|
||||
}
|
||||
@@ -329,20 +331,22 @@ private:
|
||||
LatchDetectGraphVertex* const parentp = m_graph.currentp();
|
||||
LatchDetectGraphVertex* const branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
|
||||
m_graph.addPathVertex(branchp, "IF");
|
||||
iterateAndNextNull(nodep->thensp());
|
||||
iterateAndNextConstNull(nodep->thensp());
|
||||
m_graph.addPathVertex(branchp, "ELSE");
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
iterateAndNextConstNull(nodep->elsesp());
|
||||
m_graph.currentp(parentp);
|
||||
} else {
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
ActiveLatchCheckVisitor(AstNode* nodep, bool expectLatch) {
|
||||
m_graph.begin();
|
||||
iterate(nodep);
|
||||
iterateConst(nodep);
|
||||
m_graph.latchCheck(nodep, expectLatch);
|
||||
}
|
||||
~ActiveLatchCheckVisitor() override = default;
|
||||
@@ -652,5 +656,5 @@ public:
|
||||
void V3Active::activeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("active", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("active", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+1
-1
@@ -145,5 +145,5 @@ public:
|
||||
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveTopVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("activetop", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("activetop", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+21
-11
@@ -20,6 +20,7 @@
|
||||
#include "V3Assert.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
@@ -51,14 +52,22 @@ private:
|
||||
bool m_inSampled = false; // True inside a sampled expression
|
||||
|
||||
// METHODS
|
||||
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message) {
|
||||
return (string("[%0t] " + prefix + ": ") + nodep->fileline()->filebasename() + ":"
|
||||
+ cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m"
|
||||
+ ((message != "") ? ": " : "") + message + "\n");
|
||||
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message,
|
||||
VDisplayType severity) {
|
||||
if (severity == VDisplayType::DT_ERROR || severity == VDisplayType::DT_FATAL) {
|
||||
return (string{"[%0t] " + prefix + ": "} + nodep->fileline()->filebasename() + ":"
|
||||
+ cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m"
|
||||
+ ((message != "") ? ": " : "") + message + "\n");
|
||||
} else {
|
||||
return (string{"[%0t] " + prefix + ": "} + nodep->fileline()->filebasename() + ":"
|
||||
+ cvtToStr(nodep->fileline()->lineno()) + ": %m"
|
||||
+ ((message != "") ? ": " : "") + message + "\n");
|
||||
}
|
||||
}
|
||||
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
|
||||
nodep->fmtp()->text(
|
||||
assertDisplayMessage(nodep, prefix, nodep->fmtp()->text(), nodep->displayType()));
|
||||
nodep->displayType(VDisplayType::DT_WRITE);
|
||||
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
|
||||
// cppcheck-suppress nullPointer
|
||||
AstNodeExpr* const timenewp = new AstTime{nodep->fileline(), m_modp->timeunit()};
|
||||
if (AstNodeExpr* const timesp = nodep->fmtp()->exprsp()) {
|
||||
@@ -148,7 +157,7 @@ private:
|
||||
sentreep->unlinkFrBack();
|
||||
if (m_procedurep) {
|
||||
// To support this need queue of asserts to activate
|
||||
nodep->v3error("Unsupported: Procedural concurent assertion with"
|
||||
nodep->v3error("Unsupported: Procedural concurrent assertion with"
|
||||
" clocking event inside always (IEEE 1800-2917 16.14.6)");
|
||||
}
|
||||
}
|
||||
@@ -182,7 +191,7 @@ private:
|
||||
const bool force = VN_IS(nodep, AssertIntrinsic);
|
||||
if (passsp) passsp = newIfAssertOn(passsp, force);
|
||||
if (failsp) failsp = newIfAssertOn(failsp, force);
|
||||
if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
|
||||
if (!passsp && !failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
|
||||
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
|
||||
@@ -247,7 +256,7 @@ private:
|
||||
ifp = nextifp;
|
||||
} while (ifp);
|
||||
|
||||
AstNode* const newifp = nodep->cloneTree(false);
|
||||
AstIf* const newifp = nodep->cloneTree(false);
|
||||
const bool allow_none = nodep->unique0Pragma();
|
||||
|
||||
// Empty case means no property
|
||||
@@ -419,9 +428,10 @@ private:
|
||||
replaceDisplay(nodep, "-Info");
|
||||
} else if (nodep->displayType() == VDisplayType::DT_WARNING) {
|
||||
replaceDisplay(nodep, "%%Warning");
|
||||
} else if (nodep->displayType() == VDisplayType::DT_ERROR
|
||||
|| nodep->displayType() == VDisplayType::DT_FATAL) {
|
||||
} else if (nodep->displayType() == VDisplayType::DT_ERROR) {
|
||||
replaceDisplay(nodep, "%%Error");
|
||||
} else if (nodep->displayType() == VDisplayType::DT_FATAL) {
|
||||
replaceDisplay(nodep, "%%Fatal");
|
||||
} else if (nodep->displayType() == VDisplayType::DT_MONITOR) {
|
||||
nodep->displayType(VDisplayType::DT_DISPLAY);
|
||||
const auto fl = nodep->fileline();
|
||||
@@ -535,5 +545,5 @@ public:
|
||||
void V3Assert::assertAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assert", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("assert", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
||||
+3
-3
@@ -229,7 +229,7 @@ private:
|
||||
// always queue.push(<sampled var>);
|
||||
AstCMethodHard* const pushp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, queueVarp, VAccess::WRITE}, "push",
|
||||
new AstTime(nodep->fileline(), m_modp->timeunit())};
|
||||
new AstTime{nodep->fileline(), m_modp->timeunit()}};
|
||||
pushp->addPinsp(exprp->cloneTree(false));
|
||||
pushp->dtypeSetVoid();
|
||||
m_clockingp->addNextHere(
|
||||
@@ -238,7 +238,7 @@ private:
|
||||
// always @<clocking event> queue.pop(<skew>, /*out*/<skewed var>});
|
||||
AstCMethodHard* const popp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, "pop",
|
||||
new AstTime(nodep->fileline(), m_modp->timeunit())};
|
||||
new AstTime{nodep->fileline(), m_modp->timeunit()}};
|
||||
popp->addPinsp(skewp->unlinkFrBack());
|
||||
popp->addPinsp(refp);
|
||||
popp->dtypeSetVoid();
|
||||
@@ -496,5 +496,5 @@ public:
|
||||
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertPreVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+131
-47
@@ -55,12 +55,28 @@ bool VNUser5InUse::s_userBusy = false;
|
||||
int AstNodeDType::s_uniqueNum = 0;
|
||||
|
||||
//######################################################################
|
||||
// V3AstType
|
||||
// VNType
|
||||
|
||||
const VNTypeInfo VNType::typeInfoTable[] = {
|
||||
#include "V3Ast__gen_type_info.h" // From ./astgen
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, VNType rhs);
|
||||
|
||||
//######################################################################
|
||||
// Creators
|
||||
// VSelfPointerText
|
||||
|
||||
const std::shared_ptr<const string> VSelfPointerText::s_emptyp = std::make_shared<string>("");
|
||||
const std::shared_ptr<const string> VSelfPointerText::s_thisp = std::make_shared<string>("this");
|
||||
|
||||
string VSelfPointerText::protect(bool useSelfForThis, bool protect) const {
|
||||
const string& sp
|
||||
= useSelfForThis ? VString::replaceWord(asString(), "this", "vlSelf") : asString();
|
||||
return VIdProtect::protectWordsIf(sp, protect);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// AstNode
|
||||
|
||||
AstNode::AstNode(VNType t, FileLine* fl)
|
||||
: m_type{t}
|
||||
@@ -155,7 +171,7 @@ string AstNode::vcdName(const string& namein) {
|
||||
return prettyName(pretty);
|
||||
}
|
||||
|
||||
string AstNode::prettyName(const string& namein) {
|
||||
string AstNode::prettyName(const string& namein) VL_PURE {
|
||||
// This function is somewhat hot, so we short-circuit some compares
|
||||
string pretty;
|
||||
pretty.reserve(namein.length());
|
||||
@@ -296,17 +312,19 @@ void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int line
|
||||
// Called on all major tree changers.
|
||||
// Only for use for those really nasty bugs relating to internals
|
||||
// Note this may be null.
|
||||
// if (debug()) cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "
|
||||
// <<prefix<<": "<<cvtToHex(this)<<" <e"<<AstNode::s_editCntGbl<<">"<<endl;
|
||||
// if (debug() && nodep) cout << "-treeChange: V3Ast.cpp:" << lineno
|
||||
// << " Tree Change for " << prefix << ": "
|
||||
// << cvtToHex(nodep) << " <e" << AstNode::s_editCntGbl << ">"
|
||||
// << "m_iterpp=" << (void*)nodep->m_iterpp << endl;
|
||||
// if (debug()) {
|
||||
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
|
||||
// // Commenting out the section below may crash, as the tree state
|
||||
// // between edits is not always consistent for printing
|
||||
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
|
||||
// v3Global.rootp()->dumpTree("- treeChange: ");
|
||||
// if (next||1) this->dumpTreeAndNext(cout, prefix);
|
||||
// else this->dumpTree(prefix);
|
||||
// this->checkTree();
|
||||
// if (next||1) nodep->dumpTreeAndNext(cout, prefix);
|
||||
// else nodep->dumpTree(prefix);
|
||||
// nodep->checkTree();
|
||||
// v3Global.rootp()->checkTree();
|
||||
//}
|
||||
#endif
|
||||
@@ -345,7 +363,8 @@ AstNode* AstNode::addNext<AstNode, AstNode>(AstNode* nodep, AstNode* newp) {
|
||||
newtailp->m_headtailp = headp;
|
||||
headp->m_headtailp = newtailp;
|
||||
newp->editCountInc();
|
||||
if (oldtailp->m_iterpp) *(oldtailp->m_iterpp) = newp; // Iterate on new item
|
||||
// No change of m_iterpp, as only changing m_nextp of current node;
|
||||
// the current node is still the one at the iteration point
|
||||
}
|
||||
debugTreeChange(nodep, "-addNextOut:", __LINE__, true);
|
||||
return nodep;
|
||||
@@ -395,7 +414,8 @@ void AstNode::addNextHere(AstNode* newp) {
|
||||
} // else is head, and we're inserting into the middle, so no other change
|
||||
}
|
||||
|
||||
if (this->m_iterpp) *(this->m_iterpp) = newp; // Iterate on new item
|
||||
// No change of m_iterpp, as adding after current node;
|
||||
// the current node is still the one at the iteration point
|
||||
debugTreeChange(this, "-addHereOut: ", __LINE__, true);
|
||||
}
|
||||
|
||||
@@ -563,8 +583,10 @@ AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) {
|
||||
// Relink
|
||||
oldp->m_backp = nullptr;
|
||||
// Iterator fixup
|
||||
if (oldp->m_iterpp) *(oldp->m_iterpp) = nullptr;
|
||||
oldp->m_iterpp = nullptr;
|
||||
if (oldp->m_iterpp) {
|
||||
*(oldp->m_iterpp) = nullptr;
|
||||
oldp->m_iterpp = nullptr;
|
||||
}
|
||||
debugTreeChange(oldp, "-unlinkWNextOut: ", __LINE__, true);
|
||||
return oldp;
|
||||
}
|
||||
@@ -578,7 +600,11 @@ AstNode* AstNode::unlinkFrBack(VNRelinker* linkerp) {
|
||||
if (linkerp) {
|
||||
linkerp->m_oldp = oldp;
|
||||
linkerp->m_backp = backp;
|
||||
linkerp->m_iterpp = oldp->m_iterpp;
|
||||
if (oldp->m_iterpp) { // Assumes we will always relink() if want to keep iterating
|
||||
linkerp->m_iterpp = oldp->m_iterpp;
|
||||
*(oldp->m_iterpp) = nullptr;
|
||||
oldp->m_iterpp = nullptr;
|
||||
}
|
||||
if (backp->m_nextp == oldp) {
|
||||
linkerp->m_chg = VNRelinker::RELINK_NEXT;
|
||||
} else if (backp->m_op1p == oldp) {
|
||||
@@ -623,12 +649,15 @@ AstNode* AstNode::unlinkFrBack(VNRelinker* linkerp) {
|
||||
}
|
||||
}
|
||||
// Iterator fixup
|
||||
if (oldp->m_iterpp) *(oldp->m_iterpp) = oldp->m_nextp;
|
||||
if (oldp->m_iterpp) { // Only if no linker, point to next in list
|
||||
if (oldp->m_nextp) oldp->m_nextp->m_iterpp = oldp->m_iterpp;
|
||||
*(oldp->m_iterpp) = oldp->m_nextp;
|
||||
oldp->m_iterpp = nullptr;
|
||||
}
|
||||
// Relink
|
||||
oldp->m_nextp = nullptr;
|
||||
oldp->m_backp = nullptr;
|
||||
oldp->m_headtailp = this;
|
||||
oldp->m_iterpp = nullptr;
|
||||
oldp->m_headtailp = oldp;
|
||||
debugTreeChange(oldp, "-unlinkFrBkOut: ", __LINE__, true);
|
||||
return oldp;
|
||||
}
|
||||
@@ -666,12 +695,12 @@ void AstNode::relink(VNRelinker* linkerp) {
|
||||
// Iterator fixup
|
||||
if (linkerp->m_iterpp) {
|
||||
// If we're iterating over a next() link, we need to follow links off the
|
||||
// NEW node. Thus we pass iteration information via a pointer in the node.
|
||||
// NEW node, which is always assumed to be what we are relinking to.
|
||||
// This adds a unfortunate hot 8 bytes to every AstNode, but is faster than passing
|
||||
// across every function.
|
||||
// If anyone has a cleaner way, I'd be grateful.
|
||||
*(linkerp->m_iterpp) = newp;
|
||||
newp->m_iterpp = linkerp->m_iterpp;
|
||||
*(newp->m_iterpp) = newp;
|
||||
}
|
||||
// Empty the linker so not used twice accidentally
|
||||
linkerp->m_backp = nullptr;
|
||||
@@ -833,6 +862,7 @@ void AstNode::deleteNode() {
|
||||
this->m_op2p = reinterpret_cast<AstNode*>(0x1);
|
||||
this->m_op3p = reinterpret_cast<AstNode*>(0x1);
|
||||
this->m_op4p = reinterpret_cast<AstNode*>(0x1);
|
||||
this->m_iterpp = reinterpret_cast<AstNode**>(0x1);
|
||||
if (
|
||||
#if !defined(VL_DEBUG) || defined(VL_LEAK_CHECKS)
|
||||
true
|
||||
@@ -907,7 +937,7 @@ void AstNode::iterateChildren(VNVisitor& v) {
|
||||
if (m_op4p) m_op4p->iterateAndNext(v);
|
||||
}
|
||||
|
||||
void AstNode::iterateChildrenConst(VNVisitor& v) {
|
||||
void AstNode::iterateChildrenConst(VNVisitorConst& v) {
|
||||
// This is a very hot function
|
||||
ASTNODE_PREFETCH(m_op1p);
|
||||
ASTNODE_PREFETCH(m_op2p);
|
||||
@@ -948,13 +978,13 @@ void AstNode::iterateAndNext(VNVisitor& v) {
|
||||
niterp->m_iterpp = nullptr;
|
||||
if (VL_UNLIKELY(niterp != nodep)) { // Edited node inside accept
|
||||
nodep = niterp;
|
||||
} else { // Unchanged node, just continue loop
|
||||
} else { // Unchanged node (though maybe updated m_next), just continue loop
|
||||
nodep = niterp->m_nextp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::iterateListBackwards(VNVisitor& v) {
|
||||
void AstNode::iterateListBackwardsConst(VNVisitorConst& v) {
|
||||
AstNode* nodep = this;
|
||||
while (nodep->m_nextp) nodep = nodep->m_nextp;
|
||||
while (nodep) {
|
||||
@@ -968,14 +998,14 @@ void AstNode::iterateListBackwards(VNVisitor& v) {
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::iterateChildrenBackwards(VNVisitor& v) {
|
||||
if (m_op1p) m_op1p->iterateListBackwards(v);
|
||||
if (m_op2p) m_op2p->iterateListBackwards(v);
|
||||
if (m_op3p) m_op3p->iterateListBackwards(v);
|
||||
if (m_op4p) m_op4p->iterateListBackwards(v);
|
||||
void AstNode::iterateChildrenBackwardsConst(VNVisitorConst& v) {
|
||||
if (m_op1p) m_op1p->iterateListBackwardsConst(v);
|
||||
if (m_op2p) m_op2p->iterateListBackwardsConst(v);
|
||||
if (m_op3p) m_op3p->iterateListBackwardsConst(v);
|
||||
if (m_op4p) m_op4p->iterateListBackwardsConst(v);
|
||||
}
|
||||
|
||||
void AstNode::iterateAndNextConst(VNVisitor& v) {
|
||||
void AstNode::iterateAndNextConst(VNVisitorConst& v) {
|
||||
// Keep following the current list even if edits change it
|
||||
AstNode* nodep = this;
|
||||
do {
|
||||
@@ -1065,10 +1095,12 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
|
||||
// private: Return true if the two trees are identical
|
||||
if (!node1p && !node2p) return true;
|
||||
if (!node1p || !node2p) return false;
|
||||
if (node1p->type() != node2p->type() || node1p->dtypep() != node2p->dtypep()
|
||||
|| !node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) {
|
||||
return false;
|
||||
}
|
||||
if (node1p->type() != node2p->type()) return false;
|
||||
UASSERT_OBJ(
|
||||
(!node1p->dtypep() && !node2p->dtypep()) || (node1p->dtypep() && node2p->dtypep()), node1p,
|
||||
"Comparison of a node with dtypep() with a node without dtypep()\n-node2=" << node2p);
|
||||
if (node1p->dtypep() && !node1p->dtypep()->similarDType(node2p->dtypep())) return false;
|
||||
if (!node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) return false;
|
||||
return (sameTreeIter(node1p->m_op1p, node2p->m_op1p, false, gateOnly)
|
||||
&& sameTreeIter(node1p->m_op2p, node2p->m_op2p, false, gateOnly)
|
||||
&& sameTreeIter(node1p->m_op3p, node2p->m_op3p, false, gateOnly)
|
||||
@@ -1079,12 +1111,60 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
|
||||
//======================================================================
|
||||
// Debugging
|
||||
|
||||
void AstNode::checkTreeIter(const AstNode* prevBackp) const {
|
||||
void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE {
|
||||
// private: Check a tree and children
|
||||
UASSERT_OBJ(prevBackp == this->backp(), this, "Back node inconsistent");
|
||||
switch (this->type()) {
|
||||
#include "V3Ast__gen_op_checks.h"
|
||||
default: VL_UNREACHABLE; // LCOV_EXCL_LINE
|
||||
const VNTypeInfo& typeInfo = *type().typeInfo();
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
AstNode* nodep = nullptr;
|
||||
switch (i) {
|
||||
case 1: nodep = op1p(); break;
|
||||
case 2: nodep = op2p(); break;
|
||||
case 3: nodep = op3p(); break;
|
||||
case 4: nodep = op4p(); break;
|
||||
default: this->v3fatalSrc("Bad case"); break;
|
||||
}
|
||||
const char* opName = typeInfo.m_opNamep[i - 1];
|
||||
switch (typeInfo.m_opType[i - 1]) {
|
||||
case VNTypeInfo::OP_UNUSED:
|
||||
UASSERT_OBJ(!nodep, this, typeInfo.m_namep << " must not use " << opName << "()");
|
||||
break;
|
||||
case VNTypeInfo::OP_USED:
|
||||
UASSERT_OBJ(nodep, this,
|
||||
typeInfo.m_namep << " must have non nullptr " << opName << "()");
|
||||
UASSERT_OBJ(!nodep->nextp(), this,
|
||||
typeInfo.m_namep << "::" << opName
|
||||
<< "() cannot have a non nullptr nextp()");
|
||||
nodep->checkTreeIter(this);
|
||||
break;
|
||||
case VNTypeInfo::OP_LIST:
|
||||
if (const AstNode* const headp = nodep) {
|
||||
const AstNode* backp = this;
|
||||
const AstNode* tailp = headp;
|
||||
const AstNode* opp = headp;
|
||||
do {
|
||||
opp->checkTreeIter(backp);
|
||||
UASSERT_OBJ(opp == headp || !opp->nextp() || !opp->m_headtailp, opp,
|
||||
"Headtailp should be null in middle of lists");
|
||||
backp = tailp = opp;
|
||||
opp = opp->nextp();
|
||||
} while (opp);
|
||||
UASSERT_OBJ(headp->m_headtailp == tailp, headp,
|
||||
"Tail in headtailp is inconsistent");
|
||||
UASSERT_OBJ(tailp->m_headtailp == headp, tailp,
|
||||
"Head in headtailp is inconsistent");
|
||||
}
|
||||
break;
|
||||
case VNTypeInfo::OP_OPTIONAL:
|
||||
if (nodep) {
|
||||
UASSERT_OBJ(!nodep->nextp(), this,
|
||||
typeInfo.m_namep << "::" << opName
|
||||
<< "() cannot have a non-nullptr nextp()");
|
||||
nodep->checkTreeIter(this);
|
||||
}
|
||||
break;
|
||||
default: this->v3fatalSrc("Bad case"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1129,7 +1209,7 @@ void AstNode::checkIter() const {
|
||||
if (m_iterpp) {
|
||||
dumpPtrs(cout);
|
||||
// Perhaps something forgot to clear m_iterpp?
|
||||
this->v3fatalSrc("Iteration link should be nullptr");
|
||||
this->v3fatalSrc("Iteration link m_iterpp should be nullptr");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1201,7 +1281,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() && ::dumpTree() < 9) {
|
||||
if (editCountGbl() == editCountLast() && ::dumpTreeLevel() < 9) {
|
||||
*logsp << '\n';
|
||||
*logsp << "No changes since last dump!\n";
|
||||
} else {
|
||||
@@ -1211,7 +1291,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() || ::dumpTree())) {
|
||||
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTreeLevel())) {
|
||||
// Error check
|
||||
checkTree();
|
||||
// Broken isn't part of check tree because it can munge iterp's
|
||||
@@ -1273,12 +1353,6 @@ bool AstNode::isTreePureRecurse() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
string AstNode::instanceStr() const {
|
||||
// Max iterations before giving up on location search,
|
||||
// in case we have some circular reference bug.
|
||||
@@ -1303,9 +1377,9 @@ string AstNode::instanceStr() const {
|
||||
|
||||
return "";
|
||||
}
|
||||
void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
void AstNode::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
if (!m_fileline) {
|
||||
V3Error::s().v3errorEnd(str, instanceStr());
|
||||
V3Error::v3errorEnd(str, instanceStr());
|
||||
} else {
|
||||
std::ostringstream nsstr;
|
||||
nsstr << str.str();
|
||||
@@ -1322,6 +1396,11 @@ void AstNode::v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s()
|
||||
nsstr, m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr());
|
||||
}
|
||||
}
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
v3errorEnd(str);
|
||||
assert(0); // LCOV_EXCL_LINE
|
||||
VL_UNREACHABLE;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Data type conversion
|
||||
@@ -1342,7 +1421,9 @@ void AstNode::dtypeChgWidthSigned(int width, int widthMin, VSigning numeric) {
|
||||
dtypeSetLogicUnsized(width, widthMin, numeric);
|
||||
} else {
|
||||
if (width == dtypep()->width() && widthMin == dtypep()->widthMin()
|
||||
&& numeric == dtypep()->numeric())
|
||||
&& numeric == dtypep()->numeric()
|
||||
// Enums need to become direct sizes to avoid later ENUMVALUE errors
|
||||
&& !VN_IS(dtypep()->skipRefToEnump(), EnumDType))
|
||||
return; // Correct already
|
||||
// FUTURE: We may be pointing at a two state data type, and this may
|
||||
// convert it to logic. Since the AstVar remains correct, we
|
||||
@@ -1388,6 +1469,9 @@ AstNodeDType* AstNode::findQueueIndexDType() const {
|
||||
AstNodeDType* AstNode::findVoidDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
|
||||
}
|
||||
AstNodeDType* AstNode::findStreamDType() const {
|
||||
return v3Global.rootp()->typeTablep()->findStreamDType(fileline());
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// VNDeleter
|
||||
|
||||
+280
-167
@@ -82,12 +82,36 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
|
||||
// Ast<nodetypename>*' is returned.
|
||||
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename, decltype(nodep)>(nodep))
|
||||
|
||||
// Same as VN_AS, but only checks the type in debug builds. Type checking is less critical in node
|
||||
// child getters (the strongly-typed functions that wrap op*p pointers). This is because the op*p
|
||||
// pointers are usually populated by code that already asserts the correct type. Having fewer type
|
||||
// assertions yields better performance in release builds.
|
||||
#ifdef VL_DEBUG
|
||||
#define VN_DBG_AS(nodep, nodetypename) VN_AS(nodep, nodetypename)
|
||||
#else
|
||||
#define VN_DBG_AS(nodep, nodetypename) \
|
||||
(AstNode::unsafePrivateAs<Ast##nodetypename, decltype(nodep)>(nodep))
|
||||
#endif
|
||||
|
||||
// (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
|
||||
#define VN_DELETED(nodep) VL_UNLIKELY((uint64_t)(nodep) == 0x1)
|
||||
|
||||
//######################################################################
|
||||
|
||||
struct VNTypeInfo {
|
||||
const char* m_namep;
|
||||
enum uint8_t {
|
||||
OP_UNUSED,
|
||||
OP_USED,
|
||||
OP_LIST,
|
||||
OP_OPTIONAL,
|
||||
} m_opType[4];
|
||||
const char* m_opNamep[4];
|
||||
};
|
||||
|
||||
class VNType final {
|
||||
static const VNTypeInfo typeInfoTable[];
|
||||
|
||||
public:
|
||||
#include "V3Ast__gen_type_enum.h" // From ./astgen
|
||||
// Above include has:
|
||||
@@ -100,13 +124,14 @@ public:
|
||||
constexpr VNType(en _e) VL_MT_SAFE : m_e{_e} {}
|
||||
explicit VNType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
const VNTypeInfo* typeInfo() const VL_MT_SAFE { return &typeInfoTable[m_e]; }
|
||||
constexpr operator en() const VL_MT_SAFE { return m_e; }
|
||||
};
|
||||
constexpr bool operator==(const VNType& lhs, const VNType& rhs) VL_MT_SAFE {
|
||||
constexpr bool operator==(const VNType& lhs, const VNType& rhs) VL_PURE {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
}
|
||||
constexpr bool operator==(const VNType& lhs, VNType::en rhs) { return lhs.m_e == rhs; }
|
||||
constexpr bool operator==(VNType::en lhs, const VNType& rhs) { return lhs == rhs.m_e; }
|
||||
constexpr bool operator==(const VNType& lhs, VNType::en rhs) VL_PURE { return lhs.m_e == rhs; }
|
||||
constexpr bool operator==(VNType::en lhs, const VNType& rhs) VL_PURE { return lhs == rhs.m_e; }
|
||||
inline std::ostream& operator<<(std::ostream& os, const VNType& rhs) { return os << rhs.ascii(); }
|
||||
|
||||
// ######################################################################
|
||||
@@ -461,6 +486,7 @@ public:
|
||||
TRIGGER_SCHEDULER,
|
||||
DYNAMIC_TRIGGER_SCHEDULER,
|
||||
FORK_SYNC,
|
||||
PROCESS_REFERENCE,
|
||||
// Unsigned and two state; fundamental types
|
||||
UINT32,
|
||||
UINT64,
|
||||
@@ -470,7 +496,7 @@ public:
|
||||
_ENUM_MAX
|
||||
};
|
||||
enum en m_e;
|
||||
const char* ascii() const {
|
||||
const char* ascii() const VL_MT_SAFE {
|
||||
static const char* const names[] = {"%E-unk",
|
||||
"bit",
|
||||
"byte",
|
||||
@@ -493,6 +519,7 @@ public:
|
||||
"VlTriggerScheduler",
|
||||
"VlDynamicTriggerScheduler",
|
||||
"VlFork",
|
||||
"VlProcessRef",
|
||||
"IData",
|
||||
"QData",
|
||||
"LOGIC_IMPLICIT",
|
||||
@@ -500,13 +527,20 @@ public:
|
||||
return names[m_e];
|
||||
}
|
||||
const char* dpiType() const {
|
||||
static const char* const names[]
|
||||
= {"%E-unk", "svBit", "char", "void*", "char",
|
||||
"int", "%E-integer", "svLogic", "long long", "double",
|
||||
"short", "%E-time", "const char*", "%E-untyped", "dpiScope",
|
||||
"const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched",
|
||||
"%E-dyn-sched", "%E-fork", "IData", "QData", "%E-logic-implct",
|
||||
" MAX"};
|
||||
static const char* const names[] = {"%E-unk", "svBit",
|
||||
"char", "void*",
|
||||
"char", "int",
|
||||
"%E-integer", "svLogic",
|
||||
"long long", "double",
|
||||
"short", "%E-time",
|
||||
"const char*", "%E-untyped",
|
||||
"dpiScope", "const char*",
|
||||
"%E-mtaskstate", "%E-triggervec",
|
||||
"%E-dly-sched", "%E-trig-sched",
|
||||
"%E-dyn-sched", "%E-fork",
|
||||
"%E-proc-ref", "IData",
|
||||
"QData", "%E-logic-implct",
|
||||
" MAX"};
|
||||
return names[m_e];
|
||||
}
|
||||
static void selfTest() {
|
||||
@@ -545,6 +579,7 @@ public:
|
||||
case TRIGGER_SCHEDULER: return 0; // opaque
|
||||
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
|
||||
case FORK_SYNC: return 0; // opaque
|
||||
case PROCESS_REFERENCE: return 0; // opaque
|
||||
case UINT32: return 32;
|
||||
case UINT64: return 64;
|
||||
default: return 0;
|
||||
@@ -584,7 +619,7 @@ public:
|
||||
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|
||||
|| m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|
||||
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|
||||
|| m_e == DOUBLE || m_e == UNTYPED);
|
||||
|| m_e == PROCESS_REFERENCE || m_e == DOUBLE || m_e == UNTYPED);
|
||||
}
|
||||
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
|
||||
bool isEvent() const { return m_e == EVENT; }
|
||||
@@ -781,6 +816,7 @@ public:
|
||||
"BLOCKTEMP", "MODULETEMP", "STMTTEMP", "XTEMP", "IFACEREF", "MEMBER"};
|
||||
return names[m_e];
|
||||
}
|
||||
bool isParam() const { return m_e == GPARAM || m_e == LPARAM; }
|
||||
bool isSignal() const {
|
||||
return (m_e == WIRE || m_e == WREAL || m_e == IMPLICITWIRE || m_e == TRIWIRE || m_e == TRI0
|
||||
|| m_e == TRI1 || m_e == PORT || m_e == SUPPLY0 || m_e == SUPPLY1 || m_e == VAR);
|
||||
@@ -1097,21 +1133,21 @@ public:
|
||||
}
|
||||
//
|
||||
VNumRange() = default;
|
||||
VNumRange(int hi, int lo, bool littleEndian) { init(hi, lo, littleEndian); }
|
||||
VNumRange(int hi, int lo, bool ascending) { init(hi, lo, ascending); }
|
||||
VNumRange(int left, int right)
|
||||
: m_left{left}
|
||||
, m_right{right}
|
||||
, m_ranged{true} {}
|
||||
~VNumRange() = default;
|
||||
// MEMBERS
|
||||
void init(int hi, int lo, bool littleEndian) {
|
||||
void init(int hi, int lo, bool ascending) {
|
||||
if (lo > hi) {
|
||||
const int t = hi;
|
||||
hi = lo;
|
||||
lo = t;
|
||||
}
|
||||
m_left = littleEndian ? lo : hi;
|
||||
m_right = littleEndian ? hi : lo;
|
||||
m_left = ascending ? lo : hi;
|
||||
m_right = ascending ? hi : lo;
|
||||
m_ranged = true;
|
||||
}
|
||||
int left() const { return m_left; }
|
||||
@@ -1122,10 +1158,10 @@ public:
|
||||
int lo() const VL_MT_SAFE {
|
||||
return m_left > m_right ? m_right : m_left;
|
||||
} // How to show a declaration
|
||||
int leftToRightInc() const { return littleEndian() ? 1 : -1; }
|
||||
int leftToRightInc() const { return ascending() ? 1 : -1; }
|
||||
int elements() const VL_MT_SAFE { return hi() - lo() + 1; }
|
||||
bool ranged() const { return m_ranged; }
|
||||
bool littleEndian() const { return m_left < m_right; }
|
||||
bool ascending() const { return m_left < m_right; }
|
||||
int hiMaxSelect() const {
|
||||
return (lo() < 0 ? hi() - lo() : hi());
|
||||
} // Maximum value a [] select may index
|
||||
@@ -1147,30 +1183,34 @@ inline std::ostream& operator<<(std::ostream& os, const VNumRange& rhs) {
|
||||
class VUseType final {
|
||||
public:
|
||||
enum en : uint8_t {
|
||||
IMP_INCLUDE, // Implementation (.cpp) needs an include
|
||||
INT_INCLUDE, // Interface (.h) needs an include
|
||||
IMP_FWD_CLASS, // Implementation (.cpp) needs a forward class declaration
|
||||
INT_FWD_CLASS, // Interface (.h) needs a forward class declaration
|
||||
// Enum values are compared with <, so order matters
|
||||
INT_FWD_CLASS = 1 << 0, // Interface (.h) needs a forward class declaration
|
||||
INT_INCLUDE = 1 << 1, // Interface (.h) needs an include
|
||||
};
|
||||
enum en m_e;
|
||||
VUseType()
|
||||
: m_e{IMP_FWD_CLASS} {}
|
||||
: m_e{INT_FWD_CLASS} {}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
constexpr VUseType(en _e)
|
||||
: m_e{_e} {}
|
||||
explicit VUseType(int _e)
|
||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
bool isInclude() const { return m_e == IMP_INCLUDE || m_e == INT_INCLUDE; }
|
||||
bool isFwdClass() const { return m_e == IMP_FWD_CLASS || m_e == INT_FWD_CLASS; }
|
||||
constexpr operator en() const { return m_e; }
|
||||
bool containsAny(VUseType other) { return m_e & other.m_e; }
|
||||
const char* ascii() const {
|
||||
static const char* const names[] = {"IMP_INC", "INT_INC", "IMP_FWD", "INT_FWD"};
|
||||
return names[m_e];
|
||||
static const char* const names[] = {"INT_FWD", "INT_INC", "INT_FWD_INC"};
|
||||
return names[m_e - 1];
|
||||
}
|
||||
};
|
||||
constexpr bool operator==(const VUseType& lhs, const VUseType& rhs) { return lhs.m_e == rhs.m_e; }
|
||||
constexpr bool operator==(const VUseType& lhs, VUseType::en rhs) { return lhs.m_e == rhs; }
|
||||
constexpr bool operator==(VUseType::en lhs, const VUseType& rhs) { return lhs == rhs.m_e; }
|
||||
constexpr VUseType::en operator|(VUseType::en lhs, VUseType::en rhs) {
|
||||
return VUseType::en((uint8_t)lhs | (uint8_t)rhs);
|
||||
}
|
||||
constexpr VUseType::en operator&(VUseType::en lhs, VUseType::en rhs) {
|
||||
return VUseType::en((uint8_t)lhs & (uint8_t)rhs);
|
||||
}
|
||||
inline std::ostream& operator<<(std::ostream& os, const VUseType& rhs) {
|
||||
return os << rhs.ascii();
|
||||
}
|
||||
@@ -1211,6 +1251,44 @@ public:
|
||||
~VBasicTypeKey() = default;
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
// VSelfPointerText - Represents text to be emitted before a given var reference, call, etc. to
|
||||
// serve as a pointer to a 'self' object. For example, it could be empty (no self pointer), or the
|
||||
// string 'this', or 'vlSymsp->...'
|
||||
|
||||
class VSelfPointerText final {
|
||||
private:
|
||||
// STATIC MEMBERS
|
||||
// Keep these in shared pointers to avoid branching for special cases
|
||||
static const std::shared_ptr<const string> s_emptyp; // Holds ""
|
||||
static const std::shared_ptr<const string> s_thisp; // Holds "this"
|
||||
|
||||
// MEMBERS
|
||||
std::shared_ptr<const string> m_strp;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
class Empty {}; // for creator type-overload selection
|
||||
VSelfPointerText(Empty)
|
||||
: m_strp{s_emptyp} {}
|
||||
class This {}; // for creator type-overload selection
|
||||
VSelfPointerText(This)
|
||||
: m_strp{s_thisp} {}
|
||||
VSelfPointerText(This, const string& field)
|
||||
: m_strp{std::make_shared<const string>("this->" + field)} {}
|
||||
class VlSyms {}; // for creator type-overload selection
|
||||
VSelfPointerText(VlSyms, const string& field)
|
||||
: m_strp{std::make_shared<const string>("(&vlSymsp->" + field + ')')} {}
|
||||
|
||||
// METHODS
|
||||
bool isEmpty() const { return m_strp == s_emptyp; }
|
||||
bool isVlSym() const { return m_strp->find("vlSymsp") != string::npos; }
|
||||
bool hasThis() const { return m_strp == s_thisp || VString::startsWith(*m_strp, "this"); }
|
||||
string protect(bool useSelfForThis, bool protect) const;
|
||||
const std::string& asString() const { return *m_strp; }
|
||||
bool operator==(const VSelfPointerText& other) const { return *m_strp == *other.m_strp; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// AstNUser - Generic base class for AST User nodes.
|
||||
// - Also used to allow parameter passing up/down iterate calls
|
||||
@@ -1293,55 +1371,55 @@ protected:
|
||||
class VNUser1InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser1InUse() { allocate(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser1InUse() { free (1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser1InUse() { allocate(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser1InUse() { free (1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser2InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser3InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser4InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
class VNUser5InUse final : VNUserInUseBase {
|
||||
protected:
|
||||
friend class AstNode;
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
static uint32_t s_userCntGbl; // Count of which usage of userp() this is
|
||||
static bool s_userBusy; // Count is in use
|
||||
public:
|
||||
VNUser5InUse() { allocate(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser5InUse() { free (5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
VNUser5InUse() { allocate(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
~VNUser5InUse() { free (5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void clear() { clearcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
static void check() { checkcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); }
|
||||
};
|
||||
@@ -1373,13 +1451,38 @@ public:
|
||||
virtual ~VNDeleter() { doDeletes(); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// VNVisitorConst -- Allows new functions to be called on each node
|
||||
// type without changing the base classes. See "Modern C++ Design".
|
||||
// This only has the constant fuctions for non-modifying visitors.
|
||||
// For more typical usage see VNVisitor
|
||||
|
||||
class VNVisitorConst VL_NOT_FINAL : public VNDeleter {
|
||||
friend class AstNode;
|
||||
|
||||
public:
|
||||
/// Call visit()s on nodep
|
||||
inline void iterateConst(AstNode* nodep);
|
||||
/// Call visit()s on nodep
|
||||
inline void iterateConstNull(AstNode* nodep);
|
||||
/// Call visit()s on const nodep's children
|
||||
inline void iterateChildrenConst(AstNode* nodep);
|
||||
/// Call visit()s on nodep's children in backp() order
|
||||
inline void iterateChildrenBackwardsConst(AstNode* nodep);
|
||||
/// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list
|
||||
inline void iterateAndNextConstNull(AstNode* nodep);
|
||||
/// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list, in reverse order
|
||||
inline void iterateAndNextConstNullBackwards(AstNode* nodep);
|
||||
|
||||
virtual void visit(AstNode* nodep) = 0;
|
||||
#include "V3Ast__gen_visitor_decls.h" // From ./astgen
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// VNVisitor -- Allows new functions to be called on each node
|
||||
// type without changing the base classes. See "Modern C++ Design".
|
||||
|
||||
class VNVisitor VL_NOT_FINAL : public VNDeleter {
|
||||
friend class AstNode;
|
||||
|
||||
class VNVisitor VL_NOT_FINAL : public VNVisitorConst {
|
||||
public:
|
||||
/// Call visit()s on nodep
|
||||
inline void iterate(AstNode* nodep);
|
||||
@@ -1387,21 +1490,10 @@ public:
|
||||
inline void iterateNull(AstNode* nodep);
|
||||
/// Call visit()s on nodep's children
|
||||
inline void iterateChildren(AstNode* nodep);
|
||||
/// Call visit()s on nodep's children in backp() order
|
||||
inline void iterateChildrenBackwards(AstNode* nodep);
|
||||
/// Call visit()s on const nodep's children
|
||||
inline void iterateChildrenConst(AstNode* nodep);
|
||||
/// Call visit()s on nodep (maybe nullptr) and nodep's nextp() list
|
||||
inline void iterateAndNextNull(AstNode* nodep);
|
||||
/// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list
|
||||
inline void iterateAndNextConstNull(AstNode* nodep);
|
||||
/// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list, in reverse order
|
||||
inline void iterateAndNextConstNullBackwards(AstNode* nodep);
|
||||
/// Return edited nodep; see comments in V3Ast.cpp
|
||||
inline AstNode* iterateSubtreeReturnEdits(AstNode* nodep);
|
||||
|
||||
virtual void visit(AstNode* nodep) = 0;
|
||||
#include "V3Ast__gen_visitor_decls.h" // From ./astgen
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
@@ -1426,6 +1518,13 @@ protected:
|
||||
|
||||
public:
|
||||
VNRelinker() = default;
|
||||
~VNRelinker() {
|
||||
// Relink is needed so m_iterpp's get restored, e.g. can't have:
|
||||
// ->unlinkFrBack(relinker);
|
||||
// if (only_sometimes) relinker.relink(newp);
|
||||
UDEBUGONLY(
|
||||
UASSERT_STATIC(!m_backp, "Active linker must be relink()ed before destruction"););
|
||||
}
|
||||
inline void relink(AstNode* newp);
|
||||
AstNode* oldp() const { return m_oldp; }
|
||||
void dump(std::ostream& str = std::cout) const;
|
||||
@@ -1545,7 +1644,7 @@ class AstNode VL_NOT_FINAL {
|
||||
private:
|
||||
AstNode* cloneTreeIter();
|
||||
AstNode* cloneTreeIterList();
|
||||
void checkTreeIter(const AstNode* prevBackp) const VL_MT_SAFE;
|
||||
void checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE;
|
||||
bool gateTreeIter() const;
|
||||
static bool sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext,
|
||||
bool gateOnly);
|
||||
@@ -1601,14 +1700,14 @@ public:
|
||||
// ACCESSORS
|
||||
VNType type() const VL_MT_SAFE { return m_type; }
|
||||
const char* typeName() const VL_MT_SAFE { return type().ascii(); } // See also prettyTypeName
|
||||
AstNode* nextp() const VL_MT_SAFE { return m_nextp; }
|
||||
AstNode* backp() const VL_MT_SAFE { return m_backp; }
|
||||
AstNode* abovep() const; // Parent node above, only when no nextp() as otherwise slow
|
||||
AstNode* op1p() const VL_MT_SAFE { return m_op1p; }
|
||||
AstNode* op2p() const VL_MT_SAFE { return m_op2p; }
|
||||
AstNode* op3p() const VL_MT_SAFE { return m_op3p; }
|
||||
AstNode* op4p() const VL_MT_SAFE { return m_op4p; }
|
||||
AstNodeDType* dtypep() const VL_MT_SAFE { return m_dtypep; }
|
||||
AstNode* nextp() const VL_MT_STABLE { return m_nextp; }
|
||||
AstNode* backp() const VL_MT_STABLE { return m_backp; }
|
||||
AstNode* abovep() const; // Get parent node above, only for list head and tail
|
||||
AstNode* op1p() const VL_MT_STABLE { return m_op1p; }
|
||||
AstNode* op2p() const VL_MT_STABLE { return m_op2p; }
|
||||
AstNode* op3p() const VL_MT_STABLE { return m_op3p; }
|
||||
AstNode* op4p() const VL_MT_STABLE { return m_op4p; }
|
||||
AstNodeDType* dtypep() const VL_MT_STABLE { return m_dtypep; }
|
||||
AstNode* clonep() const { return ((m_cloneCnt == s_cloneCntGbl) ? m_clonep : nullptr); }
|
||||
AstNode* firstAbovep() const { // Returns nullptr when second or later in list
|
||||
return ((backp() && backp()->nextp() != this) ? backp() : nullptr);
|
||||
@@ -1655,7 +1754,7 @@ public:
|
||||
static constexpr int INSTR_COUNT_PLI = 20; // PLI routines
|
||||
|
||||
// ACCESSORS
|
||||
virtual string name() const VL_MT_SAFE { return ""; }
|
||||
virtual string name() const { return ""; }
|
||||
virtual string origName() const { return ""; }
|
||||
virtual void name(const string& name) {
|
||||
this->v3fatalSrc("name() called on object without name() method");
|
||||
@@ -1663,22 +1762,23 @@ public:
|
||||
virtual void tag(const string& text) {}
|
||||
virtual string tag() const { return ""; }
|
||||
virtual string verilogKwd() const { return ""; }
|
||||
string nameProtect() const VL_MT_SAFE; // Name with --protect-id applied
|
||||
string nameProtect() const; // Name with --protect-id applied
|
||||
string origNameProtect() const; // origName with --protect-id applied
|
||||
string shortName() const; // Name with __PVT__ removed for concatenating scopes
|
||||
static string dedotName(const string& namein); // Name with dots removed
|
||||
static string prettyName(const string& namein); // Name for printing out to the user
|
||||
static string prettyName(const string& namein) VL_PURE; // Name for printing out to the user
|
||||
static string vpiName(const string& namein); // Name for vpi access
|
||||
static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors)
|
||||
return std::string{"'"} + prettyName(namein) + "'";
|
||||
}
|
||||
static string
|
||||
encodeName(const string& namein); // Encode user name into internal C representation
|
||||
// Encode user name into internal C representation
|
||||
static string encodeName(const string& namein);
|
||||
static string encodeNumber(int64_t num); // Encode number into internal C representation
|
||||
static string vcdName(const string& namein); // Name for printing out to vcd files
|
||||
string prettyName() const VL_MT_SAFE { return prettyName(name()); }
|
||||
string prettyName() const { return prettyName(name()); }
|
||||
string prettyNameQ() const { return prettyNameQ(name()); }
|
||||
string prettyTypeName() const; // "VARREF" for error messages (NOT dtype's pretty name)
|
||||
// "VARREF" for error messages (NOT dtype's pretty name)
|
||||
string prettyTypeName() const;
|
||||
virtual string prettyOperatorName() const { return "operator " + prettyTypeName(); }
|
||||
FileLine* fileline() const VL_MT_SAFE { return m_fileline; }
|
||||
void fileline(FileLine* fl) { m_fileline = fl; }
|
||||
@@ -1697,87 +1797,87 @@ public:
|
||||
void protect(bool flag) { m_flags.protect = flag; }
|
||||
|
||||
// TODO stomp these width functions out, and call via dtypep() instead
|
||||
inline int width() const VL_MT_SAFE;
|
||||
inline int width() const VL_MT_STABLE;
|
||||
inline int widthMin() const;
|
||||
int widthMinV() const {
|
||||
return v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH ? widthMin() : width();
|
||||
}
|
||||
int widthWords() const { return VL_WORDS_I(width()); }
|
||||
bool isQuad() const VL_MT_SAFE { return (width() > VL_IDATASIZE && width() <= VL_QUADSIZE); }
|
||||
bool isWide() const VL_MT_SAFE { return (width() > VL_QUADSIZE); }
|
||||
inline bool isDouble() const;
|
||||
inline bool isSigned() const;
|
||||
inline bool isString() const;
|
||||
bool isQuad() const VL_MT_STABLE { return (width() > VL_IDATASIZE && width() <= VL_QUADSIZE); }
|
||||
bool isWide() const VL_MT_STABLE { return (width() > VL_QUADSIZE); }
|
||||
inline bool isDouble() const VL_MT_STABLE;
|
||||
inline bool isSigned() const VL_MT_STABLE;
|
||||
inline bool isString() const VL_MT_STABLE;
|
||||
|
||||
// clang-format off
|
||||
VNUser user1u() const VL_MT_SAFE {
|
||||
VNUser user1u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user1Cnt==VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "user1p used without AstUserInUse");
|
||||
return ((m_user1Cnt == VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
|
||||
}
|
||||
AstNode* user1p() const VL_MT_SAFE { return user1u().toNodep(); }
|
||||
void user1u(const VNUser& user) { m_user1u=user; m_user1Cnt=VNUser1InUse::s_userCntGbl; }
|
||||
void user1p(void* userp) { user1u(VNUser{userp}); }
|
||||
int user1() const { return user1u().toInt(); }
|
||||
void user1(int val) { user1u(VNUser{val}); }
|
||||
int user1Inc(int val=1) { int v=user1(); user1(v+val); return v; }
|
||||
int user1SetOnce() { int v=user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
|
||||
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
|
||||
void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; }
|
||||
void user1p(void* userp) { user1u(VNUser{userp}); }
|
||||
void user1(int val) { user1u(VNUser{val}); }
|
||||
int user1() const { return user1u().toInt(); }
|
||||
int user1Inc(int val = 1) { int v = user1(); user1(v + val); return v; }
|
||||
int user1SetOnce() { int v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
|
||||
static void user1ClearTree() { VNUser1InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user2u() const VL_MT_SAFE {
|
||||
VNUser user2u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user2Cnt==VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "user2p used without AstUserInUse");
|
||||
return ((m_user2Cnt == VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
|
||||
}
|
||||
AstNode* user2p() const VL_MT_SAFE { return user2u().toNodep(); }
|
||||
void user2u(const VNUser& user) { m_user2u=user; m_user2Cnt=VNUser2InUse::s_userCntGbl; }
|
||||
void user2p(void* userp) { user2u(VNUser{userp}); }
|
||||
int user2() const { return user2u().toInt(); }
|
||||
void user2(int val) { user2u(VNUser{val}); }
|
||||
int user2Inc(int val=1) { int v=user2(); user2(v+val); return v; }
|
||||
int user2SetOnce() { int v=user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
|
||||
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
|
||||
void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; }
|
||||
void user2p(void* userp) { user2u(VNUser{userp}); }
|
||||
void user2(int val) { user2u(VNUser{val}); }
|
||||
int user2() const { return user2u().toInt(); }
|
||||
int user2Inc(int val = 1) { int v = user2(); user2(v + val); return v; }
|
||||
int user2SetOnce() { int v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
|
||||
static void user2ClearTree() { VNUser2InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user3u() const VL_MT_SAFE {
|
||||
VNUser user3u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user3Cnt==VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "user3p used without AstUserInUse");
|
||||
return ((m_user3Cnt == VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
|
||||
}
|
||||
AstNode* user3p() const VL_MT_SAFE { return user3u().toNodep(); }
|
||||
void user3u(const VNUser& user) { m_user3u=user; m_user3Cnt=VNUser3InUse::s_userCntGbl; }
|
||||
void user3p(void* userp) { user3u(VNUser{userp}); }
|
||||
int user3() const { return user3u().toInt(); }
|
||||
void user3(int val) { user3u(VNUser{val}); }
|
||||
int user3Inc(int val=1) { int v=user3(); user3(v+val); return v; }
|
||||
int user3SetOnce() { int v=user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
|
||||
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
|
||||
void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; }
|
||||
void user3p(void* userp) { user3u(VNUser{userp}); }
|
||||
void user3(int val) { user3u(VNUser{val}); }
|
||||
int user3() const { return user3u().toInt(); }
|
||||
int user3Inc(int val = 1) { int v = user3(); user3(v + val); return v; }
|
||||
int user3SetOnce() { int v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
|
||||
static void user3ClearTree() { VNUser3InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user4u() const VL_MT_SAFE {
|
||||
VNUser user4u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user4Cnt==VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "user4p used without AstUserInUse");
|
||||
return ((m_user4Cnt == VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
|
||||
}
|
||||
AstNode* user4p() const VL_MT_SAFE { return user4u().toNodep(); }
|
||||
void user4u(const VNUser& user) { m_user4u=user; m_user4Cnt=VNUser4InUse::s_userCntGbl; }
|
||||
void user4p(void* userp) { user4u(VNUser{userp}); }
|
||||
int user4() const { return user4u().toInt(); }
|
||||
void user4(int val) { user4u(VNUser{val}); }
|
||||
int user4Inc(int val=1) { int v=user4(); user4(v+val); return v; }
|
||||
int user4SetOnce() { int v=user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
|
||||
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
|
||||
void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; }
|
||||
void user4p(void* userp) { user4u(VNUser{userp}); }
|
||||
void user4(int val) { user4u(VNUser{val}); }
|
||||
int user4() const { return user4u().toInt(); }
|
||||
int user4Inc(int val = 1) { int v = user4(); user4(v + val); return v; }
|
||||
int user4SetOnce() { int v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
|
||||
static void user4ClearTree() { VNUser4InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
|
||||
VNUser user5u() const VL_MT_SAFE {
|
||||
VNUser user5u() const VL_MT_STABLE {
|
||||
// Slows things down measurably, so disabled by default
|
||||
//UASSERT_STATIC(VNUser5InUse::s_userBusy, "userp set w/o busy");
|
||||
return ((m_user5Cnt==VNUser5InUse::s_userCntGbl) ? m_user5u : VNUser{0});
|
||||
//UASSERT_STATIC(VNUser5InUse::s_userBusy, "user5p used without AstUserInUse");
|
||||
return ((m_user5Cnt == VNUser5InUse::s_userCntGbl) ? m_user5u : VNUser{0});
|
||||
}
|
||||
AstNode* user5p() const VL_MT_SAFE { return user5u().toNodep(); }
|
||||
void user5u(const VNUser& user) { m_user5u=user; m_user5Cnt=VNUser5InUse::s_userCntGbl; }
|
||||
void user5p(void* userp) { user5u(VNUser{userp}); }
|
||||
int user5() const { return user5u().toInt(); }
|
||||
void user5(int val) { user5u(VNUser{val}); }
|
||||
int user5Inc(int val=1) { int v=user5(); user5(v+val); return v; }
|
||||
int user5SetOnce() { int v=user5(); if (!v) user5(1); return v; } // Better for cache than user5Inc()
|
||||
AstNode* user5p() const VL_MT_STABLE { return user5u().toNodep(); }
|
||||
void user5u(const VNUser& user) { m_user5u = user; m_user5Cnt = VNUser5InUse::s_userCntGbl; }
|
||||
void user5p(void* userp) { user5u(VNUser{userp}); }
|
||||
void user5(int val) { user5u(VNUser{val}); }
|
||||
int user5() const { return user5u().toInt(); }
|
||||
int user5Inc(int val = 1) { int v = user5(); user5(v + val); return v; }
|
||||
int user5SetOnce() { int v = user5(); if (!v) user5(1); return v; } // Better for cache than user5Inc()
|
||||
static void user5ClearTree() { VNUser5InUse::clear(); } // Clear userp()'s across the entire tree
|
||||
// clang-format on
|
||||
|
||||
@@ -1795,6 +1895,7 @@ public:
|
||||
|
||||
// ACCESSORS for specific types
|
||||
// Alas these can't be virtual or they break when passed a nullptr
|
||||
inline bool isClassHandleValue() const;
|
||||
inline bool isZero() const;
|
||||
inline bool isOne() const;
|
||||
inline bool isNeqZero() const;
|
||||
@@ -1834,6 +1935,7 @@ public:
|
||||
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
|
||||
void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); }
|
||||
void dtypeSetVoid() { dtypep(findVoidDType()); }
|
||||
void dtypeSetStream() { dtypep(findStreamDType()); }
|
||||
|
||||
// Data type locators
|
||||
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); }
|
||||
@@ -1845,6 +1947,7 @@ public:
|
||||
AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
|
||||
AstNodeDType* findEmptyQueueDType() const;
|
||||
AstNodeDType* findVoidDType() const;
|
||||
AstNodeDType* findStreamDType() const;
|
||||
AstNodeDType* findQueueIndexDType() const;
|
||||
AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const;
|
||||
AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const;
|
||||
@@ -1855,9 +1958,9 @@ public:
|
||||
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
|
||||
|
||||
// METHODS - dump and error
|
||||
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex);
|
||||
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex);
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN
|
||||
VL_REQUIRES(V3Error::s().m_mutex);
|
||||
VL_RELEASE(V3Error::s().m_mutex);
|
||||
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
return fileline()->warnContextPrimary();
|
||||
}
|
||||
@@ -1893,21 +1996,15 @@ public:
|
||||
// Iterate and insert - assumes tree format
|
||||
virtual void addNextStmt(AstNode* newp,
|
||||
AstNode* belowp); // When calling, "this" is second argument
|
||||
virtual void addBeforeStmt(AstNode* newp,
|
||||
AstNode* belowp); // When calling, "this" is second argument
|
||||
|
||||
// METHODS - Iterate on a tree
|
||||
// Clone or return nullptr if nullptr
|
||||
static AstNode* cloneTreeNull(AstNode* nodep, bool cloneNextLink) {
|
||||
return nodep ? nodep->cloneTree(cloneNextLink) : nullptr;
|
||||
}
|
||||
AstNode* cloneTree(bool cloneNextLink); // Not const, as sets clonep() on original nodep
|
||||
bool gateTree() { return gateTreeIter(); } // Is tree isGateOptimizable?
|
||||
inline bool sameTree(const AstNode* node2p) const; // Does tree of this == node2p?
|
||||
// Does tree of this == node2p?, not allowing non-isGateOptimizable
|
||||
inline bool sameGateTree(const AstNode* node2p) const;
|
||||
void deleteTree(); // Always deletes the next link
|
||||
void checkTree() const VL_MT_SAFE {
|
||||
void checkTree() const VL_MT_STABLE {
|
||||
if (v3Global.opt.debugCheck()) checkTreeIter(backp());
|
||||
}
|
||||
void checkIter() const;
|
||||
@@ -1964,26 +2061,27 @@ public:
|
||||
virtual const char* broken() const { return nullptr; }
|
||||
|
||||
// INVOKERS
|
||||
virtual void accept(VNVisitor& v) = 0;
|
||||
virtual void accept(VNVisitorConst& v) = 0;
|
||||
|
||||
protected:
|
||||
// All VNVisitor related functions are called as methods off the visitor
|
||||
friend class VNVisitor;
|
||||
friend class VNVisitorConst;
|
||||
// Use instead VNVisitor::iterateChildren
|
||||
void iterateChildren(VNVisitor& v);
|
||||
// Use instead VNVisitor::iterateChildrenBackwards
|
||||
void iterateChildrenBackwards(VNVisitor& v);
|
||||
// Use instead VNVisitor::iterateChildrenBackwardsConst
|
||||
void iterateChildrenBackwardsConst(VNVisitorConst& v);
|
||||
// Use instead VNVisitor::iterateChildrenConst
|
||||
void iterateChildrenConst(VNVisitor& v);
|
||||
void iterateChildrenConst(VNVisitorConst& v);
|
||||
// Use instead VNVisitor::iterateAndNextNull
|
||||
void iterateAndNext(VNVisitor& v);
|
||||
// Use instead VNVisitor::iterateAndNextConstNull
|
||||
void iterateAndNextConst(VNVisitor& v);
|
||||
void iterateAndNextConst(VNVisitorConst& v);
|
||||
// Use instead VNVisitor::iterateSubtreeReturnEdits
|
||||
AstNode* iterateSubtreeReturnEdits(VNVisitor& v);
|
||||
|
||||
private:
|
||||
void iterateListBackwards(VNVisitor& v);
|
||||
void iterateListBackwardsConst(VNVisitorConst& v);
|
||||
|
||||
// For internal use only.
|
||||
// Note: specializations for particular node types are provided by 'astgen'
|
||||
@@ -1992,7 +2090,7 @@ private:
|
||||
|
||||
// For internal use only.
|
||||
template <typename TargetType, typename DeclType>
|
||||
constexpr static bool uselessCast() {
|
||||
constexpr static bool uselessCast() VL_PURE {
|
||||
using NonRef = typename std::remove_reference<DeclType>::type;
|
||||
using NonPtr = typename std::remove_pointer<NonRef>::type;
|
||||
using NonCV = typename std::remove_cv<NonPtr>::type;
|
||||
@@ -2001,7 +2099,7 @@ private:
|
||||
|
||||
// For internal use only.
|
||||
template <typename TargetType, typename DeclType>
|
||||
constexpr static bool impossibleCast() {
|
||||
constexpr static bool impossibleCast() VL_PURE {
|
||||
using NonRef = typename std::remove_reference<DeclType>::type;
|
||||
using NonPtr = typename std::remove_pointer<NonRef>::type;
|
||||
using NonCV = typename std::remove_cv<NonPtr>::type;
|
||||
@@ -2033,24 +2131,34 @@ public:
|
||||
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<const T*>(nodep) : nullptr;
|
||||
}
|
||||
|
||||
// For use via the VN_AS macro only
|
||||
// For use via privateAs or the VN_DBG_AS macro only
|
||||
template <typename T, typename E>
|
||||
static T* privateAs(AstNode* nodep) VL_MT_SAFE {
|
||||
static T* unsafePrivateAs(AstNode* nodep) VL_PURE {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
|
||||
<< "'");
|
||||
return reinterpret_cast<T*>(nodep);
|
||||
}
|
||||
template <typename T, typename E>
|
||||
static const T* privateAs(const AstNode* nodep) VL_MT_SAFE {
|
||||
static const T* unsafePrivateAs(const AstNode* nodep) VL_PURE {
|
||||
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
|
||||
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
|
||||
return reinterpret_cast<const T*>(nodep);
|
||||
}
|
||||
|
||||
// For use via the VN_AS macro only
|
||||
template <typename T, typename E>
|
||||
static T* privateAs(AstNode* nodep) VL_PURE {
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
|
||||
<< "'");
|
||||
return reinterpret_cast<const T*>(nodep);
|
||||
return unsafePrivateAs<T, E>(nodep);
|
||||
}
|
||||
template <typename T, typename E>
|
||||
static const T* privateAs(const AstNode* nodep) VL_PURE {
|
||||
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
|
||||
"AstNode is not of expected type, but instead has type '" << nodep->typeName()
|
||||
<< "'");
|
||||
return unsafePrivateAs<T, E>(nodep);
|
||||
}
|
||||
|
||||
// Predicate that returns true if the given 'nodep' might have a descendant of type 'T_Node'.
|
||||
@@ -2483,24 +2591,29 @@ struct std::equal_to<VNRef<T_Node>> final {
|
||||
//######################################################################
|
||||
// Inline VNVisitor METHODS
|
||||
|
||||
void VNVisitorConst::iterateConst(AstNode* nodep) { nodep->accept(*this); }
|
||||
void VNVisitorConst::iterateConstNull(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->accept(*this);
|
||||
}
|
||||
void VNVisitorConst::iterateChildrenConst(AstNode* nodep) { nodep->iterateChildrenConst(*this); }
|
||||
void VNVisitorConst::iterateChildrenBackwardsConst(AstNode* nodep) {
|
||||
nodep->iterateChildrenBackwardsConst(*this);
|
||||
}
|
||||
void VNVisitorConst::iterateAndNextConstNullBackwards(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->iterateListBackwardsConst(*this);
|
||||
}
|
||||
void VNVisitorConst::iterateAndNextConstNull(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->iterateAndNextConst(*this);
|
||||
}
|
||||
|
||||
void VNVisitor::iterate(AstNode* nodep) { nodep->accept(*this); }
|
||||
void VNVisitor::iterateNull(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->accept(*this);
|
||||
}
|
||||
void VNVisitor::iterateChildren(AstNode* nodep) { nodep->iterateChildren(*this); }
|
||||
void VNVisitor::iterateChildrenBackwards(AstNode* nodep) {
|
||||
nodep->iterateChildrenBackwards(*this);
|
||||
}
|
||||
void VNVisitor::iterateChildrenConst(AstNode* nodep) { nodep->iterateChildrenConst(*this); }
|
||||
void VNVisitor::iterateAndNextNull(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->iterateAndNext(*this);
|
||||
}
|
||||
void VNVisitor::iterateAndNextConstNullBackwards(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->iterateListBackwards(*this);
|
||||
}
|
||||
void VNVisitor::iterateAndNextConstNull(AstNode* nodep) {
|
||||
if (VL_LIKELY(nodep)) nodep->iterateAndNextConst(*this);
|
||||
}
|
||||
AstNode* VNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) {
|
||||
return nodep->iterateSubtreeReturnEdits(*this);
|
||||
}
|
||||
|
||||
+27
-19
@@ -25,7 +25,7 @@
|
||||
//######################################################################
|
||||
// Inline METHODS
|
||||
|
||||
int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
|
||||
int AstNode::width() const VL_MT_STABLE { 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;
|
||||
@@ -33,14 +33,18 @@ bool AstNode::width1() const { // V3Const uses to know it can optimize
|
||||
int AstNode::widthInstrs() const {
|
||||
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
|
||||
}
|
||||
bool AstNode::isDouble() const VL_MT_SAFE {
|
||||
bool AstNode::isDouble() const VL_MT_STABLE {
|
||||
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_AS(dtypep(), BasicDType)->isDouble();
|
||||
}
|
||||
bool AstNode::isString() const VL_MT_SAFE {
|
||||
bool AstNode::isString() const VL_MT_STABLE {
|
||||
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
|
||||
}
|
||||
bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
|
||||
bool AstNode::isSigned() const VL_MT_STABLE { return dtypep() && dtypep()->isSigned(); }
|
||||
|
||||
bool AstNode::isClassHandleValue() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNull())
|
||||
|| VN_IS(dtypep(), ClassRefDType);
|
||||
}
|
||||
bool AstNode::isZero() const {
|
||||
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
|
||||
}
|
||||
@@ -61,12 +65,12 @@ bool AstNode::sameGateTree(const AstNode* node2p) const {
|
||||
return sameTreeIter(this, node2p, true, true);
|
||||
}
|
||||
|
||||
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()}; }
|
||||
int AstNodeArrayDType::left() const VL_MT_STABLE { return rangep()->leftConst(); }
|
||||
int AstNodeArrayDType::right() const VL_MT_STABLE { return rangep()->rightConst(); }
|
||||
int AstNodeArrayDType::hi() const VL_MT_STABLE { return rangep()->hiConst(); }
|
||||
int AstNodeArrayDType::lo() const VL_MT_STABLE { return rangep()->loConst(); }
|
||||
int AstNodeArrayDType::elementsConst() const VL_MT_STABLE { return rangep()->elementsConst(); }
|
||||
VNumRange AstNodeArrayDType::declRange() const VL_MT_STABLE { return VNumRange{left(), right()}; }
|
||||
|
||||
AstRange::AstRange(FileLine* fl, int left, int right)
|
||||
: ASTGEN_SUPER_Range(fl) {
|
||||
@@ -78,16 +82,16 @@ AstRange::AstRange(FileLine* fl, const VNumRange& range)
|
||||
leftp(new AstConst{fl, static_cast<uint32_t>(range.left())});
|
||||
rightp(new AstConst{fl, static_cast<uint32_t>(range.right())});
|
||||
}
|
||||
int AstRange::leftConst() const {
|
||||
int AstRange::leftConst() const VL_MT_STABLE {
|
||||
AstConst* const constp = VN_CAST(leftp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
int AstRange::rightConst() const {
|
||||
int AstRange::rightConst() const VL_MT_STABLE {
|
||||
AstConst* const constp = VN_CAST(rightp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
int AstQueueDType::boundConst() const VL_MT_SAFE {
|
||||
int AstQueueDType::boundConst() const VL_MT_STABLE {
|
||||
AstConst* const constp = VN_CAST(boundp(), Const);
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
@@ -123,8 +127,8 @@ int AstBasicDType::lo() const { return (rangep() ? rangep()->loConst() : m.m_nra
|
||||
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 AstBasicDType::ascending() const {
|
||||
return (rangep() ? rangep()->ascending() : m.m_nrange.ascending());
|
||||
}
|
||||
|
||||
bool AstActive::hasClocked() const { return m_sensesp->hasClocked(); }
|
||||
@@ -150,12 +154,15 @@ AstCExpr::AstCExpr(FileLine* fl, const string& textStmt, int setwidth, bool clea
|
||||
}
|
||||
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varp->name(), varp, access) {}
|
||||
: ASTGEN_SUPER_VarRef(fl, 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) {
|
||||
: ASTGEN_SUPER_VarRef(fl, varscp->varp(), access) {
|
||||
varScopep(varscp);
|
||||
}
|
||||
|
||||
string AstVarRef::name() const { return varp() ? varp()->name() : "<null>"; }
|
||||
|
||||
bool AstVarRef::same(const AstVarRef* samep) const {
|
||||
if (varScopep()) {
|
||||
return (varScopep() == samep->varScopep() && access() == samep->access());
|
||||
@@ -169,13 +176,14 @@ bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
|
||||
return (varScopep() == samep->varScopep());
|
||||
} else {
|
||||
return (selfPointer() == samep->selfPointer()
|
||||
&& (!selfPointer().empty() || !samep->selfPointer().empty())
|
||||
&& (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty())
|
||||
&& varp()->name() == samep->varp()->name());
|
||||
}
|
||||
}
|
||||
|
||||
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, varp->name(), varp, access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, varp, access)
|
||||
, m_name{varp->name()}
|
||||
, m_dotted{dotted} {
|
||||
dtypeFrom(varp);
|
||||
}
|
||||
|
||||
+137
-103
@@ -63,9 +63,9 @@ public:
|
||||
// Integral or packed, allowed inside an unpacked union/struct
|
||||
virtual bool isIntegralOrPacked() const { return !isCompound(); }
|
||||
// (Slow) recurse down to find basic data type
|
||||
virtual AstBasicDType* basicp() const = 0;
|
||||
virtual AstBasicDType* basicp() const VL_MT_STABLE = 0;
|
||||
// recurses over typedefs/const/enum to next non-typeref type
|
||||
virtual AstNodeDType* skipRefp() const = 0;
|
||||
virtual AstNodeDType* skipRefp() const VL_MT_STABLE = 0;
|
||||
// recurses over typedefs to next non-typeref-or-const type
|
||||
virtual AstNodeDType* skipRefToConstp() const = 0;
|
||||
// recurses over typedefs/const to next non-typeref-or-enum/struct type
|
||||
@@ -86,7 +86,7 @@ public:
|
||||
// Assignable equivalence. Call skipRefp() on this and samep before calling
|
||||
virtual bool similarDType(const AstNodeDType* samep) const = 0;
|
||||
// Iff has a non-null subDTypep(), as generic node function
|
||||
virtual AstNodeDType* subDTypep() const { return nullptr; }
|
||||
virtual AstNodeDType* subDTypep() const VL_MT_SAFE { return nullptr; }
|
||||
virtual bool isFourstate() const;
|
||||
// Ideally an IEEE $typename
|
||||
virtual string prettyDTypeName() const { return prettyTypeName(); }
|
||||
@@ -126,12 +126,13 @@ public:
|
||||
const char* charIQWN() const {
|
||||
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
|
||||
}
|
||||
string cType(const string& name, bool forFunc, bool isRef) const VL_MT_SAFE;
|
||||
bool isLiteralType() const VL_MT_SAFE; // Represents a C++ LiteralType? (can be constexpr)
|
||||
string cType(const string& name, bool forFunc, bool isRef) const;
|
||||
// Represents a C++ LiteralType? (can be constexpr)
|
||||
bool isLiteralType() const VL_MT_STABLE;
|
||||
|
||||
private:
|
||||
class CTypeRecursed;
|
||||
CTypeRecursed cTypeRecurse(bool compound) const VL_MT_SAFE;
|
||||
CTypeRecursed cTypeRecurse(bool compound) const;
|
||||
};
|
||||
class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
|
||||
// Array data type, ie "some_dtype var_name [2:0]"
|
||||
@@ -170,40 +171,37 @@ public:
|
||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
|
||||
}
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_SAFE {
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE {
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE {
|
||||
return subDTypep()->basicp();
|
||||
} // (Slow) recurse down to find basic data type
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
|
||||
int widthTotalBytes() const override {
|
||||
return elementsConst() * subDTypep()->widthTotalBytes();
|
||||
}
|
||||
inline int left() const;
|
||||
inline int right() const;
|
||||
inline int hi() const;
|
||||
inline int lo() const;
|
||||
inline int elementsConst() const;
|
||||
inline VNumRange declRange() const;
|
||||
inline int left() const VL_MT_STABLE;
|
||||
inline int right() const VL_MT_STABLE;
|
||||
inline int hi() const VL_MT_STABLE;
|
||||
inline int lo() const VL_MT_STABLE;
|
||||
inline int elementsConst() const VL_MT_STABLE;
|
||||
inline VNumRange declRange() const VL_MT_STABLE;
|
||||
};
|
||||
class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType {
|
||||
// A struct or union; common handling
|
||||
// @astgen op1 := membersp : List[AstMemberDType]
|
||||
private:
|
||||
// TYPES
|
||||
using MemberNameMap = std::map<const std::string, AstMemberDType*>;
|
||||
// MEMBERS
|
||||
string m_name; // Name from upper typedef, if any
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
MemberNameMap m_members;
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package it will be emitted with
|
||||
const int m_uniqueNum;
|
||||
bool m_packed;
|
||||
bool m_isFourstate = false; // V3Width computes
|
||||
@@ -231,7 +229,7 @@ public:
|
||||
: VN_AS(findBitRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
|
||||
BasicDType));
|
||||
}
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
// (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this)
|
||||
@@ -241,20 +239,14 @@ public:
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
return this == samep; // We don't compare members, require exact equivalence
|
||||
}
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
bool packed() const VL_MT_SAFE { return m_packed; }
|
||||
void packed(bool flag) { m_packed = flag; }
|
||||
// packed() but as don't support unpacked, presently all structs
|
||||
static bool packedUnsup() { return true; }
|
||||
void isFourstate(bool flag) { m_isFourstate = flag; }
|
||||
bool isFourstate() const override { return m_isFourstate; }
|
||||
void clearCache() { m_members.clear(); }
|
||||
void repairMemberCache();
|
||||
AstMemberDType* findMember(const string& name) const {
|
||||
const auto it = m_members.find(name);
|
||||
return (it == m_members.end()) ? nullptr : it->second;
|
||||
}
|
||||
bool isFourstate() const override VL_MT_SAFE { return m_isFourstate; }
|
||||
static int lo() { return 0; }
|
||||
int hi() const { return dtypep()->width() - 1; } // Packed classes look like arrays
|
||||
VNumRange declRange() const { return VNumRange{hi(), lo()}; }
|
||||
@@ -280,7 +272,7 @@ public:
|
||||
this->valuep(valuep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstEnumItem;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool hasDType() const override { return true; }
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
@@ -336,7 +328,7 @@ public:
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_SAFE {
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
@@ -345,13 +337,13 @@ public:
|
||||
AstNodeDType* virtRefDType2p() const override { return m_keyDTypep; }
|
||||
void virtRefDType2p(AstNodeDType* nodep) override { keyDTypep(nodep); }
|
||||
//
|
||||
AstNodeDType* keyDTypep() const VL_MT_SAFE {
|
||||
AstNodeDType* keyDTypep() const VL_MT_STABLE {
|
||||
return m_keyDTypep ? m_keyDTypep : keyChildDTypep();
|
||||
}
|
||||
void keyDTypep(AstNodeDType* nodep) { m_keyDTypep = nodep; }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
|
||||
@@ -401,14 +393,11 @@ public:
|
||||
ASTGEN_MEMBERS_AstBasicDType;
|
||||
void dump(std::ostream& str) const override;
|
||||
// width/widthMin/numeric compared elsewhere
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstBasicDType* const sp = static_cast<const AstBasicDType*>(samep);
|
||||
return m == sp->m;
|
||||
}
|
||||
bool same(const AstNode* samep) const override;
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
return type() == samep->type() && same(samep);
|
||||
}
|
||||
string name() const override { return m.m_keyword.ascii(); }
|
||||
string name() const override VL_MT_STABLE { return m.m_keyword.ascii(); }
|
||||
string prettyDTypeName() const override;
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(dtypep() != this);
|
||||
@@ -423,8 +412,8 @@ public:
|
||||
}
|
||||
}
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return (AstBasicDType*)this; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return (AstBasicDType*)this; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
// (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this)
|
||||
@@ -440,6 +429,7 @@ public:
|
||||
bool isEvent() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::EVENT; }
|
||||
bool isTriggerVec() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::TRIGGERVEC; }
|
||||
bool isForkSync() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::FORK_SYNC; }
|
||||
bool isProcessRef() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::PROCESS_REFERENCE; }
|
||||
bool isDelayScheduler() const VL_MT_SAFE {
|
||||
return keyword() == VBasicDTypeKwd::DELAY_SCHEDULER;
|
||||
}
|
||||
@@ -467,9 +457,9 @@ public:
|
||||
inline int hi() const;
|
||||
inline int lo() const;
|
||||
inline int elements() const;
|
||||
int left() const { return littleEndian() ? lo() : hi(); } // How to show a declaration
|
||||
int right() const { return littleEndian() ? hi() : lo(); }
|
||||
inline bool littleEndian() const;
|
||||
int left() const { return ascending() ? lo() : hi(); } // How to show a declaration
|
||||
int right() const { return ascending() ? hi() : lo(); }
|
||||
inline bool ascending() const;
|
||||
bool implicit() const { return keyword() == VBasicDTypeKwd::LOGIC_IMPLICIT; }
|
||||
bool untyped() const { return keyword() == VBasicDTypeKwd::UNTYPED; }
|
||||
VNumRange declRange() const { return isRanged() ? VNumRange{left(), right()} : VNumRange{}; }
|
||||
@@ -491,13 +481,13 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstBracketArrayDType;
|
||||
bool similarDType(const AstNodeDType* samep) const override { V3ERROR_NA_RETURN(false); }
|
||||
AstNodeDType* subDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE { return childDTypep(); }
|
||||
// METHODS
|
||||
// Will be removed in V3Width, which relies on this
|
||||
// being a child not a dtype pointed node
|
||||
bool maybePointedTo() const override { return false; }
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { V3ERROR_NA_RETURN(0); }
|
||||
@@ -530,19 +520,19 @@ public:
|
||||
}
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string name() const override;
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
string name() const override VL_MT_STABLE;
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return 0; }
|
||||
int widthTotalBytes() const override { return 0; }
|
||||
AstNodeDType* virtRefDTypep() const override { return nullptr; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override {}
|
||||
AstNodeDType* subDTypep() const override { return nullptr; }
|
||||
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
AstClass* classp() const { return m_classp; }
|
||||
AstClass* classp() const VL_MT_STABLE { return m_classp; }
|
||||
void classp(AstClass* nodep) { m_classp = nodep; }
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
@@ -578,13 +568,15 @@ public:
|
||||
return skipRefp()->similarDType(samep->skipRefp());
|
||||
}
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return subDTypep()->skipRefp(); }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return subDTypep()->skipRefToEnump(); }
|
||||
int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
|
||||
@@ -625,18 +617,20 @@ public:
|
||||
return type() == samep->type() && same(samep);
|
||||
}
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override { return dtypep() ? dtypep() : childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return dtypep() ? dtypep() : childDTypep();
|
||||
}
|
||||
void* containerp() const { return m_containerp; }
|
||||
// METHODS
|
||||
// op1 = Range of variable
|
||||
AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); }
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return dtypep()->widthAlignBytes(); }
|
||||
int widthTotalBytes() const override { return dtypep()->widthTotalBytes(); }
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
bool isCompound() const override { return false; }
|
||||
};
|
||||
@@ -679,15 +673,15 @@ public:
|
||||
string prettyDTypeName() const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_SAFE {
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
|
||||
@@ -706,13 +700,13 @@ public:
|
||||
bool hasDType() const override { return true; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool undead() const override { return true; }
|
||||
AstNodeDType* subDTypep() const override { return nullptr; }
|
||||
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeDType* virtRefDTypep() const override { return nullptr; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override {}
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
@@ -756,17 +750,19 @@ public:
|
||||
}
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return subDTypep()->skipRefp(); }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); }
|
||||
AstNodeDType* skipRefToConstp() const override { return subDTypep()->skipRefToConstp(); }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
@@ -818,8 +814,8 @@ public:
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
void cloneRelink() override;
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
@@ -872,7 +868,7 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstMemberDType;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string name() const override { return m_name; } // * = Var name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
bool hasDType() const override { return true; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
const char* broken() const override {
|
||||
@@ -883,7 +879,10 @@ public:
|
||||
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||
}
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
AstNodeUOrStructDType* getChildStructp() const;
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
@@ -891,10 +890,10 @@ public:
|
||||
//
|
||||
// (Slow) recurse down to find basic data type (Note don't need virtual -
|
||||
// AstVar isn't a NodeDType)
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||
// op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType)
|
||||
AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return subDTypep()->skipRefp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); }
|
||||
AstNodeDType* skipRefToConstp() const override { return subDTypep()->skipRefToConstp(); }
|
||||
AstNodeDType* skipRefToEnump() const override { return subDTypep()->skipRefToEnump(); }
|
||||
// (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this)
|
||||
@@ -931,9 +930,11 @@ public:
|
||||
ASTGEN_MEMBERS_AstParamTypeDType;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override { return dtypep() ? dtypep() : childDTypep(); }
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return subDTypep()->skipRefp(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return dtypep() ? dtypep() : childDTypep();
|
||||
}
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); }
|
||||
AstNodeDType* skipRefToConstp() const override { return subDTypep()->skipRefToConstp(); }
|
||||
AstNodeDType* skipRefToEnump() const override { return subDTypep()->skipRefToEnump(); }
|
||||
bool similarDType(const AstNodeDType* samep) const override {
|
||||
@@ -944,7 +945,7 @@ public:
|
||||
int widthAlignBytes() const override { return dtypep()->widthAlignBytes(); }
|
||||
int widthTotalBytes() const override { return dtypep()->widthTotalBytes(); }
|
||||
// METHODS
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool hasDType() const override { return true; }
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
@@ -967,8 +968,8 @@ public:
|
||||
AstNodeDType* dtypep() const { return nullptr; }
|
||||
// METHODS
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return nullptr; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return nullptr; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
@@ -1022,17 +1023,17 @@ public:
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string prettyDTypeName() const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_SAFE {
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
inline int boundConst() const;
|
||||
inline int boundConst() const VL_MT_STABLE;
|
||||
AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
@@ -1052,7 +1053,7 @@ private:
|
||||
// Post-width typedefs are removed and point to type directly
|
||||
AstNodeDType* m_refDTypep = nullptr; // data type pointed to, BELOW the AstTypedef
|
||||
string m_name; // Name of an AstTypedef
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Class/package in which it was defined
|
||||
public:
|
||||
AstRefDType(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_RefDType(fl)
|
||||
@@ -1067,6 +1068,7 @@ public:
|
||||
AstRefDType(FileLine* fl, FlagTypeOfExpr, AstNode* typeofp)
|
||||
: ASTGEN_SUPER_RefDType(fl) {
|
||||
this->typeofp(typeofp);
|
||||
if (AstNodeDType* const dtp = VN_CAST(typeofp, NodeDType)) refDTypep(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRefDType;
|
||||
// METHODS
|
||||
@@ -1082,15 +1084,15 @@ public:
|
||||
}
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
string prettyDTypeName() const override {
|
||||
return subDTypep() ? prettyName(subDTypep()->name()) : prettyName();
|
||||
}
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE {
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE {
|
||||
return subDTypep() ? subDTypep()->basicp() : nullptr;
|
||||
}
|
||||
AstNodeDType* subDTypep() const override;
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE {
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE;
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE {
|
||||
// Skip past both the Ref and the Typedef
|
||||
if (subDTypep()) {
|
||||
return subDTypep()->skipRefp();
|
||||
@@ -1119,9 +1121,9 @@ public:
|
||||
int widthTotalBytes() const override { return dtypeSkipRefp()->widthTotalBytes(); }
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); }
|
||||
AstTypedef* typedefp() const { return m_typedefp; }
|
||||
AstTypedef* typedefp() const VL_MT_SAFE { return m_typedefp; }
|
||||
void typedefp(AstTypedef* nodep) { m_typedefp = nodep; }
|
||||
AstNodeDType* refDTypep() const { return m_refDTypep; }
|
||||
AstNodeDType* refDTypep() const VL_MT_SAFE { return m_refDTypep; }
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
AstNodeDType* virtRefDTypep() const override { return refDTypep(); }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
@@ -1163,19 +1165,49 @@ public:
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
// op1 = Range of variable
|
||||
AstNodeDType* subDTypep() const override { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return sizeof(std::map<std::string, std::string>); }
|
||||
int widthTotalBytes() const override { return sizeof(std::map<std::string, std::string>); }
|
||||
bool isCompound() const override { return true; }
|
||||
};
|
||||
class AstStreamDType final : public AstNodeDType {
|
||||
// Stream data type, used only as data type of stream operations
|
||||
// Should behave like AstPackArrayDType, but it doesn't have a size
|
||||
public:
|
||||
explicit AstStreamDType(FileLine* fl)
|
||||
: ASTGEN_SUPER_StreamDType(fl) {
|
||||
dtypep(this);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstStreamDType;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
bool hasDType() const override { return true; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool undead() const override { return true; }
|
||||
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeDType* virtRefDTypep() const override { return nullptr; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override {}
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return 1; }
|
||||
int widthTotalBytes() const override { return 1; }
|
||||
bool isCompound() const override { return false; }
|
||||
};
|
||||
class AstUnsizedArrayDType final : public AstNodeDType {
|
||||
// Unsized/open-range Array data type, ie "some_dtype var_name []"
|
||||
// @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width
|
||||
@@ -1201,13 +1233,15 @@ public:
|
||||
bool similarDType(const AstNodeDType* samep) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
|
||||
@@ -1226,13 +1260,13 @@ public:
|
||||
bool hasDType() const override { return true; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool undead() const override { return true; }
|
||||
AstNodeDType* subDTypep() const override { return nullptr; }
|
||||
AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; }
|
||||
AstNodeDType* virtRefDTypep() const override { return nullptr; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override {}
|
||||
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return nullptr; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
// cppcheck-suppress csyleCast
|
||||
@@ -1266,15 +1300,15 @@ public:
|
||||
bool similarDType(const AstNodeDType* samep) const override;
|
||||
void dumpSmall(std::ostream& str) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const override VL_MT_SAFE {
|
||||
AstNodeDType* subDTypep() const override VL_MT_STABLE {
|
||||
return m_refDTypep ? m_refDTypep : childDTypep();
|
||||
}
|
||||
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||
AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||
// METHODS
|
||||
AstBasicDType* basicp() const override VL_MT_SAFE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_SAFE { return (AstNodeDType*)this; }
|
||||
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||
int widthAlignBytes() const override { return sizeof(std::map<std::string, std::string>); }
|
||||
|
||||
+184
-80
@@ -214,7 +214,7 @@ class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeExpr {
|
||||
// @astgen op3 := pinsp : List[AstNodeExpr]
|
||||
// @astgen op4 := scopeNamep : Optional[AstScopeName]
|
||||
AstNodeFTask* m_taskp = nullptr; // [AfterLink] Pointer to task referenced
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the task
|
||||
string m_name; // Name of variable
|
||||
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
|
||||
string m_inlinedDots; // Dotted hierarchy flattened out
|
||||
@@ -236,7 +236,7 @@ public:
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
string name() const override { return m_name; } // * = Var name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
bool isGateOptimizable() const override;
|
||||
string dotted() const { return m_dotted; } // * = Scope name or ""
|
||||
string inlinedDots() const { return m_inlinedDots; }
|
||||
@@ -322,7 +322,7 @@ public:
|
||||
ASTGEN_MEMBERS_AstNodeTermop;
|
||||
// Know no children, and hot function, so skip iterator for speed
|
||||
// cppcheck-suppress functionConst
|
||||
void iterateChildren(VNVisitor& v) {}
|
||||
void iterateChildren(VNVisitorConst& v) {}
|
||||
void dump(std::ostream& str) const override;
|
||||
};
|
||||
class AstNodeTriop VL_NOT_FINAL : public AstNodeExpr {
|
||||
@@ -360,14 +360,8 @@ class AstNodeCond VL_NOT_FINAL : public AstNodeTriop {
|
||||
// @astgen alias op2 := thenp
|
||||
// @astgen alias op3 := elsep
|
||||
protected:
|
||||
AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp, AstNodeExpr* elsep)
|
||||
: AstNodeTriop{t, fl, condp, thenp, elsep} {
|
||||
if (thenp) {
|
||||
dtypeFrom(thenp);
|
||||
} else if (elsep) {
|
||||
dtypeFrom(elsep);
|
||||
}
|
||||
}
|
||||
AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp,
|
||||
AstNodeExpr* elsep);
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstNodeCond;
|
||||
@@ -449,21 +443,19 @@ class AstNodeVarRef VL_NOT_FINAL : public AstNodeExpr {
|
||||
VAccess m_access; // Left hand side assignment
|
||||
AstVar* m_varp; // [AfterLink] Pointer to variable itself
|
||||
AstVarScope* m_varScopep = nullptr; // Varscope for hierarchy
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Package hierarchy
|
||||
string m_name; // Name of variable
|
||||
string m_selfPointer; // Output code object pointer (e.g.: 'this')
|
||||
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the variable
|
||||
VSelfPointerText m_selfPointer
|
||||
= VSelfPointerText{VSelfPointerText::Empty()}; // Output code object
|
||||
// pointer (e.g.: 'this')
|
||||
protected:
|
||||
AstNodeVarRef(VNType t, FileLine* fl, const string& name, const VAccess& access)
|
||||
AstNodeVarRef(VNType t, FileLine* fl, const VAccess& access)
|
||||
: AstNodeExpr{t, fl}
|
||||
, m_access{access}
|
||||
, m_name{name} {
|
||||
, m_access{access} {
|
||||
varp(nullptr);
|
||||
}
|
||||
AstNodeVarRef(VNType t, FileLine* fl, const string& name, AstVar* varp, const VAccess& access)
|
||||
AstNodeVarRef(VNType t, FileLine* fl, AstVar* varp, const VAccess& access)
|
||||
: AstNodeExpr{t, fl}
|
||||
, m_access{access}
|
||||
, m_name{name} {
|
||||
, m_access{access} {
|
||||
// May have varp==nullptr
|
||||
this->varp(varp);
|
||||
}
|
||||
@@ -474,8 +466,6 @@ public:
|
||||
const char* broken() const override;
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
void cloneRelink() override;
|
||||
string name() const override { return m_name; } // * = Var name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
VAccess access() const { return m_access; }
|
||||
void access(const VAccess& flag) { m_access = flag; } // Avoid using this; Set in constructor
|
||||
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
|
||||
@@ -485,14 +475,16 @@ public:
|
||||
}
|
||||
AstVarScope* varScopep() const { return m_varScopep; }
|
||||
void varScopep(AstVarScope* varscp) { m_varScopep = varscp; }
|
||||
string selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const string& value) { m_selfPointer = value; }
|
||||
string selfPointerProtect(bool useSelfForThis) const;
|
||||
const VSelfPointerText& selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
|
||||
string selfPointerProtect(bool useSelfForThis) const {
|
||||
return selfPointer().protect(useSelfForThis, protect());
|
||||
}
|
||||
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
|
||||
// Know no children, and hot function, so skip iterator for speed
|
||||
// cppcheck-suppress functionConst
|
||||
void iterateChildren(VNVisitor& v) {}
|
||||
void iterateChildren(VNVisitorConst& v) {}
|
||||
};
|
||||
|
||||
// === Concrete node types =====================================================
|
||||
@@ -532,7 +524,7 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstArg;
|
||||
bool hasDType() const override { return false; }
|
||||
string name() const override { return m_name; } // * = Pin name, ""=go by number
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Pin name, ""=go by number
|
||||
void name(const string& name) override { m_name = name; }
|
||||
bool emptyConnectNoNext() const { return !exprp() && name() == "" && !nextp(); }
|
||||
|
||||
@@ -593,35 +585,29 @@ class AstCMethodHard final : public AstNodeExpr {
|
||||
string m_name; // Name of method
|
||||
bool m_pure = false; // Pure optimizable
|
||||
public:
|
||||
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name,
|
||||
AstNodeExpr* pinsp = nullptr)
|
||||
: ASTGEN_SUPER_CMethodHard(fl)
|
||||
, m_name{name} {
|
||||
// TODO: this constructor is exactly the same as the other, bar the ignored tag argument
|
||||
this->fromp(fromp);
|
||||
this->addPinsp(pinsp);
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, const string& name,
|
||||
AstNodeExpr* pinsp = nullptr)
|
||||
: ASTGEN_SUPER_CMethodHard(fl)
|
||||
, m_name{name} {
|
||||
this->fromp(fromp);
|
||||
this->addPinsp(pinsp);
|
||||
setPurity();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCMethodHard;
|
||||
string name() const override { return m_name; } // * = Var name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstCMethodHard* asamep = static_cast<const AstCMethodHard*>(samep);
|
||||
return (m_name == asamep->m_name);
|
||||
}
|
||||
bool isPure() const override { return m_pure; }
|
||||
void pure(bool flag) { m_pure = flag; }
|
||||
int instrCount() const override;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
|
||||
private:
|
||||
void setPurity();
|
||||
};
|
||||
class AstCast final : public AstNodeExpr {
|
||||
// Cast to appropriate data type
|
||||
@@ -644,7 +630,7 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
AstNodeDType* subDTypep() const VL_MT_STABLE { return dtypep() ? dtypep() : childDTypep(); }
|
||||
};
|
||||
class AstCastParse final : public AstNodeExpr {
|
||||
// Cast to appropriate type, where we haven't determined yet what the data type is
|
||||
@@ -689,7 +675,7 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCellArrayRef;
|
||||
// ACCESSORS
|
||||
string name() const override { return m_name; } // * = Array name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Array name
|
||||
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -710,7 +696,7 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCellRef;
|
||||
// ACCESSORS
|
||||
string name() const override { return m_name; } // * = Array name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Array name
|
||||
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -721,7 +707,7 @@ class AstClassOrPackageRef final : public AstNodeExpr {
|
||||
private:
|
||||
string m_name;
|
||||
// Node not NodeModule to appease some early parser usage
|
||||
AstNode* m_classOrPackageNodep; // Package hierarchy
|
||||
AstNode* m_classOrPackageNodep; // Pointer to class/package referenced
|
||||
public:
|
||||
AstClassOrPackageRef(FileLine* fl, const string& name, AstNode* classOrPackageNodep,
|
||||
AstPin* paramsp)
|
||||
@@ -746,7 +732,7 @@ public:
|
||||
== static_cast<const AstClassOrPackageRef*>(samep)->m_classOrPackageNodep);
|
||||
}
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
string name() const override { return m_name; } // * = Var name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
AstNode* classOrPackageNodep() const { return m_classOrPackageNodep; }
|
||||
void classOrPackageNodep(AstNode* nodep) { m_classOrPackageNodep = nodep; }
|
||||
AstNodeModule* classOrPackagep() const;
|
||||
@@ -975,6 +961,20 @@ public:
|
||||
, m_num(this, 1, on) {
|
||||
dtypeSetBit();
|
||||
}
|
||||
class All0 {};
|
||||
AstConst(FileLine* fl, All0)
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num(this, "'0") {
|
||||
initWithNumber();
|
||||
fl->warnOff(V3ErrorCode::NEWERSTD, true);
|
||||
}
|
||||
class All1 {};
|
||||
AstConst(FileLine* fl, All1)
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num(this, "'1") {
|
||||
initWithNumber();
|
||||
fl->warnOff(V3ErrorCode::NEWERSTD, true);
|
||||
}
|
||||
class Null {};
|
||||
AstConst(FileLine* fl, Null)
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
@@ -990,7 +990,7 @@ public:
|
||||
initWithNumber();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConst;
|
||||
string name() const override { return num().ascii(); } // * = Value
|
||||
string name() const override VL_MT_STABLE { return num().ascii(); } // * = Value
|
||||
const V3Number& num() const VL_MT_SAFE { return m_num; } // * = Value
|
||||
V3Number& num() { return m_num; } // * = Value
|
||||
uint32_t toUInt() const { return num().toUInt(); }
|
||||
@@ -1010,6 +1010,34 @@ public:
|
||||
// May return nullptr on parse failure.
|
||||
static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
|
||||
};
|
||||
class AstCvtDynArrayToPacked final : public AstNodeExpr {
|
||||
// Cast from dynamic queue data type to packed array
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtDynArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtDynArrayToPacked(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtDynArrayToPacked;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstCvtPackedToDynArray final : public AstNodeExpr {
|
||||
// Cast from packed array to dynamic queue data type
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
public:
|
||||
AstCvtPackedToDynArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_CvtPackedToDynArray(fl) {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtPackedToDynArray;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstDot final : public AstNodeExpr {
|
||||
// A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef
|
||||
// These are eliminated in the link stage
|
||||
@@ -1048,7 +1076,7 @@ public:
|
||||
};
|
||||
class AstEnumItemRef final : public AstNodeExpr {
|
||||
AstEnumItem* m_itemp; // [AfterLink] Pointer to item
|
||||
AstNodeModule* m_classOrPackagep; // Package hierarchy
|
||||
AstNodeModule* m_classOrPackagep; // Class/package in which it was defined
|
||||
public:
|
||||
AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
|
||||
: ASTGEN_SUPER_EnumItemRef(fl)
|
||||
@@ -1058,7 +1086,7 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstEnumItemRef;
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override { return itemp()->name(); }
|
||||
string name() const override VL_MT_STABLE { return itemp()->name(); }
|
||||
int instrCount() const override { return 0; }
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override {
|
||||
@@ -1068,7 +1096,7 @@ public:
|
||||
const AstEnumItemRef* const sp = static_cast<const AstEnumItemRef*>(samep);
|
||||
return itemp() == sp->itemp();
|
||||
}
|
||||
AstEnumItem* itemp() const { return m_itemp; }
|
||||
AstEnumItem* itemp() const VL_MT_STABLE { return m_itemp; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
@@ -1092,7 +1120,8 @@ public:
|
||||
// METHODS
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool isPure() const override { return false; }
|
||||
bool same(const AstNode*) const override { return true; }
|
||||
};
|
||||
class AstFError final : public AstNodeExpr {
|
||||
@@ -1109,9 +1138,50 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
int instrCount() const override { return widthInstrs() * 64; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstFOpen final : public AstNodeExpr {
|
||||
// @astgen op2 := filenamep : AstNodeExpr
|
||||
// @astgen op3 := modep : AstNodeExpr
|
||||
public:
|
||||
AstFOpen(FileLine* fl, AstNodeExpr* filenamep, AstNodeExpr* modep)
|
||||
: ASTGEN_SUPER_FOpen(fl) {
|
||||
this->filenamep(filenamep);
|
||||
this->modep(modep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstFOpen;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
string verilogKwd() const override { return "$fopen"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstFOpenMcd final : public AstNodeExpr {
|
||||
// @astgen op2 := filenamep : AstNodeExpr
|
||||
public:
|
||||
AstFOpenMcd(FileLine* fl, AstNodeExpr* filenamep)
|
||||
: ASTGEN_SUPER_FOpenMcd(fl) {
|
||||
this->filenamep(filenamep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstFOpenMcd;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
string verilogKwd() const override { return "$fopen"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstFRead final : public AstNodeExpr {
|
||||
// @astgen op1 := memp : AstNode // VarRef for result
|
||||
// @astgen op2 := filep : AstNode // file (must be a VarRef)
|
||||
@@ -1168,7 +1238,7 @@ public:
|
||||
this->filep(filep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstFScanF;
|
||||
string name() const override { return m_text; }
|
||||
string name() const override VL_MT_STABLE { return m_text; }
|
||||
string verilogKwd() const override { return "$fscanf"; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -1258,7 +1328,9 @@ public:
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstImplication final : public AstNodeExpr {
|
||||
// Verilog |-> |=>
|
||||
// Verilog Implication Operator
|
||||
// Nonoverlapping "|=>"
|
||||
// Overlapping "|->" (doesn't currently use this - might make new Ast type)
|
||||
// @astgen op1 := lhsp : AstNodeExpr
|
||||
// @astgen op2 := rhsp : AstNodeExpr
|
||||
// @astgen op3 := sentreep : Optional[AstSenTree]
|
||||
@@ -1365,7 +1437,7 @@ public:
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
string name() const override { return m_name; } // * = Var name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
bool index() const { return m_index; }
|
||||
};
|
||||
@@ -1373,6 +1445,7 @@ class AstMemberSel final : public AstNodeExpr {
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
// Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
|
||||
string m_name;
|
||||
VAccess m_access; // Read or write, as in AstNodeVarRef
|
||||
AstVar* m_varp = nullptr; // Post link, variable within class that is target of selection
|
||||
public:
|
||||
AstMemberSel(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name)
|
||||
@@ -1391,12 +1464,14 @@ public:
|
||||
void cloneRelink() override;
|
||||
const char* broken() const override;
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
VAccess access() const { return m_access; }
|
||||
void access(const VAccess& flag) { m_access = flag; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool same(const AstNode* samep) const override { return true; } // dtype comparison does it
|
||||
bool cleanOut() const override { return true; }
|
||||
bool same(const AstNode* samep) const override;
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
AstVar* varp() const { return m_varp; }
|
||||
void varp(AstVar* nodep) { m_varp = nodep; }
|
||||
@@ -1456,7 +1531,7 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstParseRef;
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override { return m_name; } // * = Var name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstParseRef* const asamep = static_cast<const AstParseRef*>(samep);
|
||||
return (expect() == asamep->expect() && m_name == asamep->m_name);
|
||||
@@ -1533,7 +1608,7 @@ public:
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
AstNodeDType* subDTypep() const VL_MT_STABLE { return dtypep() ? dtypep() : childDTypep(); }
|
||||
};
|
||||
class AstRand final : public AstNodeExpr {
|
||||
// $random/$random(seed) or $urandom/$urandom(seed)
|
||||
@@ -1579,6 +1654,26 @@ public:
|
||||
bool reset() const { return m_reset; }
|
||||
bool urandom() const { return m_urandom; }
|
||||
};
|
||||
class AstRandRNG final : public AstNodeExpr {
|
||||
// Random used in a class using VlRNG
|
||||
// Return a random number, based upon width()
|
||||
public:
|
||||
AstRandRNG(FileLine* fl, AstNodeDType* dtp)
|
||||
: ASTGEN_SUPER_RandRNG(fl) {
|
||||
dtypep(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstRandRNG;
|
||||
string emitVerilog() override { return "%f$rngrandom()"; }
|
||||
string emitC() override {
|
||||
return isWide() ? "VL_RANDOM_RNG_%nq(__Vm_rng, %nw, %P)" //
|
||||
: "VL_RANDOM_RNG_%nq(__Vm_rng)";
|
||||
}
|
||||
bool cleanOut() const override { return false; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstRose final : public AstNodeExpr {
|
||||
// Verilog $rose
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
@@ -1630,7 +1725,7 @@ public:
|
||||
addExprsp(exprsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSFormatF;
|
||||
string name() const override { return m_text; }
|
||||
string name() const override VL_MT_STABLE { return m_text; }
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
bool same(const AstNode* samep) const override {
|
||||
return text() == static_cast<const AstSFormatF*>(samep)->text();
|
||||
@@ -1665,7 +1760,7 @@ public:
|
||||
this->fromp(fromp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSScanF;
|
||||
string name() const override { return m_text; }
|
||||
string name() const override VL_MT_STABLE { return m_text; }
|
||||
string verilogKwd() const override { return "$sscanf"; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
@@ -1836,7 +1931,7 @@ public:
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstStructSel final : public AstNodeExpr {
|
||||
// Unpacked struct member access
|
||||
// Unpacked struct/union member access
|
||||
// Parents: math|stmt
|
||||
// Children: varref, math
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
@@ -1850,11 +1945,14 @@ public:
|
||||
dtypep(nullptr); // V3Width will resolve
|
||||
}
|
||||
ASTGEN_MEMBERS_AstStructSel;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return false; }
|
||||
bool cleanOut() const override {
|
||||
// Not a union
|
||||
return VN_IS(fromp()->dtypep()->skipRefp(), StructDType);
|
||||
}
|
||||
bool same(const AstNode* samep) const override {
|
||||
const AstStructSel* const sp = static_cast<const AstStructSel*>(samep);
|
||||
return m_name == sp->m_name;
|
||||
@@ -1931,7 +2029,7 @@ public:
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
bool cleanOut() const override { return true; }
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
AstNodeDType* subDTypep() const VL_MT_STABLE { return dtypep() ? dtypep() : childDTypep(); }
|
||||
};
|
||||
class AstTimePrecision final : public AstNodeExpr {
|
||||
// Verilog $timeprecision
|
||||
@@ -2047,14 +2145,14 @@ class AstWith final : public AstNodeExpr {
|
||||
// Children: expression (equation establishing the with)
|
||||
// @astgen op1 := indexArgRefp : AstLambdaArgRef
|
||||
// @astgen op2 := valueArgRefp : AstLambdaArgRef
|
||||
// @astgen op3 := exprp : AstNodeExpr
|
||||
// @astgen op3 := exprp : List[AstNode]
|
||||
public:
|
||||
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
|
||||
AstNodeExpr* exprp)
|
||||
: ASTGEN_SUPER_With(fl) {
|
||||
this->indexArgRefp(indexArgRefp);
|
||||
this->valueArgRefp(valueArgRefp);
|
||||
this->exprp(exprp);
|
||||
this->addExprp(exprp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstWith;
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
@@ -2159,7 +2257,7 @@ public:
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opCompareNN(lhs, rhs, m_ignoreCase);
|
||||
}
|
||||
string name() const override { return m_ignoreCase ? "icompare" : "compare"; }
|
||||
string name() const override VL_MT_STABLE { return m_ignoreCase ? "icompare" : "compare"; }
|
||||
string emitVerilog() override {
|
||||
return m_ignoreCase ? "%k(%l.icompare(%r))" : "%k(%l.compare(%r))";
|
||||
}
|
||||
@@ -2364,6 +2462,7 @@ public:
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() * 64; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
AstNode* filep() const { return lhsp(); }
|
||||
AstNode* charp() const { return rhsp(); }
|
||||
@@ -2382,7 +2481,7 @@ public:
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opGetcN(lhs, rhs);
|
||||
}
|
||||
string name() const override { return "getc"; }
|
||||
string name() const override VL_MT_STABLE { return "getc"; }
|
||||
string emitVerilog() override { return "%k(%l.getc(%r))"; }
|
||||
string emitC() override { return "VL_GETC_N(%li,%ri)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
@@ -3946,16 +4045,19 @@ public:
|
||||
// === AstNodeCCall ===
|
||||
class AstCCall final : public AstNodeCCall {
|
||||
// C++ function call
|
||||
string m_selfPointer; // Output code object pointer (e.g.: 'this')
|
||||
|
||||
VSelfPointerText m_selfPointer
|
||||
= VSelfPointerText{VSelfPointerText::Empty()}; // Output code object
|
||||
// pointer (e.g.: 'this')
|
||||
public:
|
||||
AstCCall(FileLine* fl, AstCFunc* funcp, AstNodeExpr* argsp = nullptr)
|
||||
: ASTGEN_SUPER_CCall(fl, funcp, argsp) {}
|
||||
ASTGEN_MEMBERS_AstCCall;
|
||||
|
||||
string selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const string& value) { m_selfPointer = value; }
|
||||
string selfPointerProtect(bool useSelfForThis) const;
|
||||
const VSelfPointerText& selfPointer() const { return m_selfPointer; }
|
||||
void selfPointer(const VSelfPointerText& selfPointer) { m_selfPointer = selfPointer; }
|
||||
string selfPointerProtect(bool useSelfForThis) const {
|
||||
return selfPointer().protect(useSelfForThis, protect());
|
||||
}
|
||||
};
|
||||
class AstCMethodCall final : public AstNodeCCall {
|
||||
// C++ method call
|
||||
@@ -4012,7 +4114,6 @@ public:
|
||||
BROKEN_RTN(!fromp());
|
||||
return nullptr;
|
||||
}
|
||||
void dump(std::ostream& str) const override;
|
||||
};
|
||||
class AstNew final : public AstNodeFTaskRef {
|
||||
// New as constructor
|
||||
@@ -4263,7 +4364,7 @@ public:
|
||||
const V3Number& ths) override {
|
||||
out.opPutcN(lhs, rhs, ths);
|
||||
}
|
||||
string name() const override { return "putc"; }
|
||||
string name() const override VL_MT_STABLE { return "putc"; }
|
||||
string emitVerilog() override { return "%k(%l.putc(%r,%t))"; }
|
||||
string emitC() override { return "VL_PUTC_N(%li,%ri,%ti)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
@@ -4370,7 +4471,7 @@ public:
|
||||
const V3Number& ths) override {
|
||||
out.opSubstrN(lhs, rhs, ths);
|
||||
}
|
||||
string name() const override { return "substr"; }
|
||||
string name() const override VL_MT_STABLE { return "substr"; }
|
||||
string emitVerilog() override { return "%k(%l.substr(%r,%t))"; }
|
||||
string emitC() override { return "VL_SUBSTR_N(%li,%ri,%ti)"; }
|
||||
string emitSimpleOperator() override { return ""; }
|
||||
@@ -4447,7 +4548,7 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstAtoN;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAtoN(lhs, m_fmt); }
|
||||
string name() const override {
|
||||
string name() const override VL_MT_STABLE {
|
||||
switch (m_fmt) {
|
||||
case ATOI: return "atoi";
|
||||
case ATOHEX: return "atohex";
|
||||
@@ -4596,7 +4697,7 @@ public:
|
||||
ASTGEN_MEMBERS_AstCvtPackString;
|
||||
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
|
||||
string emitVerilog() override { return "%f$_CAST(%l)"; }
|
||||
string emitC() override { return "VL_CVT_PACK_STR_N%lq(%lW, %li)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
@@ -5274,15 +5375,15 @@ public:
|
||||
class AstVarRef final : public AstNodeVarRef {
|
||||
// A reference to a variable (lvalue or rvalue)
|
||||
public:
|
||||
AstVarRef(FileLine* fl, const string& name, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, name, nullptr, access) {}
|
||||
// This form only allowed post-link because output/wire compression may
|
||||
// lead to deletion of AstVar's
|
||||
inline AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access);
|
||||
// This form only allowed post-link (see above)
|
||||
inline AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access);
|
||||
ASTGEN_MEMBERS_AstVarRef;
|
||||
inline string name() const override; // * = Var name
|
||||
void dump(std::ostream& str) const override;
|
||||
const char* broken() const override;
|
||||
bool same(const AstNode* samep) const override;
|
||||
inline bool same(const AstVarRef* samep) const;
|
||||
inline bool sameNoLvalue(AstVarRef* samep) const;
|
||||
@@ -5294,14 +5395,17 @@ public:
|
||||
class AstVarXRef final : public AstNodeVarRef {
|
||||
// A VarRef to something in another module before AstScope.
|
||||
// Includes pin on a cell, as part of a ASSIGN statement to connect I/Os until AstScope
|
||||
string m_name;
|
||||
string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
|
||||
string m_inlinedDots; // Dotted hierarchy flattened out
|
||||
public:
|
||||
AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, name, nullptr, access)
|
||||
: ASTGEN_SUPER_VarXRef(fl, nullptr, access)
|
||||
, m_name{name}
|
||||
, m_dotted{dotted} {}
|
||||
inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access);
|
||||
ASTGEN_MEMBERS_AstVarXRef;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void dump(std::ostream& str) const override;
|
||||
string dotted() const { return m_dotted; }
|
||||
void dotted(const string& dotted) { m_dotted = dotted; }
|
||||
|
||||
+172
-141
@@ -32,7 +32,7 @@
|
||||
|
||||
class AstNodeBlock VL_NOT_FINAL : public AstNode {
|
||||
// A Begin/fork block
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
// @astgen op2 := stmtsp : List[AstNode]
|
||||
// Parents: statement
|
||||
private:
|
||||
string m_name; // Name of block
|
||||
@@ -48,7 +48,7 @@ protected:
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstNodeBlock;
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override { return m_name; } // * = Block name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Block name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
bool unnamed() const { return m_unnamed; }
|
||||
bool isFirstInMyListOfStatements(AstNode* nodep) const override { return nodep == stmtsp(); }
|
||||
@@ -81,11 +81,12 @@ private:
|
||||
bool m_isConstructor : 1; // Class constructor
|
||||
bool m_isHideLocal : 1; // Verilog local
|
||||
bool m_isHideProtected : 1; // Verilog protected
|
||||
bool m_pure : 1; // DPI import pure (vs. virtual pure)
|
||||
bool m_dpiPure : 1; // DPI import pure (vs. virtual pure)
|
||||
bool m_pureVirtual : 1; // Pure virtual
|
||||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
bool m_underGenerate : 1; // Under generate (for warning)
|
||||
bool m_virtual : 1; // Virtual method in class
|
||||
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
protected:
|
||||
AstNodeFTask(VNType t, FileLine* fl, const string& name, AstNode* stmtsp)
|
||||
@@ -106,21 +107,25 @@ protected:
|
||||
, m_isConstructor{false}
|
||||
, m_isHideLocal{false}
|
||||
, m_isHideProtected{false}
|
||||
, m_pure{false}
|
||||
, m_dpiPure{false}
|
||||
, m_pureVirtual{false}
|
||||
, m_recursive{false}
|
||||
, m_underGenerate{false}
|
||||
, m_virtual{false} {
|
||||
, m_virtual{false}
|
||||
, m_needProcess{false} {
|
||||
addStmtsp(stmtsp);
|
||||
cname(name); // Might be overridden by dpi import/export
|
||||
}
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstNodeFTask;
|
||||
virtual AstNodeFTask* cloneType(const string& name) = 0;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
string name() const override { return m_name; } // * = Var name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool isGateOptimizable() const override { return !((m_dpiExport || m_dpiImport) && !m_pure); }
|
||||
bool isGateOptimizable() const override {
|
||||
return !((m_dpiExport || m_dpiImport) && !m_dpiPure);
|
||||
}
|
||||
// {AstFunc only} op1 = Range output variable
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string cname() const { return m_cname; }
|
||||
@@ -160,8 +165,8 @@ public:
|
||||
void isHideLocal(bool flag) { m_isHideLocal = flag; }
|
||||
bool isHideProtected() const { return m_isHideProtected; }
|
||||
void isHideProtected(bool flag) { m_isHideProtected = flag; }
|
||||
void pure(bool flag) { m_pure = flag; }
|
||||
bool pure() const { return m_pure; }
|
||||
void dpiPure(bool flag) { m_dpiPure = flag; }
|
||||
bool dpiPure() const { return m_dpiPure; }
|
||||
void pureVirtual(bool flag) { m_pureVirtual = flag; }
|
||||
bool pureVirtual() const { return m_pureVirtual; }
|
||||
void recursive(bool flag) { m_recursive = flag; }
|
||||
@@ -170,9 +175,20 @@ public:
|
||||
bool underGenerate() const { return m_underGenerate; }
|
||||
void isVirtual(bool flag) { m_virtual = flag; }
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
void propagateAttrFrom(const AstNodeFTask* fromp) {
|
||||
// Creating a wrapper with e.g. cloneType(); preserve some attributes
|
||||
classMethod(fromp->classMethod());
|
||||
isHideLocal(fromp->isHideLocal());
|
||||
isHideProtected(fromp->isHideProtected());
|
||||
isVirtual(fromp->isVirtual());
|
||||
lifetime(fromp->lifetime());
|
||||
underGenerate(fromp->underGenerate());
|
||||
}
|
||||
};
|
||||
class AstNodeFile VL_NOT_FINAL : public AstNode {
|
||||
// Emitted Output file
|
||||
@@ -186,7 +202,7 @@ public:
|
||||
, m_name{name} {}
|
||||
ASTGEN_MEMBERS_AstNodeFile;
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstNodeModule VL_NOT_FINAL : public AstNode {
|
||||
@@ -234,7 +250,7 @@ public:
|
||||
ASTGEN_MEMBERS_AstNodeModule;
|
||||
void dump(std::ostream& str) const override;
|
||||
bool maybePointedTo() const override { return true; }
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
virtual bool timescaleMatters() const = 0;
|
||||
// ACCESSORS
|
||||
void name(const string& name) override { m_name = name; }
|
||||
@@ -272,10 +288,13 @@ public:
|
||||
class AstNodeProcedure VL_NOT_FINAL : public AstNode {
|
||||
// IEEE procedure: initial, final, always
|
||||
// @astgen op2 := stmtsp : List[AstNode] // Note: op1 is used in some sub-types only
|
||||
bool m_suspendable = false; // Is suspendable by a Delay, EventControl, etc.
|
||||
bool m_suspendable : 1; // Is suspendable by a Delay, EventControl, etc.
|
||||
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
||||
protected:
|
||||
AstNodeProcedure(VNType t, FileLine* fl, AstNode* stmtsp)
|
||||
: AstNode{t, fl} {
|
||||
m_needProcess = false;
|
||||
m_suspendable = false;
|
||||
addStmtsp(stmtsp);
|
||||
}
|
||||
|
||||
@@ -286,6 +305,8 @@ public:
|
||||
bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); }
|
||||
bool isSuspendable() const { return m_suspendable; }
|
||||
void setSuspendable() { m_suspendable = true; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
};
|
||||
class AstNodeRange VL_NOT_FINAL : public AstNode {
|
||||
// A range, sized or unsized
|
||||
@@ -308,8 +329,6 @@ public:
|
||||
// METHODS
|
||||
void addNextStmt(AstNode* newp,
|
||||
AstNode* belowp) override; // Stop statement searchback here
|
||||
void addBeforeStmt(AstNode* newp,
|
||||
AstNode* belowp) override; // Stop statement searchback here
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
};
|
||||
class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt {
|
||||
@@ -373,7 +392,7 @@ public:
|
||||
this->addPasssp(passsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstNodeCoverOrAssert;
|
||||
string name() const override { return m_name; } // * = Var name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
bool same(const AstNode* samep) const override { return samep->name() == name(); }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
@@ -512,7 +531,7 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstActive;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override;
|
||||
// Statements are broken into pieces, as some must come before others.
|
||||
@@ -537,7 +556,7 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstBind;
|
||||
// ACCESSORS
|
||||
string name() const override { return m_name; } // * = Bind Target name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Bind Target name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
};
|
||||
class AstCFunc final : public AstNode {
|
||||
@@ -571,13 +590,14 @@ private:
|
||||
bool m_isInline : 1; // Inline function
|
||||
bool m_isVirtual : 1; // Virtual function
|
||||
bool m_entryPoint : 1; // User may call into this top level function
|
||||
bool m_pure : 1; // Pure function
|
||||
bool m_dpiPure : 1; // Pure DPI function
|
||||
bool m_dpiContext : 1; // Declared as 'context' DPI import/export function
|
||||
bool m_dpiExportDispatcher : 1; // This is the DPI export entry point (i.e.: called by user)
|
||||
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
|
||||
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
|
||||
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
|
||||
bool m_dpiTraceInit : 1; // DPI trace_init
|
||||
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
||||
public:
|
||||
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
|
||||
: ASTGEN_SUPER_CFunc(fl) {
|
||||
@@ -597,8 +617,9 @@ public:
|
||||
m_isLoose = false;
|
||||
m_isInline = false;
|
||||
m_isVirtual = false;
|
||||
m_needProcess = false;
|
||||
m_entryPoint = false;
|
||||
m_pure = false;
|
||||
m_dpiPure = false;
|
||||
m_dpiContext = false;
|
||||
m_dpiExportDispatcher = false;
|
||||
m_dpiExportImpl = false;
|
||||
@@ -607,7 +628,7 @@ public:
|
||||
m_dpiTraceInit = false;
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCFunc;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override;
|
||||
bool maybePointedTo() const override { return true; }
|
||||
@@ -665,10 +686,12 @@ public:
|
||||
void isInline(bool flag) { m_isInline = flag; }
|
||||
bool isVirtual() const { return m_isVirtual; }
|
||||
void isVirtual(bool flag) { m_isVirtual = flag; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool entryPoint() const { return m_entryPoint; }
|
||||
void entryPoint(bool flag) { m_entryPoint = flag; }
|
||||
bool pure() const { return m_pure; }
|
||||
void pure(bool flag) { m_pure = flag; }
|
||||
bool dpiPure() const { return m_dpiPure; }
|
||||
void dpiPure(bool flag) { m_dpiPure = flag; }
|
||||
bool dpiContext() const { return m_dpiContext; }
|
||||
void dpiContext(bool flag) { m_dpiContext = flag; }
|
||||
bool dpiExportDispatcher() const VL_MT_SAFE { return m_dpiExportDispatcher; }
|
||||
@@ -688,6 +711,16 @@ public:
|
||||
&& finalsp() == nullptr;
|
||||
}
|
||||
};
|
||||
class AstCLocalScope final : public AstNode {
|
||||
// Pack statements into an unnamed scope when generating C++
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
public:
|
||||
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
|
||||
: ASTGEN_SUPER_CLocalScope(fl) {
|
||||
this->addStmtsp(stmtsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCLocalScope;
|
||||
};
|
||||
class AstCUse final : public AstNode {
|
||||
// C++ use of a class or #include; indicates need of forward declaration
|
||||
// Parents: NODEMODULE
|
||||
@@ -702,7 +735,7 @@ public:
|
||||
, m_useType{useType} {}
|
||||
ASTGEN_MEMBERS_AstCUse;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
VUseType useType() const { return m_useType; }
|
||||
};
|
||||
class AstCaseItem final : public AstNode {
|
||||
@@ -755,7 +788,7 @@ public:
|
||||
const char* broken() const override;
|
||||
bool maybePointedTo() const override { return true; }
|
||||
// ACCESSORS
|
||||
string name() const override { return m_name; } // * = Cell name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Cell name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string origName() const override { return m_origName; } // * = Original name
|
||||
void origName(const string& name) { m_origName = name; }
|
||||
@@ -794,7 +827,7 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
const char* broken() const override;
|
||||
// ACCESSORS
|
||||
string name() const override { return m_name; } // * = Cell name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Cell name
|
||||
string origModName() const { return m_origModName; } // * = modp()->origName() before inlining
|
||||
void name(const string& name) override { m_name = name; }
|
||||
void scopep(AstScope* scp) { m_scopep = scp; }
|
||||
@@ -809,6 +842,8 @@ class AstClassExtends final : public AstNode {
|
||||
// @astgen op1 := childDTypep : Optional[AstNodeDType]
|
||||
// @astgen op2 := classOrPkgsp : Optional[AstNode]
|
||||
const bool m_isImplements = false; // class implements
|
||||
bool m_parameterized = false; // has parameters in its statement
|
||||
|
||||
public:
|
||||
AstClassExtends(FileLine* fl, AstNode* classOrPkgsp, bool isImplements)
|
||||
: ASTGEN_SUPER_ClassExtends(fl)
|
||||
@@ -819,8 +854,12 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
bool hasDType() const override { return true; }
|
||||
string verilogKwd() const override { return isImplements() ? "implements" : "extends"; }
|
||||
AstClass* classp() const; // Class being extended (after link)
|
||||
// Class being extended (after link and instantiation if needed)
|
||||
AstClass* classOrNullp() const;
|
||||
AstClass* classp() const; // Like above, but throws error if nulll
|
||||
bool isImplements() const { return m_isImplements; }
|
||||
void parameterized(bool flag) { m_parameterized = flag; }
|
||||
bool parameterized() const { return m_parameterized; }
|
||||
};
|
||||
class AstClocking final : public AstNode {
|
||||
// Parents: MODULE
|
||||
@@ -845,7 +884,7 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstClocking;
|
||||
void dump(std::ostream& str) const override;
|
||||
std::string name() const override { return m_name; }
|
||||
std::string name() const override VL_MT_STABLE { return m_name; }
|
||||
bool isDefault() const { return m_isDefault; }
|
||||
bool isGlobal() const { return m_isGlobal; }
|
||||
};
|
||||
@@ -863,8 +902,8 @@ public:
|
||||
: ASTGEN_SUPER_ClockingItem(fl) {
|
||||
m_direction = direction;
|
||||
this->skewp(skewp);
|
||||
if (AstAssign* const assignp = VN_CAST(clockingDeclp, Assign)) {
|
||||
this->assignp(assignp);
|
||||
if (AstAssign* const clkAssignp = VN_CAST(clockingDeclp, Assign)) {
|
||||
this->assignp(clkAssignp);
|
||||
} else {
|
||||
exprp(VN_AS(clockingDeclp, NodeExpr));
|
||||
}
|
||||
@@ -916,7 +955,7 @@ public:
|
||||
, m_path{path} {
|
||||
this->rhsp(rhsp);
|
||||
}
|
||||
string name() const override { return m_name; } // * = Scope name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
|
||||
ASTGEN_MEMBERS_AstDefParam;
|
||||
bool same(const AstNode*) const override { return true; }
|
||||
string path() const { return m_path; }
|
||||
@@ -934,7 +973,7 @@ public:
|
||||
, m_name{vname}
|
||||
, m_cname{cname} {}
|
||||
ASTGEN_MEMBERS_AstDpiExport;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string cname() const { return m_cname; }
|
||||
void cname(const string& cname) { m_cname = cname; }
|
||||
@@ -952,7 +991,7 @@ public:
|
||||
BROKEN_RTN(!fmtp());
|
||||
return nullptr;
|
||||
}
|
||||
string verilogKwd() const override { return (string("$") + string(displayType().ascii())); }
|
||||
string verilogKwd() const override { return string{"$"} + string{displayType().ascii()}; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; } // SPECIAL: $display has 'visual' ordering
|
||||
@@ -995,7 +1034,7 @@ public:
|
||||
BROKEN_RTN(!m_depGraphp);
|
||||
return nullptr;
|
||||
}
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
V3Graph* depGraphp() { return m_depGraphp; }
|
||||
const V3Graph* depGraphp() const { return m_depGraphp; }
|
||||
};
|
||||
@@ -1033,7 +1072,7 @@ public:
|
||||
AstIntfRef(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_IntfRef(fl)
|
||||
, m_name{name} {}
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
ASTGEN_MEMBERS_AstIntfRef;
|
||||
};
|
||||
class AstMTaskBody final : public AstNode {
|
||||
@@ -1070,7 +1109,7 @@ public:
|
||||
, m_name{name} {
|
||||
this->addVarsp(varsp);
|
||||
}
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
ASTGEN_MEMBERS_AstModport;
|
||||
};
|
||||
@@ -1092,7 +1131,7 @@ public:
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override;
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
bool isImport() const { return !m_export; }
|
||||
bool isExport() const { return m_export; }
|
||||
AstNodeFTask* ftaskp() const { return m_ftaskp; } // [After Link] Pointer to variable
|
||||
@@ -1115,7 +1154,7 @@ public:
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override;
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void direction(const VDirection& flag) { m_direction = flag; }
|
||||
VDirection direction() const { return m_direction; }
|
||||
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
|
||||
@@ -1149,9 +1188,9 @@ public:
|
||||
ASTGEN_MEMBERS_AstNetlist;
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override { V3ERROR_NA; }
|
||||
string name() const override { return "$root"; }
|
||||
string name() const override VL_MT_STABLE { return "$root"; }
|
||||
void dump(std::ostream& str) const override;
|
||||
AstNodeModule* topModulep() const { // Top module in hierarchy
|
||||
AstNodeModule* topModulep() const VL_MT_STABLE { // Top module in hierarchy
|
||||
return modulesp(); // First one in the list, for now
|
||||
}
|
||||
AstTypeTable* typeTablep() { return m_typeTablep; }
|
||||
@@ -1198,7 +1237,7 @@ public:
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override;
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
AstPackage* packagep() const { return m_packagep; }
|
||||
void packagep(AstPackage* nodep) { m_packagep = nodep; }
|
||||
};
|
||||
@@ -1224,7 +1263,7 @@ public:
|
||||
const char* broken() const override;
|
||||
void cloneRelink() override;
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
AstPackage* packagep() const { return m_packagep; }
|
||||
void packagep(AstPackage* nodep) { m_packagep = nodep; }
|
||||
};
|
||||
@@ -1250,7 +1289,7 @@ public:
|
||||
ASTGEN_MEMBERS_AstPin;
|
||||
void dump(std::ostream& str) const override;
|
||||
const char* broken() const override;
|
||||
string name() const override { return m_name; } // * = Pin name, ""=go by number
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Pin name, ""=go by number
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string prettyOperatorName() const override;
|
||||
bool dotStar() const { return name() == ".*"; } // Fake name for .* connections until linked
|
||||
@@ -1278,7 +1317,7 @@ public:
|
||||
, m_pinNum{pinnum}
|
||||
, m_name{name} {}
|
||||
ASTGEN_MEMBERS_AstPort;
|
||||
string name() const override { return m_name; } // * = Port name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Port name
|
||||
int pinNum() const { return m_pinNum; } // * = Pin number, for order based instantiation
|
||||
};
|
||||
class AstPragma final : public AstNode {
|
||||
@@ -1356,11 +1395,11 @@ public:
|
||||
void cloneRelink() override;
|
||||
const char* broken() const override;
|
||||
bool maybePointedTo() const override { return true; }
|
||||
string name() const override { return m_name; } // * = Scope name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
|
||||
void name(const string& name) override { m_name = name; }
|
||||
void dump(std::ostream& str) const override;
|
||||
bool same(const AstNode* samep) const override;
|
||||
string nameDotless() const;
|
||||
string nameVlSym() const { return ((string("vlSymsp->")) + nameDotless()); }
|
||||
AstNodeModule* modp() const { return m_modp; }
|
||||
//
|
||||
AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; }
|
||||
@@ -1495,6 +1534,7 @@ class AstTypeTable final : public AstNode {
|
||||
AstEmptyQueueDType* m_emptyQueuep = nullptr;
|
||||
AstQueueDType* m_queueIndexp = nullptr;
|
||||
AstVoidDType* m_voidp = nullptr;
|
||||
AstStreamDType* m_streamp = nullptr;
|
||||
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
|
||||
//
|
||||
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
|
||||
@@ -1520,6 +1560,7 @@ public:
|
||||
AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl);
|
||||
AstQueueDType* findQueueIndexDType(FileLine* fl);
|
||||
AstVoidDType* findVoidDType(FileLine* fl);
|
||||
AstStreamDType* findStreamDType(FileLine* fl);
|
||||
void clearCache();
|
||||
void repairCache();
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
@@ -1544,9 +1585,11 @@ public:
|
||||
ASTGEN_MEMBERS_AstTypedef;
|
||||
void dump(std::ostream& str) const override;
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
|
||||
virtual AstNodeDType* subDTypep() const VL_MT_STABLE {
|
||||
return dtypep() ? dtypep() : childDTypep();
|
||||
}
|
||||
// METHODS
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool hasDType() const override { return true; }
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
@@ -1566,7 +1609,7 @@ public:
|
||||
, m_name{name} {}
|
||||
ASTGEN_MEMBERS_AstTypedefFwd;
|
||||
// METHODS
|
||||
string name() const override { return m_name; }
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
};
|
||||
class AstUdpTable final : public AstNode {
|
||||
@@ -1586,7 +1629,7 @@ public:
|
||||
: ASTGEN_SUPER_UdpTableLine(fl)
|
||||
, m_text{text} {}
|
||||
ASTGEN_MEMBERS_AstUdpTableLine;
|
||||
string name() const override { return m_text; }
|
||||
string name() const override VL_MT_STABLE { return m_text; }
|
||||
string text() const { return m_text; }
|
||||
};
|
||||
class AstVar final : public AstNode {
|
||||
@@ -1626,6 +1669,7 @@ class AstVar final : public AstNode {
|
||||
bool m_usedLoopIdx : 1; // Variable subject of for unrolling
|
||||
bool m_usedVirtIface : 1; // Signal used through a virtual interface
|
||||
bool m_funcLocal : 1; // Local variable for a function
|
||||
bool m_funcLocalSticky : 1; // As m_funcLocal but remains set if var is moved to a static
|
||||
bool m_funcReturn : 1; // Return variable for a function
|
||||
bool m_attrScBv : 1; // User force bit vector attribute
|
||||
bool m_attrIsolateAssign : 1; // User isolate_assignments attribute
|
||||
@@ -1669,6 +1713,7 @@ class AstVar final : public AstNode {
|
||||
m_sigUserRdPublic = false;
|
||||
m_sigUserRWPublic = false;
|
||||
m_funcLocal = false;
|
||||
m_funcLocalSticky = false;
|
||||
m_funcReturn = false;
|
||||
m_attrScBv = false;
|
||||
m_attrIsolateAssign = false;
|
||||
@@ -1756,7 +1801,8 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstVar;
|
||||
void dump(std::ostream& str) const override;
|
||||
string name() const override VL_MT_SAFE { return m_name; } // * = Var name
|
||||
bool same(const AstNode* samep) const override;
|
||||
string name() const override VL_MT_STABLE VL_MT_SAFE { return m_name; } // * = Var name
|
||||
bool hasDType() const override { return true; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
string origName() const override { return m_origName; } // * = Original name
|
||||
@@ -1787,17 +1833,17 @@ public:
|
||||
string dpiTmpVarType(const string& varName) const;
|
||||
// Return Verilator internal type for argument: CData, SData, IData, WData
|
||||
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
|
||||
bool asRef = false) const VL_MT_SAFE;
|
||||
bool asRef = false) const;
|
||||
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
|
||||
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
||||
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
|
||||
void combineType(VVarType type);
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
AstNodeDType* dtypeSkipRefp() const VL_MT_SAFE { return subDTypep()->skipRefp(); }
|
||||
AstNodeDType* dtypeSkipRefp() const VL_MT_STABLE { return subDTypep()->skipRefp(); }
|
||||
// (Slow) recurse down to find basic data type (Note don't need virtual -
|
||||
// AstVar isn't a NodeDType)
|
||||
AstBasicDType* basicp() const VL_MT_SAFE { return subDTypep()->basicp(); }
|
||||
virtual AstNodeDType* subDTypep() const VL_MT_SAFE {
|
||||
AstBasicDType* basicp() const VL_MT_STABLE { return subDTypep()->basicp(); }
|
||||
virtual AstNodeDType* subDTypep() const VL_MT_STABLE {
|
||||
return dtypep() ? dtypep() : childDTypep();
|
||||
}
|
||||
void ansi(bool flag) { m_ansi = flag; }
|
||||
@@ -1831,7 +1877,10 @@ public:
|
||||
void isContinuously(bool flag) { m_isContinuously = flag; }
|
||||
void isStatic(bool flag) { m_isStatic = flag; }
|
||||
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
|
||||
void funcLocal(bool flag) { m_funcLocal = flag; }
|
||||
void funcLocal(bool flag) {
|
||||
m_funcLocal = flag;
|
||||
if (flag) m_funcLocalSticky = true;
|
||||
}
|
||||
void funcReturn(bool flag) { m_funcReturn = flag; }
|
||||
void hasStrengthAssignment(bool flag) { m_hasStrengthAssignment = flag; }
|
||||
bool hasStrengthAssignment() { return m_hasStrengthAssignment; }
|
||||
@@ -1884,9 +1933,7 @@ public:
|
||||
bool isClassMember() const { return varType() == VVarType::MEMBER; }
|
||||
bool isStatementTemp() const { return (varType() == VVarType::STMTTEMP); }
|
||||
bool isXTemp() const { return (varType() == VVarType::XTEMP); }
|
||||
bool isParam() const VL_MT_SAFE {
|
||||
return (varType() == VVarType::LPARAM || varType() == VVarType::GPARAM);
|
||||
}
|
||||
bool isParam() const { return varType().isParam(); }
|
||||
bool isGParam() const { return (varType() == VVarType::GPARAM); }
|
||||
bool isGenVar() const { return (varType() == VVarType::GENVAR); }
|
||||
bool isBitLogic() const {
|
||||
@@ -1913,6 +1960,7 @@ public:
|
||||
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
|
||||
bool isLatched() const { return m_isLatched; }
|
||||
bool isFuncLocal() const { return m_funcLocal; }
|
||||
bool isFuncLocalSticky() const { return m_funcLocalSticky; }
|
||||
bool isFuncReturn() const { return m_funcReturn; }
|
||||
bool isPullup() const { return m_isPullup; }
|
||||
bool isPulldown() const { return m_isPulldown; }
|
||||
@@ -1926,18 +1974,25 @@ public:
|
||||
string verilogKwd() const override;
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
void propagateAttrFrom(AstVar* fromp) {
|
||||
void propagateAttrFrom(const AstVar* fromp) {
|
||||
// This is getting connected to fromp; keep attributes
|
||||
// Note the method below too
|
||||
if (fromp->attrFileDescr()) attrFileDescr(true);
|
||||
if (fromp->attrIsolateAssign()) attrIsolateAssign(true);
|
||||
if (fromp->isContinuously()) isContinuously(true);
|
||||
}
|
||||
void propagateWrapAttrFrom(const AstVar* fromp) {
|
||||
// Creating a function wrapper; keep attributes
|
||||
propagateAttrFrom(fromp);
|
||||
direction(fromp->direction());
|
||||
declDirection(fromp->declDirection());
|
||||
lifetime(fromp->lifetime());
|
||||
}
|
||||
bool gateMultiInputOptimizable() const {
|
||||
// Ok to gate optimize; must return false if propagateAttrFrom would do anything
|
||||
return !isUsedClock();
|
||||
}
|
||||
void combineType(AstVar* typevarp) {
|
||||
void combineType(const AstVar* typevarp) {
|
||||
// This is same as typevarp (for combining input & reg decls)
|
||||
// "this" is the input var. typevarp is the reg var.
|
||||
propagateAttrFrom(typevarp);
|
||||
@@ -1996,11 +2051,12 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
bool maybePointedTo() const override { return true; }
|
||||
string name() const override { return scopep()->name() + "->" + varp()->name(); }
|
||||
string name() const override VL_MT_STABLE { return scopep()->name() + "->" + varp()->name(); }
|
||||
void dump(std::ostream& str) const override;
|
||||
bool same(const AstNode* samep) const override;
|
||||
bool hasDType() const override { return true; }
|
||||
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
|
||||
AstScope* scopep() const { return m_scopep; } // Pointer to scope it's under
|
||||
AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable
|
||||
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
|
||||
void scopep(AstScope* nodep) { m_scopep = nodep; }
|
||||
bool isTrace() const { return m_trace; }
|
||||
void trace(bool flag) { m_trace = flag; }
|
||||
@@ -2010,7 +2066,7 @@ public:
|
||||
class AstBegin final : public AstNodeBlock {
|
||||
// A Begin/end named block, only exists shortly after parsing until linking
|
||||
// Parents: statement
|
||||
// @astgen op2 := genforp : Optional[AstNode]
|
||||
// @astgen op1 := genforp : Optional[AstNode]
|
||||
|
||||
bool m_generate; // Underneath a generate
|
||||
const bool m_implied; // Not inserted by user
|
||||
@@ -2029,6 +2085,7 @@ public:
|
||||
};
|
||||
class AstFork final : public AstNodeBlock {
|
||||
// A fork named block
|
||||
// @astgen op1 := initsp : List[AstNode]
|
||||
// Parents: statement
|
||||
// Children: statements
|
||||
private:
|
||||
@@ -2054,6 +2111,24 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstFunc;
|
||||
bool hasDType() const override { return true; }
|
||||
AstNodeFTask* cloneType(const string& name) override {
|
||||
return new AstFunc{fileline(), name, nullptr, nullptr};
|
||||
}
|
||||
};
|
||||
class AstLet final : public AstNodeFTask {
|
||||
// Verilog "let" statement
|
||||
// Parents: MODULE
|
||||
// stmtp is always a StmtExpr as Let always returns AstNodeExpr
|
||||
public:
|
||||
AstLet(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_Let(fl, name, nullptr) {}
|
||||
ASTGEN_MEMBERS_AstLet;
|
||||
bool hasDType() const override { return true; }
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(!VN_IS(stmtsp(), StmtExpr));
|
||||
return nullptr;
|
||||
}
|
||||
AstNodeFTask* cloneType(const string& name) override { return new AstLet{fileline(), name}; }
|
||||
};
|
||||
class AstProperty final : public AstNodeFTask {
|
||||
// A property inside a module
|
||||
@@ -2062,6 +2137,9 @@ public:
|
||||
: ASTGEN_SUPER_Property(fl, name, stmtp) {}
|
||||
ASTGEN_MEMBERS_AstProperty;
|
||||
bool hasDType() const override { return true; }
|
||||
AstNodeFTask* cloneType(const string& name) override {
|
||||
return new AstProperty{fileline(), name, nullptr};
|
||||
}
|
||||
};
|
||||
class AstTask final : public AstNodeFTask {
|
||||
// A task inside a module
|
||||
@@ -2069,6 +2147,9 @@ public:
|
||||
AstTask(FileLine* fl, const string& name, AstNode* stmtp)
|
||||
: ASTGEN_SUPER_Task(fl, name, stmtp) {}
|
||||
ASTGEN_MEMBERS_AstTask;
|
||||
AstNodeFTask* cloneType(const string& name) override {
|
||||
return new AstTask{fileline(), name, nullptr};
|
||||
}
|
||||
};
|
||||
|
||||
// === AstNodeFile ===
|
||||
@@ -2107,15 +2188,13 @@ public:
|
||||
// === AstNodeModule ===
|
||||
class AstClass final : public AstNodeModule {
|
||||
// @astgen op4 := extendsp : List[AstClassExtends]
|
||||
// TYPES
|
||||
using MemberNameMap = std::map<const std::string, AstNode*>;
|
||||
// MEMBERS
|
||||
MemberNameMap m_members; // Members or method children
|
||||
AstClassPackage* m_classOrPackagep = nullptr; // Class package this is under
|
||||
AstClassPackage* m_classOrPackagep = nullptr; // Package it will be emitted with
|
||||
bool m_extended = false; // Is extension or extended by other classes
|
||||
bool m_interfaceClass = false; // Interface class
|
||||
bool m_needRNG = false; // Need RNG, uses srandom/randomize
|
||||
bool m_virtual = false; // Virtual class
|
||||
void insertCache(AstNode* nodep);
|
||||
bool m_parameterized = false; // Parameterized class
|
||||
|
||||
public:
|
||||
AstClass(FileLine* fl, const string& name)
|
||||
@@ -2130,25 +2209,23 @@ public:
|
||||
AstClassPackage* classOrPackagep() const VL_MT_SAFE { return m_classOrPackagep; }
|
||||
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
|
||||
AstNode* membersp() const { return stmtsp(); }
|
||||
void addMembersp(AstNode* nodep) {
|
||||
insertCache(nodep);
|
||||
addStmtsp(nodep);
|
||||
}
|
||||
void clearCache() { m_members.clear(); }
|
||||
void repairCache();
|
||||
AstNode* findMember(const string& name) const {
|
||||
const auto it = m_members.find(name);
|
||||
return (it == m_members.end()) ? nullptr : it->second;
|
||||
}
|
||||
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
|
||||
bool isExtended() const { return m_extended; }
|
||||
void isExtended(bool flag) { m_extended = flag; }
|
||||
bool isInterfaceClass() const { return m_interfaceClass; }
|
||||
void isInterfaceClass(bool flag) { m_interfaceClass = flag; }
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
void isVirtual(bool flag) { m_virtual = flag; }
|
||||
bool needRNG() const { return m_needRNG; }
|
||||
void needRNG(bool flag) { m_needRNG = flag; }
|
||||
bool isParameterized() const { return m_parameterized; }
|
||||
void isParameterized(bool flag) { m_parameterized = flag; }
|
||||
// Return true if this class is an extension of base class (SLOW)
|
||||
// Accepts nullptrs
|
||||
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
|
||||
// Return the lowest class extended from, or this class
|
||||
AstClass* baseMostClassp();
|
||||
static bool isCacheableChild(const AstNode* nodep);
|
||||
};
|
||||
class AstClassPackage final : public AstNodeModule {
|
||||
// The static information portion of a class (treated similarly to a package)
|
||||
@@ -2334,20 +2411,20 @@ public:
|
||||
inline AstRange(FileLine* fl, int left, int right);
|
||||
inline AstRange(FileLine* fl, const VNumRange& range);
|
||||
ASTGEN_MEMBERS_AstRange;
|
||||
inline int leftConst() const VL_MT_SAFE;
|
||||
inline int rightConst() const VL_MT_SAFE;
|
||||
int hiConst() const VL_MT_SAFE {
|
||||
inline int leftConst() const VL_MT_STABLE;
|
||||
inline int rightConst() const VL_MT_STABLE;
|
||||
int hiConst() const VL_MT_STABLE {
|
||||
const int l = leftConst();
|
||||
const int r = rightConst();
|
||||
return l > r ? l : r;
|
||||
}
|
||||
int loConst() const VL_MT_SAFE {
|
||||
int loConst() const VL_MT_STABLE {
|
||||
const int l = leftConst();
|
||||
const int r = rightConst();
|
||||
return l > r ? r : l;
|
||||
}
|
||||
int elementsConst() const VL_MT_SAFE { return hiConst() - loConst() + 1; }
|
||||
bool littleEndian() const { return leftConst() < rightConst(); }
|
||||
int elementsConst() const VL_MT_STABLE { return hiConst() - loConst() + 1; }
|
||||
bool ascending() const { return leftConst() < rightConst(); }
|
||||
void dump(std::ostream& str) const override;
|
||||
virtual string emitC() { V3ERROR_NA_RETURN(""); }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
@@ -2450,7 +2527,7 @@ public:
|
||||
, m_name{name}
|
||||
, m_showAt{showAt} {}
|
||||
ASTGEN_MEMBERS_AstComment;
|
||||
string name() const override { return m_name; } // * = Text
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Text
|
||||
bool same(const AstNode* samep) const override { return true; } // Ignore name in comments
|
||||
virtual bool showAt() const { return m_showAt; }
|
||||
};
|
||||
@@ -2591,7 +2668,7 @@ public:
|
||||
: ASTGEN_SUPER_Disable(fl)
|
||||
, m_name{name} {}
|
||||
ASTGEN_MEMBERS_AstDisable;
|
||||
string name() const override { return m_name; } // * = Block name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Block name
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
bool isBrancher() const override {
|
||||
return true; // SPECIAL: We don't process code after breaks
|
||||
@@ -2633,8 +2710,8 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
string verilogKwd() const override {
|
||||
return (filep() ? string("$f") + string(displayType().ascii())
|
||||
: string("$") + string(displayType().ascii()));
|
||||
return (filep() ? string{"$f"} + string{displayType().ascii()}
|
||||
: string{"$"} + string{displayType().ascii()});
|
||||
}
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
@@ -2651,17 +2728,13 @@ public:
|
||||
bool addNewline() const { return displayType().addNewline(); }
|
||||
};
|
||||
class AstDoWhile final : public AstNodeStmt {
|
||||
// @astgen op1 := precondsp : List[AstNode]
|
||||
// @astgen op2 := condp : AstNodeExpr
|
||||
// @astgen op3 := stmtsp : List[AstNode]
|
||||
// @astgen op4 := incsp : List[AstNode]
|
||||
// @astgen op1 := condp : AstNodeExpr
|
||||
// @astgen op2 := stmtsp : List[AstNode]
|
||||
public:
|
||||
AstDoWhile(FileLine* fl, AstNodeExpr* conditionp, AstNode* stmtsp = nullptr,
|
||||
AstNode* incsp = nullptr)
|
||||
AstDoWhile(FileLine* fl, AstNodeExpr* conditionp, AstNode* stmtsp = nullptr)
|
||||
: ASTGEN_SUPER_DoWhile(fl) {
|
||||
condp(conditionp);
|
||||
addStmtsp(stmtsp);
|
||||
addIncsp(incsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstDoWhile;
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
@@ -2739,46 +2812,6 @@ public:
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstFOpen final : public AstNodeStmt {
|
||||
// Although a system function in IEEE, here a statement which sets the file pointer (MCD)
|
||||
// @astgen op1 := filep : AstNodeExpr
|
||||
// @astgen op2 := filenamep : AstNodeExpr
|
||||
// @astgen op3 := modep : AstNodeExpr
|
||||
public:
|
||||
AstFOpen(FileLine* fl, AstNodeExpr* filep, AstNodeExpr* filenamep, AstNodeExpr* modep)
|
||||
: ASTGEN_SUPER_FOpen(fl) {
|
||||
this->filep(filep);
|
||||
this->filenamep(filenamep);
|
||||
this->modep(modep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstFOpen;
|
||||
string verilogKwd() const override { return "$fopen"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstFOpenMcd final : public AstNodeStmt {
|
||||
// Although a system function in IEEE, here a statement which sets the file pointer (MCD)
|
||||
// @astgen op1 := filep : AstNodeExpr
|
||||
// @astgen op2 := filenamep : AstNodeExpr
|
||||
public:
|
||||
AstFOpenMcd(FileLine* fl, AstNodeExpr* filep, AstNodeExpr* filenamep)
|
||||
: ASTGEN_SUPER_FOpenMcd(fl) {
|
||||
this->filep(filep);
|
||||
this->filenamep(filenamep);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstFOpenMcd;
|
||||
string verilogKwd() const override { return "$fopen"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
bool isPredictOptimizable() const override { return false; }
|
||||
bool isPure() const override { return false; }
|
||||
bool isOutputter() const override { return true; }
|
||||
bool isUnlikely() const override { return true; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
class AstFinish final : public AstNodeStmt {
|
||||
public:
|
||||
explicit AstFinish(FileLine* fl)
|
||||
@@ -2821,7 +2854,7 @@ public:
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
};
|
||||
class AstJumpBlock final : public AstNodeStmt {
|
||||
// Block of code including a JumpGo and JumpLabel
|
||||
// Block of code including a single JumpLabel, and 0+ JumpGo's to that label
|
||||
// Parents: {statement list}
|
||||
// Children: {statement list, with JumpGo and JumpLabel below}
|
||||
// @astgen op1 := stmtsp : List[AstNode]
|
||||
@@ -2926,7 +2959,7 @@ public:
|
||||
: ASTGEN_SUPER_PrintTimeScale(fl) {}
|
||||
ASTGEN_MEMBERS_AstPrintTimeScale;
|
||||
void name(const string& name) override { m_name = name; }
|
||||
string name() const override { return m_name; } // * = Var name
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void dump(std::ostream& str) const override;
|
||||
string verilogKwd() const override { return "$printtimescale"; }
|
||||
bool isGateOptimizable() const override { return false; }
|
||||
@@ -3145,7 +3178,7 @@ public:
|
||||
void dump(std::ostream& str) const override;
|
||||
int instrCount() const override { return 100; } // Large...
|
||||
ASTGEN_MEMBERS_AstTraceDecl;
|
||||
string name() const override { return m_showname; }
|
||||
string name() const override VL_MT_STABLE { return m_showname; }
|
||||
bool maybePointedTo() const override { return true; }
|
||||
bool hasDType() const override { return true; }
|
||||
bool same(const AstNode* samep) const override { return false; }
|
||||
@@ -3274,8 +3307,6 @@ public:
|
||||
int instrCount() const override { return INSTR_COUNT_BRANCH; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
// Stop statement searchback here
|
||||
void addBeforeStmt(AstNode* newp, AstNode* belowp) override;
|
||||
// Stop statement searchback here
|
||||
void addNextStmt(AstNode* newp, AstNode* belowp) override;
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
};
|
||||
|
||||
+189
-116
@@ -25,6 +25,7 @@
|
||||
#include "V3Hasher.h"
|
||||
#include "V3PartitionGraph.h" // Just for mtask dumping
|
||||
#include "V3String.h"
|
||||
#include "V3Width.h"
|
||||
|
||||
#include "V3Ast__gen_macros.h" // Generated by 'astgen'
|
||||
|
||||
@@ -63,7 +64,7 @@ void AstNodeFTaskRef::cloneRelink() {
|
||||
bool AstNodeFTaskRef::isPure() const {
|
||||
// TODO: For non-DPI functions we could traverse the AST of function's body to determine
|
||||
// pureness.
|
||||
return this->taskp() && this->taskp()->dpiImport() && this->taskp()->pure();
|
||||
return this->taskp() && this->taskp()->dpiImport() && this->taskp()->dpiPure();
|
||||
}
|
||||
|
||||
bool AstNodeFTaskRef::isGateOptimizable() const { return m_taskp && m_taskp->isGateOptimizable(); }
|
||||
@@ -83,12 +84,6 @@ void AstNodeVarRef::cloneRelink() {
|
||||
}
|
||||
}
|
||||
|
||||
string AstNodeVarRef::selfPointerProtect(bool useSelfForThis) const {
|
||||
const string& sp
|
||||
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
|
||||
return VIdProtect::protectWordsIf(sp, protect());
|
||||
}
|
||||
|
||||
void AstAddrOfCFunc::cloneRelink() {
|
||||
if (m_funcp && m_funcp->clonep()) m_funcp = m_funcp->clonep();
|
||||
}
|
||||
@@ -103,28 +98,8 @@ int AstNodeSel::bitConst() const {
|
||||
return (constp ? constp->toSInt() : 0);
|
||||
}
|
||||
|
||||
void AstNodeUOrStructDType::repairMemberCache() {
|
||||
clearCache();
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
if (m_members.find(itemp->name()) != m_members.end()) {
|
||||
itemp->v3error("Duplicate declaration of member name: " << itemp->prettyNameQ());
|
||||
} else {
|
||||
m_members.emplace(itemp->name(), itemp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* AstNodeUOrStructDType::broken() const {
|
||||
std::unordered_set<AstMemberDType*> exists;
|
||||
for (AstMemberDType* itemp = membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
exists.insert(itemp);
|
||||
}
|
||||
for (MemberNameMap::const_iterator it = m_members.begin(); it != m_members.end(); ++it) {
|
||||
if (VL_UNCOVERABLE(exists.find(it->second) == exists.end())) {
|
||||
this->v3error("Internal: Structure member broken: " << it->first);
|
||||
return "member broken";
|
||||
}
|
||||
}
|
||||
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -146,14 +121,22 @@ const char* AstNodeCCall::broken() const {
|
||||
BROKEN_RTN(m_funcp && !m_funcp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
bool AstNodeCCall::isPure() const { return funcp()->pure(); }
|
||||
bool AstNodeCCall::isPure() const { return funcp()->dpiPure(); }
|
||||
|
||||
string AstCCall::selfPointerProtect(bool useSelfForThis) const {
|
||||
const string& sp
|
||||
= useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer();
|
||||
return VIdProtect::protectWordsIf(sp, protect());
|
||||
AstNodeCond::AstNodeCond(VNType t, FileLine* fl, AstNodeExpr* condp, AstNodeExpr* thenp,
|
||||
AstNodeExpr* elsep)
|
||||
: AstNodeTriop{t, fl, condp, thenp, elsep} {
|
||||
UASSERT_OBJ(thenp, this, "No thenp expression");
|
||||
UASSERT_OBJ(elsep, this, "No elsep expression");
|
||||
if (thenp->isClassHandleValue() && elsep->isClassHandleValue()) {
|
||||
// Get the most-deriving class type that both arguments can be casted to.
|
||||
AstNodeDType* const commonClassTypep = V3Width::getCommonClassTypep(thenp, elsep);
|
||||
UASSERT_OBJ(commonClassTypep, this, "No common base class exists");
|
||||
dtypep(commonClassTypep);
|
||||
} else {
|
||||
dtypeFrom(thenp);
|
||||
}
|
||||
}
|
||||
|
||||
void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
|
||||
const V3Number& ths) {
|
||||
if (lhs.isNeqZero()) {
|
||||
@@ -232,6 +215,15 @@ int AstBasicDType::widthTotalBytes() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool AstBasicDType::same(const AstNode* samep) const {
|
||||
const AstBasicDType* const sp = static_cast<const AstBasicDType*>(samep);
|
||||
if (!rangep() && !sp->rangep() && m == sp->m) {
|
||||
return true;
|
||||
} else {
|
||||
return m == sp->m && rangep() && rangep()->sameTree(sp->rangep());
|
||||
}
|
||||
}
|
||||
|
||||
int AstNodeUOrStructDType::widthTotalBytes() const {
|
||||
if (width() <= 8) {
|
||||
return 1;
|
||||
@@ -381,13 +373,15 @@ string AstVar::verilogKwd() const {
|
||||
return "wreal";
|
||||
} else if (varType() == VVarType::IFACEREF) {
|
||||
return "ifaceref";
|
||||
} else {
|
||||
} else if (dtypep()) {
|
||||
return dtypep()->name();
|
||||
} else {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
|
||||
bool asRef) const VL_MT_SAFE {
|
||||
bool asRef) const {
|
||||
UASSERT_OBJ(!forReturn, this,
|
||||
"Internal data is never passed as return, but as first argument");
|
||||
string ostatic;
|
||||
@@ -573,7 +567,7 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
|
||||
} else {
|
||||
class converter final : public dpiTypesToStringConverter {
|
||||
string bitLogicVector(const AstVar* varp, bool isBit) const override {
|
||||
return string(varp->isReadOnly() ? "const " : "")
|
||||
return string{varp->isReadOnly() ? "const " : ""}
|
||||
+ dpiTypesToStringConverter::bitLogicVector(varp, isBit) + '*';
|
||||
}
|
||||
string primitive(const AstVar* varp) const override {
|
||||
@@ -636,13 +630,13 @@ string AstVar::dpiTmpVarType(const string& varName) const {
|
||||
|
||||
string AstVar::scType() const {
|
||||
if (isScBigUint()) {
|
||||
return (string("sc_biguint<") + cvtToStr(widthMin())
|
||||
return (string{"sc_biguint<"} + cvtToStr(widthMin())
|
||||
+ "> "); // Keep the space so don't get >>
|
||||
} else if (isScUint()) {
|
||||
return (string("sc_uint<") + cvtToStr(widthMin())
|
||||
return (string{"sc_uint<"} + cvtToStr(widthMin())
|
||||
+ "> "); // Keep the space so don't get >>
|
||||
} else if (isScBv()) {
|
||||
return (string("sc_bv<") + cvtToStr(widthMin()) + "> "); // Keep the space so don't get >>
|
||||
return (string{"sc_bv<"} + cvtToStr(widthMin()) + "> "); // Keep the space so don't get >>
|
||||
} else if (widthMin() == 1) {
|
||||
return "bool";
|
||||
} else if (widthMin() <= VL_IDATASIZE) {
|
||||
@@ -704,7 +698,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const VL_MT_SAFE {
|
||||
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
|
||||
const CTypeRecursed info = cTypeRecurse(false);
|
||||
return info.render(name, isRef);
|
||||
}
|
||||
@@ -734,9 +728,9 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||
info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">";
|
||||
info.m_type = "VlClassRef<" + EmitCBase::prefixNameProtect(adtypep) + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
||||
info.m_type = EmitCBaseVisitor::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
|
||||
info.m_type = EmitCBase::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);
|
||||
@@ -745,7 +739,7 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
info.m_type += ">";
|
||||
} else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) {
|
||||
const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
|
||||
info.m_type = EmitCBaseVisitor::prefixNameProtect(sdtypep);
|
||||
info.m_type = EmitCBase::prefixNameProtect(sdtypep);
|
||||
} else if (const AstBasicDType* const bdtypep = dtypep->basicp()) {
|
||||
// We don't print msb()/lsb() as multidim packed would require recursion,
|
||||
// and may confuse users as C++ data is stored always with bit 0 used
|
||||
@@ -772,6 +766,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||
info.m_type = "VlDynamicTriggerScheduler";
|
||||
} else if (bdtypep->isForkSync()) {
|
||||
info.m_type = "VlForkSync";
|
||||
} else if (bdtypep->isProcessRef()) {
|
||||
info.m_type = "VlProcessRef";
|
||||
} else if (bdtypep->isEvent()) {
|
||||
info.m_type = "VlEvent";
|
||||
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
|
||||
@@ -846,7 +842,7 @@ int AstNodeDType::widthPow2() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool AstNodeDType::isLiteralType() const VL_MT_SAFE {
|
||||
bool AstNodeDType::isLiteralType() const VL_MT_STABLE {
|
||||
if (const auto* const dtypep = VN_CAST(skipRefp(), BasicDType)) {
|
||||
return dtypep->keyword().isLiteralType();
|
||||
} else if (const auto* const dtypep = VN_CAST(skipRefp(), UnpackArrayDType)) {
|
||||
@@ -916,10 +912,10 @@ void AstScope::cloneRelink() {
|
||||
}
|
||||
}
|
||||
string AstScope::nameDotless() const {
|
||||
string out = shortName();
|
||||
string result = shortName();
|
||||
string::size_type pos;
|
||||
while ((pos = out.find('.')) != string::npos) out.replace(pos, 1, "__");
|
||||
return out;
|
||||
while ((pos = result.find('.')) != string::npos) result.replace(pos, 1, "__");
|
||||
return result;
|
||||
}
|
||||
|
||||
AstVarScope* AstScope::createTemp(const string& name, unsigned width) {
|
||||
@@ -1057,6 +1053,15 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
|
||||
return m_voidp;
|
||||
}
|
||||
|
||||
AstStreamDType* AstTypeTable::findStreamDType(FileLine* fl) {
|
||||
if (VL_UNLIKELY(!m_streamp)) {
|
||||
AstStreamDType* const newp = new AstStreamDType{fl};
|
||||
addTypesp(newp);
|
||||
m_streamp = newp;
|
||||
}
|
||||
return m_streamp;
|
||||
}
|
||||
|
||||
AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) {
|
||||
if (VL_UNLIKELY(!m_queueIndexp)) {
|
||||
AstQueueDType* const newp = new AstQueueDType{fl, AstNode::findUInt32DType(), nullptr};
|
||||
@@ -1259,44 +1264,17 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) {
|
||||
//======================================================================
|
||||
// Special walking tree inserters
|
||||
|
||||
void AstNode::addBeforeStmt(AstNode* newp, AstNode*) {
|
||||
UASSERT_OBJ(backp(), newp, "Can't find current statement to addBeforeStmt");
|
||||
// Look up; virtual call will find where to put it
|
||||
this->backp()->addBeforeStmt(newp, this);
|
||||
}
|
||||
void AstNode::addNextStmt(AstNode* newp, AstNode*) {
|
||||
UASSERT_OBJ(backp(), newp, "Can't find current statement to addNextStmt");
|
||||
// Look up; virtual call will find where to put it
|
||||
this->backp()->addNextStmt(newp, this);
|
||||
}
|
||||
|
||||
void AstNodeStmt::addBeforeStmt(AstNode* newp, AstNode*) {
|
||||
// Insert newp before current node
|
||||
this->addHereThisAsNext(newp);
|
||||
}
|
||||
void AstNodeStmt::addNextStmt(AstNode* newp, AstNode*) {
|
||||
// Insert newp after current node
|
||||
this->addNextHere(newp);
|
||||
}
|
||||
|
||||
void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) {
|
||||
// Special, as statements need to be put in different places
|
||||
// Belowp is how we came to recurse up to this point
|
||||
// Preconditions insert first just before themselves (the normal rule
|
||||
// for other statement types)
|
||||
if (belowp == precondsp()) {
|
||||
// Must have been first statement in precondsp list, so newp is new first statement
|
||||
belowp->addHereThisAsNext(newp);
|
||||
} else if (belowp == condp()) {
|
||||
// Goes before condition, IE in preconditions
|
||||
addPrecondsp(newp);
|
||||
} else if (belowp == stmtsp()) {
|
||||
// Was first statement in body, so new front
|
||||
belowp->addHereThisAsNext(newp);
|
||||
} else {
|
||||
belowp->v3fatalSrc("Doesn't look like this was really under the while");
|
||||
}
|
||||
}
|
||||
void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
|
||||
// Special, as statements need to be put in different places
|
||||
// Belowp is how we came to recurse up to this point
|
||||
@@ -1367,6 +1345,7 @@ void AstNode::dump(std::ostream& str) const {
|
||||
void AstNodeProcedure::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isSuspendable()) str << " [SUSP]";
|
||||
if (needProcess()) str << " [NPRC]";
|
||||
}
|
||||
|
||||
void AstAlways::dump(std::ostream& str) const {
|
||||
@@ -1430,29 +1409,17 @@ const char* AstClassPackage::broken() const {
|
||||
void AstClassPackage::cloneRelink() {
|
||||
if (m_classp && m_classp->clonep()) m_classp = m_classp->clonep();
|
||||
}
|
||||
void AstClass::insertCache(AstNode* nodep) {
|
||||
const bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|
||||
|| VN_IS(nodep, CFunc));
|
||||
if (doit) {
|
||||
if (m_members.find(nodep->name()) != m_members.end()) {
|
||||
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
|
||||
} else {
|
||||
m_members.emplace(nodep->name(), nodep);
|
||||
}
|
||||
}
|
||||
bool AstClass::isCacheableChild(const AstNode* nodep) {
|
||||
return (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|
||||
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|
||||
|| VN_IS(nodep, CFunc));
|
||||
}
|
||||
void AstClass::repairCache() {
|
||||
clearCache();
|
||||
for (auto* itemp = membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (const auto* const scopep = VN_CAST(itemp, Scope)) {
|
||||
for (auto* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
|
||||
insertCache(blockp);
|
||||
}
|
||||
} else {
|
||||
insertCache(itemp);
|
||||
}
|
||||
AstClass* AstClass::baseMostClassp() {
|
||||
AstClass* basep = this;
|
||||
while (basep->extendsp() && basep->extendsp()->classp()) {
|
||||
basep = basep->extendsp()->classp();
|
||||
}
|
||||
return basep;
|
||||
}
|
||||
bool AstClass::isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp) {
|
||||
// TAIL RECURSIVE
|
||||
@@ -1482,13 +1449,20 @@ void AstClassExtends::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isImplements()) str << " [IMPLEMENTS]";
|
||||
}
|
||||
AstClass* AstClassExtends::classp() const {
|
||||
const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
|
||||
if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.'
|
||||
refp = VN_AS(childDTypep(), ClassRefDType);
|
||||
AstClass* AstClassExtends::classOrNullp() const {
|
||||
const AstNodeDType* const dtp = dtypep() ? dtypep() : childDTypep();
|
||||
const AstClassRefDType* const refp = VN_CAST(dtp, ClassRefDType);
|
||||
if (refp && !refp->paramsp()) {
|
||||
// Class already resolved
|
||||
return refp->classp();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
UASSERT_OBJ(refp, this, "class extends non-ref");
|
||||
return refp->classp();
|
||||
}
|
||||
AstClass* AstClassExtends::classp() const {
|
||||
AstClass* const clsp = classOrNullp();
|
||||
UASSERT_OBJ(clsp, this, "Extended class is unresolved");
|
||||
return clsp;
|
||||
}
|
||||
void AstClassRefDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
@@ -1652,10 +1626,25 @@ void AstLogOr::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (sideEffect()) str << " [SIDE]";
|
||||
}
|
||||
|
||||
void AstMemberDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "member";
|
||||
}
|
||||
AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
|
||||
AstNodeDType* subdtp = skipRefp();
|
||||
while (AstNodeArrayDType* const asubdtp = VN_CAST(subdtp, NodeArrayDType)) {
|
||||
subdtp = asubdtp->subDTypep();
|
||||
}
|
||||
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr
|
||||
}
|
||||
|
||||
bool AstMemberSel::same(const AstNode* samep) const {
|
||||
const AstMemberSel* const sp = static_cast<const AstMemberSel*>(samep);
|
||||
return sp != nullptr && access() == sp->access() && fromp()->same(sp->fromp())
|
||||
&& name() == sp->name() && varp()->same(sp->varp());
|
||||
}
|
||||
|
||||
void AstMemberSel::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
str << " -> ";
|
||||
@@ -1672,15 +1661,6 @@ 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);
|
||||
str << " -> ";
|
||||
if (taskp()) {
|
||||
taskp()->dump(str);
|
||||
} else {
|
||||
str << " -> UNLINKED";
|
||||
}
|
||||
}
|
||||
void AstModportFTaskRef::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isExport()) str << " EXPORT";
|
||||
@@ -1767,7 +1747,7 @@ void AstTypedef::dump(std::ostream& str) const {
|
||||
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]";
|
||||
if (ascending()) str << " [ASCENDING]";
|
||||
}
|
||||
void AstParamTypeDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
@@ -1813,7 +1793,7 @@ void AstRefDType::cloneRelink() {
|
||||
m_classOrPackagep = m_classOrPackagep->clonep();
|
||||
}
|
||||
}
|
||||
AstNodeDType* AstRefDType::subDTypep() const {
|
||||
AstNodeDType* AstRefDType::subDTypep() const VL_MT_STABLE {
|
||||
if (typedefp()) return typedefp()->subDTypep();
|
||||
return refDTypep(); // Maybe nullptr
|
||||
}
|
||||
@@ -1821,6 +1801,7 @@ void AstNodeUOrStructDType::dump(std::ostream& str) const {
|
||||
this->AstNodeDType::dump(str);
|
||||
if (packed()) str << " [PACKED]";
|
||||
if (isFourstate()) str << " [4STATE]";
|
||||
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
|
||||
}
|
||||
void AstNodeDType::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
@@ -2052,6 +2033,10 @@ void AstVoidDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "void";
|
||||
}
|
||||
void AstStreamDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "stream";
|
||||
}
|
||||
void AstVarScope::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (isTrace()) str << " [T]";
|
||||
@@ -2063,6 +2048,10 @@ void AstVarScope::dump(std::ostream& str) const {
|
||||
str << " ->UNLINKED";
|
||||
}
|
||||
}
|
||||
bool AstVarScope::same(const AstNode* samep) const {
|
||||
const AstVarScope* const asamep = static_cast<const AstVarScope*>(samep);
|
||||
return varp()->same(asamep->varp()) && scopep()->same(asamep->scopep());
|
||||
}
|
||||
void AstNodeVarRef::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
|
||||
@@ -2090,6 +2079,10 @@ void AstVarRef::dump(std::ostream& str) const {
|
||||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstVarRef::broken() const {
|
||||
BROKEN_RTN(!varp());
|
||||
return AstNodeVarRef::broken();
|
||||
}
|
||||
bool AstVarRef::same(const AstNode* samep) const {
|
||||
return same(static_cast<const AstVarRef*>(samep));
|
||||
}
|
||||
@@ -2126,12 +2119,23 @@ void AstVar::dump(std::ostream& str) const {
|
||||
if (!lifetime().isNone()) str << " [" << lifetime().ascii() << "] ";
|
||||
str << " " << varType();
|
||||
}
|
||||
bool AstVar::same(const AstNode* samep) const {
|
||||
const AstVar* const asamep = static_cast<const AstVar*>(samep);
|
||||
return name() == asamep->name() && varType() == asamep->varType();
|
||||
}
|
||||
void AstScope::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " [abovep=" << reinterpret_cast<const void*>(aboveScopep()) << "]";
|
||||
str << " [cellp=" << reinterpret_cast<const void*>(aboveCellp()) << "]";
|
||||
str << " [modp=" << reinterpret_cast<const void*>(modp()) << "]";
|
||||
}
|
||||
bool AstScope::same(const AstNode* samep) const {
|
||||
const AstScope* const asamep = static_cast<const AstScope*>(samep);
|
||||
return name() == asamep->name()
|
||||
&& ((!aboveScopep() && !asamep->aboveScopep())
|
||||
|| (aboveScopep() && asamep->aboveScopep()
|
||||
&& aboveScopep()->name() == asamep->aboveScopep()->name()));
|
||||
}
|
||||
void AstScopeName::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
if (dpiExport()) str << " [DPIEX]";
|
||||
@@ -2283,7 +2287,7 @@ void AstCFile::dump(std::ostream& str) const {
|
||||
void AstCFunc::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (slow()) str << " [SLOW]";
|
||||
if (pure()) str << " [PURE]";
|
||||
if (dpiPure()) str << " [DPIPURE]";
|
||||
if (isStatic()) str << " [STATIC]";
|
||||
if (dpiExportDispatcher()) str << " [DPIED]";
|
||||
if (dpiExportImpl()) str << " [DPIEI]";
|
||||
@@ -2294,6 +2298,7 @@ void AstCFunc::dump(std::ostream& str) const {
|
||||
if (isDestructor()) str << " [DTOR]";
|
||||
if (isVirtual()) str << " [VIRT]";
|
||||
if (isCoroutine()) str << " [CORO]";
|
||||
if (needProcess()) str << " [NPRC]";
|
||||
}
|
||||
const char* AstCAwait::broken() const {
|
||||
BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists());
|
||||
@@ -2312,8 +2317,8 @@ void AstCAwait::dump(std::ostream& str) const {
|
||||
int AstCMethodHard::instrCount() const {
|
||||
if (AstBasicDType* const basicp = fromp()->dtypep()->basicp()) {
|
||||
// TODO: add a more structured description of library methods, rather than using string
|
||||
// matching. See #3715.
|
||||
if (basicp->isTriggerVec() && m_name == "at") {
|
||||
// matching. See issue #3715.
|
||||
if (basicp->isTriggerVec() && m_name == "word") {
|
||||
// This is an important special case for scheduling so we compute it precisely,
|
||||
// it is simply a load.
|
||||
return INSTR_COUNT_LD;
|
||||
@@ -2321,6 +2326,74 @@ int AstCMethodHard::instrCount() const {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void AstCMethodHard::setPurity() {
|
||||
static const std::map<std::string, bool> isPureMethod{{"andNot", false},
|
||||
{"any", true},
|
||||
{"assign", false},
|
||||
{"at", true},
|
||||
{"atBack", true},
|
||||
{"awaitingCurrentTime", true},
|
||||
{"clear", false},
|
||||
{"clearFired", false},
|
||||
{"commit", false},
|
||||
{"delay", false},
|
||||
{"done", false},
|
||||
{"erase", false},
|
||||
{"evaluate", false},
|
||||
{"evaluation", false},
|
||||
{"exists", true},
|
||||
{"find", true},
|
||||
{"find_first", true},
|
||||
{"find_first_index", true},
|
||||
{"find_index", true},
|
||||
{"find_last", true},
|
||||
{"find_last_index", true},
|
||||
{"fire", false},
|
||||
{"first", false},
|
||||
{"init", false},
|
||||
{"insert", false},
|
||||
{"isFired", true},
|
||||
{"isTriggered", true},
|
||||
{"join", false},
|
||||
{"last", false},
|
||||
{"max", true},
|
||||
{"min", true},
|
||||
{"neq", true},
|
||||
{"next", false},
|
||||
{"pop", false},
|
||||
{"pop_back", false},
|
||||
{"pop_front", false},
|
||||
{"prev", false},
|
||||
{"push", false},
|
||||
{"push_back", false},
|
||||
{"push_front", false},
|
||||
{"r_and", true},
|
||||
{"r_or", true},
|
||||
{"r_product", true},
|
||||
{"r_sum", true},
|
||||
{"r_xor", true},
|
||||
{"renew", false},
|
||||
{"renew_copy", false},
|
||||
{"resume", false},
|
||||
{"reverse", false},
|
||||
{"rsort", false},
|
||||
{"set", false},
|
||||
{"shuffle", false},
|
||||
{"size", true},
|
||||
{"slice", true},
|
||||
{"sliceBackBack", true},
|
||||
{"sliceFrontBack", true},
|
||||
{"sort", false},
|
||||
{"thisOr", false},
|
||||
{"trigger", false},
|
||||
{"unique", true},
|
||||
{"unique_index", true},
|
||||
{"word", true}};
|
||||
|
||||
auto isPureIt = isPureMethod.find(name());
|
||||
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
|
||||
m_pure = isPureIt->second;
|
||||
}
|
||||
const char* AstCFunc::broken() const {
|
||||
BROKEN_RTN((m_scopep && !m_scopep->brokeExists()));
|
||||
return nullptr;
|
||||
|
||||
+21
-28
@@ -72,12 +72,12 @@ private:
|
||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
int m_ifDepth = 0; // Current if depth
|
||||
bool m_keepBegins = false; // True if begins should not be inlined
|
||||
std::set<AstVar*> m_staticFuncVars; // Static variables from m_ftaskp
|
||||
|
||||
// METHODS
|
||||
|
||||
string dot(const string& a, const string& b) {
|
||||
if (a == "") return b;
|
||||
if (b == "") return a;
|
||||
return a + "__DOT__" + b;
|
||||
}
|
||||
|
||||
@@ -123,30 +123,22 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void renameAndStaticsRecurse(AstNode* const nodep) {
|
||||
// Rename references and move InitialStatic items
|
||||
if (AstVarRef* const varrefp = VN_CAST(nodep, VarRef)) {
|
||||
const auto it = m_staticFuncVars.find(varrefp->varp());
|
||||
if (it != m_staticFuncVars.end()) varrefp->name((*it)->name());
|
||||
}
|
||||
|
||||
if (nodep->op1p()) renameAndStaticsRecurse(nodep->op1p());
|
||||
if (nodep->op2p()) renameAndStaticsRecurse(nodep->op2p());
|
||||
if (nodep->op3p()) renameAndStaticsRecurse(nodep->op3p());
|
||||
if (nodep->op4p()) renameAndStaticsRecurse(nodep->op4p());
|
||||
if (nodep->nextp()) renameAndStaticsRecurse(nodep->nextp());
|
||||
|
||||
if (VN_IS(nodep, InitialStatic)) {
|
||||
nodep->unlinkFrBack();
|
||||
m_ftaskp->addHereThisAsNext(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstFork* nodep) override {
|
||||
// Keep this begin to group its statements together
|
||||
// Keep begins in forks to group their statements together
|
||||
VL_RESTORER(m_keepBegins);
|
||||
m_keepBegins = true;
|
||||
// If a statement is not a begin, wrap it in a begin. This fixes an issue when the
|
||||
// statement is a task call that gets inlined later (or any other statement that gets
|
||||
// replaced with multiple statements)
|
||||
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (!VN_IS(stmtp, Begin)) {
|
||||
AstBegin* const beginp = new AstBegin{stmtp->fileline(), "", nullptr};
|
||||
stmtp->replaceWith(beginp);
|
||||
beginp->addStmtsp(stmtp);
|
||||
stmtp = beginp;
|
||||
}
|
||||
}
|
||||
dotNames(nodep, "__FORK__");
|
||||
nodep->name("");
|
||||
}
|
||||
@@ -186,7 +178,10 @@ private:
|
||||
m_ftaskp = nodep;
|
||||
m_liftedp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
renameAndStaticsRecurse(nodep);
|
||||
nodep->foreach([&](AstInitialStatic* const initp) {
|
||||
initp->unlinkFrBack();
|
||||
m_ftaskp->addHereThisAsNext(initp);
|
||||
});
|
||||
if (m_liftedp) {
|
||||
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
|
||||
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
||||
@@ -196,7 +191,6 @@ private:
|
||||
nodep->addStmtsp(m_liftedp);
|
||||
m_liftedp = nullptr;
|
||||
}
|
||||
m_staticFuncVars.clear();
|
||||
m_ftaskp = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -235,11 +229,11 @@ private:
|
||||
void visit(AstVar* nodep) override {
|
||||
// If static variable, move it outside a function.
|
||||
if (nodep->lifetime().isStatic() && m_ftaskp) {
|
||||
const std::string newName = m_ftaskp->name() + "__Vstatic__" + nodep->name();
|
||||
const std::string newName
|
||||
= m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name());
|
||||
nodep->name(newName);
|
||||
nodep->unlinkFrBack();
|
||||
m_ftaskp->addHereThisAsNext(nodep);
|
||||
m_staticFuncVars.insert(nodep);
|
||||
nodep->funcLocal(false);
|
||||
} else if (m_unnamedScope != "") {
|
||||
// Rename it
|
||||
@@ -288,7 +282,7 @@ private:
|
||||
// To keep correct visual order, must add before other Text's
|
||||
AstText* const afterp = nodep->scopeAttrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->addScopeAttrp(new AstText{nodep->fileline(), string("__DOT__") + scname});
|
||||
nodep->addScopeAttrp(new AstText{nodep->fileline(), string{"__DOT__"} + scname});
|
||||
if (afterp) nodep->addScopeAttrp(afterp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
@@ -350,7 +344,6 @@ private:
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (nodep->varp()->user1()) { // It was converted
|
||||
UINFO(9, " relinVarRef " << nodep << endl);
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@@ -381,5 +374,5 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
|
||||
{ BeginVisitor{nodep, &state}; }
|
||||
if (state.anyFuncInBegin()) { BeginRelinkVisitor{nodep, &state}; }
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("begin", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("begin", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+11
-9
@@ -38,17 +38,19 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
//######################################################################
|
||||
// Branch state, as a visitor of each AstNode
|
||||
|
||||
class BranchVisitor final : public VNVisitor {
|
||||
class BranchVisitor final : public VNVisitorConst {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstFTask::user1() -> int. Number of references
|
||||
const VNUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
// STATE - across all visitors
|
||||
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
|
||||
|
||||
// STATE - for current visit position (use VL_RESTORER)
|
||||
int m_likely = false; // Excuses for branch likely taken
|
||||
int m_unlikely = false; // Excuses for branch likely not taken
|
||||
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
|
||||
|
||||
// METHODS
|
||||
|
||||
@@ -71,12 +73,12 @@ private:
|
||||
{
|
||||
// Do if
|
||||
reset();
|
||||
iterateAndNextNull(nodep->thensp());
|
||||
iterateAndNextConstNull(nodep->thensp());
|
||||
const int ifLikely = m_likely;
|
||||
const int ifUnlikely = m_unlikely;
|
||||
// Do else
|
||||
reset();
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
iterateAndNextConstNull(nodep->elsesp());
|
||||
const int elseLikely = m_likely;
|
||||
const int elseUnlikely = m_unlikely;
|
||||
// Compute
|
||||
@@ -91,16 +93,16 @@ private:
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
checkUnlikely(nodep);
|
||||
nodep->funcp()->user1Inc();
|
||||
iterateChildren(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
checkUnlikely(nodep);
|
||||
m_cfuncsp.push_back(nodep);
|
||||
iterateChildren(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
checkUnlikely(nodep);
|
||||
iterateChildren(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
|
||||
// METHODS
|
||||
@@ -114,7 +116,7 @@ public:
|
||||
// CONSTRUCTORS
|
||||
explicit BranchVisitor(AstNetlist* nodep) {
|
||||
reset();
|
||||
iterateChildren(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
calc_tasks();
|
||||
}
|
||||
~BranchVisitor() override = default;
|
||||
|
||||
+38
-14
@@ -62,6 +62,8 @@ public:
|
||||
}
|
||||
} s_brokenCntGlobal;
|
||||
|
||||
static bool s_brokenAllowMidvisitorCheck = false;
|
||||
|
||||
//######################################################################
|
||||
// Table of allocated AstNode pointers
|
||||
|
||||
@@ -144,23 +146,26 @@ bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinka
|
||||
//######################################################################
|
||||
// Check every node in tree
|
||||
|
||||
class BrokenCheckVisitor final : public VNVisitor {
|
||||
bool m_inScope = false; // Under AstScope
|
||||
|
||||
class BrokenCheckVisitor final : public VNVisitorConst {
|
||||
// Constants for marking we are under/not under a node
|
||||
const uint8_t m_brokenCntCurrentNotUnder = s_brokenCntGlobal.get(); // Top bit is clear
|
||||
const uint8_t m_brokenCntCurrentUnder = m_brokenCntCurrentNotUnder | 0x80; // Top bit is set
|
||||
|
||||
// Current CFunc, if any
|
||||
const AstCFunc* m_cfuncp = nullptr;
|
||||
// STATE - across all visitors
|
||||
// All local variables declared in current function
|
||||
std::unordered_set<const AstVar*> m_localVars;
|
||||
std::set<const AstVar*> m_localVars;
|
||||
// Variable references in current function that do not reference an in-scope local
|
||||
std::unordered_map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
|
||||
std::map<const AstVar*, const AstNodeVarRef*> m_suspectRefs;
|
||||
// Local variables declared in the scope of the current statement
|
||||
std::vector<std::unordered_set<const AstVar*>> m_localsStack;
|
||||
|
||||
// STATE - for current visit position (use VL_RESTORER)
|
||||
const AstCFunc* m_cfuncp = nullptr; // Current CFunc, if any
|
||||
bool m_inScope = false; // Under AstScope
|
||||
std::set<std::string> m_cFuncNames; // CFunc by name in current class/module
|
||||
|
||||
private:
|
||||
// METHODS
|
||||
static void checkWidthMin(const AstNode* nodep) {
|
||||
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|
||||
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
|
||||
@@ -172,6 +177,7 @@ private:
|
||||
const char* const whyp = nodep->broken();
|
||||
UASSERT_OBJ(!whyp, nodep,
|
||||
"Broken link in node (or something without maybePointedTo): " << whyp);
|
||||
if (!s_brokenAllowMidvisitorCheck) nodep->checkIter();
|
||||
if (nodep->dtypep()) {
|
||||
UASSERT_OBJ(nodep->dtypep()->brokeExists(), nodep,
|
||||
"Broken link in node->dtypep() to " << cvtToHex(nodep->dtypep()));
|
||||
@@ -217,6 +223,7 @@ private:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// VISITORS
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && nodep->brokeLhsMustBeLvalue()
|
||||
@@ -232,10 +239,20 @@ private:
|
||||
}
|
||||
void visit(AstScope* nodep) override {
|
||||
VL_RESTORER(m_inScope);
|
||||
{
|
||||
m_inScope = true;
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
m_inScope = true;
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeUOrStructDType* nodep) override {
|
||||
VL_RESTORER(m_cFuncNames);
|
||||
m_cFuncNames.clear();
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
processAndIterate(nodep);
|
||||
@@ -257,12 +274,19 @@ private:
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
|
||||
VL_RESTORER(m_cfuncp);
|
||||
m_cfuncp = nodep;
|
||||
m_localVars.clear();
|
||||
m_suspectRefs.clear();
|
||||
m_localsStack.clear();
|
||||
pushLocalScope();
|
||||
|
||||
// Check for duplicate names, otherwise linker might just ignore them
|
||||
string nameArgs = nodep->name();
|
||||
if (!nodep->argTypes().empty()) nameArgs += "(" + nodep->argTypes() + ")";
|
||||
UASSERT_OBJ(m_cFuncNames.emplace(nameArgs).second, nodep,
|
||||
"Duplicate cfunc name: '" << nameArgs << "'");
|
||||
|
||||
processAndIterate(nodep);
|
||||
|
||||
// Check suspect references are all to non-locals
|
||||
@@ -270,8 +294,6 @@ private:
|
||||
UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second,
|
||||
"Local variable not in scope where referenced: " << pair.first);
|
||||
}
|
||||
|
||||
m_cfuncp = nullptr;
|
||||
}
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
// Each branch is a separate local variable scope
|
||||
@@ -312,7 +334,7 @@ private:
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit BrokenCheckVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
explicit BrokenCheckVisitor(AstNetlist* nodep) { iterateConstNull(nodep); }
|
||||
~BrokenCheckVisitor() override = default;
|
||||
};
|
||||
|
||||
@@ -351,6 +373,8 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
|
||||
}
|
||||
}
|
||||
|
||||
void V3Broken::allowMidvisitorCheck(bool flag) { s_brokenAllowMidvisitorCheck = flag; }
|
||||
|
||||
//######################################################################
|
||||
// Self test
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@ public:
|
||||
static bool isLinkable(const AstNode* nodep);
|
||||
static void addNewed(const AstNode* nodep);
|
||||
static void deleted(const AstNode* nodep);
|
||||
// Called on error to say may be inside visitor,
|
||||
// Disables checks that may misfire if not called at stable point between visitors
|
||||
static void allowMidvisitorCheck(bool flag);
|
||||
static void selfTest();
|
||||
};
|
||||
|
||||
|
||||
+6
-5
@@ -70,7 +70,7 @@ private:
|
||||
funcp->slow(!m_type.isClass()); // Only classes construct on fast path
|
||||
string preventUnusedStmt;
|
||||
if (m_type.isClass()) {
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->argTypes(EmitCBase::symClassVar());
|
||||
preventUnusedStmt = "if (false && vlSymsp) {} // Prevent unused\n";
|
||||
} else if (m_type.isCoverage()) {
|
||||
funcp->argTypes("bool first");
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
callp->argTypes("vlSymsp");
|
||||
} else {
|
||||
if (m_type.isCoverage()) callp->argTypes("first");
|
||||
callp->selfPointer("this");
|
||||
callp->selfPointer(VSelfPointerText{VSelfPointerText::This()});
|
||||
}
|
||||
rootFuncp->addStmtsp(callp->makeStmt());
|
||||
}
|
||||
@@ -148,6 +148,7 @@ private:
|
||||
m_modp = nodep;
|
||||
V3CCtorsBuilder var_reset{nodep, "_ctor_var_reset",
|
||||
VN_IS(nodep, Class) ? VCtorType::CLASS : VCtorType::MODULE};
|
||||
// cppcheck-suppress danglingLifetime
|
||||
m_varResetp = &var_reset;
|
||||
iterateChildren(nodep);
|
||||
|
||||
@@ -201,7 +202,7 @@ private:
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
CCtorsVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
explicit CCtorsVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
~CCtorsVisitor() override = default;
|
||||
};
|
||||
|
||||
@@ -228,7 +229,7 @@ void V3CCtors::evalAsserts() {
|
||||
// if (signal & CONST(upper_non_clean_mask)) { fail; }
|
||||
AstVarRef* const vrefp
|
||||
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
|
||||
vrefp->selfPointer("this");
|
||||
vrefp->selfPointer(VSelfPointerText{VSelfPointerText::This()});
|
||||
AstNodeExpr* newp = vrefp;
|
||||
if (varp->isWide()) {
|
||||
newp = new AstWordSel{
|
||||
@@ -256,5 +257,5 @@ void V3CCtors::cctorsAll() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
evalAsserts();
|
||||
{ CCtorsVisitor{v3Global.rootp()}; }
|
||||
V3Global::dumpCheckGlobalTree("cctors", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("cctors", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+28
-18
@@ -28,9 +28,11 @@
|
||||
#include "V3CUse.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Global.h"
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
@@ -45,32 +47,32 @@ class CUseVisitor final : public VNVisitor {
|
||||
const VNUser1InUse m_inuser1;
|
||||
|
||||
// MEMBERS
|
||||
bool m_impOnly = false; // In details needed only for implementation
|
||||
AstNodeModule* const m_modp; // Current module
|
||||
std::set<std::pair<VUseType, std::string>> m_didUse; // What we already used
|
||||
std::map<std::string, std::pair<FileLine*, VUseType>> m_didUse; // What we already used
|
||||
|
||||
// METHODS
|
||||
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
|
||||
if (m_didUse.emplace(useType, name).second) {
|
||||
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
|
||||
m_modp->addStmtsp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
auto e = m_didUse.emplace(name, std::make_pair(nodep->fileline(), useType));
|
||||
if (e.second || ((e.first->second.second & useType) != useType)) {
|
||||
e.first->second.second = e.first->second.second | useType;
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstClassRefDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
|
||||
// Need to include extends() when we implement, but no need for pointers to know
|
||||
VL_RESTORER(m_impOnly);
|
||||
{
|
||||
m_impOnly = true;
|
||||
iterateChildren(nodep->classp()); // This also gets all extend classes
|
||||
}
|
||||
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateAndNextNull(nodep->argsp());
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
}
|
||||
void visit(AstCCall* nodep) override { return; }
|
||||
void visit(AstCReturn* nodep) override {
|
||||
UASSERT(!nodep->user1SetOnce(), "Visited same return twice.");
|
||||
iterate(nodep->lhsp()->dtypep());
|
||||
}
|
||||
void visit(AstNodeDType* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
|
||||
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
|
||||
|
||||
@@ -79,11 +81,13 @@ class CUseVisitor final : public VNVisitor {
|
||||
if (stypep && stypep->classOrPackagep()) {
|
||||
addNewUse(nodep, VUseType::INT_INCLUDE, stypep->classOrPackagep()->name());
|
||||
iterateChildren(stypep);
|
||||
} else if (AstClassRefDType* const classp = VN_CAST(nodep->skipRefp(), ClassRefDType)) {
|
||||
addNewUse(nodep, VUseType::INT_FWD_CLASS, classp->name());
|
||||
}
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process once
|
||||
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
|
||||
if (nodep->dtypep()) iterate(nodep->dtypep());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCell* nodep) override {
|
||||
@@ -98,6 +102,12 @@ public:
|
||||
explicit CUseVisitor(AstNodeModule* modp)
|
||||
: m_modp(modp) {
|
||||
iterate(modp);
|
||||
|
||||
for (auto& used : m_didUse) {
|
||||
AstCUse* const newp = new AstCUse{used.second.first, used.second.second, used.first};
|
||||
m_modp->addStmtsp(newp);
|
||||
UINFO(8, "Insert " << newp << endl);
|
||||
}
|
||||
}
|
||||
~CUseVisitor() override = default;
|
||||
VL_UNCOPYABLE(CUseVisitor);
|
||||
@@ -115,5 +125,5 @@ void V3CUse::cUseAll() {
|
||||
// for each output file and put under that
|
||||
CUseVisitor{modp};
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("cuse", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("cuse", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+89
-46
@@ -53,7 +53,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CaseLintVisitor final : public VNVisitor {
|
||||
class CaseLintVisitor final : public VNVisitorConst {
|
||||
private:
|
||||
const AstNodeCase* m_caseExprp
|
||||
= nullptr; // Under a CASE value node, if so the relevant case statement
|
||||
@@ -79,10 +79,10 @@ private:
|
||||
// Check for X/Z in non-casex statements
|
||||
{
|
||||
m_caseExprp = nodep;
|
||||
iterate(nodep->exprp());
|
||||
iterateConst(nodep->exprp());
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
iterateAndNextNull(itemp->condsp());
|
||||
iterateAndNextConstNull(itemp->condsp());
|
||||
}
|
||||
m_caseExprp = nullptr;
|
||||
}
|
||||
@@ -108,11 +108,11 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CaseLintVisitor(AstNodeCase* nodep) { iterate(nodep); }
|
||||
explicit CaseLintVisitor(AstNodeCase* nodep) { iterateConst(nodep); }
|
||||
~CaseLintVisitor() override = default;
|
||||
};
|
||||
|
||||
@@ -139,32 +139,42 @@ private:
|
||||
std::array<AstNode*, 1 << CASE_OVERLAP_WIDTH> m_valueItem;
|
||||
|
||||
// METHODS
|
||||
bool caseIsEnumComplete(AstCase* nodep, uint32_t numCases) {
|
||||
// Return true if case is across an enum, and every value in the case
|
||||
// statement corresponds to one of the enum values
|
||||
if (!nodep->uniquePragma() && !nodep->unique0Pragma()) return false;
|
||||
AstEnumDType* const enumDtp
|
||||
//! Determine whether we should check case items are complete
|
||||
//! @return Enum's dtype if should check, nullptr if shouldn't
|
||||
const AstEnumDType* getEnumCompletionCheckDType(const AstCase* const nodep) {
|
||||
if (!nodep->uniquePragma() && !nodep->unique0Pragma()) return nullptr;
|
||||
const AstEnumDType* const enumDtp
|
||||
= VN_CAST(nodep->exprp()->dtypep()->skipRefToEnump(), EnumDType);
|
||||
if (!enumDtp) return false; // Case isn't enum
|
||||
AstBasicDType* const basicp = enumDtp->subDTypep()->basicp();
|
||||
if (!basicp) return false; // Not simple type (perhaps IEEE illegal)
|
||||
if (basicp->width() > 32) return false;
|
||||
// Find all case values into a set
|
||||
std::set<uint32_t> caseSet;
|
||||
for (uint32_t i = 0; i < numCases; ++i) { // All case items
|
||||
if (m_valueItem[i]) caseSet.emplace(i);
|
||||
}
|
||||
// Find all enum values into a set
|
||||
std::set<uint32_t> enumSet;
|
||||
for (AstEnumItem* itemp = enumDtp->itemsp(); itemp;
|
||||
if (!enumDtp) return nullptr; // Case isn't enum
|
||||
const AstBasicDType* const basicp = enumDtp->subDTypep()->basicp();
|
||||
if (!basicp) return nullptr; // Not simple type (perhaps IEEE illegal)
|
||||
if (basicp->width() > 32) return nullptr;
|
||||
return enumDtp;
|
||||
}
|
||||
//! @return True if case items are complete, false if there are uncovered enums
|
||||
bool checkCaseEnumComplete(const AstCase* const nodep, const AstEnumDType* const dtype) {
|
||||
const uint32_t numCases = 1UL << m_caseWidth;
|
||||
for (AstEnumItem* itemp = dtype->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
||||
AstConst* const econstp = VN_AS(itemp->valuep(), Const);
|
||||
const uint32_t val = econstp->toUInt();
|
||||
// UINFO(9, "Complete enum item " << val << ": " << itemp << endl);
|
||||
enumSet.emplace(val);
|
||||
V3Number nummask{itemp, econstp->width()};
|
||||
nummask.opBitsNonX(econstp->num());
|
||||
const uint32_t mask = nummask.toUInt();
|
||||
V3Number numval{itemp, econstp->width()};
|
||||
numval.opBitsOne(econstp->num());
|
||||
const uint32_t val = numval.toUInt();
|
||||
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if ((i & mask) == val) {
|
||||
if (!m_valueItem[i]) {
|
||||
nodep->v3warn(CASEINCOMPLETE, "Enum item " << itemp->prettyNameQ()
|
||||
<< " not covered by case\n");
|
||||
return false; // enum has uncovered value by case items
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If sets match, all covered
|
||||
return (caseSet == enumSet);
|
||||
return true; // enum is fully covered
|
||||
}
|
||||
bool isCaseTreeFast(AstCase* nodep) {
|
||||
int width = 0;
|
||||
@@ -193,6 +203,8 @@ private:
|
||||
// We can cheat and use uint32_t's because we only support narrow case's
|
||||
bool reportedOverlap = false;
|
||||
bool reportedSubcase = false;
|
||||
bool hasDefaultCase = false;
|
||||
std::map<AstNode*, AstCaseItem*> caseItemMap; // case condition -> case item
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) {
|
||||
@@ -202,6 +214,7 @@ private:
|
||||
if (neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
} else {
|
||||
const bool isCondWildcard = iconstp->num().isAnyXZ();
|
||||
V3Number nummask{itemp, iconstp->width()};
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
const uint32_t mask = nummask.toUInt();
|
||||
@@ -210,16 +223,17 @@ private:
|
||||
const uint32_t val = numval.toUInt();
|
||||
|
||||
uint32_t firstOverlap = 0;
|
||||
bool foundOverlap = false;
|
||||
AstNode* overlappedCondp = nullptr;
|
||||
bool foundHit = false;
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if ((i & mask) == val) {
|
||||
if (!m_valueItem[i]) {
|
||||
m_valueItem[i] = itemp;
|
||||
m_valueItem[i] = icondp;
|
||||
caseItemMap[icondp] = itemp;
|
||||
foundHit = true;
|
||||
} else if (!foundOverlap) {
|
||||
} else if (!overlappedCondp) {
|
||||
firstOverlap = i;
|
||||
foundOverlap = true;
|
||||
overlappedCondp = m_valueItem[i];
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
}
|
||||
}
|
||||
@@ -227,9 +241,19 @@ private:
|
||||
if (!nodep->priorityPragma()) {
|
||||
// If this case statement doesn't have the priority
|
||||
// keyword, we want to warn on any overlap.
|
||||
if (!reportedOverlap && foundOverlap) {
|
||||
icondp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x"
|
||||
<< std::hex << firstOverlap << ")");
|
||||
if (!reportedOverlap && overlappedCondp) {
|
||||
std::ostringstream examplePattern;
|
||||
if (isCondWildcard) {
|
||||
examplePattern << " (example pattern 0x" << std::hex
|
||||
<< firstOverlap << ")";
|
||||
}
|
||||
icondp->v3warn(CASEOVERLAP,
|
||||
"Case conditions overlap"
|
||||
<< examplePattern.str() << "\n"
|
||||
<< icondp->warnContextPrimary() << '\n'
|
||||
<< overlappedCondp->warnOther()
|
||||
<< "... Location of overlapping condition\n"
|
||||
<< overlappedCondp->warnContextSecondary());
|
||||
reportedOverlap = true;
|
||||
}
|
||||
} else {
|
||||
@@ -240,7 +264,11 @@ private:
|
||||
if (!reportedSubcase && !foundHit) {
|
||||
icondp->v3warn(CASEOVERLAP,
|
||||
"Case item ignored: every matching value is covered "
|
||||
"by an earlier item");
|
||||
"by an earlier condition\n"
|
||||
<< icondp->warnContextPrimary() << '\n'
|
||||
<< overlappedCondp->warnOther()
|
||||
<< "... Location of previous condition\n"
|
||||
<< overlappedCondp->warnContextPrimary());
|
||||
reportedSubcase = true;
|
||||
}
|
||||
}
|
||||
@@ -251,17 +279,28 @@ private:
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (!m_valueItem[i]) m_valueItem[i] = itemp;
|
||||
}
|
||||
caseItemMap[itemp] = itemp;
|
||||
hasDefaultCase = true;
|
||||
}
|
||||
}
|
||||
if (!caseIsEnumComplete(nodep, numCases)) {
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (!m_valueItem[i]) {
|
||||
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered "
|
||||
"(example pattern 0x"
|
||||
<< std::hex << i << ")");
|
||||
if (!hasDefaultCase) {
|
||||
const AstEnumDType* const dtype = getEnumCompletionCheckDType(nodep);
|
||||
if (dtype) {
|
||||
if (!checkCaseEnumComplete(nodep, dtype)) {
|
||||
// checkCaseEnumComplete has already warned of incompletion
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (!m_valueItem[i]) { // has uncovered case
|
||||
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered "
|
||||
"(example pattern 0x"
|
||||
<< std::hex << i << ")");
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,8 +313,10 @@ private:
|
||||
// Convert valueItem from AstCaseItem* to the expression
|
||||
// Not done earlier, as we may now have a nullptr because it's just a ";" NOP branch
|
||||
for (uint32_t i = 0; i < numCases; ++i) {
|
||||
if (AstCaseItem* const itemp = VN_AS(m_valueItem[i], CaseItem)) {
|
||||
m_valueItem[i] = itemp->stmtsp();
|
||||
if (AstNode* const condp = m_valueItem[i]) {
|
||||
AstCaseItem* caseItemp = caseItemMap[condp];
|
||||
UASSERT(caseItemp, "caseItemp should exist");
|
||||
m_valueItem[i] = caseItemp->stmtsp();
|
||||
}
|
||||
}
|
||||
return true; // All is fine
|
||||
@@ -543,10 +584,12 @@ private:
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
void visit(AstNode* nodep) override {
|
||||
if (VN_IS(nodep, Always)) m_alwaysp = nodep;
|
||||
void visit(AstAlways* nodep) override {
|
||||
VL_RESTORER(m_alwaysp)
|
||||
m_alwaysp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -566,7 +609,7 @@ public:
|
||||
void V3Case::caseAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CaseVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("case", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("case", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
void V3Case::caseLint(AstNodeCase* nodep) {
|
||||
UINFO(4, __FUNCTION__ << ": " << endl);
|
||||
|
||||
+28
-2
@@ -122,6 +122,31 @@ private:
|
||||
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
|
||||
}
|
||||
void visit(AstNodeCond* nodep) override {
|
||||
// All class types are castable to each other. If they are of different types,
|
||||
// a compilation error will be thrown, so an explicit cast is required. Types were
|
||||
// already checked by V3Width and dtypep of a condition operator is a type of their
|
||||
// common base class, so both classes can be safetly casted.
|
||||
const AstClassRefDType* const thenClassDtypep
|
||||
= VN_CAST(nodep->thenp()->dtypep(), ClassRefDType);
|
||||
const AstClassRefDType* const elseClassDtypep
|
||||
= VN_CAST(nodep->elsep()->dtypep(), ClassRefDType);
|
||||
const bool castRequired = thenClassDtypep && elseClassDtypep
|
||||
&& (thenClassDtypep->classp() != elseClassDtypep->classp());
|
||||
if (castRequired) {
|
||||
const AstClass* const commonBaseClassp
|
||||
= VN_AS(nodep->dtypep(), ClassRefDType)->classp();
|
||||
if (thenClassDtypep->classp() != commonBaseClassp) {
|
||||
AstNodeExpr* thenp = nodep->thenp()->unlinkFrBack();
|
||||
nodep->thenp(new AstCCast{thenp->fileline(), thenp, nodep});
|
||||
}
|
||||
if (elseClassDtypep->classp() != commonBaseClassp) {
|
||||
AstNodeExpr* elsep = nodep->elsep()->unlinkFrBack();
|
||||
nodep->elsep(new AstCCast{elsep->fileline(), elsep, nodep});
|
||||
}
|
||||
}
|
||||
visit(static_cast<AstNodeTriop*>(nodep));
|
||||
}
|
||||
void visit(AstNodeTriop* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1());
|
||||
@@ -161,7 +186,8 @@ private:
|
||||
&& !VN_IS(backp, NodeCCall) && !VN_IS(backp, CMethodHard) && !VN_IS(backp, SFormatF)
|
||||
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, StructSel) && !VN_IS(backp, RedXor)
|
||||
&& (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec()
|
||||
&& !nodep->varp()->basicp()->isForkSync())
|
||||
&& !nodep->varp()->basicp()->isForkSync()
|
||||
&& !nodep->varp()->basicp()->isProcessRef())
|
||||
&& 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);
|
||||
@@ -208,5 +234,5 @@ public:
|
||||
void V3Cast::castAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CastVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("cast", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("cast", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+12
-7
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
@@ -41,6 +42,7 @@ private:
|
||||
|
||||
// MEMBERS
|
||||
string m_prefix; // String prefix to add to name based on hier
|
||||
V3UniqueNames m_names; // For unique naming of structs and unions
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstNodeModule* m_classPackagep = nullptr; // Package moving into
|
||||
const AstScope* m_classScopep = nullptr; // Package moving scopes into
|
||||
@@ -62,8 +64,10 @@ private:
|
||||
AstClassPackage* const packagep
|
||||
= new AstClassPackage{nodep->fileline(), nodep->origName()};
|
||||
packagep->name(nodep->name() + "__Vclpkg");
|
||||
nodep->editCountInc();
|
||||
nodep->classOrPackagep(packagep);
|
||||
packagep->classp(nodep);
|
||||
packagep->timeunit(nodep->timeunit());
|
||||
v3Global.rootp()->addModulesp(packagep);
|
||||
// Add package to hierarchy
|
||||
AstCell* const cellp = new AstCell{packagep->fileline(),
|
||||
@@ -102,7 +106,6 @@ private:
|
||||
m_prefix = nodep->name() + "__02e"; // .
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
nodep->repairCache();
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
|
||||
@@ -177,14 +180,15 @@ private:
|
||||
}
|
||||
|
||||
void setStructModulep(AstNodeUOrStructDType* const dtypep) {
|
||||
// Give it a pointer to its package and a final name
|
||||
dtypep->classOrPackagep(m_modp);
|
||||
dtypep->name(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")
|
||||
+ cvtToStr(dtypep->uniqueNum()));
|
||||
// Give struct a pointer to its package and a final name
|
||||
dtypep->editCountInc();
|
||||
dtypep->classOrPackagep(m_classPackagep ? m_classPackagep : m_modp);
|
||||
dtypep->name(
|
||||
m_names.get(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")));
|
||||
|
||||
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
AstNodeUOrStructDType* const subp = VN_CAST(itemp->skipRefp(), NodeUOrStructDType);
|
||||
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
|
||||
// Recurse only into anonymous unpacked structs inside this definition,
|
||||
// other unpacked structs will be reached from another typedefs
|
||||
if (subp && !subp->packed() && subp->name().empty()) setStructModulep(subp);
|
||||
@@ -193,6 +197,7 @@ private:
|
||||
void visit(AstTypedef* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
iterateChildren(nodep);
|
||||
if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack());
|
||||
|
||||
AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType);
|
||||
if (dtypep && !dtypep->packed()) {
|
||||
@@ -243,5 +248,5 @@ public:
|
||||
void V3Class::classAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ClassVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("class", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("class", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+7
-2
@@ -95,6 +95,7 @@ private:
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), StreamDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
|
||||
} else {
|
||||
@@ -298,7 +299,11 @@ private:
|
||||
}
|
||||
void visit(AstWith* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureCleanAndNext(nodep->exprp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstCReturn* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureClean(nodep->lhsp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstIntfRef* nodep) override {
|
||||
@@ -325,5 +330,5 @@ public:
|
||||
void V3Clean::cleanAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CleanVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("clean", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("clean", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+6
-4
@@ -95,7 +95,7 @@ private:
|
||||
if (vscp->user1p()) return VN_AS(vscp->user1p(), VarScope);
|
||||
const AstVar* const varp = vscp->varp();
|
||||
const string newvarname
|
||||
= string("__Vsampled__") + vscp->scopep()->nameDotless() + "__" + varp->name();
|
||||
= string{"__Vsampled__"} + vscp->scopep()->nameDotless() + "__" + varp->name();
|
||||
FileLine* const flp = vscp->fileline();
|
||||
AstVar* const newvarp = new AstVar{flp, VVarType::MODULETEMP, newvarname, varp->dtypep()};
|
||||
newvarp->noReset(true); // Reset by below assign
|
||||
@@ -146,17 +146,19 @@ private:
|
||||
UASSERT_OBJ(nodep->hasClocked(), nodep, "Should have been converted by V3Sched");
|
||||
UASSERT_OBJ(nodep->stmtsp(), nodep, "Should not have been created if empty");
|
||||
|
||||
VNRelinker relinker;
|
||||
nodep->unlinkFrBack(&relinker);
|
||||
AstNode* const stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
|
||||
// Create 'if' statement, if needed
|
||||
if (!m_lastSenp || !nodep->sensesp()->sameTree(m_lastSenp)) {
|
||||
VNRelinker relinker;
|
||||
nodep->unlinkFrBack(&relinker);
|
||||
clearLastSen();
|
||||
m_lastSenp = nodep->sensesp();
|
||||
// Make a new if statement
|
||||
m_lastIfp = makeActiveIf(m_lastSenp);
|
||||
relinker.relink(m_lastIfp);
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
|
||||
// Move statements to if
|
||||
@@ -222,5 +224,5 @@ public:
|
||||
void V3Clock::clockAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ClockVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("clock", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("clock", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+1
-1
@@ -238,5 +238,5 @@ public:
|
||||
void V3Combine::combineAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
CombineVisitor::apply(nodep);
|
||||
V3Global::dumpCheckGlobalTree("combine", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("combine", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+11
-8
@@ -37,7 +37,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
static void makeVlToString(AstClass* nodep) {
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
|
||||
funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep) + ">& obj");
|
||||
funcp->argTypes("const VlClassRef<" + EmitCBase::prefixNameProtect(nodep) + ">& obj");
|
||||
funcp->isMethod(false);
|
||||
funcp->isConst(false);
|
||||
funcp->isStatic(false);
|
||||
@@ -51,7 +51,7 @@ static void makeVlToString(AstClass* nodep) {
|
||||
static void makeVlToString(AstIface* nodep) {
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
|
||||
funcp->argTypes("const " + EmitCBaseVisitor::prefixNameProtect(nodep) + "* obj");
|
||||
funcp->argTypes("const " + EmitCBase::prefixNameProtect(nodep) + "* obj");
|
||||
funcp->isMethod(false);
|
||||
funcp->isConst(false);
|
||||
funcp->isStatic(false);
|
||||
@@ -63,9 +63,10 @@ static void makeVlToString(AstIface* nodep) {
|
||||
}
|
||||
static void makeVlToString(AstNodeUOrStructDType* nodep) {
|
||||
AstNodeModule* const modp = nodep->classOrPackagep();
|
||||
UASSERT_OBJ(modp, nodep, "Unlinked struct package");
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
|
||||
funcp->argTypes("const " + EmitCBaseVisitor::prefixNameProtect(nodep) + "& obj");
|
||||
funcp->argTypes("const " + EmitCBase::prefixNameProtect(nodep) + "& obj");
|
||||
funcp->isMethod(false);
|
||||
funcp->isConst(false);
|
||||
funcp->isStatic(false);
|
||||
@@ -81,11 +82,13 @@ static void makeVlToString(AstNodeUOrStructDType* nodep) {
|
||||
}
|
||||
stmt += VIdProtect::protect(itemp->prettyName()) + ":\" + ";
|
||||
if (VN_IS(itemp->dtypep()->skipRefp(), BasicDType) && itemp->isWide()) {
|
||||
stmt += "VL_TO_STRING_W";
|
||||
stmt += "VL_TO_STRING_W(";
|
||||
stmt += cvtToStr(itemp->widthWords());
|
||||
stmt += ", ";
|
||||
} else {
|
||||
stmt += "VL_TO_STRING";
|
||||
stmt += "VL_TO_STRING(";
|
||||
}
|
||||
stmt += "(obj." + itemp->nameProtect() + ");\n";
|
||||
stmt += "obj." + itemp->nameProtect() + ");\n";
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
|
||||
}
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "out += \"}\";\n"});
|
||||
@@ -137,7 +140,7 @@ static void makeToStringMiddle(AstClass* nodep) {
|
||||
string stmt = "out += ";
|
||||
if (!comma.empty()) stmt += "\", \"+ ";
|
||||
// comma = ", "; // Nothing further so not needed
|
||||
stmt += EmitCBaseVisitor::prefixNameProtect(nodep->extendsp()->dtypep());
|
||||
stmt += EmitCBase::prefixNameProtect(nodep->extendsp()->dtypep());
|
||||
stmt += "::to_string_middle();\n";
|
||||
nodep->user1(true); // So what we extend dumps this
|
||||
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
|
||||
@@ -172,5 +175,5 @@ void V3Common::commonAll() {
|
||||
if (!dtypep->packed()) makeVlToString(dtypep);
|
||||
}
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("common", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("common", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+15
-6
@@ -40,25 +40,31 @@ template <typename T>
|
||||
class V3ConfigWildcardResolver final {
|
||||
using Map = std::map<const std::string, T>;
|
||||
|
||||
Map m_mapWildcard; // Wildcard strings to entities
|
||||
Map m_mapResolved; // Resolved strings to converged entities
|
||||
mutable V3Mutex m_mutex; // protects members
|
||||
Map m_mapWildcard VL_GUARDED_BY(m_mutex); // Wildcard strings to entities
|
||||
Map m_mapResolved VL_GUARDED_BY(m_mutex); // Resolved strings to converged entities
|
||||
public:
|
||||
V3ConfigWildcardResolver() = default;
|
||||
~V3ConfigWildcardResolver() = default;
|
||||
|
||||
/// Update into maps from other
|
||||
void update(const V3ConfigWildcardResolver& other) {
|
||||
void update(const V3ConfigWildcardResolver& other) VL_MT_SAFE_EXCLUDES(m_mutex)
|
||||
VL_EXCLUDES(other.m_mutex) {
|
||||
V3LockGuard lock{m_mutex};
|
||||
V3LockGuard otherLock{other.m_mutex};
|
||||
for (const auto& itr : other.m_mapResolved) m_mapResolved[itr.first].update(itr.second);
|
||||
for (const auto& itr : other.m_mapWildcard) m_mapWildcard[itr.first].update(itr.second);
|
||||
}
|
||||
|
||||
// Access and create a (wildcard) entity
|
||||
T& at(const string& name) {
|
||||
T& at(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
V3LockGuard lock{m_mutex};
|
||||
// Don't store into wildcards if the name is not a wildcard string
|
||||
return m_mapWildcard[name];
|
||||
}
|
||||
// Access an entity and resolve wildcards that match it
|
||||
T* resolve(const string& name) {
|
||||
T* resolve(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
V3LockGuard lock{m_mutex};
|
||||
// Lookup if it was resolved before, typically not
|
||||
auto it = m_mapResolved.find(name);
|
||||
if (VL_UNLIKELY(it != m_mapResolved.end())) return &it->second;
|
||||
@@ -78,7 +84,10 @@ public:
|
||||
return newp;
|
||||
}
|
||||
// Flush on update
|
||||
void flush() { m_mapResolved.clear(); }
|
||||
void flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
V3LockGuard lock{m_mutex};
|
||||
m_mapResolved.clear();
|
||||
}
|
||||
};
|
||||
|
||||
// Only public_flat_rw has the sensitity tree
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Mutex.h"
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
||||
+235
-132
@@ -70,7 +70,7 @@ static int countTrailingZeroes(uint64_t val) {
|
||||
// This visitor can be used in the post-expanded Ast from V3Expand, where the Ast satisfies:
|
||||
// - Constants are 64 bit at most (because words are accessed via AstWordSel)
|
||||
// - Variables are scoped.
|
||||
class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
||||
// NODE STATE
|
||||
// AstVarRef::user4u -> Base index of m_varInfos that points VarInfo
|
||||
// AstVarScope::user4u -> Same as AstVarRef::user4
|
||||
@@ -416,7 +416,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
{
|
||||
VL_RESTORER(m_leafp);
|
||||
m_leafp = &info;
|
||||
iterate(nodep);
|
||||
iterateConst(nodep);
|
||||
}
|
||||
|
||||
bool ok = !m_failed;
|
||||
@@ -431,7 +431,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
// VISITORS
|
||||
void visit(AstNode* nodep) override { CONST_BITOP_SET_FAILED("Hit unexpected op", nodep); }
|
||||
void visit(AstCCast* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
if (m_leafp) m_leafp->updateBitRange(nodep);
|
||||
}
|
||||
void visit(AstShiftR* nodep) override {
|
||||
@@ -440,7 +440,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
CONST_BITOP_RETURN_IF(!constp, nodep->rhsp());
|
||||
m_lsb += constp->toUInt();
|
||||
incrOps(nodep, __LINE__);
|
||||
iterate(nodep->lhsp());
|
||||
iterateConst(nodep->lhsp());
|
||||
m_leafp->updateBitRange(nodep);
|
||||
m_lsb -= constp->toUInt();
|
||||
}
|
||||
@@ -449,12 +449,10 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
AstNode* lhsp = nodep->lhsp();
|
||||
AstCCast* const castp = VN_CAST(lhsp, CCast);
|
||||
if (castp) lhsp = castp->lhsp();
|
||||
CONST_BITOP_RETURN_IF(!VN_IS(lhsp, VarRef) && !VN_IS(lhsp, Xor) && !VN_IS(lhsp, RedXor)
|
||||
&& !VN_IS(lhsp, ShiftR),
|
||||
lhsp);
|
||||
CONST_BITOP_RETURN_IF(!isXorTree() && !VN_IS(lhsp, VarRef) && !VN_IS(lhsp, ShiftR), lhsp);
|
||||
incrOps(nodep, __LINE__);
|
||||
m_polarity = !m_polarity;
|
||||
iterateChildren(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
// Don't restore m_polarity for Xor as it counts parity of the entire tree
|
||||
if (!isXorTree()) m_polarity = !m_polarity;
|
||||
if (m_leafp && castp) m_leafp->updateBitRange(castp);
|
||||
@@ -465,7 +463,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
CONST_BITOP_RETURN_IF(!constp, nodep->rhsp());
|
||||
UASSERT_OBJ(m_leafp->wordIdx() == -1, nodep, "Unexpected nested WordSel");
|
||||
m_leafp->wordIdx(constp->toSInt());
|
||||
iterate(nodep->fromp());
|
||||
iterateConst(nodep->fromp());
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
|
||||
@@ -529,7 +527,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
// Always reach past a plain making AND
|
||||
Restorer restorer{*this};
|
||||
incrOps(nodep, __LINE__);
|
||||
iterate(nodep->rhsp());
|
||||
iterateConst(nodep->rhsp());
|
||||
CONST_BITOP_RETURN_IF(m_failed, nodep->rhsp());
|
||||
restorer.disableRestore(); // Now all checks passed
|
||||
} else if (nodep->type() == m_rootp->type()) { // And, Or, Xor
|
||||
@@ -542,13 +540,14 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
m_leafp = &leafInfo;
|
||||
AstNodeExpr* opp = right ? nodep->rhsp() : nodep->lhsp();
|
||||
const bool origFailed = m_failed;
|
||||
iterate(opp);
|
||||
iterateConst(opp);
|
||||
if (leafInfo.constp() || m_failed) {
|
||||
// Revert changes in leaf
|
||||
restorer.restoreNow();
|
||||
// Reach past a cast then add to frozen nodes to be added to final reduction
|
||||
if (const AstCCast* const castp = VN_CAST(opp, CCast)) opp = castp->lhsp();
|
||||
m_frozenNodes.emplace_back(opp, FrozenNodeInfo{m_polarity, m_lsb});
|
||||
const bool pol = isXorTree() || m_polarity; // Only AND/OR tree needs polarity
|
||||
m_frozenNodes.emplace_back(opp, FrozenNodeInfo{pol, m_lsb});
|
||||
m_failed = origFailed;
|
||||
continue;
|
||||
}
|
||||
@@ -647,11 +646,11 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
|
||||
m_varInfos.push_back(nullptr);
|
||||
CONST_BITOP_RETURN_IF(!isAndTree() && !isOrTree() && !isXorTree(), nodep);
|
||||
if (AstNodeBiop* const biopp = VN_CAST(nodep, NodeBiop)) {
|
||||
iterate(biopp);
|
||||
iterateConst(biopp);
|
||||
} else {
|
||||
UASSERT_OBJ(VN_IS(nodep, RedXor), nodep, "Must be RedXor");
|
||||
incrOps(nodep, __LINE__);
|
||||
iterateChildren(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
for (auto&& entry : m_bitPolarities) {
|
||||
getVarInfo(entry.m_info).setPolarity(entry.m_polarity, entry.m_bit);
|
||||
@@ -1800,7 +1799,7 @@ private:
|
||||
UASSERT_OBJ((rstart + rwidth) == lstart, nodep,
|
||||
"tried to merge two selects which are not adjacent");
|
||||
AstSel* const newselp = new AstSel{
|
||||
lselp->fromp()->fileline(), rselp->fromp()->cloneTree(false), rstart, lwidth + rwidth};
|
||||
lselp->fromp()->fileline(), rselp->fromp()->unlinkFrBack(), rstart, lwidth + rwidth};
|
||||
UINFO(5, "merged two adjacent sel " << lselp << " and " << rselp << " to one " << newselp
|
||||
<< endl);
|
||||
|
||||
@@ -1810,6 +1809,7 @@ private:
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
void replaceConcatMerge(AstConcat* nodep) {
|
||||
// {llp OP lrp, rlp OP rrp} => {llp, rlp} OP {lrp, rrp}, where OP = AND/OR/XOR
|
||||
AstNodeBiop* const lp = VN_AS(nodep->lhsp(), NodeBiop);
|
||||
AstNodeBiop* const rp = VN_AS(nodep->rhsp(), NodeBiop);
|
||||
AstNodeExpr* const llp = lp->lhsp()->cloneTree(false);
|
||||
@@ -2053,7 +2053,7 @@ private:
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return true;
|
||||
}
|
||||
} else if (m_doV && VN_IS(nodep->lhsp(), Concat)) {
|
||||
} else if (m_doV && VN_IS(nodep->lhsp(), Concat) && nodep->isTreePureRecurse()) {
|
||||
bool need_temp = false;
|
||||
if (m_warn && !VN_IS(nodep, AssignDly)) { // Is same var on LHS and RHS?
|
||||
// Note only do this (need user4) when m_warn, which is
|
||||
@@ -2146,33 +2146,42 @@ private:
|
||||
return true;
|
||||
} else if (m_doV && VN_IS(nodep->rhsp(), StreamR)) {
|
||||
// The right-streaming operator on rhs of assignment does not
|
||||
// change the order of bits. Eliminate stream but keep its lhsp
|
||||
// Unlink the stuff
|
||||
AstNodeExpr* const srcp = VN_AS(nodep->rhsp(), StreamR)->lhsp()->unlinkFrBack();
|
||||
AstNode* const sizep = VN_AS(nodep->rhsp(), StreamR)->rhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack();
|
||||
// change the order of bits. Eliminate stream but keep its lhsp.
|
||||
// Add a cast if needed.
|
||||
AstStreamR* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack();
|
||||
AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack();
|
||||
AstNodeDType* const srcDTypep = srcp->dtypep();
|
||||
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
|
||||
if (nodep->lhsp()->widthMin() > 64) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
|
||||
"array to a variable of size greater than 64");
|
||||
}
|
||||
srcp = new AstCvtDynArrayToPacked{srcp->fileline(), srcp, srcDTypep};
|
||||
}
|
||||
nodep->rhsp(srcp);
|
||||
// Cleanup
|
||||
VL_DO_DANGLING(sizep->deleteTree(), sizep);
|
||||
VL_DO_DANGLING(streamp->deleteTree(), streamp);
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
} else if (m_doV && VN_IS(nodep->lhsp(), StreamL)) {
|
||||
// Push the stream operator to the rhs of the assignment statement
|
||||
const int dWidth = VN_AS(nodep->lhsp(), StreamL)->lhsp()->width();
|
||||
const int sWidth = nodep->rhsp()->width();
|
||||
// Unlink the stuff
|
||||
AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamL)->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* streamp = VN_AS(nodep->lhsp(), StreamL)->unlinkFrBack();
|
||||
AstNodeExpr* streamp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const dstp = VN_AS(streamp, StreamL)->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
|
||||
const int sWidth = srcp->width();
|
||||
const int dWidth = dstp->width();
|
||||
// Connect the rhs to the stream operator and update its width
|
||||
VN_AS(streamp, StreamL)->lhsp(srcp);
|
||||
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
|
||||
// Shrink the RHS if necessary
|
||||
if (sWidth > dWidth) {
|
||||
if (VN_IS(srcp->dtypep(), DynArrayDType) || VN_IS(srcp->dtypep(), QueueDType)
|
||||
|| VN_IS(srcp->dtypep(), UnpackArrayDType)) {
|
||||
streamp->dtypeSetStream();
|
||||
} else {
|
||||
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
|
||||
}
|
||||
if (dWidth == 0) {
|
||||
streamp = new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()};
|
||||
} else if (sWidth > dWidth) {
|
||||
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
|
||||
}
|
||||
// Link the nodes back in
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(streamp);
|
||||
return true;
|
||||
@@ -2180,23 +2189,35 @@ private:
|
||||
// The right stream operator on lhs of assignment statement does
|
||||
// not reorder bits. However, if the rhs is wider than the lhs,
|
||||
// then we select bits from the left-most, not the right-most.
|
||||
const int dWidth = VN_AS(nodep->lhsp(), StreamR)->lhsp()->width();
|
||||
const int sWidth = nodep->rhsp()->width();
|
||||
// Unlink the stuff
|
||||
AstNodeExpr* const dstp = VN_AS(nodep->lhsp(), StreamR)->lhsp()->unlinkFrBack();
|
||||
AstNode* const sizep = VN_AS(nodep->lhsp(), StreamR)->rhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const streamp = VN_AS(nodep->lhsp(), StreamR)->unlinkFrBack();
|
||||
AstNodeExpr* const streamp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const dstp = VN_AS(streamp, StreamR)->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
if (sWidth > dWidth) {
|
||||
const int sWidth = srcp->width();
|
||||
const int dWidth = dstp->width();
|
||||
if (dWidth == 0) {
|
||||
srcp = new AstCvtPackedToDynArray{nodep->fileline(), srcp, dstp->dtypep()};
|
||||
} else if (sWidth > dWidth) {
|
||||
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
|
||||
}
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(srcp);
|
||||
// Cleanup
|
||||
VL_DO_DANGLING(sizep->deleteTree(), sizep);
|
||||
VL_DO_DANGLING(streamp->deleteTree(), streamp);
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
} else if (m_doV && VN_IS(nodep->rhsp(), StreamL)) {
|
||||
AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep();
|
||||
AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL);
|
||||
AstNodeExpr* const srcp = streamp->lhsp();
|
||||
const AstNodeDType* const srcDTypep = srcp->dtypep();
|
||||
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
|
||||
if (lhsDtypep->widthMin() > 64) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
|
||||
"array to a variable of size greater than 64");
|
||||
}
|
||||
srcp->unlinkFrBack();
|
||||
streamp->lhsp(new AstCvtDynArrayToPacked{srcp->fileline(), srcp, lhsDtypep});
|
||||
streamp->dtypeFrom(lhsDtypep);
|
||||
}
|
||||
} else if (m_doV && replaceAssignMultiSel(nodep)) {
|
||||
return true;
|
||||
}
|
||||
@@ -2265,7 +2286,7 @@ private:
|
||||
// VISITORS
|
||||
void visit(AstNetlist* nodep) override {
|
||||
// Iterate modules backwards, in bottom-up order. That's faster
|
||||
iterateChildrenBackwards(nodep);
|
||||
iterateChildrenBackwardsConst(nodep);
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
@@ -2284,6 +2305,13 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstCLocalScope* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->stmtsp()) {
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstScope* nodep) override {
|
||||
// No ASSIGNW removals under scope, we've long eliminated INITIALs
|
||||
VL_RESTORER(m_wremove);
|
||||
@@ -2747,52 +2775,74 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
struct SenItemCmp {
|
||||
bool operator()(const AstSenItem* lhsp, const AstSenItem* rhsp) const {
|
||||
if (lhsp->type() < rhsp->type()) return true;
|
||||
if (lhsp->type() > rhsp->type()) return false;
|
||||
// Looks visually better if we keep sorted by name
|
||||
if (!lhsp->sensp() && rhsp->sensp()) return true;
|
||||
if (lhsp->sensp() && !rhsp->sensp()) return false;
|
||||
if (lhsp->varrefp() && !rhsp->varrefp()) return true;
|
||||
if (!lhsp->varrefp() && rhsp->varrefp()) return false;
|
||||
if (lhsp->varrefp() && rhsp->varrefp()) {
|
||||
if (lhsp->varrefp()->name() < rhsp->varrefp()->name()) return true;
|
||||
if (lhsp->varrefp()->name() > rhsp->varrefp()->name()) return false;
|
||||
class SenItemCmp final {
|
||||
static int cmp(const AstNodeExpr* ap, const AstNodeExpr* bp) {
|
||||
const VNType aType = ap->type();
|
||||
const VNType bType = bp->type();
|
||||
if (aType != bType) return static_cast<int>(bType) - static_cast<int>(aType);
|
||||
|
||||
if (const AstVarRef* const aRefp = VN_CAST(ap, VarRef)) {
|
||||
const AstVarRef* const bRefp = VN_AS(bp, VarRef);
|
||||
// Looks visually better if we keep sorted by name
|
||||
if (aRefp->name() < bRefp->name()) return -1;
|
||||
if (aRefp->name() > bRefp->name()) return 1;
|
||||
// But might be same name with different scopes
|
||||
if (lhsp->varrefp()->varScopep() < rhsp->varrefp()->varScopep()) return true;
|
||||
if (lhsp->varrefp()->varScopep() > rhsp->varrefp()->varScopep()) return false;
|
||||
if (aRefp->varScopep() < bRefp->varScopep()) return -1;
|
||||
if (aRefp->varScopep() > bRefp->varScopep()) return 1;
|
||||
// Or rarely, different data types
|
||||
if (lhsp->varrefp()->dtypep() < rhsp->varrefp()->dtypep()) return true;
|
||||
if (lhsp->varrefp()->dtypep() > rhsp->varrefp()->dtypep()) return false;
|
||||
} else if (AstCMethodHard* const lp = VN_CAST(lhsp->sensp(), CMethodHard)) {
|
||||
if (AstCMethodHard* const rp = VN_CAST(rhsp->sensp(), CMethodHard)) {
|
||||
if (AstVarRef* const lRefp = VN_CAST(lp->fromp(), VarRef)) {
|
||||
if (AstVarRef* const rRefp = VN_CAST(rp->fromp(), VarRef)) {
|
||||
if (lRefp->name() < rRefp->name()) return true;
|
||||
if (lRefp->name() > rRefp->name()) return false;
|
||||
// But might be same name with different scopes
|
||||
if (lRefp->varScopep() < rRefp->varScopep()) return true;
|
||||
if (lRefp->varScopep() > rRefp->varScopep()) return false;
|
||||
// Or rarely, different data types
|
||||
if (lRefp->dtypep() < rRefp->dtypep()) return true;
|
||||
if (lRefp->dtypep() > rRefp->dtypep()) return false;
|
||||
}
|
||||
}
|
||||
if (AstConst* lConstp = VN_CAST(lp->pinsp(), Const)) {
|
||||
if (AstConst* rConstp = VN_CAST(rp->pinsp(), Const)) {
|
||||
if (lConstp->toUInt() < rConstp->toUInt()) return true;
|
||||
if (lConstp->toUInt() > rConstp->toUInt()) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aRefp->dtypep() < bRefp->dtypep()) return -1;
|
||||
if (aRefp->dtypep() > bRefp->dtypep()) return 1;
|
||||
return 0;
|
||||
}
|
||||
// Sort by edge, AFTER variable, as we want multiple edges for same var adjacent.
|
||||
// note the SenTree optimizer requires this order (more
|
||||
// general first, less general last)
|
||||
if (lhsp->edgeType() < rhsp->edgeType()) return true;
|
||||
if (lhsp->edgeType() > rhsp->edgeType()) return false;
|
||||
return false;
|
||||
|
||||
if (const AstConst* const aConstp = VN_CAST(ap, Const)) {
|
||||
const AstConst* const bConstp = VN_AS(bp, Const);
|
||||
if (aConstp->toUQuad() < bConstp->toUQuad()) return -1;
|
||||
if (aConstp->toUQuad() > bConstp->toUQuad()) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (const AstNodeBiop* const aBiOpp = VN_CAST(ap, NodeBiop)) {
|
||||
const AstNodeBiop* const bBiOpp = VN_AS(bp, NodeBiop);
|
||||
// Compare RHSs first as LHS might be const, but the variable term should become
|
||||
// adjacent for optimization if identical.
|
||||
if (const int c = cmp(aBiOpp->rhsp(), bBiOpp->rhsp())) return c;
|
||||
return cmp(aBiOpp->lhsp(), bBiOpp->lhsp());
|
||||
}
|
||||
|
||||
if (const AstCMethodHard* const aCallp = VN_CAST(ap, CMethodHard)) {
|
||||
const AstCMethodHard* const bCallp = VN_AS(bp, CMethodHard);
|
||||
if (aCallp->name() < bCallp->name()) return -1;
|
||||
if (aCallp->name() > bCallp->name()) return 1;
|
||||
if (const int c = cmp(aCallp->fromp(), bCallp->fromp())) return c;
|
||||
AstNodeExpr* aPinsp = aCallp->pinsp();
|
||||
AstNodeExpr* bPinsp = bCallp->pinsp();
|
||||
while (aPinsp && bPinsp) {
|
||||
if (const int c = cmp(aPinsp, bPinsp)) return c;
|
||||
aPinsp = VN_AS(aPinsp->nextp(), NodeExpr);
|
||||
bPinsp = VN_AS(bPinsp->nextp(), NodeExpr);
|
||||
}
|
||||
return aPinsp ? -1 : bPinsp ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
bool operator()(const AstSenItem* lhsp, const AstSenItem* rhsp) const {
|
||||
AstNodeExpr* const lSensp = lhsp->sensp();
|
||||
AstNodeExpr* const rSensp = rhsp->sensp();
|
||||
if (lSensp && rSensp) {
|
||||
// If both terms have sensitivity expressions, recursively compare them
|
||||
if (const int c = cmp(lSensp, rSensp)) return c < 0;
|
||||
} else if (lSensp || rSensp) {
|
||||
// Terms with sensitivity expressions come after those without
|
||||
return rSensp;
|
||||
}
|
||||
// Finally sort by edge, AFTER variable, as we want multiple edges for same var
|
||||
// adjacent. note the SenTree optimizer requires this order (more general first,
|
||||
// less general last)
|
||||
return lhsp->edgeType() < rhsp->edgeType();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2817,9 +2867,8 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the sensitivity names so "posedge a or b" and "posedge b or a" end up together.
|
||||
// Also, remove duplicate assignments, and fold POS&NEGs into ANYEDGEs
|
||||
// Make things a little faster; check first if we need a sort
|
||||
// Pass 1: Sort the sensitivity items so "posedge a or b" and "posedge b or a" and
|
||||
// similar, optimizable expressions end up next to each other.
|
||||
for (AstSenItem *nextp, *senp = nodep->sensesp(); senp; senp = nextp) {
|
||||
nextp = VN_AS(senp->nextp(), SenItem);
|
||||
// cppcheck-suppress unassignedVariable // cppcheck bug
|
||||
@@ -2839,35 +2888,53 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// Pass2, remove dup edges
|
||||
for (AstSenItem *nextp, *senp = nodep->sensesp(); senp; senp = nextp) {
|
||||
// Pass 2, remove duplicates and simplify adjacent terms if possible
|
||||
for (AstSenItem *senp = nodep->sensesp(), *nextp; senp; senp = nextp) {
|
||||
nextp = VN_AS(senp->nextp(), SenItem);
|
||||
AstSenItem* const litemp = senp;
|
||||
AstSenItem* const ritemp = nextp;
|
||||
if (ritemp) {
|
||||
if ((litemp->sensp() && ritemp->sensp()
|
||||
&& litemp->sensp()->sameGateTree(ritemp->sensp()))
|
||||
|| (!litemp->sensp() && !ritemp->sensp())) {
|
||||
// We've sorted in the order ANY, BOTH, POS, NEG,
|
||||
// so we don't need to try opposite orders
|
||||
if ((litemp->edgeType() == VEdgeType::ET_POSEDGE // POS or NEG -> BOTH
|
||||
&& ritemp->edgeType() == VEdgeType::ET_NEGEDGE)
|
||||
|| (litemp->edgeType() == ritemp->edgeType()) // Identical edges
|
||||
) {
|
||||
// Fix edge of old node
|
||||
if (litemp->edgeType() == VEdgeType::ET_POSEDGE
|
||||
&& ritemp->edgeType() == VEdgeType::ET_NEGEDGE)
|
||||
litemp->edgeType(VEdgeType::ET_BOTHEDGE);
|
||||
// Remove redundant node
|
||||
VL_DO_DANGLING(ritemp->unlinkFrBack()->deleteTree(), ritemp);
|
||||
VL_DANGLING(ritemp);
|
||||
// Try to collapse again
|
||||
nextp = litemp;
|
||||
if (!nextp) break;
|
||||
AstSenItem* const lItemp = senp;
|
||||
AstSenItem* const rItemp = nextp;
|
||||
AstNodeExpr* const lSenp = lItemp->sensp();
|
||||
AstNodeExpr* const rSenp = rItemp->sensp();
|
||||
if (!lSenp || !rSenp) continue;
|
||||
|
||||
if (lSenp->sameGateTree(rSenp)) {
|
||||
// POSEDGE or NEGEDGE -> BOTHEDGE. (We've sorted POSEDGE, before NEGEDGE, so we
|
||||
// do not need to test for the opposite orders.)
|
||||
if (lItemp->edgeType() == VEdgeType::ET_POSEDGE
|
||||
&& rItemp->edgeType() == VEdgeType::ET_NEGEDGE) {
|
||||
// Make both terms BOTHEDGE, the second will be removed below
|
||||
lItemp->edgeType(VEdgeType::ET_BOTHEDGE);
|
||||
rItemp->edgeType(VEdgeType::ET_BOTHEDGE);
|
||||
}
|
||||
|
||||
// Remove identical expressions
|
||||
if (lItemp->edgeType() == rItemp->edgeType()) {
|
||||
VL_DO_DANGLING(rItemp->unlinkFrBack()->deleteTree(), rItemp);
|
||||
nextp = lItemp;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Not identical terms, check if they can be combined
|
||||
if (lSenp->width() != rSenp->width()) continue;
|
||||
if (AstAnd* const lAndp = VN_CAST(lSenp, And)) {
|
||||
if (AstAnd* const rAndp = VN_CAST(rSenp, And)) {
|
||||
if (AstConst* const lConstp = VN_CAST(lAndp->lhsp(), Const)) {
|
||||
if (AstConst* const rConstp = VN_CAST(rAndp->lhsp(), Const)) {
|
||||
if (lAndp->rhsp()->sameTree(rAndp->rhsp())) {
|
||||
const V3Number lNum{lConstp->num()};
|
||||
lConstp->num().opOr(lNum, rConstp->num());
|
||||
// Remove redundant term
|
||||
VL_DO_DANGLING(rItemp->unlinkFrBack()->deleteTree(), rItemp);
|
||||
nextp = lItemp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// nodep->dumpTree("- ssou: ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2975,6 +3042,7 @@ private:
|
||||
AstNode* const thensp = nodep->thensp()->unlinkFrBackWithNext();
|
||||
AstNode* const elsesp = nodep->elsesp()->unlinkFrBackWithNext();
|
||||
AstIf* const ifp = new AstIf{nodep->fileline(), condp, elsesp, thensp};
|
||||
ifp->isBoundsCheck(nodep->isBoundsCheck()); // Copy bounds check info
|
||||
ifp->branchPred(nodep->branchPred().invert());
|
||||
nodep->replaceWith(ifp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
@@ -3025,7 +3093,7 @@ private:
|
||||
&& nodep->displayType() == VDisplayType::DT_WRITE)))
|
||||
return false;
|
||||
if ((prevp->filep() && !nodep->filep()) || (!prevp->filep() && nodep->filep())
|
||||
|| !prevp->filep()->sameTree(nodep->filep()))
|
||||
|| (prevp->filep() && nodep->filep() && !prevp->filep()->sameTree(nodep->filep())))
|
||||
return false;
|
||||
if (!prevp->fmtp() || prevp->fmtp()->nextp() || !nodep->fmtp() || nodep->fmtp()->nextp())
|
||||
return false;
|
||||
@@ -3136,8 +3204,17 @@ private:
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstFuncRef* nodep) override {
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
m_hasJumpDelay = true; // As don't analyze inside tasks for timing controls
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
// Note excludes AstFuncRef as other visitor below
|
||||
iterateChildren(nodep);
|
||||
m_hasJumpDelay = true; // As don't analyze inside tasks for timing controls
|
||||
}
|
||||
void visit(AstFuncRef* nodep) override {
|
||||
visit(static_cast<AstNodeFTaskRef*>(nodep));
|
||||
if (m_params) { // Only parameters force us to do constant function call propagation
|
||||
replaceWithSimulation(nodep);
|
||||
}
|
||||
@@ -3188,7 +3265,7 @@ private:
|
||||
|
||||
void visit(AstStmtExpr* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->exprp()) {
|
||||
if (!nodep->exprp() || VN_IS(nodep->exprp(), Const)) {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
@@ -3207,7 +3284,7 @@ private:
|
||||
|
||||
void visit(AstJumpGo* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Jump to label where label immediately follows label is not useful
|
||||
// Jump to label where label immediately follows this go is not useful
|
||||
if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
// Keep the label, might be other jumps pointing to it, gets cleaned later
|
||||
@@ -3366,9 +3443,9 @@ private:
|
||||
TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)");
|
||||
TREEOP ("AstCond{$condp.castNot, $thenp, $elsep}", "AstCond{$condp->castNot()->lhsp(), $elsep, $thenp}");
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero, !$elsep.isClassHandleValue}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
|
||||
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, !$thenp.isClassHandleValue, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
|
||||
TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())");
|
||||
// Prefer constants on left, since that often needs a shift, it lets
|
||||
// constant red remove the shift
|
||||
@@ -3611,6 +3688,7 @@ private:
|
||||
public:
|
||||
// Processing Mode Enum
|
||||
enum ProcMode : uint8_t {
|
||||
PROC_PARAMS_NOWARN,
|
||||
PROC_PARAMS,
|
||||
PROC_GENERATE,
|
||||
PROC_LIVE,
|
||||
@@ -3626,16 +3704,18 @@ public:
|
||||
, m_concswapNames{globalPass ? ("__Vconcswap_" + cvtToStr(s_globalPassNum++)) : ""} {
|
||||
// clang-format off
|
||||
switch (pmode) {
|
||||
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; break;
|
||||
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; m_doGenerate = true; break;
|
||||
case PROC_LIVE: break;
|
||||
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
|
||||
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
|
||||
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
|
||||
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
|
||||
default: v3fatalSrc("Bad case"); break;
|
||||
case PROC_PARAMS_NOWARN: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = false; break;
|
||||
case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; break;
|
||||
case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true;
|
||||
m_required = true; m_doGenerate = true; break;
|
||||
case PROC_LIVE: break;
|
||||
case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break;
|
||||
case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break;
|
||||
case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break;
|
||||
case PROC_CPP: m_doV = false; m_doNConst = true; m_doCpp = true; break;
|
||||
default: v3fatalSrc("Bad case"); break;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
@@ -3683,6 +3763,29 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) {
|
||||
return nodep;
|
||||
}
|
||||
|
||||
//! Constify this cell node's parameter list if possible
|
||||
//! @return Pointer to the edited node.
|
||||
AstNode* V3Const::constifyParamsNoWarnEdit(AstNode* nodep) {
|
||||
// if (debug() > 0) nodep->dumpTree("- forceConPRE : ");
|
||||
// Resize even if the node already has a width, because buried in the tree
|
||||
// we may have a node we just created with signing, etc, that isn't sized yet.
|
||||
|
||||
// Make sure we've sized everything first
|
||||
nodep = V3Width::widthParamsEdit(nodep);
|
||||
ConstVisitor visitor{ConstVisitor::PROC_PARAMS_NOWARN, /* globalPass: */ false};
|
||||
if (AstVar* const varp = VN_CAST(nodep, Var)) {
|
||||
// If a var wants to be constified, it's really a param, and
|
||||
// we want the value to be constant. We aren't passed just the
|
||||
// init value because we need widthing above to handle the var's type.
|
||||
if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep());
|
||||
} else {
|
||||
nodep = visitor.mainAcceptEdit(nodep);
|
||||
}
|
||||
// Because we do edits, nodep links may get trashed and core dump this.
|
||||
// if (debug() > 0) nodep->dumpTree("- forceConDONE: ");
|
||||
return nodep;
|
||||
}
|
||||
|
||||
//! Force this cell node's parameter list to become a constant inside generate.
|
||||
//! If we are inside a generated "if", "case" or "for", we don't want to
|
||||
//! trigger warnings when we deal with the width. It is possible that these
|
||||
@@ -3720,7 +3823,7 @@ void V3Const::constifyAllLint(AstNetlist* nodep) {
|
||||
ConstVisitor visitor{ConstVisitor::PROC_V_WARN, /* globalPass: */ true};
|
||||
(void)visitor.mainAcceptEdit(nodep);
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
void V3Const::constifyCpp(AstNetlist* nodep) {
|
||||
@@ -3729,7 +3832,7 @@ void V3Const::constifyCpp(AstNetlist* nodep) {
|
||||
ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ true};
|
||||
(void)visitor.mainAcceptEdit(nodep);
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
AstNode* V3Const::constifyEdit(AstNode* nodep) {
|
||||
@@ -3753,7 +3856,7 @@ void V3Const::constifyAllLive(AstNetlist* nodep) {
|
||||
ConstVisitor visitor{ConstVisitor::PROC_LIVE, /* globalPass: */ true};
|
||||
(void)visitor.mainAcceptEdit(nodep);
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
void V3Const::constifyAll(AstNetlist* nodep) {
|
||||
@@ -3763,7 +3866,7 @@ void V3Const::constifyAll(AstNetlist* nodep) {
|
||||
ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ true};
|
||||
(void)visitor.mainAcceptEdit(nodep);
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("const", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) {
|
||||
|
||||
@@ -30,6 +30,10 @@ public:
|
||||
static AstNodeExpr* constifyParamsEdit(AstNodeExpr* exprp) {
|
||||
return VN_AS(constifyParamsEdit(static_cast<AstNode*>(exprp)), NodeExpr);
|
||||
}
|
||||
static AstNode* constifyParamsNoWarnEdit(AstNode* nodep);
|
||||
static AstNodeExpr* constifyParamsNoWarnEdit(AstNodeExpr* exprp) {
|
||||
return VN_AS(constifyParamsNoWarnEdit(static_cast<AstNode*>(exprp)), NodeExpr);
|
||||
}
|
||||
static AstNode* constifyGenerateParamsEdit(AstNode* nodep);
|
||||
// Only do constant pushing, without removing dead logic
|
||||
static void constifyAllLive(AstNetlist* nodep);
|
||||
|
||||
+1
-1
@@ -567,5 +567,5 @@ public:
|
||||
void V3Coverage::coverage(AstNetlist* rootp) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CoverageVisitor{rootp}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("coverage", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("coverage", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
@@ -117,5 +117,5 @@ public:
|
||||
void V3CoverageJoin::coverageJoin(AstNetlist* rootp) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CoverageJoinVisitor{rootp}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("coveragejoin", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("coveragejoin", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+29
-25
@@ -62,9 +62,10 @@ private:
|
||||
// TYPES
|
||||
using AssignMap = std::multimap<AstVarScope*, AstNodeAssign*>;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars
|
||||
// STATE - across all visitors
|
||||
const bool m_elimUserVars; // Allow removal of user's vars
|
||||
const bool m_elimDTypes; // Allow removal of DTypes
|
||||
const bool m_elimCells; // Allow removal of Cells
|
||||
// List of all encountered to avoid another loop through tree
|
||||
std::vector<AstVar*> m_varsp;
|
||||
std::vector<AstNode*> m_dtypesp;
|
||||
@@ -73,13 +74,14 @@ private:
|
||||
std::vector<AstCell*> m_cellsp;
|
||||
std::vector<AstClass*> m_classesp;
|
||||
std::vector<AstTypedef*> m_typedefsp;
|
||||
|
||||
AssignMap m_assignMap; // List of all simple assignments for each variable
|
||||
const bool m_elimUserVars; // Allow removal of user's vars
|
||||
const bool m_elimDTypes; // Allow removal of DTypes
|
||||
const bool m_elimCells; // Allow removal of Cells
|
||||
bool m_sideEffect = false; // Side effects discovered in assign RHS
|
||||
|
||||
// STATE - for current visit position (use VL_RESTORER)
|
||||
bool m_inAssign = false; // Currently in an assign
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars
|
||||
|
||||
// METHODS
|
||||
|
||||
void checkAll(AstNode* nodep) {
|
||||
@@ -107,18 +109,16 @@ private:
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
if (m_modp) m_modp->user1Inc(); // e.g. Class under Package
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
if (!nodep->dead()) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (AstClass* const classp = VN_CAST(nodep, Class)) {
|
||||
if (classp->extendsp()) classp->extendsp()->user1Inc();
|
||||
if (classp->classOrPackagep()) classp->classOrPackagep()->user1Inc();
|
||||
m_classesp.push_back(classp);
|
||||
// TODO we don't reclaim dead classes yet - graph implementation instead?
|
||||
classp->user1Inc();
|
||||
}
|
||||
m_modp = nodep;
|
||||
if (!nodep->dead()) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (AstClass* const classp = VN_CAST(nodep, Class)) {
|
||||
if (classp->extendsp()) classp->extendsp()->user1Inc();
|
||||
if (classp->classOrPackagep()) classp->classOrPackagep()->user1Inc();
|
||||
m_classesp.push_back(classp);
|
||||
// TODO we don't reclaim dead classes yet - graph implementation instead?
|
||||
classp->user1Inc();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,8 +282,11 @@ private:
|
||||
// See if simple assignments to variables may be eliminated because
|
||||
// that variable is never used.
|
||||
// Similar code in V3Life
|
||||
VL_RESTORER(m_sideEffect);
|
||||
const bool assignInAssign = m_inAssign; // Might be Assign(..., ExprStmt(Assign), ...)
|
||||
{
|
||||
VL_RESTORER(m_inAssign);
|
||||
VL_RESTORER(m_sideEffect);
|
||||
m_inAssign = true;
|
||||
m_sideEffect = false;
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
checkAll(nodep);
|
||||
@@ -299,6 +302,7 @@ private:
|
||||
}
|
||||
iterateNull(nodep->timingControlp());
|
||||
}
|
||||
if (assignInAssign) m_sideEffect = true; // Parent assign shouldn't optimize
|
||||
}
|
||||
|
||||
//-----
|
||||
@@ -539,29 +543,29 @@ void V3Dead::deadifyModules(AstNetlist* nodep) {
|
||||
{
|
||||
DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported()};
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTree() >= 6);
|
||||
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeLevel() >= 6);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor{nodep, false, true, false, false, false}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor{nodep, false, true, true, false, false}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor{nodep, true, true, false, true, false}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor{nodep, true, true, true, true, false}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+39
-29
@@ -92,21 +92,23 @@ private:
|
||||
const VNUser4InUse m_inuser4;
|
||||
const VNUser5InUse m_inuser5;
|
||||
|
||||
// STATE
|
||||
// STATE - across all visitors
|
||||
std::unordered_map<const AstVarScope*, int> m_scopeVecMap; // Next var number for each scope
|
||||
std::set<AstSenTree*> m_timingDomains; // Timing resume domains
|
||||
using VarMap = std::map<const std::pair<AstNodeModule*, std::string>, AstVar*>;
|
||||
VarMap m_modVarMap; // Table of new var names created under module
|
||||
VDouble0 m_statSharedSet; // Statistic tracking
|
||||
|
||||
// STATE - for current visit position (use VL_RESTORER)
|
||||
AstActive* m_activep = nullptr; // Current activate
|
||||
const AstCFunc* m_cfuncp = nullptr; // Current public C Function
|
||||
AstAssignDly* m_nextDlyp = nullptr; // Next delayed assignment in a list of assignments
|
||||
AstNodeProcedure* m_procp = nullptr; // Current process
|
||||
std::set<AstSenTree*> m_timingDomains; // Timing resume domains
|
||||
bool m_inDly = false; // True in delayed assignments
|
||||
bool m_inLoop = false; // True in for loops
|
||||
bool m_inInitial = false; // True in static initializers and initial blocks
|
||||
bool m_inSuspendableOrFork = false; // True in suspendable processes and forks
|
||||
bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
|
||||
using VarMap = std::map<const std::pair<AstNodeModule*, std::string>, AstVar*>;
|
||||
VarMap m_modVarMap; // Table of new var names created under module
|
||||
VDouble0 m_statSharedSet; // Statistic tracking
|
||||
std::unordered_map<const AstVarScope*, int> m_scopeVecMap; // Next var number for each scope
|
||||
|
||||
// METHODS
|
||||
|
||||
@@ -262,8 +264,8 @@ private:
|
||||
if (VN_IS(dimp, Const)) { // bit = const, can just use it
|
||||
dimreadps.push_front(dimp);
|
||||
} else {
|
||||
const string bitvarname = (string("__Vdlyvdim") + cvtToStr(dimension) + "__"
|
||||
+ oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
|
||||
const string bitvarname = string{"__Vdlyvdim"} + cvtToStr(dimension) + "__"
|
||||
+ oldvarp->shortName() + "__v" + cvtToStr(modVecNum);
|
||||
AstVarScope* const bitvscp
|
||||
= createVarSc(varrefp->varScopep(), bitvarname, dimp->width(), nullptr);
|
||||
AstAssign* const bitassignp = new AstAssign{
|
||||
@@ -282,8 +284,8 @@ private:
|
||||
// vlsb = constant, can just push constant into where we use it
|
||||
bitreadp = lsbvaluep;
|
||||
} else {
|
||||
const string bitvarname = (string("__Vdlyvlsb__") + oldvarp->shortName() + "__v"
|
||||
+ cvtToStr(modVecNum));
|
||||
const string bitvarname
|
||||
= string{"__Vdlyvlsb__"} + oldvarp->shortName() + "__v" + cvtToStr(modVecNum);
|
||||
AstVarScope* const bitvscp
|
||||
= createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width(), nullptr);
|
||||
AstAssign* const bitassignp = new AstAssign{
|
||||
@@ -301,7 +303,7 @@ private:
|
||||
valreadp = nodep->rhsp()->unlinkFrBack();
|
||||
} else {
|
||||
const string valvarname
|
||||
= (string("__Vdlyvval__") + oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
|
||||
= string{"__Vdlyvval__"} + oldvarp->shortName() + "__v" + cvtToStr(modVecNum);
|
||||
AstVarScope* const valvscp
|
||||
= createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep());
|
||||
newlhsp = new AstVarRef{nodep->fileline(), valvscp, VAccess::WRITE};
|
||||
@@ -321,7 +323,7 @@ private:
|
||||
++m_statSharedSet;
|
||||
} else { // Create new one
|
||||
const string setvarname
|
||||
= (string("__Vdlyvset__") + oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
|
||||
= string{"__Vdlyvset__"} + oldvarp->shortName() + "__v" + cvtToStr(modVecNum);
|
||||
setvscp = createVarSc(varrefp->varScopep(), setvarname, 1, nullptr);
|
||||
if (!m_inSuspendableOrFork) {
|
||||
// Suspendables reset __Vdlyvset__ in the AstAlwaysPost
|
||||
@@ -424,10 +426,8 @@ private:
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_cfuncp);
|
||||
{
|
||||
m_cfuncp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_cfuncp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstActive* nodep) override {
|
||||
m_activep = nodep;
|
||||
@@ -443,10 +443,12 @@ private:
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_inSuspendableOrFork);
|
||||
m_inSuspendableOrFork = nodep->isSuspendable();
|
||||
m_procp = nodep;
|
||||
m_timingDomains.clear();
|
||||
iterateChildren(nodep);
|
||||
m_procp = nullptr;
|
||||
{
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
m_timingDomains.clear();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
if (m_timingDomains.empty()) return;
|
||||
if (auto* const actp = VN_AS(nodep->user3p(), Active)) {
|
||||
// Merge all timing domains (and possibly the active's domain) to create a sentree for
|
||||
@@ -474,14 +476,16 @@ private:
|
||||
m_inSuspendableOrFork = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCAwait* nodep) override { m_timingDomains.insert(nodep->sensesp()); }
|
||||
void visit(AstCAwait* nodep) override {
|
||||
if (nodep->sensesp()) m_timingDomains.insert(nodep->sensesp());
|
||||
}
|
||||
void visit(AstFireEvent* nodep) override {
|
||||
UASSERT_OBJ(v3Global.hasEvents(), nodep, "Inconsistent");
|
||||
FileLine* const flp = nodep->fileline();
|
||||
if (nodep->isDelayed()) {
|
||||
AstVarRef* const vrefp = VN_AS(nodep->operandp(), VarRef);
|
||||
vrefp->unlinkFrBack();
|
||||
const string newvarname = (string("__Vdly__") + vrefp->varp()->shortName());
|
||||
const string newvarname = string{"__Vdly__"} + vrefp->varp()->shortName();
|
||||
AstVarScope* const dlyvscp = createVarSc(vrefp->varScopep(), newvarname, 1, nullptr);
|
||||
|
||||
const auto dlyRef = [=](VAccess access) {
|
||||
@@ -547,7 +551,9 @@ private:
|
||||
}
|
||||
if (!lhsp->backp()) VL_DO_DANGLING(pushDeletep(lhsp), lhsp);
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
iterate(nodep->lhsp());
|
||||
m_inDly = false;
|
||||
iterate(nodep->rhsp());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,7 +576,7 @@ private:
|
||||
checkActivePost(nodep, oldactivep);
|
||||
}
|
||||
if (!dlyvscp) { // First use of this delayed variable
|
||||
const string newvarname = (string("__Vdly__") + nodep->varp()->shortName());
|
||||
const string newvarname = string{"__Vdly__"} + nodep->varp()->shortName();
|
||||
dlyvscp = createVarSc(oldvscp, newvarname, 0, nullptr);
|
||||
AstNodeAssign* const prep = new AstAssignPre{
|
||||
nodep->fileline(),
|
||||
@@ -616,10 +622,14 @@ private:
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
VL_RESTORER(m_inLoop);
|
||||
{
|
||||
m_inLoop = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_inLoop = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstExprStmt* nodep) override {
|
||||
VL_RESTORER(m_inDly);
|
||||
// Restoring is needed, because AstExprStmt may contain assignments
|
||||
m_inDly = false;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
@@ -639,5 +649,5 @@ public:
|
||||
void V3Delayed::delayedAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DelayedVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("delayed", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("delayed", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+1
-1
@@ -166,5 +166,5 @@ public:
|
||||
void V3Depth::depthAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DepthVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("depth", 0, dumpTree() >= 6);
|
||||
V3Global::dumpCheckGlobalTree("depth", 0, dumpTreeLevel() >= 6);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ private:
|
||||
AstCCall* const callp = new AstCCall{nodep->fileline(), funcp};
|
||||
callp->dtypeSetVoid();
|
||||
if (VN_IS(m_modp, Class)) {
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->argTypes(EmitCBase::symClassVar());
|
||||
callp->argTypes("vlSymsp");
|
||||
}
|
||||
UINFO(6, " New " << callp << endl);
|
||||
@@ -93,6 +93,7 @@ private:
|
||||
}
|
||||
}
|
||||
void visit(AstStmtExpr* nodep) override {} // Stop recursion after introducing new function
|
||||
void visit(AstJumpBlock*) override {} // Stop recursion as can't break up across a jump
|
||||
void visit(AstNodeStmt* nodep) override {
|
||||
m_depth++;
|
||||
if (m_depth > v3Global.opt.compLimitBlocks()) { // Already done
|
||||
@@ -126,5 +127,5 @@ public:
|
||||
void V3DepthBlock::depthBlockAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DepthBlockVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deepblock", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("deepblock", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+39
-18
@@ -46,6 +46,10 @@ private:
|
||||
|
||||
// TYPES
|
||||
using FuncMmap = std::multimap<std::string, AstCFunc*>;
|
||||
struct ScopeSelfPtr final {
|
||||
VSelfPointerText thisPtr = VSelfPointerText{VSelfPointerText::Empty()};
|
||||
VSelfPointerText vlSymsPtr = VSelfPointerText{VSelfPointerText::Empty()};
|
||||
};
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
@@ -53,6 +57,7 @@ private:
|
||||
const AstCFunc* m_funcp = nullptr; // Current function
|
||||
bool m_modSingleton = false; // m_modp is only instantiated once
|
||||
FuncMmap m_modFuncs; // Name of public functions added
|
||||
std::map<const AstScope*, ScopeSelfPtr> m_scopeToSelf; // Scope to self pointers
|
||||
|
||||
// METHODS
|
||||
|
||||
@@ -68,12 +73,32 @@ private:
|
||||
return (instances == 1);
|
||||
}
|
||||
|
||||
// Construct a 'this' self pointer for the given scope
|
||||
VSelfPointerText scopeThis(const AstScope* scopep) {
|
||||
auto& ret = m_scopeToSelf[scopep];
|
||||
if (ret.thisPtr.isEmpty()) {
|
||||
string name = scopep->name();
|
||||
string::size_type pos;
|
||||
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
|
||||
ret.thisPtr = VSelfPointerText{VSelfPointerText::This(), name};
|
||||
}
|
||||
return ret.thisPtr;
|
||||
}
|
||||
// Construct a 'vlSyms' self pointer for the given scope
|
||||
VSelfPointerText scopeVlSyms(const AstScope* scopep) {
|
||||
auto& ret = m_scopeToSelf[scopep];
|
||||
if (ret.vlSymsPtr.isEmpty()) {
|
||||
ret.vlSymsPtr = VSelfPointerText{VSelfPointerText::VlSyms(), scopep->nameDotless()};
|
||||
}
|
||||
return ret.vlSymsPtr;
|
||||
}
|
||||
|
||||
// Construct the best self pointer to reference an object in 'scopep' from a CFunc in
|
||||
// 'm_scopep'. Result may be relative ("this->[...]") or absolute ("vlSyms->[...]").
|
||||
//
|
||||
// Using relative references allows V3Combine'ing code across multiple instances of the same
|
||||
// module.
|
||||
string descopedSelfPointer(const AstScope* scopep) {
|
||||
VSelfPointerText descopedSelfPointer(const AstScope* scopep) {
|
||||
UASSERT(scopep, "Var/Func not scoped");
|
||||
// Static functions can't use relative references via 'this->'
|
||||
const bool relativeRefOk = !m_funcp->isStatic();
|
||||
@@ -85,23 +110,20 @@ private:
|
||||
if (VN_IS(scopep->modp(), Class)) {
|
||||
// Direct reference to class members are from within the class itself, references from
|
||||
// outside the class must go via AstMemberSel
|
||||
return "this";
|
||||
return VSelfPointerText{VSelfPointerText::This()};
|
||||
} else if (relativeRefOk && scopep == m_scopep) {
|
||||
return "this";
|
||||
return VSelfPointerText{VSelfPointerText::This()};
|
||||
} else if (relativeRefOk && !m_modSingleton && scopep->aboveScopep() == m_scopep
|
||||
&& VN_IS(scopep->modp(), Module)) {
|
||||
// Reference to scope of instance directly under this module, can just "this->cell",
|
||||
// which can potentially be V3Combined, but note this requires one extra pointer
|
||||
// dereference which is slower, so we only use it if the source scope is not a
|
||||
// singleton.
|
||||
string name = scopep->name();
|
||||
string::size_type pos;
|
||||
if ((pos = name.rfind('.')) != string::npos) name.erase(0, pos + 1);
|
||||
return "this->" + name;
|
||||
return scopeThis(scopep);
|
||||
} else {
|
||||
// Reference to something elsewhere, or relative references are disabled. Use global
|
||||
// variable
|
||||
return "(&" + scopep->nameVlSym() + ")";
|
||||
return scopeVlSyms(scopep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,11 +185,10 @@ private:
|
||||
if (moreOfSame) {
|
||||
AstIf* const ifp = new AstIf{
|
||||
funcp->fileline(),
|
||||
new AstEq{
|
||||
funcp->fileline(), new AstCExpr{funcp->fileline(), "this", 64},
|
||||
new AstCExpr{funcp->fileline(),
|
||||
string("&(") + funcp->scopep()->nameVlSym() + ")",
|
||||
64}},
|
||||
new AstEq{funcp->fileline(),
|
||||
new AstCExpr{funcp->fileline(), "this", 64},
|
||||
new AstCExpr{funcp->fileline(),
|
||||
scopeVlSyms(funcp->scopep()).asString(), 64}},
|
||||
returnp};
|
||||
newfuncp->addStmtsp(ifp);
|
||||
} else {
|
||||
@@ -178,7 +199,7 @@ private:
|
||||
// to come up with some return value
|
||||
// newfuncp->addStmtsp(new AstDisplay(newfuncp->fileline(),
|
||||
// VDisplayType::DT_WARNING,
|
||||
// string("%%Error: ")+name+"() called with bad
|
||||
// string{"%%Error: "}+name+"() called with bad
|
||||
// scope", nullptr));
|
||||
// newfuncp->addStmtsp(new AstStop(newfuncp->fileline()));
|
||||
if (debug() >= 9) newfuncp->dumpTree("- newfunc: ");
|
||||
@@ -225,15 +246,15 @@ private:
|
||||
const AstScope* const scopep = nodep->varScopep()->scopep();
|
||||
if (varp->isFuncLocal()) {
|
||||
// Reference to function locals need no self pointer
|
||||
nodep->selfPointer("");
|
||||
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
|
||||
} else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) {
|
||||
// Reference to constant pool value need no self pointer
|
||||
nodep->selfPointer("");
|
||||
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
|
||||
} else {
|
||||
nodep->selfPointer(descopedSelfPointer(scopep));
|
||||
}
|
||||
nodep->varScopep(nullptr);
|
||||
UINFO(9, " refout " << nodep << " selfPtr=" << nodep->selfPointer() << endl);
|
||||
UINFO(9, " refout " << nodep << " selfPtr=" << nodep->selfPointer().asString() << endl);
|
||||
}
|
||||
void visit(AstCCall* nodep) override {
|
||||
// UINFO(9, " " << nodep << endl);
|
||||
@@ -285,5 +306,5 @@ public:
|
||||
void V3Descope::descopeAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DescopeVisitor{nodep}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("descope", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("descope", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+2
-2
@@ -503,11 +503,11 @@ public:
|
||||
inline bool inlined() const;
|
||||
|
||||
// Methods that allow DfgVertex to participate in error reporting/messaging
|
||||
void v3errorEnd(std::ostringstream& str) const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
|
||||
m_filelinep->v3errorEnd(str);
|
||||
}
|
||||
void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN
|
||||
VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
VL_RELEASE(V3Error::s().m_mutex) {
|
||||
m_filelinep->v3errorEndFatal(str);
|
||||
}
|
||||
string warnContextPrimary() const VL_REQUIRES(V3Error::s().m_mutex) {
|
||||
|
||||
+15
-11
@@ -334,17 +334,20 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||
const uint32_t bEnd = b.m_lsb + bWidth;
|
||||
const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1;
|
||||
|
||||
varp->varp()->v3warn( //
|
||||
MULTIDRIVEN,
|
||||
"Bits [" //
|
||||
<< overlapEnd << ":" << b.m_lsb << "] of signal "
|
||||
<< varp->varp()->prettyNameQ()
|
||||
<< " have multiple combinational drivers\n"
|
||||
<< a.m_fileline->warnOther() << "... Location of first driver\n"
|
||||
<< a.m_fileline->warnContextPrimary() << '\n'
|
||||
<< b.m_fileline->warnOther() << "... Location of other driver\n"
|
||||
<< b.m_fileline->warnContextSecondary() << varp->varp()->warnOther()
|
||||
<< "... Only the first driver will be respected");
|
||||
if (a.m_fileline->operatorCompare(*b.m_fileline) != 0) {
|
||||
varp->varp()->v3warn( //
|
||||
MULTIDRIVEN,
|
||||
"Bits [" //
|
||||
<< overlapEnd << ":" << b.m_lsb << "] of signal "
|
||||
<< varp->varp()->prettyNameQ()
|
||||
<< " have multiple combinational drivers\n"
|
||||
<< a.m_fileline->warnOther() << "... Location of first driver\n"
|
||||
<< a.m_fileline->warnContextPrimary() << '\n'
|
||||
<< b.m_fileline->warnOther() << "... Location of other driver\n"
|
||||
<< b.m_fileline->warnContextSecondary()
|
||||
<< varp->varp()->warnOther()
|
||||
<< "... Only the first driver will be respected");
|
||||
}
|
||||
|
||||
// If the first driver completely covers the range of the second driver,
|
||||
// we can just delete the second driver completely, otherwise adjust the
|
||||
@@ -437,6 +440,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||
void visit(AstCell* nodep) override { markReferenced(nodep); }
|
||||
void visit(AstNodeProcedure* nodep) override { markReferenced(nodep); }
|
||||
void visit(AstVar* nodep) override {
|
||||
if (nodep->isSc()) return;
|
||||
// No need to (and in fact cannot) handle variables with unsupported dtypes
|
||||
if (!DfgVertex::isSupportedDType(nodep->dtypep())) return;
|
||||
// Mark ports as having external references
|
||||
|
||||
@@ -59,8 +59,7 @@ AstCountOnes* makeNode<AstCountOnes, DfgCountOnes, AstNodeExpr*>( //
|
||||
const DfgCountOnes* vtxp, AstNodeExpr* op1) {
|
||||
AstCountOnes* const nodep = new AstCountOnes{vtxp->fileline(), op1};
|
||||
// Set dtype same as V3Width
|
||||
const int selwidth = V3Number::log2b(nodep->lhsp()->width()) + 1;
|
||||
nodep->dtypeSetLogicSized(selwidth, VSigning::UNSIGNED);
|
||||
nodep->dtypeSetLogicSized(32, VSigning::UNSIGNED);
|
||||
return nodep;
|
||||
}
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ void V3DfgOptimizer::extract(AstNetlist* netlistp) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
// Extract more optimization candidates
|
||||
DataflowExtractVisitor::apply(netlistp);
|
||||
V3Global::dumpCheckGlobalTree("dfg-extract", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("dfg-extract", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
@@ -267,7 +267,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
|
||||
// Build the DFG of this module
|
||||
const std::unique_ptr<DfgGraph> dfg{V3DfgPasses::astToDfg(*modp, ctx)};
|
||||
if (dumpDfg() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input");
|
||||
if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input");
|
||||
|
||||
// Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a
|
||||
// DAG, and large, mostly acyclic graphs could not be optimized due to the presence of
|
||||
@@ -284,7 +284,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
|
||||
// For each cyclic component
|
||||
for (auto& component : cyclicComponents) {
|
||||
if (dumpDfg() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
// TODO: Apply optimizations safe for cyclic graphs
|
||||
// Add back under the main DFG (we will convert everything back in one go)
|
||||
dfg->addGraph(*component);
|
||||
@@ -292,7 +292,7 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
|
||||
// For each acyclic component
|
||||
for (auto& component : acyclicComponents) {
|
||||
if (dumpDfg() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
// Optimize the component
|
||||
V3DfgPasses::optimize(*component, ctx);
|
||||
// Add back under the main DFG (we will convert everything back in one go)
|
||||
@@ -300,9 +300,9 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
}
|
||||
|
||||
// Convert back to Ast
|
||||
if (dumpDfg() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized");
|
||||
if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized");
|
||||
AstModule* const resultModp = V3DfgPasses::dfgToAst(*dfg, ctx);
|
||||
UASSERT_OBJ(resultModp == modp, modp, "Should be the same module");
|
||||
}
|
||||
V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTree() >= 3);
|
||||
V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTreeLevel() >= 3);
|
||||
}
|
||||
|
||||
+3
-3
@@ -285,7 +285,7 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
||||
|
||||
const auto apply = [&](int dumpLevel, const string& name, std::function<void()> pass) {
|
||||
pass();
|
||||
if (dumpDfg() >= dumpLevel) {
|
||||
if (dumpDfgLevel() >= dumpLevel) {
|
||||
const string strippedName = VString::removeWhitespace(name);
|
||||
const string label
|
||||
= ctx.prefix() + "pass-" + cvtToStr(passNumber) + "-" + strippedName;
|
||||
@@ -294,7 +294,7 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
||||
++passNumber;
|
||||
};
|
||||
|
||||
if (dumpDfg() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "input");
|
||||
if (dumpDfgLevel() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "input");
|
||||
apply(3, "input ", [&]() {});
|
||||
apply(4, "inlineVars ", [&]() { inlineVars(dfg); });
|
||||
apply(4, "cse0 ", [&]() { cse(dfg, ctx.m_cseContext0); });
|
||||
@@ -304,5 +304,5 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
||||
apply(4, "cse1 ", [&]() { cse(dfg, ctx.m_cseContext1); });
|
||||
}
|
||||
apply(4, "removeVars ", [&]() { removeVars(dfg, ctx.m_removeVarsContext); });
|
||||
if (dumpDfg() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "optimized");
|
||||
if (dumpDfgLevel() >= 8) dfg.dumpDotAllVarConesPrefixed(ctx.prefix() + "optimized");
|
||||
}
|
||||
|
||||
+2
-2
@@ -123,9 +123,9 @@ struct V3DfgPeepholeContext final {
|
||||
const std::string m_label; // Label to apply to stats
|
||||
|
||||
// Enable flags for each optimization
|
||||
bool m_enabled[VDfgPeepholePattern::_ENUM_END];
|
||||
std::array<bool, VDfgPeepholePattern::_ENUM_END> m_enabled;
|
||||
// Count of applications for each optimization (for statistics)
|
||||
VDouble0 m_count[VDfgPeepholePattern::_ENUM_END];
|
||||
std::array<VDouble0, VDfgPeepholePattern::_ENUM_END> m_count;
|
||||
|
||||
explicit V3DfgPeepholeContext(const std::string& label);
|
||||
~V3DfgPeepholeContext();
|
||||
|
||||
+1
-1
@@ -100,5 +100,5 @@ void V3DupFinder::dumpFile(const string& filename, bool tree) {
|
||||
}
|
||||
|
||||
void V3DupFinder::dumpFilePrefixed(const string& nameComment, bool tree) {
|
||||
if (dump()) dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree);
|
||||
if (dumpLevel()) dumpFile(v3Global.debugFilename(nameComment) + ".hash", tree);
|
||||
}
|
||||
|
||||
+30
-29
@@ -39,7 +39,7 @@ EmitCParentModule::EmitCParentModule() {
|
||||
//######################################################################
|
||||
// EmitCBaseVisitor implementation
|
||||
|
||||
string EmitCBaseVisitor::funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) {
|
||||
string EmitCBaseVisitorConst::funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) {
|
||||
modp = modp ? modp : EmitCParentModule::get(nodep);
|
||||
string name;
|
||||
if (nodep->isConstructor()) {
|
||||
@@ -57,15 +57,21 @@ string EmitCBaseVisitor::funcNameProtect(const AstCFunc* nodep, const AstNodeMod
|
||||
return name;
|
||||
}
|
||||
|
||||
AstCFile* EmitCBaseVisitor::newCFile(const string& filename, bool slow, bool source, bool add) {
|
||||
AstCFile* const cfilep = new AstCFile{v3Global.rootp()->fileline(), filename};
|
||||
cfilep->slow(slow);
|
||||
cfilep->source(source);
|
||||
if (add) v3Global.rootp()->addFilesp(cfilep);
|
||||
AstCFile* EmitCBaseVisitorConst::newCFile(const string& filename, bool slow, bool source) {
|
||||
AstCFile* const cfilep = createCFile(filename, slow, source);
|
||||
v3Global.rootp()->addFilesp(cfilep);
|
||||
return cfilep;
|
||||
}
|
||||
|
||||
string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) {
|
||||
AstCFile* EmitCBaseVisitorConst::createCFile(const string& filename, bool slow,
|
||||
bool source) VL_MT_SAFE {
|
||||
AstCFile* const cfilep = new AstCFile{v3Global.rootp()->fileline(), filename};
|
||||
cfilep->slow(slow);
|
||||
cfilep->source(source);
|
||||
return cfilep;
|
||||
}
|
||||
|
||||
string EmitCBaseVisitorConst::cFuncArgs(const AstCFunc* nodep) {
|
||||
// Return argument list for given C function
|
||||
string args;
|
||||
if (nodep->isLoose() && !nodep->isStatic()) {
|
||||
@@ -73,6 +79,10 @@ string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) {
|
||||
args += prefixNameProtect(EmitCParentModule::get(nodep));
|
||||
args += "* vlSelf";
|
||||
}
|
||||
if (nodep->needProcess()) {
|
||||
if (!args.empty()) args += ", ";
|
||||
args += "VlProcessRef vlProcess";
|
||||
}
|
||||
if (!nodep->argTypes().empty()) {
|
||||
if (!args.empty()) args += ", ";
|
||||
args += nodep->argTypes();
|
||||
@@ -95,8 +105,8 @@ string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) {
|
||||
return args;
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp,
|
||||
bool withScope) {
|
||||
void EmitCBaseVisitorConst::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp,
|
||||
bool withScope) {
|
||||
if (funcp->slow()) puts("VL_ATTR_COLD ");
|
||||
if (!funcp->isConstructor() && !funcp->isDestructor()) {
|
||||
puts(funcp->rtnTypeVoid());
|
||||
@@ -114,8 +124,8 @@ void EmitCBaseVisitor::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModul
|
||||
if (funcp->isConst().trueKnown() && funcp->isProperMethod()) puts(" const");
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp,
|
||||
bool cLinkage) {
|
||||
void EmitCBaseVisitorConst::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp,
|
||||
bool cLinkage) {
|
||||
ensureNewLine();
|
||||
if (!funcp->ifdef().empty()) puts("#ifdef " + funcp->ifdef() + "\n");
|
||||
if (cLinkage) puts("extern \"C\" ");
|
||||
@@ -129,7 +139,7 @@ void EmitCBaseVisitor::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule*
|
||||
if (!funcp->ifdef().empty()) puts("#endif // " + funcp->ifdef() + "\n");
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, bool asRef) {
|
||||
void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) {
|
||||
const AstBasicDType* const basicp = nodep->basicp();
|
||||
bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType);
|
||||
|
||||
@@ -217,25 +227,16 @@ void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, bool asRef) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitModCUse(const AstNodeModule* modp, VUseType useType) {
|
||||
string nl;
|
||||
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (AstCUse* const usep = VN_CAST(itemp, CUse)) {
|
||||
if (usep->useType() == useType) {
|
||||
if (usep->useType().isInclude()) {
|
||||
puts("#include \"" + prefixNameProtect(usep) + ".h\"\n");
|
||||
}
|
||||
if (usep->useType().isFwdClass()) {
|
||||
puts("class " + prefixNameProtect(usep) + ";\n");
|
||||
}
|
||||
nl = "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
puts(nl);
|
||||
void EmitCBaseVisitorConst::emitModCUse(const AstNodeModule* modp, VUseType useType) {
|
||||
bool nl = false;
|
||||
forModCUse(modp, useType, [&](string entry) {
|
||||
puts(entry);
|
||||
nl = true;
|
||||
});
|
||||
if (nl) puts("\n");
|
||||
}
|
||||
|
||||
void EmitCBaseVisitor::emitTextSection(const AstNodeModule* modp, VNType type) {
|
||||
void EmitCBaseVisitorConst::emitTextSection(const AstNodeModule* modp, VNType type) {
|
||||
// Short circuit if nothing to do. This can save a lot of time on large designs as this
|
||||
// function needs to traverse the entire module linearly.
|
||||
if (!v3Global.hasSCTextSections()) return;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user