mirror of
https://github.com/verilator/verilator.git
synced 2026-10-03 16:44:11 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52d29d238c | ||
|
|
0675510eb9 | ||
|
|
99791ac8b3 | ||
|
|
ee26378394 | ||
|
|
54c4351c39 | ||
|
|
e504e9aced | ||
|
|
ef2776034e | ||
|
|
5c658f8cd5 | ||
|
|
0ed7aaeabd | ||
|
|
5f9b0929b4 | ||
|
|
65290afe0a | ||
|
|
ed93a111c2 | ||
|
|
3487701b04 | ||
|
|
d33a3d09f7 | ||
|
|
0716a28816 | ||
|
|
8e1901da10 | ||
|
|
ecfa385f13 | ||
|
|
63df87e220 | ||
|
|
495d52d30f | ||
|
|
2df886b2b0 | ||
|
|
fcf0d03cd4 | ||
|
|
4154584c4b | ||
|
|
a57a3579c0 | ||
|
|
203993b018 | ||
|
|
347e9b4ec8 | ||
|
|
9d02082801 | ||
|
|
785c51dd0b | ||
|
|
2e4f5c863f | ||
|
|
196f3292d5 | ||
|
|
627a144b83 | ||
|
|
5688d1a935 | ||
|
|
8b0d71994d | ||
|
|
79682e6072 | ||
|
|
7e1b92fa75 | ||
|
|
ca8ef7ce73 | ||
|
|
23d538fdaf | ||
|
|
aaadc3def7 | ||
|
|
444a4a760c | ||
|
|
b42799f3b5 | ||
|
|
7b07cf912e | ||
|
|
6a3ec17887 | ||
|
|
8c3ca30c91 | ||
|
|
0e4da3b0bf | ||
|
|
e6add5e0b8 | ||
|
|
22243d1e49 | ||
|
|
bec0b7d4d0 | ||
|
|
f6f13c7fda | ||
|
|
e7068369fe | ||
|
|
b930d0731a | ||
|
|
2723223884 | ||
|
|
b6c116d4bf | ||
|
|
54e3f15dce | ||
|
|
c057847760 | ||
|
|
46c5764383 | ||
|
|
22ce36012e | ||
|
|
5c65e0cfa1 | ||
|
|
840e26b69a | ||
|
|
5e79652922 | ||
|
|
cb7b024e8f | ||
|
|
f3292a3651 | ||
|
|
76ccd332a6 | ||
|
|
3cd2c8532d | ||
|
|
c0739e908c | ||
|
|
379a947379 | ||
|
|
916a3d9066 | ||
|
|
b16b607b98 | ||
|
|
732d5bea10 | ||
|
|
d9a0d0ade2 | ||
|
|
e32ff0e1a6 | ||
|
|
5957156dee | ||
|
|
14f58ed6c7 | ||
|
|
038d57070b | ||
|
|
8a347248f5 | ||
|
|
caed086516 | ||
|
|
68927d4fd3 | ||
|
|
b2070a9407 | ||
|
|
8dacbdec3a | ||
|
|
2a110c91cf | ||
|
|
ba052beccd | ||
|
|
6f9c585452 | ||
|
|
18c26b90af | ||
|
|
ff49f797e5 | ||
|
|
c033a0d7c8 | ||
|
|
461f3c1004 | ||
|
|
90447d54d1 | ||
|
|
439d30a953 | ||
|
|
a972230b3a | ||
|
|
29a080dd9b | ||
|
|
0570cb8d9f | ||
|
|
6fa14bf029 | ||
|
|
4f0158b5e0 | ||
|
|
97add4d57a | ||
|
|
5b742571d3 | ||
|
|
a83043d735 | ||
|
|
22fcd616aa | ||
|
|
1a8188e1b4 | ||
|
|
f87fe4c3b4 | ||
|
|
f23f3ca907 | ||
|
|
965d99f1bc | ||
|
|
56ac054fb2 | ||
|
|
ced82cbac4 | ||
|
|
2fc1746ef5 | ||
|
|
10fc1f757c | ||
|
|
90009b9ec7 | ||
|
|
4367e03e46 | ||
|
|
c9634695a7 | ||
|
|
2a12b052f2 | ||
|
|
84b9502af4 | ||
|
|
694bdbc130 | ||
|
|
880cac2fdd | ||
|
|
a204b24fcf | ||
|
|
526e6b9fc7 | ||
|
|
f1ba6cb517 | ||
|
|
159cf0429c | ||
|
|
46b8dca360 | ||
|
|
4db998d357 | ||
|
|
0b843ada03 | ||
|
|
746c7ea8f7 | ||
|
|
5ed882faf2 | ||
|
|
fa4b10b4d9 | ||
|
|
cc51966ad1 | ||
|
|
c9d6344f2f | ||
|
|
acebafcbc2 | ||
|
|
4a1a2def95 | ||
|
|
09e352ef66 | ||
|
|
17976d7401 | ||
|
|
f25a9a4c80 | ||
|
|
cd2a5771b8 | ||
|
|
9c2ead90d5 | ||
|
|
a999c73ce0 | ||
|
|
b92173bf3d | ||
|
|
91823c41c5 | ||
|
|
c6bce636ee | ||
|
|
75a70bee6d | ||
|
|
1b1907af49 | ||
|
|
1b18eee5dc | ||
|
|
4931e48016 | ||
|
|
1b17acdb01 | ||
|
|
9c1cc5465d | ||
|
|
d8b5359fcb | ||
|
|
f96454adcc | ||
|
|
9da012568c | ||
|
|
9a20a258f5 | ||
|
|
10796457d2 | ||
|
|
78e659a142 | ||
|
|
2f50642ecb | ||
|
|
47bce4157d | ||
|
|
3a8a314566 | ||
|
|
050060b139 | ||
|
|
ddb678cc5b | ||
|
|
63c694f65f | ||
|
|
12093e6939 | ||
|
|
9949a6cd17 | ||
|
|
4600932d8c | ||
|
|
79ded3bb61 | ||
|
|
95145038b4 | ||
|
|
ce03293128 | ||
|
|
72e7271a14 | ||
|
|
0a8cfb8d2c | ||
|
|
0b07679ff2 | ||
|
|
550a5111b7 | ||
|
|
d162619bd3 | ||
|
|
5df14627fd | ||
|
|
fc3ce29845 | ||
|
|
bba800f2d6 | ||
|
|
fe2a1e1749 | ||
|
|
fc4ffd454e | ||
|
|
8dae4ad93a | ||
|
|
8da261f302 | ||
|
|
7bc7b5372e | ||
|
|
7d88e63bab | ||
|
|
a214fd1f78 | ||
|
|
79be097e34 | ||
|
|
11b0d36ba2 | ||
|
|
1234ee5fd2 | ||
|
|
80b73859a2 | ||
|
|
a548a77f26 | ||
|
|
d6bfb37e6a | ||
|
|
af305bf280 | ||
|
|
38a8d7fb2e | ||
|
|
0c70a0dcbf | ||
|
|
d16619fe86 | ||
|
|
ab6e1c2399 | ||
|
|
2dc85a5acd | ||
|
|
dbe1348b4c | ||
|
|
22846df03e | ||
|
|
d74536a4dc | ||
|
|
da20da264b | ||
|
|
22b9dfb9c9 | ||
|
|
27031ed688 | ||
|
|
e43c089ab8 | ||
|
|
d85b909054 | ||
|
|
75fd71d7e5 | ||
|
|
9efd64ab98 | ||
|
|
7aa01625d8 | ||
|
|
a3c58d7b70 | ||
|
|
ae466b1703 | ||
|
|
2564484429 | ||
|
|
93a044f587 | ||
|
|
81fe35ee2e | ||
|
|
08b6bdddf9 | ||
|
|
4d49db48a3 | ||
|
|
9b2266f68c | ||
|
|
752f425025 | ||
|
|
47262cd4ec | ||
|
|
47e64535d6 | ||
|
|
45d622e4bc | ||
|
|
249feaae7c | ||
|
|
9a27004ae5 | ||
|
|
42283150d7 | ||
|
|
5a1bcf9794 | ||
|
|
361cef4633 | ||
|
|
90ab746a42 | ||
|
|
e94cdcf29c | ||
|
|
1af046986d | ||
|
|
1c9263a25b | ||
|
|
fd6275a62b | ||
|
|
6b6790fc50 | ||
|
|
fb931087ab | ||
|
|
a2e1b32a1c | ||
|
|
54f89bce42 | ||
|
|
8b19d02e3b | ||
|
|
da7ad35577 | ||
|
|
937e893b6d | ||
|
|
d42a2d6494 | ||
|
|
8e8f4b1e5c | ||
|
|
298f71f2b1 | ||
|
|
2ba39b25f1 | ||
|
|
5c828b7e60 | ||
|
|
4640bea31a | ||
|
|
875361d7ce | ||
|
|
849bb5590a | ||
|
|
8d0c06e570 | ||
|
|
5b2fbf4f37 | ||
|
|
592dab2bdb | ||
|
|
51daa64e9a | ||
|
|
c0f9b0d8f6 | ||
|
|
505bba14eb | ||
|
|
ebbe24966c | ||
|
|
881c3f6e40 | ||
|
|
546aeab9f2 | ||
|
|
8de21e9bb7 | ||
|
|
2ecda74471 | ||
|
|
2136afde6b | ||
|
|
ea55db7286 | ||
|
|
819e8741cc | ||
|
|
6a5f77b278 | ||
|
|
8658a0d7dc | ||
|
|
c335aad25f | ||
|
|
9d9d647c1f | ||
|
|
d47a37fb76 | ||
|
|
24ec84851a | ||
|
|
0a3a15a66e | ||
|
|
2358ced061 | ||
|
|
2af5304884 | ||
|
|
5869fdf7f6 | ||
|
|
1a1d2ecfd9 | ||
|
|
5c356a4680 | ||
|
|
39af5d020e | ||
|
|
9ac64d0b92 | ||
|
|
7cc89b8b42 | ||
|
|
c6607724cb | ||
|
|
ebb37b0156 | ||
|
|
90dc04cf93 | ||
|
|
10cf492946 | ||
|
|
4d81eb021d | ||
|
|
83475008d9 | ||
|
|
03ac7ad730 | ||
|
|
cd50949a7e | ||
|
|
f0040c7b9a | ||
|
|
f8a0389e73 | ||
|
|
b436794773 | ||
|
|
1404319b28 | ||
|
|
90d22cbec6 | ||
|
|
33e2acfe61 | ||
|
|
db5fdfb0ee | ||
|
|
951cd73fe0 | ||
|
|
0eeb40b975 | ||
|
|
93272c13fd | ||
|
|
43abaeb055 | ||
|
|
18b9e661c9 | ||
|
|
f435d96241 | ||
|
|
d32e3f042f | ||
|
|
df5f95a5bd | ||
|
|
b0c475205b | ||
|
|
1e2219347e | ||
|
|
cbe1b8e266 | ||
|
|
2b12fe5773 | ||
|
|
a4fd6d38fb | ||
|
|
d20f22beb1 | ||
|
|
f4fe10844b | ||
|
|
122e89ffde | ||
|
|
c266739e9f | ||
|
|
96a4b3e5a5 | ||
|
|
7403226a97 | ||
|
|
fac8e76923 | ||
|
|
b864f5f5ba | ||
|
|
f9f66d787e | ||
|
|
bd211c87aa | ||
|
|
6c33e6e889 | ||
|
|
6fc25dae9e | ||
|
|
cfb6fd8b34 | ||
|
|
39d1a62f9e | ||
|
|
ba66fa7200 | ||
|
|
cb60663d49 | ||
|
|
0d2bf23d82 | ||
|
|
682a60e325 | ||
|
|
2ab6272cc7 | ||
|
|
152a6cd886 | ||
|
|
3c54d5df70 | ||
|
|
f91793e931 | ||
|
|
12925cd8b0 | ||
|
|
daac7cb90d | ||
|
|
a2d26b45bb | ||
|
|
dce8f3d25d | ||
|
|
38e5b6c1ad | ||
|
|
4859f5e1fa | ||
|
|
b9d7819faa | ||
|
|
ad2fbfe62d | ||
|
|
1f9323d086 | ||
|
|
574dbfded1 | ||
|
|
2a87387eb3 | ||
|
|
a5ddd10e31 | ||
|
|
e871cd8a44 | ||
|
|
7b431b37c7 | ||
|
|
eeef5ab4de | ||
|
|
ac4ec87942 | ||
|
|
290c2e0388 | ||
|
|
89924bda51 | ||
|
|
60eab3eb8c | ||
|
|
386401da60 | ||
|
|
31abe537a0 | ||
|
|
e0b61ceabd | ||
|
|
f9ecbdc70b | ||
|
|
30e3edb81d | ||
|
|
542e324869 | ||
|
|
1c5e5704f5 | ||
|
|
1d400dd98c | ||
|
|
a4ed3c2086 | ||
|
|
efb5caad22 | ||
|
|
3a002b6cf2 | ||
|
|
f8b7981be4 | ||
|
|
b55ee79d86 | ||
|
|
af70db88db | ||
|
|
7ef033f876 | ||
|
|
db59c07f27 | ||
|
|
9085e34d70 | ||
|
|
0dfa7d3af5 | ||
|
|
c28bf9ce24 | ||
|
|
5a1f1796d7 | ||
|
|
ffc95fcf0e | ||
|
|
b0e796ca83 | ||
|
|
3773e2ef95 | ||
|
|
00c1f67c57 | ||
|
|
3f19ba1554 | ||
|
|
f37cc2353d | ||
|
|
6a7bda6910 | ||
|
|
ff1b9930fc | ||
|
|
582da6df9a | ||
|
|
3bd830eacf | ||
|
|
f4efcbde5c | ||
|
|
658819bb71 | ||
|
|
3fc8249429 | ||
|
|
e0a38ce2c2 | ||
|
|
178e1789b5 | ||
|
|
108c900387 | ||
|
|
63507e8e29 | ||
|
|
87f1e06c41 | ||
|
|
7e8bafd217 | ||
|
|
457ad07ade | ||
|
|
c9ac9a75a6 | ||
|
|
79c901c220 | ||
|
|
b61d819fcb | ||
|
|
f4038e3674 | ||
|
|
8377514127 | ||
|
|
d8ea989eda | ||
|
|
5f3316d3dc | ||
|
|
a4fddb3fbe | ||
|
|
3d71716a8a | ||
|
|
9f37cef1bb | ||
|
|
0de1bbc85b | ||
|
|
3aa8624658 | ||
|
|
42b711b862 | ||
|
|
b25b798dbe | ||
|
|
2873dbe154 | ||
|
|
31a83cb0d8 | ||
|
|
383e384739 | ||
|
|
1bb6433649 | ||
|
|
ced39d0982 | ||
|
|
fc4d6a62af | ||
|
|
fa99cbbc73 | ||
|
|
49455721a3 | ||
|
|
11032b1936 | ||
|
|
e7ca4a69e3 | ||
|
|
f2fba51fe2 | ||
|
|
7c79f0d431 | ||
|
|
41b9b30b9b | ||
|
|
1fa82ffb7b | ||
|
|
4f93ac6477 | ||
|
|
f7533010c6 | ||
|
|
47b650d821 | ||
|
|
0c2c097377 | ||
|
|
d721f70690 | ||
|
|
ea8aaa21e8 | ||
|
|
660d1059b0 | ||
|
|
e7dc2de14b | ||
|
|
aca9fd3bed | ||
|
|
09f3f40462 | ||
|
|
e2525d8440 | ||
|
|
6769106881 | ||
|
|
32100a3c91 | ||
|
|
0f324c8309 | ||
|
|
67f7432dd7 | ||
|
|
59dc2853e3 | ||
|
|
ada58465b2 | ||
|
|
173f57c636 | ||
|
|
1f3e8640f7 | ||
|
|
6039e9dcc3 | ||
|
|
d64f979f99 | ||
|
|
0c53d19113 | ||
|
|
c64a07fd09 | ||
|
|
694919b9d1 | ||
|
|
606b35853b | ||
|
|
26b7452178 | ||
|
|
b51f887567 | ||
|
|
c4b8675d77 | ||
|
|
a7cd7a1ed9 | ||
|
|
a48c779367 | ||
|
|
cf1eccc24f | ||
|
|
d45caca011 | ||
|
|
0722f47539 | ||
|
|
3af5e7e8da | ||
|
|
160f3ee4a7 | ||
|
|
3a310f19f0 | ||
|
|
d7a75dc026 | ||
|
|
a372e010bd | ||
|
|
530817191e | ||
|
|
1282548a1c | ||
|
|
c7610ed044 | ||
|
|
ffc1c51526 | ||
|
|
b130a8cfeb | ||
|
|
551bd284dd | ||
|
|
f0a2c98376 | ||
|
|
9edccfdffa | ||
|
|
1a056f6db9 | ||
|
|
e018eb7bac | ||
|
|
0a91ddf38a | ||
|
|
67bb2c640e | ||
|
|
561eaa311d | ||
|
|
282887d9c6 | ||
|
|
3f7a248ed4 | ||
|
|
ecaa07a72a | ||
|
|
0e62cd11da | ||
|
|
99bdc27be3 | ||
|
|
c3c46967dc | ||
|
|
7f1a9239ab | ||
|
|
599d23697d | ||
|
|
c8102c8ffe | ||
|
|
5aa12e9b51 | ||
|
|
829437b20b | ||
|
|
3c4131d45d | ||
|
|
ae8d8ee1ac | ||
|
|
39b7c47e7b | ||
|
|
89ec3d16dc | ||
|
|
f6f03bc261 | ||
|
|
7602c983c5 | ||
|
|
560efb2c9e | ||
|
|
c2328ef46a | ||
|
|
766fb56651 | ||
|
|
f6035447ae | ||
|
|
38438b3373 | ||
|
|
71dedccbbe | ||
|
|
cddbe4642f | ||
|
|
bdfdc737a0 | ||
|
|
3d045c3aee | ||
|
|
9583f152ee | ||
|
|
5b2755d28d | ||
|
|
bec4610e12 | ||
|
|
9378259779 | ||
|
|
3d762282b9 | ||
|
|
184ebe72a2 | ||
|
|
30783e6a79 | ||
|
|
aa86c777f4 | ||
|
|
a2792785fe | ||
|
|
267315e7d4 | ||
|
|
2ad0bcbba9 | ||
|
|
99d39cb6ff | ||
|
|
915ceb2d04 | ||
|
|
88bb7cdca6 | ||
|
|
49c90ecbce | ||
|
|
8395004d25 | ||
|
|
5de1c619c8 | ||
|
|
a6d678d41d | ||
|
|
2b91d764b5 | ||
|
|
46de9460a6 | ||
|
|
62337bb6ac | ||
|
|
9abab2c366 | ||
|
|
b22e368b25 | ||
|
|
a9cd2998e5 | ||
|
|
f1ea30f257 | ||
|
|
0b74e9b354 | ||
|
|
8189416d0c | ||
|
|
5f0e1fae7f | ||
|
|
880a9be3b1 | ||
|
|
7bfc1a00a7 | ||
|
|
92f64de115 | ||
|
|
f4baa86b71 | ||
|
|
baff64a43d | ||
|
|
b7f2bb0e80 | ||
|
|
08e0a397d3 | ||
|
|
5f333be947 | ||
|
|
f5f4e15ce2 | ||
|
|
fbd568dc47 | ||
|
|
c79ea88576 | ||
|
|
9be4e7b576 | ||
|
|
33105f017c | ||
|
|
e02f97854c | ||
|
|
960d0de361 | ||
|
|
31ce1fdfd3 | ||
|
|
9821381059 | ||
|
|
3f7bf3d2dc | ||
|
|
f9e69984ff | ||
|
|
b1b5b5dfe2 | ||
|
|
c7440b250f | ||
|
|
bab8462789 | ||
|
|
4eaa6fdd06 | ||
|
|
38b2845e2a | ||
|
|
14d24213a8 | ||
|
|
c61258c0d0 | ||
|
|
47226236f4 | ||
|
|
e744c21f56 | ||
|
|
89a0632ecc | ||
|
|
dcc05db7f1 | ||
|
|
90fb2e5487 | ||
|
|
b5ce7d5982 | ||
|
|
f211616a4c | ||
|
|
a9cb9bac21 | ||
|
|
ae005434e4 | ||
|
|
ef87d057fc | ||
|
|
181b9a5795 | ||
|
|
9baf9c55c2 | ||
|
|
e29132377e | ||
|
|
999751c422 | ||
|
|
22656d6fdd | ||
|
|
ef0c5fd772 | ||
|
|
90c61c79d6 | ||
|
|
4ba3bff87f | ||
|
|
c3dd6f5344 | ||
|
|
321880f5a6 | ||
|
|
3737d209f6 | ||
|
|
29c4b0a141 | ||
|
|
956f64c6ba | ||
|
|
c2d18d8ae4 | ||
|
|
5b9806ae6d | ||
|
|
665fa140a8 | ||
|
|
32f843a214 | ||
|
|
72a3c756ad | ||
|
|
47069dfe52 | ||
|
|
43a84d7ad8 | ||
|
|
b6ca2a42f2 | ||
|
|
e52a4ac74f | ||
|
|
decfa6bd7a | ||
|
|
8931bd37e2 | ||
|
|
4b79d23d00 | ||
|
|
77fe7c426e | ||
|
|
331c2244fc | ||
|
|
5a3eab634a | ||
|
|
77e68acf54 | ||
|
|
7a355d448a | ||
|
|
3b7ad1820d | ||
|
|
d3b63b2653 | ||
|
|
046896e60a | ||
|
|
fb9119ff49 | ||
|
|
818aaa8b89 | ||
|
|
26bdfc3474 | ||
|
|
0e91d8a10e | ||
|
|
434c3c3ef3 | ||
|
|
21e05c43dd | ||
|
|
0e154b35ef |
+21
-11
@@ -17,7 +17,7 @@ AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
@@ -56,18 +56,29 @@ DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
|
||||
# Include grouping/sorting
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
- Regex: '"(config_build|verilated_config|verilatedos)\.h"'
|
||||
Priority: -1 # Sepecials before main header
|
||||
- Regex: '(<|")verilated.*'
|
||||
Priority: 1 # Runtime headers
|
||||
- Regex: '"V3.*__gen.*\.h"'
|
||||
Priority: 3 # Generated internal headers separately
|
||||
- Regex: '"V3.*"'
|
||||
Priority: 2 # Internal header
|
||||
- Regex: '".*"'
|
||||
Priority: 4 # Other non-system headers
|
||||
- Regex: '<[[:alnum:]_.]+>'
|
||||
Priority: 5 # Simple system headers next
|
||||
- Regex: '<.*>'
|
||||
Priority: 6 # Other system headers next
|
||||
IncludeIsMainRegex: '$'
|
||||
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
@@ -91,7 +102,6 @@ PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04]
|
||||
os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04]
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
@@ -37,9 +37,16 @@ jobs:
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-20.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-22.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-20.04
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-22.04, m32: 1}
|
||||
include:
|
||||
# Build GCC 10 on ubuntu-20.04
|
||||
- os: ubuntu-20.04
|
||||
compiler: { cc: gcc-10, cxx: g++-10 }
|
||||
m32: 0
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} ${{ matrix.m32 && '| -m32' || '' }}
|
||||
env:
|
||||
@@ -54,12 +61,12 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: repo
|
||||
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||
with:
|
||||
@@ -79,7 +86,7 @@ jobs:
|
||||
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
|
||||
|
||||
- name: Upload tar archive
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
@@ -90,7 +97,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04]
|
||||
os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04]
|
||||
compiler:
|
||||
- { cc: clang, cxx: clang++ }
|
||||
- { cc: gcc, cxx: g++ }
|
||||
@@ -99,9 +106,18 @@ jobs:
|
||||
exclude:
|
||||
# Build pull requests only with ubuntu-20.04 and without m32
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
|
||||
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-22.04' || 'do-not-exclude' }}
|
||||
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
|
||||
# Build -m32 only on ubuntu-20.04
|
||||
- {os: ubuntu-18.04, m32: 1}
|
||||
- {os: ubuntu-22.04, 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}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-1}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-2}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: vltmt-0}
|
||||
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: vltmt-1}
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }} ${{ matrix.m32 && '| -m32' || '' }}
|
||||
env:
|
||||
@@ -110,13 +126,13 @@ jobs:
|
||||
CI_M32: ${{ matrix.m32 }}
|
||||
CC: ${{ matrix.compiler.cc }}
|
||||
CXX: ${{ matrix.compiler.cxx }}
|
||||
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}-${ matrix.suite }}
|
||||
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}-${{ matrix.suite }}
|
||||
CCACHE_MAXSIZE: 64M # Per build matrix entry (2160M in total)
|
||||
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}${{ matrix.m32 && '-m32' || '' }}.tar.gz
|
||||
steps:
|
||||
|
||||
- name: Download tar archive
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
path: ${{ github.workspace }}
|
||||
@@ -126,7 +142,7 @@ jobs:
|
||||
run: tar -x -z -f ${{ env.VERILATOR_ARCHIVE }}
|
||||
|
||||
- name: Cache $CCACHE_DIR
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
|
||||
with:
|
||||
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
Test:
|
||||
name: "'docs/CONTRIBUTORS' was signed"
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- run: test_regress/t/t_dist_contributors.pl
|
||||
|
||||
@@ -24,14 +24,14 @@ jobs:
|
||||
|
||||
|
||||
Build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CI_RUNS_ON: ubuntu-22.04
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: repo
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
run: tar --posix -c -z -f ${{ env.VERILATOR_ARCHIVE }} repo
|
||||
|
||||
- name: Upload tar archive
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/${{ env.VERILATOR_ARCHIVE }}
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
@@ -73,15 +73,15 @@ jobs:
|
||||
- 9
|
||||
include:
|
||||
- { test: dist, num: '' }
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
name: test-${{ matrix.test }}${{ matrix.num }}
|
||||
env:
|
||||
CI_BUILD_STAGE_NAME: test
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CI_RUNS_ON: ubuntu-22.04
|
||||
steps:
|
||||
|
||||
- name: Download tar archive
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ env.VERILATOR_ARCHIVE }}
|
||||
path: ${{ github.workspace }}
|
||||
|
||||
@@ -10,16 +10,16 @@ on:
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-20.04
|
||||
name: Ubuntu 20.04 | format
|
||||
runs-on: ubuntu-22.04
|
||||
name: Ubuntu 22.04 | format
|
||||
env:
|
||||
CI_OS_NAME: linux
|
||||
CI_RUNS_ON: ubuntu-20.04
|
||||
CI_RUNS_ON: ubuntu-22.04
|
||||
CI_COMMIT: ${{ github.sha }}
|
||||
CI_M32: 0
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Install packages for build
|
||||
@@ -27,14 +27,14 @@ jobs:
|
||||
CI_BUILD_STAGE_NAME: build
|
||||
run: |
|
||||
bash ci/ci-install.bash &&
|
||||
sudo apt-get install clang-format-11 yapf3 &&
|
||||
sudo apt-get install clang-format-14 yapf3 &&
|
||||
git config --global user.email "[email protected]" &&
|
||||
git config --global user.name "github action"
|
||||
- name: Format code
|
||||
run: |
|
||||
autoconf &&
|
||||
./configure &&
|
||||
make -j 2 format CLANGFORMAT=clang-format-11 &&
|
||||
make -j 2 format CLANGFORMAT=clang-format-14 &&
|
||||
git status
|
||||
- name: Push
|
||||
run: |
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
\#*
|
||||
.#*
|
||||
.gdb_history
|
||||
.nfs*
|
||||
*~
|
||||
*.tidy
|
||||
|
||||
@@ -8,6 +8,193 @@ The changes in each Verilator version are described below. The
|
||||
contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.002 2022-10-29
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* This is a major new release.
|
||||
* Require C++20 for the new --timing features. Upgrading to a C++20 or
|
||||
newer compiler is strongly recommended.
|
||||
* Support the Active and NBA scheduling regions as defined by the
|
||||
SystemVerilog standard (IEEE 1800-2017 chapter 4). This means all generated
|
||||
clocks are now simulated correctly (#3278, #3384). [Geza Lore, Shunyao CAD]
|
||||
* Support timing controls (delays, event controls in any location, wait
|
||||
statements) and forks. [Krzysztof Bieganski, Antmicro Ltd]
|
||||
This may require adding --timing or --no-timing. See docs for details.
|
||||
* Introduce a new combinational logic optimizer (DFG), that can yield
|
||||
significant performance improvements on some designs. [Geza Lore, Shunyao CAD]
|
||||
* Add --binary option as alias of --main --exe --build --timing (#3625).
|
||||
For designs where C++ was only used to make a simple no-I/O testbench, we
|
||||
recommend abandoning that C++, and instead letting Verilator build it
|
||||
with --binary (or --main).
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Split UNUSED warning into genvar, param, and signal warnings (#3607). [Topa Topino]
|
||||
* Support standalone 'this' in classes (#2594) (#3248) (#3675). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Support tristate select/extend (#3604). [Ryszard Rozak, Antmicro Ltd>
|
||||
* Support linting for top module interfaces (#3635). [Kanad Kanhere]
|
||||
* Support virtual interfaces (#3654). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Support class type params without defaults (#3693). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Support empty generate_regions (#3695). [mpb27]
|
||||
* Support access to constructs inside type parameters (#3702). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Add --dump-tree-dot to enable dumping Ast Tree .dot files (#3636). [Marcel Chang]
|
||||
* Add --get-supported to determine what features are in Verilator.
|
||||
* Add error on real edge event control.
|
||||
* Fix false LATCH warning on 'unique if' (#3088). [Rachit Nigam]
|
||||
* Fix cell assigning integer array parameters (#3299). [Michael Platzer]
|
||||
* Fix LSB error on --hierarchical submodules (#3539). [danbone]
|
||||
* Fix $display of fixed-width numbers (#3565). [Iztok Jeras]
|
||||
* Fix foreach and pre/post increment in functions (#3613). [Nandu Raj]
|
||||
* Fix linker errors in user-facing timing functions (#3657). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix null access on optimized-out fork statements (#3658). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix VPI inline module naming mismatch (#3690) (#3694). [Jiuyang Liu]
|
||||
* Fix deadlock in timeprecision when using systemC (#3707). [Kamil Rakoczy, Antmicro Ltd]
|
||||
* Fix width mismatch on inside operator (#3714). [Alex Torregrosa]
|
||||
|
||||
|
||||
Verilator 4.228 2022-10-01
|
||||
==========================
|
||||
|
||||
**Announcement:**
|
||||
|
||||
* The next release is anticipated to premiere Verilator Version 5. Please
|
||||
consider beta-testing the github 'develop-v5' branch, which will soon
|
||||
merge into the github 'master' branch (#3383).
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Support some IEEE signal strengths (#3601) (#3629). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Add --main to generate main() C++ (previously was experimental only).
|
||||
* Add --build-jobs, and rework arguments for -j (#3623). [Kamil Rakoczy]
|
||||
* Rename --bin to --build-dep-bin.
|
||||
* Rename debug flags --dumpi-tree, --dumpi-graph, etc. [Geza Lore]
|
||||
* Fix thread safety in SystemC VL_ASSIGN_SBW/WSB (#3494) (#3513). [Mladen Slijepcevic]
|
||||
* Fix crash in gate optimization of circular logic (#3543). [Bill Flynn]
|
||||
* Fix arguments in non-static method call (#3547) (#3582). [Gustav Svensk]
|
||||
* Fix default --mod-prefix when --prefix is repeated (#3603). [Geza Lore]
|
||||
* Fix calling trace() after open() segfault (#3610) (#3627). [Yu-Sheng Lin]
|
||||
* Fix typedef'ed class conversion to Boolean (#3616). [Aleksander Kiryk]
|
||||
* Fix Verilation speed when disabled warnings (#3632). [Kamil Rakoczy, Antmicro Ltd]
|
||||
|
||||
|
||||
Verilator 4.226 2022-08-31
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add --future0 and --future1 options.
|
||||
* Support class parameters (#2231) (#3541). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Support wildcard index associative arrays (#3501). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Support negated properties (#3572). [Aleksander Kiryk]
|
||||
* Support $test$plusargs(expr) (#3489).
|
||||
* Rename trace rolloverSize() (#3570).
|
||||
* Improve Verilation speed with --threads on large designs. [Geza Lore]
|
||||
* Improve Verilation memory by reducing V3Number (#3521). [Mariusz Glebocki, Antmicro Ltd]
|
||||
* Fix struct pattern assignment (#2328) (#3517). [Mostafa Gamal]
|
||||
* Fix public combo propagation issues (#2905). [Todd Strader]
|
||||
* Fix incorrect tristate logic (#3399) [shareefj, Vighnesh Iyer]
|
||||
* Fix incorrect bit op tree optimization (#3470). [algrobman]
|
||||
* Fix bisonpre for MSYS2 (#3471).
|
||||
* Fix max memory usage (#3483). [Kamil Rakoczy, Antmicro Ltd]
|
||||
* Fix empty string arguments to display (#3484). [Grulfen]
|
||||
* Fix table optimizing away display (#3488). [Stefan Post]
|
||||
* Fix unique_ptr memory header for MinGW64 (#3493).
|
||||
* Fix $dump system task with --output-split-cfuncs (#3495) (#3497). [Varun Koyyalagunta]
|
||||
* Fix wrong bit op tree optimization (#3509). [Nathan Graybeal]
|
||||
* Fix nested default assignment for struct pattern (#3511) (#3524). [Mostafa Gamal]
|
||||
* Fix sformat string incorrectly cleared (#3515) (#3519). [Gustav Svensk]
|
||||
* Fix segfault exporting non-existent package (#3535).
|
||||
* Fix void-cast queue pop_front or pop_back (#3542) (#3364). [Drew Ranck]
|
||||
* Fix case statement comparing string literal (#3544). [Gustav Svensk]
|
||||
* Fix === with some tristate constants (#3551). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Fix converting classes to string (#3552). [Arkadiusz Kozdra, Antmicro Ltd]
|
||||
* Fix --hierarchical with order-based pin connections (#3583) (#3585). [Kelin9298]
|
||||
|
||||
|
||||
Verilator 4.224 2022-06-19
|
||||
==========================
|
||||
|
||||
**Major:**
|
||||
|
||||
* VCD tracing is now parallelized with --threads (#3449). [Geza Lore, Shunyao CAD]
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Add -f<optimization> options to replace -O<letter> options (#3436).
|
||||
* Changed --no-merge-const-pool to -fno-merge-const-pool (#3436).
|
||||
* Changed --no-decoration to remove output whitespace (#3460). [Kamil Rakoczy]
|
||||
* Support compile time trace signal selection with tracing_on/off (#3323). [Shunyao CAD]
|
||||
* Support non-ANSI interface port declarations (#3439). [Geza Lore, Shunyao CAD]
|
||||
* Support concat assignment to packed array (#3446).
|
||||
* Improve conditional merging optimization (#3125). [Geza Lore, Shunyao CAD]
|
||||
* Define VM_TRACE_VCD when tracing in VCD format. [Geza Lore, Shunyao CAD]
|
||||
* Add assert when VerilatedContext is mis-deleted (#3121). [Rupert Swarbrick]
|
||||
* Internal prep work towards timing control. [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix hang with large case statement optimization (#3405). [Mike Urbach]
|
||||
* Fix UNOPTFLAT warning from initial static var (#3406). [Kamil Rakoczy]
|
||||
* Fix compile error when enable VL_LEAK_CHECKS (#3411). [HungMingWu]
|
||||
* Fix cmake rules to support higher-level targets (#3377) (#3386). [Martin Stadler]
|
||||
* Fix BLKANDNBLK on $readmem/$writemem (#3379). [Alex Solomatnikov]
|
||||
* Fix 'with' operator with type casting (#3387). [xiak95]
|
||||
* Fix incorrect conditional merging (#3409). [Raynard Qiao]
|
||||
* Fix passing VL_TRACE_FST_WRITER_THREAD in CMake build. [Geza Lore, Shunyao CAD]
|
||||
* Fix compile error under strict C++11 mode (#3463). [Kevin Kiningham]
|
||||
* Fix public unpacked input ports (#3465). [Todd Strader]
|
||||
|
||||
|
||||
Verilator 4.222 2022-05-02
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Split --prof-threads into --prof-exec and --prof-pgo (#3365). [Geza Lore, Shunyao CAD]
|
||||
* Deprecate 'vluint64_t' and similar types (#3255).
|
||||
* Raise error on assignment to const in initial blocks. [Geza Lore, Shunyao CAD]
|
||||
* Issue INITIALDLY/COMBDLY/BLKSEQ warnings consistent with Verilator execution. [Geza Lore, Shunyao CAD]
|
||||
* Support LoongArch ISA multithreading (#3353) (#3354). [Xi Zhang]
|
||||
* Fix MSVC localtime_s (#3124).
|
||||
* Fix Bison 3.8.2 error (#3366). [elike-ypq]
|
||||
* Fix rare bug in -Oz (V3Localize) (#3286). [Geza Lore, Shunyao CAD]
|
||||
* Fix tracing interfaces inside interfaces (#3309). [Kevin Millis]
|
||||
* Fix filenames with dots overwriting debug .vpp files (#3373).
|
||||
* Fix including VK_USER_OBJS in make library (#3370) (#3382). [Julien Margetts]
|
||||
* Fix hang in generate symbol references (#3391) (#3398). [Yoda Lee]
|
||||
* Fix missing #include <memory> (#3392). [Aliaksei Chapyzhenka]
|
||||
* Fix crash in recursive module inlining (#3393). [david-sawatzke]
|
||||
* Fix --protect-ids mangling names of library methods. [Geza Lore, Shunyao CAD]
|
||||
* Fix foreach segmentation fault (#3400). [Kamil Rakoczy]
|
||||
|
||||
|
||||
Verilator 4.220 2022-03-12
|
||||
==========================
|
||||
|
||||
**Minor:**
|
||||
|
||||
* Removed the deprecated lint_off flag -msg; use -rule instead.
|
||||
* Removed the deprecated "fl" attribute in XML output; use "loc" attribute instead.
|
||||
* Suppress WIDTH warning on negate using carry bit (#3295). [Peter Monsson]
|
||||
* Add trace dumpvars() call for selective runtime tracing (#3322). [Shunyao CAD]
|
||||
* Add VERILATOR_VERSION_INTEGER for determining API (#3343). [Larry Doolittle]
|
||||
* Improve various V3Combine algorithm details (#3328). [Yutetsu TAKATSUKASA]
|
||||
* Improve various V3Order algorithm details. [Geza Lore]
|
||||
* Fix MacOS arm64 build (#3285) (#3291). [Guokai Chen]
|
||||
* Fix signed number operation (#3294) (#3308). [Raynard Qiao]
|
||||
* Fix FST traces to include vector range (#3296) (#3297). [Jamie Iles]
|
||||
* Fix skipping public enum values with four-state values (#3303).
|
||||
* Fix $readmem file not found to be warning not error (#3310). [Alexander Grobman]
|
||||
* Fix class stringification on wide arrays (#3312). [Iru Cai]
|
||||
* Fix $fscanf etc to return -1 on EOF (#3313). [Jose Tejada]
|
||||
* Fix public function arguments that are arrayed (#3316). [pawel256]
|
||||
* Fix unnamedblk error on foreach (#3321). [Aliaksei Chapyzhenka]
|
||||
* Fix crash in recursive module inlining (#3324). [Larry Doolittle]
|
||||
* Fix VL_RESTORER behavior on passing a lvalue reference (#3326). [HungMingWu]
|
||||
* Fix compile error with --trace-fst --sc (#3332). [leavinel]
|
||||
* Fix cast to array types (#3333). [Todd Strader]
|
||||
* Fix Vdeeptemp error with --threads and --compiler clang (#3338). [Per Karlsson]
|
||||
|
||||
|
||||
Verilator 4.218 2022-01-17
|
||||
==========================
|
||||
|
||||
@@ -57,7 +244,7 @@ Verilator 4.216 2021-12-05
|
||||
**Minor:**
|
||||
|
||||
* Internal code cleanups and improvements. [Geza Lore]
|
||||
* Improve --thread verilation-time performance.
|
||||
* Improve --thread Verilation-time performance.
|
||||
* Support task name in $display %m (#3211). [Julie Schwartz]
|
||||
* Make 'bit', 'logic' and 'time' types unsigned by default. [Geza Lore]
|
||||
* Optimize $random concatenates/selects (#3114).
|
||||
@@ -81,7 +268,7 @@ Verilator 4.214 2021-10-17
|
||||
|
||||
**Major:**
|
||||
|
||||
* Add profile-guided optmization of mtasks (#3150).
|
||||
* Add profile-guided optimization of mtasks (#3150).
|
||||
|
||||
**Minor:**
|
||||
|
||||
@@ -111,7 +298,7 @@ Verilator 4.212 2021-09-01
|
||||
* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore]
|
||||
* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore]
|
||||
* Support timeunit/timeprecision in $unit.
|
||||
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski]
|
||||
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded
|
||||
model scheduling. Default value changed to 200 (#3068). [Yinan Xu]
|
||||
* Output files are split based on the set of headers required
|
||||
@@ -147,7 +334,7 @@ Verilator 4.210 2021-07-07
|
||||
|
||||
* Add --prof-c to pass profiling to compiler (#3059). [Alexander Grobman]
|
||||
* Optimize a lot more model variables into function locals (#3027). [Geza Lore]
|
||||
* Support middle-of-design nested topmodules (#3026). [Dan Petrisko]
|
||||
* Support middle-of-design nested top modules (#3026). [Dan Petrisko]
|
||||
* Remove deprecated --no-relative-cfuncs option (#3024). [Geza Lore]
|
||||
* Remove deprecated --inhibit-sim option (#3035). [Geza Lore]
|
||||
* Merge const static data globally into a new constant pool (#3013). [Geza Lore]
|
||||
@@ -176,7 +363,7 @@ Verilator 4.204 2021-06-12
|
||||
* Prep work towards better ccache hashing/performance. [Geza Lore]
|
||||
* Fix assertion failure in bitOpTree optimization (#2891) (#2899). [Raynard Qiao]
|
||||
* Fix DPI functions not seen as vpiModule (#2893). [Todd Strader]
|
||||
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski]
|
||||
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Fix slowdown in elaboration (#2911). [Nathan Graybeal]
|
||||
* Fix initialization of assoc in assoc array (#2914). [myftptoyman]
|
||||
* Fix make support for gmake 3.x (#2920) (#2921). [Philipp Wagner]
|
||||
@@ -186,10 +373,10 @@ Verilator 4.204 2021-06-12
|
||||
* Fix to emit 'else if' without nesting (#2944). [Geza Lore]
|
||||
* Fix part select issues in LATCH warning (#2948) (#2938). [Julien Margetts]
|
||||
* Fix to not emit empty files with low split limits (#2961). [Geza Lore]
|
||||
* Fix merging of assignments in C++ code (#2970). [Ruper Swarbrick]
|
||||
* Fix merging of assignments in C++ code (#2970). [Rupert Swarbrick]
|
||||
* Fix unused variable warnings (#2991). [Pieter Kapsenberg]
|
||||
* Fix --protect-ids when using SV classes (#2994). [Geza Lore]
|
||||
* Fix constant function calls with uninit value (#2995). [yanx21]
|
||||
* Fix constant function calls with uninitialized value (#2995). [yanx21]
|
||||
* Fix Makefiles to support Windows EXEEXT usage (#3008). [Miodrag Milanovic]
|
||||
|
||||
|
||||
@@ -227,7 +414,7 @@ Verilator 4.202 2021-04-24
|
||||
* Fix Cygwin example compile issues (#2856). [Mark Shaw]
|
||||
* Fix select of with index variable (#2880). [Alexander Grobman]
|
||||
* Fix cmake version number to be numeric (#2881). [Yuri Victorovich]
|
||||
* Fix MinGW not supportting 'localtime_r' (#2882). [HyungKi Jeong]
|
||||
* Fix MinGW not supporting 'localtime_r' (#2882). [HyungKi Jeong]
|
||||
* Fix cast from packed, typedef'ed interface signal (#2884). [Todd Strader]
|
||||
* Fix VPI package reported as vpiModule (#2885). [Todd Strader]
|
||||
* Fix dumping waveforms to multiple FST files (#2889). [David Metz]
|
||||
@@ -291,7 +478,7 @@ Verilator 4.108 2021-01-10
|
||||
**Major:**
|
||||
|
||||
* Many VPI changes for IEEE compatibility, which may alter behavior from previous releases.
|
||||
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski]
|
||||
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
|
||||
**Minor:**
|
||||
|
||||
@@ -351,7 +538,7 @@ Verilator 4.104 2020-11-14
|
||||
* Support queue and associative array 'with' statements (#2616).
|
||||
* Support queue slicing (#2326).
|
||||
* Support associative array pattern assignments and defaults.
|
||||
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski]
|
||||
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
* Add error on typedef referencing self (#2539). [Cody Piersall]
|
||||
* With --debug, turn off address space layout randomization.
|
||||
* Fix iteration over mutating list bug in VPI (#2588). [Kaleb Barrett]
|
||||
@@ -374,7 +561,7 @@ Verilator 4.102 2020-10-15
|
||||
* Support # as a comment in -f files (#2497). [phantom-killua]
|
||||
* Support 'this' (#2585). [Rafal Kapuscik]
|
||||
* Support defines for FST tracing (#2592). [Markus Krause]
|
||||
* Support non-overapping implication inside properties (#1292). [Peter Monsson]
|
||||
* Support non-overlapping implication inside properties (#1292). [Peter Monsson]
|
||||
* Fix timescale with --hierarchical (#2554). [Yutetsu TAKATSUKASA]
|
||||
* Fix cmake build with --hierarchical (#2560). [Yutetsu TAKATSUKASA]
|
||||
* Fix -G dropping public indication (#2561). [Andrew Goessling]
|
||||
@@ -400,7 +587,7 @@ Verilator 4.100 2020-09-07
|
||||
* Support (with limitations) class extern, class extends, virtual class.
|
||||
* Support $urandom, $urandom_range without stability.
|
||||
* Support assume property. [Peter Monsson]
|
||||
* Support non-overapping implication inside properties (#1292). [Peter Monsson]
|
||||
* Support non-overlapping implication inside properties (#1292). [Peter Monsson]
|
||||
* Fix false DECLFILENAME on black-boxed modules (#2430). [Philipp Wagner]
|
||||
* Fix naming of "id : begin" blocks.
|
||||
* Fix class constructor error on assignments to const.
|
||||
|
||||
+14
-9
@@ -107,6 +107,7 @@ SUBDIRS = docs src test_regress \
|
||||
examples/cmake_tracing_c \
|
||||
examples/cmake_tracing_sc \
|
||||
examples/cmake_protect_lib \
|
||||
examples/make_hello_binary \
|
||||
examples/make_hello_c \
|
||||
examples/make_hello_sc \
|
||||
examples/make_tracing_c \
|
||||
@@ -243,6 +244,7 @@ installdata:
|
||||
; for p in $(VL_INST_INC_SRCDIR_FILES) ; do \
|
||||
$(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \
|
||||
done
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_hello_binary
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_hello_c
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_hello_sc
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/make_tracing_c
|
||||
@@ -278,6 +280,7 @@ uninstall:
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/include/gtkwave
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/include/vltstd
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/include
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_hello_binary
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_hello_c
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_hello_sc
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/make_tracing_c
|
||||
@@ -322,16 +325,18 @@ cppcheck: $(CPPCHECK_DEP)
|
||||
$(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 -DVL_THREADED=1 $(CPPCHECK_INC) $<
|
||||
|
||||
CLANGTIDY = clang-tidy
|
||||
CLANGTIDY_FLAGS = -config='' -checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables'
|
||||
CLANGTIDY_DEP = $(subst .h,.h.tidy,$(CPPCHECK_H)) \
|
||||
$(subst .cpp,.cpp.tidy,$(CPPCHECK_CPP))
|
||||
CLANGTIDY_FLAGS = -config='' \
|
||||
-header-filter='.*' \
|
||||
-checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables,-cppcoreguidelines-avoid-goto,-modernize-avoid-c-arrays,-readability-magic-numbers,-readability-simplify-boolean-expr,-cppcoreguidelines-macro-usage' \
|
||||
|
||||
CLANGTIDY_DEP = $(subst .cpp,.cpp.tidy,$(CPPCHECK_CPP))
|
||||
CLANGTIDY_DEFS = -DVL_DEBUG=1 -DVL_THREADED=1 -DVL_CPPCHECK=1
|
||||
|
||||
clang-tidy: $(CLANGTIDY_DEP)
|
||||
%.cpp.tidy: %.cpp
|
||||
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
|
||||
%.h.tidy: %.h
|
||||
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
|
||||
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) -x c++-header | 2>&1 tee $@
|
||||
|
||||
analyzer-src:
|
||||
-rm -rf src/obj_dbg
|
||||
@@ -343,13 +348,13 @@ analyzer-include:
|
||||
|
||||
format: clang-format yapf format-pl-exec
|
||||
|
||||
CLANGFORMAT = clang-format-11
|
||||
CLANGFORMAT = clang-format-14
|
||||
CLANGFORMAT_FLAGS = -i
|
||||
CLANGFORMAT_FILES = $(CPPCHECK_CPP) $(CPPCHECK_H) $(CPPCHECK_YL) test_regress/t/*.c* test_regress/t/*.h
|
||||
|
||||
clang-format:
|
||||
@$(CLANGFORMAT) --version | egrep 11.0 > /dev/null \
|
||||
|| echo "*** You are not using clang-format 11.0, indents may differ from master's ***"
|
||||
@$(CLANGFORMAT) --version | egrep 14.0 > /dev/null \
|
||||
|| echo "*** You are not using clang-format-14, indents may differ from master's ***"
|
||||
$(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES)
|
||||
|
||||
PY_PROGRAMS = \
|
||||
@@ -388,10 +393,10 @@ yapf:
|
||||
FLAKE8 = flake8
|
||||
FLAKE8_FLAGS = \
|
||||
--extend-exclude=fastcov.py \
|
||||
--ignore=E123,E129,E251,E501,W503,W504,E701
|
||||
--ignore=E123,E129,E251,E402,E501,W503,W504,E701
|
||||
|
||||
PYLINT = pylint
|
||||
PYLINT_FLAGS = --disable=R0801
|
||||
PYLINT_FLAGS = --score=n --disable=R0801
|
||||
|
||||
lint-py:
|
||||
-$(FLAKE8) $(FLAKE8_FLAGS) $(PY_PROGRAMS)
|
||||
|
||||
+19
-17
@@ -21,14 +21,14 @@ Welcome to Verilator
|
||||
.. list-table::
|
||||
|
||||
* - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.**
|
||||
* Accepts synthesizable Verilog or SystemVerilog
|
||||
* Accepts Verilog or SystemVerilog
|
||||
* Performs lint code-quality checks
|
||||
* Compiles into multithreaded C++, or SystemC
|
||||
* Creates XML to front-end your own tools
|
||||
- |Logo|
|
||||
* - |verilator multithreaded performance|
|
||||
- **Fast**
|
||||
* Outperforms many commercial simulators
|
||||
* Outperforms many closed-source commercial simulators
|
||||
* Single- and multi-threaded output models
|
||||
* - **Widely Used**
|
||||
* Wide industry and academic deployment
|
||||
@@ -55,25 +55,25 @@ performing lint checks, and optionally inserting assertion checks and
|
||||
coverage-analysis points. It outputs single- or multi-threaded .cpp and .h
|
||||
files, the "Verilated" code.
|
||||
|
||||
The user writes a little C++/SystemC wrapper file, which instantiates the
|
||||
"Verilated" model of the user's top level module. These C++/SystemC files
|
||||
are then compiled by a C++ compiler (gcc/clang/MSVC++). The resulting
|
||||
executable performs the design simulation. Verilator also supports linking
|
||||
its generated libraries, optionally encrypted, into other simulators.
|
||||
These Verilated C++/SystemC files are then compiled by a C++ compiler
|
||||
(gcc/clang/MSVC++), optionally along with a user's own C++/SystemC wrapper
|
||||
file to instantiate the Verilated model. Executing the resulting executable
|
||||
performs the design simulation. Verilator also supports linking Verilated
|
||||
generated libraries, optionally encrypted, into other simulators.
|
||||
|
||||
Verilator may not be the best choice if you are expecting a full featured
|
||||
replacement for NC-Verilog, VCS or another commercial Verilog simulator, or
|
||||
if you are looking for a behavioral Verilog simulator e.g. for a quick
|
||||
class project (we recommend `Icarus Verilog`_ for this.) However, if you
|
||||
are looking for a path to migrate SystemVerilog to C++ or SystemC, or your
|
||||
team is comfortable writing just a touch of C++ code, Verilator is the tool
|
||||
for you.
|
||||
replacement for a closed-source Verilog simulator, need SDF annotation,
|
||||
mixed-signal simulation, or are doing a quick class project (we recommend
|
||||
`Icarus Verilog`_ for classwork.) However, if you are looking for a path
|
||||
to migrate SystemVerilog to C++/SystemC, or want high speed simulation of
|
||||
synthesizable designs containing limited verification constructs, Verilator
|
||||
is the tool for you.
|
||||
|
||||
|
||||
Performance
|
||||
===========
|
||||
|
||||
Verilator does not simply convert Verilog HDL to C++ or SystemC. Rather,
|
||||
Verilator does not directly translate Verilog HDL to C++ or SystemC. Rather,
|
||||
Verilator compiles your code into a much faster optimized and optionally
|
||||
thread-partitioned model, which is in turn wrapped inside a C++/SystemC
|
||||
module. The results are a compiled Verilog model that executes even on a
|
||||
@@ -84,9 +84,11 @@ multithreading (yielding 200-1000x total over interpreted simulators).
|
||||
|
||||
Verilator has typically similar or better performance versus the
|
||||
closed-source Verilog simulators (Carbon Design Systems Carbonator,
|
||||
Modelsim, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and Pragmatic
|
||||
CVer/CVC). But, Verilator is open-sourced, so you can spend on computes
|
||||
rather than licenses. Thus Verilator gives you the best cycles/dollar.
|
||||
Modelsim/Questa, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and
|
||||
Pragmatic CVer/CVC). But, Verilator is open-sourced, so you can spend on
|
||||
computes rather than licenses. Thus Verilator gives you the best
|
||||
cycles/dollar.
|
||||
|
||||
|
||||
Installation & Documentation
|
||||
============================
|
||||
|
||||
+44
-27
@@ -244,6 +244,7 @@ Verilator - Translate and simulate SystemVerilog code using C++/SystemC
|
||||
|
||||
verilator --help
|
||||
verilator --version
|
||||
verilator --binary -j 0 [options] [source_files.v]... [opt_c_files.cpp/c/cc/a/o/so]
|
||||
verilator --cc [options] [source_files.v]... [opt_c_files.cpp/c/cc/a/o/so]
|
||||
verilator --sc [options] [source_files.v]... [opt_c_files.cpp/c/cc/a/o/so]
|
||||
verilator --lint-only -Wall [source_files.v]...
|
||||
@@ -282,35 +283,43 @@ detailed descriptions of these arguments.
|
||||
--autoflush Flush streams after all $displays
|
||||
--bbox-sys Blackbox unknown $system calls
|
||||
--bbox-unsup Blackbox unsupported language features
|
||||
--bin <filename> Override Verilator binary
|
||||
--binary Build model binary
|
||||
--build Build model executable/library after Verilation
|
||||
-CFLAGS <flags> C++ compiler arguments for makefile
|
||||
--build-dep-bin <filename> Override build dependency Verilator binary
|
||||
--build-jobs <jobs> Parallelism for --build
|
||||
--cc Create C++ output
|
||||
--cdc Clock domain crossing analysis
|
||||
-CFLAGS <flags> C++ compiler arguments for makefile
|
||||
--clk <signal-name> Mark specified signal as clock
|
||||
--make <build-tool> Generate scripts for specified build tool
|
||||
--no-clk <signal-name> Prevent marking specified signal as clock
|
||||
--compiler <compiler-name> Tune for specified C++ compiler
|
||||
--converge-limit <loops> Tune convergence settle time
|
||||
--coverage Enable all coverage
|
||||
--coverage-line Enable line coverage
|
||||
--coverage-max-width <width> Maximum array depth for coverage
|
||||
--coverage-toggle Enable toggle coverage
|
||||
--coverage-user Enable SVL user coverage
|
||||
--coverage-underscore Enable coverage of _signals
|
||||
--coverage-user Enable SVL user coverage
|
||||
-D<var>[=<value>] Set preprocessor define
|
||||
--debug Enable debugging
|
||||
--debug-check Enable debugging assertions
|
||||
--no-debug-leak Disable leaking memory in --debug mode
|
||||
--debugi <level> Enable debugging at a specified level
|
||||
--debugi-<srcfile> <level> Enable debugging a source file at a level
|
||||
--no-decoration Disable comments and symbol decorations
|
||||
--default-language <lang> Default language to parse
|
||||
+define+<var>=<value> Set preprocessor define
|
||||
--dpi-hdr-only Only produce the DPI header file
|
||||
--dump-defines Show preprocessor defines with -E
|
||||
--dump-tree Enable dumping .tree files
|
||||
--dump-treei <level> Enable dumping .tree files at a level
|
||||
--dump-treei-<srcfile> <level> Enable dumping .tree file at a source file at a level
|
||||
--dump-dfg Enable dumping DfgGraphs to .dot files
|
||||
--dump-graph Enable dumping V3Graphs to .dot files
|
||||
--dump-tree Enable dumping Ast .tree files
|
||||
--dump-tree-addrids Use short identifiers instead of addresses
|
||||
--dump-<srcfile> Enable dumping everything in source file
|
||||
--dumpi-dfg <level> Enable dumping DfgGraphs to .dot files at level
|
||||
--dumpi-graph <level> Enable dumping V3Graphs to .dot files at level
|
||||
--dumpi-tree <level> Enable dumping Ast .tree files at level
|
||||
--dumpi-<srcfile> <level> Enable dumping everything in source file at level
|
||||
-E Preprocess, but do not compile
|
||||
--error-limit <value> Abort after this number of errors
|
||||
--exe Link to create executable
|
||||
@@ -319,62 +328,63 @@ detailed descriptions of these arguments.
|
||||
-f <file> Parse arguments from a file
|
||||
-FI <file> Force include of a file
|
||||
--flatten Force inlining of all modules, tasks and functions
|
||||
-fno-<optimization> Disable internal optimization stage
|
||||
-G<name>=<value> Overwrite top-level parameter
|
||||
--gate-stmts <value> Tune gate optimizer depth
|
||||
--gdb Run Verilator under GDB interactively
|
||||
--gdbbt Run Verilator under GDB for backtrace
|
||||
--generate-key Create random key for --protect-key
|
||||
--getenv <var> Get environment variable with defaults
|
||||
--get-supported <feature> Get if feature is supported
|
||||
--help Display this help
|
||||
--hierarchical Enable hierarchical Verilation
|
||||
-I<dir> Directory to search for includes
|
||||
-j <jobs> Parallelism for --build
|
||||
--gate-stmts <value> Tune gate optimizer depth
|
||||
--if-depth <value> Tune IFDEPTH warning
|
||||
+incdir+<dir> Directory to search for includes
|
||||
--inline-mult <value> Tune module inlining
|
||||
--instr-count-dpi <value> Assumed dynamic instruction count of DPI imports
|
||||
-LDFLAGS <flags> Linker pre-object arguments for makefile
|
||||
-j <jobs> Parallelism for --build (alias to --build-jobs)
|
||||
--l2-name <value> Verilog scope name of the top module
|
||||
--language <lang> Default language standard to parse
|
||||
-LDFLAGS <flags> Linker pre-object arguments for makefile
|
||||
--lib-create <name> Create a DPI library
|
||||
+libext+<ext>+[ext]... Extensions for finding modules
|
||||
--lint-only Lint, but do not make output
|
||||
--make <build-tool> Generate scripts for specified build tool
|
||||
-MAKEFLAGS <flags> Arguments to pass to make during --build
|
||||
--main Generate C++ main() file
|
||||
--max-num-width <value> Maximum number width (default: 64K)
|
||||
--MMD Create .d dependency files
|
||||
--MP Create phony dependency targets
|
||||
--Mdir <directory> Name of output object directory
|
||||
--no-merge-const-pool Disable merging of different types in const pool
|
||||
--MMD Create .d dependency files
|
||||
--mod-prefix <topname> Name to prepend to lower classes
|
||||
--no-clk <signal-name> Prevent marking specified signal as clock
|
||||
--no-decoration Disable comments and symbol decorations
|
||||
--no-pins64 Don't use vluint64_t's for 33-64 bit sigs
|
||||
--no-skip-identical Disable skipping identical output
|
||||
--MP Create phony dependency targets
|
||||
+notimingchecks Ignored
|
||||
-O0 Disable optimizations
|
||||
-O3 High performance optimizations
|
||||
-O<optimization-letter> Selectable optimizations
|
||||
-o <executable> Name of final executable
|
||||
--no-order-clock-delay Disable ordering clock enable assignments
|
||||
--no-verilate Skip verilation and just compile previously Verilated code.
|
||||
--no-verilate Skip Verilation, only compile previously Verilated code
|
||||
--output-split <statements> Split .cpp files into pieces
|
||||
--output-split-cfuncs <statements> Split model functions
|
||||
--output-split-ctrace <statements> Split tracing functions
|
||||
-P Disable line numbers and blanks with -E
|
||||
--pins-bv <bits> Specify types for top level ports
|
||||
--pins-sc-uint Specify types for top level ports
|
||||
--pins-sc-biguint Specify types for top level ports
|
||||
--pins-sc-uint Specify types for top level ports
|
||||
--pins-uint8 Specify types for top level ports
|
||||
--no-pins64 Don't use uint64_t's for 33-64 bit sigs
|
||||
--pipe-filter <command> Filter all input through a script
|
||||
--pp-comments Show preprocessor comments with -E
|
||||
--prefix <topname> Name of top level class
|
||||
--private Debugging; see docs
|
||||
--prof-c Compile C++ code with profiling
|
||||
--prof-cfuncs Name functions for profiling
|
||||
--prof-threads Enable generating gantt chart data for threads
|
||||
--protect-key <key> Key for symbol protection
|
||||
--prof-exec Enable generating execution profile for gantt chart
|
||||
--prof-pgo Enable generating profiling data for PGO
|
||||
--protect-ids Hash identifier names for obscurity
|
||||
--protect-key <key> Key for symbol protection
|
||||
--protect-lib <name> Create a DPI protected library
|
||||
--private Debugging; see docs
|
||||
--public Debugging; see docs
|
||||
--public-flat-rw Mark all variables, etc as public_flat_rw
|
||||
-pvalue+<name>=<value> Overwrite toplevel parameter
|
||||
@@ -385,6 +395,7 @@ detailed descriptions of these arguments.
|
||||
--rr Run Verilator and record with rr
|
||||
--savable Enable model save-restore
|
||||
--sc Create SystemC output
|
||||
--no-skip-identical Disable skipping identical output
|
||||
--stats Create statistics file
|
||||
--stats-vars Provide statistics on variables
|
||||
-sv Enable SystemVerilog parsing
|
||||
@@ -392,6 +403,8 @@ detailed descriptions of these arguments.
|
||||
--threads <threads> Enable multithreading
|
||||
--threads-dpi <mode> Enable multithreaded DPI
|
||||
--threads-max-mtasks <mtasks> Tune maximum mtask partitioning
|
||||
--timing Enable timing support
|
||||
--no-timing Disable timing support
|
||||
--timescale <timescale> Sets default timescale
|
||||
--timescale-override <timescale> Overrides all timescales
|
||||
--top <topname> Alias of --top-module
|
||||
@@ -404,7 +417,7 @@ detailed descriptions of these arguments.
|
||||
--trace-max-width <width> Maximum array depth for tracing
|
||||
--trace-params Enable tracing of parameters
|
||||
--trace-structs Enable tracing structure names
|
||||
--trace-threads <threads> Enable waveform creation on separate threads
|
||||
--trace-threads <threads> Enable FST waveform creation on separate threads
|
||||
--trace-underscore Enable tracing of _signals
|
||||
-U<var> Undefine preprocessor define
|
||||
--unroll-count <loops> Tune maximum loop iterations
|
||||
@@ -412,6 +425,7 @@ detailed descriptions of these arguments.
|
||||
--unused-regexp <regexp> Tune UNUSED lint signals
|
||||
-V Verbose version and config
|
||||
-v <filename> Verilog library
|
||||
--no-verilate Skip verilation and just compile previously Verilated code.
|
||||
+verilog1995ext+<ext> Synonym for +1364-1995ext+<ext>
|
||||
+verilog2001ext+<ext> Synonym for +1364-2001ext+<ext>
|
||||
--version Displays program version and exits
|
||||
@@ -426,6 +440,9 @@ detailed descriptions of these arguments.
|
||||
-Wno-lint Disable all lint warnings
|
||||
-Wno-style Disable all style warnings
|
||||
-Wpedantic Warn on compliance-test issues
|
||||
-Wwarn-<message> Enable specified warning message
|
||||
-Wwarn-lint Enable lint warning message
|
||||
-Wwarn-style Enable style warning message
|
||||
--x-assign <mode> Assign non-initial Xs to this value
|
||||
--x-initial <mode> Assign initial Xs to this value
|
||||
--x-initial-edge Enable initial X->0 and X->1 edge triggers
|
||||
@@ -445,10 +462,10 @@ description of these arguments.
|
||||
+verilator+error+limit+<value> Set error limit
|
||||
+verilator+help Display help
|
||||
+verilator+noassert Disable assert checking
|
||||
+verilator+prof+threads+file+<filename> Set profile filename
|
||||
+verilator+prof+threads+start+<value> Set profile starting point
|
||||
+verilator+prof+threads+window+<value> Set profile duration
|
||||
+verilator+prof+vlt+file+<filename> Set profile guided filename
|
||||
+verilator+prof+exec+file+<filename> Set execution profile filename
|
||||
+verilator+prof+exec+start+<value> Set execution profile starting point
|
||||
+verilator+prof+exec+window+<value> Set execution profile duration
|
||||
+verilator+prof+vlt+file+<filename> Set PGO profile filename
|
||||
+verilator+rand+reset+<value> Set random reset technique
|
||||
+verilator+seed+<value> Set random seed
|
||||
+verilator+V Verbose version and config
|
||||
|
||||
@@ -38,10 +38,10 @@ def diff_dir(a, b):
|
||||
|
||||
anyfile = False
|
||||
for base in sorted(files.keys()):
|
||||
if ('a' not in files[base]) or ('b' not in files[base]):
|
||||
continue
|
||||
a = files[base]['a']
|
||||
b = files[base]['b']
|
||||
if not a or not b:
|
||||
continue
|
||||
print("=" * 70)
|
||||
print("= %s <-> %s" % (a, b))
|
||||
diff_file(a, b)
|
||||
|
||||
+165
-144
@@ -9,7 +9,7 @@ import re
|
||||
import statistics
|
||||
# from pprint import pprint
|
||||
|
||||
Threads = collections.defaultdict(lambda: {})
|
||||
Threads = collections.defaultdict(lambda: collections.defaultdict(lambda: {}))
|
||||
Mtasks = collections.defaultdict(lambda: {})
|
||||
Evals = collections.defaultdict(lambda: {})
|
||||
EvalLoops = collections.defaultdict(lambda: {})
|
||||
@@ -30,12 +30,12 @@ def process(filename):
|
||||
|
||||
def read_data(filename):
|
||||
with open(filename) as fh:
|
||||
re_prof = re.compile(
|
||||
r'^VLPROF mtask\s(\d+)\sstart\s(\d+)\selapsed\s(\d+)\spredict_start\s(\d+)\spredict_cost\s(\d+)\scpu\s(\d+)\son thread (\d+)'
|
||||
)
|
||||
re_eval = re.compile(r'^VLPROF eval\sstart\s(\d+)\selapsed\s(\d+)')
|
||||
re_loop = re.compile(
|
||||
r'^VLPROF eval_loop\sstart\s(\d+)\selapsed\s(\d+)')
|
||||
re_thread = re.compile(r'^VLPROFTHREAD (\d+)$')
|
||||
re_record = re.compile(r'^VLPROFEXEC (\S+) (\d+)(.*)$')
|
||||
re_payload_mtaskBegin = re.compile(
|
||||
r'id (\d+) predictStart (\d+) cpu (\d+)')
|
||||
re_payload_mtaskEnd = re.compile(r'id (\d+) predictCost (\d+)')
|
||||
|
||||
re_arg1 = re.compile(r'VLPROF arg\s+(\S+)\+([0-9.]*)\s*')
|
||||
re_arg2 = re.compile(r'VLPROF arg\s+(\S+)\s+([0-9.]*)\s*$')
|
||||
re_stat = re.compile(r'VLPROF stat\s+(\S+)\s+([0-9.]+)')
|
||||
@@ -43,47 +43,60 @@ def read_data(filename):
|
||||
re_proc_cpu = re.compile(r'VLPROFPROC processor\s*:\s*(\d+)\s*$')
|
||||
re_proc_dat = re.compile(r'VLPROFPROC ([a-z_ ]+)\s*:\s*(.*)$')
|
||||
cpu = None
|
||||
thread = None
|
||||
|
||||
lastEvalBeginTick = None
|
||||
lastEvalLoopBeginTick = None
|
||||
|
||||
for line in fh:
|
||||
if re_prof.match(line):
|
||||
match = re_prof.match(line)
|
||||
mtask = int(match.group(1))
|
||||
start = int(match.group(2))
|
||||
elapsed_time = int(match.group(3))
|
||||
end = start + elapsed_time
|
||||
predict_start = int(match.group(4))
|
||||
predict_cost = int(match.group(5))
|
||||
cpu = int(match.group(6))
|
||||
thread = int(match.group(7))
|
||||
if start not in Threads[thread]:
|
||||
Threads[thread][start] = {}
|
||||
Threads[thread][start]['mtask'] = mtask
|
||||
Threads[thread][start]['end'] = end
|
||||
Threads[thread][start]['cpu'] = cpu
|
||||
Threads[thread][start]['predict_start'] = predict_start
|
||||
Threads[thread][start]['predict_cost'] = predict_cost
|
||||
|
||||
if 'elapsed' not in Mtasks[mtask]:
|
||||
Mtasks[mtask] = {'end': 0, 'elapsed': 0}
|
||||
Mtasks[mtask]['thread'] = thread
|
||||
Mtasks[mtask]['elapsed'] += elapsed_time
|
||||
Mtasks[mtask]['predict_start'] = predict_start
|
||||
Mtasks[mtask]['predict_cost'] = predict_cost
|
||||
Mtasks[mtask]['end'] = max(Mtasks[mtask]['end'], end)
|
||||
elif re_eval.match(line):
|
||||
match = re_eval.match(line)
|
||||
start = int(match.group(1))
|
||||
elapsed_time = int(match.group(2))
|
||||
Evals[start]['start'] = start
|
||||
Evals[start]['end'] = start + elapsed_time
|
||||
elif re_loop.match(line):
|
||||
match = re_loop.match(line)
|
||||
start = int(match.group(1))
|
||||
elapsed_time = int(match.group(2))
|
||||
EvalLoops[start]['start'] = start
|
||||
EvalLoops[start]['end'] = start + elapsed_time
|
||||
elif re.match(r'^VLPROFTHREAD', line):
|
||||
None # pylint: disable=pointless-statement
|
||||
recordMatch = re_record.match(line)
|
||||
if recordMatch:
|
||||
kind, tick, payload = recordMatch.groups()
|
||||
tick = int(tick)
|
||||
payload = payload.strip()
|
||||
if kind == "EVAL_BEGIN":
|
||||
Evals[tick]['start'] = tick
|
||||
lastEvalBeginTick = tick
|
||||
elif kind == "EVAL_END":
|
||||
Evals[lastEvalBeginTick]['end'] = tick
|
||||
lastEvalBeginTick = None
|
||||
elif kind == "EVAL_LOOP_BEGIN":
|
||||
EvalLoops[tick]['start'] = tick
|
||||
lastEvalLoopBeginTick = tick
|
||||
elif kind == "EVAL_LOOP_END":
|
||||
EvalLoops[lastEvalLoopBeginTick]['end'] = tick
|
||||
lastEvalLoopBeginTick = None
|
||||
elif kind == "MTASK_BEGIN":
|
||||
mtask, predict_start, ecpu = re_payload_mtaskBegin.match(
|
||||
payload).groups()
|
||||
mtask = int(mtask)
|
||||
predict_start = int(predict_start)
|
||||
ecpu = int(ecpu)
|
||||
Threads[thread][tick]['mtask'] = mtask
|
||||
Threads[thread][tick]['predict_start'] = predict_start
|
||||
Threads[thread][tick]['cpu'] = ecpu
|
||||
if 'elapsed' not in Mtasks[mtask]:
|
||||
Mtasks[mtask] = {'end': 0, 'elapsed': 0}
|
||||
Mtasks[mtask]['begin'] = tick
|
||||
Mtasks[mtask]['thread'] = thread
|
||||
Mtasks[mtask]['predict_start'] = predict_start
|
||||
elif kind == "MTASK_END":
|
||||
mtask, predict_cost = re_payload_mtaskEnd.match(
|
||||
payload).groups()
|
||||
mtask = int(mtask)
|
||||
predict_cost = int(predict_cost)
|
||||
begin = Mtasks[mtask]['begin']
|
||||
Threads[thread][begin]['end'] = tick
|
||||
Threads[thread][begin]['predict_cost'] = predict_cost
|
||||
Mtasks[mtask]['elapsed'] += tick - begin
|
||||
Mtasks[mtask]['predict_cost'] = predict_cost
|
||||
Mtasks[mtask]['end'] = max(Mtasks[mtask]['end'], tick)
|
||||
elif Args.debug:
|
||||
print("-Unknown execution trace record: %s" % line)
|
||||
elif re_thread.match(line):
|
||||
thread = int(re_thread.match(line).group(1))
|
||||
elif re.match(r'^VLPROF(THREAD|VERSION)', line):
|
||||
pass
|
||||
elif re_arg1.match(line):
|
||||
match = re_arg1.match(line)
|
||||
Global['args'][match.group(1)] = match.group(2)
|
||||
@@ -105,7 +118,7 @@ def read_data(filename):
|
||||
value = re.sub(r'\s+$', '', value)
|
||||
Global['cpuinfo'][cpu][term] = value
|
||||
elif re.match(r'^#', line):
|
||||
None # pylint: disable=pointless-statement
|
||||
pass
|
||||
elif Args.debug:
|
||||
print("-Unk: %s" % line)
|
||||
# TODO -- this is parsing text printed by a client.
|
||||
@@ -131,11 +144,13 @@ def report():
|
||||
plus = "+" if re.match(r'^\+', arg) else " "
|
||||
print(" %s%s%s" % (arg, plus, Global['args'][arg]))
|
||||
|
||||
nthreads = len(Threads)
|
||||
nthreads = int(Global['stats']['threads'])
|
||||
Global['cpus'] = {}
|
||||
for thread in Threads:
|
||||
# Make potentially multiple characters per column
|
||||
for start in Threads[thread]:
|
||||
if not Threads[thread][start]:
|
||||
continue
|
||||
cpu = Threads[thread][start]['cpu']
|
||||
elapsed = Threads[thread][start]['end'] - start
|
||||
if cpu not in Global['cpus']:
|
||||
@@ -169,74 +184,79 @@ def report():
|
||||
print("\nAnalysis:")
|
||||
print(" Total threads = %d" % nthreads)
|
||||
print(" Total mtasks = %d" % len(Mtasks))
|
||||
ncpus = len(Global['cpus'])
|
||||
ncpus = max(len(Global['cpus']), 1)
|
||||
print(" Total cpus used = %d" % ncpus)
|
||||
print(" Total yields = %d" % int(Global['stats']['yields']))
|
||||
print(" Total yields = %d" %
|
||||
int(Global['stats'].get('yields', 0)))
|
||||
print(" Total evals = %d" % len(Evals))
|
||||
print(" Total eval loops = %d" % len(EvalLoops))
|
||||
print(" Total eval time = %d rdtsc ticks" %
|
||||
Global['measured_last_end'])
|
||||
print(" Longest mtask time = %d rdtsc ticks" % long_mtask_time)
|
||||
print(" All-thread mtask time = %d rdtsc ticks" %
|
||||
measured_mt_mtask_time)
|
||||
long_efficiency = long_mtask_time / (Global.get('measured_last_end', 1)
|
||||
or 1)
|
||||
print(" Longest-thread efficiency = %0.1f%%" % (long_efficiency * 100.0))
|
||||
mt_efficiency = measured_mt_mtask_time / (
|
||||
Global.get('measured_last_end', 1) * nthreads or 1)
|
||||
print(" All-thread efficiency = %0.1f%%" % (mt_efficiency * 100.0))
|
||||
print(" All-thread speedup = %0.1f" % (mt_efficiency * nthreads))
|
||||
if Global['rdtsc_cycle_time'] > 0:
|
||||
ut = measured_mt_mtask_time / Global['rdtsc_cycle_time']
|
||||
print("tot_mtask_cpu=" + measured_mt_mtask_time + " cyc=" +
|
||||
Global['rdtsc_cycle_time'] + " ut=" + ut)
|
||||
if Mtasks:
|
||||
print(" Total eval time = %d rdtsc ticks" %
|
||||
Global['measured_last_end'])
|
||||
print(" Longest mtask time = %d rdtsc ticks" % long_mtask_time)
|
||||
print(" All-thread mtask time = %d rdtsc ticks" %
|
||||
measured_mt_mtask_time)
|
||||
long_efficiency = long_mtask_time / (Global.get(
|
||||
'measured_last_end', 1) or 1)
|
||||
print(" Longest-thread efficiency = %0.1f%%" %
|
||||
(long_efficiency * 100.0))
|
||||
mt_efficiency = measured_mt_mtask_time / (
|
||||
Global.get('measured_last_end', 1) * nthreads or 1)
|
||||
print(" All-thread efficiency = %0.1f%%" %
|
||||
(mt_efficiency * 100.0))
|
||||
print(" All-thread speedup = %0.1f" %
|
||||
(mt_efficiency * nthreads))
|
||||
if Global['rdtsc_cycle_time'] > 0:
|
||||
ut = measured_mt_mtask_time / Global['rdtsc_cycle_time']
|
||||
print("tot_mtask_cpu=" + measured_mt_mtask_time + " cyc=" +
|
||||
Global['rdtsc_cycle_time'] + " ut=" + ut)
|
||||
|
||||
predict_mt_efficiency = predict_mt_mtask_time / (
|
||||
Global.get('predict_last_end', 1) * nthreads or 1)
|
||||
print("\nPrediction (what Verilator used for scheduling):")
|
||||
print(" All-thread efficiency = %0.1f%%" %
|
||||
(predict_mt_efficiency * 100.0))
|
||||
print(" All-thread speedup = %0.1f" %
|
||||
(predict_mt_efficiency * nthreads))
|
||||
predict_mt_efficiency = predict_mt_mtask_time / (
|
||||
Global.get('predict_last_end', 1) * nthreads or 1)
|
||||
print("\nPrediction (what Verilator used for scheduling):")
|
||||
print(" All-thread efficiency = %0.1f%%" %
|
||||
(predict_mt_efficiency * 100.0))
|
||||
print(" All-thread speedup = %0.1f" %
|
||||
(predict_mt_efficiency * nthreads))
|
||||
|
||||
p2e_ratios = []
|
||||
min_p2e = 1000000
|
||||
min_mtask = None
|
||||
max_p2e = -1000000
|
||||
max_mtask = None
|
||||
p2e_ratios = []
|
||||
min_p2e = 1000000
|
||||
min_mtask = None
|
||||
max_p2e = -1000000
|
||||
max_mtask = None
|
||||
|
||||
for mtask in sorted(Mtasks.keys()):
|
||||
if Mtasks[mtask]['elapsed'] > 0:
|
||||
if Mtasks[mtask]['predict_cost'] == 0:
|
||||
Mtasks[mtask]['predict_cost'] = 1 # don't log(0) below
|
||||
p2e_ratio = math.log(Mtasks[mtask]['predict_cost'] /
|
||||
Mtasks[mtask]['elapsed'])
|
||||
p2e_ratios.append(p2e_ratio)
|
||||
for mtask in sorted(Mtasks.keys()):
|
||||
if Mtasks[mtask]['elapsed'] > 0:
|
||||
if Mtasks[mtask]['predict_cost'] == 0:
|
||||
Mtasks[mtask]['predict_cost'] = 1 # don't log(0) below
|
||||
p2e_ratio = math.log(Mtasks[mtask]['predict_cost'] /
|
||||
Mtasks[mtask]['elapsed'])
|
||||
p2e_ratios.append(p2e_ratio)
|
||||
|
||||
if p2e_ratio > max_p2e:
|
||||
max_p2e = p2e_ratio
|
||||
max_mtask = mtask
|
||||
if p2e_ratio < min_p2e:
|
||||
min_p2e = p2e_ratio
|
||||
min_mtask = mtask
|
||||
if p2e_ratio > max_p2e:
|
||||
max_p2e = p2e_ratio
|
||||
max_mtask = mtask
|
||||
if p2e_ratio < min_p2e:
|
||||
min_p2e = p2e_ratio
|
||||
min_mtask = mtask
|
||||
|
||||
print("\nStatistics:")
|
||||
print(" min log(p2e) = %0.3f" % min_p2e, end="")
|
||||
print(" from mtask %d (predict %d," %
|
||||
(min_mtask, Mtasks[min_mtask]['predict_cost']),
|
||||
end="")
|
||||
print(" elapsed %d)" % Mtasks[min_mtask]['elapsed'])
|
||||
print(" max log(p2e) = %0.3f" % max_p2e, end="")
|
||||
print(" from mtask %d (predict %d," %
|
||||
(max_mtask, Mtasks[max_mtask]['predict_cost']),
|
||||
end="")
|
||||
print(" elapsed %d)" % Mtasks[max_mtask]['elapsed'])
|
||||
print("\nMTask statistics:")
|
||||
print(" min log(p2e) = %0.3f" % min_p2e, end="")
|
||||
print(" from mtask %d (predict %d," %
|
||||
(min_mtask, Mtasks[min_mtask]['predict_cost']),
|
||||
end="")
|
||||
print(" elapsed %d)" % Mtasks[min_mtask]['elapsed'])
|
||||
print(" max log(p2e) = %0.3f" % max_p2e, end="")
|
||||
print(" from mtask %d (predict %d," %
|
||||
(max_mtask, Mtasks[max_mtask]['predict_cost']),
|
||||
end="")
|
||||
print(" elapsed %d)" % Mtasks[max_mtask]['elapsed'])
|
||||
|
||||
stddev = statistics.pstdev(p2e_ratios)
|
||||
mean = statistics.mean(p2e_ratios)
|
||||
print(" mean = %0.3f" % mean)
|
||||
print(" stddev = %0.3f" % stddev)
|
||||
print(" e ^ stddev = %0.3f" % math.exp(stddev))
|
||||
stddev = statistics.pstdev(p2e_ratios)
|
||||
mean = statistics.mean(p2e_ratios)
|
||||
print(" mean = %0.3f" % mean)
|
||||
print(" stddev = %0.3f" % stddev)
|
||||
print(" e ^ stddev = %0.3f" % math.exp(stddev))
|
||||
|
||||
report_cpus()
|
||||
|
||||
@@ -375,44 +395,45 @@ def write_vcd(filename):
|
||||
vcd['values'][eval_start][elcode] = n
|
||||
vcd['values'][eval_end][elcode] = None
|
||||
|
||||
# Predicted graph
|
||||
for eval_start in EvalLoops:
|
||||
eval_end = EvalLoops[eval_start]['end']
|
||||
# Compute scale so predicted graph is of same width as eval
|
||||
measured_scaling = (eval_end -
|
||||
eval_start) / Global['predict_last_end']
|
||||
# Predict mtasks that fill the time the eval occupied
|
||||
for mtask in Mtasks:
|
||||
thread = Mtasks[mtask]['thread']
|
||||
pred_scaled_start = eval_start + int(
|
||||
Mtasks[mtask]['predict_start'] * measured_scaling)
|
||||
pred_scaled_end = eval_start + int(
|
||||
(Mtasks[mtask]['predict_start'] +
|
||||
Mtasks[mtask]['predict_cost']) * measured_scaling)
|
||||
if pred_scaled_start == pred_scaled_end:
|
||||
continue
|
||||
if Mtasks:
|
||||
# Predicted graph
|
||||
for eval_start in EvalLoops:
|
||||
eval_end = EvalLoops[eval_start]['end']
|
||||
# Compute scale so predicted graph is of same width as eval
|
||||
measured_scaling = (eval_end -
|
||||
eval_start) / Global['predict_last_end']
|
||||
# Predict mtasks that fill the time the eval occupied
|
||||
for mtask in Mtasks:
|
||||
thread = Mtasks[mtask]['thread']
|
||||
pred_scaled_start = eval_start + int(
|
||||
Mtasks[mtask]['predict_start'] * measured_scaling)
|
||||
pred_scaled_end = eval_start + int(
|
||||
(Mtasks[mtask]['predict_start'] +
|
||||
Mtasks[mtask]['predict_cost']) * measured_scaling)
|
||||
if pred_scaled_start == pred_scaled_end:
|
||||
continue
|
||||
|
||||
sig = "predicted_thread%d_mtask" % thread
|
||||
if sig not in vcd['sigs']['predicted_threads']:
|
||||
vcd['sigs']['predicted_threads'][sig] = code
|
||||
code += 1
|
||||
mcode = vcd['sigs']['predicted_threads'][sig]
|
||||
sig = "predicted_thread%d_mtask" % thread
|
||||
if sig not in vcd['sigs']['predicted_threads']:
|
||||
vcd['sigs']['predicted_threads'][sig] = code
|
||||
code += 1
|
||||
mcode = vcd['sigs']['predicted_threads'][sig]
|
||||
|
||||
vcd['values'][pred_scaled_start][mcode] = mtask
|
||||
vcd['values'][pred_scaled_end][mcode] = None
|
||||
vcd['values'][pred_scaled_start][mcode] = mtask
|
||||
vcd['values'][pred_scaled_end][mcode] = None
|
||||
|
||||
parallelism['predicted'][pred_scaled_start] += 1
|
||||
parallelism['predicted'][pred_scaled_end] -= 1
|
||||
parallelism['predicted'][pred_scaled_start] += 1
|
||||
parallelism['predicted'][pred_scaled_end] -= 1
|
||||
|
||||
# Parallelism graph
|
||||
for measpred in ('measured', 'predicted'):
|
||||
vcd['sigs']['Stats']["%s_parallelism" % measpred] = code
|
||||
pcode = code
|
||||
code += 1
|
||||
value = 0
|
||||
for time in sorted(parallelism[measpred].keys()):
|
||||
value += parallelism[measpred][time]
|
||||
vcd['values'][time][pcode] = value
|
||||
# Parallelism graph
|
||||
for measpred in ('measured', 'predicted'):
|
||||
vcd['sigs']['Stats']["%s_parallelism" % measpred] = code
|
||||
pcode = code
|
||||
code += 1
|
||||
value = 0
|
||||
for time in sorted(parallelism[measpred].keys()):
|
||||
value += parallelism[measpred][time]
|
||||
vcd['values'][time][pcode] = value
|
||||
|
||||
# Create output file
|
||||
fh.write("$version Generated by verilator_gantt $end\n")
|
||||
@@ -476,10 +497,10 @@ parser.add_argument('--no-vcd',
|
||||
action='store_true')
|
||||
parser.add_argument('--vcd',
|
||||
help='filename for vcd outpue',
|
||||
default='profile_threads.vcd')
|
||||
default='profile_exec.vcd')
|
||||
parser.add_argument('filename',
|
||||
help='input profile_threads.dat filename to process',
|
||||
default='profile_threads.dat')
|
||||
help='input profile_exec.dat filename to process',
|
||||
default='profile_exec.dat')
|
||||
|
||||
Args = parser.parse_args()
|
||||
|
||||
|
||||
+11
-3
@@ -19,6 +19,10 @@ set -x
|
||||
|
||||
cd $(dirname "$0")/..
|
||||
|
||||
# Avoid occasional cpan failures "Issued certificate has expired."
|
||||
export PERL_LWP_SSL_VERIFY_HOSTNAME=0
|
||||
echo "check_certificate = off" >> ~/.wgetrc
|
||||
|
||||
fatal() {
|
||||
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
|
||||
}
|
||||
@@ -54,8 +58,12 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
|
||||
|
||||
if [ "$CI_OS_NAME" = "linux" ]; then
|
||||
sudo apt-get update
|
||||
sudo apt-get install libfl-dev libgoogle-perftools-dev ccache
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
|
||||
sudo apt-get install libfl-dev ccache
|
||||
if [ "$CI_RUNS_ON" != "ubuntu-22.04" ]; then
|
||||
# Some conflict of libunwind verison on 22.04, can live without it for now
|
||||
sudo apt-get install libgoogle-perftools-dev
|
||||
fi
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install libsystemc libsystemc-dev
|
||||
fi
|
||||
if [ "$COVERAGE" = 1 ]; then
|
||||
@@ -85,7 +93,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
|
||||
sudo apt-get update
|
||||
# libfl-dev needed for internal coverage's test runs
|
||||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
|
||||
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
|
||||
sudo apt-get install libsystemc-dev
|
||||
fi
|
||||
if [ "$CI_M32" = 1 ]; then
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:22.04
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive \
|
||||
|
||||
@@ -8,7 +8,7 @@ Verilator build. It uses the following parameters:
|
||||
|
||||
- Source revision (default: master)
|
||||
|
||||
- Compiler (GCC 9.3.0, clang 10.0.0, default: 9.3.0)
|
||||
- Compiler (GCC 10.3.0, clang 10.0.0, default: 10.3.0)
|
||||
|
||||
The container is published as ``verilator/verilator-buildenv`` on `docker
|
||||
hub
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:22.04
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive \
|
||||
|
||||
+46
-10
@@ -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
|
||||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.218 2022-01-17],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
# When releasing, also update header of Changes file
|
||||
# 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.002 2022-10-29],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
AC_CONFIG_HEADER(src/config_build.h)
|
||||
AC_CONFIG_HEADERS(src/config_build.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
|
||||
@@ -22,6 +22,8 @@ AC_MSG_RESULT([configuring for $PACKAGE_STRING])
|
||||
PACKAGE_VERSION_NUMBER=`AS_ECHO("$PACKAGE_VERSION") | sed 's/ .*//g'`
|
||||
AC_SUBST(PACKAGE_VERSION_NUMBER)
|
||||
AC_DEFINE_UNQUOTED([PACKAGE_VERSION_NUMBER_STRING],["$PACKAGE_VERSION_NUMBER"],[Package version as a number])
|
||||
VERILATOR_VERSION_INTEGER=`AS_ECHO("$PACKAGE_VERSION") | [sed 's/\([0-9]\)\.\([0-9][0-9][0-9]\) .*/\1\2000/g']`
|
||||
AC_SUBST(VERILATOR_VERSION_INTEGER)
|
||||
|
||||
# Ignore automake flags passed by Ubuntu builds
|
||||
AC_ARG_ENABLE([dependency-tracking],
|
||||
@@ -346,14 +348,18 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE)
|
||||
|
||||
# Flag to select newest language standard supported
|
||||
# Macros work such that first option that passes is the one we take
|
||||
# Currently enabled c++14 due to packaged SystemC dependency
|
||||
# c++14 is the newest that Verilator is regressed to support
|
||||
# Currently enable c++17/c++14 due to packaged SystemC dependency
|
||||
# c++17 is the newest that Verilator is regularly tested to support
|
||||
# c++11 is the oldest that Verilator supports
|
||||
# gnu is requried for Cygwin to compile verilated.h successfully
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
|
||||
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
|
||||
case "$(which lsb_release 2>&1 > /dev/null && lsb_release -d)" in
|
||||
*Arch*Linux* | *Ubuntu*22.04*)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
|
||||
;;
|
||||
esac
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
|
||||
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
|
||||
@@ -368,6 +374,36 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wlogical-op)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wthread-safety)
|
||||
AC_SUBST(CFG_CXXFLAGS_WEXTRA)
|
||||
|
||||
# Flags for coroutine support for dynamic scheduling
|
||||
_MY_CXX_CHECK_IFELSE(
|
||||
-fcoroutines-ts,
|
||||
[CFG_CXXFLAGS_COROUTINES="-fcoroutines-ts"],
|
||||
[CFG_CXXFLAGS_COROUTINES="-fcoroutines"])
|
||||
AC_SUBST(CFG_CXXFLAGS_COROUTINES)
|
||||
|
||||
# HAVE_COROUTINES
|
||||
# Check if coroutines are supported at all
|
||||
AC_MSG_CHECKING([whether coroutines are supported by $CXX])
|
||||
ACO_SAVE_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $CFG_CXXFLAGS_COROUTINES"
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([
|
||||
#ifdef __clang__
|
||||
#define __cpp_impl_coroutine 1
|
||||
#endif
|
||||
#include <coroutine>
|
||||
],[[]])],
|
||||
[_my_result=yes
|
||||
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])],
|
||||
[AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([#include <experimental/coroutine>],[[]])],
|
||||
[_my_result=yes
|
||||
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])],
|
||||
[_my_result=no])])
|
||||
AC_MSG_RESULT($_my_result)
|
||||
CXXFLAGS="$ACO_SAVE_CXXFLAGS"
|
||||
AC_SUBST(HAVE_COROUTINES)
|
||||
|
||||
# Flags for compiling Verilator internals including parser always
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-Qunused-arguments)
|
||||
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-faligned-new)
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
*.html
|
||||
*.pdf
|
||||
_build
|
||||
guide/spelling.txt
|
||||
|
||||
@@ -5,11 +5,15 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
||||
|
||||
Adrien Le Masle
|
||||
Ahmed El-Mahmoudy
|
||||
Aleksander Kiryk
|
||||
Alex Chadwick
|
||||
Àlex Torregrosa
|
||||
Aliaksei Chapyzhenka
|
||||
Ameya Vikram Singh
|
||||
Andreas Kuster
|
||||
Arkadiusz Kozdra
|
||||
Chris Randall
|
||||
Chuxuan Wang
|
||||
Conor McCullough
|
||||
Dan Petrisko
|
||||
Daniel Bates
|
||||
@@ -17,6 +21,7 @@ David Horton
|
||||
David Metz
|
||||
David Stanford
|
||||
David Turner
|
||||
Drew Ranck
|
||||
Drew Taussig
|
||||
Driss Hafdi
|
||||
Edgar E. Iglesias
|
||||
@@ -28,21 +33,27 @@ Geza Lore
|
||||
Gianfranco Costamagna
|
||||
Glen Gibb
|
||||
Graham Rushton
|
||||
Guokai Chen
|
||||
Gustav Svensk
|
||||
Harald Heckmann
|
||||
Howard Su
|
||||
Huang Rui
|
||||
Huanghuang Zhou
|
||||
HungMingWu
|
||||
HyungKi Jeong
|
||||
Iru Cai
|
||||
Ivan Vnučec
|
||||
Iztok Jeras
|
||||
Jake Merdich
|
||||
James Hanlon
|
||||
James Hutchinson
|
||||
James Pallister
|
||||
Jamey Hicks
|
||||
Jamie Iles
|
||||
Jan Van Winkel
|
||||
Jean Berniolles
|
||||
Jeremy Bennett
|
||||
Jiuyang Liu
|
||||
John Coiner
|
||||
John Demme
|
||||
Jonathan Drolet
|
||||
@@ -50,31 +61,39 @@ Josh Redford
|
||||
Julie Schwartz
|
||||
Julien Margetts
|
||||
Kaleb Barrett
|
||||
Kamil Rakoczy
|
||||
Kanad Kanhere
|
||||
Keith Colbert
|
||||
Kevin Kiningham
|
||||
Krzysztof Bieganski
|
||||
Kuba Ober
|
||||
Larry Doolittle
|
||||
Ludwig Rogiers
|
||||
Lukasz Dalek
|
||||
Maarten De Braekeleer
|
||||
Maciej Sobkowski
|
||||
Marcel Chang
|
||||
Marco Widmer
|
||||
Mariusz Glebocki
|
||||
Markus Krause
|
||||
Marlon James
|
||||
Marshal Qiao
|
||||
Martin Schmidt
|
||||
Martin Stadler
|
||||
Matthew Ballance
|
||||
Michael Killough
|
||||
Michaël Lefebvre
|
||||
Mike Popoloski
|
||||
Miodrag Milanović
|
||||
Mladen Slijepcevic
|
||||
Morten Borup Petersen
|
||||
Mostafa Gamal
|
||||
Nandu Raj
|
||||
Nathan Kohagen
|
||||
Nathan Myers
|
||||
Patrick Stewart
|
||||
Paul Wright
|
||||
Pawel Sagan
|
||||
Peter Horvath
|
||||
Peter Monsson
|
||||
Philipp Wagner
|
||||
@@ -83,8 +102,10 @@ Pieter Kapsenberg
|
||||
Piotr Binkowski
|
||||
Qingyao Sun
|
||||
Rafal Kapuscik
|
||||
Raynard Qiao
|
||||
Richard Myers
|
||||
Rupert Swarbrick
|
||||
Ryszard Rozak
|
||||
Samuel Riedel
|
||||
Sean Cross
|
||||
Sebastien Van Cauwenberghe
|
||||
@@ -98,15 +119,21 @@ Tobias Rosenkranz
|
||||
Tobias Wölfel
|
||||
Todd Strader
|
||||
Tomasz Gorochowik
|
||||
Topa Topino
|
||||
Tymoteusz Blazejczyk
|
||||
Udi Finkelstein
|
||||
Unai Martinez-Corral
|
||||
Varun Koyyalagunta
|
||||
Vassilis Papaefstathiou
|
||||
Veripool API Bot
|
||||
Victor Besyakov
|
||||
William D. Jones
|
||||
Wilson Snyder
|
||||
Xi Zhang
|
||||
Yoda Lee
|
||||
Yossi Nivin
|
||||
Yuri Victorovich
|
||||
Yutetsu TAKATSUKASA
|
||||
Yu-Sheng Lin
|
||||
Yves Mathieu
|
||||
Zhanglei Wang
|
||||
|
||||
@@ -35,6 +35,7 @@ default:
|
||||
# Intermediate rules
|
||||
|
||||
vl-extract: ../bin/verilator ../Changes
|
||||
ln -sf ../spelling.txt guide/spelling.txt
|
||||
mkdir -p _build/gen
|
||||
$(PYTHON3) bin/vl_sphinx_extract ../bin/verilator
|
||||
sed 's/`/\&96;/g' < ../Changes > _build/gen/Changes
|
||||
@@ -77,6 +78,7 @@ clean mostlyclean distclean maintainer-clean::
|
||||
rm -f *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs *.idx
|
||||
rm -f *.ev *.evs *.ov *.ovs *.cv *.cvs *.ma *.mas
|
||||
rm -f *.tex
|
||||
rm -f guide/spelling.txt
|
||||
|
||||
distclean maintainer-clean::
|
||||
rm -f *.info* *.1 *.html *.pdf $(INFOS)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
.. comment: generated by t_lint_didnotconverge_bad
|
||||
.. code-block::
|
||||
|
||||
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request
|
||||
-V{t#,#}+ Vt_lint_didnotconverge_bad___024root___change_request_1
|
||||
-V{t#,#} CHANGE: t/t_lint_didnotconverge_bad.v:14: a
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge
|
||||
-V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] a)
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.. comment: generated by t_lint_didnotconverge_nodbg_bad
|
||||
.. code-block::
|
||||
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Verilated model didn't converge
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.. comment: generated by t_lint_stmtdly_bad
|
||||
.. code-block::
|
||||
|
||||
%Warning-STMTDLY: example.v:1:8 Unsupported: Ignoring delay on this delayed statement.
|
||||
%Warning-STMTDLY: example.v:1:7 Ignoring delay on this statement due to --no-timing
|
||||
|
||||
@@ -13,6 +13,7 @@ from datetime import datetime
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('./_ext'))
|
||||
|
||||
import sphinx_rtd_theme # pylint: disable=wrong-import-position,
|
||||
|
||||
+41
-61
@@ -86,58 +86,20 @@ Connecting to C++
|
||||
In C++ output mode (:vlopt:`--cc`), the Verilator generated model class is a
|
||||
simple C++ class. The user must write a C++ wrapper and main loop for the
|
||||
simulation, which instantiates the model class, and link with the Verilated
|
||||
model. Here is a simple example:
|
||||
model.
|
||||
|
||||
.. code-block:: C++
|
||||
Refer to ``examples/make_tracing_c`` in the distribution for a detailed
|
||||
commented example.
|
||||
|
||||
#include <verilated.h> // Defines common routines
|
||||
#include <iostream> // Need std::cout
|
||||
#include "Vtop.h" // From Verilating "top.v"
|
||||
Top level IO signals are read and written as members of the model. You
|
||||
call the model's :code:`eval()` method to evaluate the model. When the
|
||||
simulation is complete call the model's :code:`final()` method to execute
|
||||
any SystemVerilog final blocks, and complete any assertions. If using
|
||||
:vlopt:`--timing`, there are two additional functions for checking if
|
||||
there are any events pending in the simulation due to delays, and for
|
||||
retrieving the simulation time of the next delayed event. See
|
||||
:ref:`Evaluation Loop`.
|
||||
|
||||
Vtop *top; // Instantiation of model
|
||||
|
||||
vluint64_t main_time = 0; // Current simulation time
|
||||
// This is a 64-bit integer to reduce wrap over issues and
|
||||
// allow modulus. This is in units of the timeprecision
|
||||
// used in Verilog (or from --timescale-override)
|
||||
|
||||
double sc_time_stamp() { // Called by $time in Verilog
|
||||
return main_time; // converts to double, to match
|
||||
// what SystemC does
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Verilated::commandArgs(argc, argv); // Remember args
|
||||
|
||||
top = new Vtop; // Create model
|
||||
|
||||
top->reset_l = 0; // Set some inputs
|
||||
|
||||
while (!Verilated::gotFinish()) {
|
||||
if (main_time > 10) {
|
||||
top->reset_l = 1; // Deassert reset
|
||||
}
|
||||
if ((main_time % 10) == 1) {
|
||||
top->clk = 1; // Toggle clock
|
||||
}
|
||||
if ((main_time % 10) == 6) {
|
||||
top->clk = 0;
|
||||
}
|
||||
top->eval(); // Evaluate model
|
||||
cout << top->out << endl; // Read a output
|
||||
main_time++; // Time passes...
|
||||
}
|
||||
|
||||
top->final(); // Done simulating
|
||||
// // (Though this example doesn't get here)
|
||||
delete top;
|
||||
}
|
||||
|
||||
|
||||
Note top level IO signals are read and written as members of the model. You
|
||||
call the :code:`eval()` method to evaluate the model. When the simulation is
|
||||
complete call the :code:`final()` method to execute any SystemVerilog final
|
||||
blocks, and complete any assertions. See :ref:`Evaluation Loop`.
|
||||
|
||||
|
||||
Connecting to SystemC
|
||||
@@ -150,7 +112,7 @@ netlist as an instantiation.
|
||||
The SC_MODULE gets the same pinout as the Verilog module, with the
|
||||
following type conversions: Pins of a single bit become bool. Pins 2-32
|
||||
bits wide become uint32_t's. Pins 33-64 bits wide become sc_bv's or
|
||||
vluint64_t's depending on the :vlopt:`--no-pins64` option. Wider pins
|
||||
uint64_t's depending on the :vlopt:`--no-pins64` option. Wider pins
|
||||
become sc_bv's. (Uints simulate the fastest so are used where possible.)
|
||||
|
||||
Model internals, including lower level sub-modules are not pure SystemC
|
||||
@@ -200,7 +162,7 @@ DPI System Task/Functions
|
||||
-------------------------
|
||||
|
||||
Verilator extends the DPI format to allow using the same scheme to
|
||||
efficiently add system functions. Simply use a dollar-sign prefixed system
|
||||
efficiently add system functions. Use a dollar-sign prefixed system
|
||||
function name for the import, but note it must be escaped.
|
||||
|
||||
.. code-block:: sv
|
||||
@@ -257,7 +219,9 @@ with respect to that top level module, then the scope could be set with
|
||||
|
||||
#include "svdpi.h"
|
||||
...
|
||||
svSetScope(svGetScopeFromName("TOP.dut"));
|
||||
const svScope scope = svGetScopeFromName("TOP.dut");
|
||||
assert(scope); // Check for nullptr if scope not found
|
||||
svSetScope(scope);
|
||||
|
||||
(Remember that Verilator adds a "TOP" to the top of the module hierarchy.)
|
||||
|
||||
@@ -422,12 +386,12 @@ accesses the above signal "readme" would be:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cat >sim_main.cpp <<'<<EOF'
|
||||
cat >sim_main.cpp <<'EOF'
|
||||
#include "Vour.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_vpi.h" // Required to get definitions
|
||||
|
||||
vluint64_t main_time = 0; // See comments in first example
|
||||
uint64_t main_time = 0; // See comments in first example
|
||||
double sc_time_stamp() { return main_time; }
|
||||
|
||||
void read_and_check() {
|
||||
@@ -444,14 +408,15 @@ accesses the above signal "readme" would be:
|
||||
|
||||
int main(int argc, char** argv, char** env) {
|
||||
Verilated::commandArgs(argc, argv);
|
||||
Vour* top = new Vour;
|
||||
Verilated::internalsDump(); // See scopes to help debug
|
||||
while (!Verilated::gotFinish()) {
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
const std::unique_ptr<Vour> top{new Vour{contextp.get()}};
|
||||
|
||||
contextp->internalsDump(); // See scopes to help debug
|
||||
while (!contextp->gotFinish()) {
|
||||
top->eval();
|
||||
VerilatedVpi::callValueCbs(); // For signal callbacks
|
||||
read_and_check();
|
||||
}
|
||||
delete top;
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
@@ -478,10 +443,25 @@ there is only a single design, you would call :code:`eval_step()` then
|
||||
:code:`eval_end_step()`; in fact :code:`eval()` described above is just a
|
||||
wrapper which calls these two functions.
|
||||
|
||||
3. If using delays and :vlopt:`--timing`, there are two additional methods
|
||||
the user should call:
|
||||
|
||||
* :code:`designp->eventsPending()`, which returns :code:`true` if there are
|
||||
any delayed events pending,
|
||||
* :code:`designp->nextTimeSlot()`, which returns the simulation time of the
|
||||
next delayed event. This method can only be called if
|
||||
:code:`designp->nextTimeSlot()` returned :code:`true`.
|
||||
|
||||
Call :code:`eventsPending()` to check if you should continue with the
|
||||
simulation, and then :code:`nextTimeSlot()` to move simulation time forward.
|
||||
:vlopt:`--main` can be used with :vlopt:`--timing` to generate a basic example
|
||||
of a timing-enabled eval loop.
|
||||
|
||||
When :code:`eval()` (or :code:`eval_step()`) is called Verilator looks for
|
||||
changes in clock signals and evaluates related sequential always blocks,
|
||||
such as computing always_ff @ (posedge...) outputs. Then Verilator
|
||||
evaluates combinatorial logic.
|
||||
such as computing always_ff @ (posedge...) outputs. With :vlopt:`--timing`, it
|
||||
resumes any delayed processes awaiting the current simulation time. Then
|
||||
Verilator evaluates combinational logic.
|
||||
|
||||
Note combinatorial logic is not computed before sequential always blocks
|
||||
are computed (for speed reasons). Therefore it is best to set any non-clock
|
||||
@@ -506,7 +486,7 @@ structure. If a ``VerilatedContext`` is not created prior to creating a
|
||||
model, a default global one is created automatically.
|
||||
|
||||
The ``Verilated::`` methods, including the ``Verilated::commandArgs`` call
|
||||
shown above, simply call VerilatedContext methods using the default global
|
||||
shown above, call VerilatedContext methods using the default global
|
||||
VerilatedContext. (Technically they operate on the last one used by a
|
||||
given thread.) If you are using multiple simulation contexts you should
|
||||
not use the Verilated:: methods, and instead always use VerilatedContext
|
||||
|
||||
+97
-81
@@ -23,99 +23,111 @@ Contributors
|
||||
Many people have provided ideas and other assistance with Verilator.
|
||||
|
||||
Verilator is receiving major development support from the `CHIPS Alliance
|
||||
<https://chipsalliance.org>`_.
|
||||
<https://chipsalliance.org>`_, `Antmicro Ltd <https://antmicro.com>`_ and
|
||||
`Shunyao CAD <https://shunyaocad.com>`_.
|
||||
|
||||
Previous major corporate sponsors of Verilator, by providing significant
|
||||
contributions of time or funds included include: Atmel Corporation, Cavium
|
||||
Inc., Compaq Corporation, Digital Equipment Corporation, Embecosm Ltd.,
|
||||
Hicamp Systems, Intel Corporation, Mindspeed Technologies Inc., MicroTune
|
||||
Inc., picoChip Designs Ltd., Sun Microsystems Inc., Nauticus Networks Inc.,
|
||||
and SiCortex Inc.
|
||||
SiCortex Inc, and Shunyao CAD.
|
||||
|
||||
The people who have contributed major functionality are: Byron Bradley,
|
||||
Jeremy Bennett, Lane Brooks, John Coiner, Duane Galbi, Geza Lore, Todd
|
||||
Strader, Stefan Wallentowitz, Paul Wasson, Jie Xu, and Wilson Snyder.
|
||||
Major testers included Jeff Dutton, Jonathon Donaldson, Ralf Karge, David
|
||||
Hewson, Iztok Jeras, Wim Michiels, Alex Solomatnikov, Sebastien Van
|
||||
Cauwenberghe, Gene Weber, and Clifford Wolf.
|
||||
The people who have contributed major functionality are: Krzysztof
|
||||
Bieganski, Byron Bradley, Jeremy Bennett, Lane Brooks, John Coiner, Duane
|
||||
Galbi, Geza Lore, Todd Strader, Stefan Wallentowitz, Paul Wasson, Jie Xu,
|
||||
and Wilson Snyder. Major testers included Jeff Dutton, Jonathon Donaldson,
|
||||
Ralf Karge, David Hewson, Iztok Jeras, Wim Michiels, Alex Solomatnikov,
|
||||
Sebastien Van Cauwenberghe, Gene Weber, and Clifford Wolf.
|
||||
|
||||
Some of the people who have provided ideas, and feedback for Verilator
|
||||
include: David Addison, Nikana Anastasiadis, Vasu Arasanipalai, Jens Arm,
|
||||
Tariq B. Ahmad, Sharad Bagri, Matthew Ballance, Andrew Bardsley, Matthew
|
||||
Barr, Geoff Barrett, Kaleb Barrett, Julius Baxter, Jeremy Bennett, Michael
|
||||
Berman, Jean Berniolles, Victor Besyakov, Moinak Bhattacharyya, Krzysztof
|
||||
Bieganski, David Binderman, Piotr Binkowski, Johan Bjork, David Black,
|
||||
Tymoteusz Blazejczyk, Daniel Bone, Morten Borup Petersen, Gregg Bouchard,
|
||||
Christopher Boumenot, Nick Bowler, Byron Bradley, Bryan Brady, Charlie
|
||||
Brej, J Briquet, Lane Brooks, John Brownlee, Jeff Bush, Lawrence Butcher,
|
||||
Tony Bybell, Iru Cai, Ted Campbell, Chris Candler, Lauren Carlson, Donal
|
||||
Casey, Alex Chadwick, Terry Chen, Yi-Chung Chen, Enzo Chi, Robert A. Clark,
|
||||
Ryan Clarke, Allan Cochrane, John Coiner, Gianfranco Costamagna, Sean
|
||||
Cross, George Cuan, Joe DErrico, Lukasz Dalek, Gunter Dannoritzer, Ashutosh
|
||||
Das, Maarten De Braekeleer, Bernard Deadman, Alberto Del Rio, John Demme,
|
||||
Mike Denio, John Deroo, Philip Derrick, John Dickol, Ruben Diez, Danny
|
||||
Ding, Jacko Dirks, Ivan Djordjevic, Jonathon Donaldson, Sebastian Dressler,
|
||||
Alex Duller, Jeff Dutton, Tomas Dzetkulic, Richard E George, Edgar
|
||||
E. Iglesias, Usuario Eda, Charles Eddleston, Chandan Egbert, Jan Egil Ruud,
|
||||
Joe Eiler, Ahmed El-Mahmoudy, Trevor Elbourne, Mats Engstrom, Charles Eric
|
||||
LaForest, Robert Farrell, Eugen Fekete, Fabrizio Ferrandi, Udi Finkelstein,
|
||||
Brian Flachs, Andrea Foletto, Bob Fredieu, Duane Galbi, Benjamin Gartner,
|
||||
Christian Gelinek, Peter Gerst, Glen Gibb, Michael Gielda, Barbara Gigerl,
|
||||
Shankar Giri, Dan Gisselquist, Petr Gladkikh, Sam Gladstone, Andrew
|
||||
Goessling, Amir Gonnen, Chitlesh Goorah, Tomasz Gorochowik, Kai Gossner,
|
||||
Sergi Granell, Al Grant, Alexander Grobman, Xuan Guo, Driss Hafdi, Neil
|
||||
include:
|
||||
|
||||
David Addison, Tariq B. Ahmad, Nikana Anastasiadis, Vasu Arasanipalai, Jens
|
||||
Arm, Sharad Bagri, Matthew Ballance, Andrew Bardsley, Matthew Barr, Geoff
|
||||
Barrett, Kaleb Barrett, Julius Baxter, Jeremy Bennett, Michael Berman, Jean
|
||||
Berniolles, Victor Besyakov, Moinak Bhattacharyya, Krzysztof Bieganski,
|
||||
David Binderman, Piotr Binkowski, Johan Bjork, David Black, Tymoteusz
|
||||
Blazejczyk, Daniel Bone, Gregg Bouchard, Christopher Boumenot, Nick Bowler,
|
||||
Byron Bradley, Bryan Brady, Maarten De Braekeleer, Charlie Brej, J Briquet,
|
||||
Lane Brooks, John Brownlee, Jeff Bush, Lawrence Butcher, Tony Bybell, Iru
|
||||
Cai, Ted Campbell, Chris Candler, Lauren Carlson, Donal Casey, Alex
|
||||
Chadwick, Marcel Chang, Aliaksei Chapyzhenka, Guokai Chen, Terry Chen,
|
||||
Yi-Chung Chen, Enzo Chi, Robert A. Clark, Ryan Clarke, Allan Cochrane, John
|
||||
Coiner, Keith Colbert, Gianfranco Costamagna, Sean Cross, George Cuan, Joe
|
||||
DErrico, Lukasz Dalek, Gunter Dannoritzer, Ashutosh Das, Bernard Deadman,
|
||||
John Demme, Mike Denio, John Deroo, Philip Derrick, John Dickol, Ruben
|
||||
Diez, Danny Ding, Jacko Dirks, Ivan Djordjevic, Jonathon Donaldson, Larry
|
||||
Doolittle, Sebastian Dressler, Jonathan Drolet, Alex Duller, Jeff Dutton,
|
||||
Tomas Dzetkulic, Usuario Eda, Charles Eddleston, Chandan Egbert, Joe Eiler,
|
||||
Ahmed El-Mahmoudy, Trevor Elbourne, Mats Engstrom, Robert Farrell, Eugen
|
||||
Fekete, Fabrizio Ferrandi, Udi Finkelstein, Brian Flachs, Bill Flynn,
|
||||
Andrea Foletto, Bob Fredieu, Duane Galbi, Mostafa Gamal, Benjamin Gartner,
|
||||
Christian Gelinek, Richard E George, Peter Gerst, Glen Gibb, Michael
|
||||
Gielda, Barbara Gigerl, Shankar Giri, Dan Gisselquist, Petr Gladkikh, Sam
|
||||
Gladstone, Mariusz Glebocki, Andrew Goessling, Amir Gonnen, Chitlesh
|
||||
Goorah, Tomasz Gorochowik, Kai Gossner, Sergi Granell, Al Grant, Nathan
|
||||
Graybeal, Alexander Grobman, Graham Rushton, Xuan Guo, Driss Hafdi, Neil
|
||||
Hamilton, James Hanlon, Oyvind Harboe, Jannis Harder, Junji Hashimoto,
|
||||
Thomas Hawkins, Mitch Hayenga, Harald Heckmann, Robert Henry, Stephen
|
||||
Henry, David Hewson, Jamey Hicks, Joel Holdsworth, Andrew Holme, Hiroki
|
||||
Honda, Alex Hornung, Pierre-Henri Horrein, David Horton, Peter Horvath, Jae
|
||||
Hossell, Kuoping Hsu, Alan Hunter, James Hutchinson, Anderson Ignacio da
|
||||
Silva, Jamie Iles, Thomas J Whatson, Ben Jackson, Mark Jackson Pulver,
|
||||
Shareef Jalloq, Marlon James, Krzysztof Jankowski, HyungKi Jeong, Iztok
|
||||
Jeras, James Johnson, Christophe Joly, Franck Jullien, James Jung, Mike
|
||||
Kagen, Arthur Kahlich, Kaalia Kahn, Guy-Armand Kamendje, Vasu Kandadi,
|
||||
Kanad Kanhere, Patricio Kaplan, Pieter Kapsenberg, Rafal Kapuscik, Ralf
|
||||
Karge, Dan Katz, Sol Katzman, Ian Kennedy, Michael Killough, Jonathan
|
||||
Kimmitt, Olof Kindgren, Kevin Kiningham, Dan Kirkham, Sobhan Klnv, Gernot
|
||||
Koch, Jack Koenig, Soon Koh, Nathan Kohagen, Steve Kolecki, Brett Koonce,
|
||||
Will Korteland, Wojciech Koszek, Varun Koyyalagunta, Markus Krause, David
|
||||
Kravitz, Roland Kruse, Andreas Kuster, Sergey Kvachonok, Ed Lander, Steve
|
||||
Lang, Stephane Laurent, Walter Lavino, Christian Leber, Larry Lee, Igor
|
||||
Lesik, John Li, Eivind Liland, Charlie Lind, Andrew Ling, Jiuyang Liu, Paul
|
||||
Liu, Derek Lockhart, Jake Longo, Geza Lore, Arthur Low, Stefan Ludwig, Dan
|
||||
Lussier, Fred Ma, Duraid Madina, Odd Magne Reitan, Affe Mao, Julien
|
||||
Margetts, Mark Marshall, Alfonso Martinez, Unai Martinez-Corral, Yves
|
||||
Mathieu, Patrick Maupin, Conor McCullough, Jason McMullan, Elliot Mednick,
|
||||
Wim Michiels, Miodrag Milanovic, Peter Monsson, Sean Moore, Dennis
|
||||
Muhlestein, John Murphy, Matt Myers, Nathan Myers, Richard Myers, Dimitris
|
||||
Nalbantis, Peter Nelson, Bob Newgard, Paul Nitza, Yossi Nivin, Pete Nixon,
|
||||
Lisa Noack, Mark Nodine, Kuba Ober, Andreas Olofsson, Baltazar Ortiz,
|
||||
Aleksander Osman, Don Owen, James Pallister, Vassilis Papaefstathiou, Brad
|
||||
Parker, Dan Petrisko, Maciej Piechotka, David Pierce, Cody Piersall,
|
||||
Dominic Plunkett, David Poole, Mike Popoloski, Roman Popov, Rich Porter,
|
||||
Niranjan Prabhu, Usha Priyadharshini, Prateek Puri, Marshal Qiao, Nandu
|
||||
Raj, Danilo Ramos, Chris Randall, Anton Rapp, Josh Redford, Frederic
|
||||
Requin, Dustin Richmond, Samuel Riedel, Eric Rippey, Oleg Rodionov, Ludwig
|
||||
Rogiers, Paul Rolfe, Arjen Roodselaar, Tobias Rosenkranz, Huang Rui, Denis
|
||||
Rystsov, John Sanguinetti, Galen Seitz, Joseph Shaker, Salman Sheikh, Yu
|
||||
Sheng Lin, Hao Shi, Mike Shinkarovsky, Rafael Shirakawa, Jeffrey Short, Fan
|
||||
Shupei, Rodney Sinclair, Steven Slatter, Brian Small, Garrett Smith, Tim
|
||||
Snyder, Wilson Snyder, Maciej Sobkowski, Stan Sokorac, Alex Solomatnikov,
|
||||
Wei Song, Art Stamness, David Stanford, John Stevenson, Pete Stevenson,
|
||||
Patrick Stewart, Rob Stoddard, Todd Strader, John Stroebel, Sven Stucki,
|
||||
Howard Su, Emerson Suguimoto, Gene Sullivan, Wai Sum Mong, Qingyao Sun,
|
||||
Renga Sundararajan, Rupert Swarbrick, Yutetsu Takatsukasa, Thierry Tambe,
|
||||
Drew Taussig, Peter Tengstrand, Wesley Terpstra, Rui Terra, Stefan Thiede,
|
||||
Gary Thomas, Ian Thompson, Kevin Thompson, Mike Thyer, Hans Tichelaar,
|
||||
Viktor Tomov, Steve Tong, Alex Torregrosa, Michael Tresidder, David Turner,
|
||||
Neil Turton, Cong Van Nguyen, Hans Van Antwerpen, Jan Van Winkel, Sebastien
|
||||
Van Cauwenberghe, Laurens van Dam, Leendert van Doorn, Srini Vemuri, Yuri
|
||||
Hossell, Kuoping Hsu, Teng Huang, Steven Hugg, Alan Hunter, James
|
||||
Hutchinson, Ehab Ibrahim, Edgar E. Iglesias, Jamie Iles, Vighnesh Iyer, Ben
|
||||
Jackson, Shareef Jalloq, Marlon James, Krzysztof Jankowski, HyungKi Jeong,
|
||||
Iztok Jeras, Alexandre Joannou, James Johnson, Christophe Joly, Franck
|
||||
Jullien, James Jung, Mike Kagen, Arthur Kahlich, Kaalia Kahn, Guy-Armand
|
||||
Kamendje, Vasu Kandadi, Kanad Kanhere, Patricio Kaplan, Pieter Kapsenberg,
|
||||
Rafal Kapuscik, Ralf Karge, Per Karlsson, Dan Katz, Sol Katzman, Ian
|
||||
Kennedy, Michael Killough, Sun Kim, Jonathan Kimmitt, Olof Kindgren, Kevin
|
||||
Kiningham, Dan Kirkham, Aleksander Kiryk, Sobhan Klnv, Gernot Koch, Jack
|
||||
Koenig, Soon Koh, Nathan Kohagen, Steve Kolecki, Brett Koonce, Will
|
||||
Korteland, Wojciech Koszek, Varun Koyyalagunta, Arkadiusz Kozdra, Markus
|
||||
Krause, David Kravitz, Roland Kruse, Andreas Kuster, Sergey Kvachonok,
|
||||
Charles Eric LaForest, Ed Lander, Steve Lang, Stephane Laurent, Walter
|
||||
Lavino, Christian Leber, Larry Lee, Yoda Lee, Michaël Lefebvre, Igor Lesik,
|
||||
John Li, Eivind Liland, Yu Sheng Lin, Charlie Lind, Andrew Ling, Jiuyang
|
||||
Liu, Paul Liu, Derek Lockhart, Jake Longo, Geza Lore, Arthur Low, Stefan
|
||||
Ludwig, Dan Lussier, Fred Ma, Duraid Madina, Affe Mao, Julien Margetts,
|
||||
Mark Marshall, Alfonso Martinez, Unai Martinez-Corral, Adrien Le Masle,
|
||||
Yves Mathieu, Patrick Maupin, Conor McCullough, Jason McMullan, Elliot
|
||||
Mednick, David Metz, Wim Michiels, Miodrag Milanovic, Kevin Millis, Wai Sum
|
||||
Mong, Peter Monsson, Sean Moore, Dennis Muhlestein, John Murphy, Matt
|
||||
Myers, Nathan Myers, Richard Myers, Dimitris Nalbantis, Peter Nelson, Bob
|
||||
Newgard, Rachit Nigam, Paul Nitza, Yossi Nivin, Pete Nixon, Lisa Noack,
|
||||
Mark Nodine, Kuba Ober, Andreas Olofsson, Baltazar Ortiz, Aleksander Osman,
|
||||
Don Owen, James Pallister, Vassilis Papaefstathiou, Brad Parker, Morten
|
||||
Borup Petersen, Dan Petrisko, Maciej Piechotka, David Pierce, Cody
|
||||
Piersall, Michael Platzer, Dominic Plunkett, David Poole, Mike Popoloski,
|
||||
Roman Popov, Rich Porter, Stefan Post, Niranjan Prabhu, Damien Pretet, Usha
|
||||
Priyadharshini, Mark Jackson Pulver, Prateek Puri, Marshal Qiao, Nandu Raj,
|
||||
Kamil Rakoczy, Danilo Ramos, Drew Ranck, Chris Randall, Anton Rapp, Josh
|
||||
Redford, Odd Magne Reitan, Frederic Requin, Dustin Richmond, Samuel Riedel,
|
||||
Alberto Del Rio, Eric Rippey, Oleg Rodionov, Ludwig Rogiers, Paul Rolfe,
|
||||
Arjen Roodselaar, Tobias Rosenkranz, Ryszard Rozak, Huang Rui, Graham
|
||||
Rushton, Jan Egil Ruud, Denis Rystsov, John Sanguinetti, Martin Schmidt,
|
||||
Julie Schwartz, Galen Seitz, Joseph Shaker, Salman Sheikh, Hao Shi, Mike
|
||||
Shinkarovsky, Rafael Shirakawa, Jeffrey Short, Fan Shupei, Anderson Ignacio
|
||||
da Silva, Rodney Sinclair, Ameya Vikram Singh, Steven Slatter, Mladen
|
||||
Slijepcevic, Brian Small, Garrett Smith, Tim Snyder, Wilson Snyder, Maciej
|
||||
Sobkowski, Stan Sokorac, Alex Solomatnikov, Flavien Solt, Wei Song, Trefor
|
||||
Southwell, Martin Stadler, Art Stamness, David Stanford, John Stevenson,
|
||||
Pete Stevenson, Patrick Stewart, Rob Stoddard, Todd Strader, John Stroebel,
|
||||
Sven Stucki, Howard Su, Emerson Suguimoto, Gene Sullivan, Qingyao Sun,
|
||||
Renga Sundararajan, Gustav Svensk, Rupert Swarbrick, Yutetsu Takatsukasa,
|
||||
Thierry Tambe, Drew Taussig, Jose Tejada, Peter Tengstrand, Wesley
|
||||
Terpstra, Rui Terra, Stefan Thiede, Gary Thomas, Ian Thompson, Kevin
|
||||
Thompson, Mike Thyer, Hans Tichelaar, Viktor Tomov, Steve Tong, Topa
|
||||
Topino, Alex Torregrosa, Michael Tresidder, David Turner, Neil Turton, Mike
|
||||
Urbach, Hans Van Antwerpen, Sebastien Van Cauwenberghe, Laurens van Dam,
|
||||
Leendert van Doorn, Cong Van Nguyen, Jan Van Winkel, Srini Vemuri, Yuri
|
||||
Victorovich, Bogdan Vukobratovic, Holger Waechtler, Philipp Wagner, Stefan
|
||||
Wallentowitz, Shawn Wang, Paul Wasson, Greg Waters, Thomas Watts, Eugene
|
||||
Weber, David Welch, Martin Whitaker, Marco Widmer, Leon Wildman, Daniel
|
||||
Wilkerson, Gerald Williams, Trevor Williams, Jeff Winston, Joshua Wise,
|
||||
Clifford Wolf, Tobias Wolfel, Johan Wouters, Paul Wright, Junyi Xi, Ding
|
||||
Xiaoliang, Jie Xu, Mandy Xu, Yinan Xu, Luke Yang, Amir Yazdanbakhsh, and
|
||||
Keyi Zhang.
|
||||
Wallentowitz, Shawn Wang, Zhanglei Wang, Paul Wasson, Greg Waters, Thomas
|
||||
Watts, Eugene Weber, David Welch, Thomas J Whatson, Martin Whitaker, Marco
|
||||
Widmer, Leon Wildman, Daniel Wilkerson, Gerald Williams, Trevor Williams,
|
||||
Jeff Winston, Joshua Wise, Clifford Wolf, Tobias Wolfel, Johan Wouters,
|
||||
Paul Wright, Junyi Xi, Ding Xiaoliang, Jie Xu, Mandy Xu, Yinan Xu, Luke
|
||||
Yang, Amir Yazdanbakhsh, Keyi Zhang, and Xi Zhang.
|
||||
|
||||
Thanks to them, and all those we've missed including above, or wished to
|
||||
remain anonymous.
|
||||
@@ -151,6 +163,10 @@ In 2018, Verilator 4.000 was released with multithreaded support.
|
||||
In 2019, Verilator joined the `CHIPS Alliance
|
||||
<https://chipsalliance.org>`_.
|
||||
|
||||
In 2022, Verilator 5.000 was released with IEEE scheduling semantics,
|
||||
fork/join, delay handling, DFG performance optimizations, and other
|
||||
improvements.
|
||||
|
||||
Currently, various language features and performance enhancements are added
|
||||
as the need arises. Verilator is now about 3x faster than in 2002, and is
|
||||
faster than most (if not every) other simulator.
|
||||
as the need arises, with a focus towards getting to full Universal
|
||||
Verification Methodology (UVM, IEEE 1800.2-2017) support.
|
||||
|
||||
+26
-12
@@ -7,23 +7,37 @@ Deprecations
|
||||
The following deprecated items are scheduled for future removal:
|
||||
|
||||
C++11 compiler support
|
||||
Verilator currently requires C++11 or newer compilers. Verilator will
|
||||
require C++14 or newer compilers for both compiling Verilator and
|
||||
compiling Verilated models no sooner than January 2023.
|
||||
Verilator currently requires a C++20 or newer compiler for timing, and a
|
||||
C++11 or newer compiler for no-timing.
|
||||
|
||||
Verilator will require C++14 or newer compilers for both compiling
|
||||
Verilator and compiling Verilated models with --no-timing no sooner than
|
||||
January 2023.
|
||||
|
||||
Verilator will require C++20 or newer compilers for both compiling
|
||||
Verilator and compiling all Verilated models no sooner than January 2025.
|
||||
|
||||
Verilated_heavy.h
|
||||
The legacy "verilated_heavy.h" include was replaced with just including
|
||||
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
|
||||
July 2022.
|
||||
|
||||
Configuration File -msg
|
||||
The :vlopt:`lint_off` "-msg" option has been replaced with the "-rule"
|
||||
option. "-msg" is planned for removal no sooner than January 2021.
|
||||
|
||||
XML locations
|
||||
The XML "fl" attribute has been replaced with the "loc" attribute. "fl"
|
||||
is planned for removal no sooner than January 2021.
|
||||
|
||||
Option `--cdc`
|
||||
The experimental `--cdc` option is believed to be generally unused and is
|
||||
planned for removeal no sooner than January 2023.
|
||||
planned for removal no sooner than January 2023.
|
||||
|
||||
Option `-O<letter>`
|
||||
The debug `-O<letter>` options have been replaced with
|
||||
`-fno-<optimization>` debug options to match GCC. The old options are
|
||||
planned for removal no sooner than June 2023.
|
||||
|
||||
Option `--prof-threads`
|
||||
The `--prof-threads` option has been superseded by the `--prof-exec` and
|
||||
`--prof-pgo` options and is planned for removal no sooner than April 2023.
|
||||
|
||||
Verilated model options `+verilator+prof+threads+*`
|
||||
The `+verilator+prof+threads+start`, `+verilator+prof+threads+window` and
|
||||
`+verilator+prof+threads+file` options have been superseded by the
|
||||
`+verilator+prof+exec+start`, `+verilator+prof+exec+window` and
|
||||
`+verilator+prof+exec+file` options respectively and are planned for removal
|
||||
no sooner than April 2023.
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
.. Copyright 2003-2022 by Wilson Snyder.
|
||||
.. SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
.. _Example Create-Binary Execution:
|
||||
|
||||
Example Create-Binary Execution
|
||||
===============================
|
||||
|
||||
We'll compile this SystemVerilog example into a Verilated simulation binary. For
|
||||
an example that discusses the next level of detail see :ref:`Example C++
|
||||
Execution`.
|
||||
|
||||
.. include:: example_common_install.rst
|
||||
|
||||
Now, let's create an example Verilog file:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir test_our
|
||||
cd test_our
|
||||
|
||||
cat >our.v <<'EOF'
|
||||
module our;
|
||||
initial begin $display("Hello World"); $finish; end
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
Now we run Verilator on our little example.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
verilator --binary -j 0 -Wall our.v
|
||||
|
||||
Breaking this command down:
|
||||
|
||||
#. :vlopt:`--binary` telling Verilator to do everything needed to create a
|
||||
simulation executable.
|
||||
|
||||
#. :vlopt:`-j` `0` to Verilate using use as many CPU threads as the machine
|
||||
has.
|
||||
|
||||
#. :vlopt:`-Wall` so Verilator has stronger lint warnings
|
||||
enabled.
|
||||
|
||||
#. An finally, :command:`our.v` which is our SystemVerilog design file.
|
||||
|
||||
And now we run it:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
obj_dir/Vour
|
||||
|
||||
And we get as output:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
Hello World
|
||||
- our.v:2: Verilog $finish
|
||||
|
||||
Really, you're better off using a Makefile to run the steps for you so when
|
||||
your source changes it will automatically run all of the appropriate steps.
|
||||
To aid this Verilator can create a makefile dependency file. For examples
|
||||
that do this see the :file:`examples` directory in the distribution.
|
||||
@@ -43,13 +43,10 @@ Now we run Verilator on our little example.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
verilator -Wall --cc --exe --build sim_main.cpp our.v
|
||||
verilator --cc --exe --build -j 0 -Wall sim_main.cpp our.v
|
||||
|
||||
Breaking this command down:
|
||||
|
||||
#. :vlopt:`-Wall` so Verilator has stronger lint warnings
|
||||
enabled.
|
||||
|
||||
#. :vlopt:`--cc` to get C++ output (versus e.g. SystemC
|
||||
or only linting).
|
||||
|
||||
@@ -61,6 +58,12 @@ Breaking this command down:
|
||||
own compile rules, and run make yourself as we show in :ref:`Example
|
||||
SystemC Execution`.)
|
||||
|
||||
#. :vlopt:`-j` `0' to Verilate using use as many CPU threads as the machine
|
||||
has.
|
||||
|
||||
#. :vlopt:`-Wall` so Verilator has stronger lint warnings
|
||||
enabled.
|
||||
|
||||
#. An finally, :command:`our.v` which is our SystemVerilog design file.
|
||||
|
||||
Once Verilator completes we can see the generated C++ code under the
|
||||
|
||||
@@ -10,6 +10,8 @@ See the ``examples/`` directory that is part of the distribution, and
|
||||
is installed (in a OS-specific place, often in e.g.
|
||||
``/usr/local/share/verilator/examples``). These examples include:
|
||||
|
||||
examples/make_hello_binary
|
||||
Example GNU-make simple Verilog->binary conversion
|
||||
examples/make_hello_c
|
||||
Example GNU-make simple Verilog->C++ conversion
|
||||
examples/make_hello_sc
|
||||
|
||||
@@ -43,7 +43,7 @@ Now we run Verilator on our little example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
verilator -Wall --sc --exe sc_main.cpp our.v
|
||||
verilator --sc --exe -Wall sc_main.cpp our.v
|
||||
|
||||
This example does not use --build, therefore we need to explicitly compile
|
||||
it:
|
||||
|
||||
@@ -17,6 +17,7 @@ This section covers the following examples:
|
||||
:maxdepth: 1
|
||||
:hidden:
|
||||
|
||||
example_binary.rst
|
||||
example_cc.rst
|
||||
example_sc.rst
|
||||
example_dist.rst
|
||||
|
||||
+23
-11
@@ -8,7 +8,7 @@ Simulation Runtime Arguments
|
||||
|
||||
The following are the arguments that may be passed to a Verilated
|
||||
executable, provided that executable calls
|
||||
:code:`Verilated::commandArgs()`.
|
||||
:code:`VerilatedContext*->commandArgs(argc, argv)`.
|
||||
|
||||
All simulation runtime arguments begin with "+verilator", so that the
|
||||
user's executable may skip over all "+verilator" arguments when parsing its
|
||||
@@ -38,33 +38,45 @@ Summary:
|
||||
|
||||
Display help and exit.
|
||||
|
||||
.. option:: +verilator+prof+threads+file+<filename>
|
||||
.. option:: +verilator+prof+exec+file+<filename>
|
||||
|
||||
When a model was Verilated using :vlopt:`--prof-threads`, sets the
|
||||
When a model was Verilated using :vlopt:`--prof-exec`, sets the
|
||||
simulation runtime filename to dump to. Defaults to
|
||||
:file:`profile_threads.dat`.
|
||||
:file:`profile_exec.dat`.
|
||||
|
||||
.. option:: +verilator+prof+threads+start+<value>
|
||||
.. option:: +verilator+prof+exec+start+<value>
|
||||
|
||||
When a model was Verilated using :vlopt:`--prof-threads`, the simulation
|
||||
When a model was Verilated using :vlopt:`--prof-exec`, the simulation
|
||||
runtime will wait until $time is at this value (expressed in units of
|
||||
the time precision), then start the profiling warmup, then
|
||||
capturing. Generally this should be set to some time that is well within
|
||||
the normal operation of the simulation, i.e. outside of reset. If 0, the
|
||||
dump is disabled. Defaults to 1.
|
||||
|
||||
.. option:: +verilator+prof+threads+window+<value>
|
||||
.. option:: +verilator+prof+exec+window+<value>
|
||||
|
||||
When a model was Verilated using :vlopt:`--prof-threads`, after $time
|
||||
reaches :vlopt:`+verilator+prof+threads+start+\<value\>`, Verilator will
|
||||
When a model was Verilated using :vlopt:`--prof-exec`, after $time
|
||||
reaches :vlopt:`+verilator+prof+exec+start+\<value\>`, Verilator will
|
||||
warm up the profiling for this number of eval() calls, then will capture
|
||||
the profiling of this number of eval() calls. Defaults to 2, which
|
||||
makes sense for a single-clock-domain module where it's typical to want
|
||||
to capture one posedge eval() and one negedge eval().
|
||||
|
||||
.. option:: +verilator+prof+threads+file+<filename>
|
||||
|
||||
Deprecated. Alias for :vlopt:`+verilator+prof+exec+file+\<filename\>`
|
||||
|
||||
.. option:: +verilator+prof+threads+start+<value>
|
||||
|
||||
Deprecated. Alias for :vlopt:`+verilator+prof+exec+start+\<value\>`
|
||||
|
||||
.. option:: +verilator+prof+threads+window+<value>
|
||||
|
||||
Deprecated. Alias for :vlopt:`+verilator+prof+exec+window+\<value\>`
|
||||
|
||||
.. option:: +verilator+prof+vlt+file+<filename>
|
||||
|
||||
When a model was Verilated using :vlopt:`--prof-threads`, sets the
|
||||
When a model was Verilated using :vlopt:`--prof-pgo`, sets the
|
||||
profile-guided optimization data runtime filename to dump to. Defaults
|
||||
to :file:`profile.vlt`.
|
||||
|
||||
@@ -84,7 +96,7 @@ Summary:
|
||||
.. option:: +verilator+noassert
|
||||
|
||||
Disable assert checking per runtime argument. This is the same as
|
||||
calling :code:`Verilated::assertOn(false)` in the model.
|
||||
calling :code:`VerilatedContext*->assertOn(false)` in the model.
|
||||
|
||||
.. option:: +verilator+V
|
||||
|
||||
|
||||
+388
-152
@@ -99,10 +99,10 @@ Summary:
|
||||
.. option:: --bbox-sys
|
||||
|
||||
Black box any unknown $system task or function calls. System tasks will
|
||||
simply become no-operations, and system functions will be replaced with
|
||||
unsized zero. Arguments to such functions will be parsed, but not
|
||||
otherwise checked. This prevents errors when linting in the presence of
|
||||
company specific PLI calls.
|
||||
become no-operations, and system functions will be replaced with unsized
|
||||
zero. Arguments to such functions will be parsed, but not otherwise
|
||||
checked. This prevents errors when linting in the presence of company
|
||||
specific PLI calls.
|
||||
|
||||
Using this argument will likely cause incorrect simulation.
|
||||
|
||||
@@ -115,12 +115,12 @@ Summary:
|
||||
|
||||
Using this argument will likely cause incorrect simulation.
|
||||
|
||||
.. option:: --bin <filename>
|
||||
.. option:: --binary
|
||||
|
||||
Rarely needed. Override the default filename for Verilator itself.
|
||||
When a dependency (.d) file is created, this filename will become a
|
||||
source dependency, such that a change in this binary will have make
|
||||
rebuild the output files.
|
||||
Create a Verilated simulator binary. Alias for :vlopt:`--main`
|
||||
:vlopt:`--exe` :vlopt:`--build` :vlopt:`--timing`.
|
||||
|
||||
See also :vlopt:`-j`.
|
||||
|
||||
.. option:: --build
|
||||
|
||||
@@ -129,15 +129,23 @@ Summary:
|
||||
is also used). Verilator manages the build itself, and for this --build
|
||||
requires GNU Make to be available on the platform.
|
||||
|
||||
.. option:: -CFLAGS <flags>
|
||||
.. option:: --build-dep-bin <filename>
|
||||
|
||||
Add specified C compiler argument to the generated makefiles. For
|
||||
multiple flags either pass them as a single argument with space
|
||||
separators quoted in the shell (:command:`-CFLAGS "-a -b"`), or use
|
||||
multiple -CFLAGS options (:command:`-CFLAGS -a -CFLAGS -b`).
|
||||
Rarely needed. When a dependency (.d) file is created, this filename
|
||||
will become a source dependency, such that a change in this binary will
|
||||
have make rebuild the output files. Defaults to the full path to the
|
||||
Verilator binary.
|
||||
|
||||
When make is run on the generated makefile these will be passed to the
|
||||
C++ compiler (g++/clang++/msvc++).
|
||||
This option was named `--bin` prior to version 4.228.
|
||||
|
||||
.. option:: --build-jobs [<value>]
|
||||
|
||||
Specify the level of parallelism for :vlopt:`--build`. If zero, uses the
|
||||
number of threads in the current hardware. Otherwise, the <value> must
|
||||
be a positive integer specifying the maximum number of parallel build
|
||||
jobs.
|
||||
|
||||
See also :vlopt:`-j`.
|
||||
|
||||
.. option:: --cc
|
||||
|
||||
@@ -156,12 +164,19 @@ Summary:
|
||||
have interest in adding more traditional CDC checks, please contact the
|
||||
authors.
|
||||
|
||||
.. option:: -CFLAGS <flags>
|
||||
|
||||
Add specified C compiler argument to the generated makefiles. For
|
||||
multiple flags either pass them as a single argument with space
|
||||
separators quoted in the shell (:command:`-CFLAGS "-a -b"`), or use
|
||||
multiple -CFLAGS options (:command:`-CFLAGS -a -CFLAGS -b`).
|
||||
|
||||
When make is run on the generated makefile these will be passed to the
|
||||
C++ compiler (g++/clang++/msvc++).
|
||||
|
||||
.. option:: --clk <signal-name>
|
||||
|
||||
With :vlopt:`--clk`, the specified signal-name is taken as a root clock
|
||||
into the model; Verilator will mark the signal as clocker and
|
||||
propagate the clocker attribute automatically to other signals downstream in
|
||||
that clock tree.
|
||||
With :vlopt:`--clk`, the specified signal is marked as a clock signal.
|
||||
|
||||
The provided signal-name is specified using a RTL hierarchy path. For
|
||||
example, v.foo.bar. If the signal is the input to top-module, then
|
||||
@@ -173,11 +188,16 @@ Summary:
|
||||
individual bits, Verilator will attempt to decompose the vector and
|
||||
connect the single-bit clock signals.
|
||||
|
||||
The clocker attribute is useful in cases where Verilator does not
|
||||
properly distinguish clock signals from other data signals. Using
|
||||
clocker will cause the signal indicated to be considered a clock, and
|
||||
remove it from the combinatorial logic reevaluation checking code. This
|
||||
may greatly improve performance.
|
||||
In versions prior to 5.000, the clocker attribute is useful in cases where
|
||||
Verilator does not properly distinguish clock signals from other data
|
||||
signals. Using clocker will cause the signal indicated to be considered a
|
||||
clock, and remove it from the combinatorial logic reevaluation checking
|
||||
code. This may greatly improve performance.
|
||||
|
||||
.. option:: --no-clk <signal-name>
|
||||
|
||||
Prevent the specified signal from being marked as clock. See
|
||||
:vlopt:`--clk`.
|
||||
|
||||
.. option:: --compiler <compiler-name>
|
||||
|
||||
@@ -250,8 +270,8 @@ Summary:
|
||||
generally is a less-optimized binary with symbols present (so GDB can be used on it).
|
||||
* Enable debugging messages (equivalent to :vlopt:`--debugi 3 <--debugi>`).
|
||||
* Enable internal assertions (equivalent to :vlopt:`--debug-check`).
|
||||
* Enable intermediate form dump files (equivalent to :vlopt:`--dump-treei 3
|
||||
<--dump-treei>`).
|
||||
* Enable intermediate form dump files (equivalent to :vlopt:`--dumpi-tree 3
|
||||
<--dumpi-tree>`).
|
||||
* Leak to make node numbers unique (equivalent to :vlopt:`--debug-leak
|
||||
<--no-debug-leak>`.
|
||||
* Call abort() instead of exit() if there are any errors (so GDB can see
|
||||
@@ -288,6 +308,13 @@ Summary:
|
||||
<--debugi>`). Higher levels produce more detailed messages. See
|
||||
:vlopt:`--debug` for other implications of enabling debug.
|
||||
|
||||
.. option:: --no-decoration
|
||||
|
||||
When creating output Verilated code, minimize comments, white space,
|
||||
symbol names and other decorative items, at the cost of greatly reduced
|
||||
readability. This may assist C++ compile times. This will not typically
|
||||
change the ultimate model's performance, but may in some cases.
|
||||
|
||||
.. option:: --default-language <value>
|
||||
|
||||
Select the language to be used by default when first processing each
|
||||
@@ -335,25 +362,31 @@ Summary:
|
||||
|
||||
touch foo.v ; verilator -E --dump-defines foo.v
|
||||
|
||||
.. option:: --dump-dfg
|
||||
|
||||
Rarely needed. Enable dumping DfgGraph .dot debug files with dumping
|
||||
level 3.
|
||||
|
||||
.. option:: --dump-graph
|
||||
|
||||
Rarely needed. Enable dumping V3Graph .dot debug files with dumping
|
||||
level 3. Before Verilator 4.228, :vlopt:`--dump-tree` used
|
||||
to include this option.
|
||||
|
||||
.. option:: --dump-tree
|
||||
|
||||
Rarely needed. Enable writing .tree debug files with dumping level 3,
|
||||
Rarely needed. Enable dumping Ast .tree debug files with dumping level 3,
|
||||
which dumps the standard critical stages. For details on the format see
|
||||
the Verilator Internals manual. :vlopt:`--dump-tree` is enabled
|
||||
automatically with :vlopt:`--debug`, so :vlopt:`--debug --no-dump-tree
|
||||
<--dump-tree>` may be useful if the dump files are large and not
|
||||
desired.
|
||||
|
||||
.. option:: --dump-treei <level>
|
||||
.. option:: --dump-tree-dot
|
||||
|
||||
.. option:: --dump-treei-<srcfile> <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal tree dumping level
|
||||
globally to a specific dumping level or set the specified Verilator
|
||||
source file to the specified tree dumping level (e.g.
|
||||
:vlopt:`--dump-treei-V3Order 9 <--dump-treei>`). Level 0 disables dumps
|
||||
and is equivalent to :vlopt:`--no-dump-tree <--dump-tree>`. Level 9
|
||||
enables dumping of every stage.
|
||||
Rarely needed. Enable dumping Ast .tree.dot debug files in Graphviz
|
||||
Dot format. This option implies :vlopt:`--dump-tree`, unless
|
||||
:vlopt:`--dumpi-tree` was passed explicitly.
|
||||
|
||||
.. option:: --dump-tree-addrids
|
||||
|
||||
@@ -364,6 +397,33 @@ Summary:
|
||||
by a newly allocated node after a node with the same address has been
|
||||
dumped then freed.
|
||||
|
||||
.. option:: --dump-<srcfile>
|
||||
|
||||
Rarely needed - for developer use. Enable all dumping in the given
|
||||
source file at level 3.
|
||||
|
||||
.. option:: --dumpi-dfg <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal DfgGraph dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-graph <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal V3Graph dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-tree <level>
|
||||
|
||||
Rarely needed - for developer use. Set internal Ast dumping level
|
||||
globally to the specified value.
|
||||
|
||||
.. option:: --dumpi-<srcfile> <level>
|
||||
|
||||
Rarely needed - for developer use. Set the dumping level in the
|
||||
specified Verilator source file to the specified value (e.g.
|
||||
`--dumpi-V3Order 9`). Level 0 disables dumps and is equivalent to
|
||||
`--no-dump-<srcfile>`. Level 9 enables dumping of everything.
|
||||
|
||||
.. option:: -E
|
||||
|
||||
Preprocess the source code, but do not compile, similar to C++
|
||||
@@ -431,6 +491,105 @@ Summary:
|
||||
flattening large designs may require significant CPU time, memory and
|
||||
storage.
|
||||
|
||||
.. option:: -fno-acyc-simp
|
||||
|
||||
.. option:: -fno-assemble
|
||||
|
||||
.. option:: -fno-case
|
||||
|
||||
.. option:: -fno-combine
|
||||
|
||||
.. option:: -fno-const
|
||||
|
||||
.. options: -fno-const-before-dfg
|
||||
|
||||
Do not apply any global expression folding prior to the DFG pass. This
|
||||
option is solely for the purpose of DFG testing and should not be used
|
||||
otherwise.
|
||||
|
||||
.. option:: -fno-const-bit-op-tree
|
||||
|
||||
.. option:: -fno-dedup
|
||||
|
||||
.. option:: -fno-dfg
|
||||
|
||||
Disable all use of the DFG based combinational logic optimizer.
|
||||
Alias for :vlopt:`-fno-dfg-pre-inline` and :vlopt:`-fno-dfg-post-inline`.
|
||||
|
||||
.. option:: -fno-dfg-peephole
|
||||
|
||||
Disable the DFG peephole optimizer.
|
||||
|
||||
.. option:: -fno-dfg-peephole-<pattern>
|
||||
|
||||
Disable individual DFG peephole optimizer pattern.
|
||||
|
||||
.. option:: -fno-dfg-pre-inline
|
||||
|
||||
Do not apply the DFG optimizer before inlining.
|
||||
|
||||
.. option:: -fno-dfg-post-inline
|
||||
|
||||
Do not apply the DFG optimizer after inlining.
|
||||
|
||||
.. option:: -fno-expand
|
||||
|
||||
.. option:: -fno-gate
|
||||
|
||||
.. option:: -fno-inline
|
||||
|
||||
.. option:: -fno-life
|
||||
|
||||
.. option:: -fno-life-post
|
||||
|
||||
.. option:: -fno-localize
|
||||
|
||||
.. option:: -fno-merge-cond
|
||||
|
||||
.. option:: -fno-merge-cond-motion
|
||||
|
||||
.. option:: -fno-merge-const-pool
|
||||
|
||||
.. option:: -fno-reloop
|
||||
|
||||
.. option:: -fno-reorder
|
||||
|
||||
.. option:: -fno-split
|
||||
|
||||
.. option:: -fno-subst
|
||||
|
||||
.. option:: -fno-subst-const
|
||||
|
||||
.. option:: -fno-table
|
||||
|
||||
Rarely needed. Disables one of the internal optimization steps. These
|
||||
are typically used only when recommended by a maintainer to help debug
|
||||
or work around an issue.
|
||||
|
||||
.. option:: -future0 <option>
|
||||
|
||||
Rarely needed. Suppress an unknown Verilator option for an option that
|
||||
takes no additional arguments. This is used to allow scripts written
|
||||
with pragmas for a later version of Verilator to run under a older
|
||||
version. e.g. :code:`-future0 option --option` would on older versions
|
||||
that do not understand :code:`--option` or :code:`+option` suppress what
|
||||
would otherwise be an invalid option error, and on newer versions that
|
||||
implement :code:`--option`, :code:`-future0 option --option` would have
|
||||
the :code:`-future0 option` ignored and the :code:`--option` would
|
||||
function appropriately.
|
||||
|
||||
.. option:: -future1 <option>
|
||||
|
||||
Rarely needed. Suppress an unknown Verilator option for an option that
|
||||
takes an additional argument. This is used to allow scripts written
|
||||
with pragmas for a later version of Verilator to run under a older
|
||||
version. e.g. :code:`-future1 option --option arg` would on older
|
||||
versions that do not understand :code:`--option arg` or :code:`+option
|
||||
arg` suppress what would otherwise be an invalid option error, and on
|
||||
newer versions that implement :code:`--option arg`, :code:`-future1
|
||||
option --option arg` would have the :code:`-future1 option` ignored and
|
||||
the :code:`--option arg` would function appropriately.
|
||||
|
||||
.. option:: -G<name>=<value>
|
||||
|
||||
Overwrites the given parameter of the toplevel module. The value is
|
||||
@@ -489,6 +648,15 @@ Summary:
|
||||
a newline and exit immediately. This can be useful in makefiles. See
|
||||
also :vlopt:`-V`, and the various :file:`*.mk` files.
|
||||
|
||||
.. option:: --get-supported <feature>
|
||||
|
||||
If the given feature is supported, print "1" and exit
|
||||
immediately. Otherwise, print a newline and exit immediately. This can
|
||||
be useful in makefiles. See also :vlopt:`-V`, and the various
|
||||
:file:`*.mk` files.
|
||||
|
||||
Feature may be one of the following: COROUTINES, SYSTEMC.
|
||||
|
||||
.. option:: --help
|
||||
|
||||
Displays this message and program version and exits.
|
||||
@@ -531,26 +699,10 @@ Summary:
|
||||
|
||||
.. option:: -j [<value>]
|
||||
|
||||
Specify the level of parallelism for :vlopt:`--build`. The <value> must
|
||||
be a positive integer specifying the maximum number of parallel build
|
||||
jobs, or can be omitted. When <value> is omitted, the build will not try
|
||||
to limit the number of parallel build jobs but attempt to execute all
|
||||
independent build steps in parallel.
|
||||
|
||||
.. option:: -LDFLAGS <flags>
|
||||
|
||||
Add specified C linker arguments to the generated makefiles. For multiple
|
||||
flags either pass them as a single argument with space separators quoted
|
||||
in the shell (``-LDFLAGS "-a -b"``), or use multiple -LDFLAGS arguments
|
||||
(``-LDFLAGS -a -LDFLAGS -b``).
|
||||
|
||||
When make is run on the generated makefile these will be passed to the
|
||||
C++ linker (ld) **after** the primary file being linked. This flag is
|
||||
called :vlopt:`-LDFLAGS` as that's the traditional name in simulators;
|
||||
it's would have been better called LDLIBS as that's the Makefile
|
||||
variable it controls. (In Make, LDFLAGS is before the first object,
|
||||
LDLIBS after. -L libraries need to be in the Make variable LDLIBS, not
|
||||
LDFLAGS.)
|
||||
Specify the level of parallelism for :vlopt:`--build` if
|
||||
:vlopt:`--build-jobs` isn't provided. If zero, uses the number of threads
|
||||
in the current hardware. Otherwise, the <value> must be a positive
|
||||
integer specifying the maximum number of parallel build jobs.
|
||||
|
||||
.. option:: --l2-name <value>
|
||||
|
||||
@@ -568,12 +720,20 @@ Summary:
|
||||
A synonym for :vlopt:`--default-language`, for compatibility with other
|
||||
tools and earlier versions of Verilator.
|
||||
|
||||
.. option:: +libext+<ext>[+<ext>][...]
|
||||
.. option:: -LDFLAGS <flags>
|
||||
|
||||
Specify the extensions that should be used for finding modules. If for
|
||||
example module "my" is referenced, look in :file:`my.<ext>`. Note
|
||||
"+libext+" is fairly standard across Verilog tools. Defaults to
|
||||
".v+.sv".
|
||||
Add specified C linker arguments to the generated makefiles. For multiple
|
||||
flags either pass them as a single argument with space separators quoted
|
||||
in the shell (``-LDFLAGS "-a -b"``), or use multiple -LDFLAGS arguments
|
||||
(``-LDFLAGS -a -LDFLAGS -b``).
|
||||
|
||||
When make is run on the generated makefile these will be passed to the
|
||||
C++ linker (ld) **after** the primary file being linked. This flag is
|
||||
called :vlopt:`-LDFLAGS` as that's the traditional name in simulators;
|
||||
it's would have been better called LDLIBS as that's the Makefile
|
||||
variable it controls. (In Make, LDFLAGS is before the first object,
|
||||
LDLIBS after. -L libraries need to be in the Make variable LDLIBS, not
|
||||
LDFLAGS.)
|
||||
|
||||
.. option:: --lib-create <name>
|
||||
|
||||
@@ -590,8 +750,17 @@ Summary:
|
||||
model has a time resolution that is always compatible with the time
|
||||
precision of the upper instantiating module.
|
||||
|
||||
Designs compiled using this option cannot use :vlopt:`--timing` with delays.
|
||||
|
||||
See also :vlopt:`--protect-lib`.
|
||||
|
||||
.. option:: +libext+<ext>[+<ext>][...]
|
||||
|
||||
Specify the extensions that should be used for finding modules. If for
|
||||
example module "my" is referenced, look in :file:`my.<ext>`. Note
|
||||
"+libext+" is fairly standard across Verilog tools. Defaults to
|
||||
".v+.sv".
|
||||
|
||||
.. option:: --lint-only
|
||||
|
||||
Check the files for lint violations only, do not create any other
|
||||
@@ -625,22 +794,29 @@ Summary:
|
||||
(e.g. ``-MAKEFLAGS -l -MAKEFLAGS -k``). Use of this option should not be
|
||||
required for simple builds using the host toolchain.
|
||||
|
||||
.. option:: --main
|
||||
|
||||
Generates a top-level C++ main() file that supports parsing arguments,
|
||||
but does not drive any inputs. This is sufficient to use for top-level
|
||||
SystemVerilog designs that has no inputs.
|
||||
|
||||
This option can also be used once to generate a main .cpp file as a
|
||||
starting point for editing. Copy it outside the obj directory, manually
|
||||
edit, and then pass the filename on later Verilator command line
|
||||
invocations.
|
||||
|
||||
Typically used with :vlopt:`--timing` to support delay-generated clocks,
|
||||
and :vlopt:`--build`.
|
||||
|
||||
Implies :vlopt:`--cc` if no other output mode was provided.
|
||||
|
||||
See also :vlopt:`--binary`.
|
||||
|
||||
.. option:: --max-num-width <value>
|
||||
|
||||
Set the maximum number literal width (e.g. in 1024'd22 this it the
|
||||
1024). Defaults to 64K.
|
||||
|
||||
.. option:: --MMD =item --no-MMD
|
||||
|
||||
Enable/disable creation of .d dependency files, used for make dependency
|
||||
detection, similar to gcc -MMD option. By default this option is
|
||||
enabled for :vlopt:`--cc` or :vlopt:`--sc` modes.
|
||||
|
||||
.. option:: --MP
|
||||
|
||||
When creating .d dependency files with :vlopt:`--MMD` option, make phony
|
||||
targets. Similar to :command:`gcc -MP` option.
|
||||
|
||||
.. option:: --Mdir <directory>
|
||||
|
||||
Specifies the name of the Make object directory. All generated files
|
||||
@@ -648,40 +824,23 @@ Summary:
|
||||
The directory is created if it does not exist and the parent directories
|
||||
exist; otherwise manually create the Mdir before calling Verilator.
|
||||
|
||||
.. option:: --no-merge-const-pool
|
||||
.. option:: --MMD
|
||||
|
||||
Rarely needed. In order to minimize cache footprint, values of different
|
||||
data type, that are yet emitted identically in C++ are merged in the
|
||||
constant pool. This option disables this and causes every constant pool
|
||||
entry with a distinct data type to be emitted separately.
|
||||
.. option:: --no-MMD
|
||||
|
||||
Enable/disable creation of .d dependency files, used for make dependency
|
||||
detection, similar to gcc -MMD option. By default this option is
|
||||
enabled for :vlopt:`--cc` or :vlopt:`--sc` modes.
|
||||
|
||||
.. option:: --mod-prefix <topname>
|
||||
|
||||
Specifies the name to prepend to all lower level classes. Defaults to
|
||||
the same as :vlopt:`--prefix`.
|
||||
|
||||
.. option:: --no-clk <signal-name>
|
||||
.. option:: --MP
|
||||
|
||||
Prevent the specified signal from being marked as clock. See
|
||||
:vlopt:`--clk`.
|
||||
|
||||
.. option:: --no-decoration
|
||||
|
||||
When creating output Verilated code, minimize comments, white space,
|
||||
symbol names and other decorative items, at the cost of greatly reduced
|
||||
readability. This may assist C++ compile times. This will not typically
|
||||
change the ultimate model's performance, but may in some cases.
|
||||
|
||||
.. option:: --no-pins64
|
||||
|
||||
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
|
||||
|
||||
.. option:: --no-skip-identical =item --skip-identical
|
||||
|
||||
Rarely needed. Disables or enables skipping execution of Verilator if
|
||||
all source files are identical, and all output files exist with newer
|
||||
dates. By default this option is enabled for :vlopt:`--cc` or
|
||||
:vlopt:`--sc` modes only.
|
||||
When creating .d dependency files with :vlopt:`--MMD` option, make phony
|
||||
targets. Similar to :command:`gcc -MP` option.
|
||||
|
||||
.. option:: +notimingchecks
|
||||
|
||||
@@ -703,9 +862,9 @@ Summary:
|
||||
|
||||
Rarely needed. Enables or disables a specific optimizations, with the
|
||||
optimization selected based on the letter passed. A lowercase letter
|
||||
disables an optimization, an upper case letter enables it. This is
|
||||
intended for debugging use only; see the source code for
|
||||
version-dependent mappings of optimizations to -O letters.
|
||||
disables an optimization, an upper case letter enables it. This option
|
||||
is deprecated and the various `-f<optimization>` arguments should be
|
||||
used instead.
|
||||
|
||||
.. option:: -o <executable>
|
||||
|
||||
@@ -714,6 +873,10 @@ Summary:
|
||||
|
||||
.. option:: --no-order-clock-delay
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions prior to 5.000:
|
||||
|
||||
Rarely needed. Disables a bug fix for ordering of clock enables with
|
||||
delayed assignments. This option should only be used when suggested by
|
||||
the developers.
|
||||
@@ -760,15 +923,10 @@ Summary:
|
||||
With :vlopt:`-E`, disable generation of :code:`&96;line` markers and
|
||||
blank lines, similar to :command:`gcc -P`.
|
||||
|
||||
.. option:: --pins64
|
||||
|
||||
Backward compatible alias for :vlopt:`--pins-bv 65 <--pins-bv>`. Note
|
||||
that's a 65, not a 64.
|
||||
|
||||
.. option:: --pins-bv <width>
|
||||
|
||||
Specifies SystemC inputs/outputs of greater than or equal to <width>
|
||||
bits wide should use sc_bv's instead of uint32/vluint64_t's. The
|
||||
bits wide should use sc_bv's instead of uint32/uint64_t's. The
|
||||
default is "--pins-bv 65", and the value must be less than or equal
|
||||
to 65. Versions before Verilator 3.671 defaulted to "--pins-bv 33".
|
||||
The more sc_bv is used, the worse for performance. Use the
|
||||
@@ -797,6 +955,15 @@ Summary:
|
||||
of uint32_t. Likewise pins of width 9-16 will use uint16_t instead of
|
||||
uint32_t.
|
||||
|
||||
.. option:: --pins64
|
||||
|
||||
Backward compatible alias for :vlopt:`--pins-bv 65 <--pins-bv>`. Note
|
||||
that's a 65, not a 64.
|
||||
|
||||
.. option:: --no-pins64
|
||||
|
||||
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
|
||||
|
||||
.. option:: --pipe-filter <command>
|
||||
|
||||
Rarely needed. Verilator will spawn the specified command as a
|
||||
@@ -826,6 +993,11 @@ Summary:
|
||||
prepended to the name of the :vlopt:`--top` option, or V prepended to
|
||||
the first Verilog filename passed on the command line.
|
||||
|
||||
.. option:: --private
|
||||
|
||||
Opposite of :vlopt:`--public`. Is the default; this option exists for
|
||||
backwards compatibility.
|
||||
|
||||
.. option:: --prof-c
|
||||
|
||||
When compiling the C++ code, enable the compiler's profiling flag
|
||||
@@ -845,27 +1017,19 @@ Summary:
|
||||
|
||||
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`--prof-c`.
|
||||
|
||||
.. option:: --prof-exec
|
||||
|
||||
Enable collection of execution trace, that can be converted into a gantt
|
||||
chart with verilator_gantt See :ref:`Execution Profiling`.
|
||||
|
||||
.. option:: --prof-pgo
|
||||
|
||||
Enable collection of profiling data for profile guided Verilation. Currently
|
||||
this is only useful with :vlopt:`--threads`. See :ref:`Thread PGO`.
|
||||
|
||||
.. option:: --prof-threads
|
||||
|
||||
Enable gantt chart data collection for threaded builds. See :ref:`Thread
|
||||
Profiling` and :ref:`Thread PGO`.
|
||||
|
||||
.. option:: --protect-key <key>
|
||||
|
||||
Specifies the private key for :vlopt:`--protect-ids`. For best security
|
||||
this key should be 16 or more random bytes, a reasonable secure choice
|
||||
is the output of :command:`verilator --generate-key` . Typically, a key
|
||||
would be created by the user once for a given protected design library,
|
||||
then every Verilator run for subsequent versions of that library would
|
||||
be passed the same :vlopt:`--protect-key`. Thus, if the input Verilog is
|
||||
similar between library versions (Verilator runs), the Verilated code
|
||||
will likewise be mostly similar.
|
||||
|
||||
If :vlopt:`--protect-key` is not specified and a key is needed,
|
||||
Verilator will generate a new key for every Verilator run. As the key is
|
||||
not saved, this is best for security, but means every Verilator run will
|
||||
give vastly different output even for identical input, perhaps harming
|
||||
compile times (and certainly thrashing any "ccache").
|
||||
Deprecated. Same as --prof-exec and --prof-pgo together.
|
||||
|
||||
.. option:: --protect-ids
|
||||
|
||||
@@ -884,9 +1048,26 @@ Summary:
|
||||
|
||||
Using DPI imports/exports is allowed and generally relatively safe in
|
||||
terms of information disclosed, which is limited to the DPI function
|
||||
prototyptes. Use of the VPI is not recommended as many design details
|
||||
prototypes. Use of the VPI is not recommended as many design details
|
||||
may be exposed, and an INSECURE warning will be issued.
|
||||
|
||||
.. option:: --protect-key <key>
|
||||
|
||||
Specifies the private key for :vlopt:`--protect-ids`. For best security
|
||||
this key should be 16 or more random bytes, a reasonable secure choice
|
||||
is the output of :command:`verilator --generate-key` . Typically, a key
|
||||
would be created by the user once for a given protected design library,
|
||||
then every Verilator run for subsequent versions of that library would
|
||||
be passed the same :vlopt:`--protect-key`. Thus, if the input Verilog is
|
||||
similar between library versions (Verilator runs), the Verilated code
|
||||
will likewise be mostly similar.
|
||||
|
||||
If :vlopt:`--protect-key` is not specified and a key is needed,
|
||||
Verilator will generate a new key for every Verilator run. As the key is
|
||||
not saved, this is best for security, but means every Verilator run will
|
||||
give vastly different output even for identical input, perhaps harming
|
||||
compile times (and certainly thrashing any "ccache").
|
||||
|
||||
.. option:: --protect-lib <name>
|
||||
|
||||
Produces a DPI library similar to :vlopt:`--lib-create`, but hides
|
||||
@@ -898,10 +1079,7 @@ Summary:
|
||||
in the distribution for a demonstration of how to build and use the DPI
|
||||
library.
|
||||
|
||||
.. option:: --private
|
||||
|
||||
Opposite of :vlopt:`--public`. Is the default; this option exists for
|
||||
backwards compatibility.
|
||||
Designs compiled using this option cannot use :vlopt:`--timing` with delays.
|
||||
|
||||
.. option:: --public
|
||||
|
||||
@@ -995,6 +1173,15 @@ Summary:
|
||||
|
||||
Specifies SystemC output mode; see also :vlopt:`--cc` option.
|
||||
|
||||
.. option:: --skip-identical
|
||||
|
||||
.. option:: --no-skip-identical
|
||||
|
||||
Rarely needed. Disables or enables skipping execution of Verilator if
|
||||
all source files are identical, and all output files exist with newer
|
||||
dates. By default this option is enabled for :vlopt:`--cc` or
|
||||
:vlopt:`--sc` modes only.
|
||||
|
||||
.. option:: --stats
|
||||
|
||||
Creates a dump file with statistics on the design in
|
||||
@@ -1032,7 +1219,8 @@ Summary:
|
||||
is not thread safe. With "--threads 1", the generated model is single
|
||||
threaded but may run in a multithreaded environment. With "--threads N",
|
||||
where N >= 2, the model is generated to run multithreaded on up to N
|
||||
threads. See :ref:`Multithreading`.
|
||||
threads. See :ref:`Multithreading`. This option also applies to
|
||||
:vlopt:`--trace` (but not :vlopt:`--trace-fst`).
|
||||
|
||||
.. option:: --threads-dpi all
|
||||
|
||||
@@ -1084,6 +1272,16 @@ Summary:
|
||||
module. As "1fs" is the finest time precision it may be desirable to
|
||||
always use a precision of "1fs".
|
||||
|
||||
.. option:: --timing
|
||||
|
||||
.. option:: --no-timing
|
||||
|
||||
Enables/disables support for timing constructs such as delays, event
|
||||
controls (unless it's at the top of a process), wait statements, and joins.
|
||||
When disabled, timing control constructs are ignored the same way as they
|
||||
were in earlier versions of Verilator. Enabling this feature requires a C++
|
||||
compiler with coroutine support (GCC 10, Clang 5, or newer).
|
||||
|
||||
.. option:: --top <topname>
|
||||
|
||||
.. option:: --top-module <topname>
|
||||
@@ -1110,7 +1308,8 @@ Summary:
|
||||
Having tracing compiled in may result in some small performance losses,
|
||||
even when tracing is not turned on during model execution.
|
||||
|
||||
See also :vlopt:`--trace-threads` option.
|
||||
When using :vlopt:`--threads`, VCD tracing is parallelized, using the
|
||||
same number of threads as passed to :vlopt:`--threads`.
|
||||
|
||||
.. option:: --trace-coverage
|
||||
|
||||
@@ -1164,12 +1363,12 @@ Summary:
|
||||
.. option:: --trace-threads *threads*
|
||||
|
||||
Enable waveform tracing using separate threads. This is typically faster
|
||||
in simulation runtime but uses more total compute. This option is
|
||||
independent of, and works with, both :vlopt:`--trace` and
|
||||
:vlopt:`--trace-fst`. Different trace formats can take advantage of
|
||||
more trace threads to varying degrees. Currently VCD tracing can utilize
|
||||
at most "--trace-threads 1", and FST tracing can utilize at most
|
||||
"--trace-threads 2". This overrides :vlopt:`--no-threads` .
|
||||
in simulation runtime but uses more total compute. This option only
|
||||
applies to :vlopt:`--trace-fst`. FST tracing can utilize at most
|
||||
"--trace-threads 2". This overrides :vlopt:`--no-threads`.
|
||||
|
||||
This option is accepted, but has absolutely no effect with
|
||||
:vlopt:`--trace`, which respects :vlopt:`--threads` instead.
|
||||
|
||||
.. option:: --trace-underscore
|
||||
|
||||
@@ -1246,8 +1445,7 @@ Summary:
|
||||
|
||||
Enable all code style warnings, including code style warnings that are
|
||||
normally disabled by default. Equivalent to :vlopt:`-Wwarn-lint`
|
||||
:vlopt:`-Wwarn-style`. Excludes some specialty warnings,
|
||||
i.e. IMPERFECTSCH.
|
||||
:vlopt:`-Wwarn-style`. Excludes some specialty warnings.
|
||||
|
||||
.. option:: -Werror-<message>
|
||||
|
||||
@@ -1290,7 +1488,8 @@ Summary:
|
||||
equivalent to ``-Wno-ALWCOMBORDER -Wno-BSSPACE -Wno-CASEINCOMPLETE
|
||||
-Wno-CASEOVERLAP -Wno-CASEX -Wno-CASTCONST -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS
|
||||
-Wno-ENDLABEL -Wno-IMPLICIT -Wno-LITENDIAN -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED -Wno-UNUSED
|
||||
-Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED
|
||||
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
|
||||
-Wno-WIDTH`` plus the list shown for Wno-style.
|
||||
|
||||
It is strongly recommended you cleanup your code rather than using this
|
||||
@@ -1302,7 +1501,8 @@ Summary:
|
||||
Disable all code style related warning messages (note by default they are
|
||||
already disabled). This is equivalent to ``-Wno-DECLFILENAME -Wno-DEFPARAM
|
||||
-Wno-EOFNEWLINE -Wno-IMPORTSTAR -Wno-INCABSPATH -Wno-PINCONNECTEMPTY
|
||||
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNUSED
|
||||
-Wno-PINNOCONNECT -Wno-SYNCASYNCNET -Wno-UNDRIVEN
|
||||
-Wno-UNUSEDGENVAR -Wno-UNUSEDPARAM -Wno-UNUSEDSIGNAL
|
||||
-Wno-VARHIDDEN``.
|
||||
|
||||
.. option:: -Wpedantic
|
||||
@@ -1331,7 +1531,7 @@ Summary:
|
||||
Enable all code style related warning messages. This is equivalent to
|
||||
``-Wwarn ASSIGNDLY -Wwarn-DECLFILENAME -Wwarn-DEFPARAM -Wwarn-EOFNEWLINE
|
||||
-Wwarn-INCABSPATH -Wwarn-PINNOCONNECT -Wwarn-SYNCASYNCNET -Wwarn-UNDRIVEN
|
||||
-Wwarn-UNUSED -Wwarn-VARHIDDEN``.
|
||||
-Wwarn-UNUSEDGENVAR -Wwarn-UNUSEDPARAM -Wwarn-UNUSEDSIGNAL -Wwarn-VARHIDDEN``.
|
||||
|
||||
.. option:: --x-assign 0
|
||||
|
||||
@@ -1500,6 +1700,10 @@ The grammar of configuration commands is as follows:
|
||||
|
||||
.. option:: clock_enable -module "<modulename>" -var "<signame>"
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions prior to 5.000:
|
||||
|
||||
Indicate the signal is used to gate a clock, and the user takes
|
||||
responsibility for insuring there are no races related to it.
|
||||
|
||||
@@ -1514,7 +1718,7 @@ The grammar of configuration commands is as follows:
|
||||
.. option:: no_clocker -module "<modulename>" [-function "<funcname>"] -var "<signame>"
|
||||
|
||||
Indicates that the signal is used as clock or not. This information is
|
||||
used by Verilator to mark the signal and any derrived signals as
|
||||
used by Verilator to mark the signal and any derived signals as
|
||||
clocker. See :vlopt:`--clk`.
|
||||
|
||||
Same as :option:`/*verilator&32;clocker*/` metacomment.
|
||||
@@ -1638,7 +1842,7 @@ The grammar of configuration commands is as follows:
|
||||
.. option:: sc_bv -module "<modulename>" [-function "<funcname>"] -var "<signame>"
|
||||
|
||||
Sets the port to be of :code:`sc_bv<{width}>` type, instead of bool,
|
||||
vluint32_t or vluint64_t. Same as :option:`/*verilator&32;sc_bv*/`
|
||||
uint32_t or uint64_t. Same as :option:`/*verilator&32;sc_bv*/`
|
||||
metacomment.
|
||||
|
||||
.. option:: sformat [-module "<modulename>"] [-task "<taskname>"] -var "<signame>"
|
||||
@@ -1663,13 +1867,45 @@ The grammar of configuration commands is as follows:
|
||||
|
||||
Same as :option:`/*verilator&32;split_var*/` metacomment.
|
||||
|
||||
.. option:: timing_on [-file "<filename>" [-lines <line> [ - <line>]]]
|
||||
|
||||
.. option:: timing_off [-file "<filename>" [-lines <line> [ - <line>]]]
|
||||
|
||||
Enables/disables timing constructs for the specified file and lines.
|
||||
When disabled, all timing control constructs in the specified source
|
||||
code locations are ignored the same way as with the
|
||||
:option:`--no-timing`, and code:`fork`/:code:`join*` blocks are
|
||||
converted into :code:`begin`/:code:`end` blocks.
|
||||
|
||||
Same as :option:`/*verilator&32;timing_on*/`,
|
||||
:option:`/*verilator&32;timing_off*/` metacomments.
|
||||
|
||||
.. option:: tracing_on [-file "<filename>" [-lines <line> [ - <line> ]]]
|
||||
|
||||
.. option:: tracing_off [-file "<filename>" [-lines <line> [ - <line> ]]]
|
||||
|
||||
Enable/disable waveform tracing for all future signals declared in the
|
||||
specified filename (or wildcard with '\*' or '?', or all files if
|
||||
omitted) and range of line numbers (or all lines if omitted).
|
||||
.. option:: tracing_on [-scope "<scopename>" [-levels <levels> ]]
|
||||
|
||||
For tracing_off, instances below any module in the files/ranges
|
||||
specified will also not be traced.
|
||||
.. option:: tracing_off [-scope "<scopename>" [-levels <levels> ]]
|
||||
|
||||
Enable/disable waveform tracing for all future signals declared in
|
||||
all files.
|
||||
|
||||
With -file, enable/disable waveform tracing in the specified
|
||||
filename (or wildcard with '\*' or '?'), and -line range of line
|
||||
numbers (or all lines if omitted).
|
||||
|
||||
For tracing_off with -file, instances below any module in the
|
||||
files/ranges specified will also not be traced. To overcome this
|
||||
feature, use tracing_on on the upper module declaration and on any
|
||||
cells, or use the -scope flavor of the command.
|
||||
|
||||
With -scope enable/disable waveform tracing for the specified scope (or
|
||||
wildcard with '\*' or '?'), and optional --levels number of levels
|
||||
below. These controls only take place after other file/line/module
|
||||
based controls have indicated the signal should be traced.
|
||||
|
||||
With -levels (used with -scope), the number of levels below that
|
||||
scope which the rule is to match, where 0 means all levels below, 1
|
||||
the exact level as the provided scope, and 2 meaning an additional
|
||||
level of children below the provided scope, etc.
|
||||
|
||||
@@ -67,7 +67,7 @@ which have low coverage are written to the output directory.
|
||||
|
||||
Specifies if the coverage point does not include the count number of
|
||||
coverage hits, then the coverage point will be considered above the
|
||||
threashold, and the coverage report will put a "%" to indicate the coverage
|
||||
threshold, and the coverage report will put a "%" to indicate the coverage
|
||||
is not sufficient. Defaults to 10.
|
||||
|
||||
.. option:: --help
|
||||
|
||||
@@ -13,7 +13,7 @@ For an overview of use of verilator_gantt, see :ref:`Profiling`.
|
||||
Gantt Chart VCD
|
||||
---------------
|
||||
|
||||
Verilated_gnatt creates a value change dump (VCD) format dump file which
|
||||
Verilated_gantt creates a value change dump (VCD) format dump file which
|
||||
may be viewed in a waveform viewer (e.g. C<GTKWave>):
|
||||
|
||||
.. figure:: figures/fig_gantt_min.png
|
||||
@@ -72,7 +72,7 @@ verilator_gantt Arguments
|
||||
|
||||
.. option:: <filename>
|
||||
|
||||
The filename to read data from, defaults to "profile_threads.dat".
|
||||
The filename to read data from, defaults to "profile_exec.dat".
|
||||
|
||||
.. option:: --help
|
||||
|
||||
|
||||
@@ -151,6 +151,13 @@ or "`ifdef`"'s may break other tools.
|
||||
Take remaining text up to the next :option:`\`verilog` mode switch and
|
||||
treat it as Verilator configuration commands. See :ref:`Configuration Files`.
|
||||
|
||||
.. option:: `VERILATOR_TIMING
|
||||
|
||||
The VERILATOR_TIMING define is set when :vlopt:`--timing` is used to
|
||||
allow an "\`ifdef" of code dependent on this feature. Note this define
|
||||
is not affected by the :option:`timing_off` configuration file option
|
||||
nor timing metacomments.
|
||||
|
||||
.. option:: `verilog
|
||||
|
||||
Switch back to processing Verilog code after a
|
||||
@@ -160,6 +167,10 @@ or "`ifdef`"'s may break other tools.
|
||||
|
||||
.. option:: /*verilator&32;clock_enable*/
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions prior to 5.000:
|
||||
|
||||
Used after a signal declaration to indicate the signal is used to gate a
|
||||
clock, and the user takes responsibility for insuring there are no races
|
||||
related to it. (Typically by adding a latch, and running static timing
|
||||
@@ -174,9 +185,7 @@ or "`ifdef`"'s may break other tools.
|
||||
|
||||
The clock_enable attribute will cause the clock gate to be ignored in
|
||||
the scheduling algorithm, sometimes required for correct clock behavior,
|
||||
and always improving performance. It's also a good idea to enable the
|
||||
:option:`IMPERFECTSCH` warning, to ensure all clock enables are properly
|
||||
recognized.
|
||||
and always improving performance.
|
||||
|
||||
Same as :option:`clock_enable` configuration file option.
|
||||
|
||||
@@ -184,9 +193,7 @@ or "`ifdef`"'s may break other tools.
|
||||
|
||||
.. option:: /*verilator&32;no_clocker*/
|
||||
|
||||
Specifies that the signal is used as clock or not. This information is
|
||||
used by Verilator to mark the signal and any derrived signals as
|
||||
clocker. See :vlopt:`--clk`.
|
||||
Specifies that the signal is used as clock or not. See :vlopt:`--clk`.
|
||||
|
||||
Same as :option:`clocker` and :option:`no_clocker` in configuration
|
||||
files.
|
||||
@@ -217,7 +224,7 @@ or "`ifdef`"'s may break other tools.
|
||||
:option:`public_flat` signals.
|
||||
|
||||
To force a marked signal from C++, set the corresponding `__VforceVal`
|
||||
variable to the desired value, and the `__VforceEn` signal to the bitmask
|
||||
variable to the desired value, and the `__VforceEn` signal to the bit-mask
|
||||
indicating which bits of the signal to force. To force all bits of the
|
||||
target signal, set `__VforceEn` to all ones. To release the signal (or part
|
||||
thereof), set appropriate bits of the `__VforceEn` signal to zero.
|
||||
@@ -445,7 +452,7 @@ or "`ifdef`"'s may break other tools.
|
||||
.. option:: /*verilator&32;sc_bv*/
|
||||
|
||||
Used after a port declaration. It sets the port to be of
|
||||
:code:`sc_bv<{width}>` type, instead of bool, vluint32_t or vluint64_t.
|
||||
:code:`sc_bv<{width}>` type, instead of bool, uint32_t or uint64_t.
|
||||
This may be useful if the port width is parameterized and the
|
||||
instantiating C++ code wants to always have a sc_bv so it can accept any
|
||||
width. In general you should avoid using this attribute when not
|
||||
@@ -503,6 +510,22 @@ or "`ifdef`"'s may break other tools.
|
||||
Verilator) text that should be passed through to the XML output as a tag,
|
||||
for use by downstream applications.
|
||||
|
||||
.. option:: /*verilator&32;timing_off*/
|
||||
|
||||
Ignore all timing constructs after this metacomment. All timing controls
|
||||
behave as if they were not there (the same way as with
|
||||
:option:`--no-timing`), and :code:`fork`/:code:`join*` blocks are
|
||||
converted into :code:`begin`/:code:`end` blocks.
|
||||
|
||||
Same as :option:`timing_off` configuration file option.
|
||||
|
||||
.. option:: /*verilator&32;timing_on*/
|
||||
|
||||
Re-enable all timing constructs after this metacomment (only applicable
|
||||
after :option:`timing_off`).
|
||||
|
||||
Same as :option:`timing_on` configuration file option.
|
||||
|
||||
.. option:: /*verilator&32;trace_init_task*/
|
||||
|
||||
Attached to a DPI import to indicate that function should be called when
|
||||
|
||||
+38
-22
@@ -37,15 +37,15 @@ Does Verilator run under Windows?
|
||||
|
||||
Yes, ideally run Ubuntu under Windows Subsystem for Linux (WSL2).
|
||||
Alternatively use Cygwin, though this tends to be slower and is not
|
||||
regurally tested. Verilated output also compiles under Microsoft Visual
|
||||
C++, but this is also not regurally tested.
|
||||
regularly tested. Verilated output also compiles under Microsoft Visual
|
||||
C++, but this is also not regularly tested.
|
||||
|
||||
|
||||
Can you provide binaries?
|
||||
"""""""""""""""""""""""""
|
||||
|
||||
You can install Verilator via the system package manager (apt, yum, etc.)
|
||||
on many Linux distributions, including Debian, Ubuntu, SuSE, RedHat, and
|
||||
on many Linux distributions, including Debian, Ubuntu, SuSE, Red Hat, and
|
||||
others. These packages are provided by the Linux distributions and
|
||||
generally will lag the version of the mainline Verilator repository. If no
|
||||
binary package is available for your distribution, how about you set one
|
||||
@@ -72,23 +72,38 @@ a good thing for getting working silicon.
|
||||
Will Verilator output remain under my own license/copyright?
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Yes, it's just like using GCC on your programs; this is why Verilator uses
|
||||
the "GNU **Lesser** Public License Version 3" instead of the more typical
|
||||
"GNU Public License". See the licenses for details, but in brief, if you
|
||||
change Verilator itself or the header files Verilator includes, you must
|
||||
make the source code available under the GNU Lesser Public License.
|
||||
However, Verilator output (the Verilated code) only "include"s the licensed
|
||||
files, and so you are **not** required to open-source release any output
|
||||
from Verilator.
|
||||
Your SystemVerilog, VPI/DPI, or main() C++ code remains under your own license.
|
||||
|
||||
It's just like how using GCC on your programs does not change the copyright
|
||||
of your program; this is why Verilator uses the "GNU **Lesser** Public
|
||||
License Version 3" instead of the more typical "GNU Public License". See
|
||||
the licenses for details.
|
||||
|
||||
Some examples:
|
||||
|
||||
* Any SystemVerilog or other input fed into Verilator remain your own.
|
||||
|
||||
* Any of your VPI/DPI C++ routines that Verilator calls remain your own.
|
||||
|
||||
* Any of your main() C++ code that calls into Verilator remain your own.
|
||||
|
||||
* If you change Verilator itself, for example changing or adding a file
|
||||
under the src/ directory in the repository, you must make the source code
|
||||
available under the GNU Lesser Public License.
|
||||
|
||||
* If you change a header Verilator provides, for example under include/ in
|
||||
the repository, you must make the source code available under the GNU
|
||||
Lesser Public License.
|
||||
|
||||
You also have the option of using the Perl Artistic License, which again
|
||||
does not require you to release your Verilog or generated code, and also
|
||||
allows you to modify Verilator for internal use without distributing the
|
||||
modified version. But please contribute back to the community!
|
||||
does not require you to release your Verilog, C++, or generated code. This
|
||||
license also allows you to modify Verilator for internal use without
|
||||
distributing the modified version. But please contribute back to the
|
||||
community!
|
||||
|
||||
One limit is that you cannot under either license release a closed-source
|
||||
Verilog simulation product incorporating Verilator. That is you can have a
|
||||
commercial product, but must make the source code available.
|
||||
Under both license you can offer a commercial product that is based on
|
||||
Verilator either directly or embedded within. However under both licenses,
|
||||
any changes you make to Verilator for such a product must be open sourced.
|
||||
|
||||
As is standard with Open Source, contributions back to Verilator will be
|
||||
placed under the Verilator copyright and LGPL/Artistic license. Small test
|
||||
@@ -134,7 +149,8 @@ B. Or, for finer-grained control, or C++ files with multiple Verilated
|
||||
...
|
||||
Verilated::traceEverOn(true);
|
||||
VerilatedVcdC* tfp = new VerilatedVcdC;
|
||||
topp->trace(tfp, 99); // Trace 99 levels of hierarchy
|
||||
topp->trace(tfp, 99); // Trace 99 levels of hierarchy (or see below)
|
||||
// tfp->dumpvars(1, "t"); // trace 1 level under "t"
|
||||
tfp->open("obj_dir/t_trace_ena_cc/simx.vcd");
|
||||
...
|
||||
while (contextp->time() < sim_time && !contextp->gotFinish()) {
|
||||
@@ -353,8 +369,8 @@ also use the "import DPI" SystemVerilog feature to call C code (see the
|
||||
chapter above). There is also limited VPI access to public signals.
|
||||
|
||||
If you want something more complex, since Verilator emits standard C++
|
||||
code, you can simply write your own C++ routines that can access and modify
|
||||
signal values without needing any PLI interface code, and call it with
|
||||
code, you can write your own C++ routines that can access and modify signal
|
||||
values without needing any PLI interface code, and call it with
|
||||
$c("{any_c++_statement}").
|
||||
|
||||
See the :ref:`Connecting` section.
|
||||
@@ -444,7 +460,7 @@ the signal, as you would any other member variable.
|
||||
Signals are the smallest of 8-bit unsigned chars (equivalent to uint8_t),
|
||||
16-bit unsigned shorts (uint16_t), 32-bit unsigned longs (uint32_t), or
|
||||
64-bit unsigned long longs (uint64_t) that fits the width of the signal.
|
||||
Generally, you can use just uint32_t's for 1 to 32 bits, or vluint64_t for
|
||||
Generally, you can use just uint32_t's for 1 to 32 bits, or uint64_t for
|
||||
1 to 64 bits, and the compiler will properly up-convert smaller entities.
|
||||
Note even signed ports are declared as unsigned; you must sign extend
|
||||
yourself to the appropriate signal width.
|
||||
@@ -481,7 +497,7 @@ by your code or you'll get strange results.
|
||||
Should a module be in Verilog or SystemC?
|
||||
"""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Sometimes there is a block that just interconnects instances, and have a
|
||||
Sometimes there is a block that only interconnects instances, and have a
|
||||
choice as to if you write it in Verilog or SystemC. Everything else being
|
||||
equal, best performance is when Verilator sees all of the design. So, look
|
||||
at the hierarchy of your design, labeling instances as to if they are
|
||||
|
||||
+17
-11
@@ -50,7 +50,7 @@ For --cc/--sc, it creates:
|
||||
- Make include file with class names (from --make gmake)
|
||||
* - *{prefix}*\ _hier.mk
|
||||
- Make file for hierarchy blocks (from --make gmake)
|
||||
* - *{prefix|*\ _hierMkArgs.f
|
||||
* - *{prefix}*\ _hierMkArgs.f
|
||||
- Arguments for hierarchical Verilation (from --make gmake)
|
||||
* - *{prefix}*\ _hierCMakeArgs.f
|
||||
- Arguments for hierarchical Verilation (from --make cmake)
|
||||
@@ -62,13 +62,17 @@ For --cc/--sc, it creates:
|
||||
- Top level (SystemVerilog $root) internal header file
|
||||
* - *{prefix}*\ ___024root.cpp
|
||||
- Top level (SystemVerilog $root) internal C++ file
|
||||
* - *{prefix}*___024root*{__n}*\ .cpp
|
||||
- Additional top level internal C++ files (from --output-split)
|
||||
* - *{prefix}*\ ___024root\ *{__n}*\ .cpp
|
||||
- Additional top level internal C++ files
|
||||
* - *{prefix}*\ ___024root\ *{__DepSet_hash__n}*\ .cpp
|
||||
- Additional top level internal C++ files (hashed to reduce build times)
|
||||
* - *{prefix}*\ ___024root__Slow\ *{__n}*\ .cpp
|
||||
- Infrequent cold routines
|
||||
* - *{prefix}*\ ___024root__Trace{__n}*\ .cpp
|
||||
* - *{prefix}*\ ___024root\ *{__DepSet_hash__n}*\ .cpp
|
||||
- Infrequent cold routines (hashed to reduce build times)
|
||||
* - *{prefix}*\ ___024root__Trace\ *{__n}*\ .cpp
|
||||
- Wave file generation code (from --trace)
|
||||
* - *{prefix}*\ ___024root__Trace__Slow{__n}*\ .cpp
|
||||
* - *{prefix}*\ ___024root__Trace__Slow\ *{__n}*\ .cpp
|
||||
- Wave file generation code (from --trace)
|
||||
* - *{prefix}*\ __Dpi.h
|
||||
- DPI import and export declarations (from --dpi)
|
||||
@@ -87,7 +91,9 @@ For --cc/--sc, it creates:
|
||||
* - *{prefix}{each_verilog_module}*\ .cpp
|
||||
- Lower level internal C++ files
|
||||
* - *{prefix}{each_verilog_module}{__n}*\ .cpp
|
||||
- Additional lower C++ files (from --output-split)
|
||||
- Additional lower C++ files
|
||||
* - *{prefix}{each_verilog_module}{__DepSet_hash__n}*\ .cpp
|
||||
- Additional lower C++ files (hashed to reduce build times)
|
||||
|
||||
For --hierarchy mode, it creates:
|
||||
|
||||
@@ -120,7 +126,7 @@ In certain debug and other modes, it also creates:
|
||||
- Debugging graph files (from --debug)
|
||||
* - *{prefix}{misc}*\ .tree
|
||||
- Debugging files (from --debug)
|
||||
* - {mod_prefix}_{each_verilog_module}*{__n}*\ .vpp
|
||||
* - {mod_prefix}_{each_verilog_base_filename}*\ .vpp
|
||||
- Pre-processed verilog (from --debug)
|
||||
|
||||
After running Make, the C++ compiler may produce the following:
|
||||
@@ -155,13 +161,13 @@ The Verilated executable may produce the following:
|
||||
* - gmon.out
|
||||
- GCC/clang code profiler output, often fed into :command:`verilator_profcfunc`
|
||||
* - profile.vlt
|
||||
- -profile data file for :ref:`Thread PGO`
|
||||
* - profile_threads.dat
|
||||
- -profile-threads data file for :command:`verilator_gnatt`
|
||||
- --prof-pgo data file for :ref:`Thread PGO`
|
||||
* - profile_exec.dat
|
||||
- --prof-exec data file for :command:`verilator_gantt`
|
||||
|
||||
Verilator_gantt may produce the following:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - profile_threads.vcd
|
||||
* - profile_exec.vcd
|
||||
- Gantt report waveform output
|
||||
|
||||
+15
-7
@@ -77,7 +77,7 @@ OS Requirements
|
||||
---------------
|
||||
|
||||
Verilator is developed and has primary testing on Ubuntu, with additional
|
||||
testing on FreeBSD and Apple OS-X. Versions have also built on Redhat
|
||||
testing on FreeBSD and Apple OS-X. Versions have also built on Red Hat
|
||||
Linux, and other flavors of GNU/Linux-ish platforms. Verilator also works
|
||||
on Windows Subsystem for Linux (WSL2), Windows under Cygwin, and Windows
|
||||
under MinGW (gcc -mno-cygwin). Verilated output (not Verilator itself)
|
||||
@@ -87,7 +87,7 @@ compiles under all the options above, plus using MSVC++.
|
||||
Install Prerequisites
|
||||
---------------------
|
||||
|
||||
To build or run Verilator you need these standard packages:
|
||||
To build or run Verilator, you need these standard packages:
|
||||
|
||||
::
|
||||
|
||||
@@ -98,13 +98,20 @@ To build or run Verilator you need these standard packages:
|
||||
sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error)
|
||||
sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
|
||||
|
||||
To build or run the following are optional but should be installed for good
|
||||
performance:
|
||||
To build or run Verilator, the following are optional but should be installed
|
||||
for good performance:
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install ccache # If present at build, needed for run
|
||||
sudo apt-get install libgoogle-perftools-dev numactl perl-doc
|
||||
sudo apt-get install libgoogle-perftools-dev numactl
|
||||
|
||||
The following is optional but is recommended for nicely rendered command line
|
||||
help when running Verilator:
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install perl-doc
|
||||
|
||||
To build Verilator you will need to install these packages; these do not
|
||||
need to be present to run Verilator:
|
||||
@@ -117,8 +124,9 @@ Those developing Verilator itself may also want these (see internals.rst):
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install gdb graphviz cmake clang clang-format-11 gprof lcov
|
||||
sudo pip3 install sphinx sphinx_rtd_theme breathe
|
||||
sudo apt-get install gdb graphviz cmake clang clang-format-14 gprof lcov
|
||||
sudo apt-get install yapf3
|
||||
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe
|
||||
cpan install Pod::Perldoc
|
||||
cpan install Parallel::Forker
|
||||
|
||||
|
||||
+75
-26
@@ -92,7 +92,73 @@ appropriate code to detect failing cases at simulation runtime and print an
|
||||
|
||||
Verilator likewise also asserts any "unique" or "priority" SystemVerilog
|
||||
keywords on case statement, as well as "unique" on if statements. However,
|
||||
"priority if" is currently simply ignored.
|
||||
"priority if" is currently ignored.
|
||||
|
||||
|
||||
Time
|
||||
====
|
||||
|
||||
With :vlopt:`--timing`, all timing controls are supported:
|
||||
|
||||
* delay statements,
|
||||
* event control statements not only at the top of a process,
|
||||
* intra-assignment timing controls,
|
||||
* net delays,
|
||||
* :code:`wait` statements,
|
||||
|
||||
as well as all flavors of :code:`fork`.
|
||||
|
||||
Compiling a verilated design that makes use of these features requires a
|
||||
compiler with C++20 coroutine support, e.g. Clang 5, GCC 10, or newer.
|
||||
|
||||
:code:`#0` delays cause Verilator to issue the :option:`ZERODLY` warning, as
|
||||
they work differently than described in the LRM. They do not schedule process
|
||||
resumption in the Inactive region, though the process will get resumed in the
|
||||
same time slot.
|
||||
|
||||
Rising/falling/turn-off delays are currently unsupported and cause the
|
||||
:option:`RISEFALLDLY` warning.
|
||||
|
||||
Minimum/typical/maximum delays are currently unsupported. The typical delay is
|
||||
always the one chosen. Such expressions cause the :option:`MINTYPMAX` warning.
|
||||
|
||||
Another consequence of using :vlopt:`--timing` is that the :vlopt:`--main`
|
||||
option generates a main file with a proper timing eval loop, eliminating the
|
||||
need for writing any driving C++ code. You can then simply compile the
|
||||
simulation (perhaps using :vlopt:`--build`) and run it.
|
||||
|
||||
With :vlopt:`--no-timing`, all timing controls cause the :option:`NOTIMING`
|
||||
error, with the exception of:
|
||||
|
||||
* delay statements – they are ignored (as they are in synthesis), though they
|
||||
do issue a :option:`STMTDLY` warning,
|
||||
* intra-assignment timing controls – they are ignored, though they do issue an
|
||||
:option:`ASSIGNDLY` warning,
|
||||
* net delays – they are ignored,
|
||||
* event controls at the top of the procedure,
|
||||
|
||||
Forks cause this error as well, with the exception of:
|
||||
|
||||
* forks with no statements,
|
||||
* :code:`fork..join` or :code:`fork..join_any` with one statement,
|
||||
* forks with :vlopt:`--bbox-unsup`.
|
||||
|
||||
If neither :vlopt:`--timing` nor :vlopt:`--no-timing` is specified, all timing
|
||||
controls cause the :option:`NEEDTIMINGOPT` error, with the exception of event
|
||||
controls at the top of the process. Forks cause this error as well, with the
|
||||
exception of:
|
||||
|
||||
* forks with no statements,
|
||||
* :code:`fork..join` or :code:`fork..join_any` with one statement,
|
||||
* forks with :vlopt:`--bbox-unsup`.
|
||||
|
||||
Timing controls and forks can also be ignored in specific files or parts of
|
||||
files. The :option:`/*verilator&32;timing_off*/` and
|
||||
:option:`/*verilator&32;timing_off*/` metacomments will make Verilator ignore
|
||||
the encompassed timing controls and forks, regardless of the chosen
|
||||
:vlopt:`--timing` or :vlopt:`--no-timing` option. This can also be achieved
|
||||
using the :option:`timing_off` and :option:`timing_off` options in Verilator
|
||||
configuration files.
|
||||
|
||||
|
||||
.. _Language Limitations:
|
||||
@@ -136,7 +202,7 @@ ___05F (5F is the hex code of an underscore.)
|
||||
Bind
|
||||
----
|
||||
|
||||
sVerilator only supports bind to a target module name, not to an
|
||||
Verilator only supports bind to a target module name, not to an
|
||||
instance path.
|
||||
|
||||
|
||||
@@ -174,18 +240,12 @@ Structures and Unions
|
||||
---------------------
|
||||
|
||||
Presently Verilator only supports packed structs and packed unions. Rand
|
||||
and randc tags on members are simply ignored. All structures and unions
|
||||
are represented as a single vector, which means that generating one member
|
||||
of a structure from blocking, and another from non-blocking assignments is
|
||||
and randc tags on members are ignored. All structures and unions are
|
||||
represented as a single vector, which means that generating one member of a
|
||||
structure from blocking, and another from non-blocking assignments is
|
||||
unsupported.
|
||||
|
||||
|
||||
Time
|
||||
----
|
||||
|
||||
All delays (#) are ignored, as they are in synthesis.
|
||||
|
||||
|
||||
.. _Unknown States:
|
||||
|
||||
Unknown States
|
||||
@@ -276,17 +336,6 @@ probably expect, what C does. The default behavior of Verilog is
|
||||
different.)
|
||||
|
||||
|
||||
Generated Clocks
|
||||
----------------
|
||||
|
||||
Verilator attempts to deal with generated and gated clocks correctly,
|
||||
however some cases cause problems in the scheduling algorithm which is
|
||||
optimized for performance. The safest option is to have all clocks as
|
||||
primary inputs to the model, or wires directly attached to primary inputs.
|
||||
For proper behavior clock enables may also need the
|
||||
:option:`/*verilator&32;clock_enable*/` metacomment.
|
||||
|
||||
|
||||
Gate Primitives
|
||||
---------------
|
||||
|
||||
@@ -340,17 +389,17 @@ Force statement
|
||||
---------------
|
||||
|
||||
Verilator supports the procedural `force` (and corresponding `release`)
|
||||
statement. The behaviour of the `force` statement however does not entirely
|
||||
statement. The behavior of the `force` statement however does not entirely
|
||||
comply with the IEEE 1800 SystemVerilog standard. According to the standard,
|
||||
when a procedural statement of the form `force a = b;` is executed, the
|
||||
simulation should behave as if from that point onwards, a continuous
|
||||
simulation should behave as if from that point forwards, a continuous
|
||||
assignment `assign a = b;` have been added to override the drivers of `a`.
|
||||
More specifically: the value of `a` should be updated, whenever the value of
|
||||
`b` changes, all the way until a `release a;` statement is executed.
|
||||
Verilator instead evaluates the current value of `b` at the time the `force`
|
||||
statement is executed, and forces `a` to that value, without updating it
|
||||
until a new `force` or `release` statement is encountered that applies to
|
||||
`a`. This non-standard behaviour is nevertheless consistent with some other
|
||||
`a`. This non-standard behavior is nevertheless consistent with some other
|
||||
simulators.
|
||||
|
||||
|
||||
@@ -489,7 +538,7 @@ $test$plusargs, $value$plusargs
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
Verilated::commandArgs(argc, argv);
|
||||
{VerilatedContext*} ->commandArgs(argc, argv);
|
||||
|
||||
to register the command line before calling $test$plusargs or
|
||||
$value$plusargs.
|
||||
|
||||
+23
-23
@@ -7,9 +7,9 @@
|
||||
Simulating (Verilated-Model Runtime)
|
||||
************************************
|
||||
|
||||
This section describes items related to simulating, that using a Verilated
|
||||
model's executable. For the runtime arguments to a simulated model, see
|
||||
:ref:`Simulation Runtime Arguments`.
|
||||
This section describes items related to simulating, that is, the use of a
|
||||
Verilated model's executable. For the runtime arguments to a simulated
|
||||
model, see :ref:`Simulation Runtime Arguments`.
|
||||
|
||||
|
||||
.. _Benchmarking & Optimization:
|
||||
@@ -279,26 +279,25 @@ To use profiling:
|
||||
is being spent.
|
||||
|
||||
|
||||
.. _Thread Profiling:
|
||||
.. _Execution Profiling:
|
||||
|
||||
Thread Profiling
|
||||
================
|
||||
Execution Profiling
|
||||
===================
|
||||
|
||||
When using multithreaded mode (:vlopt:`--threads`), it is useful to see
|
||||
statistics and visualize how well the multiple CPUs are being utilized.
|
||||
For performance optimization, it is useful to see statistics and visualize how
|
||||
execution time is distributed in a verilated model.
|
||||
|
||||
With the :vlopt:`--prof-threads` option, Verilator will:
|
||||
With the :vlopt:`--prof-exec` option, Verilator will:
|
||||
|
||||
* Add code to the Verilated model to record the start and end time of each
|
||||
macro-task across a number of calls to eval. (What is a macro-task? See
|
||||
the Verilator internals document (:file:`docs/internals.rst` in the
|
||||
distribution.)
|
||||
* Add code to the Verilated model to record execution flow.
|
||||
|
||||
* Add code to save profiling data in non-human-friendly form to the file
|
||||
specified with :vlopt:`+verilator+prof+threads+file+\<filename\>`.
|
||||
specified with :vlopt:`+verilator+prof+exec+file+\<filename\>`.
|
||||
|
||||
* Add code to save profiling data for thread profile-guided
|
||||
optimization. See :ref:`Thread PGO`.
|
||||
* In multi-threaded models, add code to record the start and end time of each
|
||||
macro-task across a number of calls to eval. (What is a macro-task? See the
|
||||
Verilator internals document (:file:`docs/internals.rst` in the
|
||||
distribution.)
|
||||
|
||||
The :command:`verilator_gantt` program may then be run to transform the
|
||||
saved profiling file into a nicer visual format and produce some related
|
||||
@@ -406,8 +405,8 @@ others as they prove beneficial.
|
||||
Thread Profile-Guided Optimization
|
||||
----------------------------------
|
||||
|
||||
Verilator supports thread profile-guided optimization (Thread PGO) to
|
||||
improve multithreaded performance.
|
||||
Verilator supports profile-guided optimization (Verilation) of multi-threaded
|
||||
models (Thread PGO) to improve performance.
|
||||
|
||||
When using multithreading, Verilator computes how long macro tasks take and
|
||||
tries to balance those across threads. (What is a macro-task? See the
|
||||
@@ -417,13 +416,14 @@ balanced, leading to decreased performance. Thread PGO allows collecting
|
||||
profiling data to replace the estimates and better optimize these
|
||||
decisions.
|
||||
|
||||
To use Thread PGO, Verilate the model with the :vlopt:`--prof-threads`
|
||||
option.
|
||||
To use Thread PGO, Verilate the model with the :vlopt:`--prof-pgo` option. This
|
||||
will code to the verilated model to save profiling data for profile-guided
|
||||
optimization.
|
||||
|
||||
Run the model executable. When the executable exits, it will create a
|
||||
profile.vlt file.
|
||||
|
||||
Rerun Verilator, optionally omitting the :vlopt:`--prof-threads` option,
|
||||
Rerun Verilator, optionally omitting the :vlopt:`--prof-pgo` option,
|
||||
and adding the profile.vlt generated earlier to the command line.
|
||||
|
||||
Note there is no Verilator equivalent to GCC's --fprofile-use. Verilator's
|
||||
@@ -433,8 +433,8 @@ directly without any prefix.
|
||||
If results from multiple simulations are to be used in generating the
|
||||
optimization, multiple simulation's profile.vlt may be concatenated
|
||||
externally, or each of the files may be fed as separate command line
|
||||
options into Verilator. Verilator will simply sum the profile results, so
|
||||
a longer running test will have proportionally more weight for optimization
|
||||
options into Verilator. Verilator will sum the profile results, so a
|
||||
longer running test will have proportionally more weight for optimization
|
||||
than a shorter running test.
|
||||
|
||||
If you provide any profile feedback data to Verilator, and it cannot use
|
||||
|
||||
+18
-12
@@ -68,7 +68,7 @@ multi-core SoC.
|
||||
|
||||
Verilator is run in hierarchical mode on the whole SoC. Verilator will
|
||||
make two models, one for the CPU hierarchy block, and one for the SoC. The
|
||||
Verialted code for the SoC will automatically call the CPU Verilated model.
|
||||
Verilated code for the SoC will automatically call the CPU Verilated model.
|
||||
|
||||
The current hierarchical Verilation is based on :vlopt:`--lib-create`. Each
|
||||
hierarchy block is Verilated into a library. User modules of the hierarchy
|
||||
@@ -113,6 +113,8 @@ Hierarchy blocks have some limitations including:
|
||||
hierarchical model and pass up into another hierarchical model or the top
|
||||
module.
|
||||
|
||||
* Delays are not allowed in hierarchy blocks.
|
||||
|
||||
But, the following usage is supported:
|
||||
|
||||
* Nested hierarchy blocks. A hierarchy block may instantiate other
|
||||
@@ -221,9 +223,13 @@ model, it may be beneficial to performance to adjust the
|
||||
influences the partitioning of the model by adjusting the assumed execution
|
||||
time of DPI imports.
|
||||
|
||||
The :vlopt:`--trace-threads` options can be used to produce trace dumps
|
||||
using multiple threads. If :vlopt:`--trace-threads` is set without
|
||||
:vlopt:`--threads`, then :vlopt:`--trace-threads` will imply
|
||||
When using :vlopt:`--trace` to perform VCD tracing, the VCD trace
|
||||
construction is parallelized using the same number of threads as specified
|
||||
with :vlopt:`--threads`, and is executed on the same thread pool as the model.
|
||||
|
||||
The :vlopt:`--trace-threads` options can be used with :vlopt:`--trace-fst`
|
||||
to offload FST tracing using multiple threads. If :vlopt:`--trace-threads` is
|
||||
given without :vlopt:`--threads`, then :vlopt:`--trace-threads` will imply
|
||||
:vlopt:`--threads 1 <--threads>`, i.e.: the support libraries will be
|
||||
thread safe.
|
||||
|
||||
@@ -231,12 +237,12 @@ With :vlopt:`--trace-threads 0 <--trace-threads>`, trace dumps are produced
|
||||
on the main thread. This again gives the highest single thread performance.
|
||||
|
||||
With :vlopt:`--trace-threads {N} <--trace-threads>`, where N is at least 1,
|
||||
N additional threads will be created and managed by the trace files (e.g.:
|
||||
VerilatedVcdC or VerilatedFstC), to generate the trace dump. The main
|
||||
thread will be released to proceed with execution as soon as possible,
|
||||
though some blocking of the main thread is still necessary while capturing
|
||||
the trace. Different trace formats can utilize a various number of
|
||||
threads. See the :vlopt:`--trace-threads` option.
|
||||
up to N additional threads will be created and managed by the trace files
|
||||
(e.g.: VerilatedFstC), to offload construction of the trace dump. The main
|
||||
thread will be released to proceed with execution as soon as possible, though
|
||||
some blocking of the main thread is still necessary while capturing the
|
||||
trace. FST tracing can utilize up to 2 offload threads, so there is no use
|
||||
of setting :vlopt:`--trace-threads` higher than 2 at the moment.
|
||||
|
||||
When running a multithreaded model, the default Linux task scheduler often
|
||||
works against the model, by assuming threads are short lived, and thus
|
||||
@@ -265,7 +271,7 @@ This will limit memory to socket 0, and threads to cores 0, 1, 2, 3,
|
||||
(presumably on socket 0) optimizing performance. Of course this must be
|
||||
adjusted if you want another simulator using e.g. socket 1, or if you
|
||||
Verilated with a different number of threads. To see what CPUs are
|
||||
actually used, use :vlopt:`--prof-threads`.
|
||||
actually used, use :vlopt:`--prof-exec`.
|
||||
|
||||
|
||||
Multithreaded Verilog and Library Support
|
||||
@@ -441,7 +447,7 @@ SystemC include directories and link to the SystemC libraries.
|
||||
|
||||
.. describe:: TRACE_THREADS
|
||||
|
||||
Optional. Generated multi-threaded trace dumping, same as
|
||||
Optional. Generated multi-threaded FST trace dumping, same as
|
||||
"--trace-threads".
|
||||
|
||||
.. describe:: TOP_MODULE
|
||||
|
||||
+128
-66
@@ -5,6 +5,7 @@
|
||||
Errors and Warnings
|
||||
*******************
|
||||
|
||||
.. _Disabling Warnings:
|
||||
|
||||
Disabling Warnings
|
||||
==================
|
||||
@@ -12,7 +13,7 @@ Disabling Warnings
|
||||
Warnings may be disabled in multiple ways:
|
||||
|
||||
#. Disable the warning in the source code. When the warning is printed it
|
||||
will include a warning code. Simply surround the offending line with a
|
||||
will include a warning code. Surround the offending line with a
|
||||
:code:`/*verilator&32;lint_off*/` and :code:`/*verilator&32;lint_on*/`
|
||||
metacomment pair:
|
||||
|
||||
@@ -111,6 +112,8 @@ List Of Warnings
|
||||
simulators, however at one point this was a common style so disabled by
|
||||
default as a code style warning.
|
||||
|
||||
This warning is issued only if Verilator is run with :vlopt:`--no-timing`.
|
||||
|
||||
|
||||
.. option:: ASSIGNIN
|
||||
|
||||
@@ -162,7 +165,7 @@ List Of Warnings
|
||||
always @(posedge clk) foo[0] <= ...
|
||||
always_comb foo[1] = ...
|
||||
|
||||
Simply use a different register for the flop:
|
||||
Instead use a different register for the flop:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
@@ -284,7 +287,7 @@ List Of Warnings
|
||||
|
||||
.. TODO better example
|
||||
|
||||
Warns that it is simply better style to use casez, and "?" in place of
|
||||
Warns that it is better style to use casez, and "?" in place of
|
||||
"x"'s. See
|
||||
`http://www.sunburst-design.com/papers/CummingsSNUG1999Boston_FullParallelCase_rev1_1.pdf
|
||||
<http://www.sunburst-design.com/papers/CummingsSNUG1999Boston_FullParallelCase_rev1_1.pdf>`_
|
||||
@@ -315,16 +318,16 @@ List Of Warnings
|
||||
|
||||
.. option:: CLKDATA
|
||||
|
||||
.. TODO better example
|
||||
Historical, never issued since version 5.000.
|
||||
|
||||
Warns that clock signal is mixed used with/as data signal. The checking
|
||||
for this warning is enabled only if user has explicitly marked some
|
||||
signal as clocker using command line option or in-source meta comment
|
||||
(see :vlopt:`--clk`).
|
||||
Warned that clock signal was mixed used with/as data signal. The
|
||||
checking for this warning was enabled only if user has explicitly marked
|
||||
some signal as clocker using command line option or in-source meta
|
||||
comment (see :vlopt:`--clk`).
|
||||
|
||||
The warning can be disabled without affecting the simulation result. But
|
||||
it is recommended to check the warning as this may degrade the
|
||||
performance of the Verilated model.
|
||||
The warning could be disabled without affecting the simulation
|
||||
result. But it was recommended to check the warning as it may have
|
||||
degraded the performance of the Verilated model.
|
||||
|
||||
|
||||
.. option:: CMPCONST
|
||||
@@ -447,7 +450,7 @@ List Of Warnings
|
||||
p0();
|
||||
endmodule
|
||||
|
||||
Other tools with similar warnings: Veriable's forbid_defparam_rule.
|
||||
Other tools with similar warnings: Verible's forbid_defparam_rule.
|
||||
|
||||
|
||||
.. option:: DEPRECATED
|
||||
@@ -462,15 +465,11 @@ List Of Warnings
|
||||
|
||||
.. option:: DETECTARRAY
|
||||
|
||||
.. TODO better example
|
||||
Historical, never issued since version 3.862.
|
||||
|
||||
Error when Verilator tries to deal with a combinatorial loop that could
|
||||
not be flattened, and which involves a datatype which Verilator cannot
|
||||
handle, such as an unpacked struct or a large unpacked array. This
|
||||
typically occurs when :vlopt:`-Wno-UNOPTFLAT <UNOPTFLAT>` has been used
|
||||
to override an UNOPTFLAT warning (see below).
|
||||
|
||||
The solution is to break the loop, as described for UNOPTFLAT.
|
||||
Was an error when Verilator tried to deal with a combinatorial loop that
|
||||
could not be flattened, and which involves a datatype which Verilator
|
||||
could not handle, such as an unpacked struct or a large unpacked array.
|
||||
|
||||
|
||||
.. option:: DIDNOTCONVERGE
|
||||
@@ -492,7 +491,7 @@ List Of Warnings
|
||||
passing. Thus to prevent an infinite loop, the Verilated executable
|
||||
gives the DIDNOTCONVERGE error.
|
||||
|
||||
To debug this, first review any UNOPT or UNOPTFLAT warnings that were
|
||||
To debug this, first review any UNOPTFLAT warnings that were
|
||||
ignored. Though typically it is safe to ignore UNOPTFLAT (at a
|
||||
performance cost), at the time of issuing a UNOPTFLAT Verilator did not
|
||||
know if the logic would eventually converge and assumed it would.
|
||||
@@ -516,9 +515,10 @@ List Of Warnings
|
||||
constructs (e.g. always_ff and always_latch).
|
||||
|
||||
Another way DIDNOTCONVERGE may occur is if # delays are used to generate
|
||||
clocks. Verilator ignores the delays and gives an :option:`ASSIGNDLY`
|
||||
or :option:`STMTDLY` warning. If these were suppressed, due to the
|
||||
absense of the delay, the code may now oscillate.
|
||||
clocks if Verilator is run with :vlopt:`--no-timing`. In this mode,
|
||||
Verilator ignores the delays and gives an :option:`ASSIGNDLY` or
|
||||
:option:`STMTDLY` warning. If these were suppressed, due to the absence of
|
||||
the delay, the code may now oscillate.
|
||||
|
||||
Finally, rare, more difficult cases can be debugged like a C++ program;
|
||||
either enter :command:`gdb` and use its tracing facilities, or edit the
|
||||
@@ -528,7 +528,7 @@ List Of Warnings
|
||||
.. option:: ENDCAPSULATED
|
||||
|
||||
Warns that a class member is declared is local or protected, but is
|
||||
being accessed from outside that class (if local) or a derrived class
|
||||
being accessed from outside that class (if local) or a derived class
|
||||
(if protected).
|
||||
|
||||
Ignoring this warning will only suppress the lint check, it will
|
||||
@@ -589,9 +589,10 @@ List Of Warnings
|
||||
|
||||
.. option:: GENCLK
|
||||
|
||||
Deprecated and no longer used as a warning. Used to indicate that the
|
||||
specified signal was is generated inside the model, and also being used
|
||||
as a clock.
|
||||
Historical, never issued since version 5.000.
|
||||
|
||||
Indicated that the specified signal was generated inside the model, and
|
||||
was also being used as a clock.
|
||||
|
||||
|
||||
.. option:: HIERBLOCK
|
||||
@@ -653,12 +654,12 @@ List Of Warnings
|
||||
|
||||
.. option:: IMPERFECTSCH
|
||||
|
||||
.. TODO better example
|
||||
Historical, never issued since version 5.000.
|
||||
|
||||
Warns that the scheduling of the model is not absolutely perfect, and
|
||||
Warned that the scheduling of the model is not absolutely perfect, and
|
||||
some manual code edits may result in faster performance. This warning
|
||||
defaults to off, is not part of -Wall, and must be turned on explicitly
|
||||
before the top module statement is processed.
|
||||
defaulted to off, was not part of -Wall, and had to be turned on
|
||||
explicitly before the top module statement is processed.
|
||||
|
||||
|
||||
.. option:: IMPLICIT
|
||||
@@ -730,9 +731,9 @@ List Of Warnings
|
||||
Warns that a while or for statement has a condition that is always true.
|
||||
and thus results in an infinite loop if the statement ever executes.
|
||||
|
||||
This might be unintended behavior if the loop body contains statements
|
||||
that in other simulators would make time pass, which Verilator is
|
||||
ignoring due to e.g. ``STMTDLY`` warnings being disabled.
|
||||
This might be unintended behavior if Verilator is run with
|
||||
:vlopt:`--no-timing` and the loop body contains statements that would make
|
||||
time pass otherwise.
|
||||
|
||||
Ignoring this warning will only suppress the lint check, it will
|
||||
simulate correctly (i.e. hang due to the infinite loop).
|
||||
@@ -793,6 +794,16 @@ List Of Warnings
|
||||
simulate correctly.
|
||||
|
||||
|
||||
.. option:: MINTYPMAX
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
#(3:5:8) clk = ~clk;
|
||||
|
||||
Warns that minimum, typical, and maximum delay expressions are currently
|
||||
unsupported. Only the typical delay value is used by Verilator.
|
||||
|
||||
|
||||
.. option:: MODDUP
|
||||
|
||||
.. TODO better example
|
||||
@@ -863,6 +874,13 @@ List Of Warnings
|
||||
input.
|
||||
|
||||
|
||||
.. option:: NEEDTIMINGOPT
|
||||
|
||||
Error when a timing-related construct, such as an event control or delay,
|
||||
has been encountered, without specifying how Verilator should handle it
|
||||
(neither :vlopt:`--timing` nor :vlopt:`--no-timing` option was provided).
|
||||
|
||||
|
||||
.. option:: NOLATCH
|
||||
|
||||
.. TODO better example
|
||||
@@ -875,6 +893,13 @@ List Of Warnings
|
||||
simulate correctly.
|
||||
|
||||
|
||||
.. option:: NOTIMING
|
||||
|
||||
Error when a timing-related construct that requires :vlopt:`--timing` has
|
||||
been encountered. Issued only if Verilator is run with the
|
||||
:vlopt:`--no-timing` option.
|
||||
|
||||
|
||||
.. option:: NULLPORT
|
||||
|
||||
Warns that a null port was detected in the module definition port
|
||||
@@ -924,7 +949,7 @@ List Of Warnings
|
||||
simulate correctly.
|
||||
|
||||
Other tools with similar warnings: Icarus Verilog's portbind, "warning:
|
||||
Instantiating module ... with dangling impot port (...)". Slang's
|
||||
Instantiating module ... with dangling input port (...)". Slang's
|
||||
unconnected-port, "port '...' has no connection".
|
||||
|
||||
|
||||
@@ -1112,6 +1137,16 @@ List Of Warnings
|
||||
"Duplicate macro arguments with name".
|
||||
|
||||
|
||||
.. option:: RISEFALLDLY
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
and #(1,2,3) AND (out, a, b);
|
||||
|
||||
Warns that rising, falling, and turn-off delays are currently unsupported.
|
||||
The first (rising) delay is used for all cases.
|
||||
|
||||
|
||||
.. option:: SELRANGE
|
||||
|
||||
Warns that a selection index will go out of bounds.
|
||||
@@ -1211,13 +1246,13 @@ List Of Warnings
|
||||
|
||||
.. include:: ../../docs/gen/ex_STMTDLY_msg.rst
|
||||
|
||||
This is a warning because Verilator does not support delayed statements.
|
||||
It will simply ignore all such delays. In many cases ignoring a delay
|
||||
might be harmless, but if the delayed statement is, as in this example,
|
||||
used to cause some important action at a later time, it might be an
|
||||
This warning is issued only if Verilator is run with :vlopt:`--no-timing`.
|
||||
All delays on statements are ignored in this mode. In many cases ignoring a
|
||||
delay might be harmless, but if the delayed statement is, as in this
|
||||
example, used to cause some important action at a later time, it might be an
|
||||
important difference.
|
||||
|
||||
Some possible work arounds:
|
||||
Some possible workarounds:
|
||||
|
||||
* Move the delayed statement into the C++ wrapper file, where the
|
||||
stimulus and clock generation can be done in C++.
|
||||
@@ -1225,6 +1260,8 @@ List Of Warnings
|
||||
* Convert the statement into a FSM, or other statement that tests
|
||||
against $time.
|
||||
|
||||
* Run Verilator with :vlopt:`--timing`.
|
||||
|
||||
|
||||
.. option:: SYMRSVDWORD
|
||||
|
||||
@@ -1355,23 +1392,12 @@ List Of Warnings
|
||||
|
||||
.. option:: UNOPT
|
||||
|
||||
.. TODO better example
|
||||
Historical, never issued since version 5.000.
|
||||
|
||||
Warns that due to some construct, optimization of the specified signal
|
||||
or block is disabled. The construct should be cleaned up to improve
|
||||
simulation performance.
|
||||
Warned that due to some construct, optimization of the specified signal
|
||||
or block was disabled.
|
||||
|
||||
A less obvious case of this is when a module instantiates two
|
||||
submodules. Inside submodule A, signal I is input and signal O is
|
||||
output. Likewise in submodule B, signal O is an input and I is an
|
||||
output. A loop exists and a UNOPT warning will result if AI & AO both
|
||||
come from and go to combinatorial blocks in both submodules, even if
|
||||
they are unrelated always blocks. This affects performance because
|
||||
Verilator would have to evaluate each submodule multiple times to
|
||||
stabilize the signals crossing between the modules.
|
||||
|
||||
Ignoring this warning will only slow simulations, it will simulate
|
||||
correctly.
|
||||
Ignoring this warning only slowed simulations, it simulated correctly.
|
||||
|
||||
|
||||
.. option:: UNOPTFLAT
|
||||
@@ -1385,10 +1411,6 @@ List Of Warnings
|
||||
performance; two times better performance may be possible by fixing
|
||||
these warnings.
|
||||
|
||||
Unlike the ``UNOPT`` warning, this occurs after flattening the netlist,
|
||||
and indicates a more basic problem, as the less obvious case described
|
||||
under ``UNOPT`` does not apply.
|
||||
|
||||
Often UNOPTFLAT is caused by logic that isn't truly circular as viewed by
|
||||
synthesis which analyzes interconnection per-bit, but is circular to
|
||||
simulation which analyzes per-bus.
|
||||
@@ -1441,15 +1463,15 @@ List Of Warnings
|
||||
the conflict. If you run with `--report-unoptflat` Verilator will
|
||||
suggest possible candidates for :option:`/*verilator&32;split_var*/`.
|
||||
|
||||
The UNOPTFLAT warning may also be due to clock enables, identified from
|
||||
the reported path going through a clock gating instance. To fix these,
|
||||
use the clock_enable meta comment described above.
|
||||
|
||||
The UNOPTFLAT warning may also occur where outputs from a block of logic
|
||||
are independent, but occur in the same always block. To fix this, use
|
||||
the :option:`/*verilator&32;isolate_assignments*/` metacomment described
|
||||
above.
|
||||
|
||||
Prior to version 5.000, the UNOPTFLAT warning may also have been due to
|
||||
clock enables, identified from the reported path going through a clock
|
||||
gating instance. To fix these, the clock_enable meta comment was used.
|
||||
|
||||
To assist in resolving UNOPTFLAT, the option :vlopt:`--report-unoptflat`
|
||||
can be used, which will provide suggestions for variables that can be
|
||||
split up, and a graph of all the nodes connected in the loop. See the
|
||||
@@ -1506,7 +1528,7 @@ List Of Warnings
|
||||
Error that a construct might be legal according to IEEE but is not
|
||||
currently supported by Verilator.
|
||||
|
||||
A typical workaround is to recode the construct into a simpler and more
|
||||
A typical workaround is to rewrite the construct into a simpler and more
|
||||
common alternative language construct.
|
||||
|
||||
Alternatively, check if the construct is supported by other tools, and
|
||||
@@ -1520,9 +1542,31 @@ List Of Warnings
|
||||
|
||||
.. option:: UNUSED
|
||||
|
||||
Disabling/enabling UNUSED is equivalent to disabling/enabling the
|
||||
`UNUSEDGENVAR`, `UNUSEDPARAM` and `UNUSEDSIGNAL` warnings.
|
||||
|
||||
Never issued since version 5.000. Historically warned that a variable,
|
||||
parameter, or signal was unused.
|
||||
|
||||
.. option:: UNUSEDGENVAR
|
||||
|
||||
.. TODO better example
|
||||
|
||||
Warns that the specified signal or parameter is never used/consumed.
|
||||
Warns that the specified genvar is never used/consumed.
|
||||
|
||||
|
||||
.. option:: UNUSEDPARAM
|
||||
|
||||
.. TODO better example
|
||||
|
||||
Warns that the specified parameter is never used/consumed.
|
||||
|
||||
|
||||
.. option:: UNUSEDSIGNAL
|
||||
|
||||
.. TODO better example
|
||||
|
||||
Warns that the specified signal is never used/consumed.
|
||||
Verilator is fairly liberal in the usage calculations; making a signal
|
||||
public, a signal matching the :vlopt:`--unused-regexp` option (default
|
||||
"\*unused\*" or accessing only a single array element marks the entire
|
||||
@@ -1635,6 +1679,16 @@ List Of Warnings
|
||||
To resolve, rename the variable to a unique name.
|
||||
|
||||
|
||||
.. option:: WAITCONST
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
wait(0); // Blocks forever
|
||||
|
||||
Warns that a `wait` statement awaits a constant condition, which means it
|
||||
either blocks forever or never blocks.
|
||||
|
||||
|
||||
.. option:: WIDTH
|
||||
|
||||
Warns that based on width rules of Verilog:
|
||||
@@ -1702,3 +1756,11 @@ List Of Warnings
|
||||
The correct fix is to either size the 1 (:code:`32'h1`), or add the
|
||||
width to the parameter definition (:code:`parameter [31:0]`), or add the
|
||||
width to the parameter usage (:code:`{PAR[31:0], PAR[31:0]}`).
|
||||
|
||||
|
||||
.. option:: ZERODLY
|
||||
|
||||
Warns that `#0` delays do not schedule the process to be resumed in the
|
||||
Inactive region. Such processes do get resumed in the same time slot
|
||||
somewhere in the Active region. Issued only if Verilator is run with the
|
||||
:vlopt:`--timing` option.
|
||||
|
||||
+682
-23
@@ -96,10 +96,11 @@ this.
|
||||
Each ``AstNode`` has pointers to up to four children, accessed by the
|
||||
``op1p`` through ``op4p`` methods. These methods are then abstracted in a
|
||||
specific Ast\* node class to a more specific name. For example with the
|
||||
``AstIf`` node (for ``if`` statements), ``ifsp`` calls ``op2p`` to give the
|
||||
``AstIf`` node (for ``if`` statements), ``thensp`` calls ``op2p`` to give the
|
||||
pointer to the AST for the "then" block, while ``elsesp`` calls ``op3p`` to
|
||||
give the pointer to the AST for the "else" block, or NULL if there is not
|
||||
one.
|
||||
one. These accessors are automatically generated by ``astgen`` after
|
||||
parsing the ``@astgen`` directives in the specific ``AstNode`` subclasses.
|
||||
|
||||
``AstNode`` has the concept of a next and previous AST - for example the
|
||||
next and previous statements in a block. Pointers to the AST for these
|
||||
@@ -183,6 +184,568 @@ A number of predefined derived algorithm classes and access methods are
|
||||
provided and documented in ``V3GraphAlg.cpp``.
|
||||
|
||||
|
||||
``DfgGraph``
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The data-flow graph based combinational logic optimizer (DFG optimizer)
|
||||
converts an ``AstModule`` into a ``DfgGraph``. The graph represents the
|
||||
combinational equations (~continuous assignments) in the module, and for the
|
||||
duration of the DFG passes, it takes over the role of the represented
|
||||
``AstModule``. The ``DfgGraph`` keeps holds of the represented ``AstModule``,
|
||||
and the ``AstModule`` retains all other logic that is not representable as a
|
||||
data-flow graph. At the end of optimization, the combinational logic
|
||||
represented by the ``DfgGraph`` is converted back into AST form and is
|
||||
re-inserted into the corresponding ``AstModule``. The ``DfgGraph`` is distinct
|
||||
from ``V3Graph`` for efficiency and other desirable properties which make
|
||||
writing DFG passes easier.
|
||||
|
||||
|
||||
``DfgVertex``
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The ``DfgGraph`` represents combinational logic equations as a graph of
|
||||
``DfgVertex`` vertices. Each sub-class of ``DfgVertex`` corresponds to an
|
||||
expression (a sub-class of ``AstNodeMath``), a constanat, or a variable
|
||||
reference. LValues and RValues referencing the same storage location are
|
||||
represented by the same ``DfgVertex``. Consumers of such vertices read as the
|
||||
LValue, writers of such vertices write the RValue. The bulk of the final
|
||||
``DfgVertex`` sub-classes are generated by ``astgen`` from the corresponding
|
||||
``AstNode`` definitions.
|
||||
|
||||
|
||||
Scheduling
|
||||
----------
|
||||
|
||||
Verilator implements the Active and NBA regions of the SystemVerilog scheduling
|
||||
model as described in IEEE 1800-2017 chapter 4, and in particular sections
|
||||
4.5 and Figure 4.1. The static (verilation time) scheduling of SystemVerilog
|
||||
processes is performed by code in the ``V3Sched`` namespace. The single
|
||||
entry-point to the scheduling algorithm is ``V3Sched::schedule``. Some
|
||||
preparatory transformations important for scheduling are also performed in
|
||||
``V3Active`` and ``V3ActiveTop``. High level evaluation functions are
|
||||
constructed by ``V3Order``, which ``V3Sched`` invokes on subsets of the logic
|
||||
in the design.
|
||||
|
||||
Scheduling deals with the problem of evaluating 'logic' in the correct order
|
||||
and the correct number of times in order to compute the correct state of the
|
||||
SystemVerilog program. Throughout this section, we use the term 'logic' to
|
||||
refer to all SystemVerilog constructs that describe the evolution of the state
|
||||
of the program. In particular, all SystemVerilog processes and continuous
|
||||
assignments are considered 'logic', but not for example variable definitions
|
||||
without initialization or other miscellaneous constructs.
|
||||
|
||||
|
||||
Classes of logic
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
The first step in the scheduling algorithm is to gather all the logic present
|
||||
in the design, and classify it based on the conditions under which the logic
|
||||
needs to be evaluated.
|
||||
|
||||
The classes of logic we distinguish between are:
|
||||
|
||||
- SystemVerilog ``initial`` processes, that need to be executed once at
|
||||
startup.
|
||||
|
||||
- Static variable initializers. These are a separate class as they need to be
|
||||
executed before ``initial`` processes.
|
||||
|
||||
- SystemVerilog ``final`` processes.
|
||||
|
||||
- Combinational logic. Any process or construct that has an implicit
|
||||
sensitivity list with no explicit sensitivities is considered 'combinational'
|
||||
logic. This includes among other things, ``always @*`` and ``always_comb``
|
||||
processes, and continuous assignments. Verilator also converts some other
|
||||
``always`` processes to combinational logic in ``V3Active`` as described
|
||||
below.
|
||||
|
||||
- Clocked logic. Any process or construct that has an explicit sensitivity
|
||||
list, with no implicit sensitivities is considered 'clocked' (or
|
||||
'sequential') logic. This includes among other things ``always`` and
|
||||
``always_ff`` processes with an explicit sensitivity list.
|
||||
|
||||
Note that the distinction between clocked logic and combinational logic is only
|
||||
important for the scheduling algorithm within Verilator as we handle the two
|
||||
classes differently. It is possible to convert clocked logic into combinational
|
||||
logic if the explicit sensitivity list of the clocked logic is the same as the
|
||||
implicit sensitivity list of the equivalent combinational logic would be. The
|
||||
canonical examples are: ``always @(a) x = a;``, which is considered to be
|
||||
clocked logic by Verilator, and the equivalent ``assign x = a;``, which is
|
||||
considered to be combinational logic. ``V3Active`` in fact converts all clocked
|
||||
logic to combinational logic whenever possible, as this provides advantages for
|
||||
scheduling as described below.
|
||||
|
||||
There is also a 'hybrid' logic class, which has both explicit and implicit
|
||||
sensitivities. This kind of logic does not arise from a SystemVerilog
|
||||
construct, but is created during scheduling to break combinational cycles.
|
||||
Details of this process and the hybrid logic class are described below.
|
||||
|
||||
|
||||
Scheduling of simple classes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
SystemVerilog ``initial`` and ``final`` blocks can be scheduled (executed) in an
|
||||
arbitrary order.
|
||||
|
||||
Static variable initializers need to be executed in source code order in case
|
||||
there is a dependency between initializers, but the ordering of static variable
|
||||
initialization is otherwise not defined by the SystemVerilog standard
|
||||
(particularly, in the presence of hierarchical references in static variable
|
||||
initializers).
|
||||
|
||||
The scheduling algorithm handles all three of these classes the same way and
|
||||
schedules the logic in these classes in source code order. This step yields the
|
||||
``_eval_static``, ``_eval_initial`` and ``_eval_final`` functions which execute
|
||||
the corresponding logic constructs.
|
||||
|
||||
|
||||
Scheduling of clocked and combinational logic
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For performance, clocked and combinational logic needs to be ordered.
|
||||
Conceptually this minimizes the iterations through the evaluation loop
|
||||
presented in the reference algorithm in the SystemVerilog standard (IEEE
|
||||
1800-2017 section 4.5), by evaluating logic constructs in data-flow order.
|
||||
Without going into a lot of detail here, accept that well thought out ordering
|
||||
is crucial to good simulation performance, and also enables further
|
||||
optimizations later on.
|
||||
|
||||
At the highest level, ordering is performed by ``V3Order::order``, which is
|
||||
invoked by ``V3Sched::schedule`` on various subsets of the combinational and
|
||||
clocked logic as described below. The important thing to highlight now is that
|
||||
``V3Order::order`` operates by assuming that the state of all variables driven
|
||||
by combinational logic are consistent with that combinational logic. While this
|
||||
might seem subtle, it is very important, so here is an example:
|
||||
|
||||
::
|
||||
always_comb d = q + 2;
|
||||
always @(posedge clock) q <= d;
|
||||
|
||||
|
||||
During ordering, ``V3Order`` will assume that ``d`` equals ``q + 2`` at the
|
||||
beginning of an evaluation step. As a result it will order the clocked logic
|
||||
first, and all downstream combinational logic (like the assignment to ``d``)
|
||||
will execute after the clocked logic that drives inputs to the combinational
|
||||
logic, in data-flow (or dependency) order. At the end of the evaluation step,
|
||||
this ordering restores the invariant that variables driven by combinational
|
||||
logic are consistent with that combinational logic (i.e.: the circuit is in a
|
||||
settled/steady state).
|
||||
|
||||
One of the most important optimizations for performance is to only evaluate
|
||||
combinational logic, if its inputs might have changed. For example, there is no
|
||||
point in evaluating the above assignment to ``d`` on a negative edge of the
|
||||
clock signal. Verilator does this by pushing the combinational logic into the
|
||||
same (possibly multiple) event domains as the logic driving the inputs to that
|
||||
combinational logic, and only evaluating the combinational logic if at least
|
||||
one driving domains have been triggered. The impact of this activity gating is
|
||||
very high (observed 100x slowdown on large designs when turning it off), it is
|
||||
the reason we prefer to convert clocked logic to combinational logic in
|
||||
``V3Active`` whenever possible.
|
||||
|
||||
The ordering procedure described above works straight forward unless there are
|
||||
combinational logic constructs that are circularly dependent (a.k.a.: the
|
||||
UNOPTFLAT warning). Combinational scheduling loops can arise in sound
|
||||
(realizable) circuits as Verilator considers each SystemVerilog process as a
|
||||
unit of scheduling (albeit we do try to split processes into smaller ones to
|
||||
avoid this circularity problem whenever possible, this is not always possible).
|
||||
|
||||
|
||||
Breaking combinational loops
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Combinational loops are broken by the introduction of instances of the 'hybrid'
|
||||
logic class. As described in the previous section, combinational loops require
|
||||
iteration until the logic is settled, in order to restore the invariant that
|
||||
combinationally driven signals are consistent with the combinational logic.
|
||||
|
||||
To achieve this, ``V3Sched::schedule`` calls ``V3Sched::breakCycles``, which
|
||||
builds a dependency graph of all combinational logic in the design, and then
|
||||
breaks all combinational cycles by converting all combinational logic that
|
||||
consumes a variable driven via a 'back-edge' into hybrid logic. Here
|
||||
'back-edge' just means a graph edge that points from a higher rank vertex to a
|
||||
lower rank vertex in some consistent ranking of the directed graph. Variables
|
||||
driven via a back-edge in the dependency graph are marked, and all
|
||||
combinational logic that depends on such variables is converted into hybrid
|
||||
logic, with the back-edge driven variables listed as explicit 'changed'
|
||||
sensitivities.
|
||||
|
||||
Hybrid logic is handled by ``V3Order`` mostly in the same way as combinational
|
||||
logic, with two exceptions:
|
||||
|
||||
- Explicit sensitivities of hybrid logic are ignored for the purposes of
|
||||
data-flow ordering with respect to other combinational or hybrid logic. I.e.:
|
||||
an explicit sensitivity suppresses the implicit sensitivity on the same
|
||||
variable. This cold also be interpreted as ordering the hybrid logic as if
|
||||
all variables listed as explicit sensitivities were substituted as constants
|
||||
with their current values.
|
||||
|
||||
- The explicit sensitivities are included as an additional driving domain of
|
||||
the logic, and also cause evaluation when triggered.
|
||||
|
||||
This means that hybrid logic is evaluated when either any of its implicit
|
||||
sensitivities might have been updated (the same way as combinational logic, by
|
||||
pushing it into the domains that write those variables), or if any of its
|
||||
explicit sensitivities are triggered.
|
||||
|
||||
The effect of this transformation is that ``V3Order`` can proceed as if there
|
||||
are no combinational cycles (or alternatively, under the assumption that the
|
||||
back-edge driven variables don't change during one evaluation pass). The
|
||||
evaluation loop invoking the ordered code, will then re-invoke it on a follow
|
||||
on iteration, if any of the explicit sensitivities of hybrid logic have
|
||||
actually changed due to the previous invocation, iterating until all the
|
||||
combinational (including hybrid) logic have settled.
|
||||
|
||||
One might wonder if there can be a race condition between clocked logic
|
||||
triggered due to a combinational signal change from the previous evaluation
|
||||
pass, and a combinational loop settling due to hybrid logic, if the clocked
|
||||
logic reads the not yet settled combinationally driven signal. Such a race is
|
||||
indeed possible, but our evaluation is consistent with the SystemVerilog
|
||||
scheduling semantics (IEEE 1800-2017 chapter 4), and therefore any program that
|
||||
exhibits such a race has non-deterministic behaviour according to the
|
||||
SystemVerilog semantics, so we accept this.
|
||||
|
||||
|
||||
Settling combinational logic after initialization
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
At the beginning of simulation, once static initializer and ``initial`` blocks
|
||||
have been executed, we need to evaluate all combinational logic, in order to
|
||||
restore the invariant utilized by ``V3Order`` that the state of all
|
||||
combinationally driven variables are consistent with the combinational logic.
|
||||
|
||||
To achieve this, we invoke ``V3Order::order`` on all of the combinational and
|
||||
hybrid logic, and iterate the resulting evaluation function until no more
|
||||
hybrid logic is triggered. This yields the `_eval_settle` function which is
|
||||
invoked at the beginning of simulation, after the `_eval_initial`.
|
||||
|
||||
|
||||
Partitioning logic for correct NBA updates
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``V3Order`` can order logic corresponding to non-blocking assignments (NBAs) to
|
||||
yield correct simulation results, as long as all the sensitivity expressions of
|
||||
clocked logic triggered in the Active scheduling region of the current time
|
||||
step are known up front. I.e.: the ordering of NBA updates is only correct if
|
||||
derived clocks that are computed in an Active region update (that is, via a
|
||||
blocking or continuous assignment) are known up front.
|
||||
|
||||
We can ensure this by partitioning the logic into two regions. Note these
|
||||
regions are a concept of the Verilator scheduling algorithm and they do not
|
||||
directly correspond to the similarly named SystemVerilog scheduling regions
|
||||
as defined in the standard:
|
||||
|
||||
- All logic (clocked, combinational and hybrid) that transitively feeds into,
|
||||
or drives, via a non-blocking or continuous assignments (or via any update
|
||||
that SystemVerilog executes in the Active scheduling region), a variable that
|
||||
is used in the explicit sensitivity list of some clocked or hybrid logic, is
|
||||
assigned to the 'act' region.
|
||||
|
||||
- All other logic is assigned to the 'nba' region.
|
||||
|
||||
For completeness, note that a subset of the 'act' region logic, specifically,
|
||||
the logic related to the pre-assignments of NBA updates (i.e.: AstAssignPre
|
||||
nodes), is handled separately, but is executed as part of the 'act' region.
|
||||
|
||||
Also note that all logic representing the committing of an NBA (i.e.: Ast*Post)
|
||||
nodes) will be in the 'nba' region. This means that the evaluation of the 'act'
|
||||
region logic will not commit any NBA updates. As a result, the 'act' region
|
||||
logic can be iterated to compute all derived clock signals up front.
|
||||
|
||||
The correspondence between the SystemVerilog Active and NBA scheduling regions,
|
||||
and the internal 'act' and 'nba' regions, is that 'act' contains all Active
|
||||
region logic that can compute a clock signal, while 'nba' contains all other
|
||||
Active and NBA region logic. For example, if the only clocks in the design are
|
||||
top level inputs, then 'act' will be empty, and 'nba' will contain the whole of
|
||||
the design.
|
||||
|
||||
The partitioning described above is performed by ``V3Sched::partition``.
|
||||
|
||||
|
||||
Replication of combinational logic
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
We will separately invoke ``V3Order::order`` on the 'act' and 'nba' region
|
||||
logic.
|
||||
|
||||
Combinational logic that reads variables driven from both 'act' and 'nba'
|
||||
region logic has the problem of needing to be re-evaluated even if only one of
|
||||
the regions updates an input variable. We could pass additional trigger
|
||||
expressions between the regions to make sure combinational logic is always
|
||||
re-evaluated, or we can replicate combinational logic that is driven from
|
||||
multiple regions, by copying it into each region that drives it. Experiments
|
||||
show this simple replication works well performance-wise (and notably
|
||||
``V3Combine`` is good at combining the replicated code), so this is what we do
|
||||
in ``V3Sched::replicateLogic``.
|
||||
|
||||
In ``V3Sched::replicateLogic``, in addition to replicating logic into the 'act'
|
||||
and 'nba' regions, we also replicate combinational (and hybrid) logic that
|
||||
depends on top level inputs. These become a separate 'ico' region (Input
|
||||
Combinational logic), which we will always evaluate at the beginning of a
|
||||
time-step to ensure the combinational invariant holds even if input signals
|
||||
have changed. Note that this eliminates the need of changing data and clock
|
||||
signals on separate evaluations, as was necessary with earlier versions of
|
||||
Verilator).
|
||||
|
||||
|
||||
Constructing the top level `_eval` function
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To construct the top level `_eval` function, which updates the state of the
|
||||
circuit to the end of the current time step, we invoke ``V3Order::order``
|
||||
separately on the 'ico', 'act' and 'nba' logic, which yields the `_eval_ico`,
|
||||
`_eval_act`, and `_eval_nba` functions. We then put these all together with the
|
||||
corresponding functions that compute the respective trigger expressions into
|
||||
the top level `_eval` function, which on the high level has the form:
|
||||
|
||||
::
|
||||
void _eval() {
|
||||
// Update combinational logic dependent on top level inptus ('ico' region)
|
||||
while (true) {
|
||||
_eval__triggers__ico();
|
||||
// If no 'ico' region trigger is active
|
||||
if (!ico_triggers.any()) break;
|
||||
_eval_ico();
|
||||
}
|
||||
|
||||
|
||||
// Iterate 'act' and 'nba' regions together
|
||||
while (true) {
|
||||
|
||||
// Iterate 'act' region, this computes all derived clocks updaed in the
|
||||
// Active scheduling region, but does not commit any NBAs that executed
|
||||
// in 'act' region logic.
|
||||
while (true) {
|
||||
_eval__triggers__act();
|
||||
// If no 'act' region trigger is active
|
||||
if (!act_triggers.any()) break;
|
||||
// Remember what 'act' triggers were active, 'nba' uses the same
|
||||
latch_act_triggers_for_nba();
|
||||
_eval_act();
|
||||
}
|
||||
|
||||
|
||||
// If no 'nba' region trigger is active
|
||||
if (!nba_triggers.any()) break;
|
||||
|
||||
// Evaluate all other Active region logic, and commti NBAs
|
||||
_eval_nba();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timing
|
||||
------
|
||||
|
||||
Timing support in Verilator utilizes C++ coroutines, which is a new feature in
|
||||
C++20. The basic idea is to represent processes and tasks that await a certain
|
||||
event or simulation time as coroutines. These coroutines get suspended at the
|
||||
await, and resumed whenever the triggering event occurs, or at the expected
|
||||
simulation time.
|
||||
|
||||
There are several runtime classes used for managing such coroutines defined in
|
||||
``verilated_timing.h`` and ``verilated_timing.cpp``.
|
||||
|
||||
``VlCoroutineHandle``
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A thin wrapper around an ``std::coroutine_handle<>``. It forces move semantics,
|
||||
destroys the coroutine if it remains suspended at the end of the design's
|
||||
lifetime, and prevents multiple ``resume`` calls in the case of
|
||||
``fork..join_any``.
|
||||
|
||||
``VlCoroutine``
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Return value of all coroutines. Together with the promise type contained
|
||||
within, it allows for chaining coroutines – resuming coroutines from up the
|
||||
call stack. The calling coroutine's handle is saved in the promise object as a
|
||||
continuation, that is, the coroutine that must be resumed after the promise's
|
||||
coroutine finishes. This is necessary as C++ coroutines are stackless, meaning
|
||||
each one is suspended independently of others in the call graph.
|
||||
|
||||
``VlDelayScheduler``
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This class manages processes suspended by delays. There is one instance of this
|
||||
class per design. Coroutines ``co_await`` this object's ``delay`` function.
|
||||
Internally, they are stored in a heap structure sorted by simulation time in
|
||||
ascending order. When ``resume`` is called on the delay scheduler, all
|
||||
coroutines awaiting the current simulation time are resumed. The current
|
||||
simulation time is retrieved from a ``VerilatedContext`` object.
|
||||
|
||||
``VlTriggerScheduler``
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This class manages processes that await events (triggers). There is one such
|
||||
object per each trigger awaited by coroutines. Coroutines ``co_await`` this
|
||||
object's ``trigger`` function. They are stored in two stages – `uncommitted`
|
||||
and `ready`. First, they land in the `uncommitted` stage, and cannot be
|
||||
resumed. The ``resume`` function resumes all coroutines from the `ready` stage
|
||||
and moves `uncommitted` coroutines into `ready`. The ``commit`` function only
|
||||
moves `uncommitted` coroutines into `ready`.
|
||||
|
||||
This split is done to avoid self-triggering and triggering coroutines multiple
|
||||
times. See the `Scheduling with timing` section for details on how this is
|
||||
used.
|
||||
|
||||
``VlDynamicTriggerScheduler``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Like ``VlTriggerScheduler``, ``VlDynamicTriggerScheduler`` manages processes
|
||||
that await triggers. However, it does not rely on triggers evaluated externally
|
||||
by the 'act' trigger eval function. Instead, it is also responsible for trigger
|
||||
evaluation. Coroutines that make use of this scheduler must adhere to a certain
|
||||
procedure:
|
||||
|
||||
::
|
||||
__Vtrigger = 0;
|
||||
<locals and inits required for trigger eval>
|
||||
while (!__Vtrigger) {
|
||||
co_await __VdynSched.evaluation();
|
||||
<pre updates>;
|
||||
__Vtrigger = <trigger eval>;
|
||||
[optionally] co_await __VdynSched.postUpdate();
|
||||
<post updates>;
|
||||
}
|
||||
co_await __VdynSched.resumption();
|
||||
|
||||
The coroutines get resumed at trigger evaluation time, evaluate their local
|
||||
triggers, optionally await the post update step, and if the trigger is set,
|
||||
await proper resumption in the 'act' eval step.
|
||||
|
||||
``VlForkSync``
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Used for synchronizing ``fork..join`` and ``fork..join_any``. Forking
|
||||
coroutines ``co_await`` its ``join`` function, and forked ones call ``done``
|
||||
when they're finished. Once the required number of coroutines (set using
|
||||
``setCounter``) finish execution, the forking coroutine is resumed.
|
||||
|
||||
Awaitable utilities
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are also two small utility awaitable types:
|
||||
|
||||
* ``VlNow`` is an awaitable that suspends and immediately resumes coroutines.
|
||||
It is used for forcing a coroutine to be moved onto the heap. See the `Forks`
|
||||
section for more detail.
|
||||
* ``VlForever`` is used for blocking a coroutine forever. See the `Timing pass`
|
||||
section for more detail.
|
||||
|
||||
Timing pass
|
||||
^^^^^^^^^^^
|
||||
|
||||
The visitor in ``V3Timing.cpp`` transforms each timing control into a ``co_await``.
|
||||
|
||||
* event controls are turned into ``co_await`` on a trigger scheduler's
|
||||
``trigger`` method. The awaited trigger scheduler is the one corresponding to
|
||||
the sentree referenced by the event control. This sentree is also referenced
|
||||
by the ``AstCAwait`` node, to be used later by the static scheduling code.
|
||||
* if an event control waits on a local variable or class member, it uses a
|
||||
local trigger which it evaluates inline. It awaits a dynamic trigger
|
||||
scheduler multiple times: for trigger evaluation, updates, and resumption.
|
||||
The dynamic trigger scheduler is responsible for resuming the coroutine at
|
||||
the correct point of evaluation.
|
||||
* delays are turned into ``co_await`` on a delay scheduler's ``delay`` method.
|
||||
The created ``AstCAwait`` nodes also reference a special sentree related to
|
||||
delays, to be used later by the static scheduling code.
|
||||
* ``join`` and ``join_any`` are turned into ``co_await`` on a ``VlForkSync``'s
|
||||
``join`` method. Each forked process gets a ``VlForkSync::done`` call at the
|
||||
end.
|
||||
|
||||
Assignments with intra-assignment timing controls are simplified into
|
||||
assignments after those timing controls, with the LHS and RHS values evaluated
|
||||
before them and stored in temporary variables.
|
||||
|
||||
``wait`` statements are transformed into while loops that check the condition
|
||||
and then await changes in variables used in the condition. If the condition is
|
||||
always false, the ``wait`` statement is replaced by a ``co_await`` on a
|
||||
``VlForever``. This is done instead of a return in case the ``wait`` is deep in
|
||||
a call stack (otherwise the coroutine's caller would continue execution).
|
||||
|
||||
Each sub-statement of a ``fork`` is put in an ``AstBegin`` node for easier
|
||||
grouping. In a later step, each of these gets transformed into a new, separate
|
||||
function. See the `Forks` section for more detail.
|
||||
|
||||
Processes that use awaits are marked as suspendable. Later, during ``V3Sched``,
|
||||
they are transformed into coroutines. Functions that use awaits get the return
|
||||
type of ``VlCoroutine``. This immediately makes them coroutines. Note that if a
|
||||
process calls a function that is a coroutine, the call gets wrapped in an
|
||||
await, which means the process itself will be marked as suspendable. A virtual
|
||||
function is a coroutine if any of its overriding or overridden functions are
|
||||
coroutines. The visitor keeps a dependency graph of functions and processes to
|
||||
handle such cases.
|
||||
|
||||
Scheduling with timing
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Timing features in Verilator are built on top of the static scheduler. Triggers
|
||||
are used for determining which delay or trigger schedulers should resume. A
|
||||
special trigger is used for the delay scheduler. This trigger is set if there
|
||||
are any coroutines awaiting the current simulation time
|
||||
(``VlDelayScheduler::awaitingCurrentTime()``).
|
||||
|
||||
All triggers used by a suspendable process are mapped to variables written in
|
||||
that process. When ordering code using ``V3Order``, these triggers are provided
|
||||
as external domains of these variables. This ensures that the necessary
|
||||
combinational logic is triggered after a coroutine resumption.
|
||||
|
||||
There are two functions for managing timing logic called by ``_eval()``:
|
||||
|
||||
* ``_timing_commit()``, which commits all coroutines whose triggers were not set
|
||||
in the current iteration,
|
||||
* ``_timing_resume()``, which calls `resume()` on all trigger and delay
|
||||
schedulers whose triggers were set in the current iteration.
|
||||
|
||||
Thanks to this separation, a coroutine awaiting a trigger cannot be suspended
|
||||
and resumed in the same iteration, and it cannot be resumed before it suspends.
|
||||
|
||||
All coroutines are committed and resumed in the 'act' eval loop. With timing
|
||||
features enabled, the ``_eval()`` function takes this form:
|
||||
|
||||
::
|
||||
void _eval() {
|
||||
while (true) {
|
||||
_eval__triggers__ico();
|
||||
if (!ico_triggers.any()) break;
|
||||
_eval_ico();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
while (true) {
|
||||
_eval__triggers__act();
|
||||
|
||||
// Commit all non-triggered coroutines
|
||||
_timing_commit();
|
||||
|
||||
if (!act_triggers.any()) break;
|
||||
latch_act_triggers_for_nba();
|
||||
|
||||
// Resume all triggered coroutines
|
||||
_timing_resume();
|
||||
|
||||
_eval_act();
|
||||
}
|
||||
if (!nba_triggers.any()) break;
|
||||
_eval_nba();
|
||||
}
|
||||
}
|
||||
|
||||
Forks
|
||||
^^^^^
|
||||
|
||||
After the scheduling step, forks sub-statements are transformed into separate
|
||||
functions, and these functions are called in place of the sub-statements. These
|
||||
calls must be without ``co_await``, so that suspension of a forked process
|
||||
doesn't suspend the forking process.
|
||||
|
||||
In forked processes, references to local variables are only allowed in
|
||||
``fork..join``, as this is the only case that ensures the lifetime of these
|
||||
locals is at least as long as the execution of the forked processes. This is
|
||||
where ``VlNow`` is used, to ensure the locals are moved to the heap before they
|
||||
are passed by reference to the forked processes.
|
||||
|
||||
|
||||
Multithreaded Mode
|
||||
------------------
|
||||
|
||||
@@ -268,13 +831,13 @@ Estimating Logic Costs
|
||||
|
||||
To compute the cost of any given path through the graph, Verilator
|
||||
estimates an execution cost for each task. Each macro-task has an execution
|
||||
cost which is simply the sum of its tasks' costs. We assume that
|
||||
communication overhead and synchronization overhead are zero, so the cost
|
||||
of any given path through the graph is simply the sum of macro-task
|
||||
execution costs. Sarkar does almost the same thing, except that he has
|
||||
nonzero estimates for synchronization costs.
|
||||
cost which is the sum of its tasks' costs. We assume that communication
|
||||
overhead and synchronization overhead are zero, so the cost of any given
|
||||
path through the graph is the sum of macro-task execution costs. Sarkar
|
||||
does almost the same thing, except that he has nonzero estimates for
|
||||
synchronization costs.
|
||||
|
||||
Verilator's cost estimates are assigned by ``InstrCountCostVisitor``. This
|
||||
Verilator's cost estimates are assigned by ``InstrCountVisitor``. This
|
||||
class is perhaps the most fragile piece of the multithread
|
||||
implementation. It's easy to have a bug where you count something cheap
|
||||
(eg. accessing one element of a huge array) as if it were expensive (eg.
|
||||
@@ -301,7 +864,7 @@ prerequisites on other threads have finished.
|
||||
|
||||
The synchronization cost is cheap if the prereqs are done. If they're not,
|
||||
fragmentation (idle CPU cores waiting) is possible. This is the major
|
||||
source of overhead in this approach. The ``--prof-threads`` switch and the
|
||||
source of overhead in this approach. The ``--prof-exec`` switch and the
|
||||
``verilator_gantt`` script can visualize the time lost to such
|
||||
fragmentation.
|
||||
|
||||
@@ -501,22 +1064,107 @@ code:
|
||||
The ``astgen`` Script
|
||||
---------------------
|
||||
|
||||
Some of the code implementing passes is extremely repetitive, and must be
|
||||
implemented for each sub-class of ``AstNode``. However, while repetitive,
|
||||
there is more variability than can be handled in C++ macros.
|
||||
The ``astgen`` script is used to generate some of the repetitive C++ code
|
||||
related to the ``AstNode`` type hierarchy. An example is the abstract ``visit``
|
||||
methods in ``VNVisitor``. There are other uses, please see the ``*__gen*``
|
||||
files in the bulid directories and the ``astgen`` script itself for details. A
|
||||
description of the more advanced features of ``astgen`` are provided here.
|
||||
|
||||
In Verilator this is implemented by using a script, ``astgen`` to
|
||||
pre-process the C++ code. For example in ``V3Const.cpp`` this is used to
|
||||
implement the ``visit()`` functions for each binary operation using the
|
||||
``TREEOP`` macro.
|
||||
|
||||
The original C source code is transformed into C code in the ``obj_opt``
|
||||
Generating ``AstNode`` members
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some of the member s of ``AstNode`` sub-classes are generated by ``astgen``.
|
||||
These are emitted as pre-processor macro definitions, which then need to be
|
||||
added to the ``AstNode`` sub-classes they correspond to. Specifically ``class
|
||||
AstFoo`` should contain an instance of ``ASTGEN_MEMBERS_AstFoo;`` at class
|
||||
scope. The ``astgen`` script checks and errors if this is not present. The
|
||||
method generated depends on whether the class is a concrete final class, or an
|
||||
abstract ``AstNode*`` base-class, and on ``@astgen`` directives present in
|
||||
comment sections in the body of the ``AstNode`` sub-class definitions.
|
||||
|
||||
|
||||
List of ``@astgen`` directives
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``@astgen`` directives in comments contained in the body of ``AstNode``
|
||||
sub-class definitions are parsed and contribute to the code generated by
|
||||
``astgen``. The general syntax is ``@astgen <keywords> := <description>``,
|
||||
where ``<keywords>`` determines what is being defined, and ``<description>`` is
|
||||
a ``<keywords>`` dependent description of the definition. The list of
|
||||
``@astgen`` directives is as follows:
|
||||
|
||||
|
||||
``op<N>`` operand directives
|
||||
"""""""""""""""""""""""""""""
|
||||
|
||||
The ``op1``, ``op2``, ``op3`` and ``op4`` directives are used to describe the
|
||||
name and type of the up to 4 child operands of a node. The syntax of the
|
||||
``<description>`` field is ``<identifier> : <type>``, where ``<identifier>``
|
||||
will be used as the base name of the generated operand accessors, and
|
||||
``<type>`` is one of:
|
||||
|
||||
1. An ``AstNode`` sub-class, defining the operand to be of that type, always
|
||||
no-null, and with an always null ``nextp()``. That is, the child node is
|
||||
always present, and is a single ``AstNode`` (as opposed to a list).
|
||||
|
||||
2. ``Optional[<AstNode sub-class>]``. This is just like in point 1 above, but
|
||||
defines the child node to be optional, meaning it may be null.
|
||||
|
||||
3. ``List[AstNode sub-class]`` describes a list operand, which means the child
|
||||
node may have a non-null ``nextp()`` and in addition the child itself may be
|
||||
null, representing an empty list.
|
||||
|
||||
|
||||
An example of the full syntax of the directive is
|
||||
``@astgen op1 := lhsp : AstNodeMath``.
|
||||
|
||||
``astnode`` generates accessors for the child nodes based on these directives.
|
||||
For non-list children, the names of the getter and setter both are that of the
|
||||
given ``<identifier>``. For list type children, the getter is ``<identifier>``,
|
||||
and instead of the setter, there an ``add<Identifier>`` method is generated
|
||||
that appends new nodes (or lists of nodes) to the child list.
|
||||
|
||||
|
||||
``alias op<N>`` operand alias directives
|
||||
""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
If a super-class already defined a name and type for a child node using the
|
||||
``op<N>`` directive, but a more appropriate name exists in the context of a
|
||||
sub-class, then the alias directive can be used to introduce an additional name
|
||||
for the child node. The is ``alias op<N> := <identifier>`` where
|
||||
``<identifier>`` is the new name. ``op<N>`` must have been defined in some
|
||||
super-class of the current node.
|
||||
|
||||
Example: ``@astgen alias op1 := condp``
|
||||
|
||||
|
||||
Generating ``DfgVertex`` sub-classes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Most of the ``DfgVertex`` sub-classes are generated by ``astgen``, from the
|
||||
definitions of the corresponding ``AstNode`` vertices.
|
||||
|
||||
|
||||
Additional features of ``astgen``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In addition to generating ``AstNode`` members as described above,
|
||||
``astgen`` is also use to handle some of the repetitive implementation code
|
||||
that is still variable enough not to be handled in C++ macros.
|
||||
|
||||
In particular, ``astgen`` is used to pre-process some of the C++ source
|
||||
files. For example in ``V3Const.cpp``, it is used to implement the
|
||||
``visit()`` functions for each binary operation using the ``TREEOP`` macro.
|
||||
|
||||
The original C++ source code is transformed into C++ code in the ``obj_opt``
|
||||
and ``obj_dbg`` sub-directories (the former for the optimized version of
|
||||
Verilator, the latter for the debug version). So for example
|
||||
``V3Const.cpp`` into ``V3Const__gen.cpp``.
|
||||
|
||||
|
||||
Visitor Functions -----------------
|
||||
Visitor Functions
|
||||
-----------------
|
||||
|
||||
Verilator uses the "Visitor" design pattern to implement its refinement and
|
||||
optimization passes. This allows separation of the pass algorithm from the
|
||||
@@ -817,7 +1465,7 @@ which you can install using cpan.
|
||||
|
||||
There are some traps to avoid when running regression tests
|
||||
|
||||
- When checking the MANIFEST, the test will barf on unexpected code in the
|
||||
- When checking the MANIFEST, the test will fail on unexpected code in the
|
||||
Verilator tree. So make sure to keep any such code outside the tree.
|
||||
|
||||
- Not all Linux systems install Perldoc by default. This is needed for the
|
||||
@@ -1059,6 +1707,16 @@ Similarly the ``NETLIST`` has a list of modules referred to by its
|
||||
``op1p()`` pointer.
|
||||
|
||||
|
||||
.tree.dot Output
|
||||
----------------
|
||||
|
||||
``*.tree.dot`` files are dumps of the AST Tree in `Graphviz
|
||||
<https://www.graphviz.org>`__ dot format. This can be used to
|
||||
visualize the AST Tree. The vertices correspond to ``AstNode``
|
||||
instances, and the edges represent the pointers (``op1p``,
|
||||
``op2p``, etc) between the nodes.
|
||||
|
||||
|
||||
Debugging with GDB
|
||||
------------------
|
||||
|
||||
@@ -1153,8 +1811,9 @@ Generally what would you do to add a new feature?
|
||||
language and has a lot of back-and-forth with Verilator's grammar. Copy
|
||||
the appropriate rules to src/verilog.y and modify the productions.
|
||||
|
||||
4. If a new Ast type is needed, add it to V3AstNodes.h. Follow the
|
||||
convention described above about the AstNode type hierarchy.
|
||||
4. If a new Ast type is needed, add it to the appropriate V3AstNode*.h.
|
||||
Follow the convention described above about the AstNode type hierarchy.
|
||||
Ordering of definitions is enforced by ``astgen``.
|
||||
|
||||
5. Now you can run "test_regress/t/t_<newtestcase>.pl --debug" and it'll
|
||||
probably fail but you'll see a
|
||||
@@ -1183,10 +1842,10 @@ Verilator ideally would support all of IEEE, and has the goal to get close
|
||||
to full support. However the following IEEE sections and features are not
|
||||
anticipated to be ever implemented for the reasons indicated.
|
||||
|
||||
IEEE 1800-2017 3.3 recursive modules
|
||||
IEEE 1800-2017 3.3 modules within modules
|
||||
Little/no tool support, and arguably not a good practice.
|
||||
IEEE 1800-2017 6.12 "shortreal"
|
||||
Little/no tool support, and easily simply promoted to real.
|
||||
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"
|
||||
|
||||
@@ -2,15 +2,19 @@ ABCp
|
||||
Accellera
|
||||
Affe
|
||||
Aleksander
|
||||
Alexandre
|
||||
Ami
|
||||
Amir
|
||||
Anastasiadis
|
||||
Anikin
|
||||
Antmicro
|
||||
Antonin
|
||||
Antwerpen
|
||||
Arasanipalai
|
||||
Arjen
|
||||
Asciidoc
|
||||
Ashutosh
|
||||
Ast
|
||||
Atmel
|
||||
Aurelien
|
||||
Bagri
|
||||
@@ -70,6 +74,7 @@ Eda
|
||||
Eddleston
|
||||
Egbert
|
||||
Egil
|
||||
Ehab
|
||||
Eiler
|
||||
Eivind
|
||||
El
|
||||
@@ -82,6 +87,7 @@ Fabrizio
|
||||
Fekete
|
||||
Ferrandi
|
||||
Flachs
|
||||
Flavien
|
||||
Foletto
|
||||
Forker
|
||||
Francillon
|
||||
@@ -104,7 +110,10 @@ Goessling
|
||||
Gonnen
|
||||
Goorah
|
||||
Gossner
|
||||
Graphviz
|
||||
Graybeal
|
||||
Grobman
|
||||
Grulfen
|
||||
Gunter
|
||||
Guo
|
||||
Hao
|
||||
@@ -121,10 +130,12 @@ Hornung
|
||||
Hossell
|
||||
Hsu
|
||||
Hyperthreading
|
||||
Ibrahim
|
||||
Iles
|
||||
Inlines
|
||||
Inout
|
||||
Iru
|
||||
Iyer
|
||||
Iztok
|
||||
Jacko
|
||||
Jae
|
||||
@@ -134,6 +145,7 @@ Jannis
|
||||
Jens
|
||||
Jeras
|
||||
Jiuyang
|
||||
Joannou
|
||||
Joly
|
||||
Jullien
|
||||
Junji
|
||||
@@ -147,8 +159,10 @@ Kamendje
|
||||
Kandadi
|
||||
Kaplan
|
||||
Karge
|
||||
Karlsson
|
||||
Katz
|
||||
Katzman
|
||||
Kelin
|
||||
Keren
|
||||
Keyi
|
||||
Kimmitt
|
||||
@@ -161,6 +175,7 @@ Kolecki
|
||||
Koonce
|
||||
Korteland
|
||||
Koszek
|
||||
Kouping
|
||||
Kravitz
|
||||
Krolnik
|
||||
Kruse
|
||||
@@ -194,6 +209,7 @@ Mednick
|
||||
Michiels
|
||||
Microsystems
|
||||
Milanovic
|
||||
Millis
|
||||
MinW
|
||||
Mindspeed
|
||||
Miodrag
|
||||
@@ -224,12 +240,14 @@ Petr
|
||||
Piechotka
|
||||
Piersall
|
||||
Plunkett
|
||||
Popolon
|
||||
Popov
|
||||
Prabhat
|
||||
Prabhu
|
||||
Prateek
|
||||
Pre
|
||||
Preprocess
|
||||
Pretet
|
||||
Priyadharshini
|
||||
Pullup
|
||||
Pulver
|
||||
@@ -262,12 +280,15 @@ Shi
|
||||
Shinkarovsky
|
||||
Shirakawa
|
||||
Shuba
|
||||
Shunyao
|
||||
Slatter
|
||||
SoC
|
||||
Sobhan
|
||||
Sokorac
|
||||
Solaris
|
||||
Solomatnikov
|
||||
Solt
|
||||
Southwell
|
||||
Srini
|
||||
Stamness
|
||||
Stephane
|
||||
@@ -286,6 +307,7 @@ SystemVerilog
|
||||
Takatsukasa
|
||||
Tambe
|
||||
Tariq
|
||||
Tejada
|
||||
Tengstrand
|
||||
Terpstra
|
||||
Thiede
|
||||
@@ -293,8 +315,10 @@ Thierry
|
||||
Thyer
|
||||
Tichelaar
|
||||
Tomov
|
||||
Tood
|
||||
Topa
|
||||
Tota
|
||||
Trefor
|
||||
Tresidder
|
||||
Tri
|
||||
Tristate
|
||||
@@ -304,11 +328,13 @@ Ubixum
|
||||
Uints
|
||||
Undefines
|
||||
Unsized
|
||||
Urbach
|
||||
Uselib
|
||||
Usha
|
||||
Usuario
|
||||
VERILATOR
|
||||
Vasu
|
||||
Vdeeptemp
|
||||
Vdly
|
||||
Vemumtab
|
||||
Vemuri
|
||||
@@ -320,6 +346,7 @@ Verilating
|
||||
Verilation
|
||||
Verilator
|
||||
Verilog
|
||||
Vighnesh
|
||||
Viktor
|
||||
Vm
|
||||
Vukobratovic
|
||||
@@ -351,9 +378,12 @@ Zhang
|
||||
abirkmanis
|
||||
accessor
|
||||
accessors
|
||||
adrienlemasle
|
||||
agrobman
|
||||
ahouska
|
||||
al
|
||||
ala
|
||||
algrobman
|
||||
andit
|
||||
ar
|
||||
architected
|
||||
@@ -372,8 +402,13 @@ backtrace
|
||||
backtraces
|
||||
basename
|
||||
bbox
|
||||
benchmarking
|
||||
biguint
|
||||
biops
|
||||
bisonpre
|
||||
bitOpTree
|
||||
bitOpTree
|
||||
bitop
|
||||
bitstoreal
|
||||
blackbox
|
||||
bokke
|
||||
@@ -389,6 +424,7 @@ casez
|
||||
casted
|
||||
cb
|
||||
ccache
|
||||
ccall
|
||||
cdc
|
||||
ceil
|
||||
celldefine
|
||||
@@ -411,7 +447,10 @@ concat
|
||||
concats
|
||||
config
|
||||
const
|
||||
constexpr
|
||||
constpool
|
||||
coredump
|
||||
coroutine
|
||||
countbits
|
||||
countones
|
||||
cout
|
||||
@@ -426,6 +465,7 @@ da
|
||||
dat
|
||||
datadir
|
||||
datafiles
|
||||
david
|
||||
ddd
|
||||
deassign
|
||||
debugi
|
||||
@@ -433,7 +473,9 @@ defenv
|
||||
defname
|
||||
defparam
|
||||
demangling
|
||||
dep
|
||||
der
|
||||
dereference
|
||||
desassign
|
||||
destructor
|
||||
detections
|
||||
@@ -449,6 +491,7 @@ dsvf
|
||||
dtor
|
||||
dumpall
|
||||
dumpfile
|
||||
dumpi
|
||||
dumplimit
|
||||
dumpoff
|
||||
dumpon
|
||||
@@ -462,6 +505,7 @@ dumpvars
|
||||
dut
|
||||
dx
|
||||
elab
|
||||
elike
|
||||
elsif
|
||||
endcase
|
||||
endcelldefine
|
||||
@@ -480,15 +524,18 @@ enums
|
||||
env
|
||||
envvar
|
||||
eof
|
||||
errae
|
||||
erroring
|
||||
et
|
||||
eval
|
||||
evals
|
||||
exe
|
||||
executables
|
||||
expr
|
||||
extern
|
||||
fanin
|
||||
fasttrace
|
||||
fauto
|
||||
fbranch
|
||||
fclose
|
||||
fdisplay
|
||||
@@ -502,7 +549,9 @@ filesystem
|
||||
filt
|
||||
flto
|
||||
flushCall
|
||||
fno
|
||||
fopen
|
||||
forceable
|
||||
foreach
|
||||
fprintf
|
||||
fprofile
|
||||
@@ -521,6 +570,7 @@ funcs
|
||||
fwrite
|
||||
gantt
|
||||
gcc
|
||||
gcda
|
||||
gdb
|
||||
genblk
|
||||
genvar
|
||||
@@ -552,6 +602,7 @@ incdir
|
||||
includer
|
||||
inferfaces
|
||||
inhibitSim
|
||||
initarray
|
||||
initializer
|
||||
initializers
|
||||
inits
|
||||
@@ -562,6 +613,7 @@ inouts
|
||||
inserted
|
||||
instantiation
|
||||
instantiations
|
||||
intra
|
||||
iostream
|
||||
ish
|
||||
isunbounded
|
||||
@@ -570,6 +622,7 @@ killua
|
||||
lang
|
||||
lcov
|
||||
ld
|
||||
leavinel
|
||||
len
|
||||
libext
|
||||
libgoogle
|
||||
@@ -584,6 +637,7 @@ ln
|
||||
loc
|
||||
localparam
|
||||
localparams
|
||||
localtime
|
||||
logicals
|
||||
longint
|
||||
lsb
|
||||
@@ -618,6 +672,7 @@ multithreaded
|
||||
multithreading
|
||||
mutexes
|
||||
mux
|
||||
myftptoyman
|
||||
mysignal
|
||||
namespace
|
||||
nand
|
||||
@@ -642,15 +697,19 @@ onehot
|
||||
ooo
|
||||
oprofile
|
||||
oversubscription
|
||||
parallelized
|
||||
param
|
||||
parameterized
|
||||
params
|
||||
parens
|
||||
pawel
|
||||
pc
|
||||
pdf
|
||||
perf
|
||||
perftools
|
||||
pgo
|
||||
picoChip
|
||||
pinIndex
|
||||
pinout
|
||||
plusargs
|
||||
pmos
|
||||
@@ -676,9 +735,11 @@ prev
|
||||
printf
|
||||
printtimescale
|
||||
profcfunc
|
||||
profiler
|
||||
prototyptes
|
||||
ps
|
||||
pthread
|
||||
ptr
|
||||
pulldown
|
||||
pulldowns
|
||||
pullup
|
||||
@@ -703,12 +764,15 @@ regs
|
||||
reloop
|
||||
resetall
|
||||
respecified
|
||||
rodata
|
||||
rolloverSize
|
||||
rr
|
||||
rst
|
||||
runtime
|
||||
runtimes
|
||||
rw
|
||||
sVerilator
|
||||
sawatzke
|
||||
sc
|
||||
scalared
|
||||
sccanf
|
||||
@@ -716,6 +780,7 @@ seg
|
||||
setuphold
|
||||
sformat
|
||||
sformatf
|
||||
shareefj
|
||||
shortint
|
||||
shortreal
|
||||
signame
|
||||
@@ -724,6 +789,7 @@ specparam
|
||||
splitme
|
||||
spp
|
||||
sqrt
|
||||
src
|
||||
srcdir
|
||||
srcfile
|
||||
sscanf
|
||||
@@ -743,6 +809,7 @@ subcells
|
||||
subexpressions
|
||||
submodule
|
||||
submodules
|
||||
substring
|
||||
sv
|
||||
svBitVal
|
||||
svBitVecVal
|
||||
@@ -777,6 +844,7 @@ trunc
|
||||
txt
|
||||
typ
|
||||
typedef
|
||||
typedef'ed
|
||||
typedefed
|
||||
typedefs
|
||||
typename
|
||||
@@ -790,6 +858,7 @@ uniquified
|
||||
unistd
|
||||
unlink
|
||||
unlinked
|
||||
unnamedblk
|
||||
unopt
|
||||
unoptflat
|
||||
unoptimizable
|
||||
@@ -839,11 +908,14 @@ writeme
|
||||
writemem
|
||||
writememb
|
||||
writememh
|
||||
xiak
|
||||
xin
|
||||
xml
|
||||
xnor
|
||||
xout
|
||||
xuejiazidi
|
||||
yanx
|
||||
ypq
|
||||
yurivict
|
||||
zdave
|
||||
Øyvind
|
||||
|
||||
@@ -46,9 +46,6 @@ The XML document consists of 4 sections within the top level
|
||||
hierarchy. Each instance is represented with the ``<cell>`` element
|
||||
with the following attributes:
|
||||
|
||||
- ``fl`` (deprecated): The file id and line number where the module
|
||||
was instanced. Use ``loc`` instead.
|
||||
|
||||
- ``loc``: The file id, first line number, last line number, first
|
||||
column number and last column number of the identifier where the
|
||||
module was instanced, separated by commas.
|
||||
|
||||
@@ -39,30 +39,12 @@ ifneq ($(CMAKE_GT_3_8),true)
|
||||
TARGET := oldcmake
|
||||
else
|
||||
|
||||
# Test existence of SYSTEMC_INCLUDE and SYSTEMC_LIBDIR environment variabless
|
||||
ifneq (,$(SYSTEMC_INCLUDE))
|
||||
ifneq (,${SYSTEMC_LIBDIR})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
|
||||
# Test existence of SYSTEMC_ROOT environment variable
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,${SYSTEMC_ROOT})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
|
||||
# Test existence of SYSTEMC environment variable
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,${SYSTEMC})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
# Check if SC exists via a verilator call (empty if not)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
|
||||
|
||||
# Test whether SystemC is installed with CMake support
|
||||
# This will print a CMake error about processing arguments that can (currently) be ignored.
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,$(SYSTEMC_EXISTS))
|
||||
FINDSC := $(shell mkdir -p build && cd build && cmake --find-package -DNAME=SystemCLanguage -DCMAKE_USE_PTHREADS_INIT=ON -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST -DThreads_FOUND=ON)
|
||||
ifneq (,$(findstring SystemCLanguage found,$(FINDSC)))
|
||||
SYSTEMC_SET := true
|
||||
|
||||
@@ -33,5 +33,5 @@ add_executable(example ../make_tracing_c/sim_main.cpp)
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example COVERAGE TRACE
|
||||
INCLUDE_DIRS "../make_tracing_c"
|
||||
VERILATOR_ARGS -f ../make_tracing_c/input.vc -Os -x-assign 0
|
||||
VERILATOR_ARGS -f ../make_tracing_c/input.vc -x-assign fast
|
||||
SOURCES ../make_tracing_c/top.v)
|
||||
|
||||
@@ -45,7 +45,7 @@ set_property(
|
||||
# Add the Verilated circuit to the target
|
||||
verilate(example SYSTEMC COVERAGE TRACE
|
||||
INCLUDE_DIRS "../make_tracing_sc"
|
||||
VERILATOR_ARGS -f ../make_tracing_sc/input.vc -Os -x-assign 0
|
||||
VERILATOR_ARGS -f ../make_tracing_sc/input.vc -x-assign fast
|
||||
SOURCES ../make_tracing_sc/top.v)
|
||||
|
||||
verilator_link_systemc(example)
|
||||
|
||||
@@ -39,30 +39,12 @@ ifneq ($(CMAKE_GT_3_8),true)
|
||||
TARGET := oldcmake
|
||||
else
|
||||
|
||||
# Test existence of SYSTEMC_INCLUDE and SYSTEMC_LIBDIR environment variabless
|
||||
ifneq (,$(SYSTEMC_INCLUDE))
|
||||
ifneq (,${SYSTEMC_LIBDIR})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
|
||||
# Test existence of SYSTEMC_ROOT environment variable
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,${SYSTEMC_ROOT})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
|
||||
# Test existence of SYSTEMC environment variable
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,${SYSTEMC})
|
||||
SYSTEMC_SET := true
|
||||
endif
|
||||
endif
|
||||
# Check if SC exists via a verilator call (empty if not)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
|
||||
|
||||
# Test whether SystemC is installed with CMake support
|
||||
# This will print a CMake error about processing arguments that can (currently) be ignored.
|
||||
ifneq (SYSTEMC_SET, true)
|
||||
ifneq (,$(SYSTEMC_EXISTS))
|
||||
FINDSC := $(shell mkdir -p build && cd build && cmake --find-package -DNAME=SystemCLanguage -DCMAKE_USE_PTHREADS_INIT=ON -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST -DThreads_FOUND=ON)
|
||||
ifneq (,$(findstring SystemCLanguage found,$(FINDSC)))
|
||||
SYSTEMC_SET := true
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
*.dmp
|
||||
*.log
|
||||
*.csrc
|
||||
*.vcd
|
||||
obj_*
|
||||
logs
|
||||
@@ -0,0 +1,49 @@
|
||||
######################################################################
|
||||
#
|
||||
# DESCRIPTION: Verilator Example: Small Makefile
|
||||
#
|
||||
# This calls the object directory makefile. That allows the objects to
|
||||
# be placed in the "current directory" which simplifies the Makefile.
|
||||
#
|
||||
# This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
# any use, without warranty, 2020 by Wilson Snyder.
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
######################################################################
|
||||
# Check for sanity to avoid later confusion
|
||||
|
||||
ifneq ($(words $(CURDIR)),1)
|
||||
$(error Unsupported: GNU Make cannot build in directories containing spaces, build elsewhere: '$(CURDIR)')
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
|
||||
# This is intended to be a minimal example. Before copying this to start a
|
||||
# real project, it is better to start with a more complete example,
|
||||
# e.g. examples/make_tracing_c.
|
||||
|
||||
# If $VERILATOR_ROOT isn't in the environment, we assume it is part of a
|
||||
# package install, and verilator is in your path. Otherwise find the
|
||||
# binary relative to $VERILATOR_ROOT (such as when inside the git sources).
|
||||
ifeq ($(VERILATOR_ROOT),)
|
||||
VERILATOR = verilator
|
||||
else
|
||||
export VERILATOR_ROOT
|
||||
VERILATOR = $(VERILATOR_ROOT)/bin/verilator
|
||||
endif
|
||||
|
||||
default:
|
||||
@echo "-- Verilator hello-world simple binary example"
|
||||
@echo "-- VERILATE & BUILD --------"
|
||||
$(VERILATOR) --binary -j 0 top.v
|
||||
@echo "-- RUN ---------------------"
|
||||
obj_dir/Vtop
|
||||
@echo "-- DONE --------------------"
|
||||
@echo "Note: Once this example is understood, see examples/make_hello_c."
|
||||
@echo "Note: See also https://verilator.org/guide/latest/examples.html"
|
||||
|
||||
######################################################################
|
||||
|
||||
maintainer-copy::
|
||||
clean mostlyclean distclean maintainer-clean::
|
||||
-rm -rf obj_dir *.log *.dmp *.vpd core
|
||||
@@ -0,0 +1,14 @@
|
||||
// DESCRIPTION: Verilator: Verilog example module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2017 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// See also https://verilator.org/guide/latest/examples.html"
|
||||
|
||||
module top;
|
||||
initial begin
|
||||
$display("Hello World!");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -21,11 +21,14 @@ int main(int argc, char** argv, char** env) {
|
||||
// Prevent unused variable warnings
|
||||
if (false && argc && argv && env) {}
|
||||
|
||||
// Construct a VerilatedContext to hold simulation time, etc.
|
||||
VerilatedContext* contextp = new VerilatedContext;
|
||||
|
||||
// Construct the Verilated model, from Vtop.h generated from Verilating "top.v"
|
||||
Vtop* top = new Vtop;
|
||||
Vtop* top = new Vtop{contextp};
|
||||
|
||||
// Simulate until $finish
|
||||
while (!Verilated::gotFinish()) {
|
||||
while (!contextp->gotFinish()) {
|
||||
|
||||
// Evaluate model
|
||||
top->eval();
|
||||
|
||||
@@ -33,7 +33,7 @@ VERILATOR = $(VERILATOR_ROOT)/bin/verilator
|
||||
endif
|
||||
|
||||
# Check if SC exists via a verilator call (empty if not)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --getenv SYSTEMC_INCLUDE)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
|
||||
|
||||
ifneq ($(SYSTEMC_EXISTS),)
|
||||
default: run
|
||||
|
||||
@@ -33,7 +33,7 @@ VERILATOR_FLAGS =
|
||||
# Generate C++
|
||||
VERILATOR_FLAGS += -cc
|
||||
# Optimize
|
||||
VERILATOR_FLAGS += -Os -x-assign 0
|
||||
VERILATOR_FLAGS += -x-assign fast
|
||||
# Warn abount lint issues; may not want this on less solid designs
|
||||
VERILATOR_FLAGS += -Wall
|
||||
# This example does not use vl_time_stamp but rather
|
||||
|
||||
@@ -32,7 +32,7 @@ int main(int argc, char** argv, char** env) {
|
||||
// When tracing, the contents of the secret module will not be seen
|
||||
VerilatedVcdC* tfp = nullptr;
|
||||
const char* flag = contextp->commandArgsPlusMatch("trace");
|
||||
if (flag && 0 == strcmp(flag, "+trace")) {
|
||||
if (flag && 0 == std::strcmp(flag, "+trace")) {
|
||||
contextp->traceEverOn(true);
|
||||
VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n");
|
||||
tfp = new VerilatedVcdC;
|
||||
|
||||
@@ -36,7 +36,7 @@ VERILATOR_FLAGS += -cc --exe
|
||||
# Generate makefile dependencies (not shown as complicates the Makefile)
|
||||
#VERILATOR_FLAGS += -MMD
|
||||
# Optimize
|
||||
VERILATOR_FLAGS += -Os -x-assign 0
|
||||
VERILATOR_FLAGS += -x-assign fast
|
||||
# Warn abount lint issues; may not want this on less solid designs
|
||||
VERILATOR_FLAGS += -Wall
|
||||
# Make waveforms
|
||||
|
||||
@@ -34,6 +34,8 @@ int main(int argc, char** argv, char** env) {
|
||||
// Using unique_ptr is similar to
|
||||
// "VerilatedContext* contextp = new VerilatedContext" then deleting at end.
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
// Do not instead make Vtop as a file-scope static variable, as the
|
||||
// "C++ static initialization order fiasco" may cause a crash
|
||||
|
||||
// Set debug level, 0 is off, 9 is highest presently used
|
||||
// May be overridden by commandArgs argument parsing
|
||||
@@ -69,7 +71,7 @@ int main(int argc, char** argv, char** env) {
|
||||
// Historical note, before Verilator 4.200 Verilated::gotFinish()
|
||||
// was used above in place of contextp->gotFinish().
|
||||
// Most of the contextp-> calls can use Verilated:: calls instead;
|
||||
// the Verilated:: versions simply assume there's a single context
|
||||
// the Verilated:: versions just assume there's a single context
|
||||
// being used (per thread). It's faster and clearer to use the
|
||||
// newer contextp-> versions.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ VERILATOR_FLAGS += -sc --exe
|
||||
# Generate makefile dependencies (not shown as complicates the Makefile)
|
||||
#VERILATOR_FLAGS += -MMD
|
||||
# Optimize
|
||||
VERILATOR_FLAGS += -Os -x-assign 0
|
||||
VERILATOR_FLAGS += -x-assign fast
|
||||
# Warn abount lint issues; may not want this on less solid designs
|
||||
VERILATOR_FLAGS += -Wall
|
||||
# Make waveforms
|
||||
@@ -55,7 +55,7 @@ VERILATOR_FLAGS += --coverage
|
||||
VERILATOR_INPUT = -f input.vc top.v sc_main.cpp
|
||||
|
||||
# Check if SC exists via a verilator call (empty if not)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --getenv SYSTEMC_INCLUDE)
|
||||
SYSTEMC_EXISTS := $(shell $(VERILATOR) --get-supported SYSTEMC)
|
||||
|
||||
######################################################################
|
||||
|
||||
|
||||
@@ -58,11 +58,11 @@ int sc_main(int argc, char* argv[]) {
|
||||
|
||||
// Define interconnect
|
||||
sc_signal<bool> reset_l;
|
||||
sc_signal<vluint32_t> in_small;
|
||||
sc_signal<vluint64_t> in_quad;
|
||||
sc_signal<uint32_t> in_small;
|
||||
sc_signal<uint64_t> in_quad;
|
||||
sc_signal<sc_bv<70>> in_wide;
|
||||
sc_signal<vluint32_t> out_small;
|
||||
sc_signal<vluint64_t> out_quad;
|
||||
sc_signal<uint32_t> out_small;
|
||||
sc_signal<uint64_t> out_quad;
|
||||
sc_signal<sc_bv<70>> out_wide;
|
||||
|
||||
// Construct the Verilated model, from inside Vtop.h
|
||||
@@ -82,14 +82,14 @@ int sc_main(int argc, char* argv[]) {
|
||||
|
||||
// You must do one evaluation before enabling waves, in order to allow
|
||||
// SystemC to interconnect everything for testing.
|
||||
sc_start(1, SC_NS);
|
||||
sc_start(SC_ZERO_TIME);
|
||||
|
||||
#if VM_TRACE
|
||||
// If verilator was invoked with --trace argument,
|
||||
// and if at run time passed the +trace argument, turn on tracing
|
||||
VerilatedVcdSc* tfp = nullptr;
|
||||
const char* flag = Verilated::commandArgsPlusMatch("trace");
|
||||
if (flag && 0 == strcmp(flag, "+trace")) {
|
||||
if (flag && 0 == std::strcmp(flag, "+trace")) {
|
||||
cout << "Enabling waves into logs/vlt_dump.vcd...\n";
|
||||
tfp = new VerilatedVcdSc;
|
||||
top->trace(tfp, 99); // Trace 99 levels of hierarchy
|
||||
|
||||
@@ -16,12 +16,13 @@ from shutil import copy2
|
||||
|
||||
|
||||
class VlFileCopy:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
debug=0,
|
||||
output_dir='copied'): # directory name we output file uses
|
||||
output_dir='copied'): # directory name we output file uses
|
||||
|
||||
self.debug = debug
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
class VlHierGraph:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
self,
|
||||
verilator_args, # presently all verilator options are passed-thru
|
||||
# ideally this script would check against options mentioned in help
|
||||
debug=0,
|
||||
output_filename='graph.dot'): # output filename
|
||||
output_filename='graph.dot'): # output filename
|
||||
self.debug = debug
|
||||
self.next_vertex_number = 0
|
||||
self.name_to_number = {}
|
||||
|
||||
@@ -140,6 +140,10 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(FST_MACOSX) || defined(__MINGW32__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
#define FST_UNBUFFERED_IO
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* Boolean expression more often true than false */
|
||||
#define FST_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
@@ -4577,12 +4581,11 @@ if(sectype == FST_BL_ZWRAPPER)
|
||||
if(!fcomp) { tmpfile_close(&fcomp, &xc->f_nam); return(0); }
|
||||
}
|
||||
|
||||
#if defined(FST_MACOSX)
|
||||
#if defined(FST_UNBUFFERED_IO)
|
||||
setvbuf(fcomp, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */
|
||||
#endif
|
||||
|
||||
#ifdef __MINGW32__
|
||||
setvbuf(fcomp, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */
|
||||
xc->filename_unpacked = hf;
|
||||
#else
|
||||
if(hf)
|
||||
@@ -4880,7 +4883,7 @@ if((!nam)||(!(xc->f=fopen(nam, "rb"))))
|
||||
char *hf = (char *)calloc(1, flen + 6);
|
||||
int rc;
|
||||
|
||||
#if defined(__MINGW32__) || defined(FST_MACOSX)
|
||||
#if defined(FST_UNBUFFERED_IO)
|
||||
setvbuf(xc->f, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */
|
||||
#endif
|
||||
|
||||
|
||||
+359
-200
File diff suppressed because it is too large
Load Diff
+122
-56
@@ -33,6 +33,7 @@
|
||||
|
||||
// clang-format off
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_config.h"
|
||||
#if VM_SC
|
||||
# include "verilated_sc.h" // Get SYSTEMC_VERSION and time declarations
|
||||
#endif
|
||||
@@ -80,14 +81,19 @@
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
class VerilatedContext;
|
||||
class VerilatedContextImp;
|
||||
class VerilatedContextImpData;
|
||||
class VerilatedCovContext;
|
||||
class VerilatedEvalMsgQueue;
|
||||
class VerilatedFst;
|
||||
class VerilatedFstC;
|
||||
class VerilatedFstSc;
|
||||
class VerilatedScope;
|
||||
class VerilatedScopeNameMap;
|
||||
template <class, class>
|
||||
class VerilatedTrace;
|
||||
class VerilatedTraceConfig;
|
||||
class VerilatedVar;
|
||||
class VerilatedVarNameMap;
|
||||
class VerilatedVcd;
|
||||
@@ -99,11 +105,11 @@ class VerilatedVcdSc;
|
||||
|
||||
// clang-format off
|
||||
// P // Packed data of bit type (C/S/I/Q/W)
|
||||
using CData = vluint8_t; ///< Data representing 'bit' of 1-8 packed bits
|
||||
using SData = vluint16_t; ///< Data representing 'bit' of 9-16 packed bits
|
||||
using IData = vluint32_t; ///< Data representing 'bit' of 17-32 packed bits
|
||||
using QData = vluint64_t; ///< Data representing 'bit' of 33-64 packed bits
|
||||
using EData = vluint32_t; ///< Data representing one element of WData array
|
||||
using CData = uint8_t; ///< Data representing 'bit' of 1-8 packed bits
|
||||
using SData = uint16_t; ///< Data representing 'bit' of 9-16 packed bits
|
||||
using IData = uint32_t; ///< Data representing 'bit' of 17-32 packed bits
|
||||
using QData = uint64_t; ///< Data representing 'bit' of 33-64 packed bits
|
||||
using EData = uint32_t; ///< Data representing one element of WData array
|
||||
using WData = EData; ///< Data representing >64 packed bits (used as pointer)
|
||||
// F = float; // No typedef needed; Verilator uses float
|
||||
// D = double; // No typedef needed; Verilator uses double
|
||||
@@ -113,7 +119,7 @@ using WData = EData; ///< Data representing >64 packed bits (used as poin
|
||||
using WDataInP = const WData*; ///< 'bit' of >64 packed bits as array input to a function
|
||||
using WDataOutP = WData*; ///< 'bit' of >64 packed bits as array output from a function
|
||||
|
||||
enum VerilatedVarType : vluint8_t {
|
||||
enum VerilatedVarType : uint8_t {
|
||||
VLVT_UNKNOWN = 0,
|
||||
VLVT_PTR, // Pointer to something
|
||||
VLVT_UINT8, // AKA CData
|
||||
@@ -141,11 +147,11 @@ enum VerilatedVarFlags {
|
||||
// Mutex and threading support
|
||||
|
||||
// Return current thread ID (or 0), not super fast, cache if needed
|
||||
extern vluint32_t VL_THREAD_ID() VL_MT_SAFE;
|
||||
extern uint32_t VL_THREAD_ID() VL_MT_SAFE;
|
||||
|
||||
#if VL_THREADED
|
||||
|
||||
#define VL_LOCK_SPINS 50000 /// Number of times to spin for a mutex before relaxing
|
||||
#define VL_LOCK_SPINS 50000 /// Number of times to spin for a mutex before yielding
|
||||
|
||||
/// Mutex, wrapped to allow -fthread_safety checks
|
||||
class VL_CAPABILITY("mutex") VerilatedMutex final {
|
||||
@@ -158,7 +164,7 @@ public:
|
||||
~VerilatedMutex() = default;
|
||||
const VerilatedMutex& operator!() const { return *this; } // For -fthread_safety
|
||||
/// Acquire/lock mutex
|
||||
void lock() VL_ACQUIRE() {
|
||||
void lock() VL_ACQUIRE() VL_MT_SAFE {
|
||||
// Try to acquire the lock by spinning. If the wait is short,
|
||||
// avoids a trap to the OS plus OS scheduler overhead.
|
||||
if (VL_LIKELY(try_lock())) return; // Short circuit loop
|
||||
@@ -170,9 +176,9 @@ public:
|
||||
m_mutex.lock();
|
||||
}
|
||||
/// Release/unlock mutex
|
||||
void unlock() VL_RELEASE() { m_mutex.unlock(); }
|
||||
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutex.unlock(); }
|
||||
/// Try to acquire mutex. Returns true on success, and false on failure.
|
||||
bool try_lock() VL_TRY_ACQUIRE(true) { return m_mutex.try_lock(); }
|
||||
bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE { return m_mutex.try_lock(); }
|
||||
};
|
||||
|
||||
/// Lock guard for mutex (ala std::unique_lock), wrapped to allow -fthread_safety checks
|
||||
@@ -184,16 +190,16 @@ private:
|
||||
|
||||
public:
|
||||
/// Construct and hold given mutex lock until destruction or unlock()
|
||||
explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr)
|
||||
explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE
|
||||
: m_mutexr(mutexr) { // Need () or GCC 4.8 false warning
|
||||
m_mutexr.lock();
|
||||
}
|
||||
/// Destruct and unlock the mutex
|
||||
~VerilatedLockGuard() VL_RELEASE() { m_mutexr.unlock(); }
|
||||
/// Unlock the mutex
|
||||
void lock() VL_ACQUIRE() { m_mutexr.lock(); }
|
||||
void lock() VL_ACQUIRE() VL_MT_SAFE { m_mutexr.lock(); }
|
||||
/// Lock the mutex
|
||||
void unlock() VL_RELEASE() { m_mutexr.unlock(); }
|
||||
void unlock() VL_RELEASE() VL_MT_SAFE { m_mutexr.unlock(); }
|
||||
};
|
||||
|
||||
#else // !VL_THREADED
|
||||
@@ -224,7 +230,7 @@ public:
|
||||
class VerilatedAssertOneThread final {
|
||||
// MEMBERS
|
||||
#if defined(VL_THREADED) && defined(VL_DEBUG)
|
||||
vluint32_t m_threadid; // Thread that is legal
|
||||
uint32_t m_threadid; // Thread that is legal
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// The constructor establishes the thread id for all later calls.
|
||||
@@ -250,6 +256,40 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
/// Base class of a Verilator generated (Verilated) model.
|
||||
///
|
||||
/// VerilatedModel is a base class of the user facing primary class generated
|
||||
/// by Verilator.
|
||||
|
||||
class VerilatedModel VL_NOT_FINAL {
|
||||
VL_UNCOPYABLE(VerilatedModel);
|
||||
|
||||
VerilatedContext& m_context; // The VerilatedContext this model is instantiated under
|
||||
|
||||
protected:
|
||||
explicit VerilatedModel(VerilatedContext& context);
|
||||
virtual ~VerilatedModel() = default;
|
||||
|
||||
public:
|
||||
/// Returns the VerilatedContext this model is instantiated under
|
||||
/// Used to get to e.g. simulation time via contextp()->time()
|
||||
VerilatedContext* contextp() const { return &m_context; }
|
||||
/// Returns the hierarchical name of this module instance.
|
||||
virtual const char* hierName() const = 0;
|
||||
/// Returns the name of this model (the name of the generated model class).
|
||||
virtual const char* modelName() const = 0;
|
||||
/// Returns the thread level parallelism, this model was Verilated with. Always 1 or higher.
|
||||
virtual unsigned threads() const = 0;
|
||||
|
||||
private:
|
||||
// The following are for use by Verilator internals only
|
||||
template <class, class>
|
||||
friend class VerilatedTrace;
|
||||
// Run-time trace configuration requested by this model
|
||||
virtual std::unique_ptr<VerilatedTraceConfig> traceConfig() const;
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
/// Base class for all Verilated module classes.
|
||||
|
||||
@@ -264,10 +304,6 @@ public:
|
||||
const char* name() const { return m_namep; } ///< Return name of module
|
||||
};
|
||||
|
||||
/// Declare a module, ala SC_MODULE
|
||||
#define VL_MODULE(modname) class modname VL_NOT_FINAL : public VerilatedModule
|
||||
// Not class final in VL_MODULE, as users might be abstracting our models (--hierarchical)
|
||||
|
||||
//=========================================================================
|
||||
// Functions overridable by user defines
|
||||
// (Internals however must use VL_PRINTF_MT, which calls these.)
|
||||
@@ -318,10 +354,10 @@ protected:
|
||||
bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported
|
||||
bool m_gotError = false; // A $finish statement executed
|
||||
bool m_gotFinish = false; // A $finish or $stop statement executed
|
||||
vluint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
|
||||
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
|
||||
// Slow path
|
||||
vlsint8_t m_timeunit; // Time unit as 0..15
|
||||
vlsint8_t m_timeprecision; // Time precision as 0..15
|
||||
int8_t m_timeunit; // Time unit as 0..15
|
||||
int8_t m_timeprecision; // Time precision as 0..15
|
||||
int m_errorCount = 0; // Number of errors
|
||||
int m_errorLimit = 1; // Stop on error number
|
||||
int m_randReset = 0; // Random reset: 0=all 0s, 1=all 1s, 2=random
|
||||
@@ -342,10 +378,10 @@ protected:
|
||||
struct NonSerialized { // Non-serialized information
|
||||
// These are reloaded from on command-line settings, so do not need to persist
|
||||
// Fast path
|
||||
vluint64_t m_profThreadsStart = 1; // +prof+threads starting time
|
||||
vluint32_t m_profThreadsWindow = 2; // +prof+threads window size
|
||||
uint64_t m_profExecStart = 1; // +prof+exec+start time
|
||||
uint32_t m_profExecWindow = 2; // +prof+exec+window size
|
||||
// Slow path
|
||||
std::string m_profThreadsFilename; // +prof+threads filename
|
||||
std::string m_profExecFilename; // +prof+exec+file filename
|
||||
std::string m_profVltFilename; // +prof+vlt filename
|
||||
} m_ns;
|
||||
|
||||
@@ -360,6 +396,16 @@ protected:
|
||||
|
||||
// Implementation details
|
||||
const std::unique_ptr<VerilatedContextImpData> m_impdatap;
|
||||
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
|
||||
#ifdef VL_THREADED
|
||||
unsigned m_threads = std::thread::hardware_concurrency();
|
||||
#else
|
||||
const unsigned m_threads = 1;
|
||||
#endif
|
||||
// The thread pool shared by all models added to this context
|
||||
std::unique_ptr<VerilatedVirtualBase> m_threadPool;
|
||||
// The execution profiler shared by all models added to this context
|
||||
std::unique_ptr<VerilatedVirtualBase> m_executionProfiler;
|
||||
// Coverage access
|
||||
std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep()
|
||||
|
||||
@@ -372,6 +418,10 @@ protected:
|
||||
// List of free descriptors in the MCT region [4, 32)
|
||||
std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex);
|
||||
|
||||
// Magic to check for bad construction
|
||||
static constexpr uint64_t MAGIC = 0xC35F9A6E5298EE6EULL; // SHA256 "VerilatedContext"
|
||||
uint64_t m_magic = MAGIC;
|
||||
|
||||
private:
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedContext);
|
||||
@@ -407,9 +457,9 @@ public:
|
||||
VerilatedCovContext* coveragep() VL_MT_SAFE;
|
||||
/// Set debug level
|
||||
/// Debug is currently global, but for forward compatibility have a per-context method
|
||||
static void debug(int val) VL_MT_SAFE;
|
||||
static inline void debug(int val) VL_MT_SAFE;
|
||||
/// Return debug level
|
||||
static int debug() VL_MT_SAFE;
|
||||
static inline int debug() VL_MT_SAFE;
|
||||
/// Set current number of errors/assertions
|
||||
void errorCount(int val) VL_MT_SAFE;
|
||||
/// Increment current number of errors/assertions
|
||||
@@ -466,16 +516,16 @@ public:
|
||||
/// timeInc, operating on the thread's context.
|
||||
///
|
||||
/// * Else, if VL_TIME_STAMP64 is defined, time comes from the legacy
|
||||
/// 'vluint64_t vl_time_stamp64()' which must a function be defined by
|
||||
/// 'uint64_t vl_time_stamp64()' which must a function be defined by
|
||||
/// the user's wrapper.
|
||||
///
|
||||
/// * Else, time comes from the legacy 'double sc_time_stamp()' which
|
||||
/// must be a function defined by the user's wrapper.
|
||||
vluint64_t time() const VL_MT_SAFE;
|
||||
inline uint64_t time() const VL_MT_SAFE;
|
||||
/// Set current simulation time. See time() for side effect details
|
||||
void time(vluint64_t value) VL_MT_SAFE { m_s.m_time = value; }
|
||||
void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; }
|
||||
/// Advance current simulation time. See time() for side effect details
|
||||
void timeInc(vluint64_t add) VL_MT_UNSAFE { m_s.m_time += add; }
|
||||
void timeInc(uint64_t add) VL_MT_UNSAFE { m_s.m_time += add; }
|
||||
/// Return time units as power-of-ten
|
||||
int timeunit() const VL_MT_SAFE { return -m_s.m_timeunit; }
|
||||
/// Set time units as power-of-ten
|
||||
@@ -489,6 +539,12 @@ public:
|
||||
/// Get time precision as IEEE-standard text
|
||||
const char* timeprecisionString() const VL_MT_SAFE;
|
||||
|
||||
/// Get number of threads used for simulation (including the main thread)
|
||||
unsigned threads() const { return m_threads; }
|
||||
/// Set number of threads used for simulation (including the main thread)
|
||||
/// Can only be called before the thread pool is created (before first model is added).
|
||||
void threads(unsigned n);
|
||||
|
||||
/// Allow traces to at some point be enabled (disables some optimizations)
|
||||
void traceEverOn(bool flag) VL_MT_SAFE {
|
||||
if (flag) calcUnusedSigs(true);
|
||||
@@ -504,25 +560,32 @@ public:
|
||||
/// releases - contact the authors before production use.
|
||||
void scopesDump() const VL_MT_SAFE;
|
||||
|
||||
public: // But for internal use only
|
||||
// METHODS - public but for internal use only
|
||||
|
||||
// Internal: access to implementation class
|
||||
VerilatedContextImp* impp() { return reinterpret_cast<VerilatedContextImp*>(this); }
|
||||
const VerilatedContextImp* impp() const {
|
||||
return reinterpret_cast<const VerilatedContextImp*>(this);
|
||||
}
|
||||
|
||||
void addModel(VerilatedModel*);
|
||||
|
||||
VerilatedVirtualBase* threadPoolp();
|
||||
VerilatedVirtualBase*
|
||||
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
|
||||
|
||||
// Internal: $dumpfile
|
||||
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
|
||||
std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
|
||||
std::string dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
|
||||
|
||||
// Internal: --prof-threads related settings
|
||||
void profThreadsStart(vluint64_t flag) VL_MT_SAFE;
|
||||
vluint64_t profThreadsStart() const VL_MT_SAFE { return m_ns.m_profThreadsStart; }
|
||||
void profThreadsWindow(vluint64_t flag) VL_MT_SAFE;
|
||||
vluint32_t profThreadsWindow() const VL_MT_SAFE { return m_ns.m_profThreadsWindow; }
|
||||
void profThreadsFilename(const std::string& flag) VL_MT_SAFE;
|
||||
std::string profThreadsFilename() const VL_MT_SAFE;
|
||||
// Internal: --prof-exec related settings
|
||||
void profExecStart(uint64_t flag) VL_MT_SAFE;
|
||||
uint64_t profExecStart() const VL_MT_SAFE { return m_ns.m_profExecStart; }
|
||||
void profExecWindow(uint64_t flag) VL_MT_SAFE;
|
||||
uint32_t profExecWindow() const VL_MT_SAFE { return m_ns.m_profExecWindow; }
|
||||
void profExecFilename(const std::string& flag) VL_MT_SAFE;
|
||||
std::string profExecFilename() const VL_MT_SAFE;
|
||||
void profVltFilename(const std::string& flag) VL_MT_SAFE;
|
||||
std::string profVltFilename() const VL_MT_SAFE;
|
||||
|
||||
@@ -533,6 +596,10 @@ public: // But for internal use only
|
||||
// Internal: Serialization setup
|
||||
static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
|
||||
void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
|
||||
|
||||
// Internal: Check magic number
|
||||
static void checkMagic(const VerilatedContext* contextp);
|
||||
void selfTestClearMagic() { m_magic = 0x2; }
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
@@ -557,7 +624,7 @@ public: // But for internal use only
|
||||
|
||||
class VerilatedScope final {
|
||||
public:
|
||||
enum Type : vluint8_t {
|
||||
enum Type : uint8_t {
|
||||
SCOPE_MODULE,
|
||||
SCOPE_OTHER
|
||||
}; // Type of a scope, currently module is only interesting
|
||||
@@ -570,28 +637,28 @@ private:
|
||||
VerilatedVarNameMap* m_varsp = nullptr; // Variable map
|
||||
const char* m_namep = nullptr; // Scope name (Slowpath)
|
||||
const char* m_identifierp = nullptr; // Identifier of scope (with escapes removed)
|
||||
vlsint8_t m_timeunit = 0; // Timeunit in negative power-of-10
|
||||
int8_t m_timeunit = 0; // Timeunit in negative power-of-10
|
||||
Type m_type = SCOPE_OTHER; // Type of the scope
|
||||
|
||||
public: // But internals only - called from VerilatedModule's
|
||||
VerilatedScope() = default;
|
||||
~VerilatedScope();
|
||||
void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp,
|
||||
const char* identifier, vlsint8_t timeunit, const Type& type) VL_MT_UNSAFE;
|
||||
const char* identifier, int8_t timeunit, const Type& type) VL_MT_UNSAFE;
|
||||
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
|
||||
void varInsert(int finalize, const char* namep, void* datap, bool isParam,
|
||||
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE;
|
||||
// ACCESSORS
|
||||
const char* name() const { return m_namep; }
|
||||
const char* identifier() const { return m_identifierp; }
|
||||
vlsint8_t timeunit() const { return m_timeunit; }
|
||||
inline VerilatedSyms* symsp() const { return m_symsp; }
|
||||
int8_t timeunit() const { return m_timeunit; }
|
||||
VerilatedSyms* symsp() const { return m_symsp; }
|
||||
VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT;
|
||||
VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; }
|
||||
void scopeDump() const;
|
||||
void* exportFindError(int funcnum) const;
|
||||
static void* exportFindNullError(int funcnum) VL_MT_SAFE;
|
||||
static inline void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
|
||||
static void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
|
||||
if (VL_UNLIKELY(!scopep)) return exportFindNullError(funcnum);
|
||||
if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
|
||||
// m_callbacksp must be declared, as Max'es are > 0
|
||||
@@ -634,9 +701,9 @@ class Verilated final {
|
||||
// Fast path
|
||||
VerilatedContext* t_contextp = nullptr; // Thread's context
|
||||
#ifdef VL_THREADED
|
||||
vluint32_t t_mtaskId = 0; // mtask# executing on this thread
|
||||
uint32_t t_mtaskId = 0; // mtask# executing on this thread
|
||||
// Messages maybe pending on thread, needs end-of-eval calls
|
||||
vluint32_t t_endOfEvalReqd = 0;
|
||||
uint32_t t_endOfEvalReqd = 0;
|
||||
#endif
|
||||
const VerilatedScope* t_dpiScopep = nullptr; // DPI context scope
|
||||
const char* t_dpiFilename = nullptr; // DPI context filename
|
||||
@@ -660,7 +727,7 @@ public:
|
||||
/// Return debug level
|
||||
/// When multithreaded this may not immediately react to another thread
|
||||
/// changing the level (no mutex)
|
||||
static inline int debug() VL_MT_SAFE { return s_debug; }
|
||||
static int debug() VL_MT_SAFE { return s_debug; }
|
||||
#else
|
||||
/// Return constant 0 debug level, so C++'s optimizer rips up
|
||||
static constexpr int debug() VL_PURE { return 0; }
|
||||
@@ -758,11 +825,11 @@ public:
|
||||
/// Return VerilatedContext::randSeed using current thread's VerilatedContext
|
||||
static int randSeed() VL_MT_SAFE { return Verilated::threadContextp()->randSeed(); }
|
||||
/// Call VerilatedContext::time using current thread's VerilatedContext
|
||||
static void time(vluint64_t val) VL_MT_SAFE { Verilated::threadContextp()->time(val); }
|
||||
static void time(uint64_t val) VL_MT_SAFE { Verilated::threadContextp()->time(val); }
|
||||
/// Return VerilatedContext::time using current thread's VerilatedContext
|
||||
static vluint64_t time() VL_MT_SAFE { return Verilated::threadContextp()->time(); }
|
||||
static uint64_t time() VL_MT_SAFE { return Verilated::threadContextp()->time(); }
|
||||
/// Call VerilatedContext::timeInc using current thread's VerilatedContext
|
||||
static void timeInc(vluint64_t add) VL_MT_UNSAFE { Verilated::threadContextp()->timeInc(add); }
|
||||
static void timeInc(uint64_t add) VL_MT_UNSAFE { Verilated::threadContextp()->timeInc(add); }
|
||||
// Deprecated
|
||||
static int timeunit() VL_MT_SAFE { return Verilated::threadContextp()->timeunit(); }
|
||||
static int timeprecision() VL_MT_SAFE { return Verilated::threadContextp()->timeprecision(); }
|
||||
@@ -820,7 +887,6 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
// METHODS - INTERNAL USE ONLY (but public due to what uses it)
|
||||
// Internal: Create a new module name by concatenating two strings
|
||||
// Returns pointer to thread-local static data (overwritten on next call)
|
||||
@@ -848,8 +914,8 @@ public:
|
||||
#ifdef VL_THREADED
|
||||
// Internal: Set the mtaskId, called when an mtask starts
|
||||
// Per thread, so no need to be in VerilatedContext
|
||||
static void mtaskId(vluint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
|
||||
static vluint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; }
|
||||
static void mtaskId(uint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
|
||||
static uint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; }
|
||||
static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; }
|
||||
static void endOfEvalReqdDec() VL_MT_SAFE { --t_s.t_endOfEvalReqd; }
|
||||
|
||||
@@ -867,8 +933,8 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
inline void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
|
||||
inline int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
|
||||
void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
|
||||
int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
|
||||
|
||||
//=========================================================================
|
||||
// Data Types
|
||||
|
||||
+11
-18
@@ -27,6 +27,8 @@ CFG_CXXFLAGS_STD_NEWEST = @CFG_CXXFLAGS_STD_NEWEST@
|
||||
CFG_CXXFLAGS_NO_UNUSED = @CFG_CXXFLAGS_NO_UNUSED@
|
||||
# Compiler flags that turn on extra warnings
|
||||
CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
|
||||
# Compiler flags that enable coroutine support
|
||||
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
|
||||
# Linker libraries for multithreading
|
||||
CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@
|
||||
|
||||
@@ -56,6 +58,7 @@ VK_CPPFLAGS_ALWAYS += \
|
||||
-DVM_SC=$(VM_SC) \
|
||||
-DVM_TRACE=$(VM_TRACE) \
|
||||
-DVM_TRACE_FST=$(VM_TRACE_FST) \
|
||||
-DVM_TRACE_VCD=$(VM_TRACE_VCD) \
|
||||
$(CFG_CXXFLAGS_NO_UNUSED) \
|
||||
|
||||
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
|
||||
@@ -141,23 +144,9 @@ ifneq ($(VM_THREADS),0)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VM_TRACE_THREADS),0)
|
||||
ifneq ($(VM_TRACE_THREADS),)
|
||||
ifeq ($(findstring -DVL_THREADED,$(CPPFLAGS)),)
|
||||
$(error VM_TRACE_THREADS requires VM_THREADS)
|
||||
endif
|
||||
CPPFLAGS += -DVL_TRACE_THREADED
|
||||
VK_C11=1
|
||||
VK_LIBS_THREADED=1
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
ifneq ($(VM_TRACE_FST_WRITER_THREAD),0)
|
||||
ifneq ($(VM_TRACE_FST_WRITER_THREAD),)
|
||||
CPPFLAGS += -DVL_TRACE_FST_WRITER_THREAD
|
||||
VK_C11=1
|
||||
VK_LIBS_THREADED=1
|
||||
ifneq ($(VM_TIMING),0)
|
||||
ifneq ($(VM_TIMING),)
|
||||
CPPFLAGS += $(CFG_CXXFLAGS_COROUTINES)
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -186,13 +175,17 @@ VM_SLOW += $(VM_CLASSES_SLOW) $(VM_SUPPORT_SLOW)
|
||||
VK_FAST_OBJS = $(addsuffix .o, $(VM_FAST))
|
||||
VK_SLOW_OBJS = $(addsuffix .o, $(VM_SLOW))
|
||||
|
||||
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
|
||||
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
|
||||
|
||||
# Note VM_GLOBAL_FAST and VM_GLOBAL_SLOW holds the files required from the
|
||||
# run-time library. In practice everything is actually in VM_GLOBAL_FAST,
|
||||
# but keeping the distinction for compatibility for now.
|
||||
VK_GLOBAL_OBJS = $(addsuffix .o, $(VM_GLOBAL_FAST) $(VM_GLOBAL_SLOW))
|
||||
|
||||
# Need to re-build if the generated makefile changes, as compiler options might
|
||||
# have changed.
|
||||
$(VK_GLOBAL_OBJS): $(VM_PREFIX).mk
|
||||
|
||||
ifneq ($(VM_PARALLEL_BUILDS),1)
|
||||
# Fast build for small designs: All .cpp files in one fell swoop. This
|
||||
# saves total compute, but can be slower if only a little changes. It is
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
// Autoconf substitutes this with the strings from AC_INIT.
|
||||
#define VERILATOR_PRODUCT "@PACKAGE_NAME@"
|
||||
|
||||
/// Verilator version name, e.g. "1.000 2000-01-01"
|
||||
/// Verilator version name, e.g. "1.002 2000-01-01"
|
||||
// Autoconf substitutes this with the strings from AC_INIT.
|
||||
#define VERILATOR_VERSION "@PACKAGE_VERSION@"
|
||||
|
||||
/// Verilator version number as integer
|
||||
/// As major * 100000 + minor * 1000, e.g. 1002000 == 1.002
|
||||
// Autoconf substitutes this with the strings from AC_INIT.
|
||||
#define VERILATOR_VERSION_INTEGER @VERILATOR_VERSION_INTEGER@
|
||||
|
||||
+20
-22
@@ -22,8 +22,10 @@
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
|
||||
#include "verilated_cov.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_cov_key.h"
|
||||
|
||||
#include <deque>
|
||||
@@ -59,7 +61,7 @@ public: // But only local to this file
|
||||
}
|
||||
}
|
||||
virtual ~VerilatedCovImpItem() = default;
|
||||
virtual vluint64_t count() const = 0;
|
||||
virtual uint64_t count() const = 0;
|
||||
virtual void zero() const = 0;
|
||||
};
|
||||
|
||||
@@ -69,22 +71,23 @@ public: // But only local to this file
|
||||
// This isn't in the header file for auto-magic conversion because it
|
||||
// inlines to too much code and makes compilation too slow.
|
||||
|
||||
template <class T> class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
|
||||
template <class T>
|
||||
class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
|
||||
private:
|
||||
// MEMBERS
|
||||
T* m_countp; // Count value
|
||||
public:
|
||||
// METHODS
|
||||
// cppcheck-suppress truncLongCastReturn
|
||||
virtual vluint64_t count() const override { return *m_countp; }
|
||||
virtual void zero() const override { *m_countp = 0; }
|
||||
uint64_t count() const override { return *m_countp; }
|
||||
void zero() const override { *m_countp = 0; }
|
||||
// CONSTRUCTORS
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
explicit VerilatedCoverItemSpec(T* countp)
|
||||
: m_countp{countp} {
|
||||
*m_countp = 0;
|
||||
}
|
||||
virtual ~VerilatedCoverItemSpec() override = default;
|
||||
~VerilatedCoverItemSpec() override = default;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -103,7 +106,7 @@ private:
|
||||
using ItemList = std::deque<VerilatedCovImpItem*>;
|
||||
|
||||
// MEMBERS
|
||||
VerilatedMutex m_mutex; // Protects all members
|
||||
mutable VerilatedMutex m_mutex; // Protects all members
|
||||
ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex); // Unique arbitrary value for values
|
||||
IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex); // Unique arbitrary value for keys
|
||||
ItemList m_items VL_GUARDED_BY(m_mutex); // List of all items
|
||||
@@ -122,11 +125,7 @@ public:
|
||||
|
||||
protected:
|
||||
friend class VerilatedCovContext;
|
||||
virtual ~VerilatedCovImp() override { clearGuts(); }
|
||||
static VerilatedCovImp& imp() VL_MT_SAFE {
|
||||
static VerilatedCovImp s_singleton;
|
||||
return s_singleton;
|
||||
}
|
||||
~VerilatedCovImp() override { clearGuts(); }
|
||||
|
||||
private:
|
||||
// PRIVATE METHODS
|
||||
@@ -209,7 +208,7 @@ private:
|
||||
// Forward to . so we have a whole word
|
||||
const std::string suffix = *bpost ? std::string{bpost + 1} : "";
|
||||
|
||||
const std::string out = prefix + "*" + suffix;
|
||||
std::string out = prefix + "*" + suffix;
|
||||
|
||||
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
|
||||
// <<"\ncho="<<out<<endl;
|
||||
@@ -358,9 +357,6 @@ public:
|
||||
void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
#ifndef VM_COVERAGE
|
||||
VL_FATAL_MT("", 0, "", "%Error: Called VerilatedCov::write when VM_COVERAGE disabled");
|
||||
#endif
|
||||
selftest();
|
||||
|
||||
std::ofstream os{filename};
|
||||
@@ -372,7 +368,7 @@ public:
|
||||
os << "# SystemC::Coverage-3\n";
|
||||
|
||||
// Build list of events; totalize if collapsing hierarchy
|
||||
std::map<const std::string, std::pair<std::string, vluint64_t>> eventCounts;
|
||||
std::map<const std::string, std::pair<std::string, uint64_t>> eventCounts;
|
||||
for (const auto& itemp : m_items) {
|
||||
std::string name;
|
||||
std::string hier;
|
||||
@@ -439,11 +435,11 @@ void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
|
||||
}
|
||||
void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
|
||||
void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); }
|
||||
void VerilatedCovContext::_inserti(vluint32_t* itemp) VL_MT_SAFE {
|
||||
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>{itemp});
|
||||
void VerilatedCovContext::_inserti(uint32_t* itemp) VL_MT_SAFE {
|
||||
impp()->inserti(new VerilatedCoverItemSpec<uint32_t>{itemp});
|
||||
}
|
||||
void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE {
|
||||
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>{itemp});
|
||||
void VerilatedCovContext::_inserti(uint64_t* itemp) VL_MT_SAFE {
|
||||
impp()->inserti(new VerilatedCoverItemSpec<uint64_t>{itemp});
|
||||
}
|
||||
void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
|
||||
impp()->insertf(filename, lineno);
|
||||
@@ -515,9 +511,11 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
|
||||
|
||||
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
|
||||
static VerilatedMutex s_mutex;
|
||||
// cppcheck-suppress identicalInnerCondition
|
||||
if (VL_UNLIKELY(!m_coveragep)) {
|
||||
const VerilatedLockGuard lock{s_mutex};
|
||||
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
|
||||
// cppcheck-suppress identicalInnerCondition
|
||||
if (VL_LIKELY(!m_coveragep)) { // LCOV_EXCL_LINE // Not redundant, prevents race
|
||||
m_coveragep.reset(new VerilatedCovImp);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-25
@@ -26,6 +26,7 @@
|
||||
#define VERILATOR_VERILATED_COV_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include <iostream>
|
||||
@@ -34,22 +35,6 @@
|
||||
|
||||
class VerilatedCovImp;
|
||||
|
||||
//=============================================================================
|
||||
/// Conditionally compile statements only when doing coverage (when
|
||||
/// VM_COVERAGE is defined)
|
||||
|
||||
// clang-format off
|
||||
#ifdef VM_COVERAGE
|
||||
# define VL_IF_COVER(stmts) \
|
||||
do { stmts; } while (false)
|
||||
#else
|
||||
# define VL_IF_COVER(stmts) \
|
||||
do { \
|
||||
if (false) { stmts; } \
|
||||
} while (false)
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
//=============================================================================
|
||||
/// Insert a item for coverage analysis.
|
||||
/// The first argument is a pointer to the count to be dumped.
|
||||
@@ -72,7 +57,7 @@ class VerilatedCovImp;
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// vluint32_t m_cases[10]; // Storage for coverage data
|
||||
/// uint32_t m_cases[10]; // Storage for coverage data
|
||||
/// constructor() {
|
||||
/// // Initialize
|
||||
/// for (int i = 0; i < 10; ++i) m_cases[i] = 0;
|
||||
@@ -82,13 +67,17 @@ class VerilatedCovImp;
|
||||
/// }
|
||||
|
||||
#define VL_COVER_INSERT(covcontextp, countp, ...) \
|
||||
VL_IF_COVER(covcontextp->_inserti(countp); covcontextp->_insertf(__FILE__, __LINE__); \
|
||||
covcontextp->_insertp("hier", name(), __VA_ARGS__))
|
||||
do { \
|
||||
covcontextp->_inserti(countp); \
|
||||
covcontextp->_insertf(__FILE__, __LINE__); \
|
||||
covcontextp->_insertp("hier", name(), __VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
//=============================================================================
|
||||
// Convert VL_COVER_INSERT value arguments to strings, is \internal
|
||||
|
||||
template <class T> std::string vlCovCvtToStr(const T& t) VL_PURE {
|
||||
template <class T>
|
||||
std::string vlCovCvtToStr(const T& t) VL_PURE {
|
||||
std::ostringstream os;
|
||||
os << t;
|
||||
return os.str();
|
||||
@@ -120,14 +109,15 @@ public:
|
||||
/// Zero coverage points
|
||||
void zero() VL_MT_SAFE;
|
||||
|
||||
public: // But Internal use only
|
||||
// METHODS - public but Internal use only
|
||||
|
||||
// Insert a coverage item
|
||||
// We accept from 1-30 key/value pairs, all as strings.
|
||||
// Call _insert1, followed by _insert2 and _insert3
|
||||
// Do not call directly; use VL_COVER_INSERT or higher level macros instead
|
||||
// _insert1: Remember item pointer with count. (Not const, as may add zeroing function)
|
||||
void _inserti(vluint32_t* itemp) VL_MT_SAFE;
|
||||
void _inserti(vluint64_t* itemp) VL_MT_SAFE;
|
||||
void _inserti(uint32_t* itemp) VL_MT_SAFE;
|
||||
void _inserti(uint64_t* itemp) VL_MT_SAFE;
|
||||
// _insert2: Set default filename and line number
|
||||
void _insertf(const char* filename, int lineno) VL_MT_SAFE;
|
||||
// _insert3: Set parameters
|
||||
@@ -157,8 +147,8 @@ protected:
|
||||
friend class VerilatedCovImp;
|
||||
// CONSTRUCTORS
|
||||
// Internal: Only made as part of VerilatedCovImp
|
||||
VerilatedCovContext() {}
|
||||
virtual ~VerilatedCovContext() {}
|
||||
VerilatedCovContext() = default;
|
||||
~VerilatedCovContext() override = default;
|
||||
|
||||
// METHODS
|
||||
// Internal: access to implementation class
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
#define VERILATOR_VERILATED_DPI_CPP_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_dpi.h"
|
||||
|
||||
#include "verilated_imp.h"
|
||||
|
||||
// On MSVC++ we need svdpi.h to declare exports, not imports
|
||||
@@ -179,7 +181,7 @@ void svPutPartselLogic(svLogicVecVal* dp, const svLogicVecVal s, int lbit, int w
|
||||
//======================================================================
|
||||
// Open array internals
|
||||
|
||||
static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) {
|
||||
static const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) {
|
||||
if (VL_UNLIKELY(!h)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"%%Error: DPI svOpenArrayHandle function called with nullptr handle");
|
||||
@@ -417,7 +419,7 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// DPI accessors that simply call above functions
|
||||
// DPI accessors that call above functions
|
||||
|
||||
void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define VERILATOR_VERILATED_DPI_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h" // Also presumably included by caller
|
||||
#include "verilated_sym_props.h"
|
||||
|
||||
|
||||
+80
-36
@@ -28,7 +28,7 @@
|
||||
#include "verilated_fst_c.h"
|
||||
|
||||
// GTKWave configuration
|
||||
#ifdef VL_TRACE_FST_WRITER_THREAD
|
||||
#ifdef VL_THREADED
|
||||
# define HAVE_LIBPTHREAD
|
||||
# define FST_WRITER_PARALLEL
|
||||
#endif
|
||||
@@ -83,15 +83,16 @@ static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE
|
||||
//=============================================================================
|
||||
// Specialization of the generics for this trace format
|
||||
|
||||
#define VL_DERIVED_T VerilatedFst
|
||||
#include "verilated_trace_imp.cpp"
|
||||
#undef VL_DERIVED_T
|
||||
#define VL_SUB_T VerilatedFst
|
||||
#define VL_BUF_T VerilatedFstBuffer
|
||||
#include "verilated_trace_imp.h"
|
||||
#undef VL_SUB_T
|
||||
#undef VL_BUF_T
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFst
|
||||
|
||||
VerilatedFst::VerilatedFst(void* fst)
|
||||
: m_fst{fst} {}
|
||||
VerilatedFst::VerilatedFst(void* /*fst*/) {}
|
||||
|
||||
VerilatedFst::~VerilatedFst() {
|
||||
if (m_fst) fstWriterClose(m_fst);
|
||||
@@ -104,14 +105,12 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
m_fst = fstWriterCreate(filename, 1);
|
||||
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
|
||||
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
|
||||
#ifdef VL_TRACE_FST_WRITER_THREAD
|
||||
fstWriterSetParallelMode(m_fst, 1);
|
||||
#endif
|
||||
if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1);
|
||||
fullDump(true); // First dump must be full for fst
|
||||
|
||||
m_curScope.clear();
|
||||
|
||||
VerilatedTrace<VerilatedFst>::traceInit();
|
||||
Super::traceInit();
|
||||
|
||||
// Clear the scope stack
|
||||
auto it = m_curScope.begin();
|
||||
@@ -122,7 +121,7 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
|
||||
// convert m_code2symbol into an array for fast lookup
|
||||
if (!m_symbolp) {
|
||||
m_symbolp = new fstHandle[nextCode()];
|
||||
m_symbolp = new fstHandle[nextCode()]{0};
|
||||
for (const auto& i : m_code2symbol) m_symbolp[i.first] = i.second;
|
||||
}
|
||||
m_code2symbol.clear();
|
||||
@@ -133,23 +132,23 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
|
||||
void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedFst>::closeBase();
|
||||
Super::closeBase();
|
||||
fstWriterClose(m_fst);
|
||||
m_fst = nullptr;
|
||||
}
|
||||
|
||||
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedFst>::flushBase();
|
||||
Super::flushBase();
|
||||
fstWriterFlushContext(m_fst);
|
||||
}
|
||||
|
||||
void VerilatedFst::emitTimeChange(vluint64_t timeui) { fstWriterEmitTimeChange(m_fst, timeui); }
|
||||
void VerilatedFst::emitTimeChange(uint64_t timeui) { fstWriterEmitTimeChange(m_fst, timeui); }
|
||||
|
||||
//=============================================================================
|
||||
// Decl
|
||||
|
||||
void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
|
||||
void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, uint32_t elements,
|
||||
unsigned int minValbits, const char** itemNamesp,
|
||||
const char** itemValuesp) {
|
||||
const fstEnumHandle enumNum
|
||||
@@ -157,11 +156,13 @@ void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elem
|
||||
m_local2fstdtype[dtypenum] = enumNum;
|
||||
}
|
||||
|
||||
void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
void VerilatedFst::declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, bool bussed, int msb,
|
||||
int lsb) {
|
||||
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
|
||||
|
||||
VerilatedTrace<VerilatedFst>::declCode(code, bits, false);
|
||||
const bool enabled = Super::declCode(code, name, bits, false);
|
||||
if (!enabled) return;
|
||||
|
||||
std::string nameasstr = namePrefix() + name;
|
||||
std::istringstream nameiss{nameasstr};
|
||||
@@ -205,6 +206,7 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
|
||||
std::stringstream name_ss;
|
||||
name_ss << symbol_name;
|
||||
if (array) name_ss << "[" << arraynum << "]";
|
||||
if (bussed) name_ss << " [" << msb << ":" << lsb << "]";
|
||||
std::string name_str = name_ss.str();
|
||||
|
||||
if (dtypenum > 0) {
|
||||
@@ -221,66 +223,108 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedFst::declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
void VerilatedFst::declBit(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, 0, 0);
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 0, 0);
|
||||
}
|
||||
void VerilatedFst::declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
void VerilatedFst::declBus(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, msb, lsb);
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declQuad(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
void VerilatedFst::declQuad(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, msb, lsb);
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declArray(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
void VerilatedFst::declArray(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, msb, lsb);
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
|
||||
}
|
||||
void VerilatedFst::declDouble(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
void VerilatedFst::declDouble(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, 63, 0);
|
||||
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 63, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Get/commit trace buffer
|
||||
|
||||
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer() {
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) return new OffloadBuffer{*this};
|
||||
#endif
|
||||
return new Buffer{*this};
|
||||
}
|
||||
|
||||
void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) {
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
OffloadBuffer* const offloadBufferp = static_cast<OffloadBuffer*>(bufp);
|
||||
if (offloadBufferp->m_offloadBufferWritep) {
|
||||
m_offloadBufferWritep = offloadBufferp->m_offloadBufferWritep;
|
||||
return; // Buffer will be deleted by the offload thread
|
||||
}
|
||||
}
|
||||
#endif
|
||||
delete bufp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Configure
|
||||
|
||||
void VerilatedFst::configure(const VerilatedTraceConfig& config) {
|
||||
// If at least one model requests the FST writer thread, then use it
|
||||
m_useFstWriterThread |= config.m_useFstWriterThread;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstBuffer implementation
|
||||
|
||||
//=============================================================================
|
||||
// Trace rendering primitives
|
||||
|
||||
// Note: emit* are only ever called from one place (full* in
|
||||
// verilated_trace_imp.cpp, which is included in this file at the top),
|
||||
// verilated_trace_imp.h, which is included in this file at the top),
|
||||
// so always inline them.
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitBit(vluint32_t code, CData newval) {
|
||||
void VerilatedFstBuffer::emitBit(uint32_t code, CData newval) {
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], newval ? "1" : "0");
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitCData(vluint32_t code, CData newval, int bits) {
|
||||
void VerilatedFstBuffer::emitCData(uint32_t code, CData newval, int bits) {
|
||||
char buf[VL_BYTESIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtCDataToStr(buf, newval << (VL_BYTESIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitSData(vluint32_t code, SData newval, int bits) {
|
||||
void VerilatedFstBuffer::emitSData(uint32_t code, SData newval, int bits) {
|
||||
char buf[VL_SHORTSIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtSDataToStr(buf, newval << (VL_SHORTSIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitIData(vluint32_t code, IData newval, int bits) {
|
||||
void VerilatedFstBuffer::emitIData(uint32_t code, IData newval, int bits) {
|
||||
char buf[VL_IDATASIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtIDataToStr(buf, newval << (VL_IDATASIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitQData(vluint32_t code, QData newval, int bits) {
|
||||
void VerilatedFstBuffer::emitQData(uint32_t code, QData newval, int bits) {
|
||||
char buf[VL_QUADSIZE];
|
||||
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
|
||||
cvtQDataToStr(buf, newval << (VL_QUADSIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitWData(vluint32_t code, const WData* newvalp, int bits) {
|
||||
void VerilatedFstBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
int words = VL_WORDS_I(bits);
|
||||
char* wp = m_strbuf;
|
||||
// Convert the most significant word
|
||||
@@ -296,6 +340,6 @@ void VerilatedFst::emitWData(vluint32_t code, const WData* newvalp, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitDouble(vluint32_t code, double newval) {
|
||||
void VerilatedFstBuffer::emitDouble(uint32_t code, double newval) {
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], &newval);
|
||||
}
|
||||
|
||||
+94
-42
@@ -31,60 +31,64 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedFstBuffer;
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFst
|
||||
// Base class to create a Verilator FST dump
|
||||
// This is an internally used class - see VerilatedFstC for what to call from applications
|
||||
|
||||
class VerilatedFst final : public VerilatedTrace<VerilatedFst> {
|
||||
class VerilatedFst final : public VerilatedTrace<VerilatedFst, VerilatedFstBuffer> {
|
||||
public:
|
||||
using Super = VerilatedTrace<VerilatedFst, VerilatedFstBuffer>;
|
||||
|
||||
private:
|
||||
// Give the superclass access to private bits (to avoid virtual functions)
|
||||
friend class VerilatedTrace<VerilatedFst>;
|
||||
friend VerilatedFstBuffer; // Give the buffer access to the private bits
|
||||
|
||||
//=========================================================================
|
||||
// FST specific internals
|
||||
|
||||
void* m_fst;
|
||||
std::map<vluint32_t, fstHandle> m_code2symbol;
|
||||
void* m_fst = nullptr;
|
||||
std::map<uint32_t, fstHandle> m_code2symbol;
|
||||
std::map<int, fstEnumHandle> m_local2fstdtype;
|
||||
std::list<std::string> m_curScope;
|
||||
fstHandle* m_symbolp = nullptr; // same as m_code2symbol, but as an array
|
||||
char* m_strbuf = nullptr; // String buffer long enough to hold maxBits() chars
|
||||
|
||||
bool m_useFstWriterThread = false; // Whether to use the separate FST writer thread
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedFst);
|
||||
void declare(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, bool bussed, int msb, int lsb);
|
||||
|
||||
protected:
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Implementations of protected virtual methods for VerilatedTrace
|
||||
virtual void emitTimeChange(vluint64_t timeui) override;
|
||||
// Called when the trace moves forward to a new time point
|
||||
void emitTimeChange(uint64_t timeui) override;
|
||||
|
||||
// Hooks called from VerilatedTrace
|
||||
virtual bool preFullDump() override { return isOpen(); }
|
||||
virtual bool preChangeDump() override { return isOpen(); }
|
||||
bool preFullDump() override { return isOpen(); }
|
||||
bool preChangeDump() override { return isOpen(); }
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTrace. These are
|
||||
// called from only one place (namely full*) so always inline them.
|
||||
inline void emitBit(vluint32_t code, CData newval);
|
||||
inline void emitCData(vluint32_t code, CData newval, int bits);
|
||||
inline void emitSData(vluint32_t code, SData newval, int bits);
|
||||
inline void emitIData(vluint32_t code, IData newval, int bits);
|
||||
inline void emitQData(vluint32_t code, QData newval, int bits);
|
||||
inline void emitWData(vluint32_t code, const WData* newvalp, int bits);
|
||||
inline void emitDouble(vluint32_t code, double newval);
|
||||
// Trace buffer management
|
||||
Buffer* getTraceBuffer() override;
|
||||
void commitTraceBuffer(Buffer*) override;
|
||||
|
||||
// Configure sub-class
|
||||
void configure(const VerilatedTraceConfig&) override;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
// (All must be threadsafe)
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit VerilatedFst(void* fst = nullptr);
|
||||
~VerilatedFst();
|
||||
|
||||
// METHODS - All must be thread safe
|
||||
// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Close the file
|
||||
@@ -97,32 +101,75 @@ public:
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
// Inside dumping routines, declare a data type
|
||||
void declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
|
||||
unsigned int minValbits, const char** itemNamesp, const char** itemValuesp);
|
||||
|
||||
// Inside dumping routines, declare a signal
|
||||
void declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
void declBit(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
void declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
void declBus(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
void declQuad(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
void declArray(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
void declDouble(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
|
||||
void declDTypeEnum(int dtypenum, const char* name, uint32_t elements, unsigned int minValbits,
|
||||
const char** itemNamesp, const char** itemValuesp);
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// Declare specialization here as it's used in VerilatedFstC just below
|
||||
template <> void VerilatedTrace<VerilatedFst>::dump(vluint64_t timeui);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const std::string& unit);
|
||||
template <>
|
||||
void VerilatedFst::Super::dump(uint64_t time);
|
||||
template <>
|
||||
void VerilatedFst::Super::set_time_unit(const char* unitp);
|
||||
template <>
|
||||
void VerilatedFst::Super::set_time_unit(const std::string& unit);
|
||||
template <>
|
||||
void VerilatedFst::Super::set_time_resolution(const char* unitp);
|
||||
template <>
|
||||
void VerilatedFst::Super::set_time_resolution(const std::string& unit);
|
||||
template <>
|
||||
void VerilatedFst::Super::dumpvars(int level, const std::string& hier);
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstBuffer
|
||||
|
||||
class VerilatedFstBuffer VL_NOT_FINAL {
|
||||
// Give the trace file access to the private bits
|
||||
friend VerilatedFst;
|
||||
friend VerilatedFst::Super;
|
||||
friend VerilatedFst::Buffer;
|
||||
friend VerilatedFst::OffloadBuffer;
|
||||
|
||||
VerilatedFst& m_owner; // Trace file owning this buffer. Required by subclasses.
|
||||
|
||||
// The FST file handle
|
||||
void* const m_fst = m_owner.m_fst;
|
||||
// code to fstHande map, as an array
|
||||
const fstHandle* const m_symbolp = m_owner.m_symbolp;
|
||||
// String buffer long enough to hold maxBits() chars
|
||||
char* const m_strbuf = m_owner.m_strbuf;
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit VerilatedFstBuffer(VerilatedFst& owner)
|
||||
: m_owner{owner} {}
|
||||
virtual ~VerilatedFstBuffer() = default;
|
||||
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTraceBuffer interface
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
|
||||
// called from only one place (the full* methods), so always inline them.
|
||||
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstC
|
||||
/// Create a FST dump file in C standalone (no SystemC) simulations.
|
||||
@@ -154,12 +201,12 @@ public:
|
||||
/// Write one cycle of dump data
|
||||
/// Call with the current context's time just after eval'ed,
|
||||
/// e.g. ->dump(contextp->time())
|
||||
void dump(vluint64_t timeui) { m_sptrace.dump(timeui); }
|
||||
void dump(uint64_t timeui) { m_sptrace.dump(timeui); }
|
||||
/// Write one cycle of dump data - backward compatible and to reduce
|
||||
/// conversion warnings. It's better to use a vluint64_t time instead.
|
||||
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
/// conversion warnings. It's better to use a uint64_t time instead.
|
||||
void dump(double timestamp) { dump(static_cast<uint64_t>(timestamp)); }
|
||||
void dump(uint32_t timestamp) { dump(static_cast<uint64_t>(timestamp)); }
|
||||
void dump(int timestamp) { dump(static_cast<uint64_t>(timestamp)); }
|
||||
|
||||
// METHODS - Internal/backward compatible
|
||||
// \protectedsection
|
||||
@@ -178,9 +225,14 @@ public:
|
||||
void set_time_resolution(const std::string& unit) VL_MT_SAFE {
|
||||
m_sptrace.set_time_resolution(unit);
|
||||
}
|
||||
// Set variables to dump, using $dumpvars format
|
||||
// If level = 0, dump everything and hier is then ignored
|
||||
void dumpvars(int level, const std::string& hier) VL_MT_SAFE {
|
||||
m_sptrace.dumpvars(level, hier);
|
||||
}
|
||||
|
||||
// Internal class access
|
||||
inline VerilatedFst* spTrace() { return &m_sptrace; }
|
||||
VerilatedFst* spTrace() { return &m_sptrace; }
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_fst_sc.h"
|
||||
|
||||
//======================================================================
|
||||
@@ -30,7 +31,7 @@
|
||||
void VerilatedFstSc::open(const char* filename) {
|
||||
if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {
|
||||
vl_fatal(__FILE__, __LINE__, "VerilatedFstSc",
|
||||
("%Error: VerilatedFstSc::open(\"" + std::string(filename)
|
||||
("%Error: VerilatedFstSc::open(\"" + std::string{filename}
|
||||
+ "\") is called before sc_core::sc_start(). "
|
||||
"Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.")
|
||||
.c_str());
|
||||
@@ -63,9 +64,6 @@ void VerilatedFstSc::trace(const unsigned int&, const std::string&, const char**
|
||||
DECL_TRACE_METHOD_B( unsigned short )
|
||||
DECL_TRACE_METHOD_B( unsigned int )
|
||||
DECL_TRACE_METHOD_B( unsigned long )
|
||||
#ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
DECL_TRACE_METHOD_B( char )
|
||||
DECL_TRACE_METHOD_B( short )
|
||||
DECL_TRACE_METHOD_B( int )
|
||||
|
||||
+14
-13
@@ -23,8 +23,9 @@
|
||||
#define _VERILATED_FST_SC_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_sc.h"
|
||||
|
||||
#include "verilated_fst_c.h"
|
||||
#include "verilated_sc.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -51,37 +52,39 @@ public:
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
}
|
||||
virtual ~VerilatedFstSc() { close(); }
|
||||
~VerilatedFstSc() override { close(); }
|
||||
|
||||
// METHODS
|
||||
/// Called by SystemC simulate()
|
||||
virtual void cycle(bool delta_cycle) {
|
||||
void cycle(bool delta_cycle) override {
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
}
|
||||
|
||||
// Override VerilatedFstC. Must be called after starting simulation.
|
||||
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
|
||||
// Note: this is not a virtual function in the base class, so no 'override'
|
||||
virtual void open(const char* filename) VL_MT_SAFE;
|
||||
|
||||
private:
|
||||
/// Fake outs for linker
|
||||
|
||||
#ifdef NC_SYSTEMC
|
||||
// Cadence Incisive has these as abstract functions so we must create them
|
||||
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
|
||||
void set_time_unit(int exponent10_seconds) override {} // deprecated
|
||||
#endif
|
||||
virtual void set_time_unit(double v, sc_time_unit tu) {} // LCOV_EXCL_LINE
|
||||
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
|
||||
|
||||
//--------------------------------------------------
|
||||
// SystemC 2.1.v1
|
||||
#define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const std::string& name);
|
||||
#define DECL_TRACE_METHOD_A(tp) void trace(const tp& object, const std::string& name) override;
|
||||
#define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace(const tp& object, const std::string& name, int width);
|
||||
void trace(const tp& object, const std::string& name, int width) override;
|
||||
|
||||
virtual void write_comment(const std::string&);
|
||||
virtual void trace(const unsigned int&, const std::string&, const char**);
|
||||
void write_comment(const std::string&) override;
|
||||
void trace(const unsigned int&, const std::string&, const char**) override;
|
||||
|
||||
// clang-format off
|
||||
// Formatting matches that of sc_trace.h
|
||||
// LCOV_EXCL_START
|
||||
#if (SYSTEMC_VERSION >= 20171012)
|
||||
DECL_TRACE_METHOD_A( sc_event )
|
||||
DECL_TRACE_METHOD_A( sc_time )
|
||||
@@ -95,9 +98,6 @@ private:
|
||||
DECL_TRACE_METHOD_B( unsigned short )
|
||||
DECL_TRACE_METHOD_B( unsigned int )
|
||||
DECL_TRACE_METHOD_B( unsigned long )
|
||||
#ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
DECL_TRACE_METHOD_B( char )
|
||||
DECL_TRACE_METHOD_B( short )
|
||||
DECL_TRACE_METHOD_B( int )
|
||||
@@ -119,6 +119,7 @@ private:
|
||||
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_bv_base )
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_lv_base )
|
||||
// LCOV_EXCL_STOP
|
||||
// clang-format on
|
||||
|
||||
#undef DECL_TRACE_METHOD_A
|
||||
|
||||
+80
-90
@@ -90,7 +90,7 @@ extern WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp);
|
||||
extern IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE;
|
||||
extern IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE;
|
||||
inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) {
|
||||
const vluint64_t rnd = vl_rand64();
|
||||
const uint64_t rnd = vl_rand64();
|
||||
if (VL_LIKELY(hi > lo)) {
|
||||
// (hi - lo + 1) can be zero when hi is UINT_MAX and lo is zero
|
||||
if (VL_UNLIKELY(hi - lo + 1 == 0)) return rnd;
|
||||
@@ -112,15 +112,6 @@ extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp);
|
||||
/// Zero reset a signal (slow - else use VL_ZERO_W)
|
||||
extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp);
|
||||
|
||||
#if VL_THREADED
|
||||
/// Return high-precision counter for profiling, or 0x0 if not available
|
||||
inline QData VL_RDTSC_Q() {
|
||||
vluint64_t val;
|
||||
VL_RDTSC(val);
|
||||
return val;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
|
||||
const VerilatedContext* contextp) VL_MT_SAFE;
|
||||
|
||||
@@ -155,17 +146,13 @@ extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp);
|
||||
extern IData VL_SYSTEM_IQ(QData lhs);
|
||||
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
|
||||
|
||||
extern IData VL_TESTPLUSARGS_I(const char* formatp);
|
||||
extern IData VL_TESTPLUSARGS_I(const std::string& format);
|
||||
extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish
|
||||
|
||||
//=========================================================================
|
||||
// Base macros
|
||||
|
||||
// Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
|
||||
#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit)))
|
||||
#define VL_BITISSET_Q(data, bit) ((data) & (1ULL << VL_BITBIT_Q(bit)))
|
||||
#define VL_BITISSET_E(data, bit) ((data) & (VL_EUL(1) << VL_BITBIT_E(bit)))
|
||||
#define VL_BITISSET_W(data, bit) ((data)[VL_BITWORD_E(bit)] & (VL_EUL(1) << VL_BITBIT_E(bit)))
|
||||
#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
|
||||
@@ -223,29 +210,27 @@ static inline QData VL_CVT_Q_D(double lhs) VL_PURE {
|
||||
// Return double from lhs (numeric) unsigned
|
||||
double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
|
||||
static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE {
|
||||
return static_cast<double>(static_cast<vluint32_t>(lhs));
|
||||
return static_cast<double>(static_cast<uint32_t>(lhs));
|
||||
}
|
||||
static inline double VL_ITOR_D_Q(int, QData lhs) VL_PURE {
|
||||
return static_cast<double>(static_cast<vluint64_t>(lhs));
|
||||
return static_cast<double>(static_cast<uint64_t>(lhs));
|
||||
}
|
||||
// Return double from lhs (numeric) signed
|
||||
double VL_ISTOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
|
||||
static inline double VL_ISTOR_D_I(int lbits, IData lhs) VL_PURE {
|
||||
if (lbits == 32) return static_cast<double>(static_cast<vlsint32_t>(lhs));
|
||||
if (lbits == 32) return static_cast<double>(static_cast<int32_t>(lhs));
|
||||
VlWide<VL_WQ_WORDS_E> lwp;
|
||||
VL_SET_WI(lwp, lhs);
|
||||
return VL_ISTOR_D_W(lbits, lwp);
|
||||
}
|
||||
static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_PURE {
|
||||
if (lbits == 64) return static_cast<double>(static_cast<vlsint64_t>(lhs));
|
||||
if (lbits == 64) return static_cast<double>(static_cast<int64_t>(lhs));
|
||||
VlWide<VL_WQ_WORDS_E> lwp;
|
||||
VL_SET_WQ(lwp, lhs);
|
||||
return VL_ISTOR_D_W(lbits, lwp);
|
||||
}
|
||||
// Return QData from double (numeric)
|
||||
static inline IData VL_RTOI_I_D(double lhs) VL_PURE {
|
||||
return static_cast<vlsint32_t>(VL_TRUNC(lhs));
|
||||
}
|
||||
static inline IData VL_RTOI_I_D(double lhs) VL_PURE { return static_cast<int32_t>(VL_TRUNC(lhs)); }
|
||||
|
||||
// Sign extend such that if MSB set, we get ffff_ffff, else 0s
|
||||
// (Requires clean input)
|
||||
@@ -271,51 +256,33 @@ extern void _vl_debug_print_w(int lbits, WDataInP const iwp);
|
||||
//=========================================================================
|
||||
// Pli macros
|
||||
|
||||
extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE;
|
||||
|
||||
// These are deprecated and used only to establish the default precision/units.
|
||||
// Use Verilator timescale-override for better control.
|
||||
// clang-format off
|
||||
#ifndef VL_TIME_PRECISION
|
||||
# ifdef VL_TIME_PRECISION_STR
|
||||
# define VL_TIME_PRECISION VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
|
||||
# else
|
||||
# define VL_TIME_PRECISION (-12) ///< Timescale default units if not in Verilog - picoseconds
|
||||
# endif
|
||||
#endif
|
||||
#ifndef VL_TIME_UNIT
|
||||
# ifdef VL_TIME_UNIT_STR
|
||||
# define VL_TIME_UNIT VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
|
||||
# else
|
||||
# define VL_TIME_UNIT (-12) ///< Timescale default units if not in Verilog - picoseconds
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(SYSTEMC_VERSION)
|
||||
/// Return current simulation time
|
||||
// Already defined: extern sc_time sc_time_stamp();
|
||||
inline vluint64_t vl_time_stamp64() { return sc_time_stamp().value(); }
|
||||
inline uint64_t vl_time_stamp64() { return sc_time_stamp().value(); }
|
||||
#else // Non-SystemC
|
||||
# if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY)
|
||||
# ifdef VL_TIME_STAMP64
|
||||
// vl_time_stamp64() may be optionally defined by the user to return time.
|
||||
// On MSVC++ weak symbols are not supported so must be declared, or define
|
||||
// VL_TIME_CONTEXT.
|
||||
extern vluint64_t vl_time_stamp64() VL_ATTR_WEAK;
|
||||
extern uint64_t vl_time_stamp64() VL_ATTR_WEAK;
|
||||
# else
|
||||
// sc_time_stamp() may be optionally defined by the user to return time.
|
||||
// On MSVC++ weak symbols are not supported so must be declared, or define
|
||||
// VL_TIME_CONTEXT.
|
||||
extern double sc_time_stamp() VL_ATTR_WEAK; // Verilator 4.032 and newer
|
||||
inline vluint64_t vl_time_stamp64() {
|
||||
inline uint64_t vl_time_stamp64() {
|
||||
// clang9.0.1 requires & although we really do want the weak symbol value
|
||||
return VL_LIKELY(&sc_time_stamp) ? static_cast<vluint64_t>(sc_time_stamp()) : 0;
|
||||
return VL_LIKELY(&sc_time_stamp) ? static_cast<uint64_t>(sc_time_stamp()) : 0;
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
inline vluint64_t VerilatedContext::time() const VL_MT_SAFE {
|
||||
uint64_t VerilatedContext::time() const VL_MT_SAFE {
|
||||
// When using non-default context, fastest path is return time
|
||||
if (VL_LIKELY(m_s.m_time)) return m_s.m_time;
|
||||
#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY))
|
||||
@@ -340,16 +307,22 @@ inline vluint64_t VerilatedContext::time() const VL_MT_SAFE {
|
||||
// Return time precision as multiplier of time units
|
||||
double vl_time_multiplier(int scale) VL_PURE;
|
||||
// Return power of 10. e.g. returns 100 if n==2
|
||||
vluint64_t vl_time_pow10(int n) VL_PURE;
|
||||
uint64_t vl_time_pow10(int n) VL_PURE;
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
/// Evaluate statement if Verilated::debug() enabled
|
||||
/// Evaluate statement if VL_DEBUG defined
|
||||
# define VL_DEBUG_IFDEF(stmt) \
|
||||
do { \
|
||||
stmt \
|
||||
} while (false)
|
||||
/// Evaluate statement if VL_DEBUG defined and Verilated::debug() enabled
|
||||
# define VL_DEBUG_IF(stmt) \
|
||||
do { \
|
||||
if (VL_UNLIKELY(Verilated::debug())) {stmt} \
|
||||
} while (false)
|
||||
#else
|
||||
// We intentionally do not compile the stmt to improve compile speed
|
||||
# define VL_DEBUG_IFDEF(stmt) do {} while (false)
|
||||
# define VL_DEBUG_IF(stmt) do {} while (false)
|
||||
#endif
|
||||
|
||||
@@ -475,10 +448,17 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
||||
{ \
|
||||
const int words = VL_WORDS_I(obits); \
|
||||
sc_biguint<(obits)> _butemp = (svar).read(); \
|
||||
for (int i = 0; i < words; ++i) { \
|
||||
int msb = ((i + 1) * VL_IDATASIZE) - 1; \
|
||||
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
|
||||
(owp)[i] = _butemp.range(msb, i * VL_IDATASIZE).to_uint(); \
|
||||
uint32_t* chunk = _butemp.get_raw(); \
|
||||
int32_t lsb = 0; \
|
||||
while (lsb < obits - BITS_PER_DIGIT) { \
|
||||
const uint32_t data = *chunk; \
|
||||
++chunk; \
|
||||
_vl_insert_WI(owp.data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \
|
||||
lsb += BITS_PER_DIGIT; \
|
||||
} \
|
||||
if (lsb < obits) { \
|
||||
const uint32_t msb_data = *chunk; \
|
||||
_vl_insert_WI(owp.data(), msb_data, obits - 1, lsb); \
|
||||
} \
|
||||
(owp)[words - 1] &= VL_MASK_E(obits); \
|
||||
}
|
||||
@@ -519,13 +499,23 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
|
||||
{ (svar).write(rd); }
|
||||
#define VL_ASSIGN_SBQ(obits, svar, rd) \
|
||||
{ (svar).write(rd); }
|
||||
#define VL_SC_BITS_PER_DIGIT 30 // This comes from sc_nbdefs.h BITS_PER_DIGIT
|
||||
#define VL_ASSIGN_SBW(obits, svar, rwp) \
|
||||
{ \
|
||||
sc_biguint<(obits)> _butemp; \
|
||||
for (int i = 0; i < VL_WORDS_I(obits); ++i) { \
|
||||
int msb = ((i + 1) * VL_IDATASIZE) - 1; \
|
||||
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
|
||||
_butemp.range(msb, i* VL_IDATASIZE) = (rwp)[i]; \
|
||||
int32_t lsb = 0; \
|
||||
uint32_t* chunk = _butemp.get_raw(); \
|
||||
while (lsb + VL_SC_BITS_PER_DIGIT < (obits)) { \
|
||||
static_assert(std::is_same<IData, EData>::value, "IData and EData missmatch"); \
|
||||
const uint32_t data = VL_SEL_IWII(lsb + VL_SC_BITS_PER_DIGIT + 1, (rwp).data(), lsb, \
|
||||
VL_SC_BITS_PER_DIGIT); \
|
||||
*chunk = data & VL_MASK_E(VL_SC_BITS_PER_DIGIT); \
|
||||
++chunk; \
|
||||
lsb += VL_SC_BITS_PER_DIGIT; \
|
||||
} \
|
||||
if (lsb < (obits)) { \
|
||||
const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \
|
||||
*chunk = msb_data & VL_MASK_E((obits)-lsb); \
|
||||
} \
|
||||
(svar).write(_butemp); \
|
||||
}
|
||||
@@ -880,46 +870,46 @@ static inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) V
|
||||
static inline IData VL_GTS_III(int lbits, IData lhs, IData rhs) VL_PURE {
|
||||
// For lbits==32, this becomes just a single instruction, otherwise ~5.
|
||||
// GCC 3.3.4 sign extension bugs on AMD64 architecture force us to use quad logic
|
||||
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
|
||||
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
|
||||
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
|
||||
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
|
||||
return lhs_signed > rhs_signed;
|
||||
}
|
||||
static inline IData VL_GTS_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
|
||||
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
|
||||
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
|
||||
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
|
||||
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
|
||||
return lhs_signed > rhs_signed;
|
||||
}
|
||||
|
||||
static inline IData VL_GTES_III(int lbits, IData lhs, IData rhs) VL_PURE {
|
||||
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
|
||||
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
|
||||
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
|
||||
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
|
||||
return lhs_signed >= rhs_signed;
|
||||
}
|
||||
static inline IData VL_GTES_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
|
||||
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
|
||||
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
|
||||
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
|
||||
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
|
||||
return lhs_signed >= rhs_signed;
|
||||
}
|
||||
|
||||
static inline IData VL_LTS_III(int lbits, IData lhs, IData rhs) VL_PURE {
|
||||
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
|
||||
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
|
||||
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
|
||||
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
|
||||
return lhs_signed < rhs_signed;
|
||||
}
|
||||
static inline IData VL_LTS_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
|
||||
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
|
||||
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
|
||||
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
|
||||
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
|
||||
return lhs_signed < rhs_signed;
|
||||
}
|
||||
|
||||
static inline IData VL_LTES_III(int lbits, IData lhs, IData rhs) VL_PURE {
|
||||
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
|
||||
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
|
||||
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
|
||||
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
|
||||
return lhs_signed <= rhs_signed;
|
||||
}
|
||||
static inline IData VL_LTES_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
|
||||
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
|
||||
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
|
||||
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
|
||||
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
|
||||
return lhs_signed <= rhs_signed;
|
||||
}
|
||||
|
||||
@@ -1013,13 +1003,13 @@ static inline WDataOutP VL_MUL_W(int words, WDataOutP owp, WDataInP const lwp,
|
||||
}
|
||||
|
||||
static inline IData VL_MULS_III(int lbits, IData lhs, IData rhs) VL_PURE {
|
||||
const vlsint32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs);
|
||||
const vlsint32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs);
|
||||
const int32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs);
|
||||
const int32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs);
|
||||
return lhs_signed * rhs_signed;
|
||||
}
|
||||
static inline QData VL_MULS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE {
|
||||
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
|
||||
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
|
||||
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
|
||||
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
|
||||
return lhs_signed * rhs_signed;
|
||||
}
|
||||
|
||||
@@ -1065,30 +1055,30 @@ static inline IData VL_DIVS_III(int lbits, IData lhs, IData rhs) VL_PURE {
|
||||
if (VL_UNLIKELY(rhs == 0)) return 0;
|
||||
// -MAX / -1 cannot be represented in twos complement, and will cause SIGFPE
|
||||
if (VL_UNLIKELY(lhs == 0x80000000 && rhs == 0xffffffff)) return 0;
|
||||
const vlsint32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
|
||||
const vlsint32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
|
||||
const int32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
|
||||
const int32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
|
||||
return lhs_signed / rhs_signed;
|
||||
}
|
||||
static inline QData VL_DIVS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE {
|
||||
if (VL_UNLIKELY(rhs == 0)) return 0;
|
||||
// -MAX / -1 cannot be represented in twos complement, and will cause SIGFPE
|
||||
if (VL_UNLIKELY(lhs == 0x8000000000000000ULL && rhs == 0xffffffffffffffffULL)) return 0;
|
||||
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
|
||||
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
|
||||
const int64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
|
||||
const int64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
|
||||
return lhs_signed / rhs_signed;
|
||||
}
|
||||
static inline IData VL_MODDIVS_III(int lbits, IData lhs, IData rhs) VL_PURE {
|
||||
if (VL_UNLIKELY(rhs == 0)) return 0;
|
||||
if (VL_UNLIKELY(lhs == 0x80000000 && rhs == 0xffffffff)) return 0;
|
||||
const vlsint32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
|
||||
const vlsint32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
|
||||
const int32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
|
||||
const int32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
|
||||
return lhs_signed % rhs_signed;
|
||||
}
|
||||
static inline QData VL_MODDIVS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE {
|
||||
if (VL_UNLIKELY(rhs == 0)) return 0;
|
||||
if (VL_UNLIKELY(lhs == 0x8000000000000000ULL && rhs == 0xffffffffffffffffULL)) return 0;
|
||||
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
|
||||
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
|
||||
const int64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
|
||||
const int64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
|
||||
return lhs_signed % rhs_signed;
|
||||
}
|
||||
|
||||
@@ -1419,8 +1409,8 @@ static inline IData VL_STREAML_FAST_III(int lbits, IData ld, IData rd_log2) VL_P
|
||||
// ret = 10324---
|
||||
IData ret = ld;
|
||||
if (rd_log2) {
|
||||
const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits
|
||||
const vluint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS)
|
||||
const uint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits
|
||||
const uint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS)
|
||||
const IData msbMask = VL_MASK_I(lbitsRem) << lbitsFloor; // mask to sel only bits in MSS
|
||||
ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem));
|
||||
}
|
||||
@@ -1439,8 +1429,8 @@ static inline QData VL_STREAML_FAST_QQI(int lbits, QData ld, IData rd_log2) VL_P
|
||||
// Pre-shift bits in most-significant slice (see comment in VL_STREAML_FAST_III)
|
||||
QData ret = ld;
|
||||
if (rd_log2) {
|
||||
const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2);
|
||||
const vluint32_t lbitsRem = lbits - lbitsFloor;
|
||||
const uint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2);
|
||||
const uint32_t lbitsRem = lbits - lbitsFloor;
|
||||
const QData msbMask = lbitsFloor == 64 ? 0ULL : VL_MASK_Q(lbitsRem) << lbitsFloor;
|
||||
ret = (ret & ~msbMask) | ((ret & msbMask) << ((1ULL << rd_log2) - lbitsRem));
|
||||
}
|
||||
@@ -1938,8 +1928,8 @@ static inline QData VL_RTOIROUND_Q_D(double lhs) VL_PURE {
|
||||
if (lhs == 0.0) return 0;
|
||||
const QData q = VL_CVT_Q_D(lhs);
|
||||
const int lsb = static_cast<int>((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52;
|
||||
const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
|
||||
vluint64_t out = 0;
|
||||
const uint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
|
||||
uint64_t out = 0;
|
||||
if (lsb < 0) {
|
||||
out = mantissa >> -lsb;
|
||||
} else if (lsb < 64) {
|
||||
@@ -1959,7 +1949,7 @@ static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) V
|
||||
if (lhs == 0.0) return owp;
|
||||
const QData q = VL_CVT_Q_D(lhs);
|
||||
const int lsb = static_cast<int>((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52;
|
||||
const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
|
||||
const uint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
|
||||
if (lsb < 0) {
|
||||
VL_SET_WQ(owp, mantissa >> -lsb);
|
||||
} else if (lsb < obits) {
|
||||
|
||||
+34
-34
@@ -34,6 +34,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <set>
|
||||
@@ -64,7 +65,7 @@ public:
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
vluint32_t m_mtaskId; // MTask that did enqueue
|
||||
uint32_t m_mtaskId; // MTask that did enqueue
|
||||
std::function<void()> m_cb; // Lambda to execute when message received
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -77,7 +78,7 @@ public:
|
||||
VerilatedMsg& operator=(const VerilatedMsg&) = default;
|
||||
VerilatedMsg& operator=(VerilatedMsg&&) = default;
|
||||
// METHODS
|
||||
vluint32_t mtaskId() const { return m_mtaskId; }
|
||||
uint32_t mtaskId() const { return m_mtaskId; }
|
||||
// Execute the lambda function
|
||||
void run() const { m_cb(); }
|
||||
};
|
||||
@@ -88,9 +89,9 @@ public:
|
||||
class VerilatedEvalMsgQueue final {
|
||||
using VerilatedThreadQueue = std::multiset<VerilatedMsg, VerilatedMsg::Cmp>;
|
||||
|
||||
std::atomic<vluint64_t> m_depth; // Current depth of queue (see comments below)
|
||||
std::atomic<uint64_t> m_depth; // Current depth of queue (see comments below)
|
||||
|
||||
VerilatedMutex m_mutex; // Mutex protecting queue
|
||||
mutable VerilatedMutex m_mutex; // Mutex protecting queue
|
||||
VerilatedThreadQueue m_queue VL_GUARDED_BY(m_mutex); // Message queue
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -142,11 +143,10 @@ class VerilatedThreadMsgQueue final {
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedThreadMsgQueue() {}
|
||||
~VerilatedThreadMsgQueue() {
|
||||
// The only call of this with a non-empty queue is a fatal error.
|
||||
// So this does not flush the queue, as the destination queue is not known to this class.
|
||||
}
|
||||
VerilatedThreadMsgQueue() = default;
|
||||
~VerilatedThreadMsgQueue() = default;
|
||||
// The only call of destructor with a non-empty queue is a fatal error.
|
||||
// So this does not flush the queue, as the destination queue is not known to this class.
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(VerilatedThreadMsgQueue);
|
||||
@@ -187,11 +187,11 @@ class VerilatedFpList final {
|
||||
|
||||
public:
|
||||
using const_iterator = FILE* const*;
|
||||
explicit VerilatedFpList() {}
|
||||
explicit VerilatedFpList() = default;
|
||||
const_iterator begin() const { return m_fp; }
|
||||
const_iterator end() const { return m_fp + m_sz; }
|
||||
std::size_t size() const { return m_sz; }
|
||||
std::size_t capacity() const { return 31; }
|
||||
static std::size_t capacity() { return 31; }
|
||||
void push_back(FILE* fd) {
|
||||
if (VL_LIKELY(size() < capacity())) m_fp[m_sz++] = fd;
|
||||
}
|
||||
@@ -235,17 +235,16 @@ class VerilatedContextImp final : VerilatedContext {
|
||||
return s_s;
|
||||
}
|
||||
|
||||
private:
|
||||
public: // But only for verilated*.cpp
|
||||
// CONSTRUCTORS - no data can live here, use only VerilatedContext
|
||||
VerilatedContextImp() = delete;
|
||||
~VerilatedContextImp() = delete;
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - extending into VerilatedContext, call via impp()->
|
||||
|
||||
// Random seed handling
|
||||
vluint64_t randSeedDefault64() const VL_MT_SAFE;
|
||||
static vluint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
|
||||
uint64_t randSeedDefault64() const VL_MT_SAFE;
|
||||
static uint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
|
||||
|
||||
// METHODS - timeformat
|
||||
int timeFormatUnits() const VL_MT_SAFE {
|
||||
@@ -271,13 +270,12 @@ public: // But only for verilated*.cpp
|
||||
std::string argPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex);
|
||||
std::pair<int, char**> argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex);
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - scope name
|
||||
// METHODS - scope name - INTERNAL only for verilated*.cpp
|
||||
void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
|
||||
void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE;
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - file IO
|
||||
// METHODS - file IO - INTERNAL only for verilated*.cpp
|
||||
|
||||
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
if (m_fdFreeMct.empty()) return 0;
|
||||
@@ -297,7 +295,7 @@ public: // But only for verilated*.cpp
|
||||
const std::size_t start = std::max<std::size_t>(31UL + 1UL + 3UL, m_fdps.size());
|
||||
const std::size_t excess = 10;
|
||||
m_fdps.resize(start + excess);
|
||||
std::fill(m_fdps.begin() + start, m_fdps.end(), (FILE*)0);
|
||||
std::fill(m_fdps.begin() + start, m_fdps.end(), static_cast<FILE*>(nullptr));
|
||||
m_fdFree.resize(excess);
|
||||
for (std::size_t i = 0, id = start; i < m_fdFree.size(); ++i, ++id) {
|
||||
m_fdFree[i] = id;
|
||||
@@ -316,14 +314,14 @@ public: // But only for verilated*.cpp
|
||||
IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return ~0U; // -1
|
||||
return static_cast<IData>(
|
||||
std::fseek(*fdlist.begin(), static_cast<long>(offset), static_cast<int>(origin)));
|
||||
}
|
||||
IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return 0;
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return ~0U; // -1
|
||||
return static_cast<IData>(std::ftell(*fdlist.begin()));
|
||||
}
|
||||
void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
@@ -343,7 +341,7 @@ public: // But only for verilated*.cpp
|
||||
if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr
|
||||
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
|
||||
std::fclose(m_fdps[idx]);
|
||||
m_fdps[idx] = (FILE*)0;
|
||||
m_fdps[idx] = nullptr;
|
||||
m_fdFree.push_back(idx);
|
||||
} else {
|
||||
// MCD case
|
||||
@@ -358,7 +356,7 @@ public: // But only for verilated*.cpp
|
||||
}
|
||||
}
|
||||
}
|
||||
inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
|
||||
const VerilatedLockGuard lock{m_fdMutex};
|
||||
const VerilatedFpList fdlist = fdToFpList(fdi);
|
||||
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
|
||||
@@ -394,7 +392,11 @@ protected:
|
||||
// METHODS - protected
|
||||
void commandArgsAddGuts(int argc, const char** argv);
|
||||
void commandArgVl(const std::string& arg);
|
||||
bool commandArgVlValue(const std::string& arg, const std::string& prefix, std::string& valuer);
|
||||
bool commandArgVlString(const std::string& arg, const std::string& prefix,
|
||||
std::string& valuer);
|
||||
bool commandArgVlUint64(const std::string& arg, const std::string& prefix, uint64_t& valuer,
|
||||
uint64_t min = std::numeric_limits<uint64_t>::min(),
|
||||
uint64_t max = std::numeric_limits<uint64_t>::max());
|
||||
void commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex);
|
||||
};
|
||||
|
||||
@@ -459,13 +461,12 @@ public:
|
||||
// METHODS - debug
|
||||
static void versionDump() VL_MT_SAFE;
|
||||
|
||||
public:
|
||||
// METHODS - user scope tracking
|
||||
// We implement this as a single large map instead of one map per scope.
|
||||
// There's often many more scopes than userdata's and thus having a ~48byte
|
||||
// per map overhead * N scopes would take much more space and cache thrashing.
|
||||
// As scopep's are pointers, this implicitly handles multiple Context's
|
||||
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
|
||||
static void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
|
||||
if (it != s().m_userMap.end()) {
|
||||
@@ -474,17 +475,18 @@ public:
|
||||
s().m_userMap.emplace(std::make_pair(scopep, userKey), userData);
|
||||
}
|
||||
}
|
||||
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
static void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
|
||||
if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
public: // But only for verilated.cpp
|
||||
// METHODS - But only for verilated.cpp
|
||||
|
||||
// Symbol table destruction cleans up the entries for each scope.
|
||||
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope on destruction, so we simply iterate.
|
||||
// Slow ok - called once/scope on destruction, so we only iterate.
|
||||
const VerilatedLockGuard lock{s().m_userMapMutex};
|
||||
for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
|
||||
if (it->first.first == scopep) {
|
||||
@@ -507,8 +509,7 @@ public: // But only for verilated.cpp
|
||||
}
|
||||
}
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - hierarchy
|
||||
// METHODS - hierarchy - only for verilated*.cpp
|
||||
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
|
||||
// Slow ok - called at construction for VPI accessible elements
|
||||
const VerilatedLockGuard lock{s().m_hierMapMutex};
|
||||
@@ -529,8 +530,7 @@ public: // But only for verilated*.cpp
|
||||
return &s().m_hierMap;
|
||||
}
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - export names
|
||||
// METHODS - export names - only for verilated*.cpp
|
||||
|
||||
// Each function prototype is converted to a function number which we
|
||||
// then use to index a 2D table also indexed by scope number, because we
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2012-2022 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated run-time profiling implementation code
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_profiler.h"
|
||||
|
||||
#if VL_THREADED
|
||||
#include "verilated_threads.h"
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
//=============================================================================
|
||||
// Globals
|
||||
|
||||
// Internal note: Globals may multi-construct, see verilated.cpp top.
|
||||
|
||||
VL_THREAD_LOCAL VlExecutionProfiler::ExecutionTrace VlExecutionProfiler::t_trace;
|
||||
|
||||
constexpr const char* const VlExecutionRecord::s_ascii[];
|
||||
|
||||
//=============================================================================
|
||||
// VlPgoProfiler implementation
|
||||
|
||||
uint16_t VlExecutionRecord::getcpu() {
|
||||
#if defined(__linux)
|
||||
return sched_getcpu(); // TODO: this is a system call. Not exactly cheap.
|
||||
#elif defined(__APPLE__) && !defined(__arm64__)
|
||||
uint32_t info[4];
|
||||
__cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
|
||||
// info[1] is EBX, bits 24-31 are APIC ID
|
||||
if ((info[3] & (1 << 9)) == 0) {
|
||||
return -1; // no APIC on chip
|
||||
} else {
|
||||
return (unsigned)info[1] >> 24;
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
return GetCurrentProcessorNumber();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlExecutionProfiler implementation
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
VlExecutionProfiler::VlExecutionProfiler(VerilatedContext& context)
|
||||
: m_context{context} {
|
||||
// Setup profiling on main thread
|
||||
setupThread(0);
|
||||
}
|
||||
|
||||
void VlExecutionProfiler::configure() {
|
||||
|
||||
if (VL_UNLIKELY(m_enabled)) {
|
||||
--m_windowCount;
|
||||
if (VL_UNLIKELY(m_windowCount == m_context.profExecWindow())) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start collection\n"););
|
||||
clear(); // Clear the profile after the cache warm-up cycles.
|
||||
m_tickBegin = VL_CPU_TICK();
|
||||
} else if (VL_UNLIKELY(m_windowCount == 0)) {
|
||||
const uint64_t tickEnd = VL_CPU_TICK();
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+ profile end\n"););
|
||||
const std::string& fileName = m_context.profExecFilename();
|
||||
dump(fileName.c_str(), tickEnd);
|
||||
m_enabled = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t startReq = m_context.profExecStart() + 1; // + 1, so we can start at time 0
|
||||
|
||||
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= m_context.profExecStart())) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start warmup\n"););
|
||||
VL_DEBUG_IF(assert(m_windowCount == 0););
|
||||
m_enabled = true;
|
||||
m_windowCount = m_context.profExecWindow() * 2;
|
||||
m_lastStartReq = startReq;
|
||||
}
|
||||
}
|
||||
|
||||
VerilatedVirtualBase* VlExecutionProfiler::construct(VerilatedContext& context) {
|
||||
VlExecutionProfiler* const selfp = new VlExecutionProfiler{context};
|
||||
#if VL_THREADED
|
||||
if (VlThreadPool* const threadPoolp = static_cast<VlThreadPool*>(context.threadPoolp())) {
|
||||
for (int i = 0; i < threadPoolp->numThreads(); ++i) {
|
||||
// Data to pass to worker thread initialization
|
||||
struct Data {
|
||||
VlExecutionProfiler* const selfp;
|
||||
const uint32_t threadId;
|
||||
} data{selfp, static_cast<uint32_t>(i + 1)};
|
||||
|
||||
// Initialize worker thread
|
||||
threadPoolp->workerp(i)->addTask(
|
||||
[](void* userp, bool) {
|
||||
Data* const datap = static_cast<Data*>(userp);
|
||||
datap->selfp->setupThread(datap->threadId);
|
||||
},
|
||||
&data);
|
||||
|
||||
// Wait until initializationis complete
|
||||
threadPoolp->workerp(i)->wait();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return selfp;
|
||||
}
|
||||
|
||||
void VlExecutionProfiler::setupThread(uint32_t threadId) {
|
||||
// Reserve some space in the thread-local profiling buffer, in order to try to avoid malloc
|
||||
// while profiling.
|
||||
t_trace.reserve(RESERVED_TRACE_CAPACITY);
|
||||
// Register thread-local buffer in list of all buffers
|
||||
bool exists;
|
||||
{
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
exists = !m_traceps.emplace(threadId, &t_trace).second;
|
||||
}
|
||||
if (VL_UNLIKELY(exists)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "multiple initialization of profiler on some thread");
|
||||
}
|
||||
}
|
||||
|
||||
void VlExecutionProfiler::clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
for (const auto& pair : m_traceps) {
|
||||
ExecutionTrace* const tracep = pair.second;
|
||||
const size_t reserve = roundUptoMultipleOf<RESERVED_TRACE_CAPACITY>(tracep->size());
|
||||
tracep->clear();
|
||||
tracep->reserve(reserve);
|
||||
}
|
||||
}
|
||||
|
||||
void VlExecutionProfiler::dump(const char* filenamep, uint64_t tickEnd)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+prof+exec writing to '%s'\n", filenamep););
|
||||
|
||||
FILE* const fp = std::fopen(filenamep, "w");
|
||||
if (VL_UNLIKELY(!fp)) { VL_FATAL_MT(filenamep, 0, "", "+prof+exec+file file not writable"); }
|
||||
|
||||
// TODO Perhaps merge with verilated_coverage output format, so can
|
||||
// have a common merging and reporting tool, etc.
|
||||
fprintf(fp, "VLPROFVERSION 2.0 # Verilator execution profile version 2.0\n");
|
||||
fprintf(fp, "VLPROF arg +verilator+prof+exec+start+%" PRIu64 "\n",
|
||||
Verilated::threadContextp()->profExecStart());
|
||||
fprintf(fp, "VLPROF arg +verilator+prof+exec+window+%u\n",
|
||||
Verilated::threadContextp()->profExecWindow());
|
||||
const unsigned threads = static_cast<unsigned>(m_traceps.size());
|
||||
fprintf(fp, "VLPROF stat threads %u\n", threads);
|
||||
#ifdef VL_THREADED
|
||||
fprintf(fp, "VLPROF stat yields %" PRIu64 "\n", VlMTaskVertex::yields());
|
||||
#endif
|
||||
|
||||
// Copy /proc/cpuinfo into this output so verilator_gantt can be run on
|
||||
// a different machine
|
||||
{
|
||||
const std::unique_ptr<std::ifstream> ifp{new std::ifstream("/proc/cpuinfo")};
|
||||
if (!ifp->fail()) {
|
||||
std::string line;
|
||||
while (std::getline(*ifp, line)) { fprintf(fp, "VLPROFPROC %s\n", line.c_str()); }
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& pair : m_traceps) {
|
||||
const uint32_t threadId = pair.first;
|
||||
ExecutionTrace* const tracep = pair.second;
|
||||
fprintf(fp, "VLPROFTHREAD %" PRIu32 "\n", threadId);
|
||||
|
||||
for (const VlExecutionRecord& er : *tracep) {
|
||||
const char* const name = VlExecutionRecord::s_ascii[static_cast<uint8_t>(er.m_type)];
|
||||
const uint64_t time = er.m_tick - m_tickBegin;
|
||||
fprintf(fp, "VLPROFEXEC %s %" PRIu64, name, time);
|
||||
|
||||
switch (er.m_type) {
|
||||
case VlExecutionRecord::Type::EVAL_BEGIN:
|
||||
case VlExecutionRecord::Type::EVAL_END:
|
||||
case VlExecutionRecord::Type::EVAL_LOOP_BEGIN:
|
||||
case VlExecutionRecord::Type::EVAL_LOOP_END:
|
||||
// No payload
|
||||
fprintf(fp, "\n");
|
||||
break;
|
||||
case VlExecutionRecord::Type::MTASK_BEGIN: {
|
||||
const auto& payload = er.m_payload.mtaskBegin;
|
||||
fprintf(fp, " id %u predictStart %u cpu %u\n", payload.m_id,
|
||||
payload.m_predictStart, payload.m_cpu);
|
||||
break;
|
||||
}
|
||||
case VlExecutionRecord::Type::MTASK_END: {
|
||||
const auto& payload = er.m_payload.mtaskEnd;
|
||||
fprintf(fp, " id %u predictCost %u\n", payload.m_id, payload.m_predictCost);
|
||||
break;
|
||||
}
|
||||
default: abort(); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(fp, "VLPROF stat ticks %" PRIu64 "\n", tickEnd - m_tickBegin);
|
||||
|
||||
std::fclose(fp);
|
||||
}
|
||||
+186
-44
@@ -12,7 +12,7 @@
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated general profiling header
|
||||
/// \brief Verilated run-time profiling header
|
||||
///
|
||||
/// This file is not part of the Verilated public-facing API.
|
||||
/// It is only for internal use by Verilated library routines.
|
||||
@@ -23,58 +23,206 @@
|
||||
#define VERILATOR_VERILATED_PROFILER_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h" // for VerilatedMutex and clang annotations
|
||||
|
||||
#include <deque>
|
||||
#include "verilated.h"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
class VlExecutionProfiler;
|
||||
class VlThreadPool;
|
||||
|
||||
//=============================================================================
|
||||
// Macros to simplify generated code
|
||||
|
||||
#define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \
|
||||
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfilerp->enabled())) \
|
||||
(vlSymsp)->__Vm_executionProfilerp->addRecord()
|
||||
|
||||
//=============================================================================
|
||||
// Return high-precision counter for profiling, or 0x0 if not available
|
||||
VL_ATTR_ALWINLINE QData VL_CPU_TICK() {
|
||||
uint64_t val;
|
||||
VL_GET_CPU_TICK(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Private class used by VlExecutionProfiler
|
||||
|
||||
#define _VL_FOREACH_APPLY(macro, arg) macro(arg, #arg)
|
||||
|
||||
// clang-format off
|
||||
#define FOREACH_VlExecutionRecord_TYPE(macro) \
|
||||
_VL_FOREACH_APPLY(macro, EVAL_BEGIN) \
|
||||
_VL_FOREACH_APPLY(macro, EVAL_END) \
|
||||
_VL_FOREACH_APPLY(macro, EVAL_LOOP_BEGIN) \
|
||||
_VL_FOREACH_APPLY(macro, EVAL_LOOP_END) \
|
||||
_VL_FOREACH_APPLY(macro, MTASK_BEGIN) \
|
||||
_VL_FOREACH_APPLY(macro, MTASK_END)
|
||||
// clang-format on
|
||||
|
||||
class VlExecutionRecord final {
|
||||
friend class VlExecutionProfiler;
|
||||
|
||||
// TYPES
|
||||
enum class Type : uint8_t {
|
||||
#define VL_FOREACH_MACRO(id, name) id,
|
||||
FOREACH_VlExecutionRecord_TYPE(VL_FOREACH_MACRO)
|
||||
#undef VL_FOREACH_MACRO
|
||||
};
|
||||
|
||||
static constexpr const char* const s_ascii[] = {
|
||||
#define VL_FOREACH_MACRO(id, name) name,
|
||||
FOREACH_VlExecutionRecord_TYPE(VL_FOREACH_MACRO)
|
||||
#undef VL_FOREACH_MACRO
|
||||
};
|
||||
|
||||
union Payload {
|
||||
struct {
|
||||
uint32_t m_id; // MTask id
|
||||
uint32_t m_predictStart; // Time scheduler predicted would start
|
||||
uint32_t m_cpu; // Executing CPU id
|
||||
} mtaskBegin;
|
||||
struct {
|
||||
uint32_t m_id; // MTask id
|
||||
uint32_t m_predictCost; // How long scheduler predicted would take
|
||||
} mtaskEnd;
|
||||
};
|
||||
|
||||
// STATE
|
||||
// Layout below allows efficient packing.
|
||||
const uint64_t m_tick = VL_CPU_TICK(); // Tick at construction
|
||||
Payload m_payload; // The record payload
|
||||
Type m_type; // The record type
|
||||
static_assert(alignof(uint64_t) >= alignof(Payload), "Padding not allowed");
|
||||
static_assert(alignof(Payload) >= alignof(Type), "Padding not allowed");
|
||||
|
||||
static uint16_t getcpu(); // Return currently executing CPU id
|
||||
|
||||
// Profile record, private class used only by this header
|
||||
class VerilatedProfilerRec final {
|
||||
const std::string m_name; // Hashed name of mtask/etc
|
||||
const size_t m_counterNumber = 0; // Which counter has data
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlExecutionRecord() = default;
|
||||
|
||||
// METHODS
|
||||
VerilatedProfilerRec(size_t counterNumber, const std::string& name)
|
||||
: m_name{name}
|
||||
, m_counterNumber{counterNumber} {}
|
||||
VerilatedProfilerRec() = default;
|
||||
size_t counterNumber() const { return m_counterNumber; }
|
||||
std::string name() const { return m_name; }
|
||||
void evalBegin() { m_type = Type::EVAL_BEGIN; }
|
||||
void evalEnd() { m_type = Type::EVAL_END; }
|
||||
void evalLoopBegin() { m_type = Type::EVAL_LOOP_BEGIN; }
|
||||
void evalLoopEnd() { m_type = Type::EVAL_LOOP_END; }
|
||||
void mtaskBegin(uint32_t id, uint32_t predictStart) {
|
||||
m_payload.mtaskBegin.m_id = id;
|
||||
m_payload.mtaskBegin.m_predictStart = predictStart;
|
||||
m_payload.mtaskBegin.m_cpu = getcpu();
|
||||
m_type = Type::MTASK_BEGIN;
|
||||
}
|
||||
void mtaskEnd(uint32_t id, uint32_t predictCost) {
|
||||
m_payload.mtaskEnd.m_id = id;
|
||||
m_payload.mtaskEnd.m_predictCost = predictCost;
|
||||
m_type = Type::MTASK_END;
|
||||
}
|
||||
};
|
||||
|
||||
// Create some number of bucketed profilers
|
||||
template <std::size_t T_Entries> class VerilatedProfiler final {
|
||||
// Counters are stored packed, all together, versus in VerilatedProfilerRec to
|
||||
// reduce cache effects
|
||||
std::array<vluint64_t, T_Entries> m_counters{}; // Time spent on this record
|
||||
std::deque<VerilatedProfilerRec> m_records; // Record information
|
||||
static_assert(std::is_trivially_destructible<VlExecutionRecord>::value,
|
||||
"VlExecutionRecord should be trivially destructible for fast buffer clearing");
|
||||
|
||||
//=============================================================================
|
||||
// VlExecutionProfiler is for collecting profiling data about model execution
|
||||
|
||||
class VlExecutionProfiler final : public VerilatedVirtualBase {
|
||||
// CONSTANTS
|
||||
|
||||
// In order to try to avoid dynamic memory allocations during the actual profiling phase,
|
||||
// trace buffers are pre-allocated to be able to hold [a multiple] of this many records.
|
||||
static constexpr size_t RESERVED_TRACE_CAPACITY = 4096;
|
||||
|
||||
// TYPES
|
||||
|
||||
// Execution traces are recorded into thread local vectors. We can append records of profiling
|
||||
// events to this vector with very low overhead, and then dump them out later. This prevents
|
||||
// the overhead of printf/malloc/IO from corrupting the profiling data. It's super cheap to
|
||||
// append a VlProfileRec struct on the end of a pre-allocated vector; this is the only cost we
|
||||
// pay in real-time during a profiling cycle. Internal note: Globals may multi-construct, see
|
||||
// verilated.cpp top.
|
||||
using ExecutionTrace = std::vector<VlExecutionRecord>;
|
||||
|
||||
// STATE
|
||||
VerilatedContext& m_context; // The context this profiler is under
|
||||
static VL_THREAD_LOCAL ExecutionTrace t_trace; // thread-local trace buffers
|
||||
mutable VerilatedMutex m_mutex;
|
||||
// Map from thread id to &t_trace of given thread
|
||||
std::map<uint32_t, ExecutionTrace*> m_traceps VL_GUARDED_BY(m_mutex);
|
||||
|
||||
bool m_enabled = false; // Is profiling currently enabled
|
||||
|
||||
uint64_t m_tickBegin = 0; // Sample time (rdtsc() on x86) at beginning of collection
|
||||
uint64_t m_lastStartReq = 0; // Last requested profiling start (in simulation time)
|
||||
uint32_t m_windowCount = 0; // Track our position in the cache warmup and profile window
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
explicit VlExecutionProfiler(VerilatedContext& context);
|
||||
~VlExecutionProfiler() override = default;
|
||||
|
||||
// METHODS
|
||||
|
||||
// Is profiling enabled
|
||||
bool enabled() const { return m_enabled; }
|
||||
// Append a trace record to the trace buffer of the current thread
|
||||
static VlExecutionRecord& addRecord() {
|
||||
t_trace.emplace_back();
|
||||
return t_trace.back();
|
||||
}
|
||||
// Configure profiler (called in beginning of 'eval')
|
||||
void configure();
|
||||
// Setup profiling on a particular thread;
|
||||
void setupThread(uint32_t threadId);
|
||||
// Clear all profiling data
|
||||
void clear() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Write profiling data into file
|
||||
void dump(const char* filenamep, uint64_t tickEnd) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
||||
// Passed to VerilatedContext to create the VlExecutionProfiler profiler instance
|
||||
static VerilatedVirtualBase* construct(VerilatedContext& context);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlPgoProfiler is for collecting profiling data for PGO
|
||||
|
||||
template <std::size_t T_Entries>
|
||||
class VlPgoProfiler final {
|
||||
// TYPES
|
||||
struct Record final {
|
||||
const std::string m_name; // Hashed name of mtask/etc
|
||||
const size_t m_counterNumber = 0; // Which counter has data
|
||||
};
|
||||
|
||||
// Counters are stored packed, all together to reduce cache effects
|
||||
std::array<uint64_t, T_Entries> m_counters; // Time spent on this record
|
||||
std::vector<Record> m_records; // Record information
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
VerilatedProfiler() = default;
|
||||
~VerilatedProfiler() = default;
|
||||
VlPgoProfiler() = default;
|
||||
~VlPgoProfiler() = default;
|
||||
void write(const char* modelp, const std::string& filename) VL_MT_SAFE;
|
||||
void addCounter(size_t counter, const std::string& name) {
|
||||
VL_DEBUG_IF(assert(counter < T_Entries););
|
||||
m_records.emplace_back(VerilatedProfilerRec{counter, name});
|
||||
m_records.emplace_back(Record{name, counter});
|
||||
}
|
||||
void startCounter(size_t counter) {
|
||||
vluint64_t val;
|
||||
VL_RDTSC(val);
|
||||
// -= so when we add end time in stopCounter, we already subtracted
|
||||
// out, without needing to hold another temporary
|
||||
m_counters[counter] -= val;
|
||||
}
|
||||
void stopCounter(size_t counter) {
|
||||
vluint64_t val;
|
||||
VL_RDTSC(val);
|
||||
m_counters[counter] += val;
|
||||
// -= so when we add end time in stopCounter, the net effect is adding the difference,
|
||||
// without needing to hold onto a temporary
|
||||
m_counters[counter] -= VL_CPU_TICK();
|
||||
}
|
||||
void stopCounter(size_t counter) { m_counters[counter] += VL_CPU_TICK(); }
|
||||
};
|
||||
|
||||
template <std::size_t T_Entries>
|
||||
void VerilatedProfiler<T_Entries>::write(const char* modelp,
|
||||
const std::string& filename) VL_MT_SAFE {
|
||||
void VlPgoProfiler<T_Entries>::write(const char* modelp, const std::string& filename) VL_MT_SAFE {
|
||||
static VerilatedMutex s_mutex;
|
||||
const VerilatedLockGuard lock{s_mutex};
|
||||
|
||||
@@ -82,20 +230,15 @@ void VerilatedProfiler<T_Entries>::write(const char* modelp,
|
||||
// So when we have multiple models in an executable, possibly even
|
||||
// running on different threads, each will have a different symtab so
|
||||
// each will collect is own data correctly. However when each is
|
||||
// destroid we need to get all the data, not keep overwriting and only
|
||||
// destroyed we need to get all the data, not keep overwriting and only
|
||||
// get the last model's data.
|
||||
static bool s_firstCall = true;
|
||||
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+prof+vlt+file writing to '%s'\n", filename.c_str()););
|
||||
|
||||
FILE* fp = nullptr;
|
||||
if (!s_firstCall) fp = std::fopen(filename.c_str(), "a");
|
||||
if (VL_UNLIKELY(!fp))
|
||||
fp = std::fopen(filename.c_str(), "w"); // firstCall, or doesn't exist yet
|
||||
FILE* const fp = std::fopen(filename.c_str(), s_firstCall ? "w" : "a");
|
||||
if (VL_UNLIKELY(!fp)) {
|
||||
VL_FATAL_MT(filename.c_str(), 0, "", "+prof+vlt+file file not writable");
|
||||
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
|
||||
return; // LCOV_EXCL_LINE
|
||||
}
|
||||
s_firstCall = false;
|
||||
|
||||
@@ -104,10 +247,9 @@ void VerilatedProfiler<T_Entries>::write(const char* modelp,
|
||||
fprintf(fp, "// Verilated model profile-guided optimization data dump file\n");
|
||||
fprintf(fp, "`verilator_config\n");
|
||||
|
||||
for (const auto& it : m_records) {
|
||||
const std::string& name = it.name();
|
||||
for (const Record& rec : m_records) {
|
||||
fprintf(fp, "profile_data -model \"%s\" -mtask \"%s\" -cost 64'd%" PRIu64 "\n", modelp,
|
||||
name.c_str(), m_counters[it.counterNumber()]);
|
||||
rec.m_name.c_str(), m_counters[rec.m_counterNumber]);
|
||||
}
|
||||
|
||||
std::fclose(fp);
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
#define VERILATOR_VERILATED_SAVE_CPP_
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
|
||||
#include "verilated_save.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_imp.h"
|
||||
|
||||
#include <cerrno>
|
||||
@@ -38,13 +40,13 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef O_LARGEFILE // For example on WIN32
|
||||
#ifndef O_LARGEFILE // WIN32 headers omit this
|
||||
# define O_LARGEFILE 0
|
||||
#endif
|
||||
#ifndef O_NONBLOCK
|
||||
#ifndef O_NONBLOCK // WIN32 headers omit this
|
||||
# define O_NONBLOCK 0
|
||||
#endif
|
||||
#ifndef O_CLOEXEC
|
||||
#ifndef O_CLOEXEC // WIN32 headers omit this
|
||||
# define O_CLOEXEC 0
|
||||
#endif
|
||||
// clang-format on
|
||||
@@ -63,8 +65,8 @@ static const char* const VLTSAVE_TRAILER_STR = "vltsaved";
|
||||
bool VerilatedDeserialize::readDiffers(const void* __restrict datap,
|
||||
size_t size) VL_MT_UNSAFE_ONE {
|
||||
bufferCheck();
|
||||
const vluint8_t* __restrict dp = static_cast<const vluint8_t* __restrict>(datap);
|
||||
vluint8_t miss = 0;
|
||||
const uint8_t* __restrict dp = static_cast<const uint8_t* __restrict>(datap);
|
||||
uint8_t miss = 0;
|
||||
while (size--) miss |= (*dp++ ^ *m_cp++);
|
||||
return (miss != 0);
|
||||
}
|
||||
@@ -192,7 +194,7 @@ void VerilatedRestore::close() VL_MT_UNSAFE_ONE {
|
||||
void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
const vluint8_t* wp = m_bufp;
|
||||
const uint8_t* wp = m_bufp;
|
||||
while (true) {
|
||||
const ssize_t remaining = (m_cp - wp);
|
||||
if (remaining == 0) break;
|
||||
@@ -219,8 +221,8 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
// Move remaining characters down to start of buffer. (No memcpy, overlaps allowed)
|
||||
vluint8_t* rp = m_bufp;
|
||||
for (vluint8_t* sp = m_cp; sp < m_endp; *rp++ = *sp++) {} // Overlaps
|
||||
uint8_t* rp = m_bufp;
|
||||
for (uint8_t* sp = m_cp; sp < m_endp; *rp++ = *sp++) {} // Overlaps
|
||||
m_endp = m_bufp + (m_endp - m_cp);
|
||||
m_cp = m_bufp; // Reset buffer
|
||||
// Read into buffer starting at m_endp
|
||||
|
||||
+35
-33
@@ -23,6 +23,7 @@
|
||||
#define VERILATOR_VERILATED_SAVE_C_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include <string>
|
||||
@@ -40,8 +41,8 @@ class VerilatedSerialize VL_NOT_FINAL {
|
||||
protected:
|
||||
// MEMBERS
|
||||
// For speed, keep m_cp as the first member of this structure
|
||||
vluint8_t* m_cp; // Current pointer into m_bufp buffer
|
||||
vluint8_t* m_bufp; // Output buffer
|
||||
uint8_t* m_cp; // Current pointer into m_bufp buffer
|
||||
uint8_t* m_bufp; // Output buffer
|
||||
bool m_isOpen = false; // True indicates open file/stream
|
||||
std::string m_filename; // Filename, for error messages
|
||||
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
|
||||
@@ -58,7 +59,7 @@ protected:
|
||||
public:
|
||||
/// Construct
|
||||
VerilatedSerialize() {
|
||||
m_bufp = new vluint8_t[bufferSize()];
|
||||
m_bufp = new uint8_t[bufferSize()];
|
||||
m_cp = m_bufp;
|
||||
}
|
||||
/// Flish, close, and destruct
|
||||
@@ -77,12 +78,12 @@ public:
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
/// Write data to stream
|
||||
VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap;
|
||||
const uint8_t* __restrict dp = static_cast<const uint8_t* __restrict>(datap);
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
size_t blk = size;
|
||||
if (blk > bufferInsertSize()) blk = bufferInsertSize();
|
||||
const vluint8_t* __restrict maxp = dp + blk;
|
||||
const uint8_t* __restrict maxp = dp + blk;
|
||||
for (; dp < maxp; *m_cp++ = *dp++) {}
|
||||
size -= blk;
|
||||
}
|
||||
@@ -111,9 +112,9 @@ class VerilatedDeserialize VL_NOT_FINAL {
|
||||
protected:
|
||||
// MEMBERS
|
||||
// For speed, keep m_cp as the first member of this structure
|
||||
vluint8_t* m_cp; // Current pointer into m_bufp buffer
|
||||
vluint8_t* m_bufp; // Output buffer
|
||||
vluint8_t* m_endp = nullptr; // Last valid byte in m_bufp buffer
|
||||
uint8_t* m_cp; // Current pointer into m_bufp buffer
|
||||
uint8_t* m_bufp; // Output buffer
|
||||
uint8_t* m_endp = nullptr; // Last valid byte in m_bufp buffer
|
||||
bool m_isOpen = false; // True indicates open file/stream
|
||||
std::string m_filename; // Filename, for error messages
|
||||
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
|
||||
@@ -131,7 +132,7 @@ protected:
|
||||
public:
|
||||
/// Construct
|
||||
VerilatedDeserialize() {
|
||||
m_bufp = new vluint8_t[bufferSize()];
|
||||
m_bufp = new uint8_t[bufferSize()];
|
||||
m_cp = m_bufp;
|
||||
}
|
||||
/// Destruct
|
||||
@@ -150,12 +151,12 @@ public:
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
/// Read data from stream
|
||||
VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
vluint8_t* __restrict dp = static_cast<vluint8_t* __restrict>(datap);
|
||||
uint8_t* __restrict dp = static_cast<uint8_t* __restrict>(datap);
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
size_t blk = size;
|
||||
if (blk > bufferInsertSize()) blk = bufferInsertSize();
|
||||
const vluint8_t* __restrict maxp = dp + blk;
|
||||
const uint8_t* __restrict maxp = dp + blk;
|
||||
for (; dp < maxp; *dp++ = *m_cp++) {}
|
||||
size -= blk;
|
||||
}
|
||||
@@ -165,7 +166,7 @@ public:
|
||||
// Internal use:
|
||||
// Read a datum and compare with expected value
|
||||
VerilatedDeserialize& readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
|
||||
VerilatedDeserialize& readAssert(vluint64_t data) VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& readAssert(uint64_t data) VL_MT_UNSAFE_ONE {
|
||||
return readAssert(&data, sizeof(data));
|
||||
}
|
||||
|
||||
@@ -194,16 +195,16 @@ public:
|
||||
/// Construct new object
|
||||
VerilatedSave() = default;
|
||||
/// Flush, close and destruct
|
||||
virtual ~VerilatedSave() override { close(); }
|
||||
~VerilatedSave() override { close(); }
|
||||
// METHODS
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filenamep) VL_MT_UNSAFE_ONE;
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
||||
/// Flush and close the file
|
||||
virtual void close() override VL_MT_UNSAFE_ONE;
|
||||
void close() override VL_MT_UNSAFE_ONE;
|
||||
/// Flush data to file
|
||||
virtual void flush() override VL_MT_UNSAFE_ONE;
|
||||
void flush() override VL_MT_UNSAFE_ONE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
@@ -221,7 +222,7 @@ public:
|
||||
/// Construct new object
|
||||
VerilatedRestore() = default;
|
||||
/// Flush, close and destruct
|
||||
virtual ~VerilatedRestore() override { close(); }
|
||||
~VerilatedRestore() override { close(); }
|
||||
|
||||
// METHODS
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
@@ -229,35 +230,35 @@ public:
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
||||
/// Close the file
|
||||
virtual void close() override VL_MT_UNSAFE_ONE;
|
||||
virtual void flush() override VL_MT_UNSAFE_ONE {}
|
||||
virtual void fill() override VL_MT_UNSAFE_ONE;
|
||||
void close() override VL_MT_UNSAFE_ONE;
|
||||
void flush() override VL_MT_UNSAFE_ONE {}
|
||||
void fill() override VL_MT_UNSAFE_ONE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint64_t& rhs) {
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint64_t& rhs) {
|
||||
return os.write(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint64_t& rhs) {
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint64_t& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint32_t& rhs) {
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint32_t& rhs) {
|
||||
return os.write(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint32_t& rhs) {
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint32_t& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint16_t& rhs) {
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint16_t& rhs) {
|
||||
return os.write(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint16_t& rhs) {
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint16_t& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint8_t& rhs) {
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint8_t& rhs) {
|
||||
return os.write(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint8_t& rhs) {
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint8_t& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const bool& rhs) {
|
||||
@@ -279,15 +280,16 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) {
|
||||
return os.read(&rhs, sizeof(rhs));
|
||||
}
|
||||
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const std::string& rhs) {
|
||||
const vluint32_t len = rhs.length();
|
||||
const uint32_t len = rhs.length();
|
||||
os << len;
|
||||
return os.write(rhs.data(), len);
|
||||
}
|
||||
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& rhs) {
|
||||
vluint32_t len = 0;
|
||||
uint32_t len = 0;
|
||||
os >> len;
|
||||
rhs.resize(len);
|
||||
return os.read((void*)rhs.data(), len);
|
||||
// C cast is required below
|
||||
return os.read((void*)(rhs.data()), len);
|
||||
}
|
||||
VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp);
|
||||
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp);
|
||||
@@ -295,7 +297,7 @@ VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhs
|
||||
template <class T_Key, class T_Value>
|
||||
VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
|
||||
os << rhs.atDefault();
|
||||
const vluint32_t len = rhs.size();
|
||||
const uint32_t len = rhs.size();
|
||||
os << len;
|
||||
for (const auto& i : rhs) {
|
||||
const T_Key index = i.first; // Copy to get around const_iterator
|
||||
@@ -307,10 +309,10 @@ VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Val
|
||||
template <class T_Key, class T_Value>
|
||||
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
|
||||
os >> rhs.atDefault();
|
||||
vluint32_t len = 0;
|
||||
uint32_t len = 0;
|
||||
os >> len;
|
||||
rhs.clear();
|
||||
for (vluint32_t i = 0; i < len; ++i) {
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
T_Key index;
|
||||
T_Value value;
|
||||
os >> index;
|
||||
|
||||
@@ -39,10 +39,10 @@
|
||||
// This class is thread safe (though most of SystemC is not).
|
||||
class VlScBvExposer final : public sc_bv_base {
|
||||
public:
|
||||
static const vluint32_t* sp_datap(const sc_bv_base& base) VL_MT_SAFE {
|
||||
static const uint32_t* sp_datap(const sc_bv_base& base) VL_MT_SAFE {
|
||||
return static_cast<const VlScBvExposer*>(&base)->sp_datatp();
|
||||
}
|
||||
const vluint32_t* sp_datatp() const { return reinterpret_cast<vluint32_t*>(m_data); }
|
||||
const uint32_t* sp_datatp() const { return reinterpret_cast<uint32_t*>(m_data); }
|
||||
// Above reads this protected element in sc_bv_base:
|
||||
// sc_digit* m_data; // data array
|
||||
};
|
||||
|
||||
@@ -69,9 +69,9 @@ public:
|
||||
|
||||
class VerilatedVarProps VL_NOT_FINAL {
|
||||
// TYPES
|
||||
static constexpr vluint32_t MAGIC = 0xddc4f829UL;
|
||||
static constexpr uint32_t MAGIC = 0xddc4f829UL;
|
||||
// MEMBERS
|
||||
const vluint32_t m_magic; // Magic number
|
||||
const uint32_t m_magic; // Magic number
|
||||
const VerilatedVarType m_vltype; // Data type
|
||||
const VerilatedVarFlags m_vlflags; // Direction
|
||||
const int m_pdims; // Packed dimensions, 0 = none
|
||||
@@ -134,7 +134,6 @@ public:
|
||||
initUnpacked(ulims);
|
||||
}
|
||||
|
||||
public:
|
||||
~VerilatedVarProps() = default;
|
||||
// METHODS
|
||||
bool magicOk() const { return m_magic == MAGIC; }
|
||||
@@ -142,7 +141,7 @@ public:
|
||||
VerilatedVarFlags vldir() const {
|
||||
return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR);
|
||||
}
|
||||
vluint32_t entSize() const;
|
||||
uint32_t entSize() const;
|
||||
bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); }
|
||||
// DPI compatible C standard layout
|
||||
bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#define VERILATOR_VERILATED_SYMS_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_sym_props.h"
|
||||
|
||||
|
||||
+41
-156
@@ -22,10 +22,10 @@
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_threads.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -34,14 +34,12 @@
|
||||
|
||||
// Internal note: Globals may multi-construct, see verilated.cpp top.
|
||||
|
||||
std::atomic<vluint64_t> VlMTaskVertex::s_yields;
|
||||
|
||||
VL_THREAD_LOCAL VlThreadPool::ProfileTrace* VlThreadPool::t_profilep = nullptr;
|
||||
std::atomic<uint64_t> VlMTaskVertex::s_yields;
|
||||
|
||||
//=============================================================================
|
||||
// VlMTaskVertex
|
||||
|
||||
VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
|
||||
VlMTaskVertex::VlMTaskVertex(uint32_t upstreamDepCount)
|
||||
: m_upstreamDepsDone{0}
|
||||
, m_upstreamDepCount{upstreamDepCount} {
|
||||
assert(atomic_is_lock_free(&m_upstreamDepsDone));
|
||||
@@ -50,175 +48,62 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
|
||||
//=============================================================================
|
||||
// VlWorkerThread
|
||||
|
||||
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling)
|
||||
VlWorkerThread::VlWorkerThread(VerilatedContext* contextp)
|
||||
: m_ready_size{0}
|
||||
, m_poolp{poolp}
|
||||
, m_profiling{profiling} // Must init this last -- after setting up fields that it might read:
|
||||
, m_exiting{false}
|
||||
, m_cthread{startWorker, this}
|
||||
, m_contextp{contextp} {}
|
||||
, m_cthread{startWorker, this, contextp} {}
|
||||
|
||||
VlWorkerThread::~VlWorkerThread() {
|
||||
m_exiting.store(true, std::memory_order_release);
|
||||
wakeUp();
|
||||
shutdown();
|
||||
// The thread should exit; join it.
|
||||
m_cthread.join();
|
||||
}
|
||||
|
||||
void VlWorkerThread::workerLoop() {
|
||||
if (VL_UNLIKELY(m_profiling)) m_poolp->setupProfilingClientThread();
|
||||
|
||||
ExecRec work;
|
||||
work.m_fnp = nullptr;
|
||||
|
||||
while (true) {
|
||||
if (VL_LIKELY(!work.m_fnp)) dequeWork(&work);
|
||||
|
||||
// Do this here, not above, to avoid a race with the destructor.
|
||||
if (VL_UNLIKELY(m_exiting.load(std::memory_order_acquire))) break;
|
||||
|
||||
if (VL_LIKELY(work.m_fnp)) {
|
||||
work.m_fnp(work.m_selfp, work.m_evenCycle);
|
||||
work.m_fnp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(m_profiling)) m_poolp->tearDownProfilingClientThread();
|
||||
static void shutdownTask(void*, bool) { // LCOV_EXCL_LINE
|
||||
// Deliberately empty, we use the address of this function as a magic number
|
||||
}
|
||||
|
||||
void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
|
||||
Verilated::threadContextp(workerp->m_contextp);
|
||||
void VlWorkerThread::shutdown() { addTask(shutdownTask, nullptr); }
|
||||
|
||||
void VlWorkerThread::wait() {
|
||||
// Enqueue a task that sets this flag. Execution is in-order so this ensures completion.
|
||||
std::atomic<bool> flag{false};
|
||||
addTask([](void* flagp, bool) { static_cast<std::atomic<bool>*>(flagp)->store(true); }, &flag);
|
||||
// Spin wait
|
||||
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
|
||||
if (flag.load()) return;
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
// Yield wait
|
||||
while (!flag.load()) std::this_thread::yield();
|
||||
}
|
||||
|
||||
void VlWorkerThread::workerLoop() {
|
||||
ExecRec work;
|
||||
|
||||
// Wait for the first task without spinning, in case the thread is never actually used.
|
||||
dequeWork</* SpinWait: */ false>(&work);
|
||||
|
||||
while (true) {
|
||||
if (VL_UNLIKELY(work.m_fnp == shutdownTask)) break;
|
||||
work.m_fnp(work.m_selfp, work.m_evenCycle);
|
||||
// Wait for next task with spinning.
|
||||
dequeWork</* SpinWait: */ true>(&work);
|
||||
}
|
||||
}
|
||||
|
||||
void VlWorkerThread::startWorker(VlWorkerThread* workerp, VerilatedContext* contextp) {
|
||||
Verilated::threadContextp(contextp);
|
||||
workerp->workerLoop();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlThreadPool
|
||||
|
||||
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling)
|
||||
: m_profiling{profiling} {
|
||||
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
|
||||
const unsigned cpus = std::thread::hardware_concurrency();
|
||||
if (cpus < nThreads + 1) {
|
||||
static int warnedOnce = 0;
|
||||
if (!warnedOnce++) {
|
||||
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
|
||||
" --threads %d; may run slow.\n",
|
||||
cpus, nThreads + 1);
|
||||
}
|
||||
}
|
||||
// Create'em
|
||||
for (int i = 0; i < nThreads; ++i) {
|
||||
m_workers.push_back(new VlWorkerThread{this, contextp, profiling});
|
||||
}
|
||||
// Set up a profile buffer for the current thread too -- on the
|
||||
// assumption that it's the same thread that calls eval and may be
|
||||
// donated to run mtasks during the eval.
|
||||
if (VL_UNLIKELY(m_profiling)) setupProfilingClientThread();
|
||||
VlThreadPool::VlThreadPool(VerilatedContext* contextp, unsigned nThreads) {
|
||||
for (unsigned i = 0; i < nThreads; ++i) m_workers.push_back(new VlWorkerThread{contextp});
|
||||
}
|
||||
|
||||
VlThreadPool::~VlThreadPool() {
|
||||
// Each ~WorkerThread will wait for its thread to exit.
|
||||
for (auto& i : m_workers) delete i;
|
||||
if (VL_UNLIKELY(m_profiling)) tearDownProfilingClientThread();
|
||||
}
|
||||
|
||||
void VlThreadPool::tearDownProfilingClientThread() {
|
||||
assert(t_profilep);
|
||||
delete t_profilep;
|
||||
t_profilep = nullptr;
|
||||
}
|
||||
|
||||
void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
assert(!t_profilep);
|
||||
t_profilep = new ProfileTrace;
|
||||
// Reserve some space in the thread-local profiling buffer;
|
||||
// try not to malloc while collecting profiling.
|
||||
t_profilep->reserve(4096);
|
||||
{
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_allProfiles.insert(t_profilep);
|
||||
}
|
||||
}
|
||||
|
||||
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
for (const auto& profilep : m_allProfiles) {
|
||||
// Every thread's profile trace gets a copy of rec.
|
||||
profilep->emplace_back(rec);
|
||||
}
|
||||
}
|
||||
|
||||
void VlThreadPool::profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
|
||||
|
||||
FILE* const fp = std::fopen(filenamep, "w");
|
||||
if (VL_UNLIKELY(!fp)) {
|
||||
VL_FATAL_MT(filenamep, 0, "", "+prof+threads+file file not writable");
|
||||
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
|
||||
return; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
// TODO Perhaps merge with verilated_coverage output format, so can
|
||||
// have a common merging and reporting tool, etc.
|
||||
fprintf(fp, "VLPROFTHREAD 1.1 # Verilator thread profile dump version 1.1\n");
|
||||
fprintf(fp, "VLPROF arg --threads %" PRIu64 "\n", vluint64_t(m_workers.size() + 1));
|
||||
fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" PRIu64 "\n",
|
||||
Verilated::threadContextp()->profThreadsStart());
|
||||
fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n",
|
||||
Verilated::threadContextp()->profThreadsWindow());
|
||||
fprintf(fp, "VLPROF stat yields %" PRIu64 "\n", VlMTaskVertex::yields());
|
||||
|
||||
// Copy /proc/cpuinfo into this output so verilator_gantt can be run on
|
||||
// a different machine
|
||||
{
|
||||
const std::unique_ptr<std::ifstream> ifp{new std::ifstream("/proc/cpuinfo")};
|
||||
if (!ifp->fail()) {
|
||||
std::string line;
|
||||
while (std::getline(*ifp, line)) { fprintf(fp, "VLPROFPROC %s\n", line.c_str()); }
|
||||
}
|
||||
}
|
||||
|
||||
vluint32_t thread_id = 0;
|
||||
for (const auto& pi : m_allProfiles) {
|
||||
++thread_id;
|
||||
|
||||
bool printing = false; // False while in warmup phase
|
||||
for (const auto& ei : *pi) {
|
||||
switch (ei.m_type) {
|
||||
case VlProfileRec::TYPE_BARRIER: //
|
||||
printing = true;
|
||||
break;
|
||||
case VlProfileRec::TYPE_EVAL:
|
||||
if (!printing) break;
|
||||
fprintf(fp,
|
||||
"VLPROF eval start %" PRIu64 " elapsed %" PRIu64 " cpu %u on thread %u\n",
|
||||
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
|
||||
thread_id);
|
||||
break;
|
||||
case VlProfileRec::TYPE_EVAL_LOOP:
|
||||
if (!printing) break;
|
||||
fprintf(fp,
|
||||
"VLPROF eval_loop start %" PRIu64 " elapsed %" PRIu64
|
||||
" cpu %u on thread %u\n",
|
||||
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
|
||||
thread_id);
|
||||
break;
|
||||
case VlProfileRec::TYPE_MTASK_RUN:
|
||||
if (!printing) break;
|
||||
fprintf(fp,
|
||||
"VLPROF mtask %d"
|
||||
" start %" PRIu64 " elapsed %" PRIu64
|
||||
" predict_start %u predict_cost %u cpu %u on thread %u\n",
|
||||
ei.m_mtaskId, ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime),
|
||||
ei.m_predictStart, ei.m_predictCost, ei.m_cpu, thread_id);
|
||||
break;
|
||||
default: assert(false); break; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(fp, "VLPROF stat ticks %" PRIu64 "\n", tickEnd - tickStart);
|
||||
|
||||
std::fclose(fp);
|
||||
}
|
||||
|
||||
+41
-125
@@ -24,6 +24,7 @@
|
||||
#define VERILATOR_VERILATED_THREADS_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h" // for VerilatedMutex and clang annotations
|
||||
|
||||
#ifndef VL_THREADED
|
||||
@@ -35,19 +36,24 @@
|
||||
#error "verilated_threads.h/cpp expected VL_THREADED (from verilator --threads)"
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
// clang-format off
|
||||
#if defined(__linux)
|
||||
# include <sched.h> // For sched_getcpu()
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__APPLE__) && !defined(__arm64__)
|
||||
# include <cpuid.h> // For __cpuid_count()
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
class VlExecutionProfiler;
|
||||
class VlThreadPool;
|
||||
|
||||
// VlMTaskVertex and VlThreadpool will work with multiple model class types.
|
||||
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
|
||||
// as a void* here.
|
||||
@@ -58,7 +64,7 @@ using VlExecFnp = void (*)(VlSelfP, bool);
|
||||
// Track dependencies for a single MTask.
|
||||
class VlMTaskVertex final {
|
||||
// MEMBERS
|
||||
static std::atomic<vluint64_t> s_yields; // Statistics
|
||||
static std::atomic<uint64_t> s_yields; // Statistics
|
||||
|
||||
// On even cycles, _upstreamDepsDone increases as upstream
|
||||
// dependencies complete. When it reaches _upstreamDepCount,
|
||||
@@ -76,8 +82,8 @@ class VlMTaskVertex final {
|
||||
// during done-notification. Nobody's quantified that cost though.
|
||||
// If we were really serious about shrinking this class, we could
|
||||
// use 16-bit types here...)
|
||||
std::atomic<vluint32_t> m_upstreamDepsDone;
|
||||
const vluint32_t m_upstreamDepCount;
|
||||
std::atomic<uint32_t> m_upstreamDepsDone;
|
||||
const uint32_t m_upstreamDepCount;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -85,10 +91,10 @@ public:
|
||||
// 'upstreamDepCount' is the number of upstream MTaskVertex's
|
||||
// that must notify this MTaskVertex before it will become ready
|
||||
// to run.
|
||||
explicit VlMTaskVertex(vluint32_t upstreamDepCount);
|
||||
explicit VlMTaskVertex(uint32_t upstreamDepCount);
|
||||
~VlMTaskVertex() = default;
|
||||
|
||||
static vluint64_t yields() { return s_yields; }
|
||||
static uint64_t yields() { return s_yields; }
|
||||
static void yieldThread() {
|
||||
++s_yields; // Statistics
|
||||
std::this_thread::yield();
|
||||
@@ -97,24 +103,24 @@ public:
|
||||
// Upstream mtasks must call this when they complete.
|
||||
// Returns true when the current MTaskVertex becomes ready to execute,
|
||||
// false while it's still waiting on more dependencies.
|
||||
inline bool signalUpstreamDone(bool evenCycle) {
|
||||
bool signalUpstreamDone(bool evenCycle) {
|
||||
if (evenCycle) {
|
||||
const vluint32_t upstreamDepsDone
|
||||
const uint32_t upstreamDepsDone
|
||||
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
|
||||
assert(upstreamDepsDone <= m_upstreamDepCount);
|
||||
return (upstreamDepsDone == m_upstreamDepCount);
|
||||
} else {
|
||||
const vluint32_t upstreamDepsDone_prev
|
||||
const uint32_t upstreamDepsDone_prev
|
||||
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
|
||||
assert(upstreamDepsDone_prev > 0);
|
||||
return (upstreamDepsDone_prev == 1);
|
||||
}
|
||||
}
|
||||
inline bool areUpstreamDepsDone(bool evenCycle) const {
|
||||
const vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
|
||||
bool areUpstreamDepsDone(bool evenCycle) const {
|
||||
const uint32_t target = evenCycle ? m_upstreamDepCount : 0;
|
||||
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
|
||||
}
|
||||
inline void waitUntilUpstreamDone(bool evenCycle) const {
|
||||
void waitUntilUpstreamDone(bool evenCycle) const {
|
||||
unsigned ct = 0;
|
||||
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
|
||||
VL_CPU_RELAX();
|
||||
@@ -127,77 +133,14 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Profiling support
|
||||
class VlProfileRec final {
|
||||
protected:
|
||||
friend class VlThreadPool;
|
||||
enum VlProfileE { TYPE_MTASK_RUN, TYPE_EVAL, TYPE_EVAL_LOOP, TYPE_BARRIER };
|
||||
// Layout below allows efficient packing.
|
||||
// Leave endTime first, so no math needed to calculate address in endRecord
|
||||
vluint64_t m_endTime = 0; // Tick at end of execution
|
||||
vluint64_t m_startTime = 0; // Tick at start of execution
|
||||
vluint32_t m_mtaskId = 0; // Mtask we're logging
|
||||
vluint32_t m_predictStart = 0; // Time scheduler predicted would start
|
||||
vluint32_t m_predictCost = 0; // How long scheduler predicted would take
|
||||
VlProfileE m_type = TYPE_BARRIER; // Record type
|
||||
unsigned m_cpu; // Execution CPU number (at start anyways)
|
||||
public:
|
||||
class Barrier {};
|
||||
VlProfileRec() = default;
|
||||
explicit VlProfileRec(Barrier) { m_cpu = getcpu(); }
|
||||
void startEval(vluint64_t time) {
|
||||
m_type = VlProfileRec::TYPE_EVAL;
|
||||
m_startTime = time;
|
||||
m_cpu = getcpu();
|
||||
}
|
||||
void startEvalLoop(vluint64_t time) {
|
||||
m_type = VlProfileRec::TYPE_EVAL_LOOP;
|
||||
m_startTime = time;
|
||||
m_cpu = getcpu();
|
||||
}
|
||||
void startRecord(vluint64_t time, vluint32_t mtask, vluint32_t predictStart,
|
||||
vluint32_t predictCost) {
|
||||
m_type = VlProfileRec::TYPE_MTASK_RUN;
|
||||
m_mtaskId = mtask;
|
||||
m_predictStart = predictStart;
|
||||
m_predictCost = predictCost;
|
||||
m_startTime = time;
|
||||
m_cpu = getcpu();
|
||||
}
|
||||
void endRecord(vluint64_t time) { m_endTime = time; }
|
||||
static int getcpu() { // Return current executing CPU
|
||||
#if defined(__linux)
|
||||
return sched_getcpu();
|
||||
#elif defined(__APPLE__)
|
||||
vluint32_t info[4];
|
||||
__cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
|
||||
// info[1] is EBX, bits 24-31 are APIC ID
|
||||
if ((info[3] & (1 << 9)) == 0) {
|
||||
return -1; // no APIC on chip
|
||||
} else {
|
||||
return (unsigned)info[1] >> 24;
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
return GetCurrentProcessorNumber();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class VlThreadPool;
|
||||
|
||||
class VlWorkerThread final {
|
||||
private:
|
||||
// TYPES
|
||||
struct ExecRec {
|
||||
VlExecFnp m_fnp; // Function to execute
|
||||
VlSelfP m_selfp; // Symbol table to execute
|
||||
bool m_evenCycle; // Even/odd for flag alternation
|
||||
ExecRec()
|
||||
: m_fnp{nullptr}
|
||||
, m_selfp{nullptr}
|
||||
, m_evenCycle{false} {}
|
||||
VlExecFnp m_fnp = nullptr; // Function to execute
|
||||
VlSelfP m_selfp = nullptr; // Symbol table to execute
|
||||
bool m_evenCycle = false; // Even/odd for flag alternation
|
||||
ExecRec() = default;
|
||||
ExecRec(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
|
||||
: m_fnp{fnp}
|
||||
, m_selfp{selfp}
|
||||
@@ -205,7 +148,7 @@ private:
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
VerilatedMutex m_mutex;
|
||||
mutable VerilatedMutex m_mutex;
|
||||
std::condition_variable_any m_cv;
|
||||
// Only notify the condition_variable if the worker is waiting
|
||||
bool m_waiting VL_GUARDED_BY(m_mutex) = false;
|
||||
@@ -217,28 +160,24 @@ private:
|
||||
// Store the size atomically, so we can spin wait
|
||||
std::atomic<size_t> m_ready_size;
|
||||
|
||||
VlThreadPool* const m_poolp; // Our associated thread pool
|
||||
|
||||
const bool m_profiling; // Is profiling enabled?
|
||||
std::atomic<bool> m_exiting; // Worker thread should exit
|
||||
std::thread m_cthread; // Underlying C++ thread record
|
||||
VerilatedContext* const m_contextp; // Context for spawned thread
|
||||
|
||||
VL_UNCOPYABLE(VlWorkerThread);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling);
|
||||
explicit VlWorkerThread(VerilatedContext* contextp);
|
||||
~VlWorkerThread();
|
||||
|
||||
// METHODS
|
||||
inline void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
template <bool SpinWait>
|
||||
void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Spin for a while, waiting for new data
|
||||
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
|
||||
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) { //
|
||||
break;
|
||||
if VL_CONSTEXPR_CXX17 (SpinWait) {
|
||||
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
|
||||
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) break;
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
VerilatedLockGuard lock{m_mutex};
|
||||
while (m_ready.empty()) {
|
||||
@@ -252,8 +191,7 @@ public:
|
||||
m_ready.erase(m_ready.begin());
|
||||
m_ready_size.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
inline void wakeUp() { addTask(nullptr, nullptr, false); }
|
||||
inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
|
||||
void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle = false)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
bool notify;
|
||||
{
|
||||
@@ -264,55 +202,33 @@ public:
|
||||
}
|
||||
if (notify) m_cv.notify_one();
|
||||
}
|
||||
|
||||
void shutdown(); // Finish current tasks, then terminate thread
|
||||
void wait(); // Blocks calling thread until all tasks complete in this thread
|
||||
|
||||
void workerLoop();
|
||||
static void startWorker(VlWorkerThread* workerp);
|
||||
static void startWorker(VlWorkerThread* workerp, VerilatedContext* contextp);
|
||||
};
|
||||
|
||||
class VlThreadPool final {
|
||||
// TYPES
|
||||
using ProfileTrace = std::vector<VlProfileRec>;
|
||||
|
||||
class VlThreadPool final : public VerilatedVirtualBase {
|
||||
// MEMBERS
|
||||
std::vector<VlWorkerThread*> m_workers; // our workers
|
||||
const bool m_profiling; // is profiling enabled?
|
||||
|
||||
// Support profiling -- we can append records of profiling events
|
||||
// to this vector with very low overhead, and then dump them out
|
||||
// later. This prevents the overhead of printf/malloc/IO from
|
||||
// corrupting the profiling data. It's super cheap to append
|
||||
// a VlProfileRec struct on the end of a pre-allocated vector;
|
||||
// this is the only cost we pay in real-time during a profiling cycle.
|
||||
// Internal note: Globals may multi-construct, see verilated.cpp top.
|
||||
static VL_THREAD_LOCAL ProfileTrace* t_profilep;
|
||||
std::set<ProfileTrace*> m_allProfiles VL_GUARDED_BY(m_mutex);
|
||||
VerilatedMutex m_mutex;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// Construct a thread pool with 'nThreads' dedicated threads. The thread
|
||||
// pool will create these threads and make them available to execute tasks
|
||||
// via this->workerp(index)->addTask(...)
|
||||
VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling);
|
||||
~VlThreadPool();
|
||||
VlThreadPool(VerilatedContext* contextp, unsigned nThreads);
|
||||
~VlThreadPool() override;
|
||||
|
||||
// METHODS
|
||||
inline int numThreads() const { return m_workers.size(); }
|
||||
inline VlWorkerThread* workerp(int index) {
|
||||
int numThreads() const { return m_workers.size(); }
|
||||
VlWorkerThread* workerp(int index) {
|
||||
assert(index >= 0);
|
||||
assert(index < m_workers.size());
|
||||
return m_workers[index];
|
||||
}
|
||||
inline VlProfileRec* profileAppend() {
|
||||
t_profilep->emplace_back();
|
||||
return &(t_profilep->back());
|
||||
}
|
||||
void profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
void profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// In profiling mode, each executing thread must call
|
||||
// this once to setup profiling state:
|
||||
void setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
void tearDownProfilingClientThread();
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(VlThreadPool);
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2022 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU Lesser
|
||||
// General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//=========================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated timing implementation code
|
||||
///
|
||||
/// This file must be compiled and linked against all Verilated objects
|
||||
/// that use timing features.
|
||||
///
|
||||
/// See the internals documentation docs/internals.rst for details.
|
||||
///
|
||||
//=========================================================================
|
||||
|
||||
#include "verilated_timing.h"
|
||||
|
||||
//======================================================================
|
||||
// VlCoroutineHandle:: Methods
|
||||
|
||||
void VlCoroutineHandle::resume() {
|
||||
// Only null if we have a fork..join_any and one of the other child processes resumed the
|
||||
// main process
|
||||
if (VL_LIKELY(m_coro)) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Resuming: "); dump(););
|
||||
m_coro();
|
||||
m_coro = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
void VlCoroutineHandle::dump() const {
|
||||
VL_PRINTF("Process waiting at %s:%d\n", m_fileline.filename(), m_fileline.lineno());
|
||||
}
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// VlDelayScheduler:: Methods
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
void VlDelayScheduler::VlDelayedCoroutine::dump() const {
|
||||
VL_DBG_MSGF(" Awaiting time %" PRIu64 ": ", m_timestep);
|
||||
m_handle.dump();
|
||||
}
|
||||
#endif
|
||||
|
||||
void VlDelayScheduler::resume() {
|
||||
#ifdef VL_DEBUG
|
||||
VL_DEBUG_IF(dump(); VL_DBG_MSGF(" Resuming delayed processes\n"););
|
||||
#endif
|
||||
while (awaitingCurrentTime()) {
|
||||
if (m_queue.front().m_timestep != m_context.time()) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"%Error: Encountered process that should've been resumed at an "
|
||||
"earlier simulation time. Missed a time slot?");
|
||||
}
|
||||
// Move max element in the heap to the end
|
||||
std::pop_heap(m_queue.begin(), m_queue.end());
|
||||
VlCoroutineHandle handle = std::move(m_queue.back().m_handle);
|
||||
m_queue.pop_back();
|
||||
handle.resume();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t VlDelayScheduler::nextTimeSlot() const {
|
||||
if (empty()) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: There is no next time slot scheduled");
|
||||
}
|
||||
return m_queue.front().m_timestep;
|
||||
}
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
void VlDelayScheduler::dump() const {
|
||||
if (m_queue.empty()) {
|
||||
VL_DBG_MSGF(" No delayed processes:\n");
|
||||
} else {
|
||||
VL_DBG_MSGF(" Delayed processes:\n");
|
||||
for (const auto& susp : m_queue) susp.dump();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// VlTriggerScheduler:: Methods
|
||||
|
||||
void VlTriggerScheduler::resume(const char* eventDescription) {
|
||||
#ifdef VL_DEBUG
|
||||
VL_DEBUG_IF(dump(eventDescription);
|
||||
VL_DBG_MSGF(" Resuming processes waiting for %s\n", eventDescription););
|
||||
#endif
|
||||
for (auto& susp : m_ready) susp.resume();
|
||||
m_ready.clear();
|
||||
commit(eventDescription);
|
||||
}
|
||||
|
||||
void VlTriggerScheduler::commit(const char* eventDescription) {
|
||||
#ifdef VL_DEBUG
|
||||
if (!m_uncommitted.empty()) {
|
||||
VL_DEBUG_IF(
|
||||
VL_DBG_MSGF(" Committing processes waiting for %s:\n", eventDescription);
|
||||
for (const auto& susp
|
||||
: m_uncommitted) {
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
m_ready.reserve(m_ready.size() + m_uncommitted.size());
|
||||
m_ready.insert(m_ready.end(), std::make_move_iterator(m_uncommitted.begin()),
|
||||
std::make_move_iterator(m_uncommitted.end()));
|
||||
m_uncommitted.clear();
|
||||
}
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
void VlTriggerScheduler::dump(const char* eventDescription) const {
|
||||
if (m_ready.empty()) {
|
||||
VL_DBG_MSGF(" No ready processes waiting for %s\n", eventDescription);
|
||||
} else {
|
||||
for (const auto& susp : m_ready) {
|
||||
VL_DBG_MSGF(" Ready processes waiting for %s:\n", eventDescription);
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
}
|
||||
}
|
||||
if (!m_uncommitted.empty()) {
|
||||
VL_DBG_MSGF(" Uncommitted processes waiting for %s:\n", eventDescription);
|
||||
for (const auto& susp : m_uncommitted) {
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// VlDynamicTriggerScheduler:: Methods
|
||||
|
||||
bool VlDynamicTriggerScheduler::evaluate() {
|
||||
VL_DEBUG_IF(dump(););
|
||||
std::swap(m_suspended, m_evaluated);
|
||||
for (auto& coro : m_evaluated) coro.resume();
|
||||
m_evaluated.clear();
|
||||
return !m_triggered.empty();
|
||||
}
|
||||
|
||||
void VlDynamicTriggerScheduler::doPostUpdates() {
|
||||
VL_DEBUG_IF(if (!m_post.empty())
|
||||
VL_DBG_MSGF(" Doing post updates for processes:\n"); //
|
||||
for (const auto& susp
|
||||
: m_post) {
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
});
|
||||
for (auto& coro : m_post) coro.resume();
|
||||
m_post.clear();
|
||||
}
|
||||
|
||||
void VlDynamicTriggerScheduler::resume() {
|
||||
VL_DEBUG_IF(if (!m_triggered.empty()) VL_DBG_MSGF(" Resuming processes:\n"); //
|
||||
for (const auto& susp
|
||||
: m_triggered) {
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
});
|
||||
for (auto& coro : m_triggered) coro.resume();
|
||||
m_triggered.clear();
|
||||
}
|
||||
|
||||
#ifdef VL_DEBUG
|
||||
void VlDynamicTriggerScheduler::dump() const {
|
||||
if (m_suspended.empty()) {
|
||||
VL_DBG_MSGF(" No suspended processes waiting for dynamic trigger evaluation\n");
|
||||
} else {
|
||||
for (const auto& susp : m_suspended) {
|
||||
VL_DBG_MSGF(" Suspended processes waiting for dynamic trigger evaluation:\n");
|
||||
VL_DBG_MSGF(" - ");
|
||||
susp.dump();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// VlForkSync:: Methods
|
||||
|
||||
void VlForkSync::done(const char* filename, int lineno) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Process forked at %s:%d finished\n", filename, lineno););
|
||||
if (m_join->m_counter > 0) m_join->m_counter--;
|
||||
if (m_join->m_counter == 0) m_join->m_susp.resume();
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// VlCoroutine:: Methods
|
||||
|
||||
VlCoroutine::VlPromise::~VlPromise() {
|
||||
// Indicate to the return object that the coroutine has finished or been destroyed
|
||||
if (m_corop) m_corop->m_promisep = nullptr;
|
||||
// If there is a continuation, destroy it
|
||||
if (m_continuation) m_continuation.destroy();
|
||||
}
|
||||
|
||||
std::suspend_never VlCoroutine::VlPromise::final_suspend() noexcept {
|
||||
// Indicate to the return object that the coroutine has finished
|
||||
if (m_corop) {
|
||||
m_corop->m_promisep = nullptr;
|
||||
// Forget the return value, we won't need it and it won't be able to let us know if
|
||||
// it's destroyed
|
||||
m_corop = nullptr;
|
||||
}
|
||||
// If there is a continuation, resume it
|
||||
if (m_continuation) {
|
||||
m_continuation();
|
||||
m_continuation = nullptr;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2022 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU Lesser
|
||||
// General Public License Version 3 or the Perl Artistic License Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated timing header
|
||||
///
|
||||
/// This file is included automatically by Verilator in some of the C++ files
|
||||
/// it generates if timing features are used.
|
||||
///
|
||||
/// This file is not part of the Verilated public-facing API.
|
||||
/// It is only for internal use.
|
||||
///
|
||||
/// See the internals documentation docs/internals.rst for details.
|
||||
///
|
||||
//*************************************************************************
|
||||
#ifndef VERILATOR_VERILATED_TIMING_H_
|
||||
#define VERILATOR_VERILATED_TIMING_H_
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
// clang-format off
|
||||
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
|
||||
#if defined _LIBCPP_VERSION // libc++
|
||||
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
|
||||
# endif
|
||||
# include <experimental/coroutine>
|
||||
namespace std {
|
||||
using namespace experimental; // Bring std::experimental into the std namespace
|
||||
}
|
||||
#else
|
||||
# if defined __clang__ && defined __GLIBCXX__
|
||||
# define __cpp_impl_coroutine 1 // Clang doesn't define this, but it's needed for libstdc++
|
||||
# endif
|
||||
# include <coroutine>
|
||||
# if __clang_major__ < 14
|
||||
namespace std { // Bring coroutine library into std::experimental, as Clang < 14 expects it to be there
|
||||
namespace experimental {
|
||||
using namespace std;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
// Placeholder for compiling with --protect-ids
|
||||
#define VL_UNKNOWN "<unknown>"
|
||||
|
||||
//=============================================================================
|
||||
// VlFileLineDebug stores a SystemVerilog source code location. Used in VlCoroutineHandle for
|
||||
// debugging purposes.
|
||||
|
||||
class VlFileLineDebug final {
|
||||
// MEMBERS
|
||||
#ifdef VL_DEBUG
|
||||
const char* m_filename = nullptr;
|
||||
int m_lineno = 0;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// Construct
|
||||
VlFileLineDebug() = default;
|
||||
VlFileLineDebug(const char* filename, int lineno)
|
||||
#ifdef VL_DEBUG
|
||||
: m_filename{filename}
|
||||
, m_lineno{lineno}
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// METHODS
|
||||
#ifdef VL_DEBUG
|
||||
const char* filename() const { return m_filename; }
|
||||
int lineno() const { return m_lineno; }
|
||||
#endif
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
|
||||
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
|
||||
// suspended, another VlCoroutineHandle was created to manage it.
|
||||
|
||||
class VlCoroutineHandle final {
|
||||
VL_UNCOPYABLE(VlCoroutineHandle);
|
||||
|
||||
// MEMBERS
|
||||
std::coroutine_handle<> m_coro; // The wrapped coroutine handle
|
||||
VlFileLineDebug m_fileline;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
// Construct
|
||||
VlCoroutineHandle()
|
||||
: m_coro{nullptr} {}
|
||||
VlCoroutineHandle(std::coroutine_handle<> coro, VlFileLineDebug fileline)
|
||||
: m_coro{coro}
|
||||
, m_fileline{fileline} {}
|
||||
// Move the handle, leaving a nullptr
|
||||
VlCoroutineHandle(VlCoroutineHandle&& moved)
|
||||
: m_coro{std::exchange(moved.m_coro, nullptr)}
|
||||
, m_fileline{moved.m_fileline} {}
|
||||
// Destroy if the handle isn't null
|
||||
~VlCoroutineHandle() {
|
||||
// Usually these coroutines should get resumed; we only need to clean up if we destroy a
|
||||
// model with some coroutines suspended
|
||||
if (VL_UNLIKELY(m_coro)) m_coro.destroy();
|
||||
}
|
||||
// METHODS
|
||||
// Move the handle, leaving a null handle
|
||||
auto& operator=(VlCoroutineHandle&& moved) {
|
||||
m_coro = std::exchange(moved.m_coro, nullptr);
|
||||
return *this;
|
||||
}
|
||||
// Resume the coroutine if the handle isn't null
|
||||
void resume();
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlDelayScheduler stores coroutines to be resumed at a certain simulation time. If the current
|
||||
// time is equal to a coroutine's resume time, the coroutine gets resumed.
|
||||
|
||||
class VlDelayScheduler final {
|
||||
// TYPES
|
||||
struct VlDelayedCoroutine {
|
||||
uint64_t m_timestep; // Simulation time when the coroutine should be resumed
|
||||
VlCoroutineHandle m_handle; // The suspended coroutine to be resumed
|
||||
|
||||
// Comparison operator for std::push_heap(), std::pop_heap()
|
||||
bool operator<(const VlDelayedCoroutine& other) const {
|
||||
return m_timestep > other.m_timestep;
|
||||
}
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
};
|
||||
using VlDelayedCoroutineQueue = std::vector<VlDelayedCoroutine>;
|
||||
|
||||
// MEMBERS
|
||||
VerilatedContext& m_context;
|
||||
VlDelayedCoroutineQueue m_queue; // Coroutines to be restored at a certain simulation time
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit VlDelayScheduler(VerilatedContext& context)
|
||||
: m_context{context} {}
|
||||
// METHODS
|
||||
// Resume coroutines waiting for the current simulation time
|
||||
void resume();
|
||||
// Returns the simulation time of the next time slot (aborts if there are no delayed
|
||||
// coroutines)
|
||||
uint64_t nextTimeSlot() const;
|
||||
// Are there no delayed coroutines awaiting?
|
||||
bool empty() const { return m_queue.empty(); }
|
||||
// Are there coroutines to resume at the current simulation time?
|
||||
bool awaitingCurrentTime() const {
|
||||
return !empty() && m_queue.front().m_timestep <= m_context.time();
|
||||
}
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
// Used by coroutines for co_awaiting a certain simulation time
|
||||
auto delay(uint64_t delay, const char* filename = VL_UNKNOWN, int lineno = 0) {
|
||||
struct Awaitable {
|
||||
VlDelayedCoroutineQueue& queue;
|
||||
uint64_t delay;
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
queue.push_back({delay, VlCoroutineHandle{coro, fileline}});
|
||||
// Move last element to the proper place in the max-heap
|
||||
std::push_heap(queue.begin(), queue.end());
|
||||
}
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{m_queue, m_context.time() + delay, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlTriggerScheduler stores coroutines to be resumed by a trigger. It does not keep track of its
|
||||
// trigger, relying on calling code to resume when appropriate. Coroutines are kept in two stages
|
||||
// - 'uncommitted' and 'ready'. Whenever a coroutine is suspended, it lands in the 'uncommited'
|
||||
// stage. Only when commit() is called, these coroutines get moved to the 'ready' stage. That's
|
||||
// when they can be resumed. This is done to avoid resuming processes before they start waiting.
|
||||
|
||||
class VlTriggerScheduler final {
|
||||
// TYPES
|
||||
using VlCoroutineVec = std::vector<VlCoroutineHandle>;
|
||||
|
||||
// MEMBERS
|
||||
VlCoroutineVec m_uncommitted; // Coroutines suspended before commit() was called
|
||||
// (not resumable)
|
||||
VlCoroutineVec m_ready; // Coroutines that can be resumed (all coros from m_uncommitted are
|
||||
// moved here in commit())
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
// Resumes all coroutines from the 'ready' stage
|
||||
void resume(const char* eventDescription = VL_UNKNOWN);
|
||||
// Moves all coroutines from m_uncommitted to m_ready
|
||||
void commit(const char* eventDescription = VL_UNKNOWN);
|
||||
// Are there no coroutines awaiting?
|
||||
bool empty() const { return m_ready.empty() && m_uncommitted.empty(); }
|
||||
#ifdef VL_DEBUG
|
||||
void dump(const char* eventDescription) const;
|
||||
#endif
|
||||
// Used by coroutines for co_awaiting a certain trigger
|
||||
auto trigger(const char* eventDescription = VL_UNKNOWN, const char* filename = VL_UNKNOWN,
|
||||
int lineno = 0) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
|
||||
eventDescription, filename, lineno););
|
||||
struct Awaitable {
|
||||
VlCoroutineVec& suspended; // Coros waiting on trigger
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
suspended.emplace_back(coro, fileline);
|
||||
}
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{m_uncommitted, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlDynamicTriggerScheduler is used for cases where triggers cannot be statically referenced and
|
||||
// evaluated. Coroutines that make use of this scheduler must adhere to a certain procedure:
|
||||
// __Vtrigger = 0;
|
||||
// <locals and inits required for trigger eval>
|
||||
// while (!__Vtrigger) {
|
||||
// co_await __VdynSched.evaluation();
|
||||
// <pre updates>;
|
||||
// __Vtrigger = <trigger eval>;
|
||||
// [optionally] co_await __VdynSched.postUpdate();
|
||||
// <post updates>;
|
||||
// }
|
||||
// co_await __VdynSched.resumption();
|
||||
// The coroutines get resumed at trigger evaluation time, evaluate their local triggers, optionally
|
||||
// await the post update step, and if the trigger is set, await proper resumption in the 'act' eval
|
||||
// step.
|
||||
|
||||
class VlDynamicTriggerScheduler final {
|
||||
// TYPES
|
||||
using VlCoroutineVec = std::vector<VlCoroutineHandle>;
|
||||
|
||||
// MEMBERS
|
||||
VlCoroutineVec m_suspended; // Suspended coroutines awaiting trigger evaluation
|
||||
VlCoroutineVec m_evaluated; // Coroutines currently being evaluated (for evaluate())
|
||||
VlCoroutineVec m_triggered; // Coroutines whose triggers were set, and are awaiting resumption
|
||||
VlCoroutineVec m_post; // Coroutines awaiting the post update step (only relevant for triggers
|
||||
// with destructive post updates, e.g. named events)
|
||||
|
||||
// METHODS
|
||||
auto awaitable(VlCoroutineVec& queue, const char* filename, int lineno) {
|
||||
struct Awaitable {
|
||||
VlCoroutineVec& suspended; // Coros waiting on trigger
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) {
|
||||
suspended.emplace_back(coro, fileline);
|
||||
}
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{queue, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
|
||||
public:
|
||||
// Evaluates all dynamic triggers (resumed coroutines that co_await evaluation())
|
||||
bool evaluate();
|
||||
// Runs post updates for all dynamic triggers (resumes coroutines that co_await postUpdate())
|
||||
void doPostUpdates();
|
||||
// Resumes all coroutines whose triggers are set (those that co_await resumption())
|
||||
void resume();
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
#endif
|
||||
// Used by coroutines for co_awaiting trigger evaluation
|
||||
auto evaluation(const char* eventDescription, const char* filename, int lineno) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n",
|
||||
eventDescription, filename, lineno););
|
||||
return awaitable(m_suspended, filename, lineno);
|
||||
}
|
||||
// Used by coroutines for co_awaiting the trigger post update step
|
||||
auto postUpdate(const char* eventDescription, const char* filename, int lineno) {
|
||||
VL_DEBUG_IF(
|
||||
VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting the post update step\n",
|
||||
eventDescription, filename, lineno););
|
||||
return awaitable(m_post, filename, lineno);
|
||||
}
|
||||
// Used by coroutines for co_awaiting the resumption step (in 'act' eval)
|
||||
auto resumption(const char* eventDescription, const char* filename, int lineno) {
|
||||
VL_DEBUG_IF(VL_DBG_MSGF(" Process waiting for %s at %s:%d awaiting resumption\n",
|
||||
eventDescription, filename, lineno););
|
||||
return awaitable(m_triggered, filename, lineno);
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlNow is a helper awaitable type that always suspends, and then immediately resumes a coroutine.
|
||||
// Allows forcing the move of coroutine locals to the heap.
|
||||
|
||||
struct VlNow {
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
bool await_suspend(std::coroutine_handle<>) const { return false; } // Resume immediately
|
||||
void await_resume() const {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlForever is a helper awaitable type for suspending coroutines forever. Used for constant
|
||||
// wait statements.
|
||||
|
||||
struct VlForever {
|
||||
bool await_ready() const { return false; } // Always suspend
|
||||
void await_suspend(std::coroutine_handle<> coro) const { coro.destroy(); }
|
||||
void await_resume() const {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlForkSync is used to manage fork..join and fork..join_any constructs.
|
||||
|
||||
class VlForkSync final {
|
||||
// VlJoin stores the handle of a suspended coroutine that did a fork..join or fork..join_any.
|
||||
// If the counter reaches 0, the suspended coroutine shall be resumed.
|
||||
struct VlJoin {
|
||||
size_t m_counter = 0; // When reaches 0, resume suspended coroutine
|
||||
VlCoroutineHandle m_susp; // Coroutine to resume
|
||||
};
|
||||
|
||||
// The join info is shared among all forked processes
|
||||
std::shared_ptr<VlJoin> m_join;
|
||||
|
||||
public:
|
||||
// Create the join object and set the counter to the specified number
|
||||
void init(size_t count) { m_join.reset(new VlJoin{count, {}}); }
|
||||
// Called whenever any of the forked processes finishes. If the join counter reaches 0, the
|
||||
// main process gets resumed
|
||||
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
|
||||
// Used by coroutines for co_awaiting a join
|
||||
auto join(const char* filename = VL_UNKNOWN, int lineno = 0) {
|
||||
assert(m_join);
|
||||
VL_DEBUG_IF(
|
||||
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
|
||||
struct Awaitable {
|
||||
const std::shared_ptr<VlJoin> join; // Join to await on
|
||||
VlFileLineDebug fileline;
|
||||
|
||||
bool await_ready() { return join->m_counter == 0; } // Suspend if join still exists
|
||||
void await_suspend(std::coroutine_handle<> coro) { join->m_susp = {coro, fileline}; }
|
||||
void await_resume() const {}
|
||||
};
|
||||
return Awaitable{m_join, VlFileLineDebug{filename, lineno}};
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VlCoroutine
|
||||
// Return value of a coroutine. Used for chaining coroutine suspension/resumption.
|
||||
|
||||
class VlCoroutine final {
|
||||
private:
|
||||
// TYPES
|
||||
struct VlPromise {
|
||||
std::coroutine_handle<> m_continuation; // Coroutine to resume after this one finishes
|
||||
VlCoroutine* m_corop = nullptr; // Pointer to the coroutine return object
|
||||
|
||||
~VlPromise();
|
||||
|
||||
VlCoroutine get_return_object() { return {this}; }
|
||||
|
||||
// Never suspend at the start of the coroutine
|
||||
std::suspend_never initial_suspend() const { return {}; }
|
||||
|
||||
// Never suspend at the end of the coroutine (thanks to this, the coroutine will clean up
|
||||
// after itself)
|
||||
std::suspend_never final_suspend() noexcept;
|
||||
|
||||
void unhandled_exception() const { std::abort(); }
|
||||
void return_void() const {}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
VlPromise* m_promisep; // The promise created for this coroutine
|
||||
|
||||
public:
|
||||
// TYPES
|
||||
using promise_type = VlPromise; // promise_type has to be public
|
||||
|
||||
// CONSTRUCTORS
|
||||
// Construct
|
||||
VlCoroutine(VlPromise* promisep)
|
||||
: m_promisep{promisep} {
|
||||
m_promisep->m_corop = this;
|
||||
}
|
||||
// Move. Update the pointers each time the return object is moved
|
||||
VlCoroutine(VlCoroutine&& other)
|
||||
: m_promisep{std::exchange(other.m_promisep, nullptr)} {
|
||||
if (m_promisep) m_promisep->m_corop = this;
|
||||
}
|
||||
~VlCoroutine() {
|
||||
// Indicate to the promise that the return object is gone
|
||||
if (m_promisep) m_promisep->m_corop = nullptr;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
// Suspend the awaiter if the coroutine is suspended (the promise exists)
|
||||
bool await_ready() const noexcept { return !m_promisep; }
|
||||
// Set the awaiting coroutine as the continuation of the current coroutine
|
||||
void await_suspend(std::coroutine_handle<> coro) { m_promisep->m_continuation = coro; }
|
||||
void await_resume() const noexcept {}
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
+303
-154
@@ -27,26 +27,36 @@
|
||||
#include "verilated.h"
|
||||
#include "verilated_trace_defs.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
# include <condition_variable>
|
||||
#ifdef VL_THREADED
|
||||
# include <deque>
|
||||
# include <thread>
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
class VlThreadPool;
|
||||
template <class T_Buffer>
|
||||
class VerilatedTraceBuffer;
|
||||
template <class T_Buffer>
|
||||
class VerilatedTraceOffloadBuffer;
|
||||
|
||||
#ifdef VL_THREADED
|
||||
//=============================================================================
|
||||
// Threaded tracing
|
||||
// Offloaded tracing
|
||||
|
||||
// A simple synchronized first in first out queue
|
||||
template <class T> class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug
|
||||
template <class T>
|
||||
class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug
|
||||
private:
|
||||
VerilatedMutex m_mutex; // Protects m_queue
|
||||
mutable VerilatedMutex m_mutex; // Protects m_queue
|
||||
std::condition_variable_any m_cv;
|
||||
std::deque<T> m_queue VL_GUARDED_BY(m_mutex);
|
||||
|
||||
@@ -87,11 +97,11 @@ public:
|
||||
|
||||
// Commands used by thread tracing. Anonymous enum in class, as we want
|
||||
// it scoped, but we also want the automatic conversion to integer types.
|
||||
class VerilatedTraceCommand final {
|
||||
class VerilatedTraceOffloadCommand final {
|
||||
public:
|
||||
// These must all fit in 4 bit at the moment, as the tracing routines
|
||||
// pack parameters in the top bits.
|
||||
enum : vluint8_t {
|
||||
enum : uint8_t {
|
||||
CHG_BIT_0 = 0x0,
|
||||
CHG_BIT_1 = 0x1,
|
||||
CHG_CDATA = 0x2,
|
||||
@@ -101,36 +111,65 @@ public:
|
||||
CHG_WDATA = 0x6,
|
||||
CHG_DOUBLE = 0x8,
|
||||
// TODO: full..
|
||||
TIME_CHANGE = 0xd,
|
||||
TIME_CHANGE = 0xc,
|
||||
TRACE_BUFFER = 0xd,
|
||||
END = 0xe, // End of buffer
|
||||
SHUTDOWN = 0xf // Shutdown worker thread, also marks end of buffer
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTraceConfig
|
||||
|
||||
// Simple data representing trace configuration required by generated models.
|
||||
class VerilatedTraceConfig final {
|
||||
public:
|
||||
const bool m_useParallel; // Use parallel tracing
|
||||
const bool m_useOffloading; // Offloading trace rendering
|
||||
const bool m_useFstWriterThread; // Use the separate FST writer thread
|
||||
|
||||
VerilatedTraceConfig(bool useParallel, bool useOffloading, bool useFstWriterThread)
|
||||
: m_useParallel{useParallel}
|
||||
, m_useOffloading{useOffloading}
|
||||
, m_useFstWriterThread{useFstWriterThread} {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
// VerilatedTrace uses F-bounded polymorphism to access duck-typed
|
||||
// implementations in the format specific derived class, which must be passed
|
||||
// as the type parameter T_Derived
|
||||
template <class T_Derived> class VerilatedTrace VL_NOT_FINAL {
|
||||
// T_Trace is the format specific subclass of VerilatedTrace.
|
||||
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
|
||||
template <class T_Trace, class T_Buffer>
|
||||
class VerilatedTrace VL_NOT_FINAL {
|
||||
public:
|
||||
using Buffer = VerilatedTraceBuffer<T_Buffer>;
|
||||
using OffloadBuffer = VerilatedTraceOffloadBuffer<T_Buffer>;
|
||||
|
||||
//=========================================================================
|
||||
// Generic tracing internals
|
||||
|
||||
using initCb_t = void (*)(void*, T_Derived*, vluint32_t); // Type of init callbacks
|
||||
using dumpCb_t = void (*)(void*, T_Derived*); // Type of all but init callbacks
|
||||
using initCb_t = void (*)(void*, T_Trace*, uint32_t); // Type of init callbacks
|
||||
using dumpCb_t = void (*)(void*, Buffer*); // Type of dump callbacks
|
||||
using dumpOffloadCb_t = void (*)(void*, OffloadBuffer*); // Type of offload dump callbacks
|
||||
using cleanupCb_t = void (*)(void*, T_Trace*); // Type of cleanup callbacks
|
||||
|
||||
private:
|
||||
// Give the buffer (both base and derived) access to the private bits
|
||||
friend T_Buffer;
|
||||
friend Buffer;
|
||||
friend OffloadBuffer;
|
||||
|
||||
struct CallbackRecord {
|
||||
// Note: would make these fields const, but some old STL implementations
|
||||
// (the one in Ubuntu 14.04 with GCC 4.8.4 in particular) use the
|
||||
// assignment operator on inserting into collections, so they don't work
|
||||
// with const fields...
|
||||
union {
|
||||
initCb_t m_initCb; // The callback function
|
||||
dumpCb_t m_dumpCb; // The callback function
|
||||
union { // The callback
|
||||
initCb_t m_initCb;
|
||||
dumpCb_t m_dumpCb;
|
||||
dumpOffloadCb_t m_dumpOffloadCb;
|
||||
cleanupCb_t m_cleanupCb;
|
||||
};
|
||||
void* m_userp; // The user pointer to pass to the callback (the symbol table)
|
||||
CallbackRecord(initCb_t cb, void* userp)
|
||||
@@ -139,67 +178,110 @@ private:
|
||||
CallbackRecord(dumpCb_t cb, void* userp)
|
||||
: m_dumpCb{cb}
|
||||
, m_userp{userp} {}
|
||||
CallbackRecord(dumpOffloadCb_t cb, void* userp)
|
||||
: m_dumpOffloadCb{cb}
|
||||
, m_userp{userp} {}
|
||||
CallbackRecord(cleanupCb_t cb, void* userp)
|
||||
: m_cleanupCb{cb}
|
||||
, m_userp{userp} {}
|
||||
};
|
||||
|
||||
vluint32_t* m_sigs_oldvalp; // Old value store
|
||||
vluint64_t m_timeLastDump; // Last time we did a dump
|
||||
std::vector<CallbackRecord> m_initCbs; // Routines to initialize traciong
|
||||
std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
|
||||
std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
|
||||
std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump
|
||||
bool m_fullDump; // Whether a full dump is required on the next call to 'dump'
|
||||
vluint32_t m_nextCode; // Next code number to assign
|
||||
vluint32_t m_numSignals; // Number of distinct signals
|
||||
vluint32_t m_maxBits; // Number of bits in the widest signal
|
||||
std::vector<std::string> m_namePrefixStack{""}; // Path prefixes to add to signal names
|
||||
char m_scopeEscape;
|
||||
double m_timeRes; // Time resolution (ns/ms etc)
|
||||
double m_timeUnit; // Time units (ns/ms etc)
|
||||
bool m_offload = false; // Use the offload thread (ignored if !VL_THREADED)
|
||||
bool m_parallel = false; // Use parallel tracing (ignored if !VL_THREADED)
|
||||
|
||||
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord& cbRec)
|
||||
#ifdef VL_THREADED
|
||||
struct ParallelWorkerData {
|
||||
const dumpCb_t m_cb; // The callback
|
||||
void* const m_userp; // The use pointer to pass to the callback
|
||||
Buffer* const m_bufp; // The buffer pointer to pass to the callback
|
||||
std::atomic<bool> m_ready{false}; // The ready flag
|
||||
mutable VerilatedMutex m_mutex; // Mutex for suspension until ready
|
||||
std::condition_variable_any m_cv; // Condition variable for suspension
|
||||
bool m_waiting VL_GUARDED_BY(m_mutex) = false; // Whether a thread is suspended in wait()
|
||||
|
||||
void wait();
|
||||
|
||||
ParallelWorkerData(dumpCb_t cb, void* userp, Buffer* bufp)
|
||||
: m_cb{cb}
|
||||
, m_userp{userp}
|
||||
, m_bufp{bufp} {}
|
||||
};
|
||||
|
||||
// Passed a ParallelWorkerData*, second argument is ignored
|
||||
static void parallelWorkerTask(void*, bool);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
uint32_t* m_sigs_oldvalp = nullptr; // Previous value store
|
||||
EData* m_sigs_enabledp = nullptr; // Bit vector of enabled codes (nullptr = all on)
|
||||
private:
|
||||
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
|
||||
std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing
|
||||
std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
|
||||
std::vector<CallbackRecord> m_fullOffloadCbs; // Routines to perform offloaded full dump
|
||||
std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
|
||||
std::vector<CallbackRecord> m_chgOffloadCbs; // Routines to perform offloaded incremental dump
|
||||
std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump
|
||||
bool m_fullDump = true; // Whether a full dump is required on the next call to 'dump'
|
||||
uint32_t m_nextCode = 0; // Next code number to assign
|
||||
uint32_t m_numSignals = 0; // Number of distinct signals
|
||||
uint32_t m_maxBits = 0; // Number of bits in the widest signal
|
||||
std::vector<std::string> m_namePrefixStack{""}; // Path prefixes to add to signal names
|
||||
std::vector<std::pair<int, std::string>> m_dumpvars; // dumpvar() entries
|
||||
char m_scopeEscape = '.';
|
||||
double m_timeRes = 1e-9; // Time resolution (ns/ms etc)
|
||||
double m_timeUnit = 1e-0; // Time units (ns/ms etc)
|
||||
uint64_t m_timeLastDump = 0; // Last time we did a dump
|
||||
bool m_didSomeDump = false; // Did at least one dump (i.e.: m_timeLastDump is valid)
|
||||
VerilatedContext* m_contextp = nullptr; // The context used by the traced models
|
||||
std::unordered_set<const VerilatedModel*> m_models; // The collection of models being traced
|
||||
|
||||
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
||||
// Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->'
|
||||
// Equivalent to 'this' but is of the sub-type 'T_Trace*'. Use 'self()->'
|
||||
// to access duck-typed functions to avoid a virtual function call.
|
||||
T_Derived* self() { return static_cast<T_Derived*>(this); }
|
||||
T_Trace* self() { return static_cast<T_Trace*>(this); }
|
||||
|
||||
void runCallbacks(const std::vector<CallbackRecord>& cbVec);
|
||||
void runOffloadedCallbacks(const std::vector<CallbackRecord>& cbVec);
|
||||
|
||||
// Flush any remaining data for this file
|
||||
static void onFlush(void* selfp) VL_MT_UNSAFE_ONE;
|
||||
// Close the file on termination
|
||||
static void onExit(void* selfp) VL_MT_UNSAFE_ONE;
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Number of total trace buffers that have been allocated
|
||||
vluint32_t m_numTraceBuffers;
|
||||
|
||||
// Size of trace buffers
|
||||
size_t m_traceBufferSize;
|
||||
|
||||
#ifdef VL_THREADED
|
||||
// Number of total offload buffers that have been allocated
|
||||
uint32_t m_numOffloadBuffers = 0;
|
||||
// Size of offload buffers
|
||||
size_t m_offloadBufferSize = 0;
|
||||
// Buffers handed to worker for processing
|
||||
VerilatedThreadQueue<vluint32_t*> m_buffersToWorker;
|
||||
VerilatedThreadQueue<uint32_t*> m_offloadBuffersToWorker;
|
||||
// Buffers returned from worker after processing
|
||||
VerilatedThreadQueue<vluint32_t*> m_buffersFromWorker;
|
||||
|
||||
// Get a new trace buffer that can be populated. May block if none available
|
||||
vluint32_t* getTraceBuffer();
|
||||
VerilatedThreadQueue<uint32_t*> m_offloadBuffersFromWorker;
|
||||
|
||||
protected:
|
||||
// Write pointer into current buffer
|
||||
vluint32_t* m_traceBufferWritep;
|
||||
uint32_t* m_offloadBufferWritep = nullptr;
|
||||
// End of offload buffer
|
||||
uint32_t* m_offloadBufferEndp = nullptr;
|
||||
|
||||
// End of trace buffer
|
||||
vluint32_t* m_traceBufferEndp;
|
||||
|
||||
// The worker thread itself
|
||||
private:
|
||||
// The offload worker thread itself
|
||||
std::unique_ptr<std::thread> m_workerThread;
|
||||
|
||||
// The function executed by the worker thread
|
||||
void workerThreadMain();
|
||||
// Get a new offload buffer that can be populated. May block if none available
|
||||
uint32_t* getOffloadBuffer();
|
||||
|
||||
// Wait until given buffer is placed in m_buffersFromWorker
|
||||
void waitForBuffer(const vluint32_t* bufferp);
|
||||
// The function executed by the offload worker thread
|
||||
void offloadWorkerThreadMain();
|
||||
|
||||
// Wait until given offload buffer is placed in m_offloadBuffersFromWorker
|
||||
void waitForOffloadBuffer(const uint32_t* bufferp);
|
||||
|
||||
// Shut down and join worker, if it's running, otherwise do nothing
|
||||
void shutdownWorker();
|
||||
void shutdownOffloadWorker();
|
||||
#endif
|
||||
|
||||
// CONSTRUCTORS
|
||||
@@ -209,13 +291,12 @@ protected:
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
VerilatedMutex m_mutex; // Ensure dump() etc only called from single thread
|
||||
mutable VerilatedMutex m_mutex; // Ensure dump() etc only called from single thread
|
||||
|
||||
vluint32_t nextCode() const { return m_nextCode; }
|
||||
vluint32_t numSignals() const { return m_numSignals; }
|
||||
vluint32_t maxBits() const { return m_maxBits; }
|
||||
uint32_t nextCode() const { return m_nextCode; }
|
||||
uint32_t numSignals() const { return m_numSignals; }
|
||||
uint32_t maxBits() const { return m_maxBits; }
|
||||
void fullDump(bool value) { m_fullDump = value; }
|
||||
vluint64_t timeLastDump() { return m_timeLastDump; }
|
||||
|
||||
double timeRes() const { return m_timeRes; }
|
||||
double timeUnit() const { return m_timeUnit; }
|
||||
@@ -223,7 +304,8 @@ protected:
|
||||
|
||||
void traceInit() VL_MT_UNSAFE;
|
||||
|
||||
void declCode(vluint32_t code, vluint32_t bits, bool tri);
|
||||
// Declare new signal and return true if enabled
|
||||
bool declCode(uint32_t code, const char* namep, uint32_t bits, bool tri);
|
||||
|
||||
// Is this an escape?
|
||||
bool isScopeEscape(char c) { return std::isspace(c) || c == m_scopeEscape; }
|
||||
@@ -235,17 +317,32 @@ protected:
|
||||
void closeBase();
|
||||
void flushBase();
|
||||
|
||||
#ifdef VL_THREADED
|
||||
bool offload() const { return m_offload; }
|
||||
bool parallel() const { return m_parallel; }
|
||||
#else
|
||||
static constexpr bool offload() { return false; }
|
||||
static constexpr bool parallel() { return false; }
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
// Virtual functions to be provided by the format specific implementation
|
||||
|
||||
// Called when the trace moves forward to a new time point
|
||||
virtual void emitTimeChange(vluint64_t timeui) = 0;
|
||||
virtual void emitTimeChange(uint64_t timeui) = 0;
|
||||
|
||||
// These hooks are called before a full or change based dump is produced.
|
||||
// The return value indicates whether to proceed with the dump.
|
||||
virtual bool preFullDump() = 0;
|
||||
virtual bool preChangeDump() = 0;
|
||||
|
||||
// Trace buffer management
|
||||
virtual Buffer* getTraceBuffer() = 0;
|
||||
virtual void commitTraceBuffer(Buffer*) = 0;
|
||||
|
||||
// Configure sub-class
|
||||
virtual void configure(const VerilatedTraceConfig&) = 0;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
@@ -259,23 +356,58 @@ public:
|
||||
// Set time resolution (s/ms, defaults to ns)
|
||||
void set_time_resolution(const char* unitp) VL_MT_SAFE;
|
||||
void set_time_resolution(const std::string& unit) VL_MT_SAFE;
|
||||
// Set variables to dump, using $dumpvars format
|
||||
// If level = 0, dump everything and hier is then ignored
|
||||
void dumpvars(int level, const std::string& hier) VL_MT_SAFE;
|
||||
|
||||
// Call
|
||||
void dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
void dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
//=========================================================================
|
||||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
void addModel(VerilatedModel*) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
|
||||
void addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE;
|
||||
|
||||
void scopeEscape(char flag) { m_scopeEscape = flag; }
|
||||
|
||||
void pushNamePrefix(const std::string&);
|
||||
void popNamePrefix(unsigned count = 1);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTraceBuffer
|
||||
|
||||
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
|
||||
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
|
||||
template <class T_Buffer>
|
||||
class VerilatedTraceBuffer VL_NOT_FINAL : public T_Buffer {
|
||||
protected:
|
||||
// Type of the owner trace file
|
||||
using Trace = typename std::remove_cv<
|
||||
typename std::remove_reference<decltype(T_Buffer::m_owner)>::type>::type;
|
||||
|
||||
static_assert(std::has_virtual_destructor<T_Buffer>::value, "");
|
||||
static_assert(std::is_base_of<VerilatedTrace<Trace, T_Buffer>, Trace>::value, "");
|
||||
|
||||
friend Trace; // Give the trace file access to the private bits
|
||||
friend std::default_delete<VerilatedTraceBuffer<T_Buffer>>;
|
||||
|
||||
uint32_t* const m_sigs_oldvalp; // Previous value store
|
||||
EData* const m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
|
||||
|
||||
explicit VerilatedTraceBuffer(Trace& owner);
|
||||
~VerilatedTraceBuffer() override = default;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// Hot path internal interface to Verilator generated code
|
||||
|
||||
@@ -285,108 +417,51 @@ public:
|
||||
// these here, but we cannot afford dynamic dispatch for calling these as
|
||||
// this is very hot code during tracing.
|
||||
|
||||
// duck-typed void emitBit(vluint32_t code, CData newval) = 0;
|
||||
// duck-typed void emitCData(vluint32_t code, CData newval, int bits) = 0;
|
||||
// duck-typed void emitSData(vluint32_t code, SData newval, int bits) = 0;
|
||||
// duck-typed void emitIData(vluint32_t code, IData newval, int bits) = 0;
|
||||
// duck-typed void emitQData(vluint32_t code, QData newval, int bits) = 0;
|
||||
// duck-typed void emitWData(vluint32_t code, const WData* newvalp, int bits) = 0;
|
||||
// duck-typed void emitDouble(vluint32_t code, double newval) = 0;
|
||||
// duck-typed void emitBit(uint32_t code, CData newval) = 0;
|
||||
// duck-typed void emitCData(uint32_t code, CData newval, int bits) = 0;
|
||||
// duck-typed void emitSData(uint32_t code, SData newval, int bits) = 0;
|
||||
// duck-typed void emitIData(uint32_t code, IData newval, int bits) = 0;
|
||||
// duck-typed void emitQData(uint32_t code, QData newval, int bits) = 0;
|
||||
// duck-typed void emitWData(uint32_t code, const WData* newvalp, int bits) = 0;
|
||||
// duck-typed void emitDouble(uint32_t code, double newval) = 0;
|
||||
|
||||
vluint32_t* oldp(vluint32_t code) { return m_sigs_oldvalp + code; }
|
||||
VL_ATTR_ALWINLINE uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
|
||||
|
||||
// Write to previous value buffer value and emit trace entry.
|
||||
void fullBit(vluint32_t* oldp, CData newval);
|
||||
void fullCData(vluint32_t* oldp, CData newval, int bits);
|
||||
void fullSData(vluint32_t* oldp, SData newval, int bits);
|
||||
void fullIData(vluint32_t* oldp, IData newval, int bits);
|
||||
void fullQData(vluint32_t* oldp, QData newval, int bits);
|
||||
void fullWData(vluint32_t* oldp, const WData* newvalp, int bits);
|
||||
void fullDouble(vluint32_t* oldp, double newval);
|
||||
void fullBit(uint32_t* oldp, CData newval);
|
||||
void fullCData(uint32_t* oldp, CData newval, int bits);
|
||||
void fullSData(uint32_t* oldp, SData newval, int bits);
|
||||
void fullIData(uint32_t* oldp, IData newval, int bits);
|
||||
void fullQData(uint32_t* oldp, QData newval, int bits);
|
||||
void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
|
||||
void fullDouble(uint32_t* oldp, double newval);
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Threaded tracing. Just dump everything in the trace buffer
|
||||
inline void chgBit(vluint32_t code, CData newval) {
|
||||
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_BIT_0 | newval;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep += 2;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgCData(vluint32_t code, CData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_CDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep[2] = newval;
|
||||
m_traceBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgSData(vluint32_t code, SData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_SDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep[2] = newval;
|
||||
m_traceBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgIData(vluint32_t code, IData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_IDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep[2] = newval;
|
||||
m_traceBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgQData(vluint32_t code, QData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_QDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
*reinterpret_cast<QData*>(m_traceBufferWritep + 2) = newval;
|
||||
m_traceBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgWData(vluint32_t code, const WData* newvalp, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_WDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep += 2;
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_traceBufferWritep++ = newvalp[i]; }
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgDouble(vluint32_t code, double newval) {
|
||||
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_DOUBLE;
|
||||
m_traceBufferWritep[1] = code;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(m_traceBufferWritep + 2) = newval;
|
||||
m_traceBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
|
||||
#define CHG(name) chg##name##Impl
|
||||
#else
|
||||
#define CHG(name) chg##name
|
||||
#endif
|
||||
|
||||
// In non-threaded mode, these are called directly by the trace callbacks,
|
||||
// and are called chg*. In threaded mode, they are called by the worker
|
||||
// In non-offload mode, these are called directly by the trace callbacks,
|
||||
// and are called chg*. In offload mode, they are called by the worker
|
||||
// thread and are called chg*Impl
|
||||
|
||||
// Check previous dumped value of signal. If changed, then emit trace entry
|
||||
inline void CHG(Bit)(vluint32_t* oldp, CData newval) {
|
||||
const vluint32_t diff = *oldp ^ newval;
|
||||
VL_ATTR_ALWINLINE void chgBit(uint32_t* oldp, CData newval) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
|
||||
}
|
||||
inline void CHG(CData)(vluint32_t* oldp, CData newval, int bits) {
|
||||
const vluint32_t diff = *oldp ^ newval;
|
||||
VL_ATTR_ALWINLINE void chgCData(uint32_t* oldp, CData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
|
||||
}
|
||||
inline void CHG(SData)(vluint32_t* oldp, SData newval, int bits) {
|
||||
const vluint32_t diff = *oldp ^ newval;
|
||||
VL_ATTR_ALWINLINE void chgSData(uint32_t* oldp, SData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
|
||||
}
|
||||
inline void CHG(IData)(vluint32_t* oldp, IData newval, int bits) {
|
||||
const vluint32_t diff = *oldp ^ newval;
|
||||
VL_ATTR_ALWINLINE void chgIData(uint32_t* oldp, IData newval, int bits) {
|
||||
const uint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
|
||||
}
|
||||
inline void CHG(QData)(vluint32_t* oldp, QData newval, int bits) {
|
||||
const vluint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
|
||||
VL_ATTR_ALWINLINE void chgQData(uint32_t* oldp, QData newval, int bits) {
|
||||
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
|
||||
}
|
||||
inline void CHG(WData)(vluint32_t* oldp, const WData* newvalp, int bits) {
|
||||
VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) {
|
||||
if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
|
||||
fullWData(oldp, newvalp, bits);
|
||||
@@ -394,11 +469,85 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void CHG(Double)(vluint32_t* oldp, double newval) {
|
||||
VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
|
||||
}
|
||||
|
||||
#undef CHG
|
||||
};
|
||||
|
||||
#ifdef VL_THREADED
|
||||
//=============================================================================
|
||||
// VerilatedTraceOffloadBuffer
|
||||
|
||||
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
|
||||
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
|
||||
template <class T_Buffer>
|
||||
class VerilatedTraceOffloadBuffer final : public VerilatedTraceBuffer<T_Buffer> {
|
||||
using typename VerilatedTraceBuffer<T_Buffer>::Trace;
|
||||
|
||||
friend Trace; // Give the trace file access to the private bits
|
||||
|
||||
uint32_t* m_offloadBufferWritep; // Write pointer into current buffer
|
||||
uint32_t* const m_offloadBufferEndp; // End of offload buffer
|
||||
|
||||
explicit VerilatedTraceOffloadBuffer(Trace& owner);
|
||||
~VerilatedTraceOffloadBuffer() override = default;
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// Hot path internal interface to Verilator generated code
|
||||
|
||||
// Offloaded tracing. Just dump everything in the offload buffer
|
||||
void chgBit(uint32_t code, CData newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgCData(uint32_t code, CData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgSData(uint32_t code, SData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgIData(uint32_t code, IData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep[2] = newval;
|
||||
m_offloadBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgQData(uint32_t code, QData newval, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
*reinterpret_cast<QData*>(m_offloadBufferWritep + 2) = newval;
|
||||
m_offloadBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
m_offloadBufferWritep += 2;
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_offloadBufferWritep++ = newvalp[i]; }
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
void chgDouble(uint32_t code, double newval) {
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
|
||||
m_offloadBufferWritep[1] = code;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(m_offloadBufferWritep + 2) = newval;
|
||||
m_offloadBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -1,693 +0,0 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2001-2022 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilated common-format tracing implementation code
|
||||
///
|
||||
/// This file must be compiled and linked against all Verilated objects
|
||||
/// that use --trace.
|
||||
///
|
||||
/// Use "verilator --trace" to add this to the Makefile for the linker.
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef VL_CPPCHECK
|
||||
#ifndef VL_DERIVED_T
|
||||
# error "This file should be included in trace format implementations"
|
||||
#endif
|
||||
|
||||
#include "verilated_intrinsics.h"
|
||||
#include "verilated_trace.h"
|
||||
|
||||
#if 0
|
||||
# include <iostream>
|
||||
# define VL_TRACE_THREAD_DEBUG(msg) std::cout << "TRACE THREAD: " << msg << std::endl
|
||||
#else
|
||||
# define VL_TRACE_THREAD_DEBUG(msg)
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
//=============================================================================
|
||||
// Static utility functions
|
||||
|
||||
static double timescaleToDouble(const char* unitp) {
|
||||
char* endp = nullptr;
|
||||
double value = std::strtod(unitp, &endp);
|
||||
// On error so we allow just "ns" to return 1e-9.
|
||||
if (value == 0.0 && endp == unitp) value = 1;
|
||||
unitp = endp;
|
||||
for (; *unitp && std::isspace(*unitp); unitp++) {}
|
||||
switch (*unitp) {
|
||||
case 's': value *= 1e0; break;
|
||||
case 'm': value *= 1e-3; break;
|
||||
case 'u': value *= 1e-6; break;
|
||||
case 'n': value *= 1e-9; break;
|
||||
case 'p': value *= 1e-12; break;
|
||||
case 'f': value *= 1e-15; break;
|
||||
case 'a': value *= 1e-18; break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::string doubleToTimescale(double value) {
|
||||
const char* suffixp = "s";
|
||||
// clang-format off
|
||||
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
|
||||
else if (value >= 1e-3 ) { suffixp = "ms"; value *= 1e3; }
|
||||
else if (value >= 1e-6 ) { suffixp = "us"; value *= 1e6; }
|
||||
else if (value >= 1e-9 ) { suffixp = "ns"; value *= 1e9; }
|
||||
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
|
||||
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
|
||||
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
|
||||
// clang-format on
|
||||
char valuestr[100];
|
||||
VL_SNPRINTF(valuestr, 100, "%0.0f%s", value, suffixp);
|
||||
return valuestr; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
//=========================================================================
|
||||
// Buffer management
|
||||
|
||||
template <> vluint32_t* VerilatedTrace<VL_DERIVED_T>::getTraceBuffer() {
|
||||
vluint32_t* bufferp;
|
||||
// Some jitter is expected, so some number of alternative trace buffers are
|
||||
// required, but don't allocate more than 8 buffers.
|
||||
if (m_numTraceBuffers < 8) {
|
||||
// Allocate a new buffer if none is available
|
||||
if (!m_buffersFromWorker.tryGet(bufferp)) {
|
||||
++m_numTraceBuffers;
|
||||
// Note: over allocate a bit so pointer comparison is well defined
|
||||
// if we overflow only by a small amount
|
||||
bufferp = new vluint32_t[m_traceBufferSize + 16];
|
||||
}
|
||||
} else {
|
||||
// Block until a buffer becomes available
|
||||
bufferp = m_buffersFromWorker.get();
|
||||
}
|
||||
return bufferp;
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::waitForBuffer(const vluint32_t* buffp) {
|
||||
// Slow path code only called on flush/shutdown, so use a simple algorithm.
|
||||
// Collect buffers from worker and stash them until we get the one we want.
|
||||
std::deque<vluint32_t*> stash;
|
||||
do { stash.push_back(m_buffersFromWorker.get()); } while (stash.back() != buffp);
|
||||
// Now put them back in the queue, in the original order.
|
||||
while (!stash.empty()) {
|
||||
m_buffersFromWorker.put_front(stash.back());
|
||||
stash.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Worker thread
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::workerThreadMain() {
|
||||
bool shutdown = false;
|
||||
|
||||
do {
|
||||
vluint32_t* const bufferp = m_buffersToWorker.get();
|
||||
|
||||
VL_TRACE_THREAD_DEBUG("");
|
||||
VL_TRACE_THREAD_DEBUG("Got buffer: " << bufferp);
|
||||
|
||||
const vluint32_t* readp = bufferp;
|
||||
|
||||
while (true) {
|
||||
const vluint32_t cmd = readp[0];
|
||||
const vluint32_t top = cmd >> 4;
|
||||
// Always set this up, as it is almost always needed
|
||||
vluint32_t* const oldp = m_sigs_oldvalp + readp[1];
|
||||
// Note this increment needs to be undone on commands which do not
|
||||
// actually contain a code, but those are the rare cases.
|
||||
readp += 2;
|
||||
|
||||
switch (cmd & 0xF) {
|
||||
//===
|
||||
// CHG_* commands
|
||||
case VerilatedTraceCommand::CHG_BIT_0:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_BIT_0 " << top);
|
||||
chgBitImpl(oldp, 0);
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_BIT_1:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_BIT_1 " << top);
|
||||
chgBitImpl(oldp, 1);
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_CDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_CDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgCDataImpl(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_SDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_SDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgSDataImpl(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_IDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_IDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgIDataImpl(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_QDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_QDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgQDataImpl(oldp, *reinterpret_cast<const QData*>(readp), top);
|
||||
readp += 2;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_WDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_WDATA " << top);
|
||||
chgWDataImpl(oldp, readp, top);
|
||||
readp += VL_WORDS_I(top);
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_DOUBLE:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_DOUBLE " << top);
|
||||
chgDoubleImpl(oldp, *reinterpret_cast<const double*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
//===
|
||||
// Rare commands
|
||||
case VerilatedTraceCommand::TIME_CHANGE:
|
||||
VL_TRACE_THREAD_DEBUG("Command TIME_CHANGE " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
emitTimeChange(*reinterpret_cast<const vluint64_t*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
//===
|
||||
// Commands ending this buffer
|
||||
case VerilatedTraceCommand::END: VL_TRACE_THREAD_DEBUG("Command END"); break;
|
||||
case VerilatedTraceCommand::SHUTDOWN:
|
||||
VL_TRACE_THREAD_DEBUG("Command SHUTDOWN");
|
||||
shutdown = true;
|
||||
break;
|
||||
|
||||
//===
|
||||
// Unknown command
|
||||
default: { // LCOV_EXCL_START
|
||||
VL_TRACE_THREAD_DEBUG("Command UNKNOWN");
|
||||
VL_PRINTF_MT("Trace command: 0x%08x\n", cmd);
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unknown trace command");
|
||||
break;
|
||||
} // LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// The above switch will execute 'continue' when necessary,
|
||||
// so if we ever reach here, we are done with the buffer.
|
||||
break;
|
||||
}
|
||||
|
||||
VL_TRACE_THREAD_DEBUG("Returning buffer");
|
||||
|
||||
// Return buffer
|
||||
m_buffersFromWorker.put(bufferp);
|
||||
} while (VL_LIKELY(!shutdown));
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::shutdownWorker() {
|
||||
// If the worker thread is not running, done..
|
||||
if (!m_workerThread) return;
|
||||
|
||||
// Hand an buffer with a shutdown command to the worker thread
|
||||
vluint32_t* const bufferp = getTraceBuffer();
|
||||
bufferp[0] = VerilatedTraceCommand::SHUTDOWN;
|
||||
m_buffersToWorker.put(bufferp);
|
||||
// Wait for it to return
|
||||
waitForBuffer(bufferp);
|
||||
// Join the thread and delete it
|
||||
m_workerThread->join();
|
||||
m_workerThread.reset(nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Life cycle
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::closeBase() {
|
||||
#ifdef VL_TRACE_THREADED
|
||||
shutdownWorker();
|
||||
while (m_numTraceBuffers) {
|
||||
delete[] m_buffersFromWorker.get();
|
||||
--m_numTraceBuffers;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::flushBase() {
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Hand an empty buffer to the worker thread
|
||||
vluint32_t* const bufferp = getTraceBuffer();
|
||||
*bufferp = VerilatedTraceCommand::END;
|
||||
m_buffersToWorker.put(bufferp);
|
||||
// Wait for it to be returned. As the processing is in-order,
|
||||
// this ensures all previous buffers have been processed.
|
||||
waitForBuffer(bufferp);
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Callbacks to run on global events
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::onFlush(void* selfp) {
|
||||
// This calls 'flush' on the derived class (which must then get any mutex)
|
||||
reinterpret_cast<VL_DERIVED_T*>(selfp)->flush();
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::onExit(void* selfp) {
|
||||
// This calls 'close' on the derived class (which must then get any mutex)
|
||||
reinterpret_cast<VL_DERIVED_T*>(selfp)->close();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
template <>
|
||||
VerilatedTrace<VL_DERIVED_T>::VerilatedTrace()
|
||||
: m_sigs_oldvalp{nullptr}
|
||||
, m_timeLastDump{0}
|
||||
, m_fullDump{true}
|
||||
, m_nextCode{0}
|
||||
, m_numSignals{0}
|
||||
, m_maxBits{0}
|
||||
, m_scopeEscape{'.'}
|
||||
, m_timeRes{1e-9}
|
||||
, m_timeUnit {
|
||||
1e-9
|
||||
}
|
||||
#ifdef VL_TRACE_THREADED
|
||||
, m_numTraceBuffers { 0 }
|
||||
#endif
|
||||
{
|
||||
set_time_unit(Verilated::threadContextp()->timeunitString());
|
||||
set_time_resolution(Verilated::threadContextp()->timeprecisionString());
|
||||
}
|
||||
|
||||
template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() {
|
||||
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = nullptr);
|
||||
Verilated::removeFlushCb(VerilatedTrace<VL_DERIVED_T>::onFlush, this);
|
||||
Verilated::removeExitCb(VerilatedTrace<VL_DERIVED_T>::onExit, this);
|
||||
#ifdef VL_TRACE_THREADED
|
||||
closeBase();
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
|
||||
// Note: It is possible to re-open a trace file (VCD in particular),
|
||||
// so we must reset the next code here, but it must have the same number
|
||||
// of codes on re-open
|
||||
const vluint32_t expectedCodes = nextCode();
|
||||
m_nextCode = 1;
|
||||
m_numSignals = 0;
|
||||
m_maxBits = 0;
|
||||
|
||||
// Call all initialize callbacks, which will:
|
||||
// - Call decl* for each signal
|
||||
// - Store the base code
|
||||
for (vluint32_t i = 0; i < m_initCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_initCbs[i];
|
||||
cbr.m_initCb(cbr.m_userp, self(), nextCode());
|
||||
}
|
||||
|
||||
if (expectedCodes && nextCode() != expectedCodes) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Reopening trace file with different number of signals");
|
||||
}
|
||||
|
||||
// Now that we know the number of codes, allocate space for the buffer
|
||||
// holding previous signal values.
|
||||
if (!m_sigs_oldvalp) m_sigs_oldvalp = new vluint32_t[nextCode()];
|
||||
|
||||
// Set callback so flush/abort will flush this file
|
||||
Verilated::addFlushCb(VerilatedTrace<VL_DERIVED_T>::onFlush, this);
|
||||
Verilated::addExitCb(VerilatedTrace<VL_DERIVED_T>::onExit, this);
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Compute trace buffer size. we need to be able to store a new value for
|
||||
// each signal, which is 'nextCode()' entries after the init callbacks
|
||||
// above have been run, plus up to 2 more words of metadata per signal,
|
||||
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
|
||||
// update.
|
||||
m_traceBufferSize = nextCode() + numSignals() * 2 + 4;
|
||||
|
||||
// Start the worker thread
|
||||
m_workerThread.reset(new std::thread{&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this});
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::declCode(vluint32_t code, vluint32_t bits, bool tri) {
|
||||
if (VL_UNCOVERABLE(!code)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
|
||||
}
|
||||
// Note: The tri-state flag is not used by Verilator, but is here for
|
||||
// compatibility with some foreign code.
|
||||
int codesNeeded = VL_WORDS_I(bits);
|
||||
if (tri) codesNeeded *= 2;
|
||||
m_nextCode = std::max(m_nextCode, code + codesNeeded);
|
||||
++m_numSignals;
|
||||
m_maxBits = std::max(m_maxBits, bits);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <> std::string VerilatedTrace<VL_DERIVED_T>::timeResStr() const {
|
||||
return doubleToTimescale(m_timeRes);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
|
||||
m_timeUnit = timescaleToDouble(unitp);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const std::string& unit) VL_MT_SAFE {
|
||||
set_time_unit(unit.c_str());
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const char* unitp) VL_MT_SAFE {
|
||||
m_timeRes = timescaleToDouble(unitp);
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const std::string& unit) VL_MT_SAFE {
|
||||
set_time_resolution(unit.c_str());
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
|
||||
// This does get the mutex, but if multiple threads are trying to dump
|
||||
// chances are the data being dumped will have other problems
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
|
||||
VL_PRINTF_MT("%%Warning: previous dump at t=%" PRIu64 ", requesting t=%" PRIu64
|
||||
", dump call ignored\n",
|
||||
m_timeLastDump, timeui);
|
||||
return;
|
||||
} // LCOV_EXCL_STOP
|
||||
m_timeLastDump = timeui;
|
||||
|
||||
Verilated::quiesce();
|
||||
|
||||
// Call hook for format specific behaviour
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
if (!preFullDump()) return;
|
||||
} else {
|
||||
if (!preChangeDump()) return;
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Currently only incremental dumps run on the worker thread
|
||||
vluint32_t* bufferp = nullptr;
|
||||
if (VL_LIKELY(!m_fullDump)) {
|
||||
// Get the trace buffer we are about to fill
|
||||
bufferp = getTraceBuffer();
|
||||
m_traceBufferWritep = bufferp;
|
||||
m_traceBufferEndp = bufferp + m_traceBufferSize;
|
||||
|
||||
// Tell worker to update time point
|
||||
m_traceBufferWritep[0] = VerilatedTraceCommand::TIME_CHANGE;
|
||||
*reinterpret_cast<vluint64_t*>(m_traceBufferWritep + 1) = timeui;
|
||||
m_traceBufferWritep += 3;
|
||||
} else {
|
||||
// Update time point
|
||||
flushBase();
|
||||
emitTimeChange(timeui);
|
||||
}
|
||||
#else
|
||||
// Update time point
|
||||
emitTimeChange(timeui);
|
||||
#endif
|
||||
|
||||
// Run the callbacks
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No more need for next dump to be full
|
||||
for (vluint32_t i = 0; i < m_fullCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_fullCbs[i];
|
||||
cbr.m_dumpCb(cbr.m_userp, self());
|
||||
}
|
||||
} else {
|
||||
for (vluint32_t i = 0; i < m_chgCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_chgCbs[i];
|
||||
cbr.m_dumpCb(cbr.m_userp, self());
|
||||
}
|
||||
}
|
||||
|
||||
for (vluint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_cleanupCbs[i];
|
||||
cbr.m_dumpCb(cbr.m_userp, self());
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
if (VL_LIKELY(bufferp)) {
|
||||
// Mark end of the trace buffer we just filled
|
||||
*m_traceBufferWritep++ = VerilatedTraceCommand::END;
|
||||
|
||||
// Assert no buffer overflow
|
||||
assert(m_traceBufferWritep - bufferp <= m_traceBufferSize);
|
||||
|
||||
// Pass it to the worker thread
|
||||
m_buffersToWorker.put(bufferp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
|
||||
CallbackRecord& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
|
||||
const std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__
|
||||
+ " called with already open file");
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
} // LCOV_EXCL_STOP
|
||||
cbVec.push_back(cbRec);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_initCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_fullCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_chgCbs, cbr);
|
||||
}
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
CallbackRecord cbr{cb, userp};
|
||||
addCallbackRecord(m_cleanupCbs, cbr);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::pushNamePrefix(const std::string& prefix) {
|
||||
m_namePrefixStack.push_back(m_namePrefixStack.back() + prefix);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::popNamePrefix(unsigned count) {
|
||||
while (count--) m_namePrefixStack.pop_back();
|
||||
assert(!m_namePrefixStack.empty());
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Hot path internal interface to Verilator generated code
|
||||
|
||||
// These functions must write the new value back into the old value store,
|
||||
// and subsequently call the format specific emit* implementations. Note
|
||||
// that this file must be included in the format specific implementation, so
|
||||
// the emit* functions can be inlined for performance.
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::fullBit(vluint32_t* oldp, CData newval) {
|
||||
*oldp = newval;
|
||||
self()->emitBit(oldp - m_sigs_oldvalp, newval);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullCData(vluint32_t* oldp, CData newval, int bits) {
|
||||
*oldp = newval;
|
||||
self()->emitCData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullSData(vluint32_t* oldp, SData newval, int bits) {
|
||||
*oldp = newval;
|
||||
self()->emitSData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullIData(vluint32_t* oldp, IData newval, int bits) {
|
||||
*oldp = newval;
|
||||
self()->emitIData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullQData(vluint32_t* oldp, QData newval, int bits) {
|
||||
*reinterpret_cast<QData*>(oldp) = newval;
|
||||
self()->emitQData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullWData(vluint32_t* oldp, const WData* newvalp, int bits) {
|
||||
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
|
||||
self()->emitWData(oldp - m_sigs_oldvalp, newvalp, bits);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::fullDouble(vluint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(oldp) = newval;
|
||||
self()->emitDouble(oldp - m_sigs_oldvalp, newval);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Primitives converting binary values to strings...
|
||||
|
||||
// All of these take a destination pointer where the string will be emitted,
|
||||
// and a value to convert. There are a couple of variants for efficiency.
|
||||
|
||||
static inline void cvtCDataToStr(char* dstp, CData value) {
|
||||
#ifdef VL_HAVE_SSE2
|
||||
// Similar to cvtSDataToStr but only the bottom 8 byte lanes are used
|
||||
const __m128i a = _mm_cvtsi32_si128(value);
|
||||
const __m128i b = _mm_unpacklo_epi8(a, a);
|
||||
const __m128i c = _mm_shufflelo_epi16(b, 0);
|
||||
const __m128i m = _mm_set1_epi64x(0x0102040810204080);
|
||||
const __m128i d = _mm_cmpeq_epi8(_mm_and_si128(c, m), m);
|
||||
const __m128i result = _mm_sub_epi8(_mm_set1_epi8('0'), d);
|
||||
_mm_storel_epi64(reinterpret_cast<__m128i*>(dstp), result);
|
||||
#else
|
||||
dstp[0] = '0' | static_cast<char>((value >> 7) & 1);
|
||||
dstp[1] = '0' | static_cast<char>((value >> 6) & 1);
|
||||
dstp[2] = '0' | static_cast<char>((value >> 5) & 1);
|
||||
dstp[3] = '0' | static_cast<char>((value >> 4) & 1);
|
||||
dstp[4] = '0' | static_cast<char>((value >> 3) & 1);
|
||||
dstp[5] = '0' | static_cast<char>((value >> 2) & 1);
|
||||
dstp[6] = '0' | static_cast<char>((value >> 1) & 1);
|
||||
dstp[7] = '0' | static_cast<char>(value & 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtSDataToStr(char* dstp, SData value) {
|
||||
#ifdef VL_HAVE_SSE2
|
||||
// We want each bit in the 16-bit input value to end up in a byte lane
|
||||
// within the 128-bit XMM register. Note that x86 is little-endian and we
|
||||
// want the MSB of the input at the low address, so we will bit-reverse
|
||||
// at the same time.
|
||||
|
||||
// Put value in bottom of 128-bit register a[15:0] = value
|
||||
const __m128i a = _mm_cvtsi32_si128(value);
|
||||
// Interleave bytes with themselves
|
||||
// b[15: 0] = {2{a[ 7:0]}} == {2{value[ 7:0]}}
|
||||
// b[31:16] = {2{a[15:8]}} == {2{value[15:8]}}
|
||||
const __m128i b = _mm_unpacklo_epi8(a, a);
|
||||
// Shuffle bottom 64 bits, note swapping high bytes with low bytes
|
||||
// c[31: 0] = {2{b[31:16]}} == {4{value[15:8}}
|
||||
// c[63:32] = {2{b[15: 0]}} == {4{value[ 7:0}}
|
||||
const __m128i c = _mm_shufflelo_epi16(b, 0x05);
|
||||
// Shuffle whole register
|
||||
// d[ 63: 0] = {2{c[31: 0]}} == {8{value[15:8}}
|
||||
// d[126:54] = {2{c[63:32]}} == {8{value[ 7:0}}
|
||||
const __m128i d = _mm_shuffle_epi32(c, 0x50);
|
||||
// Test each bit within the bytes, this sets each byte lane to 0
|
||||
// if the bit for that lane is 0 and to 0xff if the bit is 1.
|
||||
const __m128i m = _mm_set1_epi64x(0x0102040810204080);
|
||||
const __m128i e = _mm_cmpeq_epi8(_mm_and_si128(d, m), m);
|
||||
// Convert to ASCII by subtracting the masks from ASCII '0':
|
||||
// '0' - 0 is '0', '0' - -1 is '1'
|
||||
const __m128i result = _mm_sub_epi8(_mm_set1_epi8('0'), e);
|
||||
// Store the 16 characters to the un-aligned buffer
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i*>(dstp), result);
|
||||
#else
|
||||
cvtCDataToStr(dstp, value >> 8);
|
||||
cvtCDataToStr(dstp + 8, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtIDataToStr(char* dstp, IData value) {
|
||||
#ifdef VL_HAVE_AVX2
|
||||
// Similar to cvtSDataToStr but the bottom 16-bits are processed in the
|
||||
// top half of the YMM registerss
|
||||
const __m256i a = _mm256_insert_epi32(_mm256_undefined_si256(), value, 0);
|
||||
const __m256i b = _mm256_permute4x64_epi64(a, 0);
|
||||
const __m256i s = _mm256_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3);
|
||||
const __m256i c = _mm256_shuffle_epi8(b, s);
|
||||
const __m256i m = _mm256_set1_epi64x(0x0102040810204080);
|
||||
const __m256i d = _mm256_cmpeq_epi8(_mm256_and_si256(c, m), m);
|
||||
const __m256i result = _mm256_sub_epi8(_mm256_set1_epi8('0'), d);
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp), result);
|
||||
#else
|
||||
cvtSDataToStr(dstp, value >> 16);
|
||||
cvtSDataToStr(dstp + 16, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtQDataToStr(char* dstp, QData value) {
|
||||
cvtIDataToStr(dstp, value >> 32);
|
||||
cvtIDataToStr(dstp + 32, value);
|
||||
}
|
||||
|
||||
#define cvtEDataToStr cvtIDataToStr
|
||||
|
||||
//=============================================================================
|
||||
|
||||
#ifdef VERILATED_VCD_TEST
|
||||
|
||||
void verilated_trace_imp_selftest() {
|
||||
#define SELF_CHECK(got, exp) \
|
||||
do { \
|
||||
if ((got) != (exp)) VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: selftest"); \
|
||||
} while (0)
|
||||
|
||||
#define SELF_CHECK_TS(scale) \
|
||||
SELF_CHECK(doubleToTimescale(timescaleToDouble(scale)), std::string{scale});
|
||||
SELF_CHECK_TS("100s");
|
||||
SELF_CHECK_TS("10s");
|
||||
SELF_CHECK_TS("1s");
|
||||
SELF_CHECK_TS("100ms");
|
||||
SELF_CHECK_TS("10ms");
|
||||
SELF_CHECK_TS("1ms");
|
||||
SELF_CHECK_TS("100us");
|
||||
SELF_CHECK_TS("10us");
|
||||
SELF_CHECK_TS("1us");
|
||||
SELF_CHECK_TS("100ns");
|
||||
SELF_CHECK_TS("10ns");
|
||||
SELF_CHECK_TS("1ns");
|
||||
SELF_CHECK_TS("100ps");
|
||||
SELF_CHECK_TS("10ps");
|
||||
SELF_CHECK_TS("1ps");
|
||||
SELF_CHECK_TS("100fs");
|
||||
SELF_CHECK_TS("10fs");
|
||||
SELF_CHECK_TS("1fs");
|
||||
SELF_CHECK_TS("100as");
|
||||
SELF_CHECK_TS("10as");
|
||||
SELF_CHECK_TS("1as");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // VL_CPPCHECK
|
||||
@@ -0,0 +1,939 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
// Copyright 2001-2022 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//=============================================================================
|
||||
//
|
||||
// Verilated tracing implementation code template common to all formats.
|
||||
// This file is included by the format specific implementations and
|
||||
// should not be used otherwise.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef VL_CPPCHECK
|
||||
#if !defined(VL_SUB_T) || !defined(VL_BUF_T)
|
||||
# error "This file should be included in trace format implementations"
|
||||
#endif
|
||||
|
||||
#include "verilated_intrinsics.h"
|
||||
#include "verilated_trace.h"
|
||||
#ifdef VL_THREADED
|
||||
# include "verilated_threads.h"
|
||||
# include <list>
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
# include <iostream>
|
||||
# define VL_TRACE_OFFLOAD_DEBUG(msg) std::cout << "TRACE OFFLOAD THREAD: " << msg << std::endl
|
||||
#else
|
||||
# define VL_TRACE_OFFLOAD_DEBUG(msg)
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
//=============================================================================
|
||||
// Static utility functions
|
||||
|
||||
static double timescaleToDouble(const char* unitp) {
|
||||
char* endp = nullptr;
|
||||
double value = std::strtod(unitp, &endp);
|
||||
// On error so we allow just "ns" to return 1e-9.
|
||||
if (value == 0.0 && endp == unitp) value = 1;
|
||||
unitp = endp;
|
||||
for (; *unitp && std::isspace(*unitp); unitp++) {}
|
||||
switch (*unitp) {
|
||||
case 's': value *= 1e0; break;
|
||||
case 'm': value *= 1e-3; break;
|
||||
case 'u': value *= 1e-6; break;
|
||||
case 'n': value *= 1e-9; break;
|
||||
case 'p': value *= 1e-12; break;
|
||||
case 'f': value *= 1e-15; break;
|
||||
case 'a': value *= 1e-18; break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::string doubleToTimescale(double value) {
|
||||
const char* suffixp = "s";
|
||||
// clang-format off
|
||||
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
|
||||
else if (value >= 1e-3 ) { suffixp = "ms"; value *= 1e3; }
|
||||
else if (value >= 1e-6 ) { suffixp = "us"; value *= 1e6; }
|
||||
else if (value >= 1e-9 ) { suffixp = "ns"; value *= 1e9; }
|
||||
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
|
||||
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
|
||||
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
|
||||
// clang-format on
|
||||
char valuestr[100];
|
||||
VL_SNPRINTF(valuestr, 100, "%0.0f%s", value, suffixp);
|
||||
return valuestr; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
//=========================================================================
|
||||
// Buffer management
|
||||
|
||||
template <>
|
||||
uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
|
||||
uint32_t* bufferp;
|
||||
// Some jitter is expected, so some number of alternative offlaod buffers are
|
||||
// required, but don't allocate more than 8 buffers.
|
||||
if (m_numOffloadBuffers < 8) {
|
||||
// Allocate a new buffer if none is available
|
||||
if (!m_offloadBuffersFromWorker.tryGet(bufferp)) {
|
||||
++m_numOffloadBuffers;
|
||||
// Note: over allocate a bit so pointer comparison is well defined
|
||||
// if we overflow only by a small amount
|
||||
bufferp = new uint32_t[m_offloadBufferSize + 16];
|
||||
}
|
||||
} else {
|
||||
// Block until a buffer becomes available
|
||||
bufferp = m_offloadBuffersFromWorker.get();
|
||||
}
|
||||
return bufferp;
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const uint32_t* buffp) {
|
||||
// Slow path code only called on flush/shutdown, so use a simple algorithm.
|
||||
// Collect buffers from worker and stash them until we get the one we want.
|
||||
std::deque<uint32_t*> stash;
|
||||
do { stash.push_back(m_offloadBuffersFromWorker.get()); } while (stash.back() != buffp);
|
||||
// Now put them back in the queue, in the original order.
|
||||
while (!stash.empty()) {
|
||||
m_offloadBuffersFromWorker.put_front(stash.back());
|
||||
stash.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Worker thread
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
|
||||
bool shutdown = false;
|
||||
|
||||
do {
|
||||
uint32_t* const bufferp = m_offloadBuffersToWorker.get();
|
||||
|
||||
VL_TRACE_OFFLOAD_DEBUG("");
|
||||
VL_TRACE_OFFLOAD_DEBUG("Got buffer: " << bufferp);
|
||||
|
||||
const uint32_t* readp = bufferp;
|
||||
|
||||
std::unique_ptr<Buffer> traceBufp; // We own the passed tracebuffer
|
||||
|
||||
while (true) {
|
||||
const uint32_t cmd = readp[0];
|
||||
const uint32_t top = cmd >> 4;
|
||||
// Always set this up, as it is almost always needed
|
||||
uint32_t* const oldp = m_sigs_oldvalp + readp[1];
|
||||
// Note this increment needs to be undone on commands which do not
|
||||
// actually contain a code, but those are the rare cases.
|
||||
readp += 2;
|
||||
|
||||
switch (cmd & 0xF) {
|
||||
//===
|
||||
// CHG_* commands
|
||||
case VerilatedTraceOffloadCommand::CHG_BIT_0:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_0 " << top);
|
||||
traceBufp->chgBit(oldp, 0);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_BIT_1:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_1 " << top);
|
||||
traceBufp->chgBit(oldp, 1);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_CDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_CDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgCData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_SDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_SDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgSData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_IDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_IDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgIData(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_QDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_QDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
traceBufp->chgQData(oldp, *reinterpret_cast<const QData*>(readp), top);
|
||||
readp += 2;
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_WDATA:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_WDATA " << top);
|
||||
traceBufp->chgWData(oldp, readp, top);
|
||||
readp += VL_WORDS_I(top);
|
||||
continue;
|
||||
case VerilatedTraceOffloadCommand::CHG_DOUBLE:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command CHG_DOUBLE " << top);
|
||||
traceBufp->chgDouble(oldp, *reinterpret_cast<const double*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
//===
|
||||
// Rare commands
|
||||
case VerilatedTraceOffloadCommand::TIME_CHANGE:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command TIME_CHANGE " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
emitTimeChange(*reinterpret_cast<const uint64_t*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
case VerilatedTraceOffloadCommand::TRACE_BUFFER:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
traceBufp.reset(*reinterpret_cast<Buffer* const*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
//===
|
||||
// Commands ending this buffer
|
||||
case VerilatedTraceOffloadCommand::END: //
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command END");
|
||||
break;
|
||||
case VerilatedTraceOffloadCommand::SHUTDOWN:
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command SHUTDOWN");
|
||||
shutdown = true;
|
||||
break;
|
||||
|
||||
//===
|
||||
// Unknown command
|
||||
default: { // LCOV_EXCL_START
|
||||
VL_TRACE_OFFLOAD_DEBUG("Command UNKNOWN " << cmd);
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unknown trace command");
|
||||
break;
|
||||
} // LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
// The above switch will execute 'continue' when necessary,
|
||||
// so if we ever reach here, we are done with the buffer.
|
||||
break;
|
||||
}
|
||||
|
||||
VL_TRACE_OFFLOAD_DEBUG("Returning buffer");
|
||||
|
||||
// Return buffer
|
||||
m_offloadBuffersFromWorker.put(bufferp);
|
||||
} while (VL_LIKELY(!shutdown));
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
|
||||
// If the worker thread is not running, done..
|
||||
if (!m_workerThread) return;
|
||||
|
||||
// Hand an buffer with a shutdown command to the worker thread
|
||||
uint32_t* const bufferp = getOffloadBuffer();
|
||||
bufferp[0] = VerilatedTraceOffloadCommand::SHUTDOWN;
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
// Wait for it to return
|
||||
waitForOffloadBuffer(bufferp);
|
||||
// Join the thread and delete it
|
||||
m_workerThread->join();
|
||||
m_workerThread.reset(nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Life cycle
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
shutdownOffloadWorker();
|
||||
while (m_numOffloadBuffers) {
|
||||
delete[] m_offloadBuffersFromWorker.get();
|
||||
--m_numOffloadBuffers;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
// Hand an empty buffer to the worker thread
|
||||
uint32_t* const bufferp = getOffloadBuffer();
|
||||
*bufferp = VerilatedTraceOffloadCommand::END;
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
// Wait for it to be returned. As the processing is in-order,
|
||||
// this ensures all previous buffers have been processed.
|
||||
waitForOffloadBuffer(bufferp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Callbacks to run on global events
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush(void* selfp) {
|
||||
// This calls 'flush' on the derived class (which must then get any mutex)
|
||||
reinterpret_cast<VL_SUB_T*>(selfp)->flush();
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
|
||||
// This calls 'close' on the derived class (which must then get any mutex)
|
||||
reinterpret_cast<VL_SUB_T*>(selfp)->close();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
template <>
|
||||
VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
|
||||
set_time_unit(Verilated::threadContextp()->timeunitString());
|
||||
set_time_resolution(Verilated::threadContextp()->timeprecisionString());
|
||||
}
|
||||
|
||||
template <>
|
||||
VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
|
||||
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = nullptr);
|
||||
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
|
||||
Verilated::removeFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
|
||||
Verilated::removeExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
|
||||
if (offload()) closeBase();
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
|
||||
// Note: It is possible to re-open a trace file (VCD in particular),
|
||||
// so we must reset the next code here, but it must have the same number
|
||||
// of codes on re-open
|
||||
const uint32_t expectedCodes = nextCode();
|
||||
m_nextCode = 1;
|
||||
m_numSignals = 0;
|
||||
m_maxBits = 0;
|
||||
m_sigs_enabledVec.clear();
|
||||
|
||||
// Call all initialize callbacks, which will:
|
||||
// - Call decl* for each signal (these eventually call ::declCode)
|
||||
// - Store the base code
|
||||
for (uint32_t i = 0; i < m_initCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_initCbs[i];
|
||||
cbr.m_initCb(cbr.m_userp, self(), nextCode());
|
||||
}
|
||||
|
||||
if (expectedCodes && nextCode() != expectedCodes) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Reopening trace file with different number of signals");
|
||||
}
|
||||
|
||||
// Now that we know the number of codes, allocate space for the buffer
|
||||
// holding previous signal values.
|
||||
if (!m_sigs_oldvalp) m_sigs_oldvalp = new uint32_t[nextCode()];
|
||||
|
||||
// Apply enables
|
||||
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
|
||||
if (!m_sigs_enabledVec.empty()) {
|
||||
// Else if was empty, m_sigs_enabledp = nullptr to short circuit tests
|
||||
// But it isn't, so alloc one bit for each code to indicate enablement
|
||||
// We don't want to still use m_signs_enabledVec as std::vector<bool> is not
|
||||
// guarenteed to be fast
|
||||
m_sigs_enabledp = new uint32_t[1 + VL_WORDS_I(nextCode())]{0};
|
||||
m_sigs_enabledVec.reserve(nextCode());
|
||||
for (size_t code = 0; code < nextCode(); ++code) {
|
||||
if (m_sigs_enabledVec[code]) {
|
||||
m_sigs_enabledp[VL_BITWORD_I(code)] |= 1U << VL_BITBIT_I(code);
|
||||
}
|
||||
}
|
||||
m_sigs_enabledVec.clear();
|
||||
}
|
||||
|
||||
// Set callback so flush/abort will flush this file
|
||||
Verilated::addFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
|
||||
Verilated::addExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
|
||||
|
||||
#ifdef VL_THREADED
|
||||
if (offload()) {
|
||||
// Compute offload buffer size. we need to be able to store a new value for
|
||||
// each signal, which is 'nextCode()' entries after the init callbacks
|
||||
// above have been run, plus up to 2 more words of metadata per signal,
|
||||
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
|
||||
// update.
|
||||
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
|
||||
|
||||
// Start the worker thread
|
||||
m_workerThread.reset(
|
||||
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const char* namep, uint32_t bits,
|
||||
bool tri) {
|
||||
if (VL_UNCOVERABLE(!code)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
|
||||
}
|
||||
// To keep it simple, this is O(enables * signals), but we expect few enables
|
||||
std::string declName = namePrefix() + namep;
|
||||
bool enabled = false;
|
||||
if (m_dumpvars.empty()) enabled = true;
|
||||
for (const auto& item : m_dumpvars) {
|
||||
const int dumpvarsLevel = item.first;
|
||||
const char* dvp = item.second.c_str();
|
||||
const char* np = declName.c_str();
|
||||
while (*dvp && *dvp == *np) {
|
||||
++dvp;
|
||||
++np;
|
||||
}
|
||||
if (*dvp) continue; // Didn't match dumpvar item
|
||||
if (*np && *np != ' ') continue; // e.g. "t" isn't a match for "top"
|
||||
int levels = 0;
|
||||
while (*np) {
|
||||
if (*np++ == ' ') ++levels;
|
||||
}
|
||||
if (levels > dumpvarsLevel) continue; // Too deep
|
||||
// We only need to set first code word if it's a multicode signal
|
||||
// as that's all we'll check for later
|
||||
if (m_sigs_enabledVec.size() <= code) m_sigs_enabledVec.resize((code + 1024) * 2);
|
||||
m_sigs_enabledVec[code] = true;
|
||||
enabled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Note: The tri-state flag is not used by Verilator, but is here for
|
||||
// compatibility with some foreign code.
|
||||
int codesNeeded = VL_WORDS_I(bits);
|
||||
if (tri) codesNeeded *= 2;
|
||||
m_nextCode = std::max(m_nextCode, code + codesNeeded);
|
||||
++m_numSignals;
|
||||
m_maxBits = std::max(m_maxBits, bits);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <>
|
||||
std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
|
||||
return doubleToTimescale(m_timeRes);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
|
||||
m_timeUnit = timescaleToDouble(unitp);
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const std::string& unit) VL_MT_SAFE {
|
||||
set_time_unit(unit.c_str());
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_resolution(const char* unitp) VL_MT_SAFE {
|
||||
m_timeRes = timescaleToDouble(unitp);
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_resolution(const std::string& unit) VL_MT_SAFE {
|
||||
set_time_resolution(unit.c_str());
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dumpvars(int level, const std::string& hier) VL_MT_SAFE {
|
||||
if (level == 0) {
|
||||
m_dumpvars.clear(); // empty = everything on
|
||||
} else {
|
||||
// Convert Verilog . separators to trace space separators
|
||||
std::string hierSpaced = hier;
|
||||
for (auto& i : hierSpaced) {
|
||||
if (i == '.') i = ' ';
|
||||
}
|
||||
m_dumpvars.push_back(std::make_pair(level, hierSpaced));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::parallelWorkerTask(void* datap, bool) {
|
||||
ParallelWorkerData* const wdp = reinterpret_cast<ParallelWorkerData*>(datap);
|
||||
// Run the task
|
||||
wdp->m_cb(wdp->m_userp, wdp->m_bufp);
|
||||
// Mark buffer as ready
|
||||
const VerilatedLockGuard lock{wdp->m_mutex};
|
||||
wdp->m_ready.store(true);
|
||||
if (wdp->m_waiting) wdp->m_cv.notify_one();
|
||||
}
|
||||
|
||||
template <>
|
||||
VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wait() {
|
||||
// Spin for a while, waiting for the buffer to become ready
|
||||
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
|
||||
if (VL_LIKELY(m_ready.load(std::memory_order_relaxed))) return;
|
||||
VL_CPU_RELAX();
|
||||
}
|
||||
// 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_waiting = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runCallbacks(const std::vector<CallbackRecord>& cbVec) {
|
||||
#ifdef VL_THREADED
|
||||
if (parallel()) {
|
||||
// If tracing in parallel, dispatch to the thread pool
|
||||
VlThreadPool* threadPoolp = static_cast<VlThreadPool*>(m_contextp->threadPoolp());
|
||||
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
|
||||
std::list<ParallelWorkerData> workerData;
|
||||
// We use the whole pool + the main thread
|
||||
const unsigned threads = threadPoolp->numThreads() + 1;
|
||||
// Main thread executes all jobs with index % threads == 0
|
||||
std::vector<ParallelWorkerData*> mainThreadWorkerData;
|
||||
// Enuque all the jobs
|
||||
for (unsigned i = 0; i < cbVec.size(); ++i) {
|
||||
const CallbackRecord& cbr = cbVec[i];
|
||||
// Always get the trace buffer on the main thread
|
||||
Buffer* const bufp = getTraceBuffer();
|
||||
// Create new work item
|
||||
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
|
||||
// Grab the new work item
|
||||
ParallelWorkerData* const itemp = &workerData.back();
|
||||
// Enqueue task to thread pool, or main thread
|
||||
if (unsigned rem = i % threads) {
|
||||
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp);
|
||||
} else {
|
||||
mainThreadWorkerData.push_back(itemp);
|
||||
}
|
||||
}
|
||||
// Execute main thead jobs
|
||||
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
|
||||
parallelWorkerTask(itemp, false);
|
||||
}
|
||||
// Commit all trace buffers in order
|
||||
for (ParallelWorkerData& item : workerData) {
|
||||
// Wait until ready
|
||||
item.wait();
|
||||
// Commit the buffer
|
||||
commitTraceBuffer(item.m_bufp);
|
||||
}
|
||||
|
||||
// Done
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Fall back on sequential execution
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
Buffer* const traceBufferp = getTraceBuffer();
|
||||
cbr.m_dumpCb(cbr.m_userp, traceBufferp);
|
||||
commitTraceBuffer(traceBufferp);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runOffloadedCallbacks(
|
||||
const std::vector<CallbackRecord>& cbVec) {
|
||||
// Fall back on sequential execution
|
||||
#ifdef VL_THREADED
|
||||
for (const CallbackRecord& cbr : cbVec) {
|
||||
Buffer* traceBufferp = getTraceBuffer();
|
||||
cbr.m_dumpOffloadCb(cbr.m_userp, static_cast<OffloadBuffer*>(traceBufferp));
|
||||
commitTraceBuffer(traceBufferp);
|
||||
}
|
||||
#else
|
||||
if (!cbVec.empty()) VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
|
||||
// This does get the mutex, but if multiple threads are trying to dump
|
||||
// chances are the data being dumped will have other problems
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (VL_UNCOVERABLE(m_didSomeDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
|
||||
VL_PRINTF_MT("%%Warning: previous dump at t=%" PRIu64 ", requesting t=%" PRIu64
|
||||
", dump call ignored\n",
|
||||
m_timeLastDump, timeui);
|
||||
return;
|
||||
} // LCOV_EXCL_STOP
|
||||
m_timeLastDump = timeui;
|
||||
m_didSomeDump = true;
|
||||
|
||||
Verilated::quiesce();
|
||||
|
||||
// Call hook for format specific behaviour
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
if (!preFullDump()) return;
|
||||
} else {
|
||||
if (!preChangeDump()) return;
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
uint32_t* bufferp = nullptr;
|
||||
#endif
|
||||
if (offload()) {
|
||||
#ifdef VL_THREADED
|
||||
// Currently only incremental dumps run on the worker thread
|
||||
if (VL_LIKELY(!m_fullDump)) {
|
||||
// Get the offload buffer we are about to fill
|
||||
bufferp = getOffloadBuffer();
|
||||
m_offloadBufferWritep = bufferp;
|
||||
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
|
||||
|
||||
// Tell worker to update time point
|
||||
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
|
||||
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
|
||||
m_offloadBufferWritep += 3;
|
||||
} else {
|
||||
// Update time point
|
||||
flushBase();
|
||||
emitTimeChange(timeui);
|
||||
}
|
||||
#else
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
} else {
|
||||
// Update time point
|
||||
emitTimeChange(timeui);
|
||||
}
|
||||
|
||||
// Run the callbacks
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No more need for next dump to be full
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_fullOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_fullCbs);
|
||||
}
|
||||
} else {
|
||||
if (offload()) {
|
||||
runOffloadedCallbacks(m_chgOffloadCbs);
|
||||
} else {
|
||||
runCallbacks(m_chgCbs);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
|
||||
const CallbackRecord& cbr = m_cleanupCbs[i];
|
||||
cbr.m_cleanupCb(cbr.m_userp, self());
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
if (offload() && VL_LIKELY(bufferp)) {
|
||||
// Mark end of the offload buffer we just filled
|
||||
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::END;
|
||||
|
||||
// Assert no buffer overflow
|
||||
assert(m_offloadBufferWritep - bufferp <= m_offloadBufferSize);
|
||||
|
||||
// Reset our pointers as we are giving up the buffer
|
||||
m_offloadBufferWritep = nullptr;
|
||||
m_offloadBufferEndp = nullptr;
|
||||
|
||||
// Pass it to the worker thread
|
||||
m_offloadBuffersToWorker.put(bufferp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
|
||||
const bool firstModel = m_models.empty();
|
||||
const bool newModel = m_models.insert(modelp).second;
|
||||
VerilatedContext* const contextp = modelp->contextp();
|
||||
|
||||
// Validate
|
||||
if (!newModel) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"The same model has already been added to this trace file");
|
||||
}
|
||||
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"A trace file instance can only handle models from the same context");
|
||||
}
|
||||
if (VL_UNCOVERABLE(m_didSomeDump)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Cannot add models to a trace file if 'dump' has already been called");
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
// Keep hold of the context
|
||||
m_contextp = contextp;
|
||||
|
||||
// Get the desired trace config from the model
|
||||
const std::unique_ptr<VerilatedTraceConfig> configp = modelp->traceConfig();
|
||||
#ifndef VL_THREADED
|
||||
if (configp->m_useOffloading) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use trace offloading without VL_THREADED");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Configure trace base class
|
||||
if (!firstModel) {
|
||||
if (m_offload != configp->m_useOffloading) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Either all or no models using the same trace file must use offloading");
|
||||
}
|
||||
}
|
||||
m_offload = configp->m_useOffloading;
|
||||
// If at least one model requests parallel tracing, then use it
|
||||
m_parallel |= configp->m_useParallel;
|
||||
|
||||
if (VL_UNCOVERABLE(m_parallel && m_offload)) { // LCOV_EXCL_START
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use parallel tracing with offloading");
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
// Configure format specific sub class
|
||||
configure(*(configp.get()));
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
|
||||
CallbackRecord&& cbRec)
|
||||
VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
cbVec.push_back(cbRec);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_initCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_fullCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_fullOffloadCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_chgCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_chgOffloadCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE {
|
||||
addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {
|
||||
m_namePrefixStack.push_back(m_namePrefixStack.back() + prefix);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::popNamePrefix(unsigned count) {
|
||||
while (count--) m_namePrefixStack.pop_back();
|
||||
assert(!m_namePrefixStack.empty());
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Primitives converting binary values to strings...
|
||||
|
||||
// All of these take a destination pointer where the string will be emitted,
|
||||
// and a value to convert. There are a couple of variants for efficiency.
|
||||
|
||||
static inline void cvtCDataToStr(char* dstp, CData value) {
|
||||
#ifdef VL_HAVE_SSE2
|
||||
// Similar to cvtSDataToStr but only the bottom 8 byte lanes are used
|
||||
const __m128i a = _mm_cvtsi32_si128(value);
|
||||
const __m128i b = _mm_unpacklo_epi8(a, a);
|
||||
const __m128i c = _mm_shufflelo_epi16(b, 0);
|
||||
const __m128i m = _mm_set1_epi64x(0x0102040810204080);
|
||||
const __m128i d = _mm_cmpeq_epi8(_mm_and_si128(c, m), m);
|
||||
const __m128i result = _mm_sub_epi8(_mm_set1_epi8('0'), d);
|
||||
_mm_storel_epi64(reinterpret_cast<__m128i*>(dstp), result);
|
||||
#else
|
||||
dstp[0] = '0' | static_cast<char>((value >> 7) & 1);
|
||||
dstp[1] = '0' | static_cast<char>((value >> 6) & 1);
|
||||
dstp[2] = '0' | static_cast<char>((value >> 5) & 1);
|
||||
dstp[3] = '0' | static_cast<char>((value >> 4) & 1);
|
||||
dstp[4] = '0' | static_cast<char>((value >> 3) & 1);
|
||||
dstp[5] = '0' | static_cast<char>((value >> 2) & 1);
|
||||
dstp[6] = '0' | static_cast<char>((value >> 1) & 1);
|
||||
dstp[7] = '0' | static_cast<char>(value & 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtSDataToStr(char* dstp, SData value) {
|
||||
#ifdef VL_HAVE_SSE2
|
||||
// We want each bit in the 16-bit input value to end up in a byte lane
|
||||
// within the 128-bit XMM register. Note that x86 is little-endian and we
|
||||
// want the MSB of the input at the low address, so we will bit-reverse
|
||||
// at the same time.
|
||||
|
||||
// Put value in bottom of 128-bit register a[15:0] = value
|
||||
const __m128i a = _mm_cvtsi32_si128(value);
|
||||
// Interleave bytes with themselves
|
||||
// b[15: 0] = {2{a[ 7:0]}} == {2{value[ 7:0]}}
|
||||
// b[31:16] = {2{a[15:8]}} == {2{value[15:8]}}
|
||||
const __m128i b = _mm_unpacklo_epi8(a, a);
|
||||
// Shuffle bottom 64 bits, note swapping high bytes with low bytes
|
||||
// c[31: 0] = {2{b[31:16]}} == {4{value[15:8}}
|
||||
// c[63:32] = {2{b[15: 0]}} == {4{value[ 7:0}}
|
||||
const __m128i c = _mm_shufflelo_epi16(b, 0x05);
|
||||
// Shuffle whole register
|
||||
// d[ 63: 0] = {2{c[31: 0]}} == {8{value[15:8}}
|
||||
// d[126:54] = {2{c[63:32]}} == {8{value[ 7:0}}
|
||||
const __m128i d = _mm_shuffle_epi32(c, 0x50);
|
||||
// Test each bit within the bytes, this sets each byte lane to 0
|
||||
// if the bit for that lane is 0 and to 0xff if the bit is 1.
|
||||
const __m128i m = _mm_set1_epi64x(0x0102040810204080);
|
||||
const __m128i e = _mm_cmpeq_epi8(_mm_and_si128(d, m), m);
|
||||
// Convert to ASCII by subtracting the masks from ASCII '0':
|
||||
// '0' - 0 is '0', '0' - -1 is '1'
|
||||
const __m128i result = _mm_sub_epi8(_mm_set1_epi8('0'), e);
|
||||
// Store the 16 characters to the un-aligned buffer
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i*>(dstp), result);
|
||||
#else
|
||||
cvtCDataToStr(dstp, value >> 8);
|
||||
cvtCDataToStr(dstp + 8, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtIDataToStr(char* dstp, IData value) {
|
||||
#ifdef VL_HAVE_AVX2
|
||||
// Similar to cvtSDataToStr but the bottom 16-bits are processed in the
|
||||
// top half of the YMM registerss
|
||||
const __m256i a = _mm256_insert_epi32(_mm256_undefined_si256(), value, 0);
|
||||
const __m256i b = _mm256_permute4x64_epi64(a, 0);
|
||||
const __m256i s = _mm256_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3);
|
||||
const __m256i c = _mm256_shuffle_epi8(b, s);
|
||||
const __m256i m = _mm256_set1_epi64x(0x0102040810204080);
|
||||
const __m256i d = _mm256_cmpeq_epi8(_mm256_and_si256(c, m), m);
|
||||
const __m256i result = _mm256_sub_epi8(_mm256_set1_epi8('0'), d);
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp), result);
|
||||
#else
|
||||
cvtSDataToStr(dstp, value >> 16);
|
||||
cvtSDataToStr(dstp + 16, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void cvtQDataToStr(char* dstp, QData value) {
|
||||
cvtIDataToStr(dstp, value >> 32);
|
||||
cvtIDataToStr(dstp + 32, value);
|
||||
}
|
||||
|
||||
#define cvtEDataToStr cvtIDataToStr
|
||||
|
||||
//=========================================================================
|
||||
// VerilatedTraceBuffer
|
||||
|
||||
template <>
|
||||
VerilatedTraceBuffer<VL_BUF_T>::VerilatedTraceBuffer(Trace& owner)
|
||||
: VL_BUF_T{owner}
|
||||
, m_sigs_oldvalp{owner.m_sigs_oldvalp}
|
||||
, m_sigs_enabledp{owner.m_sigs_enabledp} {}
|
||||
|
||||
// These functions must write the new value back into the old value store,
|
||||
// and subsequently call the format specific emit* implementations. Note
|
||||
// that this file must be included in the format specific implementation, so
|
||||
// the emit* functions can be inlined for performance.
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
emitBit(code, newval);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
emitCData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
emitSData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
emitIData(code, newval, bits);
|
||||
}
|
||||
|
||||
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;
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
emitQData(code, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTraceBuffer<VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
|
||||
const uint32_t code = oldp - m_sigs_oldvalp;
|
||||
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
emitWData(code, newvalp, bits);
|
||||
}
|
||||
|
||||
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;
|
||||
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
emitDouble(code, newval);
|
||||
}
|
||||
|
||||
#ifdef VL_THREADED
|
||||
//=========================================================================
|
||||
// VerilatedTraceOffloadBuffer
|
||||
|
||||
template <>
|
||||
VerilatedTraceOffloadBuffer<VL_BUF_T>::VerilatedTraceOffloadBuffer(VL_SUB_T& owner)
|
||||
: VerilatedTraceBuffer<VL_BUF_T>{owner}
|
||||
, m_offloadBufferWritep{owner.m_offloadBufferWritep}
|
||||
, m_offloadBufferEndp{owner.m_offloadBufferEndp} {
|
||||
if (m_offloadBufferWritep) {
|
||||
using This = VerilatedTraceBuffer<VL_BUF_T>*;
|
||||
// Tack on the buffer address
|
||||
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
|
||||
"This should be enough on all plafrorms");
|
||||
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
|
||||
*reinterpret_cast<This*>(m_offloadBufferWritep) = static_cast<This>(this);
|
||||
m_offloadBufferWritep += 2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // VL_CPPCHECK
|
||||
+345
-51
@@ -29,6 +29,8 @@
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@@ -70,10 +72,76 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
|
||||
#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits
|
||||
#define VL_OUTW(name, msb, lsb, words) VlWide<words> name ///< Declare output signal, 65+ bits
|
||||
|
||||
//===================================================================
|
||||
// Activity trigger vector
|
||||
|
||||
template <std::size_t T_size> //
|
||||
class VlTriggerVec final {
|
||||
// TODO: static assert T_size > 0, and don't generate when empty
|
||||
private:
|
||||
// MEMBERS
|
||||
std::array<bool, T_size> m_flags; // State of the assoc array
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlTriggerVec() { clear(); }
|
||||
~VlTriggerVec() = default;
|
||||
|
||||
// METHODS
|
||||
|
||||
// Set all elements to false
|
||||
void clear() { m_flags.fill(false); }
|
||||
|
||||
// Reference to element at 'index'
|
||||
bool& at(size_t index) { return m_flags.at(index); }
|
||||
|
||||
// Return true iff at least one element is set
|
||||
bool any() const {
|
||||
for (size_t i = 0; i < m_flags.size(); ++i)
|
||||
if (m_flags[i]) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set all elements true in 'this' that are set in 'other'
|
||||
void set(const VlTriggerVec<T_size>& other) {
|
||||
for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] |= other.m_flags[i];
|
||||
}
|
||||
|
||||
// Set elements of 'this' to 'a & !b' element-wise
|
||||
void andNot(const VlTriggerVec<T_size>& a, const VlTriggerVec<T_size>& b) {
|
||||
for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] = a.m_flags[i] && !b.m_flags[i];
|
||||
}
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// SystemVerilog event type
|
||||
|
||||
class VlEvent final {
|
||||
// MEMBERS
|
||||
bool m_fired = false; // Fired on this scheduling iteration
|
||||
bool m_triggered = false; // Triggered state of event persisting until next time step
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlEvent() = default;
|
||||
~VlEvent() = default;
|
||||
|
||||
// METHODS
|
||||
void fire() { m_fired = m_triggered = true; }
|
||||
bool isFired() const { return m_fired; }
|
||||
bool isTriggered() const { return m_triggered; }
|
||||
void clearFired() { m_fired = false; }
|
||||
void clearTriggered() { m_triggered = false; }
|
||||
};
|
||||
|
||||
inline std::string VL_TO_STRING(const VlEvent& e) {
|
||||
return std::string("triggered=") + (e.isTriggered() ? "true" : "false");
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Shuffle RNG
|
||||
|
||||
extern vluint64_t vl_rand64() VL_MT_SAFE;
|
||||
extern uint64_t vl_rand64() VL_MT_SAFE;
|
||||
|
||||
class VlURNG final {
|
||||
public:
|
||||
@@ -91,9 +159,9 @@ class VlReadMem final {
|
||||
const int m_bits; // Bit width of values
|
||||
const std::string& m_filename; // Filename
|
||||
const QData m_end; // End address (as specified by user)
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to read
|
||||
int m_linenum; // Line number last read from file
|
||||
FILE* m_fp = nullptr; // File handle for filename
|
||||
QData m_addr = 0; // Next address to read
|
||||
int m_linenum = 0; // Line number last read from file
|
||||
bool m_anyAddr = false; // Had address directive in the file
|
||||
public:
|
||||
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
@@ -107,8 +175,8 @@ public:
|
||||
class VlWriteMem final {
|
||||
const bool m_hex; // Hex format
|
||||
const int m_bits; // Bit width of values
|
||||
FILE* m_fp; // File handle for filename
|
||||
QData m_addr; // Next address to write
|
||||
FILE* m_fp = nullptr; // File handle for filename
|
||||
QData m_addr = 0; // Next address to write
|
||||
public:
|
||||
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
|
||||
~VlWriteMem();
|
||||
@@ -131,7 +199,8 @@ public:
|
||||
|
||||
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE;
|
||||
|
||||
template <std::size_t T_Words> struct VlWide final {
|
||||
template <std::size_t T_Words>
|
||||
struct VlWide final {
|
||||
// MEMBERS
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
EData m_storage[T_Words]; // Contents of the packed array
|
||||
@@ -145,6 +214,12 @@ template <std::size_t T_Words> struct VlWide final {
|
||||
// Default copy assignment operators are used.
|
||||
operator WDataOutP() { return &m_storage[0]; } // This also allows []
|
||||
operator WDataInP() const { return &m_storage[0]; } // This also allows []
|
||||
bool operator!=(const VlWide<T_Words>& that) const {
|
||||
for (size_t i = 0; i < T_Words; ++i) {
|
||||
if (m_storage[i] != that.m_storage[i]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// METHODS
|
||||
const EData& at(size_t index) const { return m_storage[index]; }
|
||||
@@ -163,7 +238,8 @@ VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
|
||||
return *((VlWide<T_Words>*)inp);
|
||||
}
|
||||
|
||||
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
|
||||
template <std::size_t T_Words>
|
||||
std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
|
||||
return VL_TO_STRING_W(T_Words, obj.data());
|
||||
}
|
||||
|
||||
@@ -174,7 +250,8 @@ template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& o
|
||||
//
|
||||
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
|
||||
// For dynamic arrays it is always zero
|
||||
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
|
||||
template <class T_Value, size_t T_MaxSize = 0>
|
||||
class VlQueue final {
|
||||
private:
|
||||
// TYPES
|
||||
using Deque = std::deque<T_Value>;
|
||||
@@ -199,7 +276,8 @@ public:
|
||||
|
||||
// Standard copy constructor works. Verilog: assoca = assocb
|
||||
// Also must allow conversion from a different T_MaxSize queue
|
||||
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
|
||||
template <size_t U_MaxSize = 0>
|
||||
VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
|
||||
m_deque = rhs.privateDeque();
|
||||
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
|
||||
return *this;
|
||||
@@ -241,7 +319,7 @@ public:
|
||||
int size() const { return m_deque.size(); }
|
||||
// Clear array. Verilog: function void delete([input index])
|
||||
void clear() { m_deque.clear(); }
|
||||
void erase(vlsint32_t index) {
|
||||
void erase(int32_t index) {
|
||||
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
|
||||
m_deque.erase(m_deque.begin() + index);
|
||||
}
|
||||
@@ -288,8 +366,8 @@ public:
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(vlsint32_t index) {
|
||||
static T_Value s_throwAway;
|
||||
T_Value& at(int32_t index) {
|
||||
static VL_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())) {
|
||||
s_throwAway = atDefault();
|
||||
@@ -299,8 +377,8 @@ public:
|
||||
}
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(vlsint32_t index) const {
|
||||
static T_Value s_throwAway;
|
||||
const T_Value& at(int32_t index) const {
|
||||
static VL_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();
|
||||
@@ -309,18 +387,18 @@ public:
|
||||
}
|
||||
}
|
||||
// function void q.insert(index, value);
|
||||
void insert(vlsint32_t index, const T_Value& value) {
|
||||
void insert(int32_t index, const T_Value& value) {
|
||||
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
|
||||
m_deque.insert(m_deque.begin() + index, value);
|
||||
}
|
||||
|
||||
// Return slice q[lsb:msb]
|
||||
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
|
||||
VlQueue slice(int32_t lsb, int32_t msb) const {
|
||||
VlQueue out;
|
||||
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
|
||||
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
|
||||
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
|
||||
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
|
||||
for (int32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -330,7 +408,8 @@ public:
|
||||
|
||||
// Methods
|
||||
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
|
||||
template <typename Func> void sort(Func with_func) {
|
||||
template <typename Func>
|
||||
void sort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaninless with sort, as it changes
|
||||
@@ -338,7 +417,8 @@ public:
|
||||
});
|
||||
}
|
||||
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
|
||||
template <typename Func> void rsort(Func with_func) {
|
||||
template <typename Func>
|
||||
void rsort(Func with_func) {
|
||||
// with_func returns arbitrary type to use for the sort comparison
|
||||
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
|
||||
// index number is meaninless with sort, as it changes
|
||||
@@ -373,7 +453,8 @@ public:
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue find(Func with_func) const {
|
||||
VlQueue out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
@@ -382,7 +463,8 @@ public:
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue<IData> find_index(Func with_func) const {
|
||||
VlQueue<IData> out;
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
@@ -391,7 +473,8 @@ public:
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue find_first(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue find_first(Func with_func) const {
|
||||
// Can't use std::find_if as need index number
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
@@ -400,7 +483,8 @@ public:
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue<IData> find_first_index(Func with_func) const {
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) {
|
||||
if (with_func(index, i)) return VlQueue<IData>::cons(index);
|
||||
@@ -408,18 +492,20 @@ public:
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
}
|
||||
template <typename Func> VlQueue find_last(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue find_last(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
|
||||
if (with_func(index, *it)) return VlQueue::cons(*it);
|
||||
for (auto& item : vlstd::reverse_view(m_deque)) {
|
||||
if (with_func(index, item)) return VlQueue::cons(item);
|
||||
--index;
|
||||
}
|
||||
return VlQueue{};
|
||||
}
|
||||
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue<IData> find_last_index(Func with_func) const {
|
||||
IData index = m_deque.size() - 1;
|
||||
for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
|
||||
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
|
||||
for (auto& item : vlstd::reverse_view(m_deque)) {
|
||||
if (with_func(index, item)) return VlQueue<IData>::cons(index);
|
||||
--index;
|
||||
}
|
||||
return VlQueue<IData>{};
|
||||
@@ -442,7 +528,8 @@ public:
|
||||
for (const auto& i : m_deque) out += i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
template <typename Func>
|
||||
T_Value r_sum(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out += with_func(index++, i);
|
||||
@@ -456,7 +543,8 @@ public:
|
||||
for (; it != m_deque.end(); ++it) out *= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
template <typename Func>
|
||||
T_Value r_product(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
IData index = 0;
|
||||
@@ -474,7 +562,8 @@ public:
|
||||
for (; it != m_deque.end(); ++it) out &= *it;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
template <typename Func>
|
||||
T_Value r_and(Func with_func) const {
|
||||
if (m_deque.empty()) return T_Value(0);
|
||||
auto it = m_deque.begin();
|
||||
IData index = 0;
|
||||
@@ -489,7 +578,8 @@ public:
|
||||
for (const auto& i : m_deque) out |= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
template <typename Func>
|
||||
T_Value r_or(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out |= with_func(index++, i);
|
||||
@@ -500,7 +590,8 @@ public:
|
||||
for (const auto& i : m_deque) out ^= i;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
template <typename Func>
|
||||
T_Value r_xor(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
IData index = 0;
|
||||
for (const auto& i : m_deque) out ^= with_func(index++, i);
|
||||
@@ -520,7 +611,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
template <class T_Value>
|
||||
std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
@@ -529,7 +621,8 @@ template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
//
|
||||
template <class T_Key, class T_Value> class VlAssocArray final {
|
||||
template <class T_Key, class T_Value>
|
||||
class VlAssocArray final {
|
||||
private:
|
||||
// TYPES
|
||||
using Map = std::map<T_Key, T_Value>;
|
||||
@@ -654,19 +747,22 @@ public:
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find(Func with_func) const {
|
||||
VlQueue<T_Value> out;
|
||||
for (const auto& i : m_map)
|
||||
if (with_func(i.first, i.second)) out.push_back(i.second);
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_index(Func with_func) const {
|
||||
VlQueue<T_Key> out;
|
||||
for (const auto& i : m_map)
|
||||
if (with_func(i.first, i.second)) out.push_back(i.first);
|
||||
return out;
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find_first(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
@@ -674,7 +770,8 @@ public:
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_first_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
@@ -682,7 +779,8 @@ public:
|
||||
if (it == m_map.end()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Key>::cons(it->first);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue<T_Value> find_last(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
@@ -690,7 +788,8 @@ public:
|
||||
if (it == m_map.rend()) return VlQueue<T_Value>{};
|
||||
return VlQueue<T_Value>::cons(it->second);
|
||||
}
|
||||
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
|
||||
template <typename Func>
|
||||
VlQueue<T_Key> find_last_index(Func with_func) const {
|
||||
const auto it
|
||||
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
|
||||
return with_func(i.first, i.second);
|
||||
@@ -724,7 +823,8 @@ public:
|
||||
for (const auto& i : m_map) out += i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_sum(Func with_func) const {
|
||||
template <typename Func>
|
||||
T_Value r_sum(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out += with_func(i.first, i.second);
|
||||
return out;
|
||||
@@ -737,7 +837,8 @@ public:
|
||||
for (; it != m_map.end(); ++it) out *= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_product(Func with_func) const {
|
||||
template <typename Func>
|
||||
T_Value r_product(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
@@ -753,7 +854,8 @@ public:
|
||||
for (; it != m_map.end(); ++it) out &= it->second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_and(Func with_func) const {
|
||||
template <typename Func>
|
||||
T_Value r_and(Func with_func) const {
|
||||
if (m_map.empty()) return T_Value(0);
|
||||
auto it = m_map.begin();
|
||||
T_Value out{with_func(it->first, it->second)};
|
||||
@@ -766,7 +868,8 @@ public:
|
||||
for (const auto& i : m_map) out |= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_or(Func with_func) const {
|
||||
template <typename Func>
|
||||
T_Value r_or(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out |= with_func(i.first, i.second);
|
||||
return out;
|
||||
@@ -776,7 +879,8 @@ public:
|
||||
for (const auto& i : m_map) out ^= i.second;
|
||||
return out;
|
||||
}
|
||||
template <typename Func> T_Value r_xor(Func with_func) const {
|
||||
template <typename Func>
|
||||
T_Value r_xor(Func with_func) const {
|
||||
T_Value out(0); // Type must have assignment operator
|
||||
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
|
||||
return out;
|
||||
@@ -839,7 +943,8 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
|
||||
/// This class may get exposed to a Verilated Model's top I/O, if the top
|
||||
/// IO has an unpacked array.
|
||||
|
||||
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
|
||||
template <class T_Value, std::size_t T_Depth>
|
||||
struct VlUnpacked final {
|
||||
// MEMBERS
|
||||
// This should be the only data member, otherwise generated static initializers need updating
|
||||
T_Value m_storage[T_Depth]; // Contents of the unpacked array
|
||||
@@ -860,6 +965,12 @@ template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
|
||||
T_Value& operator[](size_t index) { return m_storage[index]; }
|
||||
const T_Value& operator[](size_t index) const { return m_storage[index]; }
|
||||
|
||||
// *this != that, which might be used for change detection/trigger computation, but avoid
|
||||
// operator overloading in VlUnpacked for safety in other contexts.
|
||||
bool neq(const VlUnpacked<T_Value, T_Depth>& that) const { return neq(*this, that); }
|
||||
// Similar to 'neq' above, *this = that used for change detection
|
||||
void assign(const VlUnpacked<T_Value, T_Depth>& that) { *this = that; }
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
std::string out = "'{";
|
||||
@@ -870,6 +981,22 @@ template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
|
||||
}
|
||||
return out + "} ";
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T_Val, std::size_t T_Dep>
|
||||
static bool neq(const VlUnpacked<T_Val, T_Dep>& a, const VlUnpacked<T_Val, T_Dep>& b) {
|
||||
for (size_t i = 0; i < T_Dep; ++i) {
|
||||
// Recursive 'neq', in case T_Val is also a VlUnpacked<_, _>
|
||||
if (neq(a.m_storage[i], b.m_storage[i])) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T_Other> //
|
||||
static bool neq(const T_Other& a, const T_Other& b) {
|
||||
// Base case (T_Other is not VlUnpacked<_, _>), fall back on !=
|
||||
return a != b;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value, std::size_t T_Depth>
|
||||
@@ -877,12 +1004,179 @@ std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
class VlClass; // See below
|
||||
|
||||
//===================================================================
|
||||
// Class providing delayed deletion of garbage objects. Objects get deleted only when 'deleteAll()'
|
||||
// is called, or the deleter itself is destroyed.
|
||||
|
||||
class VlDeleter final {
|
||||
// MEMBERS
|
||||
// Queue of new objects that should be deleted
|
||||
std::vector<VlClass*> m_newGarbage VL_GUARDED_BY(m_mutex);
|
||||
// Queue of objects currently being deleted (only for deleteAll())
|
||||
std::vector<VlClass*> m_toDelete VL_GUARDED_BY(m_deleteMutex);
|
||||
mutable VerilatedMutex m_mutex; // Mutex protecting the 'new garbage' queue
|
||||
mutable VerilatedMutex m_deleteMutex; // Mutex protecting the delete queue
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlDeleter() = default;
|
||||
~VlDeleter() { deleteAll(); }
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(VlDeleter);
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
// Adds a new object to the 'new garbage' queue.
|
||||
void put(VlClass* const objp) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_newGarbage.push_back(objp);
|
||||
}
|
||||
|
||||
// Deletes all queued garbage objects.
|
||||
void deleteAll() VL_MT_SAFE;
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Base class for all verilated classes. Includes a reference counter, and a pointer to the deleter
|
||||
// object that should destroy it after the counter reaches 0. This allows for easy construction of
|
||||
// VlClassRefs from 'this'. Also declares a virtual constructor, so that the object can be deleted
|
||||
// using a base pointer.
|
||||
|
||||
class VlClass VL_NOT_FINAL {
|
||||
// TYPES
|
||||
template <typename T_Class>
|
||||
friend class VlClassRef; // Needed for access to the ref counter and deleter
|
||||
|
||||
// MEMBERS
|
||||
std::atomic<size_t> m_counter{0}; // Reference count for this object
|
||||
VlDeleter* m_deleter = nullptr; // The deleter that will delete this object
|
||||
|
||||
// METHODS
|
||||
// Atomically increments the reference counter
|
||||
void refCountInc() VL_MT_SAFE { ++m_counter; }
|
||||
// Atomically decrements the reference counter. Assuming VlClassRef semantics are sound, it
|
||||
// should never get called at m_counter == 0.
|
||||
void refCountDec() VL_MT_SAFE {
|
||||
if (!--m_counter) m_deleter->put(this);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlClass() = default;
|
||||
VlClass(const VlClass& copied) {}
|
||||
virtual ~VlClass() {}
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Represents the null pointer. Used for setting VlClassRef to null instead of
|
||||
// via nullptr_t, to prevent the implicit conversion of 0 to nullptr.
|
||||
struct VlNull {
|
||||
operator bool() const { return false; }
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Verilog class reference container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
|
||||
#define VlClassRef std::shared_ptr
|
||||
template <typename T_Class>
|
||||
class VlClassRef final {
|
||||
private:
|
||||
// TYPES
|
||||
template <typename T_OtherClass>
|
||||
friend class VlClassRef; // Needed for template copy/move assignments
|
||||
|
||||
// MEMBERS
|
||||
T_Class* m_objp = nullptr; // Object pointed to
|
||||
|
||||
// METHODS
|
||||
// Increase reference counter with null check
|
||||
void refCountInc() const VL_MT_SAFE {
|
||||
if (m_objp) m_objp->refCountInc();
|
||||
}
|
||||
// Decrease reference counter with null check
|
||||
void refCountDec() const VL_MT_SAFE {
|
||||
if (m_objp) m_objp->refCountDec();
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlClassRef() = default;
|
||||
// Init with nullptr
|
||||
VlClassRef(VlNull){};
|
||||
template <typename... T_Args>
|
||||
VlClassRef(VlDeleter& deleter, T_Args&&... args)
|
||||
: m_objp{new T_Class{std::forward<T_Args>(args)...}} {
|
||||
m_objp->m_deleter = &deleter;
|
||||
refCountInc();
|
||||
}
|
||||
// Explicit to avoid implicit conversion from 0
|
||||
explicit VlClassRef(T_Class* objp)
|
||||
: m_objp{objp} {
|
||||
refCountInc();
|
||||
}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlClassRef(const VlClassRef& copied)
|
||||
: m_objp{copied.m_objp} {
|
||||
refCountInc();
|
||||
}
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
VlClassRef(VlClassRef&& moved)
|
||||
: m_objp{vlstd::exchange(moved.m_objp, nullptr)} {}
|
||||
~VlClassRef() { refCountDec(); }
|
||||
|
||||
// METHODS
|
||||
// Copy and move assignments
|
||||
VlClassRef& operator=(const VlClassRef& copied) {
|
||||
refCountDec();
|
||||
m_objp = copied.m_objp;
|
||||
refCountInc();
|
||||
return *this;
|
||||
}
|
||||
VlClassRef& operator=(VlClassRef&& moved) {
|
||||
refCountDec();
|
||||
m_objp = vlstd::exchange(moved.m_objp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
template <typename T_OtherClass>
|
||||
VlClassRef& operator=(const VlClassRef<T_OtherClass>& copied) {
|
||||
refCountDec();
|
||||
m_objp = copied.m_objp;
|
||||
refCountInc();
|
||||
return *this;
|
||||
}
|
||||
template <typename T_OtherClass>
|
||||
VlClassRef& operator=(VlClassRef<T_OtherClass>&& moved) {
|
||||
refCountDec();
|
||||
m_objp = vlstd::exchange(moved.m_objp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
// Assign with nullptr
|
||||
VlClassRef& operator=(VlNull) {
|
||||
refCountDec();
|
||||
m_objp = nullptr;
|
||||
return *this;
|
||||
}
|
||||
// Dynamic caster
|
||||
template <typename T_OtherClass>
|
||||
VlClassRef<T_OtherClass> dynamicCast() const {
|
||||
return VlClassRef<T_OtherClass>{dynamic_cast<T_OtherClass*>(m_objp)};
|
||||
}
|
||||
// Dereference operators
|
||||
T_Class& operator*() const { return *m_objp; }
|
||||
T_Class* operator->() const { return m_objp; }
|
||||
// For 'if (ptr)...'
|
||||
operator bool() const { return m_objp; }
|
||||
};
|
||||
|
||||
#define VL_NEW(Class, ...) \
|
||||
VlClassRef<Class> { vlSymsp->__Vm_deleter, __VA_ARGS__ }
|
||||
|
||||
#define VL_KEEP_THIS \
|
||||
VlClassRef<std::remove_pointer<decltype(this)>::type> __Vthisref { this }
|
||||
|
||||
template <class T> // T typically of type VlClassRef<x>
|
||||
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
|
||||
@@ -892,7 +1186,7 @@ inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
|
||||
|
||||
template <typename T, typename U>
|
||||
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
|
||||
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
|
||||
VlClassRef<U> casted = in.template dynamicCast<U>();
|
||||
if (VL_LIKELY(casted)) {
|
||||
outr = casted;
|
||||
return true;
|
||||
|
||||
+180
-357
@@ -38,13 +38,13 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef O_LARGEFILE // For example on WIN32
|
||||
#ifndef O_LARGEFILE // WIN32 headers omit this
|
||||
# define O_LARGEFILE 0
|
||||
#endif
|
||||
#ifndef O_NONBLOCK
|
||||
#ifndef O_NONBLOCK // WIN32 headers omit this
|
||||
# define O_NONBLOCK 0
|
||||
#endif
|
||||
#ifndef O_CLOEXEC
|
||||
#ifndef O_CLOEXEC // WIN32 headers omit this
|
||||
# define O_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
@@ -62,12 +62,24 @@ 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
|
||||
|
||||
#define VL_DERIVED_T VerilatedVcd
|
||||
#include "verilated_trace_imp.cpp"
|
||||
#undef VL_DERIVED_T
|
||||
#define VL_SUB_T VerilatedVcd
|
||||
#define VL_BUF_T VerilatedVcdBuffer
|
||||
#include "verilated_trace_imp.h"
|
||||
#undef VL_SUB_T
|
||||
#undef VL_BUF_T
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
@@ -108,13 +120,13 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// Set member variables
|
||||
m_filename = filename; // "" is ok, as someone may overload open
|
||||
|
||||
openNextImp(m_rolloverMB != 0);
|
||||
openNextImp(m_rolloverSize != 0);
|
||||
if (!isOpen()) return;
|
||||
|
||||
dumpHeader();
|
||||
|
||||
// When using rollover, the first chunk contains the header only.
|
||||
if (m_rolloverMB) openNextImp(true);
|
||||
if (m_rolloverSize) openNextImp(true);
|
||||
}
|
||||
|
||||
void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
@@ -168,11 +180,11 @@ void VerilatedVcd::openNextImp(bool incFilename) {
|
||||
}
|
||||
|
||||
bool VerilatedVcd::preChangeDump() {
|
||||
if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) openNextImp(true);
|
||||
if (VL_UNLIKELY(m_rolloverSize && m_wroteBytes > m_rolloverSize)) openNextImp(true);
|
||||
return isOpen();
|
||||
}
|
||||
|
||||
void VerilatedVcd::emitTimeChange(vluint64_t timeui) {
|
||||
void VerilatedVcd::emitTimeChange(uint64_t timeui) {
|
||||
printStr("#");
|
||||
printQuad(timeui);
|
||||
printStr("\n");
|
||||
@@ -183,7 +195,7 @@ void VerilatedVcd::makeNameMap() {
|
||||
deleteNameMap();
|
||||
m_namemapp = new NameMap;
|
||||
|
||||
VerilatedTrace<VerilatedVcd>::traceInit();
|
||||
Super::traceInit();
|
||||
|
||||
// Though not speced, it's illegal to generate a vcd with signals
|
||||
// not under any module - it crashes at least two viewers.
|
||||
@@ -218,13 +230,19 @@ VerilatedVcd::~VerilatedVcd() {
|
||||
if (m_wrBufp) VL_DO_CLEAR(delete[] m_wrBufp, m_wrBufp = nullptr);
|
||||
deleteNameMap();
|
||||
if (m_filep && m_fileNewed) VL_DO_CLEAR(delete m_filep, m_filep = nullptr);
|
||||
#ifdef VL_THREADED
|
||||
if (parallel()) {
|
||||
assert(m_numBuffers == m_freeBuffers.size());
|
||||
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VerilatedVcd::closePrev() {
|
||||
// This function is on the flush() call path
|
||||
if (!isOpen()) return;
|
||||
|
||||
VerilatedTrace<VerilatedVcd>::flushBase();
|
||||
Super::flushBase();
|
||||
bufferFlush();
|
||||
m_isOpen = false;
|
||||
m_filep->close();
|
||||
@@ -245,20 +263,15 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
// This function is on the flush() call path
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
if (!isOpen()) return;
|
||||
if (m_evcd) {
|
||||
printStr("$vcdclose ");
|
||||
printQuad(timeLastDump());
|
||||
printStr(" $end\n");
|
||||
}
|
||||
closePrev();
|
||||
// closePrev() called VerilatedTrace<VerilatedVcd>::flush(), so we just
|
||||
// closePrev() called Super::flush(), so we just
|
||||
// need to shut down the tracing thread here.
|
||||
VerilatedTrace<VerilatedVcd>::closeBase();
|
||||
Super::closeBase();
|
||||
}
|
||||
|
||||
void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
VerilatedTrace<VerilatedVcd>::flushBase();
|
||||
Super::flushBase();
|
||||
bufferFlush();
|
||||
}
|
||||
|
||||
@@ -270,19 +283,19 @@ void VerilatedVcd::printStr(const char* str) {
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatedVcd::printQuad(vluint64_t n) {
|
||||
void VerilatedVcd::printQuad(uint64_t n) {
|
||||
constexpr size_t LEN_STR_QUAD = 40;
|
||||
char buf[LEN_STR_QUAD];
|
||||
VL_SNPRINTF(buf, LEN_STR_QUAD, "%" PRIu64, n);
|
||||
printStr(buf);
|
||||
}
|
||||
|
||||
void VerilatedVcd::bufferResize(vluint64_t minsize) {
|
||||
void VerilatedVcd::bufferResize(size_t minsize) {
|
||||
// minsize is size of largest write. We buffer at least 8 times as much data,
|
||||
// writing when we are 3/4 full (with thus 2*minsize remaining free)
|
||||
if (VL_UNLIKELY(minsize > m_wrChunkSize)) {
|
||||
const char* oldbufp = m_wrBufp;
|
||||
m_wrChunkSize = minsize * 2;
|
||||
m_wrChunkSize = roundUpToMultipleOf<1024>(minsize * 2);
|
||||
m_wrBufp = new char[m_wrChunkSize * 8];
|
||||
std::memcpy(m_wrBufp, oldbufp, m_writep - oldbufp);
|
||||
m_writep = m_wrBufp + (m_writep - oldbufp);
|
||||
@@ -292,7 +305,7 @@ void VerilatedVcd::bufferResize(vluint64_t minsize) {
|
||||
}
|
||||
|
||||
void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
// This function can be called from the trace thread
|
||||
// This function can be called from the trace offload thread
|
||||
// This function is on the flush() call path
|
||||
// We add output data to m_writep.
|
||||
// When it gets nearly full we dump it using this routine which calls write()
|
||||
@@ -328,7 +341,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
|
||||
//=============================================================================
|
||||
// VCD string code
|
||||
|
||||
char* VerilatedVcd::writeCode(char* writep, vluint32_t code) {
|
||||
char* VerilatedVcd::writeCode(char* writep, uint32_t code) {
|
||||
*writep++ = static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
while (code) {
|
||||
@@ -459,18 +472,22 @@ void VerilatedVcd::dumpHeader() {
|
||||
deleteNameMap();
|
||||
}
|
||||
|
||||
void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, bool array,
|
||||
void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, bool array,
|
||||
int arraynum, bool tri, bool bussed, int msb, int lsb) {
|
||||
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
|
||||
|
||||
VerilatedTrace<VerilatedVcd>::declCode(code, bits, tri);
|
||||
const bool enabled = Super::declCode(code, name, bits, tri);
|
||||
|
||||
if (m_suffixes.size() <= nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE) {
|
||||
m_suffixes.resize(nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE * 2, 0);
|
||||
}
|
||||
|
||||
// Make sure write buffer is large enough (one character per bit), plus header
|
||||
bufferResize(bits + 1024);
|
||||
// Keep upper bound on bytes a single signal cna emit into the buffer
|
||||
m_maxSignalBytes = std::max<size_t>(m_maxSignalBytes, bits + 32);
|
||||
// Make sure write buffer is large enough, plus header
|
||||
bufferResize(m_maxSignalBytes + 1024);
|
||||
|
||||
if (!enabled) return;
|
||||
|
||||
// Split name into basename
|
||||
// Spaces and tabs aren't legal in VCD signal names, so:
|
||||
@@ -495,38 +512,29 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
||||
|
||||
// Print reference
|
||||
std::string decl = "$var ";
|
||||
if (m_evcd) {
|
||||
decl += "port";
|
||||
} else {
|
||||
decl += wirep; // usually "wire"
|
||||
}
|
||||
decl += wirep; // usually "wire"
|
||||
|
||||
constexpr size_t bufsize = 1000;
|
||||
char buf[bufsize];
|
||||
VL_SNPRINTF(buf, bufsize, " %2d ", bits);
|
||||
decl += buf;
|
||||
if (m_evcd) {
|
||||
VL_SNPRINTF(buf, bufsize, "<%u", code);
|
||||
decl += buf;
|
||||
} else {
|
||||
// Add string code to decl
|
||||
char* const endp = writeCode(buf, code);
|
||||
*endp = '\0';
|
||||
decl += buf;
|
||||
// Build suffix array entry
|
||||
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
|
||||
const size_t length = endp - buf;
|
||||
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
|
||||
// 1 bit values don't have a ' ' separator between value and string code
|
||||
const bool isBit = bits == 1;
|
||||
entryp[0] = ' '; // Separator
|
||||
// Use memcpy as we checked size above, and strcpy is flagged unsafe
|
||||
std::memcpy(entryp + !isBit, buf,
|
||||
std::strlen(buf)); // Code (overwrite separator if isBit)
|
||||
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
|
||||
// Set length of suffix (used to increment write pointer)
|
||||
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
|
||||
}
|
||||
// Add string code to decl
|
||||
char* const endp = writeCode(buf, code);
|
||||
*endp = '\0';
|
||||
decl += buf;
|
||||
// Build suffix array entry
|
||||
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
|
||||
const size_t length = endp - buf;
|
||||
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
|
||||
// 1 bit values don't have a ' ' separator between value and string code
|
||||
const bool isBit = bits == 1;
|
||||
entryp[0] = ' '; // Separator
|
||||
// Use memcpy as we checked size above, and strcpy is flagged unsafe
|
||||
std::memcpy(entryp + !isBit, buf,
|
||||
std::strlen(buf)); // Code (overwrite separator if isBit)
|
||||
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
|
||||
// Set length of suffix (used to increment write pointer)
|
||||
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
|
||||
decl += " ";
|
||||
decl += basename;
|
||||
if (array) {
|
||||
@@ -542,56 +550,99 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
|
||||
m_namemapp->emplace(hiername, decl);
|
||||
}
|
||||
|
||||
void VerilatedVcd::declBit(vluint32_t code, const char* name, bool array, int arraynum) {
|
||||
void VerilatedVcd::declBit(uint32_t code, const char* name, bool array, int arraynum) {
|
||||
declare(code, name, "wire", array, arraynum, false, false, 0, 0);
|
||||
}
|
||||
void VerilatedVcd::declBus(vluint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
void VerilatedVcd::declBus(uint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, false, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
void VerilatedVcd::declQuad(uint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, false, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declArray(vluint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
void VerilatedVcd::declArray(uint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, false, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declDouble(vluint32_t code, const char* name, bool array, int arraynum) {
|
||||
void VerilatedVcd::declDouble(uint32_t code, const char* name, bool array, int arraynum) {
|
||||
declare(code, name, "real", array, arraynum, false, false, 63, 0);
|
||||
}
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
void VerilatedVcd::declTriBit(vluint32_t code, const char* name, bool array, int arraynum) {
|
||||
declare(code, name, "wire", array, arraynum, true, false, 0, 0);
|
||||
}
|
||||
void VerilatedVcd::declTriBus(vluint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declTriQuad(vluint32_t code, const char* name, bool array, int arraynum,
|
||||
int msb, int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
|
||||
}
|
||||
void VerilatedVcd::declTriArray(vluint32_t code, const char* name, bool array, int arraynum,
|
||||
int msb, int lsb) {
|
||||
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
|
||||
}
|
||||
#endif // VL_TRACE_VCD_OLD_API
|
||||
|
||||
//=============================================================================
|
||||
// Trace rendering prinitives
|
||||
// Get/commit trace buffer
|
||||
|
||||
static inline void
|
||||
VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
|
||||
VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer() {
|
||||
VerilatedVcd::Buffer* const bufp = new Buffer{*this};
|
||||
#ifdef VL_THREADED
|
||||
if (parallel()) {
|
||||
// Note: This is called from VeriltedVcd::dump, which already holds the lock
|
||||
// If no buffer available, allocate a new one
|
||||
if (m_freeBuffers.empty()) {
|
||||
constexpr size_t pageSize = 4096;
|
||||
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
|
||||
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
|
||||
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
|
||||
++m_numBuffers;
|
||||
}
|
||||
// Grab a buffer
|
||||
const auto pair = m_freeBuffers.back();
|
||||
m_freeBuffers.pop_back();
|
||||
// Initialize
|
||||
bufp->m_writep = bufp->m_bufp = pair.first;
|
||||
bufp->m_size = pair.second;
|
||||
bufp->adjustGrowp();
|
||||
}
|
||||
#endif
|
||||
// Return the buffer
|
||||
return bufp;
|
||||
}
|
||||
|
||||
static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
|
||||
void VerilatedVcd::commitTraceBuffer(VerilatedVcd::Buffer* bufp) {
|
||||
if (parallel()) {
|
||||
#if VL_THREADED
|
||||
// Note: This is called from VeriltedVcd::dump, which already holds the lock
|
||||
// Resize output buffer. Note, we use the full size of the trace buffer, as
|
||||
// this is a lot more stable than the actual occupancy of the trace buffer.
|
||||
// This helps us to avoid re-allocations due to small size changes.
|
||||
bufferResize(bufp->m_size);
|
||||
// Compute occupancy of buffer
|
||||
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
|
||||
// Copy to output buffer
|
||||
std::memcpy(m_writep, bufp->m_bufp, usedSize);
|
||||
// Adjust write pointer
|
||||
m_writep += usedSize;
|
||||
// Flush if necessary
|
||||
bufferCheck();
|
||||
// Put buffer back on free list
|
||||
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
|
||||
#else
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
} else {
|
||||
// Needs adjusting for emitTimeChange
|
||||
m_writep = bufp->m_writep;
|
||||
}
|
||||
delete bufp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdBuffer implementation
|
||||
|
||||
//=============================================================================
|
||||
// Trace rendering primitives
|
||||
|
||||
static void VerilatedVcdCCopyAndAppendNewLine(char* writep,
|
||||
const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
|
||||
|
||||
static void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
|
||||
// Copy the whole suffix (this avoid having hard to predict branches which
|
||||
// helps a lot). Note: The maximum length of the suffix is
|
||||
// VL_TRACE_MAX_VCD_CODE_SIZE + 2 == 7, but we unroll this here for speed.
|
||||
#ifdef VL_X86_64
|
||||
// Copy the whole 8 bytes in one go, this works on little-endian machines
|
||||
// supporting unaligned stores.
|
||||
*reinterpret_cast<vluint64_t*>(writep) = *reinterpret_cast<const vluint64_t*>(suffixp);
|
||||
*reinterpret_cast<uint64_t*>(writep) = *reinterpret_cast<const uint64_t*>(suffixp);
|
||||
#else
|
||||
// Portable variant
|
||||
writep[0] = suffixp[0];
|
||||
@@ -604,25 +655,59 @@ static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* s
|
||||
#endif
|
||||
}
|
||||
|
||||
void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
|
||||
const char* const suffixp = m_suffixes.data() + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
|
||||
void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) {
|
||||
const char* const suffixp = m_suffixes + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
|
||||
VL_DEBUG_IFDEF(assert(suffixp[0]););
|
||||
VerilatedVcdCCopyAndAppendNewLine(writep, suffixp);
|
||||
|
||||
// Now write back the write pointer incremented by the actual size of the
|
||||
// suffix, which was stored in the last byte of the suffix buffer entry.
|
||||
m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
|
||||
bufferCheck();
|
||||
|
||||
if (m_owner.parallel()) {
|
||||
#ifdef VL_THREADED
|
||||
// Double the size of the buffer if necessary
|
||||
if (VL_UNLIKELY(m_writep >= m_growp)) {
|
||||
// Compute occupied size of current buffer
|
||||
const size_t usedSize = m_writep - m_bufp;
|
||||
// We are always doubling the size
|
||||
m_size *= 2;
|
||||
// Allocate the new buffer
|
||||
char* const newBufp = new char[m_size];
|
||||
// Copy from current buffer to new buffer
|
||||
std::memcpy(newBufp, m_bufp, usedSize);
|
||||
// Delete current buffer
|
||||
delete[] m_bufp;
|
||||
// Make new buffer the current buffer
|
||||
m_bufp = newBufp;
|
||||
// Adjust write pointer
|
||||
m_writep = m_bufp + usedSize;
|
||||
// Adjust resize limit
|
||||
adjustGrowp();
|
||||
}
|
||||
#else
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
|
||||
#endif
|
||||
} else {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
|
||||
m_owner.m_writep = m_writep;
|
||||
m_owner.bufferFlush();
|
||||
m_writep = m_owner.m_writep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// emit* trace routines
|
||||
|
||||
// Note: emit* are only ever called from one place (full* in
|
||||
// verilated_trace_imp.cpp, which is included in this file at the top),
|
||||
// verilated_trace_imp.h, which is included in this file at the top),
|
||||
// so always inline them.
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitBit(vluint32_t code, CData newval) {
|
||||
void VerilatedVcdBuffer::emitBit(uint32_t code, CData newval) {
|
||||
// Don't prefetch suffix as it's a bit too late;
|
||||
char* wp = m_writep;
|
||||
*wp++ = '0' | static_cast<char>(newval);
|
||||
@@ -630,7 +715,7 @@ void VerilatedVcd::emitBit(vluint32_t code, CData newval) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitCData(vluint32_t code, CData newval, int bits) {
|
||||
void VerilatedVcdBuffer::emitCData(uint32_t code, CData newval, int bits) {
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
cvtCDataToStr(wp, newval << (VL_BYTESIZE - bits));
|
||||
@@ -638,7 +723,7 @@ void VerilatedVcd::emitCData(vluint32_t code, CData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitSData(vluint32_t code, SData newval, int bits) {
|
||||
void VerilatedVcdBuffer::emitSData(uint32_t code, SData newval, int bits) {
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
cvtSDataToStr(wp, newval << (VL_SHORTSIZE - bits));
|
||||
@@ -646,7 +731,7 @@ void VerilatedVcd::emitSData(vluint32_t code, SData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitIData(vluint32_t code, IData newval, int bits) {
|
||||
void VerilatedVcdBuffer::emitIData(uint32_t code, IData newval, int bits) {
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
cvtIDataToStr(wp, newval << (VL_IDATASIZE - bits));
|
||||
@@ -654,7 +739,7 @@ void VerilatedVcd::emitIData(vluint32_t code, IData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitQData(vluint32_t code, QData newval, int bits) {
|
||||
void VerilatedVcdBuffer::emitQData(uint32_t code, QData newval, int bits) {
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
cvtQDataToStr(wp, newval << (VL_QUADSIZE - bits));
|
||||
@@ -662,7 +747,7 @@ void VerilatedVcd::emitQData(vluint32_t code, QData newval, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitWData(vluint32_t code, const WData* newvalp, int bits) {
|
||||
void VerilatedVcdBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) {
|
||||
int words = VL_WORDS_I(bits);
|
||||
char* wp = m_writep;
|
||||
*wp++ = 'b';
|
||||
@@ -679,272 +764,10 @@ void VerilatedVcd::emitWData(vluint32_t code, const WData* newvalp, int bits) {
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedVcd::emitDouble(vluint32_t code, double newval) {
|
||||
void VerilatedVcdBuffer::emitDouble(uint32_t code, double newval) {
|
||||
char* wp = m_writep;
|
||||
// Buffer can't overflow before VL_SNPRINTF; we sized during declaration
|
||||
VL_SNPRINTF(wp, m_wrChunkSize, "r%.16g", newval);
|
||||
VL_SNPRINTF(wp, m_maxSignalBytes, "r%.16g", newval);
|
||||
wp += std::strlen(wp);
|
||||
finishLine(code, wp);
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
|
||||
void VerilatedVcd::fullBit(vluint32_t code, const vluint32_t newval) {
|
||||
// Note the &1, so we don't require clean input -- makes more common no change case faster
|
||||
*oldp(code) = newval;
|
||||
*m_writep++ = ('0' + static_cast<char>(newval & 1));
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
*oldp(code) = newval;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval & (1L << bit)) ? '1' : '0');
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
(*(reinterpret_cast<vluint64_t*>(oldp(code)))) = newval;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval & (1ULL << bit)) ? '1' : '0');
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullArray(vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) { oldp(code)[word] = newval[word]; }
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval[(bit / 32)] & (1L << (bit & 0x1f))) ? '1' : '0');
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullArray(vluint32_t code, const vluint64_t* newval, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) { oldp(code)[word] = newval[word]; }
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval[(bit / 64)] & (1ULL << (bit & 0x3f))) ? '1' : '0');
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
oldp(code)[0] = newval;
|
||||
oldp(code)[1] = newtri;
|
||||
*m_writep++ = "01zz"[newval | (newtri << 1)];
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
|
||||
int bits) {
|
||||
oldp(code)[0] = newval;
|
||||
oldp(code)[1] = newtri;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = "01zz"[((newval >> bit) & 1) | (((newtri >> bit) & 1) << 1)];
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri,
|
||||
int bits) {
|
||||
(*(reinterpret_cast<vluint64_t*>(oldp(code)))) = newval;
|
||||
(*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) = newtri;
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = "01zz"[((newval >> bit) & 1ULL) | (((newtri >> bit) & 1ULL) << 1ULL)];
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullTriArray(vluint32_t code, const vluint32_t* newvalp,
|
||||
const vluint32_t* newtrip, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
oldp(code)[word * 2] = newvalp[word];
|
||||
oldp(code)[word * 2 + 1] = newtrip[word];
|
||||
}
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
vluint32_t valbit = (newvalp[(bit / 32)] >> (bit & 0x1f)) & 1;
|
||||
vluint32_t tribit = (newtrip[(bit / 32)] >> (bit & 0x1f)) & 1;
|
||||
*m_writep++ = "01zz"[valbit | (tribit << 1)];
|
||||
}
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void VerilatedVcd::fullDouble(vluint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
(*(reinterpret_cast<double*>(oldp(code)))) = newval;
|
||||
// Buffer can't overflow before VL_SNPRINTF; we sized during declaration
|
||||
VL_SNPRINTF(m_writep, m_wrChunkSize, "r%.16g", newval);
|
||||
m_writep += std::strlen(m_writep);
|
||||
*m_writep++ = ' ';
|
||||
m_writep = writeCode(m_writep, code);
|
||||
*m_writep++ = '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
|
||||
#endif // VL_TRACE_VCD_OLD_API
|
||||
|
||||
//======================================================================
|
||||
//======================================================================
|
||||
//======================================================================
|
||||
|
||||
#ifdef VERILATED_VCD_TEST
|
||||
#include <iostream>
|
||||
|
||||
extern void verilated_trace_imp_selftest();
|
||||
|
||||
vluint32_t v1, v2, s1, s2[3];
|
||||
vluint32_t tri96[3];
|
||||
vluint32_t tri96__tri[3];
|
||||
vluint64_t quad96[2];
|
||||
vluint64_t tquad;
|
||||
vluint64_t tquad__tri;
|
||||
vluint8_t ch;
|
||||
vluint64_t timestamp = 1;
|
||||
double doub = 0.0;
|
||||
float flo = 0.0f;
|
||||
|
||||
void vcdInit(void*, VerilatedVcd* vcdp, vluint32_t) {
|
||||
vcdp->scopeEscape('.');
|
||||
vcdp->pushNamePrefix("top.");
|
||||
/**/ vcdp->declBus(0x2, "v1", -1, 0, 5, 1);
|
||||
/**/ vcdp->declBus(0x3, "v2", -1, 0, 6, 1);
|
||||
/**/ vcdp->pushNamePrefix("sub1.");
|
||||
/***/ vcdp->declBit(0x4, "s1", -1, 0);
|
||||
/***/ vcdp->declBit(0x5, "ch", -1, 0);
|
||||
/**/ vcdp->popNamePrefix();
|
||||
/**/ vcdp->pushNamePrefix("sub2.");
|
||||
/***/ vcdp->declArray(0x6, "s2", -1, 0, 40, 3);
|
||||
/**/ vcdp->popNamePrefix();
|
||||
vcdp->popNamePrefix();
|
||||
// Note need to add 3 for next code.
|
||||
vcdp->pushNamePrefix("top2.");
|
||||
/**/ vcdp->declBus(0x2, "t2v1", -1, 0, 4, 1);
|
||||
/**/ vcdp->declTriBit(0x10, "io1", -1, 0);
|
||||
/**/ vcdp->declTriBus(0x12, "io5", -1, 0, 4, 0);
|
||||
/**/ vcdp->declTriArray(0x16, "io96", -1, 0, 95, 0);
|
||||
/**/ // Note need to add 6 for next code.
|
||||
/**/ vcdp->declDouble(0x1c, "doub", -1, 0);
|
||||
/**/ // Note need to add 2 for next code.
|
||||
/**/ vcdp->declArray(0x20, "q2", -1, 0, 95, 0);
|
||||
/**/ // Note need to add 4 for next code.
|
||||
/**/ vcdp->declTriQuad(0x24, "tq", -1, 0, 63, 0);
|
||||
/**/ // Note need to add 4 for next code.
|
||||
vcdp->popNamePrefix();
|
||||
}
|
||||
|
||||
void vcdFull(void*, VerilatedVcd* vcdp) {
|
||||
vcdp->fullBus(0x2, v1, 5);
|
||||
vcdp->fullBus(0x3, v2, 7);
|
||||
vcdp->fullBit(0x4, s1);
|
||||
vcdp->fullBus(0x5, ch, 2);
|
||||
vcdp->fullArray(0x6, &s2[0], 38);
|
||||
vcdp->fullTriBit(0x10, tri96[0] & 1, tri96__tri[0] & 1);
|
||||
vcdp->fullTriBus(0x12, tri96[0] & 0x1f, tri96__tri[0] & 0x1f, 5);
|
||||
vcdp->fullTriArray(0x16, tri96, tri96__tri, 96);
|
||||
vcdp->fullDouble(0x1c, doub);
|
||||
vcdp->fullArray(0x20, &quad96[0], 96);
|
||||
vcdp->fullTriQuad(0x24, tquad, tquad__tri, 64);
|
||||
}
|
||||
|
||||
void vcdChange(void*, VerilatedVcd* vcdp) {
|
||||
vcdp->chgBus(0x2, v1, 5);
|
||||
vcdp->chgBus(0x3, v2, 7);
|
||||
vcdp->chgBit(0x4, s1);
|
||||
vcdp->chgBus(0x5, ch, 2);
|
||||
vcdp->chgArray(0x6, &s2[0], 38);
|
||||
vcdp->chgTriBit(0x10, tri96[0] & 1, tri96__tri[0] & 1);
|
||||
vcdp->chgTriBus(0x12, tri96[0] & 0x1f, tri96__tri[0] & 0x1f, 5);
|
||||
vcdp->chgTriArray(0x16, tri96, tri96__tri, 96);
|
||||
vcdp->chgDouble(0x1c, doub);
|
||||
vcdp->chgArray(0x20, &quad96[0], 96);
|
||||
vcdp->chgTriQuad(0x24, tquad, tquad__tri, 64);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
void vcdTestMain(const char* filenamep) {
|
||||
verilated_trace_imp_selftest();
|
||||
|
||||
v1 = v2 = s1 = 0;
|
||||
s2[0] = s2[1] = s2[2] = 0;
|
||||
tri96[2] = tri96[1] = tri96[0] = 0;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = ~0;
|
||||
quad96[1] = quad96[0] = 0;
|
||||
ch = 0;
|
||||
doub = 0;
|
||||
tquad = tquad__tri = 0;
|
||||
{
|
||||
VerilatedVcdC* vcdp = new VerilatedVcdC;
|
||||
vcdp->evcd(true);
|
||||
vcdp->set_time_unit("1ms");
|
||||
vcdp->set_time_unit(std::string{"1ms"});
|
||||
vcdp->set_time_resolution("1ns");
|
||||
vcdp->set_time_resolution(std::string{"1ns"});
|
||||
vcdp->spTrace()->addInitCb(&vcdInit, 0);
|
||||
vcdp->spTrace()->addFullCb(&vcdFull, 0);
|
||||
vcdp->spTrace()->addChgCb(&vcdChange, 0);
|
||||
vcdp->open(filenamep);
|
||||
// Dumping
|
||||
vcdp->dump(++timestamp);
|
||||
v1 = 0xfff;
|
||||
tri96[2] = 4; tri96[1] = 2; tri96[0] = 1;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = ~0; // Still tri
|
||||
quad96[1] = 0xffffffff; quad96[0] = 0;
|
||||
doub = 1.5;
|
||||
flo = 1.4f;
|
||||
vcdp->dump(++timestamp);
|
||||
v2 = 0x1;
|
||||
s2[1] = 2;
|
||||
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = 0; // enable w/o data change
|
||||
quad96[1] = 0; quad96[0] = ~0;
|
||||
doub = -1.66e13;
|
||||
flo = 0.123f;
|
||||
tquad = 0x00ff00ff00ff00ffULL;
|
||||
tquad__tri = 0x0000fffff0000ffffULL;
|
||||
vcdp->dump(++timestamp);
|
||||
ch = 2;
|
||||
tri96[2] = ~4; tri96[1] = ~2; tri96[0] = ~1;
|
||||
doub = -3.33e-13;
|
||||
vcdp->dump(++timestamp);
|
||||
vcdp->dump(++timestamp);
|
||||
# ifdef VERILATED_VCD_TEST_64BIT
|
||||
const vluint64_t bytesPerDump = 15ULL;
|
||||
for (vluint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) {
|
||||
v1 = i;
|
||||
vcdp->dump(++timestamp);
|
||||
}
|
||||
# endif
|
||||
vcdp->close();
|
||||
VL_DO_CLEAR(delete vcdp, vcdp = nullptr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
//********************************************************************
|
||||
// ;compile-command: "v4make test_regress/t/t_trace_c_api.pl"
|
||||
//
|
||||
// Local Variables:
|
||||
// End:
|
||||
|
||||
+207
-298
@@ -28,7 +28,196 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedVcd;
|
||||
class VerilatedVcdBuffer;
|
||||
class VerilatedVcdFile;
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcd
|
||||
// Base class to create a Verilator VCD dump
|
||||
// This is an internally used class - see VerilatedVcdC for what to call from applications
|
||||
|
||||
class VerilatedVcd VL_NOT_FINAL : public VerilatedTrace<VerilatedVcd, VerilatedVcdBuffer> {
|
||||
public:
|
||||
using Super = VerilatedTrace<VerilatedVcd, VerilatedVcdBuffer>;
|
||||
|
||||
private:
|
||||
friend VerilatedVcdBuffer; // Give the buffer access to the private bits
|
||||
|
||||
//=========================================================================
|
||||
// VCD specific internals
|
||||
|
||||
VerilatedVcdFile* m_filep; // File we're writing to
|
||||
bool m_fileNewed; // m_filep needs destruction
|
||||
bool m_isOpen = false; // True indicates open file
|
||||
std::string m_filename; // Filename we're writing to (if open)
|
||||
uint64_t m_rolloverSize = 0; // File size to rollover at
|
||||
int m_modDepth = 0; // Depth of module hierarchy
|
||||
|
||||
char* m_wrBufp; // Output buffer
|
||||
char* m_wrFlushp; // Output buffer flush trigger location
|
||||
char* m_writep; // Write pointer into output buffer
|
||||
size_t m_wrChunkSize; // Output buffer size
|
||||
size_t m_maxSignalBytes = 0; // Upper bound on number of bytes a single signal can generate
|
||||
uint64_t m_wroteBytes = 0; // Number of bytes written to this file
|
||||
|
||||
std::vector<char> m_suffixes; // VCD line end string codes + metadata
|
||||
|
||||
using NameMap = std::map<const std::string, const std::string>;
|
||||
NameMap* m_namemapp = nullptr; // List of names for the header
|
||||
|
||||
#ifdef VL_THREADED
|
||||
// Vector of free trace buffers as (pointer, size) pairs.
|
||||
std::vector<std::pair<char*, size_t>> m_freeBuffers;
|
||||
size_t m_numBuffers = 0; // Number of trace buffers allocated
|
||||
#endif
|
||||
|
||||
void bufferResize(size_t minsize);
|
||||
void bufferFlush() VL_MT_UNSAFE_ONE;
|
||||
void bufferCheck() {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush();
|
||||
}
|
||||
void openNextImp(bool incFilename);
|
||||
void closePrev();
|
||||
void closeErr();
|
||||
void makeNameMap();
|
||||
void deleteNameMap();
|
||||
void printIndent(int level_change);
|
||||
void printStr(const char* str);
|
||||
void printQuad(uint64_t n);
|
||||
void printTime(uint64_t timeui);
|
||||
void declare(uint32_t code, const char* name, const char* wirep, bool array, int arraynum,
|
||||
bool tri, bool bussed, int msb, int lsb);
|
||||
|
||||
void dumpHeader();
|
||||
|
||||
static char* writeCode(char* writep, uint32_t code);
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcd);
|
||||
|
||||
protected:
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Called when the trace moves forward to a new time point
|
||||
void emitTimeChange(uint64_t timeui) override;
|
||||
|
||||
// Hooks called from VerilatedTrace
|
||||
bool preFullDump() override { return isOpen(); }
|
||||
bool preChangeDump() override;
|
||||
|
||||
// Trace buffer management
|
||||
Buffer* getTraceBuffer() override;
|
||||
void commitTraceBuffer(Buffer*) override;
|
||||
|
||||
// Configure sub-class
|
||||
void configure(const VerilatedTraceConfig&) override{};
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit VerilatedVcd(VerilatedVcdFile* filep = nullptr);
|
||||
~VerilatedVcd();
|
||||
|
||||
// ACCESSORS
|
||||
// Set size in bytes after which new file should be created.
|
||||
void rolloverSize(uint64_t size) { m_rolloverSize = size; }
|
||||
|
||||
// METHODS - All must be thread safe
|
||||
// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Open next data-only file
|
||||
void openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Close the file
|
||||
void close() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Flush any remaining data to this file
|
||||
void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Return if file is open
|
||||
bool isOpen() const VL_MT_SAFE { return m_isOpen; }
|
||||
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
void declBit(uint32_t code, const char* name, bool array, int arraynum);
|
||||
void declBus(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble(uint32_t code, const char* name, bool array, int arraynum);
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// Declare specialization here as it's used in VerilatedFstC just below
|
||||
template <>
|
||||
void VerilatedVcd::Super::dump(uint64_t time);
|
||||
template <>
|
||||
void VerilatedVcd::Super::set_time_unit(const char* unitp);
|
||||
template <>
|
||||
void VerilatedVcd::Super::set_time_unit(const std::string& unit);
|
||||
template <>
|
||||
void VerilatedVcd::Super::set_time_resolution(const char* unitp);
|
||||
template <>
|
||||
void VerilatedVcd::Super::set_time_resolution(const std::string& unit);
|
||||
template <>
|
||||
void VerilatedVcd::Super::dumpvars(int level, const std::string& hier);
|
||||
#endif // DOXYGEN
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdBuffer
|
||||
|
||||
class VerilatedVcdBuffer VL_NOT_FINAL {
|
||||
// Give the trace file ans sub-classes access to the private bits
|
||||
friend VerilatedVcd;
|
||||
friend VerilatedVcd::Super;
|
||||
friend VerilatedVcd::Buffer;
|
||||
friend VerilatedVcd::OffloadBuffer;
|
||||
|
||||
VerilatedVcd& m_owner; // Trace file owning this buffer. Required by subclasses.
|
||||
|
||||
// Write pointer into output buffer (in parallel mode, this is set up in 'getTraceBuffer')
|
||||
char* m_writep = m_owner.parallel() ? nullptr : m_owner.m_writep;
|
||||
// Output buffer flush trigger location (only used when not parallel)
|
||||
char* const m_wrFlushp = m_owner.parallel() ? nullptr : m_owner.m_wrFlushp;
|
||||
|
||||
// VCD line end string codes + metadata
|
||||
const char* const m_suffixes = m_owner.m_suffixes.data();
|
||||
// The maximum number of bytes a single signal can emit
|
||||
const size_t m_maxSignalBytes = m_owner.m_maxSignalBytes;
|
||||
|
||||
#ifdef VL_THREADED
|
||||
// Additional data for parallel tracing only
|
||||
char* m_bufp = nullptr; // The beginning of the trace buffer
|
||||
size_t m_size = 0; // The size of the buffer at m_bufp
|
||||
char* m_growp = nullptr; // Resize limit pointer
|
||||
|
||||
void adjustGrowp() {
|
||||
m_growp = (m_bufp + m_size) - (2 * m_maxSignalBytes);
|
||||
assert(m_growp >= m_bufp + m_maxSignalBytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
void finishLine(uint32_t code, char* writep);
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit VerilatedVcdBuffer(VerilatedVcd& owner)
|
||||
: m_owner{owner} {}
|
||||
virtual ~VerilatedVcdBuffer() = default;
|
||||
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTraceBuffer interface
|
||||
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
|
||||
// called from only one place (the full* methods), so always inline them.
|
||||
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
|
||||
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
|
||||
VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
|
||||
VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFile
|
||||
@@ -52,284 +241,6 @@ public:
|
||||
virtual ssize_t write(const char* bufp, ssize_t len) VL_MT_UNSAFE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcd
|
||||
// Base class to create a Verilator VCD dump
|
||||
// This is an internally used class - see VerilatedVcdC for what to call from applications
|
||||
|
||||
class VerilatedVcd VL_NOT_FINAL : public VerilatedTrace<VerilatedVcd> {
|
||||
private:
|
||||
// Give the superclass access to private bits (to avoid virtual functions)
|
||||
friend class VerilatedTrace<VerilatedVcd>;
|
||||
|
||||
//=========================================================================
|
||||
// VCD specific internals
|
||||
|
||||
VerilatedVcdFile* m_filep; // File we're writing to
|
||||
bool m_fileNewed; // m_filep needs destruction
|
||||
bool m_isOpen = false; // True indicates open file
|
||||
bool m_evcd = false; // True for evcd format
|
||||
std::string m_filename; // Filename we're writing to (if open)
|
||||
vluint64_t m_rolloverMB = 0; // MB of file size to rollover at
|
||||
int m_modDepth = 0; // Depth of module hierarchy
|
||||
|
||||
char* m_wrBufp; // Output buffer
|
||||
const char* m_wrFlushp; // Output buffer flush trigger location
|
||||
char* m_writep; // Write pointer into output buffer
|
||||
vluint64_t m_wrChunkSize; // Output buffer size
|
||||
vluint64_t m_wroteBytes = 0; // Number of bytes written to this file
|
||||
|
||||
std::vector<char> m_suffixes; // VCD line end string codes + metadata
|
||||
|
||||
using NameMap = std::map<const std::string, const std::string>;
|
||||
NameMap* m_namemapp = nullptr; // List of names for the header
|
||||
|
||||
void bufferResize(vluint64_t minsize);
|
||||
void bufferFlush() VL_MT_UNSAFE_ONE;
|
||||
inline void bufferCheck() {
|
||||
// Flush the write buffer if there's not enough space left for new information
|
||||
// We only call this once per vector, so we need enough slop for a very wide "b###" line
|
||||
if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush();
|
||||
}
|
||||
void openNextImp(bool incFilename);
|
||||
void closePrev();
|
||||
void closeErr();
|
||||
void makeNameMap();
|
||||
void deleteNameMap();
|
||||
void printIndent(int level_change);
|
||||
void printStr(const char* str);
|
||||
void printQuad(vluint64_t n);
|
||||
void printTime(vluint64_t timeui);
|
||||
void declare(vluint32_t code, const char* name, const char* wirep, bool array, int arraynum,
|
||||
bool tri, bool bussed, int msb, int lsb);
|
||||
|
||||
void dumpHeader();
|
||||
|
||||
static char* writeCode(char* writep, vluint32_t code);
|
||||
|
||||
void finishLine(vluint32_t code, char* writep);
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcd);
|
||||
|
||||
protected:
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Implementations of protected virtual methods for VerilatedTrace
|
||||
virtual void emitTimeChange(vluint64_t timeui) override;
|
||||
|
||||
// Hooks called from VerilatedTrace
|
||||
virtual bool preFullDump() override { return isOpen(); }
|
||||
virtual bool preChangeDump() override;
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTrace. These are
|
||||
// called from only one place (namely full*) so always inline them.
|
||||
inline void emitBit(vluint32_t code, CData newval);
|
||||
inline void emitCData(vluint32_t code, CData newval, int bits);
|
||||
inline void emitSData(vluint32_t code, SData newval, int bits);
|
||||
inline void emitIData(vluint32_t code, IData newval, int bits);
|
||||
inline void emitQData(vluint32_t code, QData newval, int bits);
|
||||
inline void emitWData(vluint32_t code, const WData* newvalp, int bits);
|
||||
inline void emitDouble(vluint32_t code, double newval);
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
explicit VerilatedVcd(VerilatedVcdFile* filep = nullptr);
|
||||
~VerilatedVcd();
|
||||
|
||||
// ACCESSORS
|
||||
// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(vluint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
|
||||
|
||||
// METHODS
|
||||
// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Open next data-only file
|
||||
void openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Close the file
|
||||
void close() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Flush any remaining data to this file
|
||||
void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
|
||||
// Return if file is open
|
||||
bool isOpen() const VL_MT_SAFE { return m_isOpen; }
|
||||
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
void declBit(vluint32_t code, const char* name, bool array, int arraynum);
|
||||
void declBus(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble(vluint32_t code, const char* name, bool array, int arraynum);
|
||||
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
//=========================================================================
|
||||
// Note: These are only for testing for backward compatibility with foreign
|
||||
// code and is not used by Verilator. Do not use these as there is no
|
||||
// guarantee of functionality.
|
||||
|
||||
void declTriBit(vluint32_t code, const char* name, bool array, int arraynum);
|
||||
void declTriBus(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declTriQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb);
|
||||
void declTriArray(vluint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb);
|
||||
|
||||
void fullBit(vluint32_t* oldp, CData newval) { fullBit(oldp - this->oldp(0), newval); }
|
||||
void fullCData(vluint32_t* oldp, CData newval, int bits) {
|
||||
fullBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullSData(vluint32_t* oldp, SData newval, int bits) {
|
||||
fullBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullIData(vluint32_t* oldp, IData newval, int bits) {
|
||||
fullBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullQData(vluint32_t* oldp, QData newval, int bits) {
|
||||
fullQuad(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullWData(vluint32_t* oldp, const WData* newvalp, int bits) {
|
||||
fullArray(oldp - this->oldp(0), newvalp, bits);
|
||||
}
|
||||
void fullDouble(vluint32_t* oldp, double newval) { fullDouble(oldp - this->oldp(0), newval); }
|
||||
|
||||
inline void chgBit(vluint32_t* oldp, CData newval) { chgBit(oldp - this->oldp(0), newval); }
|
||||
inline void chgCData(vluint32_t* oldp, CData newval, int bits) {
|
||||
chgBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgSData(vluint32_t* oldp, SData newval, int bits) {
|
||||
chgBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgIData(vluint32_t* oldp, IData newval, int bits) {
|
||||
chgBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgQData(vluint32_t* oldp, QData newval, int bits) {
|
||||
chgQuad(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgWData(vluint32_t* oldp, const WData* newvalp, int bits) {
|
||||
chgArray(oldp - this->oldp(0), newvalp, bits);
|
||||
}
|
||||
inline void chgDouble(vluint32_t* oldp, double newval) {
|
||||
chgDouble(oldp - this->oldp(0), newval);
|
||||
}
|
||||
|
||||
// Inside dumping routines, dump one signal, faster when not inlined
|
||||
// due to code size reduction.
|
||||
void fullBit(vluint32_t code, const vluint32_t newval);
|
||||
void fullBus(vluint32_t code, const vluint32_t newval, int bits);
|
||||
void fullQuad(vluint32_t code, const vluint64_t newval, int bits);
|
||||
void fullArray(vluint32_t code, const vluint32_t* newvalp, int bits);
|
||||
void fullArray(vluint32_t code, const vluint64_t* newvalp, int bits);
|
||||
void fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri);
|
||||
void fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits);
|
||||
void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri, int bits);
|
||||
void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip,
|
||||
int bits);
|
||||
void fullDouble(vluint32_t code, const double newval);
|
||||
|
||||
// Inside dumping routines, dump one signal if it has changed.
|
||||
// We do want to inline these to avoid calls when the value did not change.
|
||||
inline void chgBit(vluint32_t code, const vluint32_t newval) {
|
||||
const vluint32_t diff = oldp(code)[0] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(code, newval);
|
||||
}
|
||||
inline void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
const vluint32_t diff = oldp(code)[0] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
|
||||
fullBus(code, newval, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
const vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
|
||||
fullQuad(code, newval, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgArray(vluint32_t code, const vluint32_t* newvalp, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
if (VL_UNLIKELY(oldp(code)[word] ^ newvalp[word])) {
|
||||
fullArray(code, newvalp, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgArray(vluint32_t code, const vluint64_t* newvalp, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) {
|
||||
if (VL_UNLIKELY(*(reinterpret_cast<vluint64_t*>(oldp(code + 2 * word)))
|
||||
^ newvalp[word])) {
|
||||
fullArray(code, newvalp, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below
|
||||
// is only for back compatibility
|
||||
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||
fullTriBit(code, newval, newtri);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
|
||||
int bits) {
|
||||
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
|
||||
fullTriBus(code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri,
|
||||
int bits) {
|
||||
const vluint64_t diff = (((*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval)
|
||||
| ((*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
|
||||
fullTriQuad(code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip,
|
||||
int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
if (VL_UNLIKELY((oldp(code)[word * 2] ^ newvalp[word])
|
||||
| (oldp(code)[word * 2 + 1] ^ newtrip[word]))) {
|
||||
fullTriArray(code, newvalp, newtrip, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgDouble(vluint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<double*>(oldp(code)))) != newval)) {
|
||||
fullDouble(code, newval);
|
||||
}
|
||||
}
|
||||
|
||||
// METHODS
|
||||
// Old/standalone API only
|
||||
void evcd(bool flag) { m_evcd = flag; }
|
||||
#endif // VL_TRACE_VCD_OLD_API
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN
|
||||
// Declare specializations here they are used in VerilatedVcdC just below
|
||||
template <> void VerilatedTrace<VerilatedVcd>::dump(vluint64_t timeui);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const std::string& unit);
|
||||
#endif // DOXYGEN
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdC
|
||||
/// Class representing a VCD dump file in C standalone (no SystemC)
|
||||
@@ -348,7 +259,6 @@ public:
|
||||
/// Destruct, flush, and close the dump
|
||||
virtual ~VerilatedVcdC() { close(); }
|
||||
|
||||
public:
|
||||
// METHODS - User called
|
||||
|
||||
/// Return if file is open
|
||||
@@ -361,8 +271,12 @@ public:
|
||||
/// The header is only in the first file created, this allows
|
||||
/// "cat" to be used to combine the header plus any number of data files.
|
||||
void openNext(bool incFilename = true) VL_MT_SAFE { m_sptrace.openNext(incFilename); }
|
||||
/// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(size_t rolloverMB) VL_MT_SAFE { m_sptrace.rolloverMB(rolloverMB); }
|
||||
/// Set size in bytes after which new file should be created
|
||||
/// This will create a header file, followed by each separate file
|
||||
/// which might be larger than the given size (due to chunking and
|
||||
/// alignment to a start of a given time's dump). Any file but the
|
||||
/// first may be removed. Cat files together to create viewable vcd.
|
||||
void rolloverSize(size_t size) VL_MT_SAFE { m_sptrace.rolloverSize(size); }
|
||||
/// Close dump
|
||||
void close() VL_MT_SAFE { m_sptrace.close(); }
|
||||
/// Flush dump
|
||||
@@ -370,12 +284,12 @@ public:
|
||||
/// Write one cycle of dump data
|
||||
/// Call with the current context's time just after eval'ed,
|
||||
/// e.g. ->dump(contextp->time())
|
||||
void dump(vluint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
|
||||
void dump(uint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
|
||||
/// Write one cycle of dump data - backward compatible and to reduce
|
||||
/// conversion warnings. It's better to use a vluint64_t time instead.
|
||||
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
/// conversion warnings. It's better to use a uint64_t time instead.
|
||||
void dump(double timestamp) { dump(static_cast<uint64_t>(timestamp)); }
|
||||
void dump(uint32_t timestamp) { dump(static_cast<uint64_t>(timestamp)); }
|
||||
void dump(int timestamp) { dump(static_cast<uint64_t>(timestamp)); }
|
||||
|
||||
// METHODS - Internal/backward compatible
|
||||
// \protectedsection
|
||||
@@ -392,19 +306,14 @@ public:
|
||||
void set_time_resolution(const std::string& unit) VL_MT_SAFE {
|
||||
m_sptrace.set_time_resolution(unit);
|
||||
}
|
||||
// Set variables to dump, using $dumpvars format
|
||||
// If level = 0, dump everything and hier is then ignored
|
||||
void dumpvars(int level, const std::string& hier) VL_MT_SAFE {
|
||||
m_sptrace.dumpvars(level, hier);
|
||||
}
|
||||
|
||||
// Internal class access
|
||||
inline VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
//=========================================================================
|
||||
// Note: These are only for testing for backward compatibility with foreign
|
||||
// code and is not used by Verilator. Do not use these as there is no
|
||||
// guarantee of functionality.
|
||||
|
||||
// Use evcd format
|
||||
void evcd(bool flag) VL_MT_UNSAFE_ONE { m_sptrace.evcd(flag); }
|
||||
#endif
|
||||
VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||
};
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_vcd_sc.h"
|
||||
|
||||
//======================================================================
|
||||
@@ -30,7 +31,7 @@
|
||||
void VerilatedVcdSc::open(const char* filename) {
|
||||
if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {
|
||||
vl_fatal(__FILE__, __LINE__, "VerilatedVcdSc",
|
||||
("%Error: VerilatedVcdSc::open(\"" + std::string(filename)
|
||||
("%Error: VerilatedVcdSc::open(\"" + std::string{filename}
|
||||
+ "\") is called before sc_core::sc_start(). "
|
||||
"Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.")
|
||||
.c_str());
|
||||
@@ -63,9 +64,6 @@ void VerilatedVcdSc::trace(const unsigned int&, const std::string&, const char**
|
||||
DECL_TRACE_METHOD_B( unsigned short )
|
||||
DECL_TRACE_METHOD_B( unsigned int )
|
||||
DECL_TRACE_METHOD_B( unsigned long )
|
||||
#ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
DECL_TRACE_METHOD_B( char )
|
||||
DECL_TRACE_METHOD_B( short )
|
||||
DECL_TRACE_METHOD_B( int )
|
||||
|
||||
+12
-12
@@ -23,6 +23,7 @@
|
||||
#define VERILATOR_VERILATED_VCD_SC_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated_sc.h"
|
||||
#include "verilated_vcd_c.h"
|
||||
|
||||
@@ -54,37 +55,38 @@ public:
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
}
|
||||
/// Destruct, flush, and close the dump
|
||||
virtual ~VerilatedVcdSc() { close(); }
|
||||
~VerilatedVcdSc() override { close(); }
|
||||
|
||||
// METHODS - for SC kernel
|
||||
// Called by SystemC simulate()
|
||||
virtual void cycle(bool delta_cycle) {
|
||||
void cycle(bool delta_cycle) override {
|
||||
if (!delta_cycle) this->dump(sc_time_stamp().to_double());
|
||||
}
|
||||
|
||||
// Override VerilatedVcdC. Must be called after starting simulation.
|
||||
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
|
||||
void open(const char* filename) override VL_MT_SAFE;
|
||||
|
||||
private:
|
||||
// METHODS - Fake outs for linker
|
||||
|
||||
#ifdef NC_SYSTEMC
|
||||
// Cadence Incisive has these as abstract functions so we must create them
|
||||
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
|
||||
void set_time_unit(int exponent10_seconds) override {} // deprecated
|
||||
#endif
|
||||
virtual void set_time_unit(double v, sc_time_unit tu) {} // LCOV_EXCL_LINE
|
||||
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
|
||||
|
||||
//--------------------------------------------------
|
||||
// SystemC 2.1.v1
|
||||
#define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const std::string& name);
|
||||
#define DECL_TRACE_METHOD_A(tp) void trace(const tp& object, const std::string& name) override;
|
||||
#define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace(const tp& object, const std::string& name, int width);
|
||||
void trace(const tp& object, const std::string& name, int width) override;
|
||||
|
||||
virtual void write_comment(const std::string&);
|
||||
virtual void trace(const unsigned int&, const std::string&, const char**);
|
||||
void write_comment(const std::string&) override;
|
||||
void trace(const unsigned int&, const std::string&, const char**) override;
|
||||
|
||||
// clang-format off
|
||||
// Formatting matches that of sc_trace.h
|
||||
// LCOV_EXCL_START
|
||||
#if (SYSTEMC_VERSION >= 20171012)
|
||||
DECL_TRACE_METHOD_A( sc_event )
|
||||
DECL_TRACE_METHOD_A( sc_time )
|
||||
@@ -98,9 +100,6 @@ private:
|
||||
DECL_TRACE_METHOD_B( unsigned short )
|
||||
DECL_TRACE_METHOD_B( unsigned int )
|
||||
DECL_TRACE_METHOD_B( unsigned long )
|
||||
#ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
DECL_TRACE_METHOD_B( char )
|
||||
DECL_TRACE_METHOD_B( short )
|
||||
DECL_TRACE_METHOD_B( int )
|
||||
@@ -122,6 +121,7 @@ private:
|
||||
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_bv_base )
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_lv_base )
|
||||
// LCOV_EXCL_STOP
|
||||
// clang-format on
|
||||
|
||||
#undef DECL_TRACE_METHOD_A
|
||||
|
||||
+103
-101
@@ -26,8 +26,9 @@
|
||||
|
||||
#define VERILATOR_VERILATED_VPI_CPP_
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_vpi.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_imp.h"
|
||||
|
||||
#include <list>
|
||||
@@ -66,11 +67,11 @@ class VerilatedVpio VL_NOT_FINAL {
|
||||
// CONSTANTS
|
||||
// Magic value stored in front of object to detect double free etc
|
||||
// Must be odd, as aligned pointer can never be odd
|
||||
static constexpr vluint32_t activeMagic() { return 0xfeed100f; }
|
||||
static constexpr uint32_t activeMagic() { return 0xfeed100f; }
|
||||
|
||||
// MEM MANGLEMENT
|
||||
// Internal note: Globals may multi-construct, see verilated.cpp top.
|
||||
static VL_THREAD_LOCAL vluint8_t* t_freeHead;
|
||||
static VL_THREAD_LOCAL uint8_t* t_freeHead;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
@@ -81,22 +82,23 @@ public:
|
||||
// To simplify our free list, we use a size large enough for all derived types
|
||||
// We reserve word zero for the next pointer, as that's safer in case a
|
||||
// dangling reference to the original remains around.
|
||||
static const size_t chunk = 96;
|
||||
if (VL_UNCOVERABLE(size > chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
|
||||
static constexpr size_t CHUNK_SIZE = 96;
|
||||
if (VL_UNCOVERABLE(size > CHUNK_SIZE))
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "increase CHUNK_SIZE");
|
||||
if (VL_LIKELY(t_freeHead)) {
|
||||
vluint8_t* const newp = t_freeHead;
|
||||
t_freeHead = *(reinterpret_cast<vluint8_t**>(newp));
|
||||
*(reinterpret_cast<vluint32_t*>(newp)) = activeMagic();
|
||||
uint8_t* const newp = t_freeHead;
|
||||
t_freeHead = *(reinterpret_cast<uint8_t**>(newp));
|
||||
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
|
||||
return newp + 8;
|
||||
}
|
||||
// +8: 8 bytes for next
|
||||
vluint8_t* newp = reinterpret_cast<vluint8_t*>(::operator new(chunk + 8));
|
||||
*(reinterpret_cast<vluint32_t*>(newp)) = activeMagic();
|
||||
uint8_t* newp = reinterpret_cast<uint8_t*>(::operator new(CHUNK_SIZE + 8));
|
||||
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
|
||||
return newp + 8;
|
||||
}
|
||||
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
|
||||
vluint8_t* const oldp = (static_cast<vluint8_t*>(obj)) - 8;
|
||||
if (VL_UNLIKELY(*(reinterpret_cast<vluint32_t*>(oldp)) != activeMagic())) {
|
||||
static void operator delete(void* obj, size_t /*size*/) VL_MT_SAFE {
|
||||
uint8_t* const oldp = (static_cast<uint8_t*>(obj)) - 8;
|
||||
if (VL_UNLIKELY(*(reinterpret_cast<uint32_t*>(oldp)) != activeMagic())) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"vpi_release_handle() called on same object twice, or on non-Verilator "
|
||||
"VPI object");
|
||||
@@ -112,13 +114,13 @@ public:
|
||||
static VerilatedVpio* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpio*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
inline vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
|
||||
vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
|
||||
// ACCESSORS
|
||||
virtual const char* name() const { return "<null>"; }
|
||||
virtual const char* fullname() const { return "<null>"; }
|
||||
virtual const char* defname() const { return "<null>"; }
|
||||
virtual vluint32_t type() const { return 0; }
|
||||
virtual vluint32_t size() const { return 0; }
|
||||
virtual uint32_t type() const { return 0; }
|
||||
virtual uint32_t size() const { return 0; }
|
||||
virtual const VerilatedRange* rangep() const { return nullptr; }
|
||||
virtual vpiHandle dovpi_scan() { return nullptr; }
|
||||
virtual PLI_INT32 dovpi_remove_cb() { return 0; }
|
||||
@@ -127,52 +129,52 @@ public:
|
||||
class VerilatedVpioTimedCb final : public VerilatedVpio {
|
||||
// A handle to a timed callback created with vpi_register_cb
|
||||
// User can call vpi_remove_cb or vpi_release_handle on it
|
||||
const vluint64_t m_id; // Unique id/sequence number to find schedule's event
|
||||
const uint64_t m_id; // Unique id/sequence number to find schedule's event
|
||||
const QData m_time;
|
||||
|
||||
public:
|
||||
VerilatedVpioTimedCb(vluint64_t id, QData time)
|
||||
VerilatedVpioTimedCb(uint64_t id, QData time)
|
||||
: m_id{id}
|
||||
, m_time{time} {}
|
||||
virtual ~VerilatedVpioTimedCb() override = default;
|
||||
~VerilatedVpioTimedCb() override = default;
|
||||
static VerilatedVpioTimedCb* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioTimedCb*>(reinterpret_cast<VerilatedVpioTimedCb*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiCallback; }
|
||||
virtual PLI_INT32 dovpi_remove_cb() override;
|
||||
uint32_t type() const override { return vpiCallback; }
|
||||
PLI_INT32 dovpi_remove_cb() override;
|
||||
};
|
||||
|
||||
class VerilatedVpioReasonCb final : public VerilatedVpio {
|
||||
// A handle to a non-timed callback created with vpi_register_cb
|
||||
// User can call vpi_remove_cb or vpi_release_handle on it
|
||||
const vluint64_t m_id; // Unique id/sequence number to find schedule's event
|
||||
const uint64_t m_id; // Unique id/sequence number to find schedule's event
|
||||
const PLI_INT32 m_reason; // VPI callback reason code
|
||||
|
||||
public:
|
||||
// cppcheck-suppress uninitVar // m_value
|
||||
VerilatedVpioReasonCb(vluint64_t id, PLI_INT32 reason)
|
||||
VerilatedVpioReasonCb(uint64_t id, PLI_INT32 reason)
|
||||
: m_id{id}
|
||||
, m_reason{reason} {}
|
||||
virtual ~VerilatedVpioReasonCb() override = default;
|
||||
~VerilatedVpioReasonCb() override = default;
|
||||
static VerilatedVpioReasonCb* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioReasonCb*>(reinterpret_cast<VerilatedVpioReasonCb*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiCallback; }
|
||||
virtual PLI_INT32 dovpi_remove_cb() override;
|
||||
uint32_t type() const override { return vpiCallback; }
|
||||
PLI_INT32 dovpi_remove_cb() override;
|
||||
};
|
||||
|
||||
class VerilatedVpioConst final : public VerilatedVpio {
|
||||
const vlsint32_t m_num;
|
||||
const int32_t m_num;
|
||||
|
||||
public:
|
||||
explicit VerilatedVpioConst(vlsint32_t num)
|
||||
explicit VerilatedVpioConst(int32_t num)
|
||||
: m_num{num} {}
|
||||
virtual ~VerilatedVpioConst() override = default;
|
||||
~VerilatedVpioConst() override = default;
|
||||
static VerilatedVpioConst* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioConst*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiConstant; }
|
||||
vlsint32_t num() const { return m_num; }
|
||||
uint32_t type() const override { return vpiConstant; }
|
||||
int32_t num() const { return m_num; }
|
||||
};
|
||||
|
||||
class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio {
|
||||
@@ -199,10 +201,10 @@ public:
|
||||
}
|
||||
const VerilatedVar* varp() const { return m_varp; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
virtual vluint32_t size() const override { return get_range().elements(); }
|
||||
virtual const VerilatedRange* rangep() const override { return &get_range(); }
|
||||
virtual const char* name() const override { return m_varp->name(); }
|
||||
virtual const char* fullname() const override {
|
||||
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 VL_THREAD_LOCAL std::string t_out;
|
||||
t_out = std::string{m_scopep->name()} + "." + name();
|
||||
return t_out.c_str();
|
||||
@@ -213,12 +215,12 @@ class VerilatedVpioParam final : public VerilatedVpioVarBase {
|
||||
public:
|
||||
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
: VerilatedVpioVarBase{varp, scopep} {}
|
||||
virtual ~VerilatedVpioParam() override = default;
|
||||
~VerilatedVpioParam() override = default;
|
||||
|
||||
static VerilatedVpioParam* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioParam*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiParameter; }
|
||||
uint32_t type() const override { return vpiParameter; }
|
||||
void* varDatap() const { return m_varp->datap(); }
|
||||
};
|
||||
|
||||
@@ -228,13 +230,13 @@ class VerilatedVpioRange final : public VerilatedVpio {
|
||||
public:
|
||||
explicit VerilatedVpioRange(const VerilatedRange* range)
|
||||
: m_range{range} {}
|
||||
virtual ~VerilatedVpioRange() override = default;
|
||||
~VerilatedVpioRange() override = default;
|
||||
static VerilatedVpioRange* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioRange*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiRange; }
|
||||
virtual vluint32_t size() const override { return m_range->elements(); }
|
||||
virtual const VerilatedRange* rangep() const override { return m_range; }
|
||||
uint32_t type() const override { return vpiRange; }
|
||||
uint32_t size() const override { return m_range->elements(); }
|
||||
const VerilatedRange* rangep() const override { return m_range; }
|
||||
};
|
||||
|
||||
class VerilatedVpioRangeIter final : public VerilatedVpio {
|
||||
@@ -245,12 +247,12 @@ class VerilatedVpioRangeIter final : public VerilatedVpio {
|
||||
public:
|
||||
explicit VerilatedVpioRangeIter(const VerilatedRange* range)
|
||||
: m_range{range} {}
|
||||
virtual ~VerilatedVpioRangeIter() override = default;
|
||||
~VerilatedVpioRangeIter() override = default;
|
||||
static VerilatedVpioRangeIter* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioRangeIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiIterator; }
|
||||
virtual vpiHandle dovpi_scan() override {
|
||||
uint32_t type() const override { return vpiIterator; }
|
||||
vpiHandle dovpi_scan() override {
|
||||
if (VL_UNLIKELY(m_done)) {
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
@@ -267,26 +269,26 @@ protected:
|
||||
public:
|
||||
explicit VerilatedVpioScope(const VerilatedScope* scopep)
|
||||
: m_scopep{scopep} {}
|
||||
virtual ~VerilatedVpioScope() override = default;
|
||||
~VerilatedVpioScope() override = default;
|
||||
static VerilatedVpioScope* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiScope; }
|
||||
uint32_t type() const override { return vpiScope; }
|
||||
const VerilatedScope* scopep() const { return m_scopep; }
|
||||
virtual const char* name() const override { return m_scopep->name(); }
|
||||
virtual const char* fullname() const override { return m_scopep->name(); }
|
||||
const char* name() const override { return m_scopep->name(); }
|
||||
const char* fullname() const override { return m_scopep->name(); }
|
||||
};
|
||||
|
||||
class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase {
|
||||
vluint8_t* m_prevDatap = nullptr; // Previous value of data, for cbValueChange
|
||||
uint8_t* m_prevDatap = nullptr; // Previous value of data, for cbValueChange
|
||||
union {
|
||||
vluint8_t u8[4];
|
||||
vluint32_t u32;
|
||||
uint8_t u8[4];
|
||||
uint32_t u32;
|
||||
} m_mask; // memoized variable mask
|
||||
vluint32_t m_entSize = 0; // memoized variable size
|
||||
uint32_t m_entSize = 0; // memoized variable size
|
||||
protected:
|
||||
void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries
|
||||
vlsint32_t m_index = 0;
|
||||
int32_t m_index = 0;
|
||||
|
||||
public:
|
||||
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
|
||||
@@ -307,24 +309,24 @@ public:
|
||||
m_mask.u32 = 0;
|
||||
}
|
||||
}
|
||||
virtual ~VerilatedVpioVar() override {
|
||||
~VerilatedVpioVar() override {
|
||||
if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr);
|
||||
}
|
||||
static VerilatedVpioVar* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioVar*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
vluint32_t mask() const { return m_mask.u32; }
|
||||
vluint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
|
||||
vluint32_t entSize() const { return m_entSize; }
|
||||
vluint32_t index() const { return m_index; }
|
||||
virtual vluint32_t type() const override {
|
||||
uint32_t mask() const { return m_mask.u32; }
|
||||
uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
|
||||
uint32_t entSize() const { return m_entSize; }
|
||||
uint32_t index() const { return m_index; }
|
||||
uint32_t type() const override {
|
||||
return (varp()->dims() > 1) ? vpiMemory : vpiReg; // but might be wire, logic
|
||||
}
|
||||
void* prevDatap() const { return m_prevDatap; }
|
||||
void* varDatap() const { return m_varDatap; }
|
||||
void createPrevDatap() {
|
||||
if (VL_UNLIKELY(!m_prevDatap)) {
|
||||
m_prevDatap = new vluint8_t[entSize()];
|
||||
m_prevDatap = new uint8_t[entSize()];
|
||||
std::memcpy(prevDatap(), varp()->datap(), entSize());
|
||||
}
|
||||
}
|
||||
@@ -332,20 +334,20 @@ public:
|
||||
|
||||
class VerilatedVpioMemoryWord final : public VerilatedVpioVar {
|
||||
public:
|
||||
VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep,
|
||||
vlsint32_t index, int offset)
|
||||
VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep, int32_t index,
|
||||
int offset)
|
||||
: VerilatedVpioVar{varp, scopep} {
|
||||
m_index = index;
|
||||
m_varDatap = (static_cast<vluint8_t*>(varp->datap())) + entSize() * offset;
|
||||
m_varDatap = (static_cast<uint8_t*>(varp->datap())) + entSize() * offset;
|
||||
}
|
||||
virtual ~VerilatedVpioMemoryWord() override = default;
|
||||
~VerilatedVpioMemoryWord() override = default;
|
||||
static VerilatedVpioMemoryWord* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioMemoryWord*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiMemoryWord; }
|
||||
virtual vluint32_t size() const override { return varp()->packed().elements(); }
|
||||
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
|
||||
virtual const char* fullname() const override {
|
||||
uint32_t type() const override { return vpiMemoryWord; }
|
||||
uint32_t size() const override { return varp()->packed().elements(); }
|
||||
const VerilatedRange* rangep() const override { return &(varp()->packed()); }
|
||||
const char* fullname() const override {
|
||||
static VL_THREAD_LOCAL std::string t_out;
|
||||
constexpr size_t LEN_MAX_INDEX = 25;
|
||||
char num[LEN_MAX_INDEX];
|
||||
@@ -363,12 +365,12 @@ class VerilatedVpioVarIter final : public VerilatedVpio {
|
||||
public:
|
||||
explicit VerilatedVpioVarIter(const VerilatedScope* scopep)
|
||||
: m_scopep{scopep} {}
|
||||
virtual ~VerilatedVpioVarIter() override = default;
|
||||
~VerilatedVpioVarIter() override = default;
|
||||
static VerilatedVpioVarIter* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioVarIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiIterator; }
|
||||
virtual vpiHandle dovpi_scan() override {
|
||||
uint32_t type() const override { return vpiIterator; }
|
||||
vpiHandle dovpi_scan() override {
|
||||
if (VL_LIKELY(m_scopep->varsp())) {
|
||||
const VerilatedVarNameMap* const varsp = m_scopep->varsp();
|
||||
if (VL_UNLIKELY(!m_started)) {
|
||||
@@ -394,8 +396,8 @@ public:
|
||||
class VerilatedVpioMemoryWordIter final : public VerilatedVpio {
|
||||
const vpiHandle m_handle;
|
||||
const VerilatedVar* const m_varp;
|
||||
vlsint32_t m_iteration;
|
||||
const vlsint32_t m_direction;
|
||||
int32_t m_iteration;
|
||||
const int32_t m_direction;
|
||||
bool m_done = false;
|
||||
|
||||
public:
|
||||
@@ -404,15 +406,15 @@ public:
|
||||
, m_varp{varp}
|
||||
, m_iteration{varp->unpacked().right()}
|
||||
, m_direction{VL_LIKELY(varp->unpacked().left() > varp->unpacked().right()) ? 1 : -1} {}
|
||||
virtual ~VerilatedVpioMemoryWordIter() override = default;
|
||||
~VerilatedVpioMemoryWordIter() override = default;
|
||||
static VerilatedVpioMemoryWordIter* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioMemoryWordIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiIterator; }
|
||||
uint32_t type() const override { return vpiIterator; }
|
||||
void iterationInc() {
|
||||
if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction;
|
||||
}
|
||||
virtual vpiHandle dovpi_scan() override {
|
||||
vpiHandle dovpi_scan() override {
|
||||
if (VL_UNLIKELY(m_done)) {
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
@@ -437,9 +439,9 @@ public:
|
||||
static VerilatedVpioModule* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiModule; }
|
||||
virtual const char* name() const override { return m_name; }
|
||||
virtual const char* fullname() const override { return m_fullname; }
|
||||
uint32_t type() const override { return vpiModule; }
|
||||
const char* name() const override { return m_name; }
|
||||
const char* fullname() const override { return m_fullname; }
|
||||
};
|
||||
|
||||
class VerilatedVpioModuleIter final : public VerilatedVpio {
|
||||
@@ -451,12 +453,12 @@ public:
|
||||
: m_vec{&vec} {
|
||||
m_it = m_vec->begin();
|
||||
}
|
||||
virtual ~VerilatedVpioModuleIter() override = default;
|
||||
~VerilatedVpioModuleIter() override = default;
|
||||
static VerilatedVpioModuleIter* castp(vpiHandle h) {
|
||||
return dynamic_cast<VerilatedVpioModuleIter*>(reinterpret_cast<VerilatedVpio*>(h));
|
||||
}
|
||||
virtual vluint32_t type() const override { return vpiIterator; }
|
||||
virtual vpiHandle dovpi_scan() override {
|
||||
uint32_t type() const override { return vpiIterator; }
|
||||
vpiHandle dovpi_scan() override {
|
||||
if (m_it == m_vec->end()) {
|
||||
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
|
||||
return nullptr;
|
||||
@@ -472,14 +474,14 @@ using VerilatedPliCb = PLI_INT32 (*)(struct t_cb_data*);
|
||||
|
||||
class VerilatedVpiCbHolder final {
|
||||
// Holds information needed to call a callback
|
||||
vluint64_t m_id;
|
||||
uint64_t m_id;
|
||||
s_cb_data m_cbData;
|
||||
s_vpi_value m_value;
|
||||
VerilatedVpioVar m_varo; // If a cbValueChange callback, the object we will return
|
||||
|
||||
public:
|
||||
// cppcheck-suppress uninitVar // m_value
|
||||
VerilatedVpiCbHolder(vluint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
|
||||
VerilatedVpiCbHolder(uint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
|
||||
: m_id{id}
|
||||
, m_cbData{*cbDatap}
|
||||
, m_varo{varop} {
|
||||
@@ -495,15 +497,15 @@ public:
|
||||
~VerilatedVpiCbHolder() = default;
|
||||
VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
|
||||
s_cb_data* cb_datap() { return &m_cbData; }
|
||||
vluint64_t id() const { return m_id; }
|
||||
uint64_t id() const { return m_id; }
|
||||
bool invalid() const { return !m_id; }
|
||||
void invalidate() { m_id = 0; }
|
||||
};
|
||||
|
||||
struct VerilatedVpiTimedCbsCmp {
|
||||
// Ordering sets keyed by time, then callback unique id
|
||||
bool operator()(const std::pair<QData, vluint64_t>& a,
|
||||
const std::pair<QData, vluint64_t>& b) const {
|
||||
bool operator()(const std::pair<QData, uint64_t>& a,
|
||||
const std::pair<QData, uint64_t>& b) const {
|
||||
if (a.first < b.first) return true;
|
||||
if (a.first > b.first) return false;
|
||||
return a.second < b.second;
|
||||
@@ -515,14 +517,14 @@ class VerilatedVpiError;
|
||||
class VerilatedVpiImp final {
|
||||
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maxium callback reason
|
||||
using VpioCbList = std::list<VerilatedVpiCbHolder>;
|
||||
using VpioTimedCbs = std::map<std::pair<QData, vluint64_t>, VerilatedVpiCbHolder>;
|
||||
using VpioTimedCbs = std::map<std::pair<QData, uint64_t>, VerilatedVpiCbHolder>;
|
||||
|
||||
// All only medium-speed, so use singleton function
|
||||
VpioCbList m_cbObjLists[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason
|
||||
VpioTimedCbs m_timedCbs; // Time based callbacks
|
||||
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
|
||||
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
|
||||
vluint64_t m_nextCallbackId = 1; // Id to identify callback
|
||||
uint64_t m_nextCallbackId = 1; // Id to identify callback
|
||||
|
||||
static VerilatedVpiImp& s() { // Singleton
|
||||
static VerilatedVpiImp s_s;
|
||||
@@ -531,9 +533,9 @@ class VerilatedVpiImp final {
|
||||
|
||||
public:
|
||||
static void assertOneCheck() { s().m_assertOne.check(); }
|
||||
static vluint64_t nextCallbackId() { return ++s().m_nextCallbackId; }
|
||||
static uint64_t nextCallbackId() { return ++s().m_nextCallbackId; }
|
||||
|
||||
static void cbReasonAdd(vluint64_t id, const s_cb_data* cb_data_p) {
|
||||
static void cbReasonAdd(uint64_t id, const s_cb_data* cb_data_p) {
|
||||
// The passed cb_data_p was property of the user, so need to recreate
|
||||
if (VL_UNCOVERABLE(cb_data_p->reason >= CB_ENUM_MAX_VALUE)) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
|
||||
@@ -544,7 +546,7 @@ public:
|
||||
if (cb_data_p->reason == cbValueChange) varop = VerilatedVpioVar::castp(cb_data_p->obj);
|
||||
s().m_cbObjLists[cb_data_p->reason].emplace_back(id, cb_data_p, varop);
|
||||
}
|
||||
static void cbTimedAdd(vluint64_t id, const s_cb_data* cb_data_p, QData time) {
|
||||
static void cbTimedAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
|
||||
// The passed cb_data_p was property of the user, so need to recreate
|
||||
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64
|
||||
" delay=%" PRIu64 "\n",
|
||||
@@ -553,7 +555,7 @@ public:
|
||||
std::forward_as_tuple(std::make_pair(time, id)),
|
||||
std::forward_as_tuple(id, cb_data_p, nullptr));
|
||||
}
|
||||
static void cbReasonRemove(vluint64_t id, vluint32_t reason) {
|
||||
static void cbReasonRemove(uint64_t id, uint32_t reason) {
|
||||
// Id might no longer exist, if already removed due to call after event, or teardown
|
||||
VpioCbList& cbObjList = s().m_cbObjLists[reason];
|
||||
// We do not remove it now as we may be iterating the list,
|
||||
@@ -562,7 +564,7 @@ public:
|
||||
if (ir.id() == id) ir.invalidate();
|
||||
}
|
||||
}
|
||||
static void cbTimedRemove(vluint64_t id, QData time) {
|
||||
static void cbTimedRemove(uint64_t id, QData time) {
|
||||
// Id might no longer exist, if already removed due to call after event, or teardown
|
||||
const auto it = s().m_timedCbs.find(std::make_pair(time, id));
|
||||
if (VL_LIKELY(it != s().m_timedCbs.end())) it->second.invalidate();
|
||||
@@ -592,7 +594,7 @@ public:
|
||||
if (VL_LIKELY(it != s().m_timedCbs.cend())) return it->first.first;
|
||||
return ~0ULL; // maxquad
|
||||
}
|
||||
static bool callCbs(const vluint32_t reason) VL_MT_UNSAFE_ONE {
|
||||
static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE {
|
||||
VpioCbList& cbObjList = s().m_cbObjLists[reason];
|
||||
bool called = false;
|
||||
if (cbObjList.empty()) return called;
|
||||
@@ -664,7 +666,7 @@ public:
|
||||
// Statics
|
||||
// Internal note: Globals may multi-construct, see verilated.cpp top.
|
||||
|
||||
VL_THREAD_LOCAL vluint8_t* VerilatedVpio::t_freeHead = nullptr;
|
||||
VL_THREAD_LOCAL uint8_t* VerilatedVpio::t_freeHead = nullptr;
|
||||
|
||||
//======================================================================
|
||||
// VerilatedVpiError
|
||||
@@ -744,7 +746,7 @@ void VerilatedVpi::callTimedCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::callTimedC
|
||||
|
||||
bool VerilatedVpi::callValueCbs() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::callValueCbs(); }
|
||||
|
||||
bool VerilatedVpi::callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
|
||||
bool VerilatedVpi::callCbs(uint32_t reason) VL_MT_UNSAFE_ONE {
|
||||
return VerilatedVpiImp::callCbs(reason);
|
||||
}
|
||||
|
||||
@@ -1309,7 +1311,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
QData time = 0;
|
||||
if (cb_data_p->time) time = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
|
||||
const QData abstime = VL_TIME_Q() + time;
|
||||
const vluint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
const uint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
VerilatedVpioTimedCb* const vop = new VerilatedVpioTimedCb{id, abstime};
|
||||
VerilatedVpiImp::cbTimedAdd(id, cb_data_p, abstime);
|
||||
return vop->castVpiHandle();
|
||||
@@ -1324,7 +1326,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
|
||||
case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
||||
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
|
||||
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
|
||||
const vluint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
const uint64_t id = VerilatedVpiImp::nextCallbackId();
|
||||
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
|
||||
VerilatedVpiImp::cbReasonAdd(id, cb_data_p);
|
||||
return vop->castVpiHandle();
|
||||
@@ -1474,7 +1476,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
|
||||
case vpiIndex: {
|
||||
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
|
||||
if (VL_UNLIKELY(!vop)) return nullptr;
|
||||
const vlsint32_t val = vop->index();
|
||||
const int32_t val = vop->index();
|
||||
return (new VerilatedVpioConst{val})->castVpiHandle();
|
||||
}
|
||||
case vpiScope: {
|
||||
@@ -1996,7 +1998,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
|
||||
for (int i = 0; i < chars; ++i) {
|
||||
union {
|
||||
char byte[2];
|
||||
vluint16_t half;
|
||||
uint16_t half;
|
||||
} val;
|
||||
idx = div(i * 3, 8);
|
||||
if (i < len) {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define VERILATOR_VERILATED_VPI_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_syms.h"
|
||||
|
||||
@@ -47,7 +48,7 @@ public:
|
||||
static bool callValueCbs() VL_MT_UNSAFE_ONE;
|
||||
/// Call callbacks of arbitrary types.
|
||||
/// User wrapper code should call this from their main loops.
|
||||
static bool callCbs(const vluint32_t reason) VL_MT_UNSAFE_ONE;
|
||||
static bool callCbs(uint32_t reason) VL_MT_UNSAFE_ONE;
|
||||
/// Returns time of the next registered VPI callback, or
|
||||
/// ~(0ULL) if none are registered
|
||||
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user